source: protocols/oscar/conn.c @ 0a69d7b

Last change on this file since 0a69d7b was 701acdd4, checked in by Wilmer van der Gaast <wilmer@…>, at 2005-12-16T17:58:00Z

Non-blocking SSL handshakes for GnuTLS. The rest might come later, but is
slightly less important.

  • Property mode set to 100644
File size: 15.5 KB
Line 
1
2/*
3 * conn.c
4 *
5 * Does all this gloriously nifty connection handling stuff...
6 *
7 */
8
9#include <aim.h> 
10#include "sock.h"
11
12static int aim_logoff(aim_session_t *sess);
13
14/*
15 * In OSCAR, every connection has a set of SNAC groups associated
16 * with it.  These are the groups that you can send over this connection
17 * without being guarenteed a "Not supported" SNAC error. 
18 *
19 * The grand theory of things says that these associations transcend
20 * what libfaim calls "connection types" (conn->type).  You can probably
21 * see the elegance here, but since I want to revel in it for a bit, you
22 * get to hear it all spelled out.
23 *
24 * So let us say that you have your core BOS connection running.  One
25 * of your modules has just given you a SNAC of the group 0x0004 to send
26 * you.  Maybe an IM destined for some twit in Greenland.  So you start
27 * at the top of your connection list, looking for a connection that
28 * claims to support group 0x0004.  You find one.  Why, that neat BOS
29 * connection of yours can do that.  So you send it on its way.
30 *
31 * Now, say, that fellow from Greenland has friends and they all want to
32 * meet up with you in a lame chat room.  This has landed you a SNAC
33 * in the family 0x000e and you have to admit you're a bit lost.  You've
34 * searched your connection list for someone who wants to make your life
35 * easy and deliver this SNAC for you, but there isn't one there.
36 *
37 * Here comes the good bit.  Without even letting anyone know, particularly
38 * the module that decided to send this SNAC, and definitly not that twit
39 * in Greenland, you send out a service request.  In this request, you have
40 * marked the need for a connection supporting group 0x000e.  A few seconds
41 * later, you receive a service redirect with an IP address and a cookie in
42 * it.  Great, you say.  Now I have something to do.  Off you go, making
43 * that connection.  One of the first things you get from this new server
44 * is a message saying that indeed it does support the group you were looking
45 * for.  So you continue and send rate confirmation and all that. 
46 *
47 * Then you remember you had that SNAC to send, and now you have a means to
48 * do it, and you do, and everyone is happy.  Except the Greenlander, who is
49 * still stuck in the bitter cold.
50 *
51 * Oh, and this is useful for building the Migration SNACs, too.  In the
52 * future, this may help convince me to implement rate limit mitigation
53 * for real.  We'll see.
54 *
55 * Just to make me look better, I'll say that I've known about this great
56 * scheme for quite some time now.  But I still haven't convinced myself
57 * to make libfaim work that way.  It would take a fair amount of effort,
58 * and probably some client API changes as well.  (Whenever I don't want
59 * to do something, I just say it would change the client API.  Then I
60 * instantly have a couple of supporters of not doing it.)
61 *
62 * Generally, addgroup is only called by the internal handling of the
63 * server ready SNAC.  So if you want to do something before that, you'll
64 * have to be more creative.  That is done rather early, though, so I don't
65 * think you have to worry about it.  Unless you're me.  I care deeply
66 * about such inane things.
67 *
68 */
69void aim_conn_addgroup(aim_conn_t *conn, guint16 group)
70{
71        aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
72        struct snacgroup *sg;
73
74        if (!(sg = g_malloc(sizeof(struct snacgroup))))
75                return;
76
77        sg->group = group;
78
79        sg->next = ins->groups;
80        ins->groups = sg;
81
82        return;
83}
84
85aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, guint16 group)
86{
87        aim_conn_t *cur;
88
89        for (cur = sess->connlist; cur; cur = cur->next) {
90                aim_conn_inside_t *ins = (aim_conn_inside_t *)cur->inside;
91                struct snacgroup *sg;
92
93                for (sg = ins->groups; sg; sg = sg->next) {
94                        if (sg->group == group)
95                                return cur;
96                }
97        }
98
99        return NULL;
100}
101
102static void connkill_snacgroups(struct snacgroup **head)
103{
104        struct snacgroup *sg;
105
106        for (sg = *head; sg; ) {
107                struct snacgroup *tmp;
108
109                tmp = sg->next;
110                g_free(sg);
111                sg = tmp;
112        }
113
114        *head = NULL;
115
116        return;
117}
118
119static void connkill_rates(struct rateclass **head)
120{
121        struct rateclass *rc;
122
123        for (rc = *head; rc; ) {
124                struct rateclass *tmp;
125                struct snacpair *sp;
126
127                tmp = rc->next;
128
129                for (sp = rc->members; sp; ) {
130                        struct snacpair *tmpsp;
131
132                        tmpsp = sp->next;
133                        g_free(sp);
134                        sp = tmpsp;
135                }
136                g_free(rc);
137
138                rc = tmp;
139        }
140
141        *head = NULL;
142
143        return;
144}
145
146static void connkill_real(aim_session_t *sess, aim_conn_t **deadconn)
147{
148
149        aim_rxqueue_cleanbyconn(sess, *deadconn);
150        aim_tx_cleanqueue(sess, *deadconn);
151
152        if ((*deadconn)->fd != -1) 
153                aim_conn_close(*deadconn);
154
155        /*
156         * XXX ->priv should never be touched by the library. I know
157         * it used to be, but I'm getting rid of all that.  Use
158         * ->internal instead.
159         */
160        if ((*deadconn)->priv)
161                g_free((*deadconn)->priv);
162
163        /*
164         * This will free ->internal if it necessary...
165         */
166        if ((*deadconn)->type == AIM_CONN_TYPE_CHAT)
167                aim_conn_kill_chat(sess, *deadconn);
168
169        if ((*deadconn)->inside) {
170                aim_conn_inside_t *inside = (aim_conn_inside_t *)(*deadconn)->inside;
171
172                connkill_snacgroups(&inside->groups);
173                connkill_rates(&inside->rates);
174
175                g_free(inside);
176        }
177
178        g_free(*deadconn);
179        *deadconn = NULL;
180
181        return;
182}
183
184/**
185 * aim_connrst - Clears out connection list, killing remaining connections.
186 * @sess: Session to be cleared
187 *
188 * Clears out the connection list and kills any connections left.
189 *
190 */
191static void aim_connrst(aim_session_t *sess)
192{
193
194        if (sess->connlist) {
195                aim_conn_t *cur = sess->connlist, *tmp;
196
197                while (cur) {
198                        tmp = cur->next;
199                        aim_conn_close(cur);
200                        connkill_real(sess, &cur);
201                        cur = tmp;
202                }
203        }
204
205        sess->connlist = NULL;
206
207        return;
208}
209
210/**
211 * aim_conn_init - Reset a connection to default values.
212 * @deadconn: Connection to be reset
213 *
214 * Initializes and/or resets a connection structure.
215 *
216 */
217static void aim_conn_init(aim_conn_t *deadconn)
218{
219
220        if (!deadconn)
221                return;
222
223        deadconn->fd = -1;
224        deadconn->subtype = -1;
225        deadconn->type = -1;
226        deadconn->seqnum = 0;
227        deadconn->lastactivity = 0;
228        deadconn->forcedlatency = 0;
229        deadconn->handlerlist = NULL;
230        deadconn->priv = NULL;
231        memset(deadconn->inside, 0, sizeof(aim_conn_inside_t));
232
233        return;
234}
235
236/**
237 * aim_conn_getnext - Gets a new connection structure.
238 * @sess: Session
239 *
240 * Allocate a new empty connection structure.
241 *
242 */
243static aim_conn_t *aim_conn_getnext(aim_session_t *sess)
244{
245        aim_conn_t *newconn;
246
247        if (!(newconn = g_new0(aim_conn_t,1))) 
248                return NULL;
249
250        if (!(newconn->inside = g_new0(aim_conn_inside_t,1))) {
251                g_free(newconn);
252                return NULL;
253        }
254
255        aim_conn_init(newconn);
256
257        newconn->next = sess->connlist;
258        sess->connlist = newconn;
259
260        return newconn;
261}
262
263/**
264 * aim_conn_kill - Close and free a connection.
265 * @sess: Session for the connection
266 * @deadconn: Connection to be freed
267 *
268 * Close, clear, and free a connection structure. Should never be
269 * called from within libfaim.
270 *
271 */
272void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn)
273{
274        aim_conn_t *cur, **prev;
275
276        if (!deadconn || !*deadconn)   
277                return;
278
279        for (prev = &sess->connlist; (cur = *prev); ) {
280                if (cur == *deadconn) {
281                        *prev = cur->next;
282                        break;
283                }
284                prev = &cur->next;
285        }
286
287        if (!cur)
288                return; /* oops */
289
290        connkill_real(sess, &cur);
291
292        return;
293}
294
295/**
296 * aim_conn_close - Close a connection
297 * @deadconn: Connection to close
298 *
299 * Close (but not free) a connection.
300 *
301 * This leaves everything untouched except for clearing the
302 * handler list and setting the fd to -1 (used to recognize
303 * dead connections).  It will also remove cookies if necessary.
304 *
305 */
306void aim_conn_close(aim_conn_t *deadconn)
307{
308
309        if (deadconn->fd >= 3)
310                closesocket(deadconn->fd);
311        deadconn->fd = -1;
312        if (deadconn->handlerlist)
313                aim_clearhandlers(deadconn);
314
315        return;
316}
317
318/**
319 * aim_getconn_type - Find a connection of a specific type
320 * @sess: Session to search
321 * @type: Type of connection to look for
322 *
323 * Searches for a connection of the specified type in the
324 * specified session.  Returns the first connection of that
325 * type found.
326 *
327 * XXX except for RENDEZVOUS, all uses of this should be removed and
328 * use aim_conn_findbygroup() instead.
329 */
330aim_conn_t *aim_getconn_type(aim_session_t *sess, int type)
331{
332        aim_conn_t *cur;
333
334        for (cur = sess->connlist; cur; cur = cur->next) {
335                if ((cur->type == type) && 
336                                !(cur->status & AIM_CONN_STATUS_INPROGRESS))
337                        break;
338        }
339
340        return cur;
341}
342
343aim_conn_t *aim_getconn_type_all(aim_session_t *sess, int type)
344{
345        aim_conn_t *cur;
346
347        for (cur = sess->connlist; cur; cur = cur->next) {
348                if (cur->type == type)
349                        break;
350        }
351
352        return cur;
353}
354
355/**
356 * aim_cloneconn - clone an aim_conn_t
357 * @sess: session containing parent
358 * @src: connection to clone
359 *
360 * A new connection is allocated, and the values are filled in
361 * appropriately. Note that this function sets the new connnection's
362 * ->priv pointer to be equal to that of its parent: only the pointer
363 * is copied, not the data it points to.
364 *
365 * This function returns a pointer to the new aim_conn_t, or %NULL on
366 * error
367 */
368aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src)
369{
370        aim_conn_t *conn;
371
372        if (!(conn = aim_conn_getnext(sess)))
373                return NULL;
374
375        conn->fd = src->fd;
376        conn->type = src->type;
377        conn->subtype = src->subtype;
378        conn->seqnum = src->seqnum;
379        conn->priv = src->priv;
380        conn->internal = src->internal;
381        conn->lastactivity = src->lastactivity;
382        conn->forcedlatency = src->forcedlatency;
383        conn->sessv = src->sessv;
384        aim_clonehandlers(sess, conn, src);
385
386        if (src->inside) {
387                /*
388                 * XXX should clone this section as well, but since currently
389                 * this function only gets called for some of that rendezvous
390                 * crap, and not on SNAC connections, its probably okay for
391                 * now.
392                 *
393                 */
394        }
395
396        return conn;
397}
398
399/**
400 * aim_newconn - Open a new connection
401 * @sess: Session to create connection in
402 * @type: Type of connection to create
403 * @dest: Host to connect to (in "host:port" syntax)
404 *
405 * Opens a new connection to the specified dest host of specified
406 * type, using the proxy settings if available.  If @host is %NULL,
407 * the connection is allocated and returned, but no connection
408 * is made.
409 *
410 * FIXME: Return errors in a more sane way.
411 *
412 */
413aim_conn_t *aim_newconn(aim_session_t *sess, int type, const char *dest)
414{
415        aim_conn_t *connstruct;
416        guint16 port = AIM_LOGIN_PORT;
417        char *host;
418        int i;
419
420        if (!(connstruct = aim_conn_getnext(sess)))
421                return NULL;
422
423        connstruct->sessv = (void *)sess;
424        connstruct->type = type;
425
426        if (!dest) { /* just allocate a struct */
427                connstruct->fd = -1;
428                connstruct->status = 0;
429                return connstruct;
430        }
431
432        /*
433         * As of 23 Jul 1999, AOL now sends the port number, preceded by a
434         * colon, in the BOS redirect.  This fatally breaks all previous
435         * libfaims.  Bad, bad AOL.
436         *
437         * We put this here to catch every case.
438         *
439         */
440
441        for(i = 0; i < (int)strlen(dest); i++) {
442                if (dest[i] == ':') {
443                        port = atoi(&(dest[i+1]));
444                        break;
445                }
446        }
447
448        host = (char *)g_malloc(i+1);
449        strncpy(host, dest, i);
450        host[i] = '\0';
451
452        connstruct->fd = proxy_connect(host, port, NULL, NULL);
453
454        g_free(host);
455
456        return connstruct;
457}
458
459/**
460 * aim_conn_setlatency - Set a forced latency value for connection
461 * @conn: Conn to set latency for
462 * @newval: Number of seconds to force between transmits
463 *
464 * Causes @newval seconds to be spent between transmits on a connection.
465 *
466 * This is my lame attempt at overcoming not understanding the rate
467 * limiting.
468 *
469 * XXX: This should really be replaced with something that scales and
470 * backs off like the real rate limiting does.
471 *
472 */
473int aim_conn_setlatency(aim_conn_t *conn, int newval)
474{
475
476        if (!conn)
477                return -1;
478
479        conn->forcedlatency = newval;
480        conn->lastactivity = 0; /* reset this just to make sure */
481
482        return 0;
483}
484
485/**
486 * aim_session_init - Initializes a session structure
487 * @sess: Session to initialize
488 * @flags: Flags to use. Any of %AIM_SESS_FLAGS %OR'd together.
489 * @debuglevel: Level of debugging output (zero is least)
490 *
491 * Sets up the initial values for a session.
492 *
493 */
494void aim_session_init(aim_session_t *sess, guint32 flags, int debuglevel)
495{
496
497        if (!sess)
498                return;
499
500        memset(sess, 0, sizeof(aim_session_t));
501        aim_connrst(sess);
502        sess->queue_outgoing = NULL;
503        sess->queue_incoming = NULL;
504        aim_initsnachash(sess);
505        sess->msgcookies = NULL;
506        sess->snacid_next = 0x00000001;
507
508        sess->flags = 0;
509
510        sess->modlistv = NULL;
511
512        sess->ssi.received_data = 0;
513        sess->ssi.waiting_for_ack = 0;
514        sess->ssi.holding_queue = NULL;
515        sess->ssi.revision = 0;
516        sess->ssi.items = NULL;
517        sess->ssi.timestamp = (time_t)0;
518
519        sess->locate.userinfo = NULL;
520        sess->locate.torequest = NULL;
521        sess->locate.requested = NULL;
522        sess->locate.waiting_for_response = FALSE;
523
524        sess->icq_info = NULL;
525        sess->authinfo = NULL;
526        sess->emailinfo = NULL;
527        sess->oft_info = NULL;
528
529
530        /*
531         * Default to SNAC login unless XORLOGIN is explicitly set.
532         */
533        if (!(flags & AIM_SESS_FLAGS_XORLOGIN))
534                sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
535        sess->flags |= flags;
536
537        /*
538         * This must always be set.  Default to the queue-based
539         * version for back-compatibility. 
540         */
541        aim_tx_setenqueue(sess, AIM_TX_QUEUED, NULL);
542
543
544        /*
545         * Register all the modules for this session...
546         */
547        aim__registermodule(sess, misc_modfirst); /* load the catch-all first */
548        aim__registermodule(sess, general_modfirst);
549        aim__registermodule(sess, locate_modfirst);
550        aim__registermodule(sess, buddylist_modfirst);
551        aim__registermodule(sess, msg_modfirst);
552        aim__registermodule(sess, admin_modfirst);
553        aim__registermodule(sess, bos_modfirst);
554        aim__registermodule(sess, search_modfirst);
555        aim__registermodule(sess, stats_modfirst);
556        aim__registermodule(sess, chatnav_modfirst);
557        aim__registermodule(sess, chat_modfirst);
558        /* missing 0x0f - 0x12 */
559        aim__registermodule(sess, ssi_modfirst);
560        /* missing 0x14 */
561        aim__registermodule(sess, icq_modfirst);
562        /* missing 0x16 */
563        aim__registermodule(sess, auth_modfirst);
564
565        return;
566}
567
568/**
569 * aim_session_kill - Deallocate a session
570 * @sess: Session to kill
571 *
572 */
573void aim_session_kill(aim_session_t *sess)
574{
575        aim_cleansnacs(sess, -1);
576
577        aim_logoff(sess);
578
579        aim__shutdownmodules(sess);
580
581        return;
582}
583
584/*
585 * XXX this is nearly as ugly as proxyconnect().
586 */
587int aim_conn_completeconnect(aim_session_t *sess, aim_conn_t *conn)
588{
589        fd_set fds, wfds;
590        struct timeval tv;
591        int res, error = ETIMEDOUT;
592        aim_rxcallback_t userfunc;
593
594        if (!conn || (conn->fd == -1))
595                return -1;
596
597        if (!(conn->status & AIM_CONN_STATUS_INPROGRESS))
598                return -1;
599
600        FD_ZERO(&fds);
601        FD_SET(conn->fd, &fds);
602        FD_ZERO(&wfds);
603        FD_SET(conn->fd, &wfds);
604        tv.tv_sec = 0;
605        tv.tv_usec = 0;
606
607        if ((res = select(conn->fd+1, &fds, &wfds, NULL, &tv)) == -1) {
608                error = errno;
609                aim_conn_close(conn);
610                errno = error;
611                return -1;
612        } else if (res == 0) {
613                return 0; /* hasn't really completed yet... */
614        } 
615
616        if (FD_ISSET(conn->fd, &fds) || FD_ISSET(conn->fd, &wfds)) {
617                unsigned int len = sizeof(error);
618
619                if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
620                        error = errno;
621        }
622
623        if (error) {
624                aim_conn_close(conn);
625                errno = error;
626                return -1;
627        }
628
629        sock_make_blocking(conn->fd);
630
631        conn->status &= ~AIM_CONN_STATUS_INPROGRESS;
632
633        if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE)))
634                userfunc(sess, NULL, conn);
635
636        /* Flush out the queues if there was something waiting for this conn  */
637        aim_tx_flushqueue(sess);
638
639        return 0;
640}
641
642aim_session_t *aim_conn_getsess(aim_conn_t *conn)
643{
644
645        if (!conn)
646                return NULL;
647
648        return (aim_session_t *)conn->sessv;
649}
650
651/*
652 * aim_logoff()
653 *
654 * Closes -ALL- open connections.
655 *
656 */
657static int aim_logoff(aim_session_t *sess)
658{
659
660        aim_connrst(sess);  /* in case we want to connect again */
661
662        return 0;
663
664}
665
666/*
667 * aim_flap_nop()
668 *
669 * No-op.  WinAIM 4.x sends these _every minute_ to keep
670 * the connection alive. 
671 */
672int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn)
673{
674        aim_frame_t *fr;
675
676        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x05, 0)))
677                return -ENOMEM;
678
679        aim_tx_enqueue(sess, fr);
680
681        return 0;
682}
683
684
Note: See TracBrowser for help on using the repository browser.