X-Git-Url: http://andersk.mit.edu/gitweb/libfaim.git/blobdiff_plain/d410cf58c1b99101922ced3773b50c72e1dbfe5d..031c2fb3fd6893e2bd7cda737ad2bdfa783e00c9:/src/conn.c diff --git a/src/conn.c b/src/conn.c index 0c61686..7744e50 100644 --- a/src/conn.c +++ b/src/conn.c @@ -1,6 +1,6 @@ /* - * aim_conn.c + * conn.c * * Does all this gloriously nifty connection handling stuff... * @@ -15,6 +15,146 @@ #include #endif +/* + * In OSCAR, every connection has a set of SNAC groups associated + * with it. These are the groups that you can send over this connection + * without being guarenteed a "Not supported" SNAC error. + * + * The grand theory of things says that these associations transcend + * what libfaim calls "connection types" (conn->type). You can probably + * see the elegance here, but since I want to revel in it for a bit, you + * get to hear it all spelled out. + * + * So let us say that you have your core BOS connection running. One + * of your modules has just given you a SNAC of the group 0x0004 to send + * you. Maybe an IM destined for some twit in Greenland. So you start + * at the top of your connection list, looking for a connection that + * claims to support group 0x0004. You find one. Why, that neat BOS + * connection of yours can do that. So you send it on its way. + * + * Now, say, that fellow from Greenland has friends and they all want to + * meet up with you in a lame chat room. This has landed you a SNAC + * in the family 0x000e and you have to admit you're a bit lost. You've + * searched your connection list for someone who wants to make your life + * easy and deliver this SNAC for you, but there isn't one there. + * + * Here comes the good bit. Without even letting anyone know, particularly + * the module that decided to send this SNAC, and definitly not that twit + * in Greenland, you send out a service request. In this request, you have + * marked the need for a connection supporting group 0x000e. A few seconds + * later, you receive a service redirect with an IP address and a cookie in + * it. Great, you say. Now I have something to do. Off you go, making + * that connection. One of the first things you get from this new server + * is a message saying that indeed it does support the group you were looking + * for. So you continue and send rate confirmation and all that. + * + * Then you remember you had that SNAC to send, and now you have a means to + * do it, and you do, and everyone is happy. Except the Greenlander, who is + * still stuck in the bitter cold. + * + * Oh, and this is useful for building the Migration SNACs, too. In the + * future, this may help convince me to implement rate limit mitigation + * for real. We'll see. + * + * Just to make me look better, I'll say that I've known about this great + * scheme for quite some time now. But I still haven't convinced myself + * to make libfaim work that way. It would take a fair amount of effort, + * and probably some client API changes as well. (Whenever I don't want + * to do something, I just say it would change the client API. Then I + * instantly have a couple of supporters of not doing it.) + * + * Generally, addgroup is only called by the internal handling of the + * server ready SNAC. So if you want to do something before that, you'll + * have to be more creative. That is done rather early, though, so I don't + * think you have to worry about it. Unless you're me. I care deeply + * about such inane things. + * + */ +faim_internal void aim_conn_addgroup(aim_conn_t *conn, fu16_t group) +{ + aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; + struct snacgroup *sg; + + if (!(sg = malloc(sizeof(struct snacgroup)))) + return; + + faimdprintf(aim_conn_getsess(conn), 1, "adding group 0x%04x\n", group); + sg->group = group; + + sg->next = ins->groups; + ins->groups = sg; + + return; +} + +faim_export aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, fu16_t group) +{ + aim_conn_t *cur; + + for (cur = sess->connlist; cur; cur = cur->next) { + aim_conn_inside_t *ins = (aim_conn_inside_t *)cur->inside; + struct snacgroup *sg; + + for (sg = ins->groups; sg; sg = sg->next) { + if (sg->group == group) + return cur; + } + } + + return NULL; +} + +static struct snacgroup *connkill_snacgroups(struct snacgroup *sg) +{ + + while (sg) { + struct snacgroup *tmp; + + tmp = sg->next; + free(sg); + sg = tmp; + } + + return NULL; +} + +static void connkill_real(aim_session_t *sess, aim_conn_t **deadconn) +{ + + aim_rxqueue_cleanbyconn(sess, *deadconn); + aim_tx_cleanqueue(sess, *deadconn); + + if ((*deadconn)->fd != -1) + aim_conn_close(*deadconn); + + /* + * XXX ->priv should never be touched by the library. I know + * it used to be, but I'm getting rid of all that. Use + * ->internal instead. + */ + if ((*deadconn)->priv) + free((*deadconn)->priv); + + /* + * This will free ->internal if it necessary... + */ + if ((*deadconn)->type == AIM_CONN_TYPE_RENDEZVOUS) + aim_conn_kill_rend(sess, *deadconn); + + if ((*deadconn)->inside) { + aim_conn_inside_t *inside = (aim_conn_inside_t *)(*deadconn)->inside; + + inside->groups = connkill_snacgroups(inside->groups); + + free(inside); + } + + free(*deadconn); + deadconn = NULL; + + return; +} + /** * aim_connrst - Clears out connection list, killing remaining connections. * @sess: Session to be cleared @@ -25,15 +165,13 @@ static void aim_connrst(aim_session_t *sess) { - faim_mutex_init(&sess->connlistlock); - if (sess->connlist) { aim_conn_t *cur = sess->connlist, *tmp; while (cur) { tmp = cur->next; aim_conn_close(cur); - free(cur); + connkill_real(sess, &cur); cur = tmp; } } @@ -64,8 +202,7 @@ static void aim_conn_init(aim_conn_t *deadconn) deadconn->forcedlatency = 0; deadconn->handlerlist = NULL; deadconn->priv = NULL; - faim_mutex_init(&deadconn->active); - faim_mutex_init(&deadconn->seqnum_lock); + memset(deadconn->inside, 0, sizeof(aim_conn_inside_t)); return; } @@ -79,24 +216,22 @@ static void aim_conn_init(aim_conn_t *deadconn) */ static aim_conn_t *aim_conn_getnext(aim_session_t *sess) { - aim_conn_t *newconn, *cur; + aim_conn_t *newconn; if (!(newconn = malloc(sizeof(aim_conn_t)))) return NULL; memset(newconn, 0, sizeof(aim_conn_t)); - aim_conn_init(newconn); - newconn->next = NULL; - - faim_mutex_lock(&sess->connlistlock); - if (!sess->connlist) - sess->connlist = newconn; - else { - for (cur = sess->connlist; cur->next; cur = cur->next) - ; - cur->next = newconn; + if (!(newconn->inside = malloc(sizeof(aim_conn_inside_t)))) { + free(newconn); + return NULL; } - faim_mutex_unlock(&sess->connlistlock); + memset(newconn->inside, 0, sizeof(aim_conn_inside_t)); + + aim_conn_init(newconn); + + newconn->next = sess->connlist; + sess->connlist = newconn; return newconn; } @@ -112,40 +247,23 @@ static aim_conn_t *aim_conn_getnext(aim_session_t *sess) */ faim_export void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn) { - aim_conn_t *cur; + aim_conn_t *cur, **prev; if (!deadconn || !*deadconn) return; - aim_tx_cleanqueue(sess, *deadconn); - - faim_mutex_lock(&sess->connlistlock); - if (sess->connlist == NULL) - ; - else if (sess->connlist->next == NULL) { - if (sess->connlist == *deadconn) - sess->connlist = NULL; - } else { - cur = sess->connlist; - while (cur->next) { - if (cur->next == *deadconn) { - cur->next = cur->next->next; - break; - } - cur = cur->next; + for (prev = &sess->connlist; (cur = *prev); ) { + if (cur == *deadconn) { + *prev = cur->next; + break; } + prev = &cur->next; } - faim_mutex_unlock(&sess->connlistlock); - /* XXX: do we need this for txqueue too? */ - aim_rxqueue_cleanbyconn(sess, *deadconn); + if (!cur) + return; /* oops */ - if ((*deadconn)->fd != -1) - aim_conn_close(*deadconn); - if ((*deadconn)->priv) - free((*deadconn)->priv); - free(*deadconn); - deadconn = NULL; + connkill_real(sess, &cur); return; } @@ -158,19 +276,19 @@ faim_export void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn) * * This leaves everything untouched except for clearing the * handler list and setting the fd to -1 (used to recognize - * dead connections). + * dead connections). It will also remove cookies if necessary. * */ faim_export void aim_conn_close(aim_conn_t *deadconn) { - faim_mutex_destroy(&deadconn->active); - faim_mutex_destroy(&deadconn->seqnum_lock); if (deadconn->fd >= 3) close(deadconn->fd); deadconn->fd = -1; if (deadconn->handlerlist) aim_clearhandlers(deadconn); + if (deadconn->type == AIM_CONN_TYPE_RENDEZVOUS) + aim_conn_close_rend((aim_session_t *)deadconn->sessv, deadconn); return; } @@ -184,18 +302,18 @@ faim_export void aim_conn_close(aim_conn_t *deadconn) * specified session. Returns the first connection of that * type found. * + * XXX except for RENDEZVOUS, all uses of this should be removed and + * use aim_conn_findbygroup() instead. */ faim_export aim_conn_t *aim_getconn_type(aim_session_t *sess, int type) { aim_conn_t *cur; - faim_mutex_lock(&sess->connlistlock); for (cur = sess->connlist; cur; cur = cur->next) { if ((cur->type == type) && !(cur->status & AIM_CONN_STATUS_INPROGRESS)) break; } - faim_mutex_unlock(&sess->connlistlock); return cur; } @@ -204,12 +322,10 @@ faim_export aim_conn_t *aim_getconn_type_all(aim_session_t *sess, int type) { aim_conn_t *cur; - faim_mutex_lock(&sess->connlistlock); for (cur = sess->connlist; cur; cur = cur->next) { if (cur->type == type) break; } - faim_mutex_unlock(&sess->connlistlock); return cur; } @@ -219,12 +335,10 @@ faim_export aim_conn_t *aim_getconn_fd(aim_session_t *sess, int fd) { aim_conn_t *cur; - faim_mutex_lock(&sess->connlistlock); for (cur = sess->connlist; cur; cur = cur->next) { if (cur->fd == fd) break; } - faim_mutex_unlock(&sess->connlistlock); return cur; } @@ -418,19 +532,26 @@ faim_internal aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src) if (!(conn = aim_conn_getnext(sess))) return NULL; - faim_mutex_lock(&conn->active); - conn->fd = src->fd; conn->type = src->type; conn->subtype = src->subtype; conn->seqnum = src->seqnum; conn->priv = src->priv; + conn->internal = src->internal; conn->lastactivity = src->lastactivity; conn->forcedlatency = src->forcedlatency; conn->sessv = src->sessv; aim_clonehandlers(sess, conn, src); - faim_mutex_unlock(&conn->active); + if (src->inside) { + /* + * XXX should clone this section as well, but since currently + * this function only gets called for some of that rendezvous + * crap, and not on SNAC connections, its probably okay for + * now. + * + */ + } return conn; } @@ -459,15 +580,12 @@ faim_export aim_conn_t *aim_newconn(aim_session_t *sess, int type, const char *d if (!(connstruct = aim_conn_getnext(sess))) return NULL; - faim_mutex_lock(&connstruct->active); - connstruct->sessv = (void *)sess; connstruct->type = type; if (!dest) { /* just allocate a struct */ connstruct->fd = -1; connstruct->status = 0; - faim_mutex_unlock(&connstruct->active); return connstruct; } @@ -495,13 +613,10 @@ faim_export aim_conn_t *aim_newconn(aim_session_t *sess, int type, const char *d connstruct->fd = -1; connstruct->status = (errno | AIM_CONN_STATUS_CONNERR); free(host); - faim_mutex_unlock(&connstruct->active); return connstruct; } else connstruct->fd = ret; - faim_mutex_unlock(&connstruct->active); - free(host); return connstruct; @@ -520,12 +635,10 @@ faim_export int aim_conngetmaxfd(aim_session_t *sess) int j; aim_conn_t *cur; - faim_mutex_lock(&sess->connlistlock); for (cur = sess->connlist, j = 0; cur; cur = cur->next) { if (cur->fd > j) j = cur->fd; } - faim_mutex_unlock(&sess->connlistlock); return j; } @@ -543,14 +656,10 @@ faim_export int aim_conn_in_sess(aim_session_t *sess, aim_conn_t *conn) { aim_conn_t *cur; - faim_mutex_lock(&sess->connlistlock); for (cur = sess->connlist; cur; cur = cur->next) { - if (cur == conn) { - faim_mutex_unlock(&sess->connlistlock); + if (cur == conn) return 1; - } } - faim_mutex_unlock(&sess->connlistlock); return 0; } @@ -570,8 +679,6 @@ faim_export int aim_conn_in_sess(aim_session_t *sess, aim_conn_t *conn) * 1 outgoing data pending (%NULL returned) * 2 incoming data pending (connection with pending data returned) * - * XXX: we could probably stand to do a little courser locking here. - * */ faim_export aim_conn_t *aim_select(aim_session_t *sess, struct timeval *timeout, int *status) { @@ -579,23 +686,18 @@ faim_export aim_conn_t *aim_select(aim_session_t *sess, struct timeval *timeout, fd_set fds, wfds; int maxfd, i, haveconnecting = 0; - faim_mutex_lock(&sess->connlistlock); if (!sess->connlist) { - faim_mutex_unlock(&sess->connlistlock); *status = -1; return NULL; } - faim_mutex_unlock(&sess->connlistlock); FD_ZERO(&fds); FD_ZERO(&wfds); - faim_mutex_lock(&sess->connlistlock); for (cur = sess->connlist, maxfd = 0; cur; cur = cur->next) { if (cur->fd == -1) { /* don't let invalid/dead connections sit around */ *status = 2; - faim_mutex_unlock(&sess->connlistlock); return cur; } else if (cur->status & AIM_CONN_STATUS_INPROGRESS) { FD_SET(cur->fd, &wfds); @@ -606,7 +708,6 @@ faim_export aim_conn_t *aim_select(aim_session_t *sess, struct timeval *timeout, if (cur->fd > maxfd) maxfd = cur->fd; } - faim_mutex_unlock(&sess->connlistlock); /* * If we have data waiting to be sent, return @@ -628,14 +729,12 @@ faim_export aim_conn_t *aim_select(aim_session_t *sess, struct timeval *timeout, } if ((i = select(maxfd+1, &fds, &wfds, NULL, timeout))>=1) { - faim_mutex_lock(&sess->connlistlock); for (cur = sess->connlist; cur; cur = cur->next) { if ((FD_ISSET(cur->fd, &fds)) || ((cur->status & AIM_CONN_STATUS_INPROGRESS) && FD_ISSET(cur->fd, &wfds))) { *status = 2; - faim_mutex_unlock(&sess->connlistlock); - return cur; /* XXX race condition here -- shouldnt unlock connlist */ + return cur; } } *status = 0; /* shouldn't happen */ @@ -644,8 +743,6 @@ faim_export aim_conn_t *aim_select(aim_session_t *sess, struct timeval *timeout, else *status = i; /* can be 0 or -1 */ - faim_mutex_unlock(&sess->connlistlock); - return NULL; /* no waiting or error, return */ } @@ -669,10 +766,8 @@ faim_export int aim_conn_setlatency(aim_conn_t *conn, int newval) if (!conn) return -1; - faim_mutex_lock(&conn->active); conn->forcedlatency = newval; conn->lastactivity = 0; /* reset this just to make sure */ - faim_mutex_unlock(&conn->active); return 0; }