]> andersk Git - libfaim.git/blame_incremental - aim_conn.c
- Fri Nov 10 08:24:34 UTC 2000
[libfaim.git] / aim_conn.c
... / ...
CommitLineData
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 */
24faim_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 */
48faim_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 */
79static 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 */
106faim_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 */
150faim_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 */
190faim_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 */
216static 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 */
386faim_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 */
452faim_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 */
474static 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 */
496faim_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 */
528faim_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 */
610faim_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 */
628faim_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 */
655faim_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 */
682faim_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 */
708faim_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 */
748faim_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
755faim_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.735705 seconds and 5 git commands to generate.