]> andersk Git - libfaim.git/blob - aim_conn.c
Minor typo.
[libfaim.git] / aim_conn.c
1
2 /*
3  *  aim_conn.c
4  *
5  * Does all this gloriously nifty connection handling stuff...
6  *
7  */
8
9 #include <faim/aim.h> 
10
11 #ifndef _WIN32
12 #include <netdb.h>
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #endif
16
17 /**
18  * aim_connrst - Clears out connection list, killing remaining connections.
19  * @sess: Session to be cleared
20  *
21  * Clears out the connection list and kills any connections left.
22  *
23  */
24 faim_internal void aim_connrst(struct aim_session_t *sess)
25 {
26   faim_mutex_init(&sess->connlistlock);
27   if (sess->connlist) {
28     struct aim_conn_t *cur = sess->connlist, *tmp;
29
30     while(cur) {
31       tmp = cur->next;
32       aim_conn_close(cur);
33       free(cur);
34       cur = tmp;
35     }
36   }
37   sess->connlist = NULL;
38   return;
39 }
40
41 /**
42  * aim_conn_getnext - Gets a new connection structure.
43  * @sess: Session
44  *
45  * Allocate a new empty connection structure.
46  *
47  */
48 faim_internal struct aim_conn_t *aim_conn_getnext(struct aim_session_t *sess)
49 {
50   struct aim_conn_t *newconn, *cur;
51
52   if (!(newconn = malloc(sizeof(struct aim_conn_t))))   
53     return NULL;
54
55   memset(newconn, 0, sizeof(struct aim_conn_t));
56   aim_conn_close(newconn);
57   newconn->next = NULL;
58
59   faim_mutex_lock(&sess->connlistlock);
60   if (sess->connlist == NULL)
61     sess->connlist = newconn;
62   else {
63     for (cur = sess->connlist; cur->next; cur = cur->next)
64       ;
65     cur->next = newconn;
66   }
67   faim_mutex_unlock(&sess->connlistlock);
68
69   return newconn;
70 }
71
72 /**
73  * aim_conn_init - Reset a connection to default values.
74  * @deadconn: Connection to be reset
75  *
76  * Initializes and/or resets a connection structure.
77  *
78  */
79 static void aim_conn_init(struct aim_conn_t *deadconn)
80 {
81   if (!deadconn)
82     return;
83
84   deadconn->fd = -1;
85   deadconn->subtype = -1;
86   deadconn->type = -1;
87   deadconn->seqnum = 0;
88   deadconn->lastactivity = 0;
89   deadconn->forcedlatency = 0;
90   deadconn->handlerlist = NULL;
91   deadconn->priv = NULL;
92   faim_mutex_init(&deadconn->active);
93   faim_mutex_init(&deadconn->seqnum_lock);
94   
95   return;
96 }
97
98 /**
99  * aim_conn_kill - Close and free a connection.
100  * @sess: Session for the connection
101  * @deadconn: Connection to be freed
102  *
103  * Close, clear, and free a connection structure.
104  *
105  */
106 faim_export void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **deadconn)
107 {
108   struct aim_conn_t *cur;
109
110   if (!deadconn || !*deadconn)  
111     return;
112
113   faim_mutex_lock(&sess->connlistlock);
114   if (sess->connlist == NULL)
115     ;
116   else if (sess->connlist->next == NULL) {
117     if (sess->connlist == *deadconn)
118       sess->connlist = NULL;
119   } else {
120     cur = sess->connlist;
121     while (cur->next) {
122       if (cur->next == *deadconn) {
123         cur->next = cur->next->next;
124         break;
125       }
126       cur = cur->next;
127     }
128   }
129   faim_mutex_unlock(&sess->connlistlock);
130
131   /* XXX: do we need this for txqueue too? */
132   aim_rxqueue_cleanbyconn(sess, *deadconn);
133
134   aim_conn_close(*deadconn);
135   if ((*deadconn)->priv)
136     free((*deadconn)->priv);
137   free(*deadconn);
138   deadconn = NULL;
139
140   return;
141 }
142
143 /**
144  * aim_conn_close - Close a connection
145  * @deadconn: Connection to close
146  *
147  * Close (but not free) a connection.
148  *
149  */
150 faim_export void aim_conn_close(struct aim_conn_t *deadconn)
151 {
152   int typesav = -1, subtypesav = -1;
153   void *privsav = NULL;
154
155   faim_mutex_destroy(&deadconn->active);
156   faim_mutex_destroy(&deadconn->seqnum_lock);
157   if (deadconn->fd >= 3)
158     close(deadconn->fd);
159   if (deadconn->handlerlist)
160     aim_clearhandlers(deadconn);
161
162   typesav = deadconn->type;
163   subtypesav = deadconn->subtype;
164
165   if (deadconn->priv && (deadconn->type != AIM_CONN_TYPE_RENDEZVOUS)) {
166     free(deadconn->priv);
167     deadconn->priv = NULL;
168   }
169   privsav = deadconn->priv;
170
171   aim_conn_init(deadconn);
172
173   deadconn->type = typesav;
174   deadconn->subtype = subtypesav;
175   deadconn->priv = privsav;
176
177   return;
178 }
179
180 /**
181  * aim_getconn_type - Find a connection of a specific type
182  * @sess: Session to search
183  * @type: Type of connection to look for
184  *
185  * Searches for a connection of the specified type in the 
186  * specified session.  Returns the first connection of that
187  * type found.
188  *
189  */
190 faim_internal struct aim_conn_t *aim_getconn_type(struct aim_session_t *sess,
191                                                   int type)
192 {
193   struct aim_conn_t *cur;
194
195   faim_mutex_lock(&sess->connlistlock);
196   for (cur = sess->connlist; cur; cur = cur->next) {
197     if ((cur->type == type) && !(cur->status & AIM_CONN_STATUS_INPROGRESS))
198       break;
199   }
200   faim_mutex_unlock(&sess->connlistlock);
201   return cur;
202 }
203
204 /**
205  * aim_proxyconnect - An extrememly quick and dirty SOCKS5 interface. 
206  * @sess: Session to connect
207  * @host: Host to connect to
208  * @port: Port to connect to
209  * @statusret: Return value of the connection
210  *
211  * Attempts to connect to the specified host via the configured
212  * proxy settings, if present.  If no proxy is configured for
213  * this session, the connection is done directly.
214  *
215  */
216 static int aim_proxyconnect(struct aim_session_t *sess, 
217                             char *host, unsigned short port,
218                             int *statusret)
219 {
220   int fd = -1;
221
222   if (strlen(sess->socksproxy.server)) { /* connecting via proxy */
223     int i;
224     unsigned char buf[512];
225     struct sockaddr_in sa;
226     struct hostent *hp;
227     char *proxy;
228     unsigned short proxyport = 1080;
229
230     for(i=0;i<(int)strlen(sess->socksproxy.server);i++) {
231       if (sess->socksproxy.server[i] == ':') {
232         proxyport = atoi(&(sess->socksproxy.server[i+1]));
233         break;
234       }
235     }
236     proxy = (char *)malloc(i+1);
237     strncpy(proxy, sess->socksproxy.server, i);
238     proxy[i] = '\0';
239
240     if (!(hp = gethostbyname(proxy))) {
241       printf("proxyconnect: unable to resolve proxy name\n");
242       *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR);
243       return -1;
244     }
245     free(proxy);
246
247     memset(&sa.sin_zero, 0, 8);
248     sa.sin_port = htons(proxyport);
249     memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
250     sa.sin_family = hp->h_addrtype;
251   
252     fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
253     if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
254       printf("proxyconnect: unable to connect to proxy\n");
255       close(fd);
256       return -1;
257     }
258
259     i = 0;
260     buf[0] = 0x05; /* SOCKS version 5 */
261     if (strlen(sess->socksproxy.username)) {
262       buf[1] = 0x02; /* two methods */
263       buf[2] = 0x00; /* no authentication */
264       buf[3] = 0x02; /* username/password authentication */
265       i = 4;
266     } else {
267       buf[1] = 0x01;
268       buf[2] = 0x00;
269       i = 3;
270     }
271
272     if (write(fd, buf, i) < i) {
273       *statusret = errno;
274       close(fd);
275       return -1;
276     }
277
278     if (read(fd, buf, 2) < 2) {
279       *statusret = errno;
280       close(fd);
281       return -1;
282     }
283
284     if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
285       *statusret = EINVAL;
286       close(fd);
287       return -1;
288     }
289
290     /* check if we're doing username authentication */
291     if (buf[1] == 0x02) {
292       i  = aimutil_put8(buf, 0x01); /* version 1 */
293       i += aimutil_put8(buf+i, strlen(sess->socksproxy.username));
294       i += aimutil_putstr(buf+i, sess->socksproxy.username, strlen(sess->socksproxy.username));
295       i += aimutil_put8(buf+i, strlen(sess->socksproxy.password));
296       i += aimutil_putstr(buf+i, sess->socksproxy.password, strlen(sess->socksproxy.password));
297       if (write(fd, buf, i) < i) {
298         *statusret = errno;
299         close(fd);
300         return -1;
301       }
302       if (read(fd, buf, 2) < 2) {
303         *statusret = errno;
304         close(fd);
305         return -1;
306       }
307       if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
308         *statusret = EINVAL;
309         close(fd);
310         return -1;
311       }
312     }
313
314     i  = aimutil_put8(buf, 0x05);
315     i += aimutil_put8(buf+i, 0x01); /* CONNECT */
316     i += aimutil_put8(buf+i, 0x00); /* reserved */
317     i += aimutil_put8(buf+i, 0x03); /* address type: host name */
318     i += aimutil_put8(buf+i, strlen(host));
319     i += aimutil_putstr(buf+i, host, strlen(host));
320     i += aimutil_put16(buf+i, port);
321
322     if (write(fd, buf, i) < i) {
323       *statusret = errno;
324       close(fd);
325       return -1;
326     }
327     if (read(fd, buf, 10) < 10) {
328       *statusret = errno;
329       close(fd);
330       return -1;
331     }
332     if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
333       *statusret = EINVAL;
334       close(fd);
335       return -1;
336     }
337
338   } else { /* connecting directly */
339     struct sockaddr_in sa;
340     struct hostent *hp;
341
342     if (!(hp = gethostbyname(host))) {
343       *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR);
344       return -1;
345     }
346
347     memset(&sa, 0, sizeof(struct sockaddr_in));
348     sa.sin_port = htons(port);
349     memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
350     sa.sin_family = hp->h_addrtype;
351   
352     fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
353
354     if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT)
355       fcntl(fd, F_SETFL, O_NONBLOCK); /* XXX save flags */
356
357     if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
358       if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT) {
359         if ((errno == EINPROGRESS) || (errno == EINTR)) {
360           if (statusret)
361             *statusret |= AIM_CONN_STATUS_INPROGRESS;
362           return fd;
363         }
364       }
365       close(fd);
366       fd = -1;
367     }
368   }
369   return fd;
370 }
371
372 /**
373  * aim_newconn - Open a new connection
374  * @sess: Session to create connection in
375  * @type: Type of connection to create
376  * @dest: Host to connect to (in "host:port" syntax)
377  *
378  * Opens a new connection to the specified dest host of specified
379  * type, using the proxy settings if available.  If @host is %NULL,
380  * the connection is allocated and returned, but no connection 
381  * is made.
382  *
383  * FIXME: Return errors in a more sane way.
384  *
385  */
386 faim_export struct aim_conn_t *aim_newconn(struct aim_session_t *sess,
387                                            int type, char *dest)
388 {
389   struct aim_conn_t *connstruct;
390   int ret;
391   u_short port = FAIM_LOGIN_PORT;
392   char *host = NULL;
393   int i=0;
394
395   if ((connstruct=aim_conn_getnext(sess))==NULL)
396     return NULL;
397
398   faim_mutex_lock(&connstruct->active);
399   
400   connstruct->type = type;
401
402   if (!dest) { /* just allocate a struct */
403     connstruct->fd = -1;
404     connstruct->status = 0;
405     faim_mutex_unlock(&connstruct->active);
406     return connstruct;
407   }
408
409   /* 
410    * As of 23 Jul 1999, AOL now sends the port number, preceded by a 
411    * colon, in the BOS redirect.  This fatally breaks all previous 
412    * libfaims.  Bad, bad AOL.
413    *
414    * We put this here to catch every case. 
415    *
416    */
417
418   for(i=0;i<(int)strlen(dest);i++) {
419     if (dest[i] == ':') {
420       port = atoi(&(dest[i+1]));
421       break;
422     }
423   }
424   host = (char *)malloc(i+1);
425   strncpy(host, dest, i);
426   host[i] = '\0';
427
428   if ((ret = aim_proxyconnect(sess, host, port, &connstruct->status)) < 0) {
429     connstruct->fd = -1;
430     connstruct->status = (errno | AIM_CONN_STATUS_CONNERR);
431     free(host);
432     faim_mutex_unlock(&connstruct->active);
433     return connstruct;
434   } else
435     connstruct->fd = ret;
436   
437   faim_mutex_unlock(&connstruct->active);
438
439   free(host);
440
441   return connstruct;
442 }
443
444 /**
445  * aim_conngetmaxfd - Return the highest valued file discriptor in session
446  * @sess: Session to search
447  *
448  * Returns the highest valued filed descriptor of all open 
449  * connections in @sess.
450  *
451  */
452 faim_export int aim_conngetmaxfd(struct aim_session_t *sess)
453 {
454   int j = 0;
455   struct aim_conn_t *cur;
456
457   faim_mutex_lock(&sess->connlistlock);
458   for (cur = sess->connlist; cur; cur = cur->next) {
459     if (cur->fd > j)
460       j = cur->fd;
461   }
462   faim_mutex_unlock(&sess->connlistlock);
463
464   return j;
465 }
466
467 /**
468  * aim_countconn - Return the number of open connections in the session
469  * @sess: Session to look at
470  *
471  * Returns the number of number connections in @sess.
472  *
473  */
474 static int aim_countconn(struct aim_session_t *sess)
475 {
476   int cnt = 0;
477   struct aim_conn_t *cur;
478
479   faim_mutex_lock(&sess->connlistlock);
480   for (cur = sess->connlist; cur; cur = cur->next)
481     cnt++;
482   faim_mutex_unlock(&sess->connlistlock);
483
484   return cnt;
485 }
486
487 /**
488  * aim_conn_in_sess - Predicate to test the precense of a connection in a sess
489  * @sess: Session to look in
490  * @conn: Connection to look for
491  *
492  * Searches @sess for the passed connection.  Returns 1 if its present,
493  * zero otherwise.
494  *
495  */
496 faim_export int aim_conn_in_sess(struct aim_session_t *sess, struct aim_conn_t *conn)
497 {
498   struct aim_conn_t *cur;
499
500   faim_mutex_lock(&sess->connlistlock);
501   for(cur = sess->connlist; cur; cur = cur->next)
502     if(cur == conn) {
503       faim_mutex_unlock(&sess->connlistlock);
504       return 1;
505     }
506   faim_mutex_unlock(&sess->connlistlock);
507   return 0;
508 }
509
510 /**
511  * aim_select - Wait for a socket with data or timeout
512  * @sess: Session to wait on
513  * @timeout: How long to wait
514  * @status: Return status
515  *
516  * Waits for a socket with data or for timeout, whichever comes first.
517  * See select().
518  * 
519  * Return codes in *status:
520  *   -1  error in select() (%NULL returned)
521  *    0  no events pending (%NULL returned)
522  *    1  outgoing data pending (%NULL returned)
523  *    2  incoming data pending (connection with pending data returned)
524  *
525  * XXX: we could probably stand to do a little courser locking here.
526  *
527  */ 
528 faim_export struct aim_conn_t *aim_select(struct aim_session_t *sess,
529                                           struct timeval *timeout, int *status)
530 {
531   struct aim_conn_t *cur;
532   fd_set fds, wfds;
533   int maxfd = 0;
534   int i, haveconnecting = 0;
535
536   faim_mutex_lock(&sess->connlistlock);
537   if (sess->connlist == NULL) {
538     faim_mutex_unlock(&sess->connlistlock);
539     *status = -1;
540     return NULL;
541   }
542   faim_mutex_unlock(&sess->connlistlock);
543
544   FD_ZERO(&fds);
545   FD_ZERO(&wfds);
546   maxfd = 0;
547
548   faim_mutex_lock(&sess->connlistlock);
549   for (cur = sess->connlist; cur; cur = cur->next) {
550     if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
551       FD_SET(cur->fd, &wfds);
552       haveconnecting++;
553     }
554     FD_SET(cur->fd, &fds);
555     if (cur->fd > maxfd)
556       maxfd = cur->fd;
557   }
558   faim_mutex_unlock(&sess->connlistlock);
559
560   /* 
561    * If we have data waiting to be sent, return
562    *
563    * We have to not do this if theres at least one
564    * connection thats still connecting, since that connection
565    * may have queued data and this return would prevent
566    * the connection from ever completing!  This is a major
567    * inadequacy of the libfaim way of doing things.  It means
568    * that nothing can transmit as long as there's connecting
569    * sockets. Evil.
570    *
571    * But its still better than having blocking connects.
572    *
573    */
574   if (!haveconnecting && (sess->queue_outgoing != NULL)) {
575     *status = 1;
576     return NULL;
577   } 
578
579   if ((i = select(maxfd+1, &fds, &wfds, NULL, timeout))>=1) {
580     faim_mutex_lock(&sess->connlistlock);
581     for (cur = sess->connlist; cur; cur = cur->next) {
582       if ((FD_ISSET(cur->fd, &fds)) || 
583           ((cur->status & AIM_CONN_STATUS_INPROGRESS) && 
584            FD_ISSET(cur->fd, &wfds))) {
585         *status = 2;
586         faim_mutex_unlock(&sess->connlistlock);
587         return cur; /* XXX race condition here -- shouldnt unlock connlist */
588       }
589     }
590     *status = 0; /* shouldn't happen */
591   } else if ((i == -1) && (errno == EINTR)) /* treat interrupts as a timeout */
592     *status = 0;
593   else
594     *status = i; /* can be 0 or -1 */
595
596   faim_mutex_unlock(&sess->connlistlock);
597   return NULL;  /* no waiting or error, return */
598 }
599
600 /**
601  * aim_conn_isready - Test if a connection is marked ready
602  * @conn: Connection to test
603  *
604  * Returns true if the connection is ready, false otherwise.
605  * Returns -1 if the connection is invalid.
606  *
607  * XXX: This is deprecated.
608  *
609  */
610 faim_export int aim_conn_isready(struct aim_conn_t *conn)
611 {
612   if (conn)
613     return (conn->status & 0x0001);
614   return -1;
615 }
616
617 /**
618  * aim_conn_setstatus - Set the status of a connection
619  * @conn: Connection
620  * @status: New status
621  *
622  * @newstatus is %XOR'd with the previous value of the connection
623  * status and returned.  Returns -1 if the connection is invalid.
624  *
625  * This isn't real useful.
626  *
627  */
628 faim_export int aim_conn_setstatus(struct aim_conn_t *conn, int status)
629 {
630   int val;
631
632   if (!conn)
633     return -1;
634   
635   faim_mutex_lock(&conn->active);
636   val = conn->status ^= status;
637   faim_mutex_unlock(&conn->active);
638   return val;
639 }
640
641 /**
642  * aim_conn_setlatency - Set a forced latency value for connection
643  * @conn: Conn to set latency for
644  * @newval: Number of seconds to force between transmits
645  *
646  * Causes @newval seconds to be spent between transmits on a connection.
647  *
648  * This is my lame attempt at overcoming not understanding the rate
649  * limiting. 
650  *
651  * XXX: This should really be replaced with something that scales and
652  * backs off like the real rate limiting does.
653  *
654  */
655 faim_export int aim_conn_setlatency(struct aim_conn_t *conn, int newval)
656 {
657   if (!conn)
658     return -1;
659
660   faim_mutex_lock(&conn->active);
661   conn->forcedlatency = newval;
662   conn->lastactivity = 0; /* reset this just to make sure */
663   faim_mutex_unlock(&conn->active);
664
665   return 0;
666 }
667
668 /**
669  * aim_setupproxy - Configure a proxy for this session
670  * @sess: Session to set proxy for
671  * @server: SOCKS server
672  * @username: SOCKS username
673  * @password: SOCKS password
674  *
675  * Call this with your SOCKS5 proxy server parameters before
676  * the first call to aim_newconn().  If called with all %NULL
677  * args, it will clear out a previously set proxy.  
678  *
679  * Set username and password to %NULL if not applicable.
680  *
681  */
682 faim_export void aim_setupproxy(struct aim_session_t *sess, char *server, char *username, char *password)
683 {
684   /* clear out the proxy info */
685   if (!server || !strlen(server)) {
686     memset(sess->socksproxy.server, 0, sizeof(sess->socksproxy.server));
687     memset(sess->socksproxy.username, 0, sizeof(sess->socksproxy.username));
688     memset(sess->socksproxy.password, 0, sizeof(sess->socksproxy.password));
689     return;
690   }
691
692   strncpy(sess->socksproxy.server, server, sizeof(sess->socksproxy.server));
693   if (username && strlen(username)) 
694     strncpy(sess->socksproxy.username, username, sizeof(sess->socksproxy.username));
695   if (password && strlen(password))
696     strncpy(sess->socksproxy.password, password, sizeof(sess->socksproxy.password));
697   return;
698 }
699
700 /**
701  * aim_session_init - Initializes a session structure
702  * @sess: Session to initialize
703  * @flags: Flags to use. Any of %AIM_SESS_FLAGS %OR'd together.
704  *
705  * Sets up the initial values for a session.
706  *
707  */
708 faim_export void aim_session_init(struct aim_session_t *sess, unsigned long flags)
709 {
710   if (!sess)
711     return;
712
713   memset(sess, 0, sizeof(struct aim_session_t));
714   aim_connrst(sess);
715   sess->queue_outgoing = NULL;
716   sess->queue_incoming = NULL;
717   sess->pendingjoin = NULL;
718   aim_initsnachash(sess);
719   sess->snac_nextid = 0x00000001;
720
721   sess->flags = 0;
722
723   /*
724    * Default to SNAC login unless XORLOGIN is explicitly set.
725    */
726   if (!(flags & AIM_SESS_FLAGS_XORLOGIN))
727     sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
728   sess->flags |= flags;
729
730   /*
731    * This must always be set.  Default to the queue-based
732    * version for back-compatibility.  
733    */
734   sess->tx_enqueue = &aim_tx_enqueue__queuebased;
735
736   return;
737 }
738
739 /**
740  * aim_conn_isconnecting - Determine if a connection is connecting
741  * @conn: Connection to examine
742  *
743  * Returns nonzero if the connection is in the process of
744  * connecting (or if it just completed and aim_conn_completeconnect()
745  * has yet to be called on it).
746  *
747  */
748 faim_export int aim_conn_isconnecting(struct aim_conn_t *conn)
749 {
750   if (!conn)
751     return 0;
752   return (conn->status & AIM_CONN_STATUS_INPROGRESS)?1:0;
753 }
754
755 faim_export int aim_conn_completeconnect(struct aim_session_t *sess, struct aim_conn_t *conn)
756 {
757   fd_set fds, wfds;
758   struct timeval tv;
759   int res, error = ETIMEDOUT;
760   rxcallback_t userfunc;
761
762   if (!conn || (conn->fd == -1))
763     return -1;
764
765   if (!(conn->status & AIM_CONN_STATUS_INPROGRESS))
766     return -1;
767
768   FD_ZERO(&fds);
769   FD_SET(conn->fd, &fds);
770   FD_ZERO(&wfds);
771   FD_SET(conn->fd, &wfds);
772   tv.tv_sec = 0;
773   tv.tv_usec = 0;
774
775   if ((res = select(conn->fd+1, &fds, &wfds, NULL, &tv)) == -1) {
776     error = errno;
777     aim_conn_close(conn);
778     errno = error;
779     return -1;
780   } else if (res == 0) {
781     printf("faim: aim_conn_completeconnect: false alarm on %d\n", conn->fd);
782     return 0; /* hasn't really completed yet... */
783   } 
784
785   if (FD_ISSET(conn->fd, &fds) || FD_ISSET(conn->fd, &wfds)) {
786     int len = sizeof(error);
787
788     if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
789       error = errno;
790   }
791
792   if (error) {
793     aim_conn_close(conn);
794     errno = error;
795     return -1;
796   }
797
798   fcntl(conn->fd, F_SETFL, 0); /* XXX should restore original flags */
799
800   conn->status &= ~AIM_CONN_STATUS_INPROGRESS;
801
802   if ((userfunc = aim_callhandler(conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE)))
803     userfunc(sess, NULL, conn);
804
805   /* Flush out the queues if there was something waiting for this conn  */
806   aim_tx_flushqueue(sess);
807
808   return 0;
809 }
This page took 0.404109 seconds and 5 git commands to generate.