faim_internal int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len) { if (aim_bstream_empty(srcbs) < len) return 0; /* XXX throw exception (underrun) */ if (aim_bstream_empty(bs) < len) return 0; /* XXX throw exception (overflow) */ memcpy(bs->data + bs->offset, srcbs->data + srcbs->offset, len); bs->offset += len; srcbs->offset += len; return len; }
/* called for both reply and change-reply */ static int infochange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { /* * struct { * guint16 perms; * guint16 tlvcount; * aim_tlv_t tlvs[tlvcount]; * } admin_info[n]; */ while (aim_bstream_empty(bs)) { guint16 perms, tlvcount; perms = aimbs_get16(bs); tlvcount = aimbs_get16(bs); while (tlvcount && aim_bstream_empty(bs)) { aim_rxcallback_t userfunc; guint16 type, len; guint8 *val; int str = 0; type = aimbs_get16(bs); len = aimbs_get16(bs); if ((type == 0x0011) || (type == 0x0004)) { str = 1; } if (str) { val = (guint8 *) aimbs_getstr(bs, len); } else { val = aimbs_getraw(bs, len); } /* XXX fix so its only called once for the entire packet */ if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) { userfunc(sess, rx, (snac->subtype == 0x0005) ? 1 : 0, perms, type, len, val, str); } g_free(val); tlvcount--; } } return 1; }
static int consumesnac(aim_session_t *sess, aim_frame_t *rx) { aim_module_t *cur; aim_modsnac_t snac; if (aim_bstream_empty(&rx->data) < 10) return 0; snac.family = aimbs_get16(&rx->data); snac.subtype = aimbs_get16(&rx->data); snac.flags = aimbs_get16(&rx->data); snac.id = aimbs_get32(&rx->data); for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) { if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) && (cur->family != snac.family)) continue; if (cur->snachandler(sess, cur, rx, &snac, &rx->data)) return 1; } return 0; }
/* * This still takes a length parameter even with a bstream because capabilities * are not naturally bounded. * */ faim_internal fu16_t aim_getcap(aim_session_t *sess, aim_bstream_t *bs, int len) { fu16_t flags = 0; int offset; for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x10) { fu8_t *cap; int i, identified; cap = aimbs_getraw(bs, 0x10); for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) { if (memcmp(&aim_caps[i].data, cap, 0x10) == 0) { flags |= aim_caps[i].flag; identified++; break; /* should only match once... */ } } if (!identified) faimdprintf(sess, 0, "unknown capability!\n"); free(cap); } return flags; }
static int negchan_middle(aim_session_t *sess, aim_frame_t *fr) { aim_tlvlist_t *tlvlist; char *msg = NULL; fu16_t code = 0; aim_rxcallback_t userfunc; int ret = 1; if (aim_bstream_empty(&fr->data) == 0) { /* XXX should do something with this */ return 1; } /* Used only by the older login protocol */ /* XXX remove this special case? */ if (fr->conn->type == AIM_CONN_TYPE_AUTH) return consumenonsnac(sess, fr, 0x0017, 0x0003); tlvlist = aim_tlvlist_read(&fr->data); if (aim_tlv_gettlv(tlvlist, 0x0009, 1)) code = aim_tlv_get16(tlvlist, 0x0009, 1); if (aim_tlv_gettlv(tlvlist, 0x000b, 1)) msg = aim_tlv_getstr(tlvlist, 0x000b, 1); if ((userfunc = aim_callhandler(sess, fr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR))) ret = userfunc(sess, fr, code, msg); aim_tlvlist_free(&tlvlist); free(msg); return ret; }
/** * Read a TLV chain from a buffer. * * Reads and parses a series of TLV patterns from a data buffer; the * returned structure is manipulatable with the rest of the TLV * routines. When done with a TLV chain, aim_tlvlist_free() should * be called to free the dynamic substructures. * * XXX There should be a flag setable here to have the tlvlist contain * bstream references, so that at least the ->value portion of each * element doesn't need to be malloc/memcpy'd. This could prove to be * just as effecient as the in-place TLV parsing used in a couple places * in libfaim. * * @param bs Input bstream * @param num The max number of TLVs that will be read, or -1 if unlimited. * There are a number of places where you want to read in a tlvchain, * but the chain is not at the end of the SNAC, and the chain is * preceeded by the number of TLVs. So you can limit that with this. */ faim_internal aim_tlvlist_t *aim_tlvlist_readnum(aim_bstream_t *bs, fu16_t num) { aim_tlvlist_t *list = NULL, *cur; while ((aim_bstream_empty(bs) > 0) && (num != 0)) { fu16_t type, length; type = aimbs_get16(bs); length = aimbs_get16(bs); if (length > aim_bstream_empty(bs)) { aim_tlvlist_free(&list); return NULL; } cur = (aim_tlvlist_t *)malloc(sizeof(aim_tlvlist_t)); if (!cur) { aim_tlvlist_free(&list); return NULL; } memset(cur, 0, sizeof(aim_tlvlist_t)); cur->tlv = createtlv(type, length, NULL); if (!cur->tlv) { free(cur); aim_tlvlist_free(&list); return NULL; } if (cur->tlv->length > 0) { cur->tlv->value = aimbs_getraw(bs, length); if (!cur->tlv->value) { freetlv(&cur->tlv); free(cur); aim_tlvlist_free(&list); return NULL; } } if (num > 0) num--; cur->next = list; list = cur; } return list; }
/** * aim_readtlvchain_len - Read a TLV chain from a buffer. * @param bs Input bstream * @param len The max length in bytes that will be read. * There are a number of places where you want to read in a tlvchain, * but the chain is not at the end of the SNAC, and the chain is * preceeded by the length of the TLVs. So you can limit that with this. * * Reads and parses a series of TLV patterns from a data buffer; the * returned structure is manipulatable with the rest of the TLV * routines. When done with a TLV chain, aim_freetlvchain() should * be called to free the dynamic substructures. * * XXX There should be a flag setable here to have the tlvlist contain * bstream references, so that at least the ->value portion of each * element doesn't need to be malloc/memcpy'd. This could prove to be * just as effecient as the in-place TLV parsing used in a couple places * in libfaim. * */ faim_internal aim_tlvlist_t *aim_readtlvchain_len(aim_bstream_t *bs, fu16_t len) { aim_tlvlist_t *list = NULL, *cur; while ((aim_bstream_empty(bs) > 0) && (len > 0)) { fu16_t type, length; type = aimbs_get16(bs); length = aimbs_get16(bs); if (length > aim_bstream_empty(bs)) { aim_freetlvchain(&list); return NULL; } cur = (aim_tlvlist_t *)malloc(sizeof(aim_tlvlist_t)); if (!cur) { aim_freetlvchain(&list); return NULL; } memset(cur, 0, sizeof(aim_tlvlist_t)); cur->tlv = createtlv(); if (!cur->tlv) { free(cur); aim_freetlvchain(&list); return NULL; } cur->tlv->type = type; if ((cur->tlv->length = length)) { cur->tlv->value = aimbs_getraw(bs, length); if (!cur->tlv->value) { freetlv(&cur->tlv); free(cur); aim_freetlvchain(&list); return NULL; } } len -= aim_sizetlvchain(&cur); cur->next = list; list = cur; } return list; }
faim_internal fu16_t aimbs_getle16(aim_bstream_t *bs) { if (aim_bstream_empty(bs) < 2) return 0; /* XXX throw an exception */ bs->offset += 2; return aimutil_getle16(bs->data + bs->offset - 2); }
faim_internal fu32_t aimbs_getle32(aim_bstream_t *bs) { if (aim_bstream_empty(bs) < 4) return 0; /* XXX throw an exception */ bs->offset += 4; return aimutil_getle32(bs->data + bs->offset - 4); }
int aimbs_putle8(aim_bstream_t *bs, guint8 v) { if (aim_bstream_empty(bs) < 1) return 0; /* XXX throw an exception */ bs->offset += aimutil_putle8(bs->data + bs->offset, v); return 1; }
faim_internal int aimbs_putle16(aim_bstream_t *bs, fu16_t v) { if (aim_bstream_empty(bs) < 2) return 0; /* XXX throw an exception */ bs->offset += aimutil_putle16(bs->data + bs->offset, v); return 2; }
int aimbs_put16(aim_bstream_t *bs, guint16 v) { if (aim_bstream_empty(bs) < 2) return 0; /* XXX throw an exception */ bs->offset += aimutil_put16(bs->data + bs->offset, v); return 2; }
faim_internal int aimbs_putle32(aim_bstream_t *bs, fu32_t v) { if (aim_bstream_empty(bs) < 4) return 0; /* XXX throw an exception */ bs->offset += aimutil_putle32(bs->data + bs->offset, v); return 1; }
faim_internal fu8_t aimbs_getle8(aim_bstream_t *bs) { if (aim_bstream_empty(bs) < 1) return 0; /* XXX throw an exception */ bs->offset++; return aimutil_getle8(bs->data + bs->offset - 1); }
faim_internal int aim_bstream_advance(aim_bstream_t *bs, int n) { if (aim_bstream_empty(bs) < n) return 0; /* XXX throw an exception */ bs->offset += n; return n; }
guint16 aimbs_getle16(aim_bstream_t *bs) { if (aim_bstream_empty(bs) < 2) { return 0; /* XXX throw an exception */ } bs->offset += 2; return aimutil_getle16(bs->data + bs->offset - 2); }
guint32 aimbs_getle32(aim_bstream_t *bs) { if (aim_bstream_empty(bs) < 4) { return 0; /* XXX throw an exception */ } bs->offset += 4; return aimutil_getle32(bs->data + bs->offset - 4); }
int aimbs_putle32(aim_bstream_t *bs, guint32 v) { if (aim_bstream_empty(bs) < 4) { return 0; /* XXX throw an exception */ } bs->offset += aimutil_putle32(bs->data + bs->offset, v); return 1; }
faim_internal int aimbs_putraw(aim_bstream_t *bs, const fu8_t *v, int len) { if (aim_bstream_empty(bs) < len) return 0; /* XXX throw an exception */ memcpy(bs->data + bs->offset, v, len); bs->offset += len; return len; }
guint8 aimbs_getle8(aim_bstream_t *bs) { if (aim_bstream_empty(bs) < 1) { return 0; /* XXX throw an exception */ } bs->offset++; return aimutil_getle8(bs->data + bs->offset - 1); }
faim_internal int aimbs_getrawbuf(aim_bstream_t *bs, fu8_t *buf, int len) { if (aim_bstream_empty(bs) < len) return 0; memcpy(buf, bs->data + bs->offset, len); bs->offset += len; return len; }
int aimbs_putraw(aim_bstream_t *bs, const guint8 *v, int len) { if (aim_bstream_empty(bs) < len) { return 0; /* XXX throw an exception */ } memcpy(bs->data + bs->offset, v, len); bs->offset += len; return len; }
int aimbs_getrawbuf(aim_bstream_t *bs, guint8 *buf, int len) { if (aim_bstream_empty(bs) < len) { return 0; } memcpy(buf, bs->data + bs->offset, len); bs->offset += len; return len; }
static int consumesnac(aim_session_t *sess, aim_frame_t *rx) { aim_module_t *cur; aim_modsnac_t snac; if (aim_bstream_empty(&rx->data) < 10) return 0; snac.family = aimbs_get16(&rx->data); snac.subtype = aimbs_get16(&rx->data); snac.flags = aimbs_get16(&rx->data); snac.id = aimbs_get32(&rx->data); /* SNAC flags are apparently uniform across all SNACs, so we handle them here */ if (snac.flags & 0x0001) { /* * This means the SNAC will be followed by another SNAC with * related information. We don't need to do anything about * this here. */ } if (snac.flags & 0x8000) { /* * This packet contains the version of the family that this SNAC is * in. You get this when your SSI module is version 2 or higher. * For now we have no need for this, but you could always save * it as a part of aim_modnsac_t, or something. The format is... * 2 byte length of total mini-header (which is 6 bytes), then TLV * of type 0x0001, length 0x0002, value is the 2 byte version * number */ aim_bstream_advance(&rx->data, aimbs_get16(&rx->data)); } for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) { if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) && (cur->family != snac.family)) continue; if (cur->snachandler(sess, cur, rx, &snac, &rx->data)) return 1; } return 0; }
faim_internal int aim_parse_unknown(aim_session_t *sess, aim_frame_t *frame, ...) { int i; faimdprintf(sess, 1, "\nRecieved unknown packet:"); for (i = 0; aim_bstream_empty(&frame->data); i++) { if ((i % 8) == 0) faimdprintf(sess, 1, "\n\t"); faimdprintf(sess, 1, "0x%2x ", aimbs_get8(&frame->data)); } faimdprintf(sess, 1, "\n\n"); return 1; }
faim_internal int aim_putcap(aim_bstream_t *bs, fu32_t caps) { int i; if (!bs) return -EINVAL; for (i = 0; aim_bstream_empty(bs); i++) { if (aim_caps[i].flag == AIM_CAPS_LAST) break; if (caps & aim_caps[i].flag) aimbs_putraw(bs, aim_caps[i].data, 0x10); } return 0; }
static int userlistchange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { aim_userinfo_t *userinfo = NULL; aim_rxcallback_t userfunc; int curcount = 0, ret = 0; while (aim_bstream_empty(bs)) { curcount++; userinfo = realloc(userinfo, curcount * sizeof(aim_userinfo_t)); aim_extractuserinfo(sess, bs, &userinfo[curcount-1]); } if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, curcount, userinfo); free(userinfo); return ret; }
/** * aim_readtlvchain - Read a TLV chain from a buffer. * @buf: Input buffer * @maxlen: Length of input buffer * * Reads and parses a series of TLV patterns from a data buffer; the * returned structure is manipulatable with the rest of the TLV * routines. When done with a TLV chain, aim_freetlvchain() should * be called to free the dynamic substructures. * * XXX There should be a flag setable here to have the tlvlist contain * bstream references, so that at least the ->value portion of each * element doesn't need to be malloc/memcpy'd. This could prove to be * just as effecient as the in-place TLV parsing used in a couple places * in libfaim. * */ faim_internal aim_tlvlist_t *aim_readtlvchain(aim_bstream_t *bs) { aim_tlvlist_t *list = NULL, *cur; fu16_t type, length; while (aim_bstream_empty(bs)) { type = aimbs_get16(bs); length = aimbs_get16(bs); #if 0 /* temporarily disabled until I know if they're still doing it or not */ /* * Okay, so now AOL has decided that any TLV of * type 0x0013 can only be two bytes, despite * what the actual given length is. So here * we dump any invalid TLVs of that sort. Hopefully * theres no special cases to this special case. * - mid (30jun2000) */ if ((type == 0x0013) && (length != 0x0002)) length = 0x0002; #else if (0) ; #endif else { cur = (aim_tlvlist_t *)malloc(sizeof(aim_tlvlist_t)); memset(cur, 0, sizeof(aim_tlvlist_t)); cur->tlv = createtlv(); cur->tlv->type = type; if ((cur->tlv->length = length)) cur->tlv->value = aimbs_getraw(bs, length); cur->next = list; list = cur; } } return list; }
static int missedcall(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { int ret = 0; aim_rxcallback_t userfunc; guint16 channel, nummissed, reason; aim_userinfo_t userinfo; while (aim_bstream_empty(bs)) { channel = aimbs_get16(bs); aim_extractuserinfo(sess, bs, &userinfo); nummissed = aimbs_get16(bs); reason = aimbs_get16(bs); if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, channel, &userinfo, nummissed, reason); } return ret; }
/* * Should be generic enough to handle the errors for all groups. * */ static int generror(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { int ret = 0; int error = 0; aim_rxcallback_t userfunc; aim_snac_t *snac2; snac2 = aim_remsnac(sess, snac->id); if (aim_bstream_empty(bs)) error = aimbs_get16(bs); if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) ret = userfunc(sess, rx, error, snac2 ? snac2->data : NULL); if (snac2) g_free(snac2->data); g_free(snac2); return ret; }