]> andersk Git - libfaim.git/blame_incremental - aim_conn.c
- Wed Oct 25 17:52:20 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 * Clears out connection list, killing remaining connections.
19 */
20faim_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 */
40faim_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
64static 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
83faim_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
120faim_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
150faim_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 */
167static 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 * FIXME: Return errors in a more sane way.
318 *
319 */
320faim_export struct aim_conn_t *aim_newconn(struct aim_session_t *sess,
321 int type, char *dest)
322{
323 struct aim_conn_t *connstruct;
324 int ret;
325 u_short port = FAIM_LOGIN_PORT;
326 char *host = NULL;
327 int i=0;
328
329 if ((connstruct=aim_conn_getnext(sess))==NULL)
330 return NULL;
331
332 faim_mutex_lock(&connstruct->active);
333
334 connstruct->type = type;
335
336 if (!dest) { /* just allocate a struct */
337 connstruct->fd = -1;
338 connstruct->status = 0;
339 faim_mutex_unlock(&connstruct->active);
340 return connstruct;
341 }
342
343 /*
344 * As of 23 Jul 1999, AOL now sends the port number, preceded by a
345 * colon, in the BOS redirect. This fatally breaks all previous
346 * libfaims. Bad, bad AOL.
347 *
348 * We put this here to catch every case.
349 *
350 */
351
352 for(i=0;i<(int)strlen(dest);i++) {
353 if (dest[i] == ':') {
354 port = atoi(&(dest[i+1]));
355 break;
356 }
357 }
358 host = (char *)malloc(i+1);
359 strncpy(host, dest, i);
360 host[i] = '\0';
361
362 if ((ret = aim_proxyconnect(sess, host, port, &connstruct->status)) < 0) {
363 connstruct->fd = -1;
364 connstruct->status = (errno | AIM_CONN_STATUS_CONNERR);
365 free(host);
366 faim_mutex_unlock(&connstruct->active);
367 return connstruct;
368 } else
369 connstruct->fd = ret;
370
371 faim_mutex_unlock(&connstruct->active);
372
373 free(host);
374
375 return connstruct;
376}
377
378faim_export int aim_conngetmaxfd(struct aim_session_t *sess)
379{
380 int j = 0;
381 struct aim_conn_t *cur;
382
383 faim_mutex_lock(&sess->connlistlock);
384 for (cur = sess->connlist; cur; cur = cur->next) {
385 if (cur->fd > j)
386 j = cur->fd;
387 }
388 faim_mutex_unlock(&sess->connlistlock);
389
390 return j;
391}
392
393static int aim_countconn(struct aim_session_t *sess)
394{
395 int cnt = 0;
396 struct aim_conn_t *cur;
397
398 faim_mutex_lock(&sess->connlistlock);
399 for (cur = sess->connlist; cur; cur = cur->next)
400 cnt++;
401 faim_mutex_unlock(&sess->connlistlock);
402
403 return cnt;
404}
405
406faim_export int aim_conn_in_sess(struct aim_session_t *sess, struct aim_conn_t *conn)
407{
408 struct aim_conn_t *cur;
409
410 faim_mutex_lock(&sess->connlistlock);
411 for(cur = sess->connlist; cur; cur = cur->next)
412 if(cur == conn) {
413 faim_mutex_unlock(&sess->connlistlock);
414 return 1;
415 }
416 faim_mutex_unlock(&sess->connlistlock);
417 return 0;
418}
419
420/*
421 * aim_select(timeout)
422 *
423 * Waits for a socket with data or for timeout, whichever comes first.
424 * See select(2).
425 *
426 * Return codes in *status:
427 * -1 error in select() (NULL returned)
428 * 0 no events pending (NULL returned)
429 * 1 outgoing data pending (NULL returned)
430 * 2 incoming data pending (connection with pending data returned)
431 *
432 * XXX: we could probably stand to do a little courser locking here.
433 *
434 */
435faim_export struct aim_conn_t *aim_select(struct aim_session_t *sess,
436 struct timeval *timeout, int *status)
437{
438 struct aim_conn_t *cur;
439 fd_set fds;
440 int maxfd = 0;
441 int i;
442
443 faim_mutex_lock(&sess->connlistlock);
444 if (sess->connlist == NULL) {
445 faim_mutex_unlock(&sess->connlistlock);
446 *status = -1;
447 return NULL;
448 }
449 faim_mutex_unlock(&sess->connlistlock);
450
451 /*
452 * If we have data waiting to be sent, return immediatly
453 */
454 if (sess->queue_outgoing != NULL) {
455 *status = 1;
456 return NULL;
457 }
458
459 FD_ZERO(&fds);
460 maxfd = 0;
461
462 faim_mutex_lock(&sess->connlistlock);
463 for (cur = sess->connlist; cur; cur = cur->next) {
464 FD_SET(cur->fd, &fds);
465 if (cur->fd > maxfd)
466 maxfd = cur->fd;
467 }
468 faim_mutex_unlock(&sess->connlistlock);
469
470 if ((i = select(maxfd+1, &fds, NULL, NULL, timeout))>=1) {
471 faim_mutex_lock(&sess->connlistlock);
472 for (cur = sess->connlist; cur; cur = cur->next) {
473 if (FD_ISSET(cur->fd, &fds)) {
474 *status = 2;
475 faim_mutex_unlock(&sess->connlistlock);
476 return cur;
477 }
478 }
479 *status = 0; /* shouldn't happen */
480 } else if ((i == -1) && (errno == EINTR)) /* treat interrupts as a timeout */
481 *status = 0;
482 else
483 *status = i; /* can be 0 or -1 */
484
485 faim_mutex_unlock(&sess->connlistlock);
486 return NULL; /* no waiting or error, return */
487}
488
489faim_export int aim_conn_isready(struct aim_conn_t *conn)
490{
491 if (conn)
492 return (conn->status & 0x0001);
493 return -1;
494}
495
496faim_export int aim_conn_setstatus(struct aim_conn_t *conn, int status)
497{
498 int val;
499
500 if (!conn)
501 return -1;
502
503 faim_mutex_lock(&conn->active);
504 val = conn->status ^= status;
505 faim_mutex_unlock(&conn->active);
506 return val;
507}
508
509faim_export int aim_conn_setlatency(struct aim_conn_t *conn, int newval)
510{
511 if (!conn)
512 return -1;
513
514 faim_mutex_lock(&conn->active);
515 conn->forcedlatency = newval;
516 conn->lastactivity = 0; /* reset this just to make sure */
517 faim_mutex_unlock(&conn->active);
518
519 return 0;
520}
521
522/*
523 * Call this with your SOCKS5 proxy server parameters before
524 * the first call to aim_newconn(). If called with all NULL
525 * args, it will clear out a previously set proxy.
526 *
527 * Set username and password to NULL if not applicable.
528 *
529 */
530faim_export void aim_setupproxy(struct aim_session_t *sess, char *server, char *username, char *password)
531{
532 /* clear out the proxy info */
533 if (!server || !strlen(server)) {
534 memset(sess->socksproxy.server, 0, sizeof(sess->socksproxy.server));
535 memset(sess->socksproxy.username, 0, sizeof(sess->socksproxy.username));
536 memset(sess->socksproxy.password, 0, sizeof(sess->socksproxy.password));
537 return;
538 }
539
540 strncpy(sess->socksproxy.server, server, sizeof(sess->socksproxy.server));
541 if (username && strlen(username))
542 strncpy(sess->socksproxy.username, username, sizeof(sess->socksproxy.username));
543 if (password && strlen(password))
544 strncpy(sess->socksproxy.password, password, sizeof(sess->socksproxy.password));
545 return;
546}
547
548faim_export void aim_session_init(struct aim_session_t *sess)
549{
550 if (!sess)
551 return;
552
553 memset(sess, 0, sizeof(struct aim_session_t));
554 aim_connrst(sess);
555 sess->queue_outgoing = NULL;
556 sess->queue_incoming = NULL;
557 sess->pendingjoin = NULL;
558 aim_initsnachash(sess);
559 sess->snac_nextid = 0x00000001;
560
561 /*
562 * This must always be set. Default to the queue-based
563 * version for back-compatibility.
564 */
565 sess->tx_enqueue = &aim_tx_enqueue__queuebased;
566
567 return;
568}
This page took 0.084992 seconds and 5 git commands to generate.