]> andersk Git - libfaim.git/blob - aim_conn.c
5009627a0634b87457165d686e75160dba7df849
[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)
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.sin_zero, 0, 8);
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     if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
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;
522   int maxfd = 0;
523   int i;
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   /* 
534    * If we have data waiting to be sent, return immediatly
535    */
536   if (sess->queue_outgoing != NULL) {
537     *status = 1;
538     return NULL;
539   } 
540
541   FD_ZERO(&fds);
542   maxfd = 0;
543
544   faim_mutex_lock(&sess->connlistlock);
545   for (cur = sess->connlist; cur; cur = cur->next) {
546     FD_SET(cur->fd, &fds);
547     if (cur->fd > maxfd)
548       maxfd = cur->fd;
549   }
550   faim_mutex_unlock(&sess->connlistlock);
551
552   if ((i = select(maxfd+1, &fds, NULL, NULL, timeout))>=1) {
553     faim_mutex_lock(&sess->connlistlock);
554     for (cur = sess->connlist; cur; cur = cur->next) {
555       if (FD_ISSET(cur->fd, &fds)) {
556         *status = 2;
557         faim_mutex_unlock(&sess->connlistlock);
558         return cur;
559       }
560     }
561     *status = 0; /* shouldn't happen */
562   } else if ((i == -1) && (errno == EINTR)) /* treat interrupts as a timeout */
563     *status = 0;
564   else
565     *status = i; /* can be 0 or -1 */
566
567   faim_mutex_unlock(&sess->connlistlock);
568   return NULL;  /* no waiting or error, return */
569 }
570
571 /**
572  * aim_conn_isready - Test if a connection is marked ready
573  * @conn: Connection to test
574  *
575  * Returns true if the connection is ready, false otherwise.
576  * Returns -1 if the connection is invalid.
577  *
578  * XXX: This is deprecated.
579  *
580  */
581 faim_export int aim_conn_isready(struct aim_conn_t *conn)
582 {
583   if (conn)
584     return (conn->status & 0x0001);
585   return -1;
586 }
587
588 /**
589  * aim_conn_setstatus - Set the status of a connection
590  * @conn: Connection
591  * @status: New status
592  *
593  * @newstatus is %XOR'd with the previous value of the connection
594  * status and returned.  Returns -1 if the connection is invalid.
595  *
596  */
597 faim_export int aim_conn_setstatus(struct aim_conn_t *conn, int status)
598 {
599   int val;
600
601   if (!conn)
602     return -1;
603   
604   faim_mutex_lock(&conn->active);
605   val = conn->status ^= status;
606   faim_mutex_unlock(&conn->active);
607   return val;
608 }
609
610 /**
611  * aim_conn_setlatency - Set a forced latency value for connection
612  * @conn: Conn to set latency for
613  * @newval: Number of seconds to force between transmits
614  *
615  * Causes @newval seconds to be spent between transmits on a connection.
616  *
617  * This is my lame attempt at overcoming not understanding the rate
618  * limiting. 
619  *
620  * XXX: This should really be replaced with something that scales and
621  * backs off like the real rate limiting does.
622  *
623  */
624 faim_export int aim_conn_setlatency(struct aim_conn_t *conn, int newval)
625 {
626   if (!conn)
627     return -1;
628
629   faim_mutex_lock(&conn->active);
630   conn->forcedlatency = newval;
631   conn->lastactivity = 0; /* reset this just to make sure */
632   faim_mutex_unlock(&conn->active);
633
634   return 0;
635 }
636
637 /**
638  * aim_setupproxy - Configure a proxy for this session
639  * @sess: Session to set proxy for
640  * @server: SOCKS server
641  * @username: SOCKS username
642  * @password: SOCKS password
643  *
644  * Call this with your SOCKS5 proxy server parameters before
645  * the first call to aim_newconn().  If called with all %NULL
646  * args, it will clear out a previously set proxy.  
647  *
648  * Set username and password to %NULL if not applicable.
649  *
650  */
651 faim_export void aim_setupproxy(struct aim_session_t *sess, char *server, char *username, char *password)
652 {
653   /* clear out the proxy info */
654   if (!server || !strlen(server)) {
655     memset(sess->socksproxy.server, 0, sizeof(sess->socksproxy.server));
656     memset(sess->socksproxy.username, 0, sizeof(sess->socksproxy.username));
657     memset(sess->socksproxy.password, 0, sizeof(sess->socksproxy.password));
658     return;
659   }
660
661   strncpy(sess->socksproxy.server, server, sizeof(sess->socksproxy.server));
662   if (username && strlen(username)) 
663     strncpy(sess->socksproxy.username, username, sizeof(sess->socksproxy.username));
664   if (password && strlen(password))
665     strncpy(sess->socksproxy.password, password, sizeof(sess->socksproxy.password));
666   return;
667 }
668
669 /**
670  * aim_session_init - Initializes a session structure
671  * @sess: Session to initialize
672  *
673  * Sets up the initial values for a session.
674  *
675  */
676 faim_export void aim_session_init(struct aim_session_t *sess)
677 {
678   if (!sess)
679     return;
680
681   memset(sess, 0, sizeof(struct aim_session_t));
682   aim_connrst(sess);
683   sess->queue_outgoing = NULL;
684   sess->queue_incoming = NULL;
685   sess->pendingjoin = NULL;
686   aim_initsnachash(sess);
687   sess->snac_nextid = 0x00000001;
688
689   /*
690    * This must always be set.  Default to the queue-based
691    * version for back-compatibility.  
692    */
693   sess->tx_enqueue = &aim_tx_enqueue__queuebased;
694
695   return;
696 }
This page took 0.208584 seconds and 3 git commands to generate.