]> andersk Git - libfaim.git/blame - aim_conn.c
- Wed Oct 25 17:52:20 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
040457cc 17/*
18 * Clears out connection list, killing remaining connections.
19 */
78b3fb13 20faim_internal void aim_connrst(struct aim_session_t *sess)
9de3ca7e 21{
b13c9e13 22 faim_mutex_init(&sess->connlistlock);
040457cc 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 }
e88ba395 32 }
040457cc 33 sess->connlist = NULL;
34 return;
9de3ca7e 35}
36
040457cc 37/*
38 * Gets a new connection structure.
39 */
78b3fb13 40faim_internal struct aim_conn_t *aim_conn_getnext(struct aim_session_t *sess)
9de3ca7e 41{
040457cc 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
4d7c1bfb 64static void aim_conn_init(struct aim_conn_t *deadconn)
65{
66 if (!deadconn)
67 return;
68
69 deadconn->fd = -1;
3369f8d4 70 deadconn->subtype = -1;
4d7c1bfb 71 deadconn->type = -1;
72 deadconn->seqnum = 0;
73 deadconn->lastactivity = 0;
74 deadconn->forcedlatency = 0;
75 deadconn->handlerlist = NULL;
76 deadconn->priv = NULL;
b13c9e13 77 faim_mutex_init(&deadconn->active);
78 faim_mutex_init(&deadconn->seqnum_lock);
4d7c1bfb 79
80 return;
81}
82
78b3fb13 83faim_export void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **deadconn)
040457cc 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
68ac63c2 108 /* XXX: do we need this for txqueue too? */
109 aim_rxqueue_cleanbyconn(sess, *deadconn);
110
f8ac5020 111 aim_conn_close(*deadconn);
112 if ((*deadconn)->priv)
113 free((*deadconn)->priv);
040457cc 114 free(*deadconn);
115 deadconn = NULL;
116
117 return;
9de3ca7e 118}
119
78b3fb13 120faim_export void aim_conn_close(struct aim_conn_t *deadconn)
9de3ca7e 121{
3369f8d4 122 int typesav = -1, subtypesav = -1;
123 void *privsav = NULL;
4d7c1bfb 124
125 faim_mutex_destroy(&deadconn->active);
126 faim_mutex_destroy(&deadconn->seqnum_lock);
9de3ca7e 127 if (deadconn->fd >= 3)
128 close(deadconn->fd);
0252e8c0 129 if (deadconn->handlerlist)
130 aim_clearhandlers(deadconn);
3369f8d4 131
132 typesav = deadconn->type;
133 subtypesav = deadconn->subtype;
134
135 if (deadconn->priv && (deadconn->type != AIM_CONN_TYPE_RENDEZVOUS)) {
0c20631f 136 free(deadconn->priv);
3369f8d4 137 deadconn->priv = NULL;
138 }
139 privsav = deadconn->priv;
140
4d7c1bfb 141 aim_conn_init(deadconn);
142
143 deadconn->type = typesav;
3369f8d4 144 deadconn->subtype = subtypesav;
145 deadconn->priv = privsav;
4d7c1bfb 146
147 return;
9de3ca7e 148}
149
78b3fb13 150faim_internal struct aim_conn_t *aim_getconn_type(struct aim_session_t *sess,
151 int type)
9de3ca7e 152{
040457cc 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;
9de3ca7e 162}
163
bb0dc593 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
9de3ca7e 312/*
313 * aim_newconn(type, dest)
314 *
315 * Opens a new connection to the specified dest host of type type.
316 *
9de3ca7e 317 * FIXME: Return errors in a more sane way.
318 *
319 */
78b3fb13 320faim_export struct aim_conn_t *aim_newconn(struct aim_session_t *sess,
321 int type, char *dest)
9de3ca7e 322{
323 struct aim_conn_t *connstruct;
324 int ret;
9de3ca7e 325 u_short port = FAIM_LOGIN_PORT;
a25832e6 326 char *host = NULL;
9de3ca7e 327 int i=0;
040457cc 328
e6b05d80 329 if ((connstruct=aim_conn_getnext(sess))==NULL)
9de3ca7e 330 return NULL;
331
040457cc 332 faim_mutex_lock(&connstruct->active);
333
9de3ca7e 334 connstruct->type = type;
335
e6b05d80 336 if (!dest) { /* just allocate a struct */
337 connstruct->fd = -1;
338 connstruct->status = 0;
040457cc 339 faim_mutex_unlock(&connstruct->active);
e6b05d80 340 return connstruct;
341 }
342
9de3ca7e 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
5ac21963 352 for(i=0;i<(int)strlen(dest);i++) {
e88ba395 353 if (dest[i] == ':') {
354 port = atoi(&(dest[i+1]));
355 break;
9de3ca7e 356 }
e88ba395 357 }
a25832e6 358 host = (char *)malloc(i+1);
359 strncpy(host, dest, i);
9ba272ca 360 host[i] = '\0';
361
bb0dc593 362 if ((ret = aim_proxyconnect(sess, host, port, &connstruct->status)) < 0) {
e88ba395 363 connstruct->fd = -1;
364 connstruct->status = (errno | AIM_CONN_STATUS_CONNERR);
bb0dc593 365 free(host);
040457cc 366 faim_mutex_unlock(&connstruct->active);
e88ba395 367 return connstruct;
bb0dc593 368 } else
369 connstruct->fd = ret;
a25832e6 370
040457cc 371 faim_mutex_unlock(&connstruct->active);
372
bb0dc593 373 free(host);
374
9de3ca7e 375 return connstruct;
376}
377
78b3fb13 378faim_export int aim_conngetmaxfd(struct aim_session_t *sess)
9de3ca7e 379{
040457cc 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);
e88ba395 389
9de3ca7e 390 return j;
391}
392
78b3fb13 393static int aim_countconn(struct aim_session_t *sess)
9de3ca7e 394{
040457cc 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);
e88ba395 402
9de3ca7e 403 return cnt;
404}
405
3b101546 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
9de3ca7e 420/*
421 * aim_select(timeout)
422 *
423 * Waits for a socket with data or for timeout, whichever comes first.
424 * See select(2).
425 *
b8d0da45 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 *
040457cc 432 * XXX: we could probably stand to do a little courser locking here.
433 *
9de3ca7e 434 */
78b3fb13 435faim_export struct aim_conn_t *aim_select(struct aim_session_t *sess,
436 struct timeval *timeout, int *status)
9de3ca7e 437{
040457cc 438 struct aim_conn_t *cur;
9de3ca7e 439 fd_set fds;
040457cc 440 int maxfd = 0;
9de3ca7e 441 int i;
442
040457cc 443 faim_mutex_lock(&sess->connlistlock);
444 if (sess->connlist == NULL) {
445 faim_mutex_unlock(&sess->connlistlock);
68ac63c2 446 *status = -1;
447 return NULL;
040457cc 448 }
449 faim_mutex_unlock(&sess->connlistlock);
f1a5efe0 450
9de3ca7e 451 /*
452 * If we have data waiting to be sent, return immediatly
453 */
b8d0da45 454 if (sess->queue_outgoing != NULL) {
455 *status = 1;
456 return NULL;
457 }
9de3ca7e 458
459 FD_ZERO(&fds);
040457cc 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 }
9797852c 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 */
b8d0da45 484
040457cc 485 faim_mutex_unlock(&sess->connlistlock);
b8d0da45 486 return NULL; /* no waiting or error, return */
9de3ca7e 487}
488
78b3fb13 489faim_export int aim_conn_isready(struct aim_conn_t *conn)
9de3ca7e 490{
491 if (conn)
492 return (conn->status & 0x0001);
040457cc 493 return -1;
9de3ca7e 494}
495
78b3fb13 496faim_export int aim_conn_setstatus(struct aim_conn_t *conn, int status)
9de3ca7e 497{
040457cc 498 int val;
499
500 if (!conn)
9de3ca7e 501 return -1;
040457cc 502
503 faim_mutex_lock(&conn->active);
504 val = conn->status ^= status;
505 faim_mutex_unlock(&conn->active);
506 return val;
9de3ca7e 507}
508
78b3fb13 509faim_export int aim_conn_setlatency(struct aim_conn_t *conn, int newval)
9de3ca7e 510{
511 if (!conn)
512 return -1;
040457cc 513
514 faim_mutex_lock(&conn->active);
9de3ca7e 515 conn->forcedlatency = newval;
516 conn->lastactivity = 0; /* reset this just to make sure */
040457cc 517 faim_mutex_unlock(&conn->active);
9de3ca7e 518
519 return 0;
520}
a25832e6 521
bb0dc593 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
78b3fb13 548faim_export void aim_session_init(struct aim_session_t *sess)
a25832e6 549{
a25832e6 550 if (!sess)
551 return;
552
040457cc 553 memset(sess, 0, sizeof(struct aim_session_t));
e88ba395 554 aim_connrst(sess);
a25832e6 555 sess->queue_outgoing = NULL;
556 sess->queue_incoming = NULL;
0c20631f 557 sess->pendingjoin = NULL;
b13c9e13 558 aim_initsnachash(sess);
a25832e6 559 sess->snac_nextid = 0x00000001;
560
e88ba395 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
a25832e6 567 return;
568}
This page took 1.361875 seconds and 5 git commands to generate.