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