source: protocols/oscar/conn.c @ 15c3a6b

Last change on this file since 15c3a6b was 5ebff60, checked in by dequis <dx@…>, at 2015-02-20T22:50:54Z

Reindent everything to K&R style with tabs

Used uncrustify, with the configuration file in ./doc/uncrustify.cfg

Commit author set to "Indent <please@…>" so that it's easier to
skip while doing git blame.

  • Property mode set to 100644
File size: 14.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
78        sg->group = group;
79
80        sg->next = ins->groups;
81        ins->groups = sg;
82
83        return;
84}
85
86aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, guint16 group)
87{
88        aim_conn_t *cur;
89
90        for (cur = sess->connlist; cur; cur = cur->next) {
91                aim_conn_inside_t *ins = (aim_conn_inside_t *) cur->inside;
92                struct snacgroup *sg;
93
94                for (sg = ins->groups; sg; sg = sg->next) {
95                        if (sg->group == group) {
96                                return cur;
97                        }
98                }
99        }
100
101        return NULL;
102}
103
104static void connkill_snacgroups(struct snacgroup **head)
105{
106        struct snacgroup *sg;
107
108        for (sg = *head; sg; ) {
109                struct snacgroup *tmp;
110
111                tmp = sg->next;
112                g_free(sg);
113                sg = tmp;
114        }
115
116        *head = NULL;
117
118        return;
119}
120
121static void connkill_rates(struct rateclass **head)
122{
123        struct rateclass *rc;
124
125        for (rc = *head; rc; ) {
126                struct rateclass *tmp;
127                struct snacpair *sp;
128
129                tmp = rc->next;
130
131                for (sp = rc->members; sp; ) {
132                        struct snacpair *tmpsp;
133
134                        tmpsp = sp->next;
135                        g_free(sp);
136                        sp = tmpsp;
137                }
138                g_free(rc);
139
140                rc = tmp;
141        }
142
143        *head = NULL;
144
145        return;
146}
147
148static void connkill_real(aim_session_t *sess, aim_conn_t **deadconn)
149{
150
151        aim_rxqueue_cleanbyconn(sess, *deadconn);
152        aim_tx_cleanqueue(sess, *deadconn);
153
154        if ((*deadconn)->fd != -1) {
155                aim_conn_close(*deadconn);
156        }
157
158        /*
159         * XXX ->priv should never be touched by the library. I know
160         * it used to be, but I'm getting rid of all that.  Use
161         * ->internal instead.
162         */
163        if ((*deadconn)->priv) {
164                g_free((*deadconn)->priv);
165        }
166
167        /*
168         * This will free ->internal if it necessary...
169         */
170        if ((*deadconn)->type == AIM_CONN_TYPE_CHAT) {
171                aim_conn_kill_chat(sess, *deadconn);
172        }
173
174        if ((*deadconn)->inside) {
175                aim_conn_inside_t *inside = (aim_conn_inside_t *) (*deadconn)->inside;
176
177                connkill_snacgroups(&inside->groups);
178                connkill_rates(&inside->rates);
179
180                g_free(inside);
181        }
182
183        g_free(*deadconn);
184        *deadconn = NULL;
185
186        return;
187}
188
189/**
190 * aim_connrst - Clears out connection list, killing remaining connections.
191 * @sess: Session to be cleared
192 *
193 * Clears out the connection list and kills any connections left.
194 *
195 */
196static void aim_connrst(aim_session_t *sess)
197{
198
199        if (sess->connlist) {
200                aim_conn_t *cur = sess->connlist, *tmp;
201
202                while (cur) {
203                        tmp = cur->next;
204                        aim_conn_close(cur);
205                        connkill_real(sess, &cur);
206                        cur = tmp;
207                }
208        }
209
210        sess->connlist = NULL;
211
212        return;
213}
214
215/**
216 * aim_conn_init - Reset a connection to default values.
217 * @deadconn: Connection to be reset
218 *
219 * Initializes and/or resets a connection structure.
220 *
221 */
222static void aim_conn_init(aim_conn_t *deadconn)
223{
224
225        if (!deadconn) {
226                return;
227        }
228
229        deadconn->fd = -1;
230        deadconn->subtype = -1;
231        deadconn->type = -1;
232        deadconn->seqnum = 0;
233        deadconn->lastactivity = 0;
234        deadconn->forcedlatency = 0;
235        deadconn->handlerlist = NULL;
236        deadconn->priv = NULL;
237        memset(deadconn->inside, 0, sizeof(aim_conn_inside_t));
238
239        return;
240}
241
242/**
243 * aim_conn_getnext - Gets a new connection structure.
244 * @sess: Session
245 *
246 * Allocate a new empty connection structure.
247 *
248 */
249static aim_conn_t *aim_conn_getnext(aim_session_t *sess)
250{
251        aim_conn_t *newconn;
252
253        if (!(newconn = g_new0(aim_conn_t, 1))) {
254                return NULL;
255        }
256
257        if (!(newconn->inside = g_new0(aim_conn_inside_t, 1))) {
258                g_free(newconn);
259                return NULL;
260        }
261
262        aim_conn_init(newconn);
263
264        newconn->next = sess->connlist;
265        sess->connlist = newconn;
266
267        return newconn;
268}
269
270/**
271 * aim_conn_kill - Close and free a connection.
272 * @sess: Session for the connection
273 * @deadconn: Connection to be freed
274 *
275 * Close, clear, and free a connection structure. Should never be
276 * called from within libfaim.
277 *
278 */
279void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn)
280{
281        aim_conn_t *cur, **prev;
282
283        if (!deadconn || !*deadconn) {
284                return;
285        }
286
287        for (prev = &sess->connlist; (cur = *prev); ) {
288                if (cur == *deadconn) {
289                        *prev = cur->next;
290                        break;
291                }
292                prev = &cur->next;
293        }
294
295        if (!cur) {
296                return; /* oops */
297
298        }
299        connkill_real(sess, &cur);
300
301        return;
302}
303
304/**
305 * aim_conn_close - Close a connection
306 * @deadconn: Connection to close
307 *
308 * Close (but not free) a connection.
309 *
310 * This leaves everything untouched except for clearing the
311 * handler list and setting the fd to -1 (used to recognize
312 * dead connections).  It will also remove cookies if necessary.
313 *
314 */
315void aim_conn_close(aim_conn_t *deadconn)
316{
317
318        if (deadconn->fd >= 3) {
319                closesocket(deadconn->fd);
320        }
321        deadconn->fd = -1;
322        if (deadconn->handlerlist) {
323                aim_clearhandlers(deadconn);
324        }
325
326        return;
327}
328
329/**
330 * aim_getconn_type - Find a connection of a specific type
331 * @sess: Session to search
332 * @type: Type of connection to look for
333 *
334 * Searches for a connection of the specified type in the
335 * specified session.  Returns the first connection of that
336 * type found.
337 *
338 * XXX except for RENDEZVOUS, all uses of this should be removed and
339 * use aim_conn_findbygroup() instead.
340 */
341aim_conn_t *aim_getconn_type(aim_session_t *sess, int type)
342{
343        aim_conn_t *cur;
344
345        for (cur = sess->connlist; cur; cur = cur->next) {
346                if ((cur->type == type) &&
347                    !(cur->status & AIM_CONN_STATUS_INPROGRESS)) {
348                        break;
349                }
350        }
351
352        return cur;
353}
354
355aim_conn_t *aim_getconn_type_all(aim_session_t *sess, int type)
356{
357        aim_conn_t *cur;
358
359        for (cur = sess->connlist; cur; cur = cur->next) {
360                if (cur->type == type) {
361                        break;
362                }
363        }
364
365        return cur;
366}
367
368/**
369 * aim_newconn - Open a new connection
370 * @sess: Session to create connection in
371 * @type: Type of connection to create
372 * @dest: Host to connect to (in "host:port" syntax)
373 *
374 * Opens a new connection to the specified dest host of specified
375 * type, using the proxy settings if available.  If @host is %NULL,
376 * the connection is allocated and returned, but no connection
377 * is made.
378 *
379 * FIXME: Return errors in a more sane way.
380 *
381 */
382aim_conn_t *aim_newconn(aim_session_t *sess, int type, const char *dest)
383{
384        aim_conn_t *connstruct;
385        guint16 port = AIM_LOGIN_PORT;
386        char *host;
387        int i;
388
389        if (!(connstruct = aim_conn_getnext(sess))) {
390                return NULL;
391        }
392
393        connstruct->sessv = (void *) sess;
394        connstruct->type = type;
395
396        if (!dest) { /* just allocate a struct */
397                connstruct->fd = -1;
398                connstruct->status = 0;
399                return connstruct;
400        }
401
402        /*
403         * As of 23 Jul 1999, AOL now sends the port number, preceded by a
404         * colon, in the BOS redirect.  This fatally breaks all previous
405         * libfaims.  Bad, bad AOL.
406         *
407         * We put this here to catch every case.
408         *
409         */
410
411        for (i = 0; i < (int) strlen(dest); i++) {
412                if (dest[i] == ':') {
413                        port = atoi(&(dest[i + 1]));
414                        break;
415                }
416        }
417
418        host = (char *) g_malloc(i + 1);
419        strncpy(host, dest, i);
420        host[i] = '\0';
421
422        connstruct->fd = proxy_connect(host, port, NULL, NULL);
423
424        g_free(host);
425
426        return connstruct;
427}
428
429/**
430 * aim_conn_setlatency - Set a forced latency value for connection
431 * @conn: Conn to set latency for
432 * @newval: Number of seconds to force between transmits
433 *
434 * Causes @newval seconds to be spent between transmits on a connection.
435 *
436 * This is my lame attempt at overcoming not understanding the rate
437 * limiting.
438 *
439 * XXX: This should really be replaced with something that scales and
440 * backs off like the real rate limiting does.
441 *
442 */
443int aim_conn_setlatency(aim_conn_t *conn, int newval)
444{
445
446        if (!conn) {
447                return -1;
448        }
449
450        conn->forcedlatency = newval;
451        conn->lastactivity = 0; /* reset this just to make sure */
452
453        return 0;
454}
455
456/**
457 * aim_session_init - Initializes a session structure
458 * @sess: Session to initialize
459 * @flags: Flags to use. Any of %AIM_SESS_FLAGS %OR'd together.
460 * @debuglevel: Level of debugging output (zero is least)
461 *
462 * Sets up the initial values for a session.
463 *
464 */
465void aim_session_init(aim_session_t *sess, guint32 flags, int debuglevel)
466{
467
468        if (!sess) {
469                return;
470        }
471
472        memset(sess, 0, sizeof(aim_session_t));
473        aim_connrst(sess);
474        sess->queue_outgoing = NULL;
475        sess->queue_incoming = NULL;
476        aim_initsnachash(sess);
477        sess->msgcookies = NULL;
478        sess->snacid_next = 0x00000001;
479
480        sess->flags = 0;
481
482        sess->modlistv = NULL;
483
484        sess->ssi.received_data = 0;
485        sess->ssi.waiting_for_ack = 0;
486        sess->ssi.holding_queue = NULL;
487        sess->ssi.revision = 0;
488        sess->ssi.items = NULL;
489        sess->ssi.timestamp = (time_t) 0;
490
491        sess->locate.userinfo = NULL;
492        sess->locate.torequest = NULL;
493        sess->locate.requested = NULL;
494        sess->locate.waiting_for_response = FALSE;
495
496        sess->icq_info = NULL;
497        sess->authinfo = NULL;
498        sess->emailinfo = NULL;
499        sess->oft_info = NULL;
500
501
502        /*
503         * Default to SNAC login unless XORLOGIN is explicitly set.
504         */
505        if (!(flags & AIM_SESS_FLAGS_XORLOGIN)) {
506                sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
507        }
508        sess->flags |= flags;
509
510        /*
511         * This must always be set.  Default to the queue-based
512         * version for back-compatibility.
513         */
514        aim_tx_setenqueue(sess, AIM_TX_QUEUED, NULL);
515
516
517        /*
518         * Register all the modules for this session...
519         */
520        aim__registermodule(sess, misc_modfirst); /* load the catch-all first */
521        aim__registermodule(sess, general_modfirst);
522        aim__registermodule(sess, locate_modfirst);
523        aim__registermodule(sess, buddylist_modfirst);
524        aim__registermodule(sess, msg_modfirst);
525        aim__registermodule(sess, admin_modfirst);
526        aim__registermodule(sess, bos_modfirst);
527        aim__registermodule(sess, search_modfirst);
528        aim__registermodule(sess, stats_modfirst);
529        aim__registermodule(sess, chatnav_modfirst);
530        aim__registermodule(sess, chat_modfirst);
531        /* missing 0x0f - 0x12 */
532        aim__registermodule(sess, ssi_modfirst);
533        /* missing 0x14 */
534        aim__registermodule(sess, icq_modfirst);
535        /* missing 0x16 */
536        aim__registermodule(sess, auth_modfirst);
537
538        return;
539}
540
541/**
542 * aim_session_kill - Deallocate a session
543 * @sess: Session to kill
544 *
545 */
546void aim_session_kill(aim_session_t *sess)
547{
548        aim_cleansnacs(sess, -1);
549
550        aim_logoff(sess);
551
552        aim__shutdownmodules(sess);
553
554        return;
555}
556
557/*
558 * XXX this is nearly as ugly as proxyconnect().
559 */
560int aim_conn_completeconnect(aim_session_t *sess, aim_conn_t *conn)
561{
562        fd_set fds, wfds;
563        struct timeval tv;
564        int res, error = ETIMEDOUT;
565        aim_rxcallback_t userfunc;
566
567        if (!conn || (conn->fd == -1)) {
568                return -1;
569        }
570
571        if (!(conn->status & AIM_CONN_STATUS_INPROGRESS)) {
572                return -1;
573        }
574
575        FD_ZERO(&fds);
576        FD_SET(conn->fd, &fds);
577        FD_ZERO(&wfds);
578        FD_SET(conn->fd, &wfds);
579        tv.tv_sec = 0;
580        tv.tv_usec = 0;
581
582        if ((res = select(conn->fd + 1, &fds, &wfds, NULL, &tv)) == -1) {
583                error = errno;
584                aim_conn_close(conn);
585                errno = error;
586                return -1;
587        } else if (res == 0) {
588                return 0; /* hasn't really completed yet... */
589        }
590
591        if (FD_ISSET(conn->fd, &fds) || FD_ISSET(conn->fd, &wfds)) {
592                socklen_t len = sizeof(error);
593
594                if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
595                        error = errno;
596                }
597        }
598
599        if (error) {
600                aim_conn_close(conn);
601                errno = error;
602                return -1;
603        }
604
605        sock_make_blocking(conn->fd);
606
607        conn->status &= ~AIM_CONN_STATUS_INPROGRESS;
608
609        if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE))) {
610                userfunc(sess, NULL, conn);
611        }
612
613        /* Flush out the queues if there was something waiting for this conn  */
614        aim_tx_flushqueue(sess);
615
616        return 0;
617}
618
619aim_session_t *aim_conn_getsess(aim_conn_t *conn)
620{
621
622        if (!conn) {
623                return NULL;
624        }
625
626        return (aim_session_t *) conn->sessv;
627}
628
629/*
630 * aim_logoff()
631 *
632 * Closes -ALL- open connections.
633 *
634 */
635static int aim_logoff(aim_session_t *sess)
636{
637
638        aim_connrst(sess);  /* in case we want to connect again */
639
640        return 0;
641
642}
643
644/*
645 * aim_flap_nop()
646 *
647 * No-op.  WinAIM 4.x sends these _every minute_ to keep
648 * the connection alive.
649 */
650int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn)
651{
652        aim_frame_t *fr;
653
654        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x05, 0))) {
655                return -ENOMEM;
656        }
657
658        aim_tx_enqueue(sess, fr);
659
660        return 0;
661}
662
663
Note: See TracBrowser for help on using the repository browser.