source: libfaim/rxqueue.c @ 601733d

barnowl_perlaimdebianowlrelease-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since 601733d was e374dee, checked in by James M. Kretchmar <kretch@mit.edu>, 17 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 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 -EINVAL;
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 -ENOMEM;
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                int ret = aim_get_command_rendezvous(sess, conn, newrx);
187
188                if (ret < 0) {
189                        free(newrx);
190                        return -1;
191                }
192
193                payloadlen = ret;
194        } else if (conn->type == AIM_CONN_TYPE_LISTENER) {
195                faimdprintf(sess, 0, "AIM_CONN_TYPE_LISTENER on fd %d\n", conn->fd);
196                free(newrx);
197                return -1;
198        } else
199                payloadlen = aim_get_command_flap(sess, conn, newrx);
200
201        newrx->nofree = 0; /* free by default */
202
203        if (payloadlen) {
204                fu8_t *payload = NULL;
205
206                if (!(payload = (fu8_t *) malloc(payloadlen))) {
207                        aim_frame_destroy(newrx);
208                        return -1;
209                }
210
211                aim_bstream_init(&newrx->data, payload, payloadlen);
212
213                /* read the payload */
214                if (aim_bstream_recv(&newrx->data, conn->fd, payloadlen) < payloadlen) {
215                        aim_frame_destroy(newrx); /* free's payload */
216                        aim_conn_close(conn);
217                        return -1;
218                }
219        } else
220                aim_bstream_init(&newrx->data, NULL, 0);
221
222
223        aim_bstream_rewind(&newrx->data);
224
225        newrx->conn = conn;
226
227        newrx->next = NULL;  /* this will always be at the bottom */
228
229        if (!sess->queue_incoming)
230                sess->queue_incoming = newrx;
231        else {
232                aim_frame_t *cur;
233
234                for (cur = sess->queue_incoming; cur->next; cur = cur->next)
235                        ;
236                cur->next = newrx;
237        }
238
239        newrx->conn->lastactivity = time(NULL);
240
241        return 0; 
242}
243
244/*
245 * Purge recieve queue of all handled commands (->handled==1).  Also
246 * allows for selective freeing using ->nofree so that the client can
247 * keep the data for various purposes. 
248 *
249 * If ->nofree is nonzero, the frame will be delinked from the global list,
250 * but will not be free'ed.  The client _must_ keep a pointer to the
251 * data -- libfaim will not!  If the client marks ->nofree but
252 * does not keep a pointer, it's lost forever.
253 *
254 */
255faim_export void aim_purge_rxqueue(aim_session_t *sess)
256{
257        aim_frame_t *cur, **prev;
258
259        for (prev = &sess->queue_incoming; (cur = *prev); ) {
260                if (cur->handled) {
261
262                        *prev = cur->next;
263                       
264                        if (!cur->nofree)
265                                aim_frame_destroy(cur);
266
267                } else
268                        prev = &cur->next;
269        }
270
271        return;
272}
273
274/*
275 * Since aim_get_command will aim_conn_kill dead connections, we need
276 * to clean up the rxqueue of unprocessed connections on that socket.
277 *
278 * XXX: this is something that was handled better in the old connection
279 * handling method, but eh.
280 */
281faim_internal void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn)
282{
283        aim_frame_t *currx;
284
285        for (currx = sess->queue_incoming; currx; currx = currx->next) {
286                if ((!currx->handled) && (currx->conn == conn))
287                        currx->handled = 1;
288        }       
289        return;
290}
Note: See TracBrowser for help on using the repository browser.