#include /** * 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. * */ faim_export struct aim_tlvlist_t *aim_readtlvchain(u_char *buf, int maxlen) { int pos; struct aim_tlvlist_t *list; struct aim_tlvlist_t *cur; u_short type; u_short length; if (!buf) return NULL; list = NULL; pos = 0; while (pos < maxlen) { type = aimutil_get16(buf+pos); pos += 2; if (pos < maxlen) { length = aimutil_get16(buf+pos); pos += 2; if ((pos+length) <= maxlen) { /* * 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)) { printf("faim: skipping TLV t(0013) with invalid length (0x%04x)\n", length); length = 0x0002; } else { cur = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); memset(cur, 0x00, sizeof(struct aim_tlvlist_t)); cur->tlv = aim_createtlv(); cur->tlv->type = type; cur->tlv->length = length; if (length) { cur->tlv->value = (unsigned char *)malloc(length); memcpy(cur->tlv->value, buf+pos, length); } cur->next = list; list = cur; } pos += length; } } } return list; } /** * aim_freetlvchain - Free a TLV chain structure * @list: Chain to be freed * * Walks the list of TLVs in the passed TLV chain and * frees each one. Note that any references to this data * should be removed before calling this. * */ faim_export void aim_freetlvchain(struct aim_tlvlist_t **list) { struct aim_tlvlist_t *cur, *cur2; if (!list || !(*list)) return; cur = *list; while (cur) { aim_freetlv(&cur->tlv); cur2 = cur->next; free(cur); cur = cur2; } list = NULL; return; } /** * aim_counttlvchain - Count the number of TLVs in a chain * @list: Chain to be counted * * Returns the number of TLVs stored in the passed chain. * */ faim_export int aim_counttlvchain(struct aim_tlvlist_t **list) { struct aim_tlvlist_t *cur; int count = 0; if (!list || !(*list)) return 0; for (cur = *list; cur; cur = cur->next) count++; return count; } /** * aim_sizetlvchain - Count the number of bytes in a TLV chain * @list: Chain to be sized * * Returns the number of bytes that would be needed to * write the passed TLV chain to a data buffer. * */ faim_export int aim_sizetlvchain(struct aim_tlvlist_t **list) { struct aim_tlvlist_t *cur; int size = 0; if (!list || !(*list)) return 0; for (cur = *list; cur; cur = cur->next) size += (4 + cur->tlv->length); return size; } /** * aim_addtlvtochain_str - Add a string to a TLV chain * @list: Desination chain (%NULL pointer if empty) * @type: TLV type * @str: String to add * @len: Length of string to add (not including %NULL) * * Adds the passed string as a TLV element of the passed type * to the TLV chain. * */ faim_export int aim_addtlvtochain_str(struct aim_tlvlist_t **list, unsigned short type, char *str, int len) { struct aim_tlvlist_t *newtlv; struct aim_tlvlist_t *cur; if (!list) return 0; newtlv = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); memset(newtlv, 0x00, sizeof(struct aim_tlvlist_t)); newtlv->tlv = aim_createtlv(); newtlv->tlv->type = type; newtlv->tlv->length = len; newtlv->tlv->value = (unsigned char *)malloc(newtlv->tlv->length*sizeof(unsigned char)); memcpy(newtlv->tlv->value, str, newtlv->tlv->length); newtlv->next = NULL; if (*list == NULL) { *list = newtlv; } else if ((*list)->next == NULL) { (*list)->next = newtlv; } else { for(cur = *list; cur->next; cur = cur->next) ; cur->next = newtlv; } return newtlv->tlv->length; } /** * aim_addtlvtochain16 - Add a 16bit integer to a TLV chain * @list: Destination chain * @type: TLV type to add * @val: Value to add * * Adds a two-byte unsigned integer to a TLV chain. * */ faim_export int aim_addtlvtochain16(struct aim_tlvlist_t **list, unsigned short type, unsigned short val) { struct aim_tlvlist_t *newtl; struct aim_tlvlist_t *cur; if (!list) return 0; newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); memset(newtl, 0x00, sizeof(struct aim_tlvlist_t)); newtl->tlv = aim_createtlv(); newtl->tlv->type = type; newtl->tlv->length = 2; newtl->tlv->value = (unsigned char *)malloc(newtl->tlv->length*sizeof(unsigned char)); aimutil_put16(newtl->tlv->value, val); newtl->next = NULL; if (*list == NULL) { *list = newtl; } else if ((*list)->next == NULL) { (*list)->next = newtl; } else { for(cur = *list; cur->next; cur = cur->next) ; cur->next = newtl; } return 2; } /** * aim_addtlvtochain32 - Add a 32bit integer to a TLV chain * @list: Destination chain * @type: TLV type to add * @val: Value to add * * Adds a four-byte unsigned integer to a TLV chain. * */ faim_export int aim_addtlvtochain32(struct aim_tlvlist_t **list, unsigned short type, unsigned long val) { struct aim_tlvlist_t *newtl; struct aim_tlvlist_t *cur; if (!list) return 0; newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); memset(newtl, 0x00, sizeof(struct aim_tlvlist_t)); newtl->tlv = aim_createtlv(); newtl->tlv->type = type; newtl->tlv->length = 4; newtl->tlv->value = (unsigned char *)malloc(newtl->tlv->length*sizeof(unsigned char)); aimutil_put32(newtl->tlv->value, val); newtl->next = NULL; if (*list == NULL) { *list = newtl; } else if ((*list)->next == NULL) { (*list)->next = newtl; } else { for(cur = *list; cur->next; cur = cur->next) ; cur->next = newtl; } return 4; } /** * aim_addtlvtochain_caps - Add a capability block to a TLV chain * @list: Destination chain * @type: TLV type to add * @caps: Bitfield of capability flags to send * * Adds a block of capability blocks to a TLV chain. The bitfield * passed in should be a bitwise %OR of any of the %AIM_CAPS constants: * * %AIM_CAPS_BUDDYICON Supports Buddy Icons * * %AIM_CAPS_VOICE Supports Voice Chat * * %AIM_CAPS_IMIMAGE Supports DirectIM/IMImage * * %AIM_CAPS_CHAT Supports Chat * * %AIM_CAPS_GETFILE Supports Get File functions * * %AIM_CAPS_SENDFILE Supports Send File functions * */ faim_export int aim_addtlvtochain_caps(struct aim_tlvlist_t **list, unsigned short type, unsigned short caps) { unsigned char buf[128]; /* icky fixed length buffer */ struct aim_tlvlist_t *newtl; struct aim_tlvlist_t *cur; if(!list) return 0; newtl = (struct aim_tlvlist_t *)malloc(sizeof(struct aim_tlvlist_t)); memset(newtl, 0x00, sizeof(struct aim_tlvlist_t)); newtl->tlv = aim_createtlv(); newtl->tlv->type = type; newtl->tlv->length = aim_putcap(buf, sizeof(buf), caps); newtl->tlv->value = (unsigned char *)calloc(1, newtl->tlv->length); memcpy(newtl->tlv->value, buf, newtl->tlv->length); newtl->next = NULL; if (*list == NULL) { *list = newtl; } else if ((*list)->next == NULL) { (*list)->next = newtl; } else { for(cur = *list; cur->next; cur = cur->next) ; cur->next = newtl; } return newtl->tlv->length; } /** * aim_writetlvchain - Write a TLV chain into a data buffer. * @buf: Destination buffer * @buflen: Maximum number of bytes that will be written to buffer * @list: Source TLV chain * * Copies a TLV chain into a raw data buffer, writing only the number * of bytes specified. This operation does not free the chain; * aim_freetlvchain() must still be called to free up the memory used * by the chain structures. * */ faim_export int aim_writetlvchain(u_char *buf, int buflen, struct aim_tlvlist_t **list) { int goodbuflen = 0; int i = 0; struct aim_tlvlist_t *cur; if (!list || !buf || !buflen) return 0; /* do an initial run to test total length */ for (cur = *list; cur; cur = cur->next) { goodbuflen += 2 + 2; /* type + len */ goodbuflen += cur->tlv->length; } if (goodbuflen > buflen) return 0; /* not enough buffer */ /* do the real write-out */ for (cur = *list; cur; cur = cur->next) { i += aimutil_put16(buf+i, cur->tlv->type); i += aimutil_put16(buf+i, cur->tlv->length); memcpy(buf+i, cur->tlv->value, cur->tlv->length); i += cur->tlv->length; } return i; } /** * aim_gettlv - Grab the Nth TLV of type type in the TLV list list. * @list: Source chain * @type: Requested TLV type * @nth: Index of TLV of type to get * * Returns a pointer to an aim_tlv_t of the specified type; * %NULL on error. The @nth parameter is specified starting at %1. * In most cases, there will be no more than one TLV of any type * in a chain. * */ faim_export struct aim_tlv_t *aim_gettlv(struct aim_tlvlist_t *list, u_short type, int nth) { int i; struct aim_tlvlist_t *cur; i = 0; for (cur = list; cur != NULL; cur = cur->next) { if (cur && cur->tlv) { if (cur->tlv->type == type) i++; if (i >= nth) return cur->tlv; } } return NULL; } /** * aim_gettlv_str - Retrieve the Nth TLV in chain as a string. * @list: Source TLV chain * @type: TLV type to search for * @nth: Index of TLV to return * * Same as aim_gettlv(), except that the return value is a %NULL- * terminated string instead of an aim_tlv_t. This is a * dynamic buffer and must be freed by the caller. * */ faim_export char *aim_gettlv_str(struct aim_tlvlist_t *list, u_short type, int nth) { struct aim_tlv_t *tlv; char *newstr; if (!(tlv = aim_gettlv(list, type, nth))) return NULL; newstr = (char *) malloc(tlv->length + 1); memcpy(newstr, tlv->value, tlv->length); *(newstr + tlv->length) = '\0'; return newstr; } /** * aim_grabtlv - Grab a single TLV from a data buffer * @src: Source data buffer (must be at least 4 bytes long) * * Creates a TLV structure aim_tlv_t and returns it * filled with values from a buffer, possibly including a * dynamically allocated buffer for the value portion. * * Both the aim_tlv_t and the tlv->value pointer * must be freed by the caller if non-%NULL. * */ faim_export struct aim_tlv_t *aim_grabtlv(u_char *src) { struct aim_tlv_t *dest = NULL; dest = aim_createtlv(); dest->type = src[0] << 8; dest->type += src[1]; dest->length = src[2] << 8; dest->length += src[3]; dest->value = (u_char *) malloc(dest->length*sizeof(u_char)); memset(dest->value, 0, dest->length*sizeof(u_char)); memcpy(dest->value, &(src[4]), dest->length*sizeof(u_char)); return dest; } /** * aim_grabtlvstr - Grab a single TLV from a data buffer as string * @src: Source data buffer (must be at least 4 bytes long) * * Creates a TLV structure aim_tlv_t and returns it * filled with values from a buffer, possibly including a * dynamically allocated buffer for the value portion, which * is %NULL-terminated as a string. * * Both the aim_tlv_t and the tlv->value pointer * must be freed by the caller if non-%NULL. * */ faim_export struct aim_tlv_t *aim_grabtlvstr(u_char *src) { struct aim_tlv_t *dest = NULL; dest = aim_createtlv(); dest->type = src[0] << 8; dest->type += src[1]; dest->length = src[2] << 8; dest->length += src[3]; dest->value = (u_char *) malloc((dest->length+1)*sizeof(u_char)); memset(dest->value, 0, (dest->length+1)*sizeof(u_char)); memcpy(dest->value, &(src[4]), dest->length*sizeof(u_char)); dest->value[dest->length] = '\0'; return dest; } /** * aim_puttlv - Write a aim_tlv_t into a data buffer * @dest: Destination data buffer * @newtlv: Source TLV structure * * Writes out the passed TLV structure into the buffer. No bounds * checking is done on the output buffer. * * The passed aim_tlv_t is not freed. aim_freetlv() should * still be called by the caller to free the structure. * */ faim_export int aim_puttlv(u_char *dest, struct aim_tlv_t *newtlv) { int i=0; dest[i++] = newtlv->type >> 8; dest[i++] = newtlv->type & 0x00FF; dest[i++] = newtlv->length >> 8; dest[i++] = newtlv->length & 0x00FF; memcpy(&(dest[i]), newtlv->value, newtlv->length); i+=newtlv->length; return i; } /** * aim_createtlv - Generate an aim_tlv_t structure. * * Allocates an empty TLV structure and returns a pointer * to it; %NULL on error. * */ faim_export struct aim_tlv_t *aim_createtlv(void) { struct aim_tlv_t *newtlv; if (!(newtlv = (struct aim_tlv_t *)malloc(sizeof(struct aim_tlv_t)))) return NULL; memset(newtlv, 0, sizeof(struct aim_tlv_t)); return newtlv; } /** * aim_freetlv - Free a aim_tlv_t structure * @oldtlv: TLV to be destroyed * * Frees both the TLV structure and the value portion. * */ faim_export int aim_freetlv(struct aim_tlv_t **oldtlv) { if (!oldtlv) return -1; if (!*oldtlv) return -1; if ((*oldtlv)->value) free((*oldtlv)->value); free(*(oldtlv)); (*oldtlv) = NULL; return 0; } /** * aim_puttlv_16 - Write a two-byte TLV. * @buf: Destination buffer * @t: TLV type * @v: Value * * Writes a TLV with a two-byte integer value portion. * */ faim_export int aim_puttlv_16(u_char *buf, u_short t, u_short v) { int curbyte=0; curbyte += aimutil_put16(buf+curbyte, (u_short)(t&0xffff)); curbyte += aimutil_put16(buf+curbyte, (u_short)0x0002); curbyte += aimutil_put16(buf+curbyte, (u_short)(v&0xffff)); return curbyte; } /** * aim_puttlv_32 - Write a four-byte TLV. * @buf: Destination buffer * @t: TLV type * @v: Value * * Writes a TLV with a four-byte integer value portion. * */ faim_export int aim_puttlv_32(u_char *buf, u_short t, u_long v) { int curbyte=0; curbyte += aimutil_put16(buf+curbyte, (u_short)(t&0xffff)); curbyte += aimutil_put16(buf+curbyte, (u_short)0x0004); curbyte += aimutil_put32(buf+curbyte, (u_long)(v&0xffffffff)); return curbyte; } /** * aim_puttlv_str - Write a string TLV. * @buf: Destination buffer * @t: TLV type * @l: Length of string * @v: String to write * * Writes a TLV with a string value portion. (Only the first @l * bytes of the passed string will be written, which should not * include the terminating NULL.) * */ faim_export int aim_puttlv_str(u_char *buf, u_short t, int l, char *v) { int curbyte; curbyte = 0; curbyte += aimutil_put16(buf+curbyte, (u_short)(t&0xffff)); curbyte += aimutil_put16(buf+curbyte, (u_short)(l&0xffff)); if (v) memcpy(buf+curbyte, (unsigned char *)v, l); curbyte += l; return curbyte; }