]> andersk Git - libfaim.git/blame - aim_conn.c
- Wed Nov 8 13:11:18 UTC 2000
[libfaim.git] / aim_conn.c
CommitLineData
9de3ca7e 1
2/*
3 * aim_conn.c
4 *
5 * Does all this gloriously nifty connection handling stuff...
6 *
7 */
8
a25832e6 9#include <faim/aim.h>
9de3ca7e 10
67a264ef 11#ifndef _WIN32
78b3fb13 12#include <netdb.h>
13#include <sys/socket.h>
14#include <netinet/in.h>
67a264ef 15#endif
78b3fb13 16
be67fdd0 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 *
040457cc 23 */
78b3fb13 24faim_internal void aim_connrst(struct aim_session_t *sess)
9de3ca7e 25{
b13c9e13 26 faim_mutex_init(&sess->connlistlock);
040457cc 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 }
e88ba395 36 }
040457cc 37 sess->connlist = NULL;
38 return;
9de3ca7e 39}
40
be67fdd0 41/**
42 * aim_conn_getnext - Gets a new connection structure.
43 * @sess: Session
44 *
45 * Allocate a new empty connection structure.
46 *
040457cc 47 */
78b3fb13 48faim_internal struct aim_conn_t *aim_conn_getnext(struct aim_session_t *sess)
9de3ca7e 49{
040457cc 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
be67fdd0 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 */
4d7c1bfb 79static void aim_conn_init(struct aim_conn_t *deadconn)
80{
81 if (!deadconn)
82 return;
83
84 deadconn->fd = -1;
3369f8d4 85 deadconn->subtype = -1;
4d7c1bfb 86 deadconn->type = -1;
87 deadconn->seqnum = 0;
88 deadconn->lastactivity = 0;
89 deadconn->forcedlatency = 0;
90 deadconn->handlerlist = NULL;
91 deadconn->priv = NULL;
b13c9e13 92 faim_mutex_init(&deadconn->active);
93 faim_mutex_init(&deadconn->seqnum_lock);
4d7c1bfb 94
95 return;
96}
97
be67fdd0 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 */
78b3fb13 106faim_export void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **deadconn)
040457cc 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
68ac63c2 131 /* XXX: do we need this for txqueue too? */
132 aim_rxqueue_cleanbyconn(sess, *deadconn);
133
f8ac5020 134 aim_conn_close(*deadconn);
135 if ((*deadconn)->priv)
136 free((*deadconn)->priv);
040457cc 137 free(*deadconn);
138 deadconn = NULL;
139
140 return;
9de3ca7e 141}
142
be67fdd0 143/**
144 * aim_conn_close - Close a connection
145 * @deadconn: Connection to close
146 *
147 * Close (but not free) a connection.
148 *
149 */
78b3fb13 150faim_export void aim_conn_close(struct aim_conn_t *deadconn)
9de3ca7e 151{
3369f8d4 152 int typesav = -1, subtypesav = -1;
153 void *privsav = NULL;
4d7c1bfb 154
155 faim_mutex_destroy(&deadconn->active);
156 faim_mutex_destroy(&deadconn->seqnum_lock);
9de3ca7e 157 if (deadconn->fd >= 3)
158 close(deadconn->fd);
0252e8c0 159 if (deadconn->handlerlist)
160 aim_clearhandlers(deadconn);
3369f8d4 161
162 typesav = deadconn->type;
163 subtypesav = deadconn->subtype;
164
165 if (deadconn->priv && (deadconn->type != AIM_CONN_TYPE_RENDEZVOUS)) {
0c20631f 166 free(deadconn->priv);
3369f8d4 167 deadconn->priv = NULL;
168 }
169 privsav = deadconn->priv;
170
4d7c1bfb 171 aim_conn_init(deadconn);
172
173 deadconn->type = typesav;
3369f8d4 174 deadconn->subtype = subtypesav;
175 deadconn->priv = privsav;
4d7c1bfb 176
177 return;
9de3ca7e 178}
179
be67fdd0 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 */
78b3fb13 190faim_internal struct aim_conn_t *aim_getconn_type(struct aim_session_t *sess,
191 int type)
9de3ca7e 192{
040457cc 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;
9de3ca7e 202}
203
be67fdd0 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 *
bb0dc593 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.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
be67fdd0 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)
9de3ca7e 366 *
be67fdd0 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.
9de3ca7e 371 *
9de3ca7e 372 * FIXME: Return errors in a more sane way.
373 *
374 */
78b3fb13 375faim_export struct aim_conn_t *aim_newconn(struct aim_session_t *sess,
376 int type, char *dest)
9de3ca7e 377{
378 struct aim_conn_t *connstruct;
379 int ret;
9de3ca7e 380 u_short port = FAIM_LOGIN_PORT;
a25832e6 381 char *host = NULL;
9de3ca7e 382 int i=0;
040457cc 383
e6b05d80 384 if ((connstruct=aim_conn_getnext(sess))==NULL)
9de3ca7e 385 return NULL;
386
040457cc 387 faim_mutex_lock(&connstruct->active);
388
9de3ca7e 389 connstruct->type = type;
390
e6b05d80 391 if (!dest) { /* just allocate a struct */
392 connstruct->fd = -1;
393 connstruct->status = 0;
040457cc 394 faim_mutex_unlock(&connstruct->active);
e6b05d80 395 return connstruct;
396 }
397
9de3ca7e 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
5ac21963 407 for(i=0;i<(int)strlen(dest);i++) {
e88ba395 408 if (dest[i] == ':') {
409 port = atoi(&(dest[i+1]));
410 break;
9de3ca7e 411 }
e88ba395 412 }
a25832e6 413 host = (char *)malloc(i+1);
414 strncpy(host, dest, i);
9ba272ca 415 host[i] = '\0';
416
bb0dc593 417 if ((ret = aim_proxyconnect(sess, host, port, &connstruct->status)) < 0) {
e88ba395 418 connstruct->fd = -1;
419 connstruct->status = (errno | AIM_CONN_STATUS_CONNERR);
bb0dc593 420 free(host);
040457cc 421 faim_mutex_unlock(&connstruct->active);
e88ba395 422 return connstruct;
bb0dc593 423 } else
424 connstruct->fd = ret;
a25832e6 425
040457cc 426 faim_mutex_unlock(&connstruct->active);
427
bb0dc593 428 free(host);
429
9de3ca7e 430 return connstruct;
431}
432
be67fdd0 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 */
78b3fb13 441faim_export int aim_conngetmaxfd(struct aim_session_t *sess)
9de3ca7e 442{
040457cc 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);
e88ba395 452
9de3ca7e 453 return j;
454}
455
be67fdd0 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 */
78b3fb13 463static int aim_countconn(struct aim_session_t *sess)
9de3ca7e 464{
040457cc 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);
e88ba395 472
9de3ca7e 473 return cnt;
474}
475
be67fdd0 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 */
3b101546 485faim_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
be67fdd0 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
9de3ca7e 504 *
505 * Waits for a socket with data or for timeout, whichever comes first.
be67fdd0 506 * See select().
9de3ca7e 507 *
b8d0da45 508 * Return codes in *status:
be67fdd0 509 * -1 error in select() (%NULL returned)
510 * 0 no events pending (%NULL returned)
511 * 1 outgoing data pending (%NULL returned)
b8d0da45 512 * 2 incoming data pending (connection with pending data returned)
513 *
040457cc 514 * XXX: we could probably stand to do a little courser locking here.
515 *
9de3ca7e 516 */
78b3fb13 517faim_export struct aim_conn_t *aim_select(struct aim_session_t *sess,
518 struct timeval *timeout, int *status)
9de3ca7e 519{
040457cc 520 struct aim_conn_t *cur;
9de3ca7e 521 fd_set fds;
040457cc 522 int maxfd = 0;
9de3ca7e 523 int i;
524
040457cc 525 faim_mutex_lock(&sess->connlistlock);
526 if (sess->connlist == NULL) {
527 faim_mutex_unlock(&sess->connlistlock);
68ac63c2 528 *status = -1;
529 return NULL;
040457cc 530 }
531 faim_mutex_unlock(&sess->connlistlock);
f1a5efe0 532
9de3ca7e 533 /*
534 * If we have data waiting to be sent, return immediatly
535 */
b8d0da45 536 if (sess->queue_outgoing != NULL) {
537 *status = 1;
538 return NULL;
539 }
9de3ca7e 540
541 FD_ZERO(&fds);
040457cc 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 }
9797852c 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 */
b8d0da45 566
040457cc 567 faim_mutex_unlock(&sess->connlistlock);
b8d0da45 568 return NULL; /* no waiting or error, return */
9de3ca7e 569}
570
be67fdd0 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 */
78b3fb13 581faim_export int aim_conn_isready(struct aim_conn_t *conn)
9de3ca7e 582{
583 if (conn)
584 return (conn->status & 0x0001);
040457cc 585 return -1;
9de3ca7e 586}
587
be67fdd0 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 */
78b3fb13 597faim_export int aim_conn_setstatus(struct aim_conn_t *conn, int status)
9de3ca7e 598{
040457cc 599 int val;
600
601 if (!conn)
9de3ca7e 602 return -1;
040457cc 603
604 faim_mutex_lock(&conn->active);
605 val = conn->status ^= status;
606 faim_mutex_unlock(&conn->active);
607 return val;
9de3ca7e 608}
609
be67fdd0 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 */
78b3fb13 624faim_export int aim_conn_setlatency(struct aim_conn_t *conn, int newval)
9de3ca7e 625{
626 if (!conn)
627 return -1;
040457cc 628
629 faim_mutex_lock(&conn->active);
9de3ca7e 630 conn->forcedlatency = newval;
631 conn->lastactivity = 0; /* reset this just to make sure */
040457cc 632 faim_mutex_unlock(&conn->active);
9de3ca7e 633
634 return 0;
635}
a25832e6 636
be67fdd0 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 *
bb0dc593 644 * Call this with your SOCKS5 proxy server parameters before
be67fdd0 645 * the first call to aim_newconn(). If called with all %NULL
bb0dc593 646 * args, it will clear out a previously set proxy.
647 *
be67fdd0 648 * Set username and password to %NULL if not applicable.
bb0dc593 649 *
650 */
651faim_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
be67fdd0 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 */
78b3fb13 676faim_export void aim_session_init(struct aim_session_t *sess)
a25832e6 677{
a25832e6 678 if (!sess)
679 return;
680
040457cc 681 memset(sess, 0, sizeof(struct aim_session_t));
e88ba395 682 aim_connrst(sess);
a25832e6 683 sess->queue_outgoing = NULL;
684 sess->queue_incoming = NULL;
0c20631f 685 sess->pendingjoin = NULL;
b13c9e13 686 aim_initsnachash(sess);
a25832e6 687 sess->snac_nextid = 0x00000001;
5daacaa3 688 sess->snaclogin = 1; /* default to yes, oh gods yes. */
a25832e6 689
e88ba395 690 /*
691 * This must always be set. Default to the queue-based
692 * version for back-compatibility.
693 */
694 sess->tx_enqueue = &aim_tx_enqueue__queuebased;
695
a25832e6 696 return;
697}
This page took 1.372191 seconds and 5 git commands to generate.