/* * rxqueue.c * * This file contains the management routines for the receive * (incoming packet) queue. The actual packet handlers are in * aim_rxhandlers.c. */ #define FAIM_INTERNAL #include #ifndef _WIN32 #include #endif /* * */ faim_internal int aim_recv(int fd, void *buf, size_t count) { int left, cur; for (cur = 0, left = count; left; ) { int ret; ret = recv(fd, ((unsigned char *)buf)+cur, left, 0); /* Of course EOF is an error, only morons disagree with that. */ if (ret <= 0) return -1; cur += ret; left -= ret; } return cur; } /* * Read into a byte stream. Will not read more than count, but may read * less if there is not enough room in the stream buffer. */ faim_internal int aim_bstream_recv(aim_bstream_t *bs, int fd, size_t count) { int red = 0; if (!bs || (fd < 0) || (count < 0)) return -1; if (count > (bs->len - bs->offset)) count = bs->len - bs->offset; /* truncate to remaining space */ if (count) { red = aim_recv(fd, bs->data + bs->offset, count); if (red <= 0) return -1; } bs->offset += red; return red; } /** * aim_frame_destroy - free aim_frame_t * @frame: the frame to free * * returns -1 on error; 0 on success. * */ faim_internal void aim_frame_destroy(aim_frame_t *frame) { free(frame->data.data); /* XXX aim_bstream_free */ free(frame); return; } /* * Read a FLAP header from conn into fr, and return the number of bytes in the payload. */ static int aim_get_command_flap(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr) { fu8_t flaphdr_raw[6]; aim_bstream_t flaphdr; fu16_t payloadlen; aim_bstream_init(&flaphdr, flaphdr_raw, sizeof(flaphdr_raw)); /* * Read FLAP header. Six bytes: * 0 char -- Always 0x2a * 1 char -- Channel ID. Usually 2 -- 1 and 4 are used during login. * 2 short -- Sequence number * 4 short -- Number of data bytes that follow. */ if (aim_bstream_recv(&flaphdr, conn->fd, 6) < 6) { aim_conn_close(conn); return -1; } aim_bstream_rewind(&flaphdr); /* * This shouldn't happen unless the socket breaks, the server breaks, * or we break. We must handle it just in case. */ if (aimbs_get8(&flaphdr) != 0x2a) { fu8_t start; aim_bstream_rewind(&flaphdr); start = aimbs_get8(&flaphdr); faimdprintf(sess, 0, "FLAP framing disrupted (0x%02x)", start); aim_conn_close(conn); return -1; } /* we're doing FLAP if we're here */ fr->hdrtype = AIM_FRAMETYPE_FLAP; fr->hdr.flap.type = aimbs_get8(&flaphdr); fr->hdr.flap.seqnum = aimbs_get16(&flaphdr); payloadlen = aimbs_get16(&flaphdr); /* length of payload */ return payloadlen; } /* * Read a rendezvous header from conn into fr, and return the number of bytes in the payload. */ static int aim_get_command_rendezvous(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr) { fu8_t rendhdr_raw[8]; aim_bstream_t rendhdr; aim_bstream_init(&rendhdr, rendhdr_raw, sizeof(rendhdr_raw)); if (aim_bstream_recv(&rendhdr, conn->fd, 8) < 8) { aim_conn_close(conn); return -1; } aim_bstream_rewind(&rendhdr); fr->hdrtype = AIM_FRAMETYPE_OFT; /* a misnomer--rendezvous */ aimbs_getrawbuf(&rendhdr, fr->hdr.rend.magic, 4); fr->hdr.rend.hdrlen = aimbs_get16(&rendhdr) - 8; fr->hdr.rend.type = aimbs_get16(&rendhdr); return fr->hdr.rend.hdrlen; } /* * Grab a single command sequence off the socket, and enqueue it in the incoming event queue * in a separate struct. */ faim_export int aim_get_command(aim_session_t *sess, aim_conn_t *conn) { aim_frame_t *newrx; fu16_t payloadlen; if (!sess || !conn) return -EINVAL; if (conn->fd == -1) return -1; /* it's an aim_conn_close()'d connection */ if (conn->fd < 3) /* can happen when people abuse the interface */ return -1; if (conn->status & AIM_CONN_STATUS_INPROGRESS) return aim_conn_completeconnect(sess, conn); if (!(newrx = (aim_frame_t *)calloc(sizeof(aim_frame_t), 1))) return -ENOMEM; /* * Rendezvous (client to client) connections do not speak FLAP, so this * function will break on them. */ if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) { int ret = aim_get_command_rendezvous(sess, conn, newrx); if (ret < 0) { free(newrx); return -1; } payloadlen = ret; } else if (conn->type == AIM_CONN_TYPE_LISTENER) { faimdprintf(sess, 0, "AIM_CONN_TYPE_LISTENER on fd %d\n", conn->fd); free(newrx); return -1; } else payloadlen = aim_get_command_flap(sess, conn, newrx); newrx->nofree = 0; /* free by default */ if (payloadlen) { fu8_t *payload = NULL; if (!(payload = (fu8_t *) malloc(payloadlen))) { aim_frame_destroy(newrx); return -1; } aim_bstream_init(&newrx->data, payload, payloadlen); /* read the payload */ if (aim_bstream_recv(&newrx->data, conn->fd, payloadlen) < payloadlen) { aim_frame_destroy(newrx); /* free's payload */ aim_conn_close(conn); return -1; } } else aim_bstream_init(&newrx->data, NULL, 0); aim_bstream_rewind(&newrx->data); newrx->conn = conn; newrx->next = NULL; /* this will always be at the bottom */ if (!sess->queue_incoming) sess->queue_incoming = newrx; else { aim_frame_t *cur; for (cur = sess->queue_incoming; cur->next; cur = cur->next) ; cur->next = newrx; } newrx->conn->lastactivity = time(NULL); return 0; } /* * Purge recieve queue of all handled commands (->handled==1). Also * allows for selective freeing using ->nofree so that the client can * keep the data for various purposes. * * If ->nofree is nonzero, the frame will be delinked from the global list, * but will not be free'ed. The client _must_ keep a pointer to the * data -- libfaim will not! If the client marks ->nofree but * does not keep a pointer, it's lost forever. * */ faim_export void aim_purge_rxqueue(aim_session_t *sess) { aim_frame_t *cur, **prev; for (prev = &sess->queue_incoming; (cur = *prev); ) { if (cur->handled) { *prev = cur->next; if (!cur->nofree) aim_frame_destroy(cur); } else prev = &cur->next; } return; } /* * Since aim_get_command will aim_conn_kill dead connections, we need * to clean up the rxqueue of unprocessed connections on that socket. * * XXX: this is something that was handled better in the old connection * handling method, but eh. */ faim_internal void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn) { aim_frame_t *currx; for (currx = sess->queue_incoming; currx; currx = currx->next) { if ((!currx->handled) && (currx->conn == conn)) currx->handled = 1; } return; }