]>
Commit | Line | Data |
---|---|---|
9de3ca7e | 1 | /* |
f1a5efe0 | 2 | * aim_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. | |
9de3ca7e | 7 | */ |
8 | ||
37ee990e | 9 | #define FAIM_INTERNAL |
dd60ff8b | 10 | #include <aim.h> |
54d280df | 11 | |
12 | #ifndef _WIN32 | |
78b3fb13 | 13 | #include <sys/socket.h> |
54d280df | 14 | #endif |
9de3ca7e | 15 | |
a3619f23 | 16 | /* |
78b3fb13 | 17 | * |
a3619f23 | 18 | */ |
5ac21963 | 19 | faim_internal int aim_recv(int fd, void *buf, size_t count) |
a3619f23 | 20 | { |
d410cf58 | 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 | if (ret == -1) | |
28 | return -1; | |
29 | else if (ret == 0) | |
30 | return cur; | |
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 | */ | |
43 | faim_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 | faim_internal int aim_bstream_init(aim_bstream_t *bs, fu8_t *data, int len) | |
67 | { | |
68 | ||
69 | if (!bs) | |
70 | return -1; | |
71 | ||
72 | bs->data = data; | |
73 | bs->len = len; | |
74 | bs->offset = 0; | |
75 | ||
76 | return 0; | |
77 | } | |
78 | ||
79 | faim_internal int aim_bstream_empty(aim_bstream_t *bs) | |
80 | { | |
81 | return bs->len - bs->offset; | |
82 | } | |
83 | ||
84 | faim_internal int aim_bstream_curpos(aim_bstream_t *bs) | |
85 | { | |
86 | return bs->offset; | |
87 | } | |
88 | ||
89 | faim_internal int aim_bstream_setpos(aim_bstream_t *bs, int off) | |
90 | { | |
91 | ||
92 | if (off > bs->len) | |
93 | return -1; | |
94 | ||
95 | bs->offset = off; | |
96 | ||
97 | return off; | |
98 | } | |
99 | ||
100 | faim_internal void aim_bstream_rewind(aim_bstream_t *bs) | |
101 | { | |
102 | ||
103 | aim_bstream_setpos(bs, 0); | |
104 | ||
105 | return; | |
106 | } | |
107 | ||
108 | faim_internal int aim_bstream_advance(aim_bstream_t *bs, int n) | |
109 | { | |
110 | ||
111 | if (aim_bstream_empty(bs) < n) | |
112 | return 0; /* XXX throw an exception */ | |
113 | ||
114 | bs->offset += n; | |
115 | ||
116 | return n; | |
117 | } | |
118 | ||
119 | faim_internal fu8_t aimbs_get8(aim_bstream_t *bs) | |
120 | { | |
121 | ||
122 | if (aim_bstream_empty(bs) < 1) | |
123 | return 0; /* XXX throw an exception */ | |
124 | ||
125 | bs->offset++; | |
126 | ||
127 | return aimutil_get8(bs->data + bs->offset - 1); | |
128 | } | |
129 | ||
130 | faim_internal fu16_t aimbs_get16(aim_bstream_t *bs) | |
131 | { | |
132 | ||
133 | if (aim_bstream_empty(bs) < 2) | |
134 | return 0; /* XXX throw an exception */ | |
135 | ||
136 | bs->offset += 2; | |
137 | ||
138 | return aimutil_get16(bs->data + bs->offset - 2); | |
139 | } | |
140 | ||
141 | faim_internal fu32_t aimbs_get32(aim_bstream_t *bs) | |
142 | { | |
143 | ||
144 | if (aim_bstream_empty(bs) < 4) | |
145 | return 0; /* XXX throw an exception */ | |
146 | ||
147 | bs->offset += 4; | |
148 | ||
149 | return aimutil_get32(bs->data + bs->offset - 4); | |
150 | } | |
151 | ||
152 | faim_internal int aimbs_put8(aim_bstream_t *bs, fu8_t v) | |
153 | { | |
154 | ||
155 | if (aim_bstream_empty(bs) < 1) | |
156 | return 0; /* XXX throw an exception */ | |
157 | ||
158 | bs->offset += aimutil_put8(bs->data + bs->offset, v); | |
159 | ||
160 | return 1; | |
161 | } | |
162 | ||
163 | faim_internal int aimbs_put16(aim_bstream_t *bs, fu16_t v) | |
164 | { | |
165 | ||
166 | if (aim_bstream_empty(bs) < 2) | |
167 | return 0; /* XXX throw an exception */ | |
168 | ||
169 | bs->offset += aimutil_put16(bs->data + bs->offset, v); | |
170 | ||
171 | return 2; | |
172 | } | |
173 | ||
174 | faim_internal int aimbs_put32(aim_bstream_t *bs, fu32_t v) | |
175 | { | |
176 | ||
177 | if (aim_bstream_empty(bs) < 4) | |
178 | return 0; /* XXX throw an exception */ | |
179 | ||
180 | bs->offset += aimutil_put32(bs->data + bs->offset, v); | |
181 | ||
182 | return 1; | |
183 | } | |
184 | ||
185 | faim_internal int aimbs_getrawbuf(aim_bstream_t *bs, fu8_t *buf, int len) | |
186 | { | |
187 | ||
188 | if (aim_bstream_empty(bs) < len) | |
189 | return 0; | |
190 | ||
191 | memcpy(buf, bs->data + bs->offset, len); | |
192 | bs->offset += len; | |
193 | ||
194 | return len; | |
195 | } | |
196 | ||
197 | faim_internal fu8_t *aimbs_getraw(aim_bstream_t *bs, int len) | |
198 | { | |
199 | fu8_t *ob; | |
200 | ||
201 | if (!(ob = malloc(len))) | |
202 | return NULL; | |
203 | ||
204 | if (aimbs_getrawbuf(bs, ob, len) < len) { | |
205 | free(ob); | |
206 | return NULL; | |
207 | } | |
208 | ||
209 | return ob; | |
210 | } | |
211 | ||
212 | faim_internal char *aimbs_getstr(aim_bstream_t *bs, int len) | |
213 | { | |
214 | char *ob; | |
215 | ||
216 | if (!(ob = malloc(len+1))) | |
217 | return NULL; | |
218 | ||
219 | if (aimbs_getrawbuf(bs, ob, len) < len) { | |
220 | free(ob); | |
221 | return NULL; | |
222 | } | |
223 | ||
224 | ob[len] = '\0'; | |
225 | ||
226 | return ob; | |
a3619f23 | 227 | } |
228 | ||
d410cf58 | 229 | faim_internal int aimbs_putraw(aim_bstream_t *bs, const fu8_t *v, int len) |
230 | { | |
231 | ||
232 | if (aim_bstream_empty(bs) < len) | |
233 | return 0; /* XXX throw an exception */ | |
234 | ||
235 | memcpy(bs->data + bs->offset, v, len); | |
236 | bs->offset += len; | |
237 | ||
238 | return len; | |
239 | } | |
240 | ||
241 | faim_internal int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len) | |
242 | { | |
243 | ||
244 | if (aim_bstream_empty(srcbs) < len) | |
245 | return 0; /* XXX throw exception (underrun) */ | |
246 | ||
247 | if (aim_bstream_empty(bs) < len) | |
248 | return 0; /* XXX throw exception (overflow) */ | |
249 | ||
250 | memcpy(bs->data + bs->offset, srcbs->data + srcbs->offset, len); | |
251 | bs->offset += len; | |
252 | srcbs->offset += len; | |
253 | ||
254 | return len; | |
255 | } | |
256 | ||
257 | /** | |
258 | * aim_frame_destroy - free aim_frame_t | |
259 | * @frame: the frame to free | |
260 | * | |
261 | * returns -1 on error; 0 on success. | |
262 | * | |
263 | */ | |
264 | faim_internal void aim_frame_destroy(aim_frame_t *frame) | |
265 | { | |
266 | ||
267 | free(frame->data.data); /* XXX aim_bstream_free */ | |
268 | ||
269 | if (frame->hdrtype == AIM_FRAMETYPE_OFT) | |
270 | free(frame->hdr.oft.hdr2); | |
271 | free(frame); | |
272 | ||
273 | return; | |
274 | } | |
275 | ||
276 | ||
9de3ca7e | 277 | /* |
f1a5efe0 | 278 | * Grab a single command sequence off the socket, and enqueue |
279 | * it in the incoming event queue in a seperate struct. | |
a25832e6 | 280 | */ |
d410cf58 | 281 | faim_export int aim_get_command(aim_session_t *sess, aim_conn_t *conn) |
282 | { | |
283 | fu8_t flaphdr_raw[6]; | |
284 | aim_bstream_t flaphdr; | |
285 | aim_frame_t *newrx; | |
286 | fu16_t payloadlen; | |
287 | ||
288 | if (!sess || !conn) | |
289 | return 0; | |
290 | ||
291 | if (conn->fd == -1) | |
292 | return -1; /* its a aim_conn_close()'d connection */ | |
293 | ||
294 | if (conn->fd < 3) /* can happen when people abuse the interface */ | |
295 | return 0; | |
296 | ||
297 | if (conn->status & AIM_CONN_STATUS_INPROGRESS) | |
298 | return aim_conn_completeconnect(sess, conn); | |
299 | ||
300 | /* | |
301 | * Rendezvous (client-client) connections do not speak | |
302 | * FLAP, so this function will break on them. | |
303 | */ | |
304 | if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) | |
305 | return aim_get_command_rendezvous(sess, conn); | |
306 | else if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) { | |
307 | faimdprintf(sess, 0, "AIM_CONN_TYPE_RENDEZVOUS_OUT on fd %d\n", conn->fd); | |
308 | return 0; | |
309 | } | |
310 | ||
311 | aim_bstream_init(&flaphdr, flaphdr_raw, sizeof(flaphdr_raw)); | |
312 | ||
313 | /* | |
314 | * Read FLAP header. Six bytes: | |
315 | * | |
316 | * 0 char -- Always 0x2a | |
317 | * 1 char -- Channel ID. Usually 2 -- 1 and 4 are used during login. | |
318 | * 2 short -- Sequence number | |
319 | * 4 short -- Number of data bytes that follow. | |
320 | */ | |
d410cf58 | 321 | if (aim_bstream_recv(&flaphdr, conn->fd, 6) < 6) { |
322 | aim_conn_close(conn); | |
d410cf58 | 323 | return -1; |
324 | } | |
325 | ||
326 | aim_bstream_rewind(&flaphdr); | |
327 | ||
328 | /* | |
329 | * This shouldn't happen unless the socket breaks, the server breaks, | |
330 | * or we break. We must handle it just in case. | |
331 | */ | |
332 | if (aimbs_get8(&flaphdr) != 0x2a) { | |
333 | faimdprintf(sess, 0, "FLAP framing disrupted"); | |
334 | aim_conn_close(conn); | |
d410cf58 | 335 | return -1; |
336 | } | |
337 | ||
338 | /* allocate a new struct */ | |
031c2fb3 | 339 | if (!(newrx = (aim_frame_t *)malloc(sizeof(aim_frame_t)))) |
d410cf58 | 340 | return -1; |
d410cf58 | 341 | memset(newrx, 0, sizeof(aim_frame_t)); |
342 | ||
343 | /* we're doing FLAP if we're here */ | |
344 | newrx->hdrtype = AIM_FRAMETYPE_FLAP; | |
345 | ||
346 | newrx->hdr.flap.type = aimbs_get8(&flaphdr); | |
347 | newrx->hdr.flap.seqnum = aimbs_get16(&flaphdr); | |
348 | payloadlen = aimbs_get16(&flaphdr); | |
349 | ||
350 | newrx->nofree = 0; /* free by default */ | |
351 | ||
352 | if (payloadlen) { | |
353 | fu8_t *payload = NULL; | |
354 | ||
355 | if (!(payload = (fu8_t *) malloc(payloadlen))) { | |
356 | aim_frame_destroy(newrx); | |
d410cf58 | 357 | return -1; |
358 | } | |
359 | ||
360 | aim_bstream_init(&newrx->data, payload, payloadlen); | |
361 | ||
362 | /* read the payload */ | |
363 | if (aim_bstream_recv(&newrx->data, conn->fd, payloadlen) < payloadlen) { | |
364 | free(payload); | |
365 | aim_frame_destroy(newrx); | |
366 | aim_conn_close(conn); | |
d410cf58 | 367 | return -1; |
368 | } | |
369 | } else | |
370 | aim_bstream_init(&newrx->data, NULL, 0); | |
371 | ||
d410cf58 | 372 | |
373 | aim_bstream_rewind(&newrx->data); | |
374 | ||
375 | newrx->conn = conn; | |
376 | ||
377 | newrx->next = NULL; /* this will always be at the bottom */ | |
378 | ||
379 | if (!sess->queue_incoming) | |
380 | sess->queue_incoming = newrx; | |
381 | else { | |
382 | aim_frame_t *cur; | |
383 | ||
384 | for (cur = sess->queue_incoming; cur->next; cur = cur->next) | |
385 | ; | |
386 | cur->next = newrx; | |
387 | } | |
388 | ||
389 | newrx->conn->lastactivity = time(NULL); | |
390 | ||
391 | return 0; | |
9de3ca7e | 392 | } |
393 | ||
394 | /* | |
b8d0da45 | 395 | * Purge recieve queue of all handled commands (->handled==1). Also |
396 | * allows for selective freeing using ->nofree so that the client can | |
397 | * keep the data for various purposes. | |
a25832e6 | 398 | * |
b8d0da45 | 399 | * If ->nofree is nonzero, the frame will be delinked from the global list, |
400 | * but will not be free'ed. The client _must_ keep a pointer to the | |
401 | * data -- libfaim will not! If the client marks ->nofree but | |
402 | * does not keep a pointer, it's lost forever. | |
a25832e6 | 403 | * |
9de3ca7e | 404 | */ |
d410cf58 | 405 | faim_export void aim_purge_rxqueue(aim_session_t *sess) |
406 | { | |
407 | aim_frame_t *cur, **prev; | |
408 | ||
409 | for (prev = &sess->queue_incoming; (cur = *prev); ) { | |
410 | if (cur->handled) { | |
411 | ||
412 | *prev = cur->next; | |
413 | ||
414 | if (!cur->nofree) | |
415 | aim_frame_destroy(cur); | |
416 | ||
417 | } else | |
418 | prev = &cur->next; | |
419 | } | |
420 | ||
421 | return; | |
9de3ca7e | 422 | } |
68ac63c2 | 423 | |
424 | /* | |
425 | * Since aim_get_command will aim_conn_kill dead connections, we need | |
426 | * to clean up the rxqueue of unprocessed connections on that socket. | |
427 | * | |
428 | * XXX: this is something that was handled better in the old connection | |
429 | * handling method, but eh. | |
430 | */ | |
d410cf58 | 431 | faim_internal void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn) |
68ac63c2 | 432 | { |
d410cf58 | 433 | aim_frame_t *currx; |
68ac63c2 | 434 | |
d410cf58 | 435 | for (currx = sess->queue_incoming; currx; currx = currx->next) { |
436 | if ((!currx->handled) && (currx->conn == conn)) | |
437 | currx->handled = 1; | |
438 | } | |
439 | return; | |
68ac63c2 | 440 | } |
d410cf58 | 441 |