2086
|
1 /*
|
|
2 * aim_rxhandlers.c
|
|
3 *
|
|
4 * This file contains most all of the incoming packet handlers, along
|
|
5 * with aim_rxdispatch(), the Rx dispatcher. Queue/list management is
|
|
6 * actually done in aim_rxqueue.c.
|
|
7 *
|
|
8 */
|
|
9
|
|
10 #define FAIM_INTERNAL
|
|
11 #include <aim.h>
|
|
12
|
|
13 static aim_module_t *findmodule(struct aim_session_t *sess, const char *name)
|
|
14 {
|
|
15 aim_module_t *cur;
|
|
16
|
|
17 for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
|
|
18 if (strcmp(name, cur->name) == 0)
|
|
19 return cur;
|
|
20 }
|
|
21
|
|
22 return NULL;
|
|
23 }
|
|
24
|
|
25 faim_internal int aim__registermodule(struct aim_session_t *sess, int (*modfirst)(struct aim_session_t *, aim_module_t *))
|
|
26 {
|
|
27 aim_module_t *mod;
|
|
28
|
|
29 if (!sess || !modfirst)
|
|
30 return -1;
|
|
31
|
|
32 if (!(mod = malloc(sizeof(aim_module_t))))
|
|
33 return -1;
|
|
34 memset(mod, 0, sizeof(aim_module_t));
|
|
35
|
|
36 if (modfirst(sess, mod) == -1) {
|
|
37 free(mod);
|
|
38 return -1;
|
|
39 }
|
|
40
|
|
41 if (findmodule(sess, mod->name)) {
|
|
42 if (mod->shutdown)
|
|
43 mod->shutdown(sess, mod);
|
|
44 free(mod);
|
|
45 return -1;
|
|
46 }
|
|
47
|
|
48 mod->next = (aim_module_t *)sess->modlistv;
|
|
49 (aim_module_t *)sess->modlistv = mod;
|
|
50
|
|
51 faimdprintf(sess, 1, "registered module %s (family 0x%04x)\n", mod->name, mod->family);
|
|
52
|
|
53 return 0;
|
|
54 }
|
|
55
|
|
56 faim_internal void aim__shutdownmodules(struct aim_session_t *sess)
|
|
57 {
|
|
58 aim_module_t *cur;
|
|
59
|
|
60 for (cur = (aim_module_t *)sess->modlistv; cur; ) {
|
|
61 aim_module_t *tmp;
|
|
62
|
|
63 tmp = cur->next;
|
|
64
|
|
65 if (cur->shutdown)
|
|
66 cur->shutdown(sess, cur);
|
|
67
|
|
68 free(cur);
|
|
69
|
|
70 cur = tmp;
|
|
71 }
|
|
72
|
|
73 sess->modlistv = NULL;
|
|
74
|
|
75 return;
|
|
76 }
|
|
77
|
|
78 static int consumesnac(struct aim_session_t *sess, struct command_rx_struct *rx)
|
|
79 {
|
|
80 aim_module_t *cur;
|
|
81 aim_modsnac_t snac;
|
|
82
|
|
83 snac.family = aimutil_get16(rx->data+0);
|
|
84 snac.subtype = aimutil_get16(rx->data+2);
|
|
85 snac.flags = aimutil_get16(rx->data+4);
|
|
86 snac.id = aimutil_get32(rx->data+6);
|
|
87
|
|
88 for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
|
|
89
|
|
90 if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) &&
|
|
91 (cur->family != snac.family))
|
|
92 continue;
|
|
93
|
|
94 if (cur->snachandler(sess, cur, rx, &snac, rx->data+10, rx->commandlen-10))
|
|
95 return 1;
|
|
96
|
|
97 }
|
|
98
|
|
99 return 0;
|
|
100 }
|
|
101
|
|
102 static int consumenonsnac(struct aim_session_t *sess, struct command_rx_struct *rx, unsigned short family, unsigned short subtype)
|
|
103 {
|
|
104 aim_module_t *cur;
|
|
105 aim_modsnac_t snac;
|
|
106
|
|
107 snac.family = family;
|
|
108 snac.subtype = subtype;
|
|
109 snac.flags = snac.id = 0;
|
|
110
|
|
111 for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
|
|
112
|
|
113 if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) &&
|
|
114 (cur->family != snac.family))
|
|
115 continue;
|
|
116
|
|
117 if (cur->snachandler(sess, cur, rx, &snac, rx->data, rx->commandlen))
|
|
118 return 1;
|
|
119
|
|
120 }
|
|
121
|
|
122 return 0;
|
|
123 }
|
|
124
|
|
125 /*
|
|
126 * Bleck functions get called when there's no non-bleck functions
|
|
127 * around to cleanup the mess...
|
|
128 */
|
|
129 faim_internal int bleck(struct aim_session_t *sess,struct command_rx_struct *workingPtr, ...)
|
|
130 {
|
|
131 u_short family;
|
|
132 u_short subtype;
|
|
133
|
|
134 u_short maxf;
|
|
135 u_short maxs;
|
|
136
|
|
137 /* XXX: this is ugly. and big just for debugging. */
|
|
138 char *literals[14][25] = {
|
|
139 {"Invalid",
|
|
140 NULL
|
|
141 },
|
|
142 {"General",
|
|
143 "Invalid",
|
|
144 "Error",
|
|
145 "Client Ready",
|
|
146 "Server Ready",
|
|
147 "Service Request",
|
|
148 "Redirect",
|
|
149 "Rate Information Request",
|
|
150 "Rate Information",
|
|
151 "Rate Information Ack",
|
|
152 NULL,
|
|
153 "Rate Information Change",
|
|
154 "Server Pause",
|
|
155 NULL,
|
|
156 "Server Resume",
|
|
157 "Request Personal User Information",
|
|
158 "Personal User Information",
|
|
159 "Evil Notification",
|
|
160 NULL,
|
|
161 "Migration notice",
|
|
162 "Message of the Day",
|
|
163 "Set Privacy Flags",
|
|
164 "Well Known URL",
|
|
165 "NOP"
|
|
166 },
|
|
167 {"Location",
|
|
168 "Invalid",
|
|
169 "Error",
|
|
170 "Request Rights",
|
|
171 "Rights Information",
|
|
172 "Set user information",
|
|
173 "Request User Information",
|
|
174 "User Information",
|
|
175 "Watcher Sub Request",
|
|
176 "Watcher Notification"
|
|
177 },
|
|
178 {"Buddy List Management",
|
|
179 "Invalid",
|
|
180 "Error",
|
|
181 "Request Rights",
|
|
182 "Rights Information",
|
|
183 "Add Buddy",
|
|
184 "Remove Buddy",
|
|
185 "Watcher List Query",
|
|
186 "Watcher List Response",
|
|
187 "Watcher SubRequest",
|
|
188 "Watcher Notification",
|
|
189 "Reject Notification",
|
|
190 "Oncoming Buddy",
|
|
191 "Offgoing Buddy"
|
|
192 },
|
|
193 {"Messeging",
|
|
194 "Invalid",
|
|
195 "Error",
|
|
196 "Add ICBM Parameter",
|
|
197 "Remove ICBM Parameter",
|
|
198 "Request Parameter Information",
|
|
199 "Parameter Information",
|
|
200 "Outgoing Message",
|
|
201 "Incoming Message",
|
|
202 "Evil Request",
|
|
203 "Evil Reply",
|
|
204 "Missed Calls",
|
|
205 "Message Error",
|
|
206 "Host Ack"
|
|
207 },
|
|
208 {"Advertisements",
|
|
209 "Invalid",
|
|
210 "Error",
|
|
211 "Request Ad",
|
|
212 "Ad Data (GIFs)"
|
|
213 },
|
|
214 {"Invitation / Client-to-Client",
|
|
215 "Invalid",
|
|
216 "Error",
|
|
217 "Invite a Friend",
|
|
218 "Invitation Ack"
|
|
219 },
|
|
220 {"Administrative",
|
|
221 "Invalid",
|
|
222 "Error",
|
|
223 "Information Request",
|
|
224 "Information Reply",
|
|
225 "Information Change Request",
|
|
226 "Information Chat Reply",
|
|
227 "Account Confirm Request",
|
|
228 "Account Confirm Reply",
|
|
229 "Account Delete Request",
|
|
230 "Account Delete Reply"
|
|
231 },
|
|
232 {"Popups",
|
|
233 "Invalid",
|
|
234 "Error",
|
|
235 "Display Popup"
|
|
236 },
|
|
237 {"BOS",
|
|
238 "Invalid",
|
|
239 "Error",
|
|
240 "Request Rights",
|
|
241 "Rights Response",
|
|
242 "Set group permission mask",
|
|
243 "Add permission list entries",
|
|
244 "Delete permission list entries",
|
|
245 "Add deny list entries",
|
|
246 "Delete deny list entries",
|
|
247 "Server Error"
|
|
248 },
|
|
249 {"User Lookup",
|
|
250 "Invalid",
|
|
251 "Error",
|
|
252 "Search Request",
|
|
253 "Search Response"
|
|
254 },
|
|
255 {"Stats",
|
|
256 "Invalid",
|
|
257 "Error",
|
|
258 "Set minimum report interval",
|
|
259 "Report Events"
|
|
260 },
|
|
261 {"Translate",
|
|
262 "Invalid",
|
|
263 "Error",
|
|
264 "Translate Request",
|
|
265 "Translate Reply",
|
|
266 },
|
|
267 {"Chat Navigation",
|
|
268 "Invalid",
|
|
269 "Error",
|
|
270 "Request rights",
|
|
271 "Request Exchange Information",
|
|
272 "Request Room Information",
|
|
273 "Request Occupant List",
|
|
274 "Search for Room",
|
|
275 "Outgoing Message",
|
|
276 "Incoming Message",
|
|
277 "Evil Request",
|
|
278 "Evil Reply",
|
|
279 "Chat Error",
|
|
280 }
|
|
281 };
|
|
282
|
|
283 maxf = sizeof(literals) / sizeof(literals[0]);
|
|
284 maxs = sizeof(literals[0]) / sizeof(literals[0][0]);
|
|
285
|
|
286 family = aimutil_get16(workingPtr->data+0);
|
|
287 subtype= aimutil_get16(workingPtr->data+2);
|
|
288
|
|
289 if((family < maxf) && (subtype+1 < maxs) && (literals[family][subtype] != NULL))
|
|
290 faimdprintf(sess, 0, "bleck: null handler for %04x/%04x (%s)\n", family, subtype, literals[family][subtype+1]);
|
|
291 else
|
|
292 faimdprintf(sess, 0, "bleck: null handler for %04x/%04x (no literal)\n",family,subtype);
|
|
293
|
|
294 return 1;
|
|
295 }
|
|
296
|
|
297 faim_export int aim_conn_addhandler(struct aim_session_t *sess,
|
|
298 struct aim_conn_t *conn,
|
|
299 u_short family,
|
|
300 u_short type,
|
|
301 aim_rxcallback_t newhandler,
|
|
302 u_short flags)
|
|
303 {
|
|
304 struct aim_rxcblist_t *newcb;
|
|
305
|
|
306 if (!conn)
|
|
307 return -1;
|
|
308
|
|
309 faimdprintf(sess, 1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type);
|
|
310
|
|
311 if (!(newcb = (struct aim_rxcblist_t *)calloc(1, sizeof(struct aim_rxcblist_t))))
|
|
312 return -1;
|
|
313 newcb->family = family;
|
|
314 newcb->type = type;
|
|
315 newcb->flags = flags;
|
|
316 if (!newhandler)
|
|
317 newcb->handler = &bleck;
|
|
318 else
|
|
319 newcb->handler = newhandler;
|
|
320 newcb->next = NULL;
|
|
321
|
|
322 if (!conn->handlerlist)
|
|
323 conn->handlerlist = newcb;
|
|
324 else {
|
|
325 struct aim_rxcblist_t *cur;
|
|
326
|
|
327 cur = conn->handlerlist;
|
|
328
|
|
329 while (cur->next)
|
|
330 cur = cur->next;
|
|
331 cur->next = newcb;
|
|
332 }
|
|
333
|
|
334 return 0;
|
|
335 }
|
|
336
|
|
337 faim_export int aim_clearhandlers(struct aim_conn_t *conn)
|
|
338 {
|
|
339 struct aim_rxcblist_t *cur;
|
|
340
|
|
341 if (!conn)
|
|
342 return -1;
|
|
343
|
|
344 for (cur = conn->handlerlist; cur; ) {
|
|
345 struct aim_rxcblist_t *tmp;
|
|
346
|
|
347 tmp = cur->next;
|
|
348 free(cur);
|
|
349 cur = tmp;
|
|
350 }
|
|
351 conn->handlerlist = NULL;
|
|
352
|
|
353 return 0;
|
|
354 }
|
|
355
|
|
356 faim_internal aim_rxcallback_t aim_callhandler(struct aim_session_t *sess,
|
|
357 struct aim_conn_t *conn,
|
|
358 unsigned short family,
|
|
359 unsigned short type)
|
|
360 {
|
|
361 struct aim_rxcblist_t *cur;
|
|
362
|
|
363 if (!conn)
|
|
364 return NULL;
|
|
365
|
|
366 faimdprintf(sess, 1, "aim_callhandler: calling for %04x/%04x\n", family, type);
|
|
367
|
|
368 for (cur = conn->handlerlist; cur; cur = cur->next) {
|
|
369 if ((cur->family == family) && (cur->type == type))
|
|
370 return cur->handler;
|
|
371 }
|
|
372
|
|
373 if (type == AIM_CB_SPECIAL_DEFAULT) {
|
|
374 faimdprintf(sess, 1, "aim_callhandler: no default handler for family 0x%04x\n", family);
|
|
375 return NULL; /* prevent infinite recursion */
|
|
376 }
|
|
377
|
|
378 faimdprintf(sess, 1, "aim_callhandler: no handler for 0x%04x/0x%04x\n", family, type);
|
|
379
|
|
380 return aim_callhandler(sess, conn, family, AIM_CB_SPECIAL_DEFAULT);
|
|
381 }
|
|
382
|
|
383 faim_internal int aim_callhandler_noparam(struct aim_session_t *sess,
|
|
384 struct aim_conn_t *conn,
|
|
385 u_short family,
|
|
386 u_short type,
|
|
387 struct command_rx_struct *ptr)
|
|
388 {
|
|
389 aim_rxcallback_t userfunc = NULL;
|
|
390 userfunc = aim_callhandler(sess, conn, family, type);
|
|
391 if (userfunc)
|
|
392 return userfunc(sess, ptr);
|
|
393 return 1; /* XXX */
|
|
394 }
|
|
395
|
|
396 /*
|
|
397 aim_rxdispatch()
|
|
398
|
|
399 Basically, heres what this should do:
|
|
400 1) Determine correct packet handler for this packet
|
|
401 2) Mark the packet handled (so it can be dequeued in purge_queue())
|
|
402 3) Send the packet to the packet handler
|
|
403 4) Go to next packet in the queue and start over
|
|
404 5) When done, run purge_queue() to purge handled commands
|
|
405
|
|
406 Note that any unhandlable packets should probably be left in the
|
|
407 queue. This is the best way to prevent data loss. This means
|
|
408 that a single packet may get looked at by this function multiple
|
|
409 times. This is more good than bad! This behavior may change.
|
|
410
|
|
411 Aren't queue's fun?
|
|
412
|
|
413 TODO: Get rid of all the ugly if's.
|
|
414 TODO: Clean up.
|
|
415 TODO: More support for mid-level handlers.
|
|
416 TODO: Allow for NULL handlers.
|
|
417
|
|
418 */
|
|
419 faim_export int aim_rxdispatch(struct aim_session_t *sess)
|
|
420 {
|
|
421 int i = 0;
|
|
422 struct command_rx_struct *workingPtr = NULL;
|
|
423 static int critical = 0;
|
|
424
|
|
425 if (critical)
|
|
426 return 0; /* don't call recursively! */
|
|
427
|
|
428 critical = 1;
|
|
429
|
|
430 if (sess->queue_incoming == NULL) {
|
|
431 faimdprintf(sess, 1, "parse_generic: incoming packet queue empty.\n");
|
|
432 } else {
|
|
433 workingPtr = sess->queue_incoming;
|
|
434 for (i = 0; workingPtr != NULL; workingPtr = workingPtr->next, i++) {
|
|
435 unsigned short family,subtype;
|
|
436
|
|
437 /*
|
|
438 * XXX: This is still fairly ugly.
|
|
439 */
|
|
440 if (workingPtr->handled)
|
|
441 continue;
|
|
442
|
|
443 /*
|
|
444 * This is a debugging/sanity check only and probably could/should be removed
|
|
445 * for stable code.
|
|
446 */
|
|
447 if (((workingPtr->hdrtype == AIM_FRAMETYPE_OFT) &&
|
|
448 (workingPtr->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) ||
|
|
449 ((workingPtr->hdrtype == AIM_FRAMETYPE_OSCAR) &&
|
|
450 (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) {
|
|
451 faimdprintf(sess, 0, "rxhandlers: incompatible frame type %d on connection type 0x%04x\n", workingPtr->hdrtype, workingPtr->conn->type);
|
|
452 workingPtr->handled = 1;
|
|
453 continue;
|
|
454 }
|
|
455
|
|
456 if (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS) {
|
|
457 /* make sure that we only get OFT frames on these connections */
|
|
458 if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
|
|
459 faimdprintf(sess, 0, "internal error: non-OFT frames on OFT connection\n");
|
|
460 workingPtr->handled = 1; /* get rid of it */
|
|
461 } else {
|
|
462 /* XXX: implement this */
|
|
463 faimdprintf(sess, 0, "faim: OFT frame!\n");
|
|
464 workingPtr->handled = 1; /* get rid of it */
|
|
465 }
|
|
466 continue;
|
|
467 }
|
|
468
|
|
469 if (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
|
|
470 /* not possible */
|
|
471 faimdprintf(sess, 0, "rxdispatch called on RENDEZVOUS_OUT connection!\n");
|
|
472 workingPtr->handled = 1;
|
|
473 continue;
|
|
474 }
|
|
475
|
|
476 if ((workingPtr->commandlen == 4) &&
|
|
477 (aimutil_get32(workingPtr->data) == 0x00000001)) {
|
|
478 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
|
|
479 continue;
|
|
480 }
|
|
481
|
|
482 if (workingPtr->hdr.oscar.type == 0x04) {
|
|
483 workingPtr->handled = aim_negchan_middle(sess, workingPtr);
|
|
484 continue;
|
|
485 }
|
|
486
|
|
487 if ((workingPtr->handled = consumesnac(sess, workingPtr)))
|
|
488 continue;
|
|
489
|
|
490 if (!workingPtr->handled) {
|
|
491 family = aimutil_get16(workingPtr->data);
|
|
492 subtype = aimutil_get16(workingPtr->data+2);
|
|
493
|
|
494 faimdprintf(sess, 1, "warning: unhandled packet %04x/%04x\n", family, subtype);
|
|
495 consumenonsnac(sess, workingPtr, 0xffff, 0xffff); /* last chance! */
|
|
496 workingPtr->handled = 1;
|
|
497 }
|
|
498 }
|
|
499 }
|
|
500
|
|
501 /*
|
|
502 * This doesn't have to be called here. It could easily be done
|
|
503 * by a seperate thread or something. It's an administrative operation,
|
|
504 * and can take a while. Though the less you call it the less memory
|
|
505 * you'll have :)
|
|
506 */
|
|
507 aim_purge_rxqueue(sess);
|
|
508
|
|
509 critical = 0;
|
|
510
|
|
511 return 0;
|
|
512 }
|
|
513
|
|
514 faim_internal int aim_parse_unknown(struct aim_session_t *sess,
|
|
515 struct command_rx_struct *command, ...)
|
|
516 {
|
|
517 u_int i = 0;
|
|
518
|
|
519 if (!sess || !command)
|
|
520 return 1;
|
|
521
|
|
522 faimdprintf(sess, 1, "\nRecieved unknown packet:");
|
|
523
|
|
524 for (i = 0; i < command->commandlen; i++)
|
|
525 {
|
|
526 if ((i % 8) == 0)
|
|
527 faimdprintf(sess, 1, "\n\t");
|
|
528
|
|
529 faimdprintf(sess, 1, "0x%2x ", command->data[i]);
|
|
530 }
|
|
531
|
|
532 faimdprintf(sess, 1, "\n\n");
|
|
533
|
|
534 return 1;
|
|
535 }
|
|
536
|
|
537
|
|
538 faim_internal int aim_negchan_middle(struct aim_session_t *sess,
|
|
539 struct command_rx_struct *command)
|
|
540 {
|
|
541 struct aim_tlvlist_t *tlvlist;
|
|
542 char *msg = NULL;
|
|
543 unsigned short code = 0;
|
|
544 aim_rxcallback_t userfunc = NULL;
|
|
545 int ret = 1;
|
|
546
|
|
547 /* Used only by the older login protocol */
|
|
548 /* XXX remove this special case? */
|
|
549 if (command->conn->type == AIM_CONN_TYPE_AUTH)
|
|
550 return consumenonsnac(sess, command, 0x0017, 0x0003);
|
|
551
|
|
552 tlvlist = aim_readtlvchain(command->data, command->commandlen);
|
|
553
|
|
554 if (aim_gettlv(tlvlist, 0x0009, 1))
|
|
555 code = aim_gettlv16(tlvlist, 0x0009, 1);
|
|
556
|
|
557 if (aim_gettlv(tlvlist, 0x000b, 1))
|
|
558 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
|
|
559
|
|
560 if ((userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
|
|
561 ret = userfunc(sess, command, code, msg);
|
|
562
|
|
563 aim_freetlvchain(&tlvlist);
|
|
564
|
|
565 if (msg)
|
|
566 free(msg);
|
|
567
|
|
568 return ret;
|
|
569 }
|
|
570
|