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