X-Git-Url: http://andersk.mit.edu/gitweb/libfaim.git/blobdiff_plain/be67fdd00ab2bb00bc6c7e5be8660909662d36e2..89bce17768d14f77b0a3fa612d56372f3b6ded5c:/aim_conn.c diff --git a/aim_conn.c b/aim_conn.c index 5009627..836c0d4 100644 --- a/aim_conn.c +++ b/aim_conn.c @@ -38,6 +38,32 @@ faim_internal void aim_connrst(struct aim_session_t *sess) return; } +/** + * aim_conn_init - Reset a connection to default values. + * @deadconn: Connection to be reset + * + * Initializes and/or resets a connection structure. + * + */ +static void aim_conn_init(struct aim_conn_t *deadconn) +{ + if (!deadconn) + return; + + deadconn->fd = -1; + deadconn->subtype = -1; + deadconn->type = -1; + deadconn->seqnum = 0; + deadconn->lastactivity = 0; + deadconn->forcedlatency = 0; + deadconn->handlerlist = NULL; + deadconn->priv = NULL; + faim_mutex_init(&deadconn->active); + faim_mutex_init(&deadconn->seqnum_lock); + + return; +} + /** * aim_conn_getnext - Gets a new connection structure. * @sess: Session @@ -53,7 +79,7 @@ faim_internal struct aim_conn_t *aim_conn_getnext(struct aim_session_t *sess) return NULL; memset(newconn, 0, sizeof(struct aim_conn_t)); - aim_conn_close(newconn); + aim_conn_init(newconn); newconn->next = NULL; faim_mutex_lock(&sess->connlistlock); @@ -69,32 +95,6 @@ faim_internal struct aim_conn_t *aim_conn_getnext(struct aim_session_t *sess) return newconn; } -/** - * aim_conn_init - Reset a connection to default values. - * @deadconn: Connection to be reset - * - * Initializes and/or resets a connection structure. - * - */ -static void aim_conn_init(struct aim_conn_t *deadconn) -{ - if (!deadconn) - return; - - deadconn->fd = -1; - deadconn->subtype = -1; - deadconn->type = -1; - deadconn->seqnum = 0; - deadconn->lastactivity = 0; - deadconn->forcedlatency = 0; - deadconn->handlerlist = NULL; - deadconn->priv = NULL; - faim_mutex_init(&deadconn->active); - faim_mutex_init(&deadconn->seqnum_lock); - - return; -} - /** * aim_conn_kill - Close and free a connection. * @sess: Session for the connection @@ -131,7 +131,8 @@ faim_export void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **d /* XXX: do we need this for txqueue too? */ aim_rxqueue_cleanbyconn(sess, *deadconn); - aim_conn_close(*deadconn); + if ((*deadconn)->fd != -1) + aim_conn_close(*deadconn); if ((*deadconn)->priv) free((*deadconn)->priv); free(*deadconn); @@ -146,34 +147,22 @@ faim_export void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **d * * Close (but not free) a connection. * + * This leaves everything untouched except for clearing the + * handler list and setting the fd to -1 (used to recognize + * dead connections). + * */ faim_export void aim_conn_close(struct aim_conn_t *deadconn) { - int typesav = -1, subtypesav = -1; - void *privsav = NULL; 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); - typesav = deadconn->type; - subtypesav = deadconn->subtype; - - if (deadconn->priv && (deadconn->type != AIM_CONN_TYPE_RENDEZVOUS)) { - free(deadconn->priv); - deadconn->priv = NULL; - } - privsav = deadconn->priv; - - aim_conn_init(deadconn); - - deadconn->type = typesav; - deadconn->subtype = subtypesav; - deadconn->priv = privsav; - return; } @@ -194,7 +183,7 @@ faim_internal struct aim_conn_t *aim_getconn_type(struct aim_session_t *sess, faim_mutex_lock(&sess->connlistlock); for (cur = sess->connlist; cur; cur = cur->next) { - if (cur->type == type) + if ((cur->type == type) && !(cur->status & AIM_CONN_STATUS_INPROGRESS)) break; } faim_mutex_unlock(&sess->connlistlock); @@ -344,13 +333,24 @@ static int aim_proxyconnect(struct aim_session_t *sess, return -1; } - memset(&sa.sin_zero, 0, 8); + memset(&sa, 0, sizeof(struct sockaddr_in)); sa.sin_port = htons(port); memcpy(&sa.sin_addr, hp->h_addr, hp->h_length); sa.sin_family = hp->h_addrtype; fd = socket(hp->h_addrtype, SOCK_STREAM, 0); + + if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT) + fcntl(fd, F_SETFL, O_NONBLOCK); /* XXX save flags */ + if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) { + if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT) { + if ((errno == EINPROGRESS) || (errno == EINTR)) { + if (statusret) + *statusret |= AIM_CONN_STATUS_INPROGRESS; + return fd; + } + } close(fd); fd = -1; } @@ -518,9 +518,9 @@ faim_export struct aim_conn_t *aim_select(struct aim_session_t *sess, struct timeval *timeout, int *status) { struct aim_conn_t *cur; - fd_set fds; + fd_set fds, wfds; int maxfd = 0; - int i; + int i, haveconnecting = 0; faim_mutex_lock(&sess->connlistlock); if (sess->connlist == NULL) { @@ -530,32 +530,55 @@ faim_export struct aim_conn_t *aim_select(struct aim_session_t *sess, } faim_mutex_unlock(&sess->connlistlock); - /* - * If we have data waiting to be sent, return immediatly - */ - if (sess->queue_outgoing != NULL) { - *status = 1; - return NULL; - } - FD_ZERO(&fds); + FD_ZERO(&wfds); maxfd = 0; faim_mutex_lock(&sess->connlistlock); for (cur = sess->connlist; 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); + haveconnecting++; + } FD_SET(cur->fd, &fds); if (cur->fd > maxfd) maxfd = cur->fd; } faim_mutex_unlock(&sess->connlistlock); - if ((i = select(maxfd+1, &fds, NULL, NULL, timeout))>=1) { + /* + * If we have data waiting to be sent, return + * + * We have to not do this if theres at least one + * connection thats still connecting, since that connection + * may have queued data and this return would prevent + * the connection from ever completing! This is a major + * inadequacy of the libfaim way of doing things. It means + * that nothing can transmit as long as there's connecting + * sockets. Evil. + * + * But its still better than having blocking connects. + * + */ + if (!haveconnecting && (sess->queue_outgoing != NULL)) { + *status = 1; + return NULL; + } + + 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)) { + 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; + return cur; /* XXX race condition here -- shouldnt unlock connlist */ } } *status = 0; /* shouldn't happen */ @@ -593,6 +616,8 @@ faim_export int aim_conn_isready(struct aim_conn_t *conn) * @newstatus is %XOR'd with the previous value of the connection * status and returned. Returns -1 if the connection is invalid. * + * This isn't real useful. + * */ faim_export int aim_conn_setstatus(struct aim_conn_t *conn, int status) { @@ -669,11 +694,12 @@ faim_export void aim_setupproxy(struct aim_session_t *sess, char *server, char * /** * aim_session_init - Initializes a session structure * @sess: Session to initialize + * @flags: Flags to use. Any of %AIM_SESS_FLAGS %OR'd together. * * Sets up the initial values for a session. * */ -faim_export void aim_session_init(struct aim_session_t *sess) +faim_export void aim_session_init(struct aim_session_t *sess, unsigned long flags) { if (!sess) return; @@ -683,9 +709,20 @@ faim_export void aim_session_init(struct aim_session_t *sess) sess->queue_outgoing = NULL; sess->queue_incoming = NULL; sess->pendingjoin = NULL; + sess->pendingjoinexchange = 0; aim_initsnachash(sess); + sess->msgcookies = NULL; sess->snac_nextid = 0x00000001; + sess->flags = 0; + + /* + * Default to SNAC login unless XORLOGIN is explicitly set. + */ + if (!(flags & AIM_SESS_FLAGS_XORLOGIN)) + sess->flags |= AIM_SESS_FLAGS_SNACLOGIN; + sess->flags |= flags; + /* * This must always be set. Default to the queue-based * version for back-compatibility. @@ -694,3 +731,75 @@ faim_export void aim_session_init(struct aim_session_t *sess) return; } + +/** + * aim_conn_isconnecting - Determine if a connection is connecting + * @conn: Connection to examine + * + * Returns nonzero if the connection is in the process of + * connecting (or if it just completed and aim_conn_completeconnect() + * has yet to be called on it). + * + */ +faim_export int aim_conn_isconnecting(struct aim_conn_t *conn) +{ + if (!conn) + return 0; + return (conn->status & AIM_CONN_STATUS_INPROGRESS)?1:0; +} + +faim_export int aim_conn_completeconnect(struct aim_session_t *sess, struct aim_conn_t *conn) +{ + fd_set fds, wfds; + struct timeval tv; + int res, error = ETIMEDOUT; + rxcallback_t userfunc; + + if (!conn || (conn->fd == -1)) + return -1; + + if (!(conn->status & AIM_CONN_STATUS_INPROGRESS)) + return -1; + + FD_ZERO(&fds); + FD_SET(conn->fd, &fds); + FD_ZERO(&wfds); + FD_SET(conn->fd, &wfds); + tv.tv_sec = 0; + tv.tv_usec = 0; + + if ((res = select(conn->fd+1, &fds, &wfds, NULL, &tv)) == -1) { + error = errno; + aim_conn_close(conn); + errno = error; + return -1; + } else if (res == 0) { + printf("faim: aim_conn_completeconnect: false alarm on %d\n", conn->fd); + return 0; /* hasn't really completed yet... */ + } + + if (FD_ISSET(conn->fd, &fds) || FD_ISSET(conn->fd, &wfds)) { + int len = sizeof(error); + + if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) + error = errno; + } + + if (error) { + aim_conn_close(conn); + errno = error; + return -1; + } + + fcntl(conn->fd, F_SETFL, 0); /* XXX should restore original flags */ + + conn->status &= ~AIM_CONN_STATUS_INPROGRESS; + + if ((userfunc = aim_callhandler(conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE))) + userfunc(sess, NULL, conn); + + /* Flush out the queues if there was something waiting for this conn */ + aim_tx_flushqueue(sess); + + return 0; +}