diff --git a/epan/reassemble.c b/epan/reassemble.c index 4224c39664..9a19f8566d 100644 --- a/epan/reassemble.c +++ b/epan/reassemble.c @@ -64,6 +64,33 @@ static int fragment_init_count = 200; fd_i->next=(fd); \ } +/* copy a fragment key to heap store to insert in the hash */ +static void *fragment_key_copy(const void *k) +{ + const fragment_key* key = (const fragment_key*) k; + fragment_key *new_key = g_mem_chunk_alloc(fragment_key_chunk); + + COPY_ADDRESS(&new_key->src, &key->src); + COPY_ADDRESS(&new_key->dst, &key->dst); + new_key->id = key->id; + return new_key; +} + +/* copy a dcerpc fragment key to heap store to insert in the hash */ +static void *dcerpc_fragment_key_copy(const void *k) +{ + const dcerpc_fragment_key* key = (const dcerpc_fragment_key*) k; + dcerpc_fragment_key *new_key = se_alloc(sizeof(dcerpc_fragment_key)); + + COPY_ADDRESS(&new_key->src, &key->src); + COPY_ADDRESS(&new_key->dst, &key->dst); + new_key->id = key->id; + new_key->act_id = key->act_id; + + return new_key; +} + + static gint fragment_equal(gconstpointer k1, gconstpointer k2) { @@ -445,6 +472,7 @@ fragment_set_tot_len(packet_info *pinfo, guint32 id, GHashTable *fragment_table, if(fd_head){ fd_head->datalen = tot_len; + fd_head->flags |= FD_DATALEN_SET; } return; @@ -624,7 +652,7 @@ fragment_add_work(fragment_data *fd_head, tvbuff_t *tvb, int offset, } fd_i->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS); } - fd_head->flags ^= FD_DEFRAGMENTED|FD_PARTIAL_REASSEMBLY; + fd_head->flags &= ~(FD_DEFRAGMENTED|FD_PARTIAL_REASSEMBLY|FD_DATALEN_SET); fd_head->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS); fd_head->datalen=0; fd_head->reassembled_in=0; @@ -634,7 +662,7 @@ fragment_add_work(fragment_data *fd_head, tvbuff_t *tvb, int offset, /* * This is the tail fragment in the sequence. */ - if (fd_head->datalen) { + if (fd_head->flags & FD_DATALEN_SET) { /* ok we have already seen other tails for this packet * it might be a duplicate. */ @@ -650,6 +678,7 @@ fragment_add_work(fragment_data *fd_head, tvbuff_t *tvb, int offset, * length of the packet */ fd_head->datalen = fd->offset + fd->len; + fd_head->flags |= FD_DATALEN_SET; } } @@ -696,7 +725,7 @@ fragment_add_work(fragment_data *fd_head, tvbuff_t *tvb, int offset, LINK_FRAG(fd_head,fd); - if( !(fd_head->datalen) ){ + if( !(fd_head->flags & FD_DATALEN_SET) ){ /* if we dont know the datalen, there are still missing * packets. Cheaper than the check below. */ @@ -1066,12 +1095,43 @@ fragment_add_check(tvbuff_t *tvb, int offset, packet_info *pinfo, static gboolean fragment_add_seq_work(fragment_data *fd_head, tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 frag_number, - guint32 frag_data_len, gboolean more_frags) + guint32 frag_data_len, gboolean more_frags, + guint32 flags) { fragment_data *fd; fragment_data *fd_i; fragment_data *last_fd; guint32 max, dfpos, size; + void *old_data; + + /* if the partial reassembly flag has been set, and we are extending + * the pdu, un-reassemble the pdu. This means pointing old fds to malloc'ed data. + */ + if(fd_head->flags & FD_DEFRAGMENTED && frag_number >= fd_head->datalen && + fd_head->flags & FD_PARTIAL_REASSEMBLY){ + guint32 lastdfpos = 0; + dfpos = 0; + for(fd_i=fd_head->next; fd_i; fd_i=fd_i->next){ + if( !fd_i->data ) { + if( fd_i->flags & FD_OVERLAP ) { + /* this is a duplicate of the previous + * fragment. */ + fd_i->data = fd_head->data + lastdfpos; + } else { + fd_i->data = fd_head->data + dfpos; + lastdfpos = dfpos; + dfpos += fd_i->len; + } + fd_i->flags |= FD_NOT_MALLOCED; + } + fd_i->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS); + } + fd_head->flags &= ~(FD_DEFRAGMENTED|FD_PARTIAL_REASSEMBLY|FD_DATALEN_SET); + fd_head->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS); + fd_head->datalen=0; + fd_head->reassembled_in=0; + } + /* create new fd describing this fragment */ fd = g_mem_chunk_alloc(fragment_data_chunk); @@ -1086,7 +1146,7 @@ fragment_add_seq_work(fragment_data *fd_head, tvbuff_t *tvb, int offset, /* * This is the tail fragment in the sequence. */ - if (fd_head->datalen) { + if (fd_head->flags&FD_DATALEN_SET) { /* ok we have already seen other tails for this packet * it might be a duplicate. */ @@ -1103,6 +1163,7 @@ fragment_add_seq_work(fragment_data *fd_head, tvbuff_t *tvb, int offset, * the length of the packet!) */ fd_head->datalen = fd->offset; + fd_head->flags |= FD_DATALEN_SET; } } @@ -1209,7 +1270,7 @@ fragment_add_seq_work(fragment_data *fd_head, tvbuff_t *tvb, int offset, LINK_FRAG(fd_head,fd); - if( !(fd_head->datalen) ){ + if( !(fd_head->flags & FD_DATALEN_SET) ){ /* if we dont know the sequence number of the last fragment, * there are definitely still missing packets. Cheaper than * the check below. @@ -1253,6 +1314,8 @@ fragment_add_seq_work(fragment_data *fd_head, tvbuff_t *tvb, int offset, } last_fd=fd_i; } + /* store old data in case the fd_i->data pointers refer to it */ + old_data=fd_head->data; fd_head->data = g_malloc(size); fd_head->len = size; /* record size for caller */ @@ -1268,7 +1331,7 @@ fragment_add_seq_work(fragment_data *fd_head, tvbuff_t *tvb, int offset, /* duplicate/retransmission/overlap */ fd_i->flags |= FD_OVERLAP; fd_head->flags |= FD_OVERLAP; - if( (last_fd->len!=fd_i->datalen) + if( (last_fd->len!=fd_i->len) || memcmp(last_fd->data, fd_i->data, last_fd->len) ){ fd->flags |= FD_OVERLAPCONFLICT; fd_head->flags |= FD_OVERLAPCONFLICT; @@ -1280,12 +1343,16 @@ fragment_add_seq_work(fragment_data *fd_head, tvbuff_t *tvb, int offset, /* we have defragmented the pdu, now free all fragments*/ for (fd_i=fd_head->next;fd_i;fd_i=fd_i->next) { - if(fd_i->data){ - g_free(fd_i->data); - fd_i->data=NULL; - } + if( fd_i->flags & FD_NOT_MALLOCED ) + fd_i->flags &= ~FD_NOT_MALLOCED; + else + g_free(fd_i->data); + fd_i->data=NULL; } + if( old_data ) + g_free(old_data); + /* mark this packet as defragmented. allows us to skip any trailing fragments */ fd_head->flags |= FD_DEFRAGMENTED; @@ -1311,15 +1378,50 @@ fragment_add_seq(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, GHashTable *fragment_table, guint32 frag_number, guint32 frag_data_len, gboolean more_frags) { - fragment_key key, *new_key; - fragment_data *fd_head; + fragment_key key; /* create key to search hash with */ key.src = pinfo->src; key.dst = pinfo->dst; key.id = id; - fd_head = g_hash_table_lookup(fragment_table, &key); + return fragment_add_seq_key(tvb, offset, pinfo, + &key, fragment_key_copy, + fragment_table, frag_number, + frag_data_len, more_frags, 0); +} + + +fragment_data * +fragment_add_dcerpc_dg(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, + void *v_act_id, + GHashTable *fragment_table, guint32 frag_number, + guint32 frag_data_len, gboolean more_frags) +{ + e_uuid_t *act_id = (e_uuid_t *)v_act_id; + dcerpc_fragment_key key; + + /* create key to search hash with */ + key.src = pinfo->src; + key.dst = pinfo->dst; + key.id = id; + key.act_id = *act_id; + + return fragment_add_seq_key(tvb, offset, pinfo, + &key, dcerpc_fragment_key_copy, + fragment_table, frag_number, + frag_data_len, more_frags, 0); +} + +fragment_data * +fragment_add_seq_key(tvbuff_t *tvb, int offset, packet_info *pinfo, + void *key, fragment_key_copier key_copier, + GHashTable *fragment_table, guint32 frag_number, + guint32 frag_data_len, gboolean more_frags, + guint32 flags) +{ + fragment_data *fd_head; + fd_head = g_hash_table_lookup(fragment_table, key); /* have we already seen this frame ?*/ if (pinfo->fd->flags.visited) { @@ -1348,21 +1450,89 @@ fragment_add_seq(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, fd_head->data=NULL; fd_head->reassembled_in=0; + if((flags & (REASSEMBLE_FLAGS_NO_FRAG_NUMBER|REASSEMBLE_FLAGS_802_11_HACK)) + && !more_frags) { + /* + * This is the last fragment for this packet, and + * is the only one we've seen. + * + * Either we don't have sequence numbers, in which + * case we assume this is the first fragment for + * this packet, or we're doing special 802.11 + * processing, in which case we assume it's one + * of those reassembled packets with a non-zero + * fragment number (see packet-80211.c); just + * return a pointer to the head of the list; + * fragment_add_seq_check will then add it to the table + * of reassembled packets. + */ + fd_head->reassembled_in=pinfo->fd->num; + return fd_head; + } + /* * We're going to use the key to insert the fragment, - * so allocate a structure for it, and copy the - * addresses, allocating new buffers for the address - * data. + * so copy it to a long-term store. */ - new_key = g_mem_chunk_alloc(fragment_key_chunk); - COPY_ADDRESS(&new_key->src, &key.src); - COPY_ADDRESS(&new_key->dst, &key.dst); - new_key->id = key.id; - g_hash_table_insert(fragment_table, new_key, fd_head); + if(key_copier != NULL) + key = key_copier(key); + g_hash_table_insert(fragment_table, key, fd_head); + + /* + * If we weren't given an initial fragment number, + * make it 0. + */ + if (flags & REASSEMBLE_FLAGS_NO_FRAG_NUMBER) + frag_number = 0; + } else { + if (flags & REASSEMBLE_FLAGS_NO_FRAG_NUMBER) { + fragment_data *fd; + /* + * If we weren't given an initial fragment number, + * use the next expected fragment number as the fragment + * number for this fragment. + */ + for (fd = fd_head; fd != NULL; fd = fd->next) { + if (fd->next == NULL) + frag_number = fd->offset + 1; + } + } } + /* + * XXX I've copied this over from the old separate + * fragment_add_seq_check_work, but I'm not convinced it's doing the + * right thing -- rav + * + * If we don't have all the data that is in this fragment, + * then we can't, and don't, do reassembly on it. + * + * If it's the first frame, handle it as an unfragmented packet. + * Otherwise, just handle it as a fragment. + * + * If "more_frags" isn't set, we get rid of the entry in the + * hash table for this reassembly, as we don't need it any more. + */ + if ((flags & REASSEMBLE_FLAGS_CHECK_DATA_PRESENT) && + !tvb_bytes_exist(tvb, offset, frag_data_len)) { + if (!more_frags) { + gpointer orig_key; + /* + * Remove this from the table of in-progress + * reassemblies, and free up any memory used for + * it in that table. + */ + if (g_hash_table_lookup_extended(fragment_table, key, + &orig_key, NULL)) { + fragment_unhash(fragment_table, (fragment_key *)orig_key); + } + } + fd_head -> flags |= FD_DATA_NOT_PRESENT; + return frag_number == 0 ? fd_head : NULL; + } + if (fragment_add_seq_work(fd_head, tvb, offset, pinfo, - frag_number, frag_data_len, more_frags)) { + frag_number, frag_data_len, more_frags, flags)) { /* * Reassembly is complete. */ @@ -1375,79 +1545,6 @@ fragment_add_seq(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, } } -fragment_data * -fragment_add_dcerpc_dg(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, - void *v_act_id, - GHashTable *fragment_table, guint32 frag_number, - guint32 frag_data_len, gboolean more_frags) -{ - dcerpc_fragment_key key, *new_key; - fragment_data *fd_head; - e_uuid_t *act_id = (e_uuid_t *)v_act_id; - - /* create key to search hash with */ - key.src = pinfo->src; - key.dst = pinfo->dst; - key.id = id; - key.act_id = *act_id; - - fd_head = g_hash_table_lookup(fragment_table, &key); - - /* have we already seen this frame ?*/ - if (pinfo->fd->flags.visited) { - if (fd_head != NULL && fd_head->flags & FD_DEFRAGMENTED) { - return fd_head; - } else { - return NULL; - } - } - - if (fd_head==NULL){ - /* not found, this must be the first snooped fragment for this - * packet. Create list-head. - */ - fd_head=g_mem_chunk_alloc(fragment_data_chunk); - - /* head/first structure in list only holds no other data than - * 'datalen' then we don't have to change the head of the list - * even if we want to keep it sorted - */ - fd_head->next=NULL; - fd_head->datalen=0; - fd_head->offset=0; - fd_head->len=0; - fd_head->flags=FD_BLOCKSEQUENCE; - fd_head->data=NULL; - fd_head->reassembled_in=0; - - /* - * We're going to use the key to insert the fragment, - * so allocate a structure for it, and copy the - * addresses, allocating new buffers for the address - * data. - */ - new_key = se_alloc(sizeof(dcerpc_fragment_key)); - COPY_ADDRESS(&new_key->src, &key.src); - COPY_ADDRESS(&new_key->dst, &key.dst); - new_key->id = key.id; - new_key->act_id = key.act_id; - g_hash_table_insert(fragment_table, new_key, fd_head); - } - - if (fragment_add_seq_work(fd_head, tvb, offset, pinfo, - frag_number, frag_data_len, more_frags)) { - /* - * Reassembly is complete. - */ - return fd_head; - } else { - /* - * Reassembly isn't complete. - */ - return NULL; - } -} - /* * This does the work for "fragment_add_seq_check()" and * "fragment_add_seq_next()". @@ -1468,10 +1565,10 @@ fragment_add_dcerpc_dg(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id * * This fragment is added to the linked list of fragments for this packet. * - * If "more_frags" is false and "frag_802_11_hack" (as the name implies, - * a special hack for 802.11) or "no_frag_number" (implying messages must be - * in order since there's no sequence number) are true, then this (one - * element) list is returned. + * If "more_frags" is false and REASSEMBLE_FLAGS_802_11_HACK (as the name + * implies, a special hack for 802.11) or REASSEMBLE_FLAGS_NO_FRAG_NUMBER + * (implying messages must be in order since there's no sequence number) are + * set in "flags", then this (one element) list is returned. * * If, after processing this fragment, we have all the fragments, * "fragment_add_seq_check_work()" removes that from the fragment hash @@ -1487,12 +1584,11 @@ fragment_add_seq_check_work(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, GHashTable *fragment_table, GHashTable *reassembled_table, guint32 frag_number, guint32 frag_data_len, gboolean more_frags, - gboolean no_frag_number, gboolean frag_802_11_hack) + guint32 flags) { reassembled_key reass_key; - fragment_key key, *new_key, *old_key; - gpointer orig_key, value; - fragment_data *fd_head, *fd; + fragment_key key; + fragment_data *fd_head; /* * Have we already seen this frame? @@ -1509,122 +1605,34 @@ fragment_add_seq_check_work(tvbuff_t *tvb, int offset, packet_info *pinfo, key.dst = pinfo->dst; key.id = id; - if (!g_hash_table_lookup_extended(fragment_table, &key, - &orig_key, &value)) { - /* not found, this must be the first snooped fragment for this - * packet. Create list-head. - */ - fd_head=g_mem_chunk_alloc(fragment_data_chunk); + fd_head = fragment_add_seq_key(tvb, offset, pinfo, + &key, fragment_key_copy, + fragment_table, frag_number, + frag_data_len, more_frags, flags|REASSEMBLE_FLAGS_CHECK_DATA_PRESENT); + if (fd_head) { + gpointer orig_key; - /* head/first structure in list only holds no other data than - * 'datalen' then we don't have to change the head of the list - * even if we want to keep it sorted - */ - fd_head->next=NULL; - fd_head->datalen=0; - fd_head->offset=0; - fd_head->len=0; - fd_head->flags=FD_BLOCKSEQUENCE; - fd_head->data=NULL; - fd_head->reassembled_in=0; - - if ((no_frag_number || frag_802_11_hack) && !more_frags) { - /* - * This is the last fragment for this packet, and - * is the only one we've seen. - * - * Either we don't have sequence numbers, in which - * case we assume this is the first fragment for - * this packet, or we're doing special 802.11 - * processing, in which case we assume it's one - * of those reassembled packets with a non-zero - * fragment number (see packet-80211.c); just - * add the fragment to the table of reassembled - * packets, and return a pointer to the head of - * the list. - */ - fragment_reassembled(fd_head, pinfo, - reassembled_table, id); + if(fd_head->flags & FD_DATA_NOT_PRESENT) { + /* this is the first fragment of a datagram with + * truncated fragments. Don't move it to the + * reassembled table. */ return fd_head; } - - /* - * We're going to use the key to insert the fragment, - * so allocate a structure for it, and copy the - * addresses, allocating new buffers for the address - * data. - */ - new_key = g_mem_chunk_alloc(fragment_key_chunk); - COPY_ADDRESS(&new_key->src, &key.src); - COPY_ADDRESS(&new_key->dst, &key.dst); - new_key->id = key.id; - g_hash_table_insert(fragment_table, new_key, fd_head); - - orig_key = new_key; /* for unhashing it later */ - - /* - * If we weren't given an initial fragment number, - * make it 0. - */ - if (no_frag_number) - frag_number = 0; - } else { - /* - * We found it. - */ - fd_head = value; - - /* - * If we weren't given an initial fragment number, - * use the next expected fragment number as the fragment - * number for this fragment. - */ - if (no_frag_number) { - for (fd = fd_head; fd != NULL; fd = fd->next) { - if (fd->next == NULL) - frag_number = fd->offset + 1; - } - } - } - - /* - * If we don't have all the data that is in this fragment, - * then we can't, and don't, do reassembly on it. - * - * If it's the first frame, handle it as an unfragmented packet. - * Otherwise, just handle it as a fragment. - * - * If "more_frags" isn't set, we get rid of the entry in the - * hash table for this reassembly, as we don't need it any more. - */ - if (!tvb_bytes_exist(tvb, offset, frag_data_len)) { - if (!more_frags) { - /* - * Remove this from the table of in-progress - * reassemblies, and free up any memory used for - * it in that table. - */ - old_key = orig_key; - fragment_unhash(fragment_table, old_key); - } - return frag_number == 0 ? fd_head : NULL; - } - - if (fragment_add_seq_work(fd_head, tvb, offset, pinfo, - frag_number, frag_data_len, more_frags)) { + /* * Reassembly is complete. * Remove this from the table of in-progress * reassemblies, add it to the table of * reassembled packets, and return it. */ - - /* - * Remove this from the table of in-progress reassemblies, - * and free up any memory used for it in that table. - */ - old_key = orig_key; - fragment_unhash(fragment_table, old_key); + if (g_hash_table_lookup_extended(fragment_table, &key, + &orig_key, NULL)) { + /* + * Remove this from the table of in-progress reassemblies, + * and free up any memory used for it in that table. + */ + fragment_unhash(fragment_table, (fragment_key *)orig_key); + } /* * Add this item to the table of reassembled packets. @@ -1647,7 +1655,7 @@ fragment_add_seq_check(tvbuff_t *tvb, int offset, packet_info *pinfo, { return fragment_add_seq_check_work(tvb, offset, pinfo, id, fragment_table, reassembled_table, frag_number, frag_data_len, - more_frags, FALSE, FALSE); + more_frags, 0); } fragment_data * @@ -1658,7 +1666,7 @@ fragment_add_seq_802_11(tvbuff_t *tvb, int offset, packet_info *pinfo, { return fragment_add_seq_check_work(tvb, offset, pinfo, id, fragment_table, reassembled_table, frag_number, frag_data_len, - more_frags, FALSE, TRUE); + more_frags, REASSEMBLE_FLAGS_802_11_HACK); } fragment_data * @@ -1669,7 +1677,7 @@ fragment_add_seq_next(tvbuff_t *tvb, int offset, packet_info *pinfo, { return fragment_add_seq_check_work(tvb, offset, pinfo, id, fragment_table, reassembled_table, 0, frag_data_len, - more_frags, TRUE, FALSE); + more_frags, REASSEMBLE_FLAGS_NO_FRAG_NUMBER); } /* @@ -1925,3 +1933,11 @@ show_fragment_seq_tree(fragment_data *fd_head, const fragment_items *fit, return show_fragment_errs_in_col(fd_head, fit, pinfo); } + +/* + * Local Variables: + * c-basic-offset: 8 + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff --git a/epan/reassemble.h b/epan/reassemble.h index 923a60f645..1197cbb7ab 100644 --- a/epan/reassemble.h +++ b/epan/reassemble.h @@ -51,12 +51,28 @@ into the defragmented packet */ #define FD_BLOCKSEQUENCE 0x0100 +/* if REASSEMBLE_FLAGS_CHECK_DATA_PRESENT is set, and the first fragment is + * incomplete, this flag is set in the flags word on the fd_head returned. + * + * It's all a fudge to preserve historical behaviour. + */ +#define FD_DATA_NOT_PRESENT 0x0200 + +/* This flag is set in the to denote that datalen has ben set to a valid value. + * It's implied by FD_DEFRAGMENTED (we must know the total length of the + * datagram if we have defragmented it...) + */ +#define FD_DATALEN_SET 0x0400 + typedef struct _fragment_data { struct _fragment_data *next; guint32 frame; guint32 offset; guint32 len; - guint32 datalen; /*Only valid in first item of list */ + guint32 datalen; /* Only valid in first item of list and when + * flags&FD_DATALEN_SET is set; + * number of bytes or (if flags&FD_BLOCKSEQUENCE set) + * segments in the datagram */ guint32 reassembled_in; /* frame where this PDU was reassembled, only valid in the first item of the list and when FD_DEFRAGMENTED is set*/ @@ -64,6 +80,26 @@ typedef struct _fragment_data { unsigned char *data; } fragment_data; + +/* + * Flags for fragment_add_seq_* + */ + +/* we don't have any sequence numbers - fragments are assumed to appear in + * order */ +#define REASSEMBLE_FLAGS_NO_FRAG_NUMBER 0x0001 + +/* a special fudge for the 802.11 dissector */ +#define REASSEMBLE_FLAGS_802_11_HACK 0x0002 + +/* causes fragment_add_seq_key to check that all the fragment data is present + * in the tvb, and if not, do something a bit odd. */ +#define REASSEMBLE_FLAGS_CHECK_DATA_PRESENT 0x0004 + +/* a function for copying hash keys */ +typedef void *(*fragment_key_copier)(const void *key); + + /* * Initialize a fragment table. */ @@ -105,10 +141,51 @@ extern fragment_data *fragment_add_check(tvbuff_t *tvb, int offset, /* same as fragment_add() but this one assumes frag_number is a block sequence number. note that frag_number is 0 for the first fragment. */ + +/* + * These functions add a new fragment to the fragment hash table, + * assuming that frag_number is a block sequence number (starting from zero for + * the first fragment of each datagram). + * + * If this is the first fragment seen for this datagram, a new + * "fragment_data" structure is allocated to refer to the reassembled + * packet, and: + * + * if "more_frags" is false, and either we have no sequence numbers, or + * are using the 802.11 hack, it is assumed that this is the only fragment + * in the datagram. The structure is not added to the hash + * table, and not given any fragments to refer to, but is just returned. + * + * In this latter case reassembly wasn't done (since there was only one + * fragment in the packet); dissectors can check the 'next' pointer on the + * returned list to see if this case was hit or not. + * + * Otherwise, this fragment is just added to the linked list of fragments + * for this packet; the fragment_data is also added to the fragment hash if + * necessary. + * + * If this packet completes assembly, these functions return the head of the + * fragment data; otherwise, they return null. + */ + +/* "key" should be an arbitrary key used for indexing the fragment hash; + * "key_copier" is called to copy the key to a more appropriate store before + * inserting a new entry to the hash. + */ +extern fragment_data * +fragment_add_seq_key(tvbuff_t *tvb, int offset, packet_info *pinfo, + void *key, fragment_key_copier key_copier, + GHashTable *fragment_table, guint32 frag_number, + guint32 frag_data_len, gboolean more_frags, + guint32 flags); + +/* a wrapper for fragment_add_seq_key - uses a key of source, dest and frame number */ extern fragment_data *fragment_add_seq(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, GHashTable *fragment_table, guint32 frag_number, guint32 frag_data_len, gboolean more_frags); +/* another wrapper for fragment_add_seq_key - uses a key of source, dest, frame + * number and act_id */ extern fragment_data * fragment_add_dcerpc_dg(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id, void *act_id, @@ -116,44 +193,12 @@ fragment_add_dcerpc_dg(tvbuff_t *tvb, int offset, packet_info *pinfo, guint32 id guint32 frag_data_len, gboolean more_frags); /* - * These functions add a new fragment to the fragment hash table. - * If this is the first fragment seen for this datagram, a new - * "fragment_data" structure is allocated to refer to the reassembled, - * packet, and: - * - * in "fragment_add_seq_802_11()", if "more_frags" is false, - * the structure is not added to the hash table, and not given - * any fragments to refer to, but is just returned; - * - * otherwise, this fragment is added to the linked list of fragments - * for this packet, and the "fragment_data" structure is put into - * the hash table. - * - * Otherwise, this fragment is just added to the linked list of fragments - * for this packet. + * These routines extend fragment_add_seq_key to use a "reassembled_table". * * If, after processing this fragment, we have all the fragments, they * remove that from the fragment hash table if necessary and add it * to the table of reassembled fragments, and return a pointer to the * head of the fragment list. - * - * If this is the first fragment we've seen, and "more_frags" is false, - * the fragment is added to the list. "fragment_add_seq_check()" will - * return NULL (waiting for the earlier sequence numbers) while - * "fragment_add_seq_802_11()" (a special hack for the 802.11 dissector) and - * "fragment_add_seq_next()" will return a pointer to the (one element) list. - * In this latter case reassembly wasn't done (since there was only one - * fragment in the packet); dissectors can check the 'next' pointer on the - * returned list to see if this case was hit or not. - * - * Otherwise, they return NULL. - * - * "fragment_add_seq_check()" and "fragment_add_seq_802_11()" assume - * frag_number is a block sequence number. - * The bsn for the first block is 0. - * - * "fragment_add_seq_next()" is for protocols with no sequence number, - * and assumes fragments always appear in sequence. */ extern fragment_data * fragment_add_seq_check(tvbuff_t *tvb, int offset, packet_info *pinfo, diff --git a/epan/reassemble_test.c b/epan/reassemble_test.c new file mode 100644 index 0000000000..8f48946684 --- /dev/null +++ b/epan/reassemble_test.c @@ -0,0 +1,1057 @@ +/* Standalone program to test functionality of reassemble.h API + * + * These aren't particularly complete - they just test a few corners of + * functionality which I was interested in. In particular, they only test the + * fragment_add_seq_* (ie, FD_BLOCKSEQUENCE) family of routines. However, + * hopefully they will inspire people to write additional tests, and provide a + * useful basis on which to do so. + * + * $Id$ + * + * Copyright (c) 2007 MX Telecom Ltd. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#include + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#define ASSERT(b) do_test((b),"Assertion failed at line %i: %s\n", __LINE__, #b) +#define ASSERT_EQ(exp,act) do_test((exp)==(act),"Assertion failed at line %i: %s==%s (%i==%i)\n", __LINE__, #exp, #act, exp, act) +#define ASSERT_NE(exp,act) do_test((exp)!=(act),"Assertion failed at line %i: %s!=%s (%i!=%i)\n", __LINE__, #exp, #act, exp, act) + +int failure = 0; + +void do_test(int condition, char *format, ...) +{ + va_list ap; + + if(condition) + return; + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + failure = 1; + + /* many of the tests assume this routine doesn't return on failure; if we + * do, it may provide more information, but may cause a segfault. Uncomment + * this line if you wish. + */ + exit(1); +} + +#define DATA_LEN 256 + +char *data; +tvbuff_t *tvb; +packet_info pinfo; + +/* fragment_table maps from datagram ids to head of fragment_data list + reassembled_table maps from to head of + fragment_data list */ +GHashTable *fragment_table = NULL, *reassembled_table = NULL; + +/********************************************************************************** + * + * fragment_add_seq + * + *********************************************************************************/ + +/* Simple test case for fragment_add_seq. + * Adds three fragments (out of order, with one for a different datagram in between), + * and checks that they are reassembled correctly. + */ +static void test_simple_fragment_add_seq(void) +{ + fragment_data *fd_head, *fdh0; + + printf("Starting test test_simple_fragment_add_seq\n"); + + pinfo.fd->num = 1; + fd_head=fragment_add_seq(tvb, 10, &pinfo, 12, fragment_table, + 0, 50, TRUE); + + ASSERT_EQ(1,g_hash_table_size(fragment_table)); + ASSERT_EQ(NULL,fd_head); + + /* adding the same fragment again should do nothing, even with different + * offset etc */ + pinfo.fd->flags.visited = 1; + fd_head=fragment_add_seq(tvb, 5, &pinfo, 12, fragment_table, + 0, 60, TRUE); + ASSERT_EQ(1,g_hash_table_size(fragment_table)); + ASSERT_EQ(NULL,fd_head); + + /* start another pdu (just to confuse things) */ + pinfo.fd->flags.visited = 0; + pinfo.fd->num = 2; + fd_head=fragment_add_seq(tvb, 15, &pinfo, 13, fragment_table, + 0, 60, TRUE); + ASSERT_EQ(2,g_hash_table_size(fragment_table)); + ASSERT_EQ(NULL,fd_head); + + /* now we add the terminal fragment of the first datagram */ + pinfo.fd->num = 3; + fd_head=fragment_add_seq(tvb, 5, &pinfo, 12, fragment_table, + 2, 60, FALSE); + + /* we haven't got all the fragments yet ... */ + ASSERT_EQ(2,g_hash_table_size(fragment_table)); + ASSERT_EQ(NULL,fd_head); + + /* finally, add the missing fragment */ + pinfo.fd->num = 4; + fd_head=fragment_add_seq(tvb, 15, &pinfo, 12, fragment_table, + 1, 60, TRUE); + + ASSERT_EQ(2,g_hash_table_size(fragment_table)); + ASSERT_NE(NULL,fd_head); + + /* check the contents of the structure */ + ASSERT_EQ(0,fd_head->frame); /* unused */ + ASSERT_EQ(0,fd_head->offset); /* unused */ + ASSERT_EQ(170,fd_head->len); /* the length of data we have */ + ASSERT_EQ(2,fd_head->datalen); /* seqno of the last fragment we have */ + ASSERT_EQ(4,fd_head->reassembled_in); + ASSERT_EQ(FD_DEFRAGMENTED|FD_BLOCKSEQUENCE|FD_DATALEN_SET,fd_head->flags); + ASSERT_NE(NULL,fd_head->data); + ASSERT_NE(NULL,fd_head->next); + + ASSERT_EQ(1,fd_head->next->frame); + ASSERT_EQ(0,fd_head->next->offset); /* seqno */ + ASSERT_EQ(50,fd_head->next->len); /* segment length */ + ASSERT_EQ(0,fd_head->next->flags); + ASSERT_EQ(NULL,fd_head->next->data); + ASSERT_NE(NULL,fd_head->next->next); + + ASSERT_EQ(4,fd_head->next->next->frame); + ASSERT_EQ(1,fd_head->next->next->offset); /* seqno */ + ASSERT_EQ(60,fd_head->next->next->len); /* segment length */ + ASSERT_EQ(0,fd_head->next->next->flags); + ASSERT_EQ(NULL,fd_head->next->next->data); + ASSERT_NE(NULL,fd_head->next->next->next); + + ASSERT_EQ(3,fd_head->next->next->next->frame); + ASSERT_EQ(2,fd_head->next->next->next->offset); /* seqno */ + ASSERT_EQ(60,fd_head->next->next->next->len); /* segment length */ + ASSERT_EQ(0,fd_head->next->next->next->flags); + ASSERT_EQ(NULL,fd_head->next->next->next->data); + ASSERT_EQ(NULL,fd_head->next->next->next->next); + + /* test the actual reassembly */ + ASSERT(!memcmp(fd_head->data,data+10,50)); + ASSERT(!memcmp(fd_head->data+50,data+15,60)); + ASSERT(!memcmp(fd_head->data+110,data+5,60)); + + /* what happens if we revisit the packets now? */ + fdh0 = fd_head; + pinfo.fd->flags.visited = 1; + pinfo.fd->num = 1; + fd_head=fragment_add_seq(tvb, 10, &pinfo, 12, fragment_table, + 0, 50, TRUE); + /* + * this api relies on the caller to check fd_head -> reassembled_in + * + * Redoing all the tests seems like overkill - just check the pointer + */ + ASSERT_EQ(fdh0,fd_head); + + pinfo.fd->num = 3; + fd_head=fragment_add_seq(tvb, 5, &pinfo, 12, fragment_table, + 2, 60, FALSE); + ASSERT_EQ(fdh0,fd_head); + + pinfo.fd->num = 4; + fd_head=fragment_add_seq(tvb, 15, &pinfo, 12, fragment_table, + 1, 60, TRUE); + ASSERT_EQ(fdh0,fd_head); +} + +/* XXX ought to have some tests for overlapping fragments */ + +/* This tests the functionality of fragment_set_partial_reassembly for + * FD_BLOCKSEQUENCE reassembly. + * + * We add a sequence of fragments thus: + * seqno frame offset len (initial) more_frags + * ----- ----- ------ --- -------------------- + * 0 1 10 50 false + * 1 2 0 40 true + * 1 3 0 40 true (a duplicate fragment) + * 2 4 20 100 false + * 3 5 0 40 false + */ +static void test_fragment_add_seq_partial_reassembly(void) +{ + fragment_data *fd_head, *fd; + + printf("Starting test test_fragment_add_seq_partial_reassembly\n"); + + /* generally it's probably fair to assume that we will be called with + * more_frags=FALSE. + */ + pinfo.fd->num = 1; + fd_head=fragment_add_seq(tvb, 10, &pinfo, 12, fragment_table, + 0, 50, FALSE); + + ASSERT_EQ(1,g_hash_table_size(fragment_table)); + ASSERT_NE(NULL,fd_head); + + /* check the contents of the structure */ + ASSERT_EQ(0,fd_head->frame); /* unused */ + ASSERT_EQ(0,fd_head->offset); /* unused */ + ASSERT_EQ(50,fd_head->len); /* the length of data we have */ + ASSERT_EQ(0,fd_head->datalen); /* seqno of the last fragment we have */ + ASSERT_EQ(1,fd_head->reassembled_in); + ASSERT_EQ(FD_DEFRAGMENTED|FD_BLOCKSEQUENCE|FD_DATALEN_SET,fd_head->flags); + ASSERT_NE(NULL,fd_head->data); + ASSERT_NE(NULL,fd_head->next); + + ASSERT_EQ(1,fd_head->next->frame); + ASSERT_EQ(0,fd_head->next->offset); /* seqno */ + ASSERT_EQ(50,fd_head->next->len); /* segment length */ + ASSERT_EQ(0,fd_head->next->flags); + ASSERT_EQ(NULL,fd_head->next->data); + ASSERT_EQ(NULL,fd_head->next->next); + + /* test the actual reassembly */ + ASSERT(!memcmp(fd_head->data,data+10,50)); + + /* now we announce that the reassembly wasn't complete after all. */ + fragment_set_partial_reassembly(&pinfo,12,fragment_table); + + /* and add another segment. To mix things up slightly (and so that we can + * check on the state of things), we're going to set the more_frags flag + * here + */ + pinfo.fd->num = 2; + fd_head=fragment_add_seq(tvb, 0, &pinfo, 12, fragment_table, + 1, 40, TRUE); + + ASSERT_EQ(1,g_hash_table_size(fragment_table)); + ASSERT_EQ(NULL,fd_head); + + fd_head=fragment_get(&pinfo,12,fragment_table); + ASSERT_NE(NULL,fd_head); + + /* check the contents of the structure */ + ASSERT_EQ(0,fd_head->frame); /* unused */ + ASSERT_EQ(0,fd_head->offset); /* unused */ + /* ASSERT_EQ(50,fd_head->len); the length of data we have */ + ASSERT_EQ(0,fd_head->datalen); /* seqno of the last fragment we have */ + ASSERT_EQ(0,fd_head->reassembled_in); + ASSERT_EQ(FD_BLOCKSEQUENCE,fd_head->flags); + ASSERT_NE(NULL,fd_head->data); + ASSERT_NE(NULL,fd_head->next); + + fd=fd_head->next; + ASSERT_EQ(1,fd->frame); + ASSERT_EQ(0,fd->offset); /* seqno */ + ASSERT_EQ(50,fd->len); /* segment length */ + ASSERT_EQ(FD_NOT_MALLOCED,fd->flags); + ASSERT_EQ(fd_head->data,fd->data); + ASSERT_NE(NULL,fd->next); + + fd=fd->next; + ASSERT_EQ(2,fd->frame); + ASSERT_EQ(1,fd->offset); /* seqno */ + ASSERT_EQ(40,fd->len); /* segment length */ + ASSERT_EQ(0,fd->flags); + ASSERT_NE(NULL,fd->data); + ASSERT_EQ(NULL,fd->next); + + /* Another copy of the second segment. + */ + pinfo.fd->num = 3; + fd_head=fragment_add_seq(tvb, 0, &pinfo, 12, fragment_table, + 1, 40, TRUE); + + ASSERT_EQ(1,g_hash_table_size(fragment_table)); + ASSERT_EQ(NULL,fd_head); + fd_head=fragment_get(&pinfo,12,fragment_table); + ASSERT_NE(NULL,fd_head); + ASSERT_EQ(0,fd_head->frame); /* unused */ + ASSERT_EQ(0,fd_head->offset); /* unused */ + /* ASSERT_EQ(50,fd_head->len); the length of data we have */ + ASSERT_EQ(0,fd_head->datalen); /* seqno of the last fragment we have */ + ASSERT_EQ(0,fd_head->reassembled_in); + ASSERT_EQ(FD_BLOCKSEQUENCE,fd_head->flags); + ASSERT_NE(NULL,fd_head->data); + ASSERT_NE(NULL,fd_head->next); + + fd=fd_head->next; + ASSERT_EQ(1,fd->frame); + ASSERT_EQ(0,fd->offset); /* seqno */ + ASSERT_EQ(50,fd->len); /* segment length */ + ASSERT_EQ(FD_NOT_MALLOCED,fd->flags); + ASSERT_EQ(fd_head->data,fd->data); + ASSERT_NE(NULL,fd->next); + + fd=fd->next; + ASSERT_EQ(2,fd->frame); + ASSERT_EQ(1,fd->offset); /* seqno */ + ASSERT_EQ(40,fd->len); /* segment length */ + ASSERT_EQ(0,fd->flags); + ASSERT_NE(NULL,fd->data); + ASSERT_NE(NULL,fd->next); + + fd=fd->next; + ASSERT_EQ(3,fd->frame); + ASSERT_EQ(1,fd->offset); /* seqno */ + ASSERT_EQ(40,fd->len); /* segment length */ + ASSERT_EQ(0,fd->flags); + ASSERT_NE(NULL,fd->data); + ASSERT_EQ(NULL,fd->next); + + + + /* have another go at wrapping things up */ + pinfo.fd->num = 4; + fd_head=fragment_add_seq(tvb, 20, &pinfo, 12, fragment_table, + 2, 100, FALSE); + + ASSERT_EQ(1,g_hash_table_size(fragment_table)); + ASSERT_NE(NULL,fd_head); + + /* check the contents of the structure */ + ASSERT_EQ(0,fd_head->frame); /* unused */ + ASSERT_EQ(0,fd_head->offset); /* unused */ + ASSERT_EQ(190,fd_head->len); /* the length of data we have */ + ASSERT_EQ(2,fd_head->datalen); /* seqno of the last fragment we have */ + ASSERT_EQ(4,fd_head->reassembled_in); + ASSERT_EQ(FD_DEFRAGMENTED|FD_BLOCKSEQUENCE|FD_DATALEN_SET|FD_OVERLAP,fd_head->flags); + ASSERT_NE(NULL,fd_head->data); + ASSERT_NE(NULL,fd_head->next); + + fd=fd_head->next; + ASSERT_EQ(1,fd->frame); + ASSERT_EQ(0,fd->offset); /* seqno */ + ASSERT_EQ(50,fd->len); /* segment length */ + ASSERT_EQ(0,fd->flags); + ASSERT_EQ(NULL,fd->data); + ASSERT_NE(NULL,fd->next); + + fd=fd->next; + ASSERT_EQ(2,fd->frame); + ASSERT_EQ(1,fd->offset); /* seqno */ + ASSERT_EQ(40,fd->len); /* segment length */ + ASSERT_EQ(0,fd->flags); + ASSERT_EQ(NULL,fd->data); + ASSERT_NE(NULL,fd->next); + + fd=fd->next; + ASSERT_EQ(3,fd->frame); + ASSERT_EQ(1,fd->offset); /* seqno */ + ASSERT_EQ(40,fd->len); /* segment length */ + ASSERT_EQ(FD_OVERLAP,fd->flags); + ASSERT_EQ(NULL,fd->data); + ASSERT_NE(NULL,fd->next); + + fd=fd->next; + ASSERT_EQ(4,fd->frame); + ASSERT_EQ(2,fd->offset); /* seqno */ + ASSERT_EQ(100,fd->len); /* segment length */ + ASSERT_EQ(0,fd->flags); + ASSERT_EQ(NULL,fd->data); + ASSERT_EQ(NULL,fd->next); + + /* test the actual reassembly */ + ASSERT(!memcmp(fd_head->data,data+10,50)); + ASSERT(!memcmp(fd_head->data+50,data,40)); + ASSERT(!memcmp(fd_head->data+90,data+20,100)); + + + /* do it again (this time it is more complicated, with an overlap in the + * reassembly) */ + + fragment_set_partial_reassembly(&pinfo,12,fragment_table); + + pinfo.fd->num = 5; + fd_head=fragment_add_seq(tvb, 0, &pinfo, 12, fragment_table, + 3, 40, FALSE); + + fd_head=fragment_get(&pinfo,12,fragment_table); + ASSERT_NE(NULL,fd_head); + ASSERT_EQ(0,fd_head->frame); /* unused */ + ASSERT_EQ(0,fd_head->offset); /* unused */ + ASSERT_EQ(230,fd_head->len); /* the length of data we have */ + ASSERT_EQ(3,fd_head->datalen); /* seqno of the last fragment we have */ + ASSERT_EQ(5,fd_head->reassembled_in); + ASSERT_EQ(FD_DEFRAGMENTED|FD_BLOCKSEQUENCE|FD_DATALEN_SET|FD_OVERLAP,fd_head->flags); + ASSERT_NE(NULL,fd_head->data); + ASSERT_NE(NULL,fd_head->next); + + fd=fd_head->next; + ASSERT_EQ(1,fd->frame); + ASSERT_EQ(0,fd->offset); /* seqno */ + ASSERT_EQ(50,fd->len); /* segment length */ + ASSERT_EQ(0,fd->flags); + ASSERT_EQ(NULL,fd->data); + ASSERT_NE(NULL,fd->next); + + fd=fd->next; + ASSERT_EQ(2,fd->frame); + ASSERT_EQ(1,fd->offset); /* seqno */ + ASSERT_EQ(40,fd->len); /* segment length */ + ASSERT_EQ(0,fd->flags); + ASSERT_EQ(NULL,fd->data); + ASSERT_NE(NULL,fd->next); + + fd=fd->next; + ASSERT_EQ(3,fd->frame); + ASSERT_EQ(1,fd->offset); /* seqno */ + ASSERT_EQ(40,fd->len); /* segment length */ + ASSERT_EQ(FD_OVERLAP,fd->flags); + ASSERT_EQ(NULL,fd->data); + ASSERT_NE(NULL,fd->next); + + fd=fd->next; + ASSERT_EQ(4,fd->frame); + ASSERT_EQ(2,fd->offset); /* seqno */ + ASSERT_EQ(100,fd->len); /* segment length */ + ASSERT_EQ(0,fd->flags); + ASSERT_EQ(NULL,fd->data); + ASSERT_NE(NULL,fd->next); + + fd=fd->next; + ASSERT_EQ(5,fd->frame); + ASSERT_EQ(3,fd->offset); /* seqno */ + ASSERT_EQ(40,fd->len); /* segment length */ + ASSERT_EQ(0,fd->flags); + ASSERT_EQ(NULL,fd->data); + ASSERT_EQ(NULL,fd->next); + + /* test the actual reassembly */ + ASSERT(!memcmp(fd_head->data,data+10,50)); + ASSERT(!memcmp(fd_head->data+50,data,40)); + ASSERT(!memcmp(fd_head->data+90,data+20,100)); + ASSERT(!memcmp(fd_head->data+190,data,40)); +} + +/********************************************************************************** + * + * fragment_add_dcerpc_dg + * + *********************************************************************************/ + +/* This can afford to be reasonably minimal, as it's just the same logic with a + * different hash key to fragment_add_seq + */ +static void test_fragment_add_dcerpc_dg(void) +{ + e_uuid_t act_id = {1,2,3,{4,5,6,7,8,9,10,11}}; + + fragment_data *fd_head, *fdh0; + GHashTable *fragment_table = NULL; + + printf("Starting test test_fragment_add_dcerpc_dg\n"); + + /* we need our own fragment table */ + dcerpc_fragment_table_init(&fragment_table); + fd_head=fragment_add_dcerpc_dg(tvb, 10, &pinfo, 12, &act_id, fragment_table, + 0, 50, TRUE); + + ASSERT_EQ(1,g_hash_table_size(fragment_table)); + ASSERT_EQ(NULL,fd_head); + + /* start another pdu (just to confuse things) */ + pinfo.fd->num = 2; + fd_head=fragment_add_dcerpc_dg(tvb, 15, &pinfo, 13, &act_id, fragment_table, + 0, 60, TRUE); + ASSERT_EQ(2,g_hash_table_size(fragment_table)); + ASSERT_EQ(NULL,fd_head); + + /* another pdu, with the same fragment_id, but a different act_id, to the + * first one */ + pinfo.fd->num = 3; + act_id.Data1=2; + fd_head=fragment_add_dcerpc_dg(tvb, 15, &pinfo, 12, &act_id, fragment_table, + 0, 60, TRUE); + ASSERT_EQ(3,g_hash_table_size(fragment_table)); + ASSERT_EQ(NULL,fd_head); + act_id.Data1=1; + + /* now we add the terminal fragment of the first datagram */ + pinfo.fd->num = 4; + fd_head=fragment_add_dcerpc_dg(tvb, 5, &pinfo, 12, &act_id, fragment_table, + 1, 60, FALSE); + + ASSERT_EQ(3,g_hash_table_size(fragment_table)); + ASSERT_NE(NULL,fd_head); + + /* check the contents of the structure */ + ASSERT_EQ(0,fd_head->frame); /* unused */ + ASSERT_EQ(0,fd_head->offset); /* unused */ + ASSERT_EQ(110,fd_head->len); /* the length of data we have */ + ASSERT_EQ(1,fd_head->datalen); /* seqno of the last fragment we have */ + ASSERT_EQ(4,fd_head->reassembled_in); + ASSERT_EQ(FD_DEFRAGMENTED|FD_BLOCKSEQUENCE|FD_DATALEN_SET,fd_head->flags); + ASSERT_NE(NULL,fd_head->data); + ASSERT_NE(NULL,fd_head->next); + + /* test the actual reassembly */ + ASSERT(!memcmp(fd_head->data,data+10,50)); + ASSERT(!memcmp(fd_head->data+50,data+5,60)); + + /* what happens if we revisit the packets now? */ + fdh0 = fd_head; + pinfo.fd->flags.visited = 1; + pinfo.fd->num = 1; + fd_head=fragment_add_dcerpc_dg(tvb, 10, &pinfo, 12, &act_id, fragment_table, + 0, 50, TRUE); + /* + * this api relies on the caller to check fd_head -> reassembled_in + * + * Redoing all the tests seems like overkill - just check the pointer + */ + ASSERT_EQ(fdh0,fd_head); +} + +/********************************************************************************** + * + * fragment_add_seq_check + * + *********************************************************************************/ + + +/* This routine is used for both fragment_add_seq_802_11 and + * fragment_add_seq_check. + * + * Adds a couple of out-of-order fragments and checks their reassembly. + */ +static void test_fragment_add_seq_check_work( + fragment_data *(*fn)(tvbuff_t *, int, packet_info *, guint32, GHashTable *, + GHashTable *, guint32, guint32, gboolean)) +{ + fragment_data *fd_head; + + pinfo.fd -> num = 1; + fd_head=fn(tvb, 10, &pinfo, 12, fragment_table, + reassembled_table, 0, 50, TRUE); + + ASSERT_EQ(1,g_hash_table_size(fragment_table)); + ASSERT_EQ(0,g_hash_table_size(reassembled_table)); + ASSERT_EQ(NULL,fd_head); + + /* start another pdu (just to confuse things) */ + pinfo.fd->num = 2; + fd_head=fn(tvb, 15, &pinfo, 13, fragment_table, + reassembled_table, 0, 60, TRUE); + ASSERT_EQ(2,g_hash_table_size(fragment_table)); + ASSERT_EQ(0,g_hash_table_size(reassembled_table)); + ASSERT_EQ(NULL,fd_head); + + /* add the terminal fragment of the first datagram */ + pinfo.fd->num = 3; + fd_head=fn(tvb, 5, &pinfo, 12, fragment_table, + reassembled_table, 2, 60, FALSE); + + /* we haven't got all the fragments yet ... */ + ASSERT_EQ(2,g_hash_table_size(fragment_table)); + ASSERT_EQ(0,g_hash_table_size(reassembled_table)); + ASSERT_EQ(NULL,fd_head); + + /* finally, add the missing fragment */ + pinfo.fd->num = 4; + fd_head=fn(tvb, 15, &pinfo, 12, fragment_table, + reassembled_table, 1, 60, TRUE); + + ASSERT_EQ(1,g_hash_table_size(fragment_table)); + ASSERT_EQ(3,g_hash_table_size(reassembled_table)); + ASSERT_NE(NULL,fd_head); + + /* check the contents of the structure */ + ASSERT_EQ(0,fd_head->frame); /* unused */ + ASSERT_EQ(0,fd_head->offset); /* unused */ + ASSERT_EQ(170,fd_head->len); /* the length of data we have */ + ASSERT_EQ(2,fd_head->datalen); /* seqno of the last fragment we have */ + ASSERT_EQ(4,fd_head->reassembled_in); + ASSERT_EQ(FD_DEFRAGMENTED|FD_BLOCKSEQUENCE|FD_DATALEN_SET,fd_head->flags); + ASSERT_NE(NULL,fd_head->data); + ASSERT_NE(NULL,fd_head->next); + + ASSERT_EQ(1,fd_head->next->frame); + ASSERT_EQ(0,fd_head->next->offset); /* seqno */ + ASSERT_EQ(50,fd_head->next->len); /* segment length */ + ASSERT_EQ(0,fd_head->next->flags); + ASSERT_EQ(NULL,fd_head->next->data); + ASSERT_NE(NULL,fd_head->next->next); + + ASSERT_EQ(4,fd_head->next->next->frame); + ASSERT_EQ(1,fd_head->next->next->offset); /* seqno */ + ASSERT_EQ(60,fd_head->next->next->len); /* segment length */ + ASSERT_EQ(0,fd_head->next->next->flags); + ASSERT_EQ(NULL,fd_head->next->next->data); + ASSERT_NE(NULL,fd_head->next->next->next); + + ASSERT_EQ(3,fd_head->next->next->next->frame); + ASSERT_EQ(2,fd_head->next->next->next->offset); /* seqno */ + ASSERT_EQ(60,fd_head->next->next->next->len); /* segment length */ + ASSERT_EQ(0,fd_head->next->next->next->flags); + ASSERT_EQ(NULL,fd_head->next->next->next->data); + ASSERT_EQ(NULL,fd_head->next->next->next->next); + + /* test the actual reassembly */ + ASSERT(!memcmp(fd_head->data,data+10,50)); + ASSERT(!memcmp(fd_head->data+50,data+15,60)); + ASSERT(!memcmp(fd_head->data+110,data+5,60)); +} + +/* Simple test case for fragment_add_seq_check + */ +static void test_fragment_add_seq_check(void) +{ + printf("Starting test test_fragment_add_seq_check\n"); + + test_fragment_add_seq_check_work(fragment_add_seq_check); +} + + +/* This tests the case that the 802.11 hack does something different for: when + * the terminal segment in a fragmented datagram arrives first. + */ +static void test_fragment_add_seq_check_1(void) +{ + fragment_data *fd_head; + + printf("Starting test test_fragment_add_seq_check_1\n"); + + pinfo.fd->num = 1; + fd_head=fragment_add_seq_check(tvb, 10, &pinfo, 12, fragment_table, + reassembled_table, 1, 50, FALSE); + + ASSERT_EQ(1,g_hash_table_size(fragment_table)); + ASSERT_EQ(0,g_hash_table_size(reassembled_table)); + ASSERT_EQ(NULL,fd_head); + + /* Now add the missing segment */ + pinfo.fd->num = 2; + fd_head=fragment_add_seq_check(tvb, 5, &pinfo, 12, fragment_table, + reassembled_table, 0, 60, TRUE); + + ASSERT_EQ(0,g_hash_table_size(fragment_table)); + ASSERT_EQ(2,g_hash_table_size(reassembled_table)); + ASSERT_NE(NULL,fd_head); + + /* check the contents of the structure */ + ASSERT_EQ(0,fd_head->frame); /* unused */ + ASSERT_EQ(0,fd_head->offset); /* unused */ + ASSERT_EQ(110,fd_head->len); /* the length of data we have */ + ASSERT_EQ(1,fd_head->datalen); /* seqno of the last fragment we have */ + ASSERT_EQ(2,fd_head->reassembled_in); + ASSERT_EQ(FD_DEFRAGMENTED|FD_BLOCKSEQUENCE|FD_DATALEN_SET,fd_head->flags); + ASSERT_NE(NULL,fd_head->data); + ASSERT_NE(NULL,fd_head->next); + + ASSERT_EQ(2,fd_head->next->frame); + ASSERT_EQ(0,fd_head->next->offset); /* seqno */ + ASSERT_EQ(60,fd_head->next->len); /* segment length */ + ASSERT_EQ(0,fd_head->next->flags); + ASSERT_EQ(NULL,fd_head->next->data); + ASSERT_NE(NULL,fd_head->next->next); + + ASSERT_EQ(1,fd_head->next->next->frame); + ASSERT_EQ(1,fd_head->next->next->offset); /* seqno */ + ASSERT_EQ(50,fd_head->next->next->len); /* segment length */ + ASSERT_EQ(0,fd_head->next->next->flags); + ASSERT_EQ(NULL,fd_head->next->next->data); + ASSERT_EQ(NULL,fd_head->next->next->next); + + /* test the actual reassembly */ + ASSERT(!memcmp(fd_head->data,data+5,60)); + ASSERT(!memcmp(fd_head->data+60,data+10,50)); +} + +/********************************************************************************** + * + * fragment_add_seq_802_11 + * + *********************************************************************************/ + +/* Tests the 802.11 hack. + */ +static void test_fragment_add_seq_802_11_0(void) +{ + fragment_data *fd_head; + + printf("Starting test test_fragment_add_seq_802_11_0\n"); + + /* the 802.11 hack is that some non-fragmented datagrams have non-zero + * fragment_number; test for this. */ + + pinfo.fd->num = 1; + fd_head=fragment_add_seq_802_11(tvb, 10, &pinfo, 12, fragment_table, + reassembled_table, 10, 50, FALSE); + + ASSERT_EQ(0,g_hash_table_size(fragment_table)); + ASSERT_EQ(1,g_hash_table_size(reassembled_table)); + ASSERT_NE(NULL,fd_head); + + /* check the contents of the structure */ + ASSERT_EQ(0,fd_head->frame); /* unused */ + ASSERT_EQ(0,fd_head->offset); /* unused */ + ASSERT_EQ(0,fd_head->len); /* unused */ + ASSERT_EQ(0,fd_head->datalen); /* unused */ + ASSERT_EQ(1,fd_head->reassembled_in); + ASSERT_EQ(FD_DEFRAGMENTED|FD_BLOCKSEQUENCE,fd_head->flags); + ASSERT_EQ(NULL,fd_head->data); + ASSERT_EQ(NULL,fd_head->next); +} + +/* Reuse the fragment_add_seq_check testcases */ +static void test_fragment_add_seq_802_11_1(void) +{ + printf("Starting test test_fragment_add_seq_802_11_1\n"); + test_fragment_add_seq_check_work(fragment_add_seq_802_11); +} + +/********************************************************************************** + * + * fragment_add_seq_next + * + *********************************************************************************/ + +/* Simple test case for fragment_add_seq_next. + * Adds a couple of fragments (with one for a different datagram in between), + * and checks that they are reassembled correctly. + */ +static void test_simple_fragment_add_seq_next(void) +{ + fragment_data *fd_head; + + printf("Starting test test_simple_fragment_add_seq_next\n"); + + pinfo.fd->num = 1; + fd_head=fragment_add_seq_next(tvb, 10, &pinfo, 12, fragment_table, + reassembled_table, 50, TRUE); + + ASSERT_EQ(1,g_hash_table_size(fragment_table)); + ASSERT_EQ(0,g_hash_table_size(reassembled_table)); + ASSERT_EQ(NULL,fd_head); + + /* adding the same fragment again should do nothing, even with different + * offset etc */ + pinfo.fd->flags.visited = 1; + fd_head=fragment_add_seq_next(tvb, 5, &pinfo, 12, fragment_table, + reassembled_table, 60, TRUE); + ASSERT_EQ(1,g_hash_table_size(fragment_table)); + ASSERT_EQ(0,g_hash_table_size(reassembled_table)); + ASSERT_EQ(NULL,fd_head); + + /* start another pdu (just to confuse things) */ + pinfo.fd->flags.visited = 0; + pinfo.fd->num = 2; + fd_head=fragment_add_seq_next(tvb, 15, &pinfo, 13, fragment_table, + reassembled_table, 60, TRUE); + ASSERT_EQ(2,g_hash_table_size(fragment_table)); + ASSERT_EQ(0,g_hash_table_size(reassembled_table)); + ASSERT_EQ(NULL,fd_head); + + + /* now we add the terminal fragment of the first datagram */ + pinfo.fd->num = 3; + fd_head=fragment_add_seq_next(tvb, 5, &pinfo, 12, fragment_table, + reassembled_table, 60, FALSE); + + ASSERT_EQ(1,g_hash_table_size(fragment_table)); + ASSERT_EQ(2,g_hash_table_size(reassembled_table)); + ASSERT_NE(NULL,fd_head); + + /* check the contents of the structure */ + ASSERT_EQ(0,fd_head->frame); /* unused */ + ASSERT_EQ(0,fd_head->offset); /* unused */ + ASSERT_EQ(110,fd_head->len); /* the length of data we have */ + ASSERT_EQ(1,fd_head->datalen); /* seqno of the last fragment we have */ + ASSERT_EQ(3,fd_head->reassembled_in); + ASSERT_EQ(FD_DEFRAGMENTED|FD_BLOCKSEQUENCE|FD_DATALEN_SET,fd_head->flags); + ASSERT_NE(NULL,fd_head->data); + ASSERT_NE(NULL,fd_head->next); + + ASSERT_EQ(1,fd_head->next->frame); + ASSERT_EQ(0,fd_head->next->offset); /* seqno */ + ASSERT_EQ(50,fd_head->next->len); /* segment length */ + ASSERT_EQ(0,fd_head->next->flags); + ASSERT_EQ(NULL,fd_head->next->data); + ASSERT_NE(NULL,fd_head->next->next); + + ASSERT_EQ(3,fd_head->next->next->frame); + ASSERT_EQ(1,fd_head->next->next->offset); /* seqno */ + ASSERT_EQ(60,fd_head->next->next->len); /* segment length */ + ASSERT_EQ(0,fd_head->next->next->flags); + ASSERT_EQ(NULL,fd_head->next->next->data); + ASSERT_EQ(NULL,fd_head->next->next->next); + + /* test the actual reassembly */ + ASSERT(!memcmp(fd_head->data,data+10,50)); + ASSERT(!memcmp(fd_head->data+50,data+5,60)); +} + + +/* This tests the case where some data is missing from one of the fragments. + * It should prevent reassembly. + */ +static void test_missing_data_fragment_add_seq_next(void) +{ + fragment_data *fd_head; + + printf("Starting test test_missing_data_fragment_add_seq_next\n"); + + /* attempt to add a fragment which is longer than the data available */ + pinfo.fd->num = 1; + fd_head=fragment_add_seq_next(tvb, 10, &pinfo, 12, fragment_table, + reassembled_table, DATA_LEN-9, TRUE); + + ASSERT_EQ(1,g_hash_table_size(fragment_table)); + ASSERT_EQ(0,g_hash_table_size(reassembled_table)); + ASSERT_NE(NULL,fd_head); + + /* check the contents of the structure. Reassembly failed so everything + * should be null (meaning, just use the original tvb) */ + ASSERT_EQ(0,fd_head->frame); /* unused */ + ASSERT_EQ(0,fd_head->offset); /* unused */ + ASSERT_EQ(0,fd_head->len); /* the length of data we have */ + ASSERT_EQ(0,fd_head->datalen); /* seqno of the last fragment we have */ + ASSERT_EQ(0,fd_head->reassembled_in); + ASSERT_EQ(FD_BLOCKSEQUENCE,fd_head->flags & 0x1ff); + ASSERT_EQ(NULL,fd_head->data); + ASSERT_EQ(NULL,fd_head->next); + + /* add another fragment (with all data present) */ + pinfo.fd->num = 4; + fd_head=fragment_add_seq_next(tvb, 5, &pinfo, 12, fragment_table, + reassembled_table, 60, FALSE); + + /* XXX: it's not clear that this is the right result; however it's what the + * code does... + */ + ASSERT_EQ(1,g_hash_table_size(fragment_table)); + ASSERT_EQ(0,g_hash_table_size(reassembled_table)); + ASSERT_EQ(NULL,fd_head); + + + /* check what happens when we revisit the packets */ + pinfo.fd->flags.visited = TRUE; + pinfo.fd->num = 1; + + fd_head=fragment_add_seq_next(tvb, 10, &pinfo, 12, fragment_table, + reassembled_table, DATA_LEN-9, TRUE); + + /* We just look in the reassembled_table for this packet. It never got put + * there, so this always returns null. + * + * That's crazy, because it means that the subdissector will see the data + * exactly once - on the first pass through the capture (well, assuming it + * doesn't bother to check fd_head->reassembled_in); however, that's + * what the code does... + */ + ASSERT_EQ(1,g_hash_table_size(fragment_table)); + ASSERT_EQ(0,g_hash_table_size(reassembled_table)); + ASSERT_EQ(NULL,fd_head); + + pinfo.fd->num = 4; + fd_head=fragment_add_seq_next(tvb, 5, &pinfo, 12, fragment_table, + reassembled_table, 60, FALSE); + ASSERT_EQ(1,g_hash_table_size(fragment_table)); + ASSERT_EQ(0,g_hash_table_size(reassembled_table)); + ASSERT_EQ(NULL,fd_head); +} + + +/* + * we're going to do something similar now, but this time it is the second + * fragment which has something missing. + */ +static void test_missing_data_fragment_add_seq_next_2(void) +{ + fragment_data *fd_head; + + printf("Starting test test_missing_data_fragment_add_seq_next_2\n"); + + pinfo.fd->num = 11; + fd_head=fragment_add_seq_next(tvb, 10, &pinfo, 24, fragment_table, + reassembled_table, 50, TRUE); + + ASSERT_EQ(1,g_hash_table_size(fragment_table)); + ASSERT_EQ(0,g_hash_table_size(reassembled_table)); + ASSERT_EQ(NULL,fd_head); + + pinfo.fd->num = 12; + fd_head=fragment_add_seq_next(tvb, 5, &pinfo, 24, fragment_table, + reassembled_table, DATA_LEN-4, FALSE); + + /* XXX: again, i'm really dubious about this. Surely this should return all + * the data we had, for a best-effort attempt at dissecting it? + * And it ought to go into the reassembled table? + */ + ASSERT_EQ(0,g_hash_table_size(fragment_table)); + ASSERT_EQ(0,g_hash_table_size(reassembled_table)); + ASSERT_EQ(NULL,fd_head); + + /* check what happens when we revisit the packets */ + pinfo.fd->flags.visited = TRUE; + pinfo.fd->num = 11; + + fd_head=fragment_add_seq_next(tvb, 10, &pinfo, 24, fragment_table, + reassembled_table, 50, TRUE); + + /* As before, this returns NULL because the fragment isn't in the + * reassembled_table. At least this is a bit more consistent than before. + */ + ASSERT_EQ(0,g_hash_table_size(fragment_table)); + ASSERT_EQ(0,g_hash_table_size(reassembled_table)); + ASSERT_EQ(NULL,fd_head); + + pinfo.fd->num = 12; + fd_head=fragment_add_seq_next(tvb, 5, &pinfo, 24, fragment_table, + reassembled_table, DATA_LEN-4, FALSE); + ASSERT_EQ(0,g_hash_table_size(fragment_table)); + ASSERT_EQ(0,g_hash_table_size(reassembled_table)); + ASSERT_EQ(NULL,fd_head); + +} + +/* + * This time, our datagram only has one segment, but it has data missing. + */ +static void test_missing_data_fragment_add_seq_next_3(void) +{ + fragment_data *fd_head; + + printf("Starting test test_missing_data_fragment_add_seq_next_3\n"); + + pinfo.fd->num = 20; + fd_head=fragment_add_seq_next(tvb, 5, &pinfo, 30, fragment_table, + reassembled_table, DATA_LEN-4, FALSE); + + ASSERT_EQ(0,g_hash_table_size(fragment_table)); + ASSERT_EQ(1,g_hash_table_size(reassembled_table)); + ASSERT_NE(NULL,fd_head); + + /* check the contents of the structure. */ + ASSERT_EQ(0,fd_head->frame); /* unused */ + ASSERT_EQ(0,fd_head->offset); /* unused */ + ASSERT_EQ(0,fd_head->len); /* the length of data we have */ + ASSERT_EQ(0,fd_head->datalen); /* seqno of the last fragment we have */ + ASSERT_EQ(20,fd_head->reassembled_in); + ASSERT_EQ(FD_BLOCKSEQUENCE|FD_DEFRAGMENTED,fd_head->flags); + ASSERT_EQ(NULL,fd_head->data); + ASSERT_EQ(NULL,fd_head->next); + + /* revisiting the packet ought to produce the same result. */ + pinfo.fd->flags.visited = TRUE; + + pinfo.fd->num = 20; + fd_head=fragment_add_seq_next(tvb, 5, &pinfo, 30, fragment_table, + reassembled_table, DATA_LEN-4, FALSE); + + ASSERT_EQ(0,g_hash_table_size(fragment_table)); + ASSERT_EQ(1,g_hash_table_size(reassembled_table)); + ASSERT_NE(NULL,fd_head); + ASSERT_EQ(0,fd_head->frame); /* unused */ + ASSERT_EQ(0,fd_head->offset); /* unused */ + ASSERT_EQ(0,fd_head->len); /* the length of data we have */ + ASSERT_EQ(0,fd_head->datalen); /* seqno of the last fragment we have */ + ASSERT_EQ(20,fd_head->reassembled_in); + ASSERT_EQ(FD_BLOCKSEQUENCE|FD_DEFRAGMENTED,fd_head->flags); + ASSERT_EQ(NULL,fd_head->data); + ASSERT_EQ(NULL,fd_head->next); +} + + +/********************************************************************************** + * + * main + * + *********************************************************************************/ + +int main(int argc, char **argv) +{ + frame_data fd; + char src[] = {1,2,3,4}, dst[] = {5,6,7,8}; + unsigned int i; + void (*tests[])(void) = { + test_simple_fragment_add_seq, + test_fragment_add_seq_partial_reassembly, + test_fragment_add_dcerpc_dg, + test_fragment_add_seq_check, + test_fragment_add_seq_check_1, + test_fragment_add_seq_802_11_0, + test_fragment_add_seq_802_11_1, + test_simple_fragment_add_seq_next, + test_missing_data_fragment_add_seq_next, + test_missing_data_fragment_add_seq_next_2, + test_missing_data_fragment_add_seq_next_3 + }; + + /* we don't use our params */ + argc=argc; argv=argv; + + /* initialise stuff */ + ep_init_chunk(); + tvbuff_init(); + reassemble_init(); + + /* a tvbuff for testing with */ + data = g_malloc(DATA_LEN); + /* make sure it's full of stuff */ + for(i=0; iflags.visited = FALSE; + + tests[i](); + } + + printf(failure?"FAILURE\n":"SUCCESS\n"); + return failure; +}