]> andersk Git - libfaim.git/blob - aim_conn.c
- Fri Sep 22 22:47:49 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  * Clears out connection list, killing remaining connections.
19  */
20 faim_internal void aim_connrst(struct aim_session_t *sess)
21 {
22   faim_mutex_init(&sess->connlistlock);
23   if (sess->connlist) {
24     struct aim_conn_t *cur = sess->connlist, *tmp;
25
26     while(cur) {
27       tmp = cur->next;
28       aim_conn_close(cur);
29       free(cur);
30       cur = tmp;
31     }
32   }
33   sess->connlist = NULL;
34   return;
35 }
36
37 /*
38  * Gets a new connection structure.
39  */
40 faim_internal struct aim_conn_t *aim_conn_getnext(struct aim_session_t *sess)
41 {
42   struct aim_conn_t *newconn, *cur;
43
44   if (!(newconn = malloc(sizeof(struct aim_conn_t))))   
45     return NULL;
46
47   memset(newconn, 0, sizeof(struct aim_conn_t));
48   aim_conn_close(newconn);
49   newconn->next = NULL;
50
51   faim_mutex_lock(&sess->connlistlock);
52   if (sess->connlist == NULL)
53     sess->connlist = newconn;
54   else {
55     for (cur = sess->connlist; cur->next; cur = cur->next)
56       ;
57     cur->next = newconn;
58   }
59   faim_mutex_unlock(&sess->connlistlock);
60
61   return newconn;
62 }
63
64 static void aim_conn_init(struct aim_conn_t *deadconn)
65 {
66   if (!deadconn)
67     return;
68
69   deadconn->fd = -1;
70   deadconn->subtype = -1;
71   deadconn->type = -1;
72   deadconn->seqnum = 0;
73   deadconn->lastactivity = 0;
74   deadconn->forcedlatency = 0;
75   deadconn->handlerlist = NULL;
76   deadconn->priv = NULL;
77   faim_mutex_init(&deadconn->active);
78   faim_mutex_init(&deadconn->seqnum_lock);
79   
80   return;
81 }
82
83 faim_export void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **deadconn)
84 {
85   struct aim_conn_t *cur;
86
87   if (!deadconn || !*deadconn)  
88     return;
89
90   faim_mutex_lock(&sess->connlistlock);
91   if (sess->connlist == NULL)
92     ;
93   else if (sess->connlist->next == NULL) {
94     if (sess->connlist == *deadconn)
95       sess->connlist = NULL;
96   } else {
97     cur = sess->connlist;
98     while (cur->next) {
99       if (cur->next == *deadconn) {
100         cur->next = cur->next->next;
101         break;
102       }
103       cur = cur->next;
104     }
105   }
106   faim_mutex_unlock(&sess->connlistlock);
107
108   /* XXX: do we need this for txqueue too? */
109   aim_rxqueue_cleanbyconn(sess, *deadconn);
110
111   aim_conn_close(*deadconn);
112   if ((*deadconn)->priv)
113     free((*deadconn)->priv);
114   free(*deadconn);
115   deadconn = NULL;
116
117   return;
118 }
119
120 faim_export void aim_conn_close(struct aim_conn_t *deadconn)
121 {
122   int typesav = -1, subtypesav = -1;
123   void *privsav = NULL;
124
125   faim_mutex_destroy(&deadconn->active);
126   faim_mutex_destroy(&deadconn->seqnum_lock);
127   if (deadconn->fd >= 3)
128     close(deadconn->fd);
129   if (deadconn->handlerlist)
130     aim_clearhandlers(deadconn);
131
132   typesav = deadconn->type;
133   subtypesav = deadconn->subtype;
134
135   if (deadconn->priv && (deadconn->type != AIM_CONN_TYPE_RENDEZVOUS)) {
136     free(deadconn->priv);
137     deadconn->priv = NULL;
138   }
139   privsav = deadconn->priv;
140
141   aim_conn_init(deadconn);
142
143   deadconn->type = typesav;
144   deadconn->subtype = subtypesav;
145   deadconn->priv = privsav;
146
147   return;
148 }
149
150 faim_internal struct aim_conn_t *aim_getconn_type(struct aim_session_t *sess,
151                                                   int type)
152 {
153   struct aim_conn_t *cur;
154
155   faim_mutex_lock(&sess->connlistlock);
156   for (cur = sess->connlist; cur; cur = cur->next) {
157     if (cur->type == type)
158       break;
159   }
160   faim_mutex_unlock(&sess->connlistlock);
161   return cur;
162 }
163
164 /*
165  * An extrememly quick and dirty SOCKS5 interface. 
166  */
167 static int aim_proxyconnect(struct aim_session_t *sess, 
168                             char *host, unsigned short port,
169                             int *statusret)
170 {
171   int fd = -1;
172
173   if (strlen(sess->socksproxy.server)) { /* connecting via proxy */
174     int i;
175     unsigned char buf[512];
176     struct sockaddr_in sa;
177     struct hostent *hp;
178     char *proxy;
179     unsigned short proxyport = 1080;
180
181     for(i=0;i<(int)strlen(sess->socksproxy.server);i++) {
182       if (sess->socksproxy.server[i] == ':') {
183         proxyport = atoi(&(sess->socksproxy.server[i+1]));
184         break;
185       }
186     }
187     proxy = (char *)malloc(i+1);
188     strncpy(proxy, sess->socksproxy.server, i);
189     proxy[i] = '\0';
190
191     if (!(hp = gethostbyname(proxy))) {
192       printf("proxyconnect: unable to resolve proxy name\n");
193       *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR);
194       return -1;
195     }
196     free(proxy);
197
198     memset(&sa.sin_zero, 0, 8);
199     sa.sin_port = htons(proxyport);
200     memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
201     sa.sin_family = hp->h_addrtype;
202   
203     fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
204     if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
205       printf("proxyconnect: unable to connect to proxy\n");
206       close(fd);
207       return -1;
208     }
209
210     i = 0;
211     buf[0] = 0x05; /* SOCKS version 5 */
212     if (strlen(sess->socksproxy.username)) {
213       buf[1] = 0x02; /* two methods */
214       buf[2] = 0x00; /* no authentication */
215       buf[3] = 0x02; /* username/password authentication */
216       i = 4;
217     } else {
218       buf[1] = 0x01;
219       buf[2] = 0x00;
220       i = 3;
221     }
222
223     if (write(fd, buf, i) < i) {
224       *statusret = errno;
225       close(fd);
226       return -1;
227     }
228
229     if (read(fd, buf, 2) < 2) {
230       *statusret = errno;
231       close(fd);
232       return -1;
233     }
234
235     if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
236       *statusret = EINVAL;
237       close(fd);
238       return -1;
239     }
240
241     /* check if we're doing username authentication */
242     if (buf[1] == 0x02) {
243       i  = aimutil_put8(buf, 0x01); /* version 1 */
244       i += aimutil_put8(buf+i, strlen(sess->socksproxy.username));
245       i += aimutil_putstr(buf+i, sess->socksproxy.username, strlen(sess->socksproxy.username));
246       i += aimutil_put8(buf+i, strlen(sess->socksproxy.password));
247       i += aimutil_putstr(buf+i, sess->socksproxy.password, strlen(sess->socksproxy.password));
248       if (write(fd, buf, i) < i) {
249         *statusret = errno;
250         close(fd);
251         return -1;
252       }
253       if (read(fd, buf, 2) < 2) {
254         *statusret = errno;
255         close(fd);
256         return -1;
257       }
258       if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
259         *statusret = EINVAL;
260         close(fd);
261         return -1;
262       }
263     }
264
265     i  = aimutil_put8(buf, 0x05);
266     i += aimutil_put8(buf+i, 0x01); /* CONNECT */
267     i += aimutil_put8(buf+i, 0x00); /* reserved */
268     i += aimutil_put8(buf+i, 0x03); /* address type: host name */
269     i += aimutil_put8(buf+i, strlen(host));
270     i += aimutil_putstr(buf+i, host, strlen(host));
271     i += aimutil_put16(buf+i, port);
272
273     if (write(fd, buf, i) < i) {
274       *statusret = errno;
275       close(fd);
276       return -1;
277     }
278     if (read(fd, buf, 10) < 10) {
279       *statusret = errno;
280       close(fd);
281       return -1;
282     }
283     if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
284       *statusret = EINVAL;
285       close(fd);
286       return -1;
287     }
288
289   } else { /* connecting directly */
290     struct sockaddr_in sa;
291     struct hostent *hp;
292
293     if (!(hp = gethostbyname(host))) {
294       *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR);
295       return -1;
296     }
297
298     memset(&sa.sin_zero, 0, 8);
299     sa.sin_port = htons(port);
300     memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
301     sa.sin_family = hp->h_addrtype;
302   
303     fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
304     if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
305       close(fd);
306       fd = -1;
307     }
308   }
309   return fd;
310 }
311
312 /*
313  * aim_newconn(type, dest)
314  *
315  * Opens a new connection to the specified dest host of type type.
316  *
317  * TODO: fix for proxies
318  * FIXME: Return errors in a more sane way.
319  *
320  */
321 faim_export struct aim_conn_t *aim_newconn(struct aim_session_t *sess,
322                                            int type, char *dest)
323 {
324   struct aim_conn_t *connstruct;
325   int ret;
326   struct sockaddr_in sa;
327   struct hostent *hp;
328   u_short port = FAIM_LOGIN_PORT;
329   char *host = NULL;
330   int i=0;
331
332   if ((connstruct=aim_conn_getnext(sess))==NULL)
333     return NULL;
334
335   faim_mutex_lock(&connstruct->active);
336   
337   connstruct->type = type;
338
339   if (!dest) { /* just allocate a struct */
340     connstruct->fd = -1;
341     connstruct->status = 0;
342     faim_mutex_unlock(&connstruct->active);
343     return connstruct;
344   }
345
346   /* 
347    * As of 23 Jul 1999, AOL now sends the port number, preceded by a 
348    * colon, in the BOS redirect.  This fatally breaks all previous 
349    * libfaims.  Bad, bad AOL.
350    *
351    * We put this here to catch every case. 
352    *
353    */
354
355   for(i=0;i<(int)strlen(dest);i++) {
356     if (dest[i] == ':') {
357       port = atoi(&(dest[i+1]));
358       break;
359     }
360   }
361   host = (char *)malloc(i+1);
362   strncpy(host, dest, i);
363   host[i] = '\0';
364
365   if ((ret = aim_proxyconnect(sess, host, port, &connstruct->status)) < 0) {
366     connstruct->fd = -1;
367     connstruct->status = (errno | AIM_CONN_STATUS_CONNERR);
368     free(host);
369     faim_mutex_unlock(&connstruct->active);
370     return connstruct;
371   } else
372     connstruct->fd = ret;
373   
374   faim_mutex_unlock(&connstruct->active);
375
376   free(host);
377
378   return connstruct;
379 }
380
381 faim_export int aim_conngetmaxfd(struct aim_session_t *sess)
382 {
383   int j = 0;
384   struct aim_conn_t *cur;
385
386   faim_mutex_lock(&sess->connlistlock);
387   for (cur = sess->connlist; cur; cur = cur->next) {
388     if (cur->fd > j)
389       j = cur->fd;
390   }
391   faim_mutex_unlock(&sess->connlistlock);
392
393   return j;
394 }
395
396 static int aim_countconn(struct aim_session_t *sess)
397 {
398   int cnt = 0;
399   struct aim_conn_t *cur;
400
401   faim_mutex_lock(&sess->connlistlock);
402   for (cur = sess->connlist; cur; cur = cur->next)
403     cnt++;
404   faim_mutex_unlock(&sess->connlistlock);
405
406   return cnt;
407 }
408
409 /*
410  * aim_select(timeout)
411  *
412  * Waits for a socket with data or for timeout, whichever comes first.
413  * See select(2).
414  * 
415  * Return codes in *status:
416  *   -1  error in select() (NULL returned)
417  *    0  no events pending (NULL returned)
418  *    1  outgoing data pending (NULL returned)
419  *    2  incoming data pending (connection with pending data returned)
420  *
421  * XXX: we could probably stand to do a little courser locking here.
422  *
423  */ 
424 faim_export struct aim_conn_t *aim_select(struct aim_session_t *sess,
425                                           struct timeval *timeout, int *status)
426 {
427   struct aim_conn_t *cur;
428   fd_set fds;
429   int maxfd = 0;
430   int i;
431
432   faim_mutex_lock(&sess->connlistlock);
433   if (sess->connlist == NULL) {
434     faim_mutex_unlock(&sess->connlistlock);
435     *status = -1;
436     return NULL;
437   }
438   faim_mutex_unlock(&sess->connlistlock);
439
440   /* 
441    * If we have data waiting to be sent, return immediatly
442    */
443   if (sess->queue_outgoing != NULL) {
444     *status = 1;
445     return NULL;
446   } 
447
448   FD_ZERO(&fds);
449   maxfd = 0;
450
451   faim_mutex_lock(&sess->connlistlock);
452   for (cur = sess->connlist; cur; cur = cur->next) {
453     FD_SET(cur->fd, &fds);
454     if (cur->fd > maxfd)
455       maxfd = cur->fd;
456   }
457   faim_mutex_unlock(&sess->connlistlock);
458
459   if ((i = select(maxfd+1, &fds, NULL, NULL, timeout))>=1) {
460     faim_mutex_lock(&sess->connlistlock);
461     for (cur = sess->connlist; cur; cur = cur->next) {
462       if (FD_ISSET(cur->fd, &fds)) {
463         *status = 2;
464         faim_mutex_unlock(&sess->connlistlock);
465         return cur;
466       }
467     }
468     *status = 0; /* shouldn't happen */
469   } else if ((i == -1) && (errno == EINTR)) /* treat interrupts as a timeout */
470     *status = 0;
471   else
472     *status = i; /* can be 0 or -1 */
473
474   faim_mutex_unlock(&sess->connlistlock);
475   return NULL;  /* no waiting or error, return */
476 }
477
478 faim_export int aim_conn_isready(struct aim_conn_t *conn)
479 {
480   if (conn)
481     return (conn->status & 0x0001);
482   return -1;
483 }
484
485 faim_export int aim_conn_setstatus(struct aim_conn_t *conn, int status)
486 {
487   int val;
488
489   if (!conn)
490     return -1;
491   
492   faim_mutex_lock(&conn->active);
493   val = conn->status ^= status;
494   faim_mutex_unlock(&conn->active);
495   return val;
496 }
497
498 faim_export int aim_conn_setlatency(struct aim_conn_t *conn, int newval)
499 {
500   if (!conn)
501     return -1;
502
503   faim_mutex_lock(&conn->active);
504   conn->forcedlatency = newval;
505   conn->lastactivity = 0; /* reset this just to make sure */
506   faim_mutex_unlock(&conn->active);
507
508   return 0;
509 }
510
511 /*
512  * Call this with your SOCKS5 proxy server parameters before
513  * the first call to aim_newconn().  If called with all NULL
514  * args, it will clear out a previously set proxy.  
515  *
516  * Set username and password to NULL if not applicable.
517  *
518  */
519 faim_export void aim_setupproxy(struct aim_session_t *sess, char *server, char *username, char *password)
520 {
521   /* clear out the proxy info */
522   if (!server || !strlen(server)) {
523     memset(sess->socksproxy.server, 0, sizeof(sess->socksproxy.server));
524     memset(sess->socksproxy.username, 0, sizeof(sess->socksproxy.username));
525     memset(sess->socksproxy.password, 0, sizeof(sess->socksproxy.password));
526     return;
527   }
528
529   strncpy(sess->socksproxy.server, server, sizeof(sess->socksproxy.server));
530   if (username && strlen(username)) 
531     strncpy(sess->socksproxy.username, username, sizeof(sess->socksproxy.username));
532   if (password && strlen(password))
533     strncpy(sess->socksproxy.password, password, sizeof(sess->socksproxy.password));
534   return;
535 }
536
537 faim_export void aim_session_init(struct aim_session_t *sess)
538 {
539   if (!sess)
540     return;
541
542   memset(sess, 0, sizeof(struct aim_session_t));
543   aim_connrst(sess);
544   sess->queue_outgoing = NULL;
545   sess->queue_incoming = NULL;
546   sess->pendingjoin = NULL;
547   aim_initsnachash(sess);
548   sess->snac_nextid = 0x00000001;
549
550   /*
551    * This must always be set.  Default to the queue-based
552    * version for back-compatibility.  
553    */
554   sess->tx_enqueue = &aim_tx_enqueue__queuebased;
555
556   return;
557 }
This page took 0.070361 seconds and 5 git commands to generate.