source: protocols/oscar/conn.c @ 8813282

Last change on this file since 8813282 was a39ede7, checked in by dequis <dx@…>, at 2018-03-31T02:04:45Z

oscar: remove old_icq_auth (XOR login, default off since 2010)

Holy crap this login method is *bad*

  • Property mode set to 100644
File size: 14.0 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 guaranteed 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 definitely 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                proxy_disconnect(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
386        if (!(connstruct = aim_conn_getnext(sess))) {
387                return NULL;
388        }
389
390        connstruct->sessv = (void *) sess;
391        connstruct->type = type;
392
393        if (!dest) { /* just allocate a struct */
394                connstruct->fd = -1;
395                connstruct->status = 0;
396                return connstruct;
397        }
398
399        /* The code that used to be here was very broken */
400        g_return_val_if_reached(connstruct);
401
402        return connstruct;
403}
404
405/**
406 * aim_conn_setlatency - Set a forced latency value for connection
407 * @conn: Conn to set latency for
408 * @newval: Number of seconds to force between transmits
409 *
410 * Causes @newval seconds to be spent between transmits on a connection.
411 *
412 * This is my lame attempt at overcoming not understanding the rate
413 * limiting.
414 *
415 * XXX: This should really be replaced with something that scales and
416 * backs off like the real rate limiting does.
417 *
418 */
419int aim_conn_setlatency(aim_conn_t *conn, int newval)
420{
421
422        if (!conn) {
423                return -1;
424        }
425
426        conn->forcedlatency = newval;
427        conn->lastactivity = 0; /* reset this just to make sure */
428
429        return 0;
430}
431
432/**
433 * aim_session_init - Initializes a session structure
434 * @sess: Session to initialize
435 * @flags: Flags to use. Any of %AIM_SESS_FLAGS %OR'd together.
436 * @debuglevel: Level of debugging output (zero is least)
437 *
438 * Sets up the initial values for a session.
439 *
440 */
441void aim_session_init(aim_session_t *sess, guint32 flags, int debuglevel)
442{
443
444        if (!sess) {
445                return;
446        }
447
448        memset(sess, 0, sizeof(aim_session_t));
449        aim_connrst(sess);
450        sess->queue_outgoing = NULL;
451        sess->queue_incoming = NULL;
452        aim_initsnachash(sess);
453        sess->msgcookies = NULL;
454        sess->snacid_next = 0x00000001;
455
456        sess->flags = 0;
457
458        sess->modlistv = NULL;
459
460        sess->ssi.received_data = 0;
461        sess->ssi.waiting_for_ack = 0;
462        sess->ssi.holding_queue = NULL;
463        sess->ssi.revision = 0;
464        sess->ssi.items = NULL;
465        sess->ssi.timestamp = (time_t) 0;
466
467        sess->locate.userinfo = NULL;
468        sess->locate.torequest = NULL;
469        sess->locate.requested = NULL;
470        sess->locate.waiting_for_response = FALSE;
471
472        sess->icq_info = NULL;
473        sess->authinfo = NULL;
474        sess->emailinfo = NULL;
475        sess->oft_info = NULL;
476
477        sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
478        sess->flags |= flags;
479
480        /*
481         * This must always be set.  Default to the queue-based
482         * version for back-compatibility.
483         */
484        aim_tx_setenqueue(sess, AIM_TX_QUEUED, NULL);
485
486
487        /*
488         * Register all the modules for this session...
489         */
490        aim__registermodule(sess, misc_modfirst); /* load the catch-all first */
491        aim__registermodule(sess, general_modfirst);
492        aim__registermodule(sess, locate_modfirst);
493        aim__registermodule(sess, buddylist_modfirst);
494        aim__registermodule(sess, msg_modfirst);
495        aim__registermodule(sess, admin_modfirst);
496        aim__registermodule(sess, bos_modfirst);
497        aim__registermodule(sess, search_modfirst);
498        aim__registermodule(sess, stats_modfirst);
499        aim__registermodule(sess, chatnav_modfirst);
500        aim__registermodule(sess, chat_modfirst);
501        /* missing 0x0f - 0x12 */
502        aim__registermodule(sess, ssi_modfirst);
503        /* missing 0x14 */
504        aim__registermodule(sess, icq_modfirst);
505        /* missing 0x16 */
506        aim__registermodule(sess, auth_modfirst);
507
508        return;
509}
510
511/**
512 * aim_session_kill - Deallocate a session
513 * @sess: Session to kill
514 *
515 */
516void aim_session_kill(aim_session_t *sess)
517{
518        aim_cleansnacs(sess, -1);
519
520        aim_logoff(sess);
521
522        aim__shutdownmodules(sess);
523
524        return;
525}
526
527/*
528 * XXX this is nearly as ugly as proxyconnect().
529 */
530int aim_conn_completeconnect(aim_session_t *sess, aim_conn_t *conn)
531{
532        fd_set fds, wfds;
533        struct timeval tv;
534        int res, error = ETIMEDOUT;
535        aim_rxcallback_t userfunc;
536
537        if (!conn || (conn->fd == -1)) {
538                return -1;
539        }
540
541        if (!(conn->status & AIM_CONN_STATUS_INPROGRESS)) {
542                return -1;
543        }
544
545        FD_ZERO(&fds);
546        FD_SET(conn->fd, &fds);
547        FD_ZERO(&wfds);
548        FD_SET(conn->fd, &wfds);
549        tv.tv_sec = 0;
550        tv.tv_usec = 0;
551
552        if ((res = select(conn->fd + 1, &fds, &wfds, NULL, &tv)) == -1) {
553                error = errno;
554                aim_conn_close(conn);
555                errno = error;
556                return -1;
557        } else if (res == 0) {
558                return 0; /* hasn't really completed yet... */
559        }
560
561        if (FD_ISSET(conn->fd, &fds) || FD_ISSET(conn->fd, &wfds)) {
562                socklen_t len = sizeof(error);
563
564                if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
565                        error = errno;
566                }
567        }
568
569        if (error) {
570                aim_conn_close(conn);
571                errno = error;
572                return -1;
573        }
574
575        sock_make_blocking(conn->fd);
576
577        conn->status &= ~AIM_CONN_STATUS_INPROGRESS;
578
579        if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE))) {
580                userfunc(sess, NULL, conn);
581        }
582
583        /* Flush out the queues if there was something waiting for this conn  */
584        aim_tx_flushqueue(sess);
585
586        return 0;
587}
588
589aim_session_t *aim_conn_getsess(aim_conn_t *conn)
590{
591
592        if (!conn) {
593                return NULL;
594        }
595
596        return (aim_session_t *) conn->sessv;
597}
598
599/*
600 * aim_logoff()
601 *
602 * Closes -ALL- open connections.
603 *
604 */
605static int aim_logoff(aim_session_t *sess)
606{
607
608        aim_connrst(sess);  /* in case we want to connect again */
609
610        return 0;
611
612}
613
614/*
615 * aim_flap_nop()
616 *
617 * No-op.  WinAIM 4.x sends these _every minute_ to keep
618 * the connection alive.
619 */
620int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn)
621{
622        aim_frame_t *fr;
623
624        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x05, 0))) {
625                return -ENOMEM;
626        }
627
628        aim_tx_enqueue(sess, fr);
629
630        return 0;
631}
632
633
Note: See TracBrowser for help on using the repository browser.