source: libfaim/rxqueue.c @ 453bd70

barnowl_perlaimdebianowlrelease-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since 453bd70 was 862371b, checked in by James M. Kretchmar <kretch@mit.edu>, 18 years ago
*** empty log message ***
  • Property mode set to 100644
File size: 6.4 KB
Line 
1/*
2 * rxqueue.c
3 *
4 * This file contains the management routines for the receive
5 * (incoming packet) queue.  The actual packet handlers are in
6 * aim_rxhandlers.c.
7 */
8
9#define FAIM_INTERNAL
10#include <aim.h>
11
12#ifndef _WIN32
13#include <sys/socket.h>
14#endif
15
16/*
17 *
18 */
19faim_internal int aim_recv(int fd, void *buf, size_t count)
20{
21        int left, cur; 
22
23        for (cur = 0, left = count; left; ) {
24                int ret;
25               
26                ret = recv(fd, ((unsigned char *)buf)+cur, left, 0);
27
28                /* Of course EOF is an error, only morons disagree with that. */
29                if (ret <= 0)
30                        return -1;
31
32                cur += ret;
33                left -= ret;
34        }
35
36        return cur;
37}
38
39/*
40 * Read into a byte stream.  Will not read more than count, but may read
41 * less if there is not enough room in the stream buffer.
42 */
43faim_internal int aim_bstream_recv(aim_bstream_t *bs, int fd, size_t count)
44{
45        int red = 0;
46
47        if (!bs || (fd < 0) || (count < 0))
48                return -1;
49       
50        if (count > (bs->len - bs->offset))
51                count = bs->len - bs->offset; /* truncate to remaining space */
52
53        if (count) {
54
55                red = aim_recv(fd, bs->data + bs->offset, count);
56
57                if (red <= 0)
58                        return -1;
59        }
60
61        bs->offset += red;
62
63        return red;
64}
65
66/**
67 * aim_frame_destroy - free aim_frame_t
68 * @frame: the frame to free
69 *
70 * returns -1 on error; 0 on success.
71 *
72 */
73faim_internal void aim_frame_destroy(aim_frame_t *frame)
74{
75
76        free(frame->data.data); /* XXX aim_bstream_free */
77        free(frame);
78
79        return;
80}
81
82/*
83 * Read a FLAP header from conn into fr, and return the number of bytes in the payload.
84 */
85static faim_shortfunc int aim_get_command_flap(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr)
86{
87        fu8_t flaphdr_raw[6];
88        aim_bstream_t flaphdr;
89        fu16_t payloadlen;
90       
91        aim_bstream_init(&flaphdr, flaphdr_raw, sizeof(flaphdr_raw));
92
93        /*
94         * Read FLAP header.  Six bytes:
95         *   0 char  -- Always 0x2a
96         *   1 char  -- Channel ID.  Usually 2 -- 1 and 4 are used during login.
97         *   2 short -- Sequence number
98         *   4 short -- Number of data bytes that follow.
99         */
100        if (aim_bstream_recv(&flaphdr, conn->fd, 6) < 6) {
101                aim_conn_close(conn);
102                return -1;
103        }
104
105        aim_bstream_rewind(&flaphdr);
106
107        /*
108         * This shouldn't happen unless the socket breaks, the server breaks,
109         * or we break.  We must handle it just in case.
110         */
111        if (aimbs_get8(&flaphdr) != 0x2a) {
112                fu8_t start;
113
114                aim_bstream_rewind(&flaphdr);
115                start = aimbs_get8(&flaphdr);
116                faimdprintf(sess, 0, "FLAP framing disrupted (0x%02x)", start);
117                aim_conn_close(conn);
118                return -1;
119        }       
120
121        /* we're doing FLAP if we're here */
122        fr->hdrtype = AIM_FRAMETYPE_FLAP;
123
124        fr->hdr.flap.type = aimbs_get8(&flaphdr);
125        fr->hdr.flap.seqnum = aimbs_get16(&flaphdr);
126        payloadlen = aimbs_get16(&flaphdr); /* length of payload */
127
128        return payloadlen;
129}
130
131/*
132 * Read a rendezvous header from conn into fr, and return the number of bytes in the payload.
133 */
134static int aim_get_command_rendezvous(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr)
135{
136        fu8_t rendhdr_raw[8];
137        aim_bstream_t rendhdr;
138
139        aim_bstream_init(&rendhdr, rendhdr_raw, sizeof(rendhdr_raw));
140
141        if (aim_bstream_recv(&rendhdr, conn->fd, 8) < 8) {
142                aim_conn_close(conn);
143                return -1;
144        }
145
146        aim_bstream_rewind(&rendhdr);
147
148        fr->hdrtype = AIM_FRAMETYPE_OFT; /* a misnomer--rendezvous */
149
150        aimbs_getrawbuf(&rendhdr, fr->hdr.rend.magic, 4);
151        fr->hdr.rend.hdrlen = aimbs_get16(&rendhdr) - 8;
152        fr->hdr.rend.type = aimbs_get16(&rendhdr);
153
154        return fr->hdr.rend.hdrlen;
155}
156
157/*
158 * Grab a single command sequence off the socket, and enqueue it in the incoming event queue
159 * in a separate struct.
160 */
161faim_export int aim_get_command(aim_session_t *sess, aim_conn_t *conn)
162{
163        aim_frame_t *newrx;
164        fu16_t payloadlen;
165
166        if (!sess || !conn)
167                return -1;
168
169        if (conn->fd == -1)
170                return -1; /* it's an aim_conn_close()'d connection */
171
172        if (conn->fd < 3) /* can happen when people abuse the interface */
173                return -1;
174
175        if (conn->status & AIM_CONN_STATUS_INPROGRESS)
176                return aim_conn_completeconnect(sess, conn);
177
178        if (!(newrx = (aim_frame_t *)calloc(sizeof(aim_frame_t), 1)))
179                return -1;
180
181        /*
182         * Rendezvous (client to client) connections do not speak FLAP, so this
183         * function will break on them.
184         */
185        if (conn->type == AIM_CONN_TYPE_RENDEZVOUS)
186                payloadlen = aim_get_command_rendezvous(sess, conn, newrx);
187        else if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
188                faimdprintf(sess, 0, "AIM_CONN_TYPE_RENDEZVOUS_OUT on fd %d\n", conn->fd);
189                free(newrx);
190                return -1;
191        } else
192                payloadlen = aim_get_command_flap(sess, conn, newrx);
193
194        newrx->nofree = 0; /* free by default */
195
196        if (payloadlen) {
197                fu8_t *payload = NULL;
198
199                if (!(payload = (fu8_t *) malloc(payloadlen))) {
200                        aim_frame_destroy(newrx);
201                        return -1;
202                }
203
204                aim_bstream_init(&newrx->data, payload, payloadlen);
205
206                /* read the payload */
207                if (aim_bstream_recv(&newrx->data, conn->fd, payloadlen) < payloadlen) {
208                        aim_frame_destroy(newrx); /* free's payload */
209                        aim_conn_close(conn);
210                        return -1;
211                }
212        } else
213                aim_bstream_init(&newrx->data, NULL, 0);
214
215
216        aim_bstream_rewind(&newrx->data);
217
218        newrx->conn = conn;
219
220        newrx->next = NULL;  /* this will always be at the bottom */
221
222        if (!sess->queue_incoming)
223                sess->queue_incoming = newrx;
224        else {
225                aim_frame_t *cur;
226
227                for (cur = sess->queue_incoming; cur->next; cur = cur->next)
228                        ;
229                cur->next = newrx;
230        }
231
232        newrx->conn->lastactivity = time(NULL);
233
234        return 0; 
235}
236
237/*
238 * Purge recieve queue of all handled commands (->handled==1).  Also
239 * allows for selective freeing using ->nofree so that the client can
240 * keep the data for various purposes. 
241 *
242 * If ->nofree is nonzero, the frame will be delinked from the global list,
243 * but will not be free'ed.  The client _must_ keep a pointer to the
244 * data -- libfaim will not!  If the client marks ->nofree but
245 * does not keep a pointer, it's lost forever.
246 *
247 */
248faim_export void aim_purge_rxqueue(aim_session_t *sess)
249{
250        aim_frame_t *cur, **prev;
251
252        for (prev = &sess->queue_incoming; (cur = *prev); ) {
253                if (cur->handled) {
254
255                        *prev = cur->next;
256                       
257                        if (!cur->nofree)
258                                aim_frame_destroy(cur);
259
260                } else
261                        prev = &cur->next;
262        }
263
264        return;
265}
266
267/*
268 * Since aim_get_command will aim_conn_kill dead connections, we need
269 * to clean up the rxqueue of unprocessed connections on that socket.
270 *
271 * XXX: this is something that was handled better in the old connection
272 * handling method, but eh.
273 */
274faim_internal void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn)
275{
276        aim_frame_t *currx;
277
278        for (currx = sess->queue_incoming; currx; currx = currx->next) {
279                if ((!currx->handled) && (currx->conn == conn))
280                        currx->handled = 1;
281        }       
282        return;
283}
Note: See TracBrowser for help on using the repository browser.