/* reassemble.c
* Routines for {fragment,segment} reassembly
*
- * $Id$
- *
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "config.h"
#include <string.h>
#include <epan/packet.h>
-
+#include <epan/exceptions.h>
#include <epan/reassemble.h>
+#include <epan/tvbuff-int.h>
-#include <epan/dissectors/packet-dcerpc.h>
+#include <wsutil/str_util.h>
-typedef struct _fragment_key {
+/*
+ * Functions for reassembly tables where the endpoint addresses, and a
+ * fragment ID, are used as the key.
+ */
+typedef struct _fragment_addresses_key {
address src;
address dst;
guint32 id;
-} fragment_key;
+} fragment_addresses_key;
-typedef struct _dcerpc_fragment_key {
- address src;
- address dst;
- guint32 id;
- e_uuid_t act_id;
-} dcerpc_fragment_key;
+GList* reassembly_table_list = NULL;
-static void LINK_FRAG(fragment_data *fd_head,fragment_data *fd)
+static guint
+fragment_addresses_hash(gconstpointer k)
{
- fragment_data *fd_i;
+ const fragment_addresses_key* key = (const fragment_addresses_key*) k;
+ guint hash_val;
+/*
+ int i;
+*/
- /* add fragment to list, keep list sorted */
- for(fd_i= fd_head; fd_i->next;fd_i=fd_i->next) {
- if (fd->offset < fd_i->next->offset )
- break;
- }
- fd->next=fd_i->next;
- fd_i->next=fd;
+ hash_val = 0;
+
+/* More than likely: in most captures src and dst addresses are the
+ same, and would hash the same.
+ We only use id as the hash as an optimization.
+
+ for (i = 0; i < key->src.len; i++)
+ hash_val += key->src.data[i];
+ for (i = 0; i < key->dst.len; i++)
+ hash_val += key->dst.data[i];
+*/
+
+ hash_val += key->id;
+
+ return hash_val;
}
-/* copy a fragment key to heap store to insert in the hash */
-static void *fragment_key_copy(const void *k)
+static gint
+fragment_addresses_equal(gconstpointer k1, gconstpointer k2)
{
- const fragment_key* key = (const fragment_key*) k;
- fragment_key *new_key = g_slice_new(fragment_key);
+ const fragment_addresses_key* key1 = (const fragment_addresses_key*) k1;
+ const fragment_addresses_key* key2 = (const fragment_addresses_key*) k2;
- COPY_ADDRESS(&new_key->src, &key->src);
- COPY_ADDRESS(&new_key->dst, &key->dst);
- new_key->id = key->id;
- return new_key;
+ /*
+ * key.id is the first item to compare since it's the item most
+ * likely to differ between sessions, thus short-circuiting
+ * the comparison of addresses.
+ */
+ return (key1->id == key2->id) &&
+ (addresses_equal(&key1->src, &key2->src)) &&
+ (addresses_equal(&key1->dst, &key2->dst));
}
-/* copy a dcerpc fragment key to heap store to insert in the hash */
-static void *dcerpc_fragment_key_copy(const void *k)
+/*
+ * Create a fragment key for temporary use; it can point to non-
+ * persistent data, and so must only be used to look up and
+ * delete entries, not to add them.
+ */
+static gpointer
+fragment_addresses_temporary_key(const packet_info *pinfo, const guint32 id,
+ const void *data _U_)
{
- const dcerpc_fragment_key* key = (const dcerpc_fragment_key*) k;
+ fragment_addresses_key *key = g_slice_new(fragment_addresses_key);
- dcerpc_fragment_key *new_key = g_slice_new(dcerpc_fragment_key);
+ /*
+ * Do a shallow copy of the addresses.
+ */
+ copy_address_shallow(&key->src, &pinfo->src);
+ copy_address_shallow(&key->dst, &pinfo->dst);
+ key->id = id;
- 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 (gpointer)key;
+}
- return new_key;
+/*
+ * Create a fragment key for permanent use; it must point to persistent
+ * data, so that it can be used to add entries.
+ */
+static gpointer
+fragment_addresses_persistent_key(const packet_info *pinfo, const guint32 id,
+ const void *data _U_)
+{
+ fragment_addresses_key *key = g_slice_new(fragment_addresses_key);
+
+ /*
+ * Do a deep copy of the addresses.
+ */
+ copy_address(&key->src, &pinfo->src);
+ copy_address(&key->dst, &pinfo->dst);
+ key->id = id;
+
+ return (gpointer)key;
}
+static void
+fragment_addresses_free_temporary_key(gpointer ptr)
+{
+ fragment_addresses_key *key = (fragment_addresses_key *)ptr;
+ g_slice_free(fragment_addresses_key, key);
+}
-static gint
-fragment_equal(gconstpointer k1, gconstpointer k2)
+static void
+fragment_addresses_free_persistent_key(gpointer ptr)
{
- const fragment_key* key1 = (const fragment_key*) k1;
- const fragment_key* key2 = (const fragment_key*) k2;
+ fragment_addresses_key *key = (fragment_addresses_key *)ptr;
- /*key.id is the first item to compare since item is most
- likely to differ between sessions, thus shortcircuiting
- the comparasion of addresses.
- */
- return ( ( (key1->id == key2->id) &&
- (ADDRESSES_EQUAL(&key1->src, &key2->src)) &&
- (ADDRESSES_EQUAL(&key1->dst, &key2->dst))
- ) ?
- TRUE : FALSE);
+ if(key){
+ /*
+ * Free up the copies of the addresses from the old key.
+ */
+ free_address(&key->src);
+ free_address(&key->dst);
+
+ g_slice_free(fragment_addresses_key, key);
+ }
}
+const reassembly_table_functions
+addresses_reassembly_table_functions = {
+ fragment_addresses_hash,
+ fragment_addresses_equal,
+ fragment_addresses_temporary_key,
+ fragment_addresses_persistent_key,
+ fragment_addresses_free_temporary_key,
+ fragment_addresses_free_persistent_key
+};
+
+/*
+ * Functions for reassembly tables where the endpoint addresses and ports,
+ * and a fragment ID, are used as the key.
+ */
+typedef struct _fragment_addresses_ports_key {
+ address src_addr;
+ address dst_addr;
+ guint32 src_port;
+ guint32 dst_port;
+ guint32 id;
+} fragment_addresses_ports_key;
+
static guint
-fragment_hash(gconstpointer k)
+fragment_addresses_ports_hash(gconstpointer k)
{
- const fragment_key* key = (const fragment_key*) k;
+ const fragment_addresses_ports_key* key = (const fragment_addresses_ports_key*) k;
guint hash_val;
/*
int i;
hash_val = 0;
-/* More than likely: in most captures src and dst addresses are the
- same, and would hash the same.
+/* More than likely: in most captures src and dst addresses and ports
+ are the same, and would hash the same.
We only use id as the hash as an optimization.
for (i = 0; i < key->src.len; i++)
- hash_val += key->src.data[i];
+ hash_val += key->src_addr.data[i];
for (i = 0; i < key->dst.len; i++)
- hash_val += key->dst.data[i];
+ hash_val += key->dst_addr.data[i];
+ hash_val += key->src_port;
+ hash_val += key->dst_port;
*/
hash_val += key->id;
}
static gint
-dcerpc_fragment_equal(gconstpointer k1, gconstpointer k2)
+fragment_addresses_ports_equal(gconstpointer k1, gconstpointer k2)
{
- const dcerpc_fragment_key* key1 = (const dcerpc_fragment_key*) k1;
- const dcerpc_fragment_key* key2 = (const dcerpc_fragment_key*) k2;
+ const fragment_addresses_ports_key* key1 = (const fragment_addresses_ports_key*) k1;
+ const fragment_addresses_ports_key* key2 = (const fragment_addresses_ports_key*) k2;
- /*key.id is the first item to compare since item is most
- likely to differ between sessions, thus shortcircuiting
- the comparison of addresses.
- */
- return (((key1->id == key2->id)
- && (ADDRESSES_EQUAL(&key1->src, &key2->src))
- && (ADDRESSES_EQUAL(&key1->dst, &key2->dst))
- && (memcmp (&key1->act_id, &key2->act_id, sizeof (e_uuid_t)) == 0))
- ? TRUE : FALSE);
+ /*
+ * key.id is the first item to compare since it's the item most
+ * likely to differ between sessions, thus short-circuiting
+ * the comparison of addresses and ports.
+ */
+ return (key1->id == key2->id) &&
+ (addresses_equal(&key1->src_addr, &key2->src_addr)) &&
+ (addresses_equal(&key1->dst_addr, &key2->dst_addr)) &&
+ (key1->src_port == key2->src_port) &&
+ (key1->dst_port == key2->dst_port);
}
-static guint
-dcerpc_fragment_hash(gconstpointer k)
+/*
+ * Create a fragment key for temporary use; it can point to non-
+ * persistent data, and so must only be used to look up and
+ * delete entries, not to add them.
+ */
+static gpointer
+fragment_addresses_ports_temporary_key(const packet_info *pinfo, const guint32 id,
+ const void *data _U_)
{
- const dcerpc_fragment_key* key = (const dcerpc_fragment_key*) k;
- guint hash_val;
+ fragment_addresses_ports_key *key = g_slice_new(fragment_addresses_ports_key);
- hash_val = 0;
+ /*
+ * Do a shallow copy of the addresses.
+ */
+ copy_address_shallow(&key->src_addr, &pinfo->src);
+ copy_address_shallow(&key->dst_addr, &pinfo->dst);
+ key->src_port = pinfo->srcport;
+ key->dst_port = pinfo->destport;
+ key->id = id;
- hash_val += key->id;
- hash_val += key->act_id.Data1;
- hash_val += key->act_id.Data2 << 16;
- hash_val += key->act_id.Data3;
+ return (gpointer)key;
+}
- return hash_val;
+/*
+ * Create a fragment key for permanent use; it must point to persistent
+ * data, so that it can be used to add entries.
+ */
+static gpointer
+fragment_addresses_ports_persistent_key(const packet_info *pinfo,
+ const guint32 id, const void *data _U_)
+{
+ fragment_addresses_ports_key *key = g_slice_new(fragment_addresses_ports_key);
+
+ /*
+ * Do a deep copy of the addresses.
+ */
+ copy_address(&key->src_addr, &pinfo->src);
+ copy_address(&key->dst_addr, &pinfo->dst);
+ key->src_port = pinfo->srcport;
+ key->dst_port = pinfo->destport;
+ key->id = id;
+
+ return (gpointer)key;
}
+static void
+fragment_addresses_ports_free_temporary_key(gpointer ptr)
+{
+ fragment_addresses_ports_key *key = (fragment_addresses_ports_key *)ptr;
+ g_slice_free(fragment_addresses_ports_key, key);
+}
+
+static void
+fragment_addresses_ports_free_persistent_key(gpointer ptr)
+{
+ fragment_addresses_ports_key *key = (fragment_addresses_ports_key *)ptr;
+
+ if(key){
+ /*
+ * Free up the copies of the addresses from the old key.
+ */
+ free_address(&key->src_addr);
+ free_address(&key->dst_addr);
+
+ g_slice_free(fragment_addresses_ports_key, key);
+ }
+}
+
+const reassembly_table_functions
+addresses_ports_reassembly_table_functions = {
+ fragment_addresses_ports_hash,
+ fragment_addresses_ports_equal,
+ fragment_addresses_ports_temporary_key,
+ fragment_addresses_ports_persistent_key,
+ fragment_addresses_ports_free_temporary_key,
+ fragment_addresses_ports_free_persistent_key
+};
+
typedef struct _reassembled_key {
guint32 id;
guint32 frame;
return key->frame;
}
+static void
+reassembled_key_free(gpointer ptr)
+{
+ g_slice_free(reassembled_key, (reassembled_key *)ptr);
+}
+
/*
* For a fragment hash table entry, free the associated fragments.
* The entry value (fd_chain) is freed herein and the entry is freed
- * when fragment_free_key() [or dcerpc_fragment_free_key()] is called
- * (as a consequence of returning TRUE from this function).
+ * when the key freeing routine is called (as a consequence of returning
+ * TRUE from this function).
*/
static gboolean
free_all_fragments(gpointer key_arg _U_, gpointer value, gpointer user_data _U_)
{
- fragment_data *fd_head, *tmp_fd;
+ fragment_head *fd_head;
+ fragment_item *tmp_fd;
/* g_hash_table_new_full() was used to supply a function
- * to free the key and the addresses.
+ * to free the key and anything to which it points
*/
- for (fd_head = value; fd_head != NULL; fd_head = tmp_fd) {
+ for (fd_head = (fragment_head *)value; fd_head != NULL; fd_head = tmp_fd) {
tmp_fd=fd_head->next;
- if(fd_head->data && !(fd_head->flags&FD_NOT_MALLOCED))
- g_free(fd_head->data);
- g_slice_free(fragment_data, fd_head);
+ if(fd_head->tvb_data && !(fd_head->flags&FD_SUBSET_TVB))
+ tvb_free(fd_head->tvb_data);
+ g_slice_free(fragment_item, fd_head);
}
return TRUE;
}
/* ------------------------- */
-static fragment_data *new_head(const guint32 flags)
+static fragment_head *new_head(const guint32 flags)
{
- fragment_data *fd_head;
+ fragment_head *fd_head;
/* If 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=g_slice_new0(fragment_data);
+ fd_head=g_slice_new0(fragment_head);
fd_head->flags=flags;
return fd_head;
* to which the value refers and also the key itself.
*/
static gboolean
-free_all_reassembled_fragments(gpointer key_arg, gpointer value,
+free_all_reassembled_fragments(gpointer key_arg _U_, gpointer value,
gpointer user_data)
{
GPtrArray *allocated_fragments = (GPtrArray *) user_data;
- fragment_data *fd_head;
+ fragment_head *fd_head;
- for (fd_head = value; fd_head != NULL; fd_head = fd_head->next) {
+ for (fd_head = (fragment_head *)value; fd_head != NULL; fd_head = fd_head->next) {
/*
* A reassembled packet is inserted into the
* hash table once for every frame that made
* free_fragments()
*/
if (fd_head->flags != FD_VISITED_FREE) {
- if (fd_head->flags & FD_NOT_MALLOCED)
- fd_head->data = NULL;
+ if (fd_head->flags & FD_SUBSET_TVB)
+ fd_head->tvb_data = NULL;
g_ptr_array_add(allocated_fragments, fd_head);
fd_head->flags = FD_VISITED_FREE;
}
}
- g_slice_free(reassembled_key, (reassembled_key *)key_arg);
-
return TRUE;
}
static void
-fragment_free_key(void *ptr)
+free_fragments(gpointer data, gpointer user_data _U_)
{
- fragment_key *key = (fragment_key *)ptr;
-
- if(key){
- /*
- * Free up the copies of the addresses from the old key.
- */
- g_free((gpointer)key->src.data);
- g_free((gpointer)key->dst.data);
+ fragment_item *fd_head = (fragment_item *) data;
- g_slice_free(fragment_key, key);
- }
+ if (fd_head->tvb_data)
+ tvb_free(fd_head->tvb_data);
+ g_slice_free(fragment_item, fd_head);
}
-static void
-dcerpc_fragment_free_key(void *ptr)
+typedef struct register_reassembly_table {
+ reassembly_table *table;
+ const reassembly_table_functions *funcs;
+} register_reassembly_table_t;
+
+/*
+ * Register a reassembly table.
+ */
+void
+reassembly_table_register(reassembly_table *table,
+ const reassembly_table_functions *funcs)
{
- dcerpc_fragment_key *key = (dcerpc_fragment_key *)ptr;
+ register_reassembly_table_t* reg_table;
- if(key){
- /*
- * Free up the copies of the addresses from the old key.
- */
- g_free((gpointer)key->src.data);
- g_free((gpointer)key->dst.data);
+ DISSECTOR_ASSERT(table);
+ DISSECTOR_ASSERT(funcs);
- g_slice_free(dcerpc_fragment_key, key);
- }
+ reg_table = g_new(register_reassembly_table_t,1);
+
+ reg_table->table = table;
+ reg_table->funcs = funcs;
+
+ reassembly_table_list = g_list_prepend(reassembly_table_list, reg_table);
}
/*
- * Initialize a fragment table.
+ * Initialize a reassembly table, with specified functions.
*/
void
-fragment_table_init(GHashTable **fragment_table)
+reassembly_table_init(reassembly_table *table,
+ const reassembly_table_functions *funcs)
{
- if (*fragment_table != NULL) {
+ if (table->temporary_key_func == NULL)
+ table->temporary_key_func = funcs->temporary_key_func;
+ if (table->persistent_key_func == NULL)
+ table->persistent_key_func = funcs->persistent_key_func;
+ if (table->free_temporary_key_func == NULL)
+ table->free_temporary_key_func = funcs->free_temporary_key_func;
+ if (table->fragment_table != NULL) {
/*
* The fragment hash table exists.
*
* Remove all entries and free fragment data for each entry.
*
- * The keys are freed by calling fragment_free_key()
- * and the values are freed in free_all_fragments().
- *
- * free_all_fragments()
- * will free the address data associated with the key
+ * The keys, and anything to which they point, are freed by
+ * calling the table's key freeing function. The values
+ * are freed in free_all_fragments().
*/
- g_hash_table_foreach_remove(*fragment_table,
- free_all_fragments, NULL);
+ g_hash_table_foreach_remove(table->fragment_table,
+ free_all_fragments, NULL);
} else {
/* The fragment table does not exist. Create it */
- *fragment_table = g_hash_table_new_full(fragment_hash,
- fragment_equal, fragment_free_key, NULL);
+ table->fragment_table = g_hash_table_new_full(funcs->hash_func,
+ funcs->equal_func, funcs->free_persistent_key_func, NULL);
}
-}
-/*
- * Destroy a fragment table.
- */
-void
-frgment_table_destroy(GHashTable **fragment_table)
-{
- if (*fragment_table != NULL) {
+ if (table->reassembled_table != NULL) {
+ GPtrArray *allocated_fragments;
+
/*
- * The fragment hash table exists.
- *
- * Remove all entries and free fragment data for each entry.
- *
- * The keys are freed by calling fragment_free_key()
- * and the values are freed in free_all_fragments().
+ * The reassembled-packet hash table exists.
*
- * free_all_fragments()
- * will free the address data associated with the key
+ * Remove all entries and free reassembled packet
+ * data and key for each entry.
*/
- g_hash_table_foreach_remove(*fragment_table,
- free_all_fragments, NULL);
- g_hash_table_destroy(*fragment_table);
- *fragment_table = NULL;
+ allocated_fragments = g_ptr_array_new();
+ g_hash_table_foreach_remove(table->reassembled_table,
+ free_all_reassembled_fragments, allocated_fragments);
+
+ g_ptr_array_foreach(allocated_fragments, free_fragments, NULL);
+ g_ptr_array_free(allocated_fragments, TRUE);
+ } else {
+ /* The fragment table does not exist. Create it */
+ table->reassembled_table = g_hash_table_new_full(reassembled_hash,
+ reassembled_equal, reassembled_key_free, NULL);
}
}
+/*
+ * Destroy a reassembly table.
+ */
void
-dcerpc_fragment_table_init(GHashTable **fragment_table)
+reassembly_table_destroy(reassembly_table *table)
{
- if (*fragment_table != NULL) {
+ /*
+ * Clear the function pointers.
+ */
+ table->temporary_key_func = NULL;
+ table->persistent_key_func = NULL;
+ table->free_temporary_key_func = NULL;
+ if (table->fragment_table != NULL) {
/*
* The fragment hash table exists.
*
* Remove all entries and free fragment data for each entry.
*
- * If slices are used (GLIB >= 2.10)
- * the keys are freed by calling dcerpc_fragment_free_key()
- * and the values are freed in free_all_fragments().
- *
- * free_all_fragments()
- * will free the adrress data associated with the key
+ * The keys, and anything to which they point, are freed by
+ * calling the table's key freeing function. The values
+ * are freed in free_all_fragments().
*/
- g_hash_table_foreach_remove(*fragment_table,
- free_all_fragments, NULL);
- } else {
- /* The fragment table does not exist. Create it */
- *fragment_table = g_hash_table_new_full(dcerpc_fragment_hash,
- dcerpc_fragment_equal, dcerpc_fragment_free_key, NULL);
- }
-}
+ g_hash_table_foreach_remove(table->fragment_table,
+ free_all_fragments, NULL);
-static void
-free_fragments(gpointer data, gpointer user_data _U_)
-{
- fragment_data *fd_head = (fragment_data *) data;
-
- g_free(fd_head->data);
- g_slice_free(fragment_data, fd_head);
-}
-
-/*
- * Initialize a reassembled-packet table.
- */
-void
-reassembled_table_init(GHashTable **reassembled_table)
-{
- if (*reassembled_table != NULL) {
+ /*
+ * Now destroy the hash table.
+ */
+ g_hash_table_destroy(table->fragment_table);
+ table->fragment_table = NULL;
+ }
+ if (table->reassembled_table != NULL) {
GPtrArray *allocated_fragments;
/*
*/
allocated_fragments = g_ptr_array_new();
- g_hash_table_foreach_remove(*reassembled_table,
+ g_hash_table_foreach_remove(table->reassembled_table,
free_all_reassembled_fragments, allocated_fragments);
g_ptr_array_foreach(allocated_fragments, free_fragments, NULL);
g_ptr_array_free(allocated_fragments, TRUE);
- } else {
- /* The fragment table does not exist. Create it */
- *reassembled_table = g_hash_table_new(reassembled_hash, reassembled_equal);
+
+ /*
+ * Now destroy the hash table.
+ */
+ g_hash_table_destroy(table->reassembled_table);
+ table->reassembled_table = NULL;
}
}
+/*
+ * Look up an fd_head in the fragment table, optionally returning the key
+ * for it.
+ */
+static fragment_head *
+lookup_fd_head(reassembly_table *table, const packet_info *pinfo,
+ const guint32 id, const void *data, gpointer *orig_keyp)
+{
+ gpointer key;
+ gpointer value;
+
+ /* Create key to search hash with */
+ key = table->temporary_key_func(pinfo, id, data);
+
+ /*
+ * Look up the reassembly in the fragment table.
+ */
+ if (!g_hash_table_lookup_extended(table->fragment_table, key, orig_keyp,
+ &value))
+ value = NULL;
+ /* Free the key */
+ table->free_temporary_key_func(key);
+
+ return (fragment_head *)value;
+}
+
+/*
+ * Insert an fd_head into the fragment table, and return the key used.
+ */
+static gpointer
+insert_fd_head(reassembly_table *table, fragment_head *fd_head,
+ const packet_info *pinfo, const guint32 id, const void *data)
+{
+ gpointer key;
+
+ /*
+ * We're going to use the key to insert the fragment,
+ * so make a persistent version of it.
+ */
+ key = table->persistent_key_func(pinfo, id, data);
+ g_hash_table_insert(table->fragment_table, key, fd_head);
+ return key;
+}
+
/* This function cleans up the stored state and removes the reassembly data and
* (with one exception) all allocated memory for matching reassembly.
*
* The exception is :
- * If the PDU was already completely reassembled, then the buffer containing the
- * reassembled data WILL NOT be free()d, and the pointer to that buffer will be
+ * If the PDU was already completely reassembled, then the tvbuff containing the
+ * reassembled data WILL NOT be free()d, and the pointer to that tvbuff will be
* returned.
* Othervise the function will return NULL.
*
- * So, if you call fragment_delete and it returns non-NULL, YOU are responsible to
- * g_free() that buffer.
+ * So, if you call fragment_delete and it returns non-NULL, YOU are responsible
+ * to tvb_free() that tvbuff.
*/
-unsigned char *
-fragment_delete(const packet_info *pinfo, const guint32 id, GHashTable *fragment_table)
+tvbuff_t *
+fragment_delete(reassembly_table *table, const packet_info *pinfo,
+ const guint32 id, const void *data)
{
- fragment_data *fd_head, *fd;
- fragment_key key;
- unsigned char *data=NULL;
-
- /* 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);
+ fragment_head *fd_head;
+ fragment_item *fd;
+ tvbuff_t *fd_tvb_data=NULL;
+ gpointer key;
+ fd_head = lookup_fd_head(table, pinfo, id, data, &key);
if(fd_head==NULL){
/* We do not recognize this as a PDU we have seen before. return */
return NULL;
}
- data=fd_head->data;
- /* loop over all partial fragments and free any buffers */
+ fd_tvb_data=fd_head->tvb_data;
+ /* loop over all partial fragments and free any tvbuffs */
for(fd=fd_head->next;fd;){
- fragment_data *tmp_fd;
+ fragment_item *tmp_fd;
tmp_fd=fd->next;
- if( !(fd->flags&FD_NOT_MALLOCED) )
- g_free(fd->data);
- g_slice_free(fragment_data, fd);
+ if (fd->tvb_data && !(fd->flags & FD_SUBSET_TVB))
+ tvb_free(fd->tvb_data);
+ g_slice_free(fragment_item, fd);
fd=tmp_fd;
}
- g_slice_free(fragment_data, fd_head);
- g_hash_table_remove(fragment_table, &key);
+ g_slice_free(fragment_head, fd_head);
+ g_hash_table_remove(table->fragment_table, key);
- return data;
+ return fd_tvb_data;
}
/* This function is used to check if there is partial or completed reassembly state
* matching this packet. I.e. Is there reassembly going on or not for this packet?
*/
-fragment_data *
-fragment_get(const packet_info *pinfo, const guint32 id, GHashTable *fragment_table)
+fragment_head *
+fragment_get(reassembly_table *table, const packet_info *pinfo,
+ const guint32 id, const void *data)
{
- 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 fd_head;
+ return lookup_fd_head(table, pinfo, id, data, NULL);
}
/* id *must* be the frame number for this to work! */
-fragment_data *
-fragment_get_reassembled(const guint32 id, GHashTable *reassembled_table)
+fragment_head *
+fragment_get_reassembled(reassembly_table *table, const guint32 id)
{
- fragment_data *fd_head;
+ fragment_head *fd_head;
reassembled_key key;
/* create key to search hash with */
key.frame = id;
key.id = id;
- fd_head = g_hash_table_lookup(reassembled_table, &key);
+ fd_head = (fragment_head *)g_hash_table_lookup(table->reassembled_table, &key);
return fd_head;
}
-fragment_data *
-fragment_get_reassembled_id(const packet_info *pinfo, const guint32 id, GHashTable *reassembled_table)
+fragment_head *
+fragment_get_reassembled_id(reassembly_table *table, const packet_info *pinfo,
+ const guint32 id)
{
- fragment_data *fd_head;
+ fragment_head *fd_head;
reassembled_key key;
/* create key to search hash with */
- key.frame = pinfo->fd->num;
+ key.frame = pinfo->num;
key.id = id;
- fd_head = g_hash_table_lookup(reassembled_table, &key);
+ fd_head = (fragment_head *)g_hash_table_lookup(table->reassembled_table, &key);
return fd_head;
}
+/* To specify the offset for the fragment numbering, the first fragment is added with 0, and
+ * afterwards this offset is set. All additional calls to off_seq_check will calculate
+ * the number in sequence in regards to the offset */
+void
+fragment_add_seq_offset(reassembly_table *table, const packet_info *pinfo, const guint32 id,
+ const void *data, const guint32 fragment_offset)
+{
+ fragment_head *fd_head;
+
+ fd_head = lookup_fd_head(table, pinfo, id, data, NULL);
+ if (!fd_head)
+ return;
+
+ /* Reseting the offset is not allowed */
+ if ( fd_head->fragment_nr_offset != 0 )
+ return;
+
+ fd_head->fragment_nr_offset = fragment_offset;
+}
+
/* This function can be used to explicitly set the total length (if known)
* for reassembly of a PDU.
* This is useful for reassembly of PDUs where one may have the total length specified
* actually means we want to defragment 3 blocks, block 0, 1 and 2.
*/
void
-fragment_set_tot_len(const packet_info *pinfo, const guint32 id, GHashTable *fragment_table,
- const guint32 tot_len)
+fragment_set_tot_len(reassembly_table *table, const packet_info *pinfo,
+ const guint32 id, const void *data, const guint32 tot_len)
{
- fragment_data *fd_head;
- fragment_key key;
+ fragment_head *fd_head;
+ fragment_item *fd;
+ guint32 max_offset = 0;
- /* create key to search hash with */
- key.src = pinfo->src;
- key.dst = pinfo->dst;
- key.id = id;
+ fd_head = lookup_fd_head(table, pinfo, id, data, NULL);
+ if (!fd_head)
+ return;
- fd_head = g_hash_table_lookup(fragment_table, &key);
+ /* If we're setting a block sequence number, verify that it
+ * doesn't conflict with values set by existing fragments.
+ * XXX - eliminate this check?
+ */
+ fd = fd_head;
+ if (fd_head->flags & FD_BLOCKSEQUENCE) {
+ while (fd) {
+ if (fd->offset > max_offset) {
+ max_offset = fd->offset;
+ if (max_offset > tot_len) {
+ fd_head->error = "Bad total reassembly block count";
+ THROW_MESSAGE(ReassemblyError, fd_head->error);
+ }
+ }
+ fd = fd->next;
+ }
+ }
- if(fd_head){
- fd_head->datalen = tot_len;
- fd_head->flags |= FD_DATALEN_SET;
+ if (fd_head->flags & FD_DEFRAGMENTED) {
+ if (max_offset != tot_len) {
+ fd_head->error = "Defragmented complete but total length not satisfied";
+ THROW_MESSAGE(ReassemblyError, fd_head->error);
+ }
}
- return;
+ /* We got this far so the value is sane. */
+ fd_head->datalen = tot_len;
+ fd_head->flags |= FD_DATALEN_SET;
}
guint32
-fragment_get_tot_len(const packet_info *pinfo, const guint32 id, GHashTable *fragment_table)
+fragment_get_tot_len(reassembly_table *table, const packet_info *pinfo,
+ const guint32 id, const void *data)
{
- fragment_data *fd_head;
- fragment_key key;
-
- /* create key to search hash with */
- key.src = pinfo->src;
- key.dst = pinfo->dst;
- key.id = id;
+ fragment_head *fd_head;
- fd_head = g_hash_table_lookup(fragment_table, &key);
+ fd_head = lookup_fd_head(table, pinfo, id, data, NULL);
if(fd_head){
return fd_head->datalen;
*/
void
-fragment_set_partial_reassembly(const packet_info *pinfo, const guint32 id, GHashTable *fragment_table)
+fragment_set_partial_reassembly(reassembly_table *table,
+ const packet_info *pinfo, const guint32 id,
+ const void *data)
{
- fragment_data *fd_head;
- fragment_key key;
-
- /* create key to search hash with */
- key.src = pinfo->src;
- key.dst = pinfo->dst;
- key.id = id;
+ fragment_head *fd_head;
- fd_head = g_hash_table_lookup(fragment_table, &key);
+ fd_head = lookup_fd_head(table, pinfo, id, data, NULL);
/*
* XXX - why not do all the stuff done early in "fragment_add_work()",
/*
* This function gets rid of an entry from a fragment table, given
- * a pointer to the key for that entry; it also frees up the key
- * and the addresses in it.
- * Note: If we use slices keys are freed by fragment_free_key()
- [or dcerpc_fragment_free_key()] being called
- * during g_hash_table_remove().
+ * a pointer to the key for that entry.
+ *
+ * The key freeing routine will be called by g_hash_table_remove().
*/
static void
-fragment_unhash(GHashTable *fragment_table, fragment_key *key)
+fragment_unhash(reassembly_table *table, gpointer key)
{
/*
* Remove the entry from the fragment table.
*/
- g_hash_table_remove(fragment_table, key);
-
- /*
- * Free the key itself.
- */
+ g_hash_table_remove(table->fragment_table, key);
}
/*
- * This function adds fragment_data structure to a reassembled-packet
+ * This function adds fragment_head structure to a reassembled-packet
* hash table, using the frame numbers of each of the frames from
* which it was reassembled as keys, and sets the "reassembled_in"
* frame number.
*/
static void
-fragment_reassembled(fragment_data *fd_head, const packet_info *pinfo,
- GHashTable *reassembled_table, const guint32 id)
+fragment_reassembled(reassembly_table *table, fragment_head *fd_head,
+ const packet_info *pinfo, const guint32 id)
{
reassembled_key *new_key;
- fragment_data *fd;
+ fragment_item *fd;
if (fd_head->next == NULL) {
/*
* table; just hash it using the current frame number.
*/
new_key = g_slice_new(reassembled_key);
- new_key->frame = pinfo->fd->num;
+ new_key->frame = pinfo->num;
new_key->id = id;
- g_hash_table_insert(reassembled_table, new_key, fd_head);
+ g_hash_table_insert(table->reassembled_table, new_key, fd_head);
} else {
/*
* Hash it with the frame numbers for all the frames.
new_key = g_slice_new(reassembled_key);
new_key->frame = fd->frame;
new_key->id = id;
- g_hash_table_insert(reassembled_table, new_key,
+ g_hash_table_insert(table->reassembled_table, new_key,
+ fd_head);
+ }
+ }
+ fd_head->flags |= FD_DEFRAGMENTED;
+ fd_head->reassembled_in = pinfo->num;
+ fd_head->reas_in_layer_num = pinfo->curr_layer_num;
+}
+
+/*
+ * This function is a variant of the above for the single sequence
+ * case, using id+offset (i.e., the original sequence number) for the id
+ * in the key.
+ */
+static void
+fragment_reassembled_single(reassembly_table *table, fragment_head *fd_head,
+ const packet_info *pinfo, const guint32 id)
+{
+ reassembled_key *new_key;
+ fragment_item *fd;
+
+ if (fd_head->next == NULL) {
+ /*
+ * This was not fragmented, so there's no fragment
+ * table; just hash it using the current frame number.
+ */
+ new_key = g_slice_new(reassembled_key);
+ new_key->frame = pinfo->num;
+ new_key->id = id;
+ g_hash_table_insert(table->reassembled_table, new_key, fd_head);
+ } else {
+ /*
+ * Hash it with the frame numbers for all the frames.
+ */
+ for (fd = fd_head->next; fd != NULL; fd = fd->next){
+ new_key = g_slice_new(reassembled_key);
+ new_key->frame = fd->frame;
+ new_key->id = id + fd->offset;
+ g_hash_table_insert(table->reassembled_table, new_key,
fd_head);
}
}
fd_head->flags |= FD_DEFRAGMENTED;
- fd_head->reassembled_in = pinfo->fd->num;
+ fd_head->reassembled_in = pinfo->num;
+ fd_head->reas_in_layer_num = pinfo->curr_layer_num;
}
+static void
+LINK_FRAG(fragment_head *fd_head,fragment_item *fd)
+{
+ fragment_item *fd_i;
+
+ /* add fragment to list, keep list sorted */
+ for(fd_i= fd_head; fd_i->next;fd_i=fd_i->next) {
+ if (fd->offset < fd_i->next->offset )
+ break;
+ }
+ fd->next=fd_i->next;
+ fd_i->next=fd;
+}
+
+static void
+MERGE_FRAG(fragment_head *fd_head, fragment_item *fd)
+{
+ fragment_item *fd_i, *tmp;
+
+ if (fd == NULL) return;
+
+ for(fd_i = fd_head; fd_i->next; fd_i=fd_i->next) {
+ if (fd->offset < fd_i->next->offset) {
+ tmp = fd_i->next;
+ fd_i->next = fd;
+ fd = tmp;
+ }
+ }
+ fd_i->next = fd;
+}
/*
* This function adds a new fragment to the fragment hash table.
* If this is the first fragment seen for this datagram, a new entry
* are lowered when a new extension process is started.
*/
static gboolean
-fragment_add_work(fragment_data *fd_head, tvbuff_t *tvb, const int offset,
+fragment_add_work(fragment_head *fd_head, tvbuff_t *tvb, const int offset,
const packet_info *pinfo, const guint32 frag_offset,
const guint32 frag_data_len, const gboolean more_frags)
{
- fragment_data *fd;
- fragment_data *fd_i;
- guint32 max, dfpos;
- unsigned char *old_data;
+ fragment_item *fd;
+ fragment_item *fd_i;
+ guint32 max, dfpos, fraglen;
+ tvbuff_t *old_tvb_data;
+ guint8 *data;
/* create new fd describing this fragment */
- fd = g_slice_new(fragment_data);
+ fd = g_slice_new(fragment_item);
fd->next = NULL;
fd->flags = 0;
- fd->frame = pinfo->fd->num;
- if (fd->frame > fd_head->frame)
- fd_head->frame = fd->frame;
+ fd->frame = pinfo->num;
fd->offset = frag_offset;
+ fd->fragment_nr_offset = 0; /* will only be used with sequence */
fd->len = frag_data_len;
- fd->data = NULL;
+ fd->tvb_data = NULL;
+ fd->error = NULL;
/*
- * If it was already defragmented and this new fragment goes beyond
- * data limits, set flag in already empty fds & point old fds to malloc'ed data.
+ * Are we adding to an already-completed reassembly?
*/
- if(fd_head->flags & FD_DEFRAGMENTED && (frag_offset+frag_data_len) >= fd_head->datalen &&
- fd_head->flags & FD_PARTIAL_REASSEMBLY){
- for(fd_i=fd_head->next; fd_i; fd_i=fd_i->next){
- if( !fd_i->data ) {
- fd_i->data = fd_head->data + fd_i->offset;
- fd_i->flags |= FD_NOT_MALLOCED;
+ if (fd_head->flags & FD_DEFRAGMENTED) {
+ /*
+ * Yes. Does this fragment go past the end of the results
+ * of that reassembly?
+ * XXX - shouldn't this be ">"? If frag_offset + frag_data_len
+ * == fd_head->datalen, this overlaps the end of the
+ * reassembly, but doesn't go past it, right?
+ */
+ if (frag_offset + frag_data_len >= fd_head->datalen) {
+ /*
+ * Yes. Have we been requested to continue reassembly?
+ */
+ if (fd_head->flags & FD_PARTIAL_REASSEMBLY) {
+ /*
+ * Yes. Set flag in already empty fds &
+ * point old fds to malloc'ed data.
+ */
+ for(fd_i=fd_head->next; fd_i; fd_i=fd_i->next){
+ if( !fd_i->tvb_data ) {
+ fd_i->tvb_data = tvb_new_subset_remaining(fd_head->tvb_data, fd_i->offset);
+ fd_i->flags |= FD_SUBSET_TVB;
+ }
+ 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;
+ fd_head->reas_in_layer_num = 0;
+ } else {
+ /*
+ * No. Bail out since we have no idea what to
+ * do with this fragment (and if we keep going
+ * we'll run past the end of a buffer sooner
+ * or later).
+ */
+ g_slice_free(fragment_item, fd);
+
+ /*
+ * This is an attempt to add a fragment to a
+ * reassembly that had already completed.
+ * If it had no error, we don't want to
+ * mark it with an error, and if it had an
+ * error, we don't want to overwrite it, so
+ * we don't set fd_head->error.
+ */
+ if (frag_offset >= fd_head->datalen) {
+ /*
+ * The fragment starts past the end
+ * of the reassembled data.
+ */
+ THROW_MESSAGE(ReassemblyError, "New fragment past old data limits");
+ } else {
+ /*
+ * The fragment starts before the end
+ * of the reassembled data, but
+ * runs past the end. That could
+ * just be a retransmission.
+ */
+ THROW_MESSAGE(ReassemblyError, "New fragment overlaps old data (retransmission?)");
+ }
}
- fd_i->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS);
+ } else {
+ /*
+ * No. That means it still overlaps that, so report
+ * this as a problem, possibly a retransmission.
+ */
+ g_slice_free(fragment_item, fd);
+ THROW_MESSAGE(ReassemblyError, "New fragment overlaps old data (retransmission?)");
}
- 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;
}
+ /* Do this after we may have bailed out (above) so that we don't leave
+ * fd_head->frame in a bad state if we do */
+ if (fd->frame > fd_head->frame)
+ fd_head->frame = fd->frame;
+
if (!more_frags) {
/*
* This is the tail fragment in the sequence.
fd_head->flags |= FD_MULTIPLETAILS;
}
} else {
- /* this was the first tail fragment, now we know the
- * length of the packet
+ /* This was the first tail fragment; now we know
+ * what the length of the packet should be.
*/
fd_head->datalen = fd->offset + fd->len;
fd_head->flags |= FD_DATALEN_SET;
-
/* If the packet is already defragmented, this MUST be an overlap.
* The entire defragmented packet is in fd_head->data.
* Even if we have previously defragmented this packet, we still
fd_head->flags |= FD_TOOLONGFRAGMENT;
}
/* make sure it doesn't conflict with previous data */
- else if ( memcmp(fd_head->data+fd->offset,
+ else if ( tvb_memeql(fd_head->tvb_data, fd->offset,
tvb_get_ptr(tvb,offset,fd->len),fd->len) ){
fd->flags |= FD_OVERLAPCONFLICT;
fd_head->flags |= FD_OVERLAPCONFLICT;
/* If we have reached this point, the packet is not defragmented yet.
* Save all payload in a buffer until we can defragment.
- * XXX - what if we didn't capture the entire fragment due
- * to a too-short snapshot length?
*/
- fd->data = g_malloc(fd->len);
- tvb_memcpy(tvb, fd->data, offset, fd->len);
+ if (!tvb_bytes_exist(tvb, offset, fd->len)) {
+ g_slice_free(fragment_item, fd);
+ THROW(BoundsError);
+ }
+ fd->tvb_data = tvb_clone_offset_len(tvb, offset, fd->len);
LINK_FRAG(fd_head,fd);
if( !(fd_head->flags & FD_DATALEN_SET) ){
- /* if we dont know the datalen, there are still missing
+ /* if we don't know the datalen, there are still missing
* packets. Cheaper than the check below.
*/
return FALSE;
* The amount of contiguous data we have is less than the
* amount of data we're trying to reassemble, so we haven't
* received all packets yet.
- */
- return FALSE;
- }
-
-
- if (max > (fd_head->datalen)) {
- /*XXX not sure if current fd was the TOOLONG*/
- /*XXX is it fair to flag current fd*/
- /* oops, too long fragment detected */
- fd->flags |= FD_TOOLONGFRAGMENT;
- fd_head->flags |= FD_TOOLONGFRAGMENT;
+ */
+ return FALSE;
}
/* we have received an entire packet, defragment it and
* free all fragments
*/
/* store old data just in case */
- old_data=fd_head->data;
- fd_head->data = g_malloc(max);
+ old_tvb_data=fd_head->tvb_data;
+ data = (guint8 *) g_malloc(fd_head->datalen);
+ fd_head->tvb_data = tvb_new_real_data(data, fd_head->datalen, fd_head->datalen);
+ tvb_set_free_cb(fd_head->tvb_data, g_free);
/* add all data fragments */
for (dfpos=0,fd_i=fd_head;fd_i;fd_i=fd_i->next) {
if (fd_i->len) {
- /* dfpos is always >= than fd_i->offset */
- /* No gaps can exist here, max_loop(above) does this */
- /* XXX - true? Can we get fd_i->offset+fd-i->len */
- /* overflowing, for example? */
- /* Actually: there is at least one pathological case wherein there can be fragments
- * on the list which are for offsets greater than max (i.e.: following a gap after max).
- * (Apparently a "DESEGMENT_UNTIL_FIN" was involved wherein the FIN packet had an offset
- * less than the highest fragment offset seen. [Seen from a fuzz-test: bug #2470]).
- * Note that the "overlap" compare must only be done for fragments with (offset+len) <= max
- * and thus within the newly g_malloc'd buffer.
+ /*
+ * The loop above that calculates max also
+ * ensures that the only gaps that exist here
+ * are ones where a fragment starts past the
+ * end of the reassembled datagram, and there's
+ * a gap between the previous fragment and
+ * that fragment.
+ *
+ * A "DESEGMENT_UNTIL_FIN" was involved wherein the
+ * FIN packet had an offset less than the highest
+ * fragment offset seen. [Seen from a fuzz-test:
+ * bug #2470]).
+ *
+ * Note that the "overlap" compare must only be
+ * done for fragments with (offset+len) <= fd_head->datalen
+ * and thus within the newly g_malloc'd buffer.
*/
-
- if ( fd_i->offset+fd_i->len > dfpos ) {
- if (fd_i->offset+fd_i->len > max)
- g_warning("Reassemble error in frame %u: offset %u + len %u > max %u",
- pinfo->fd->num, fd_i->offset,
- fd_i->len, max);
- else if (dfpos < fd_i->offset)
- g_warning("Reassemble error in frame %u: dfpos %u < offset %u",
- pinfo->fd->num, dfpos, fd_i->offset);
- else if (dfpos-fd_i->offset > fd_i->len)
- g_warning("Reassemble error in frame %u: dfpos %u - offset %u > len %u",
- pinfo->fd->num, dfpos, fd_i->offset,
- fd_i->len);
- else if (!fd_head->data)
- g_warning("Reassemble error in frame %u: no data",
- pinfo->fd->num);
+ if (fd_i->offset + fd_i->len > dfpos) {
+ if (fd_i->offset >= fd_head->datalen) {
+ /*
+ * Fragment starts after the end
+ * of the reassembled packet.
+ *
+ * This can happen if the length was
+ * set after the offending fragment
+ * was added to the reassembly.
+ *
+ * Flag this fragment, but don't
+ * try to extract any data from
+ * it, as there's no place to put
+ * it.
+ *
+ * XXX - add different flag value
+ * for this.
+ */
+ fd_i->flags |= FD_TOOLONGFRAGMENT;
+ fd_head->flags |= FD_TOOLONGFRAGMENT;
+ } else if (dfpos < fd_i->offset) {
+ /*
+ * XXX - can this happen? We've
+ * already rejected fragments that
+ * start past the end of the
+ * reassembled datagram, and
+ * the loop that calculated max
+ * should have ruled out gaps,
+ * but could fd_i->offset +
+ * fd_i->len overflow?
+ */
+ fd_head->error = "dfpos < offset";
+ } else if (dfpos - fd_i->offset > fd_i->len)
+ fd_head->error = "dfpos - offset > len";
+ else if (!fd_head->tvb_data)
+ fd_head->error = "no data";
else {
+ fraglen = fd_i->len;
+ if (fd_i->offset + fraglen > fd_head->datalen) {
+ /*
+ * Fragment goes past the end
+ * of the packet, as indicated
+ * by the last fragment.
+ *
+ * This can happen if the
+ * length was set after the
+ * offending fragment was
+ * added to the reassembly.
+ *
+ * Mark it as such, and only
+ * copy from it what fits in
+ * the packet.
+ */
+ fd_i->flags |= FD_TOOLONGFRAGMENT;
+ fd_head->flags |= FD_TOOLONGFRAGMENT;
+ fraglen = fd_head->datalen - fd_i->offset;
+ }
if (fd_i->offset < dfpos) {
+ guint32 cmp_len = MIN(fd_i->len,(dfpos-fd_i->offset));
+
fd_i->flags |= FD_OVERLAP;
fd_head->flags |= FD_OVERLAP;
- if ( memcmp(fd_head->data+fd_i->offset,
- fd_i->data,
- MIN(fd_i->len,(dfpos-fd_i->offset))
- ) ) {
+ if ( memcmp(data + fd_i->offset,
+ tvb_get_ptr(fd_i->tvb_data, 0, cmp_len),
+ cmp_len)
+ ) {
fd_i->flags |= FD_OVERLAPCONFLICT;
fd_head->flags |= FD_OVERLAPCONFLICT;
}
}
- memcpy(fd_head->data+dfpos,
- fd_i->data+(dfpos-fd_i->offset),
- fd_i->len-(dfpos-fd_i->offset));
+ if (fraglen < dfpos - fd_i->offset) {
+ /*
+ * XXX - can this happen?
+ */
+ fd_head->error = "fraglen < dfpos - offset";
+ } else {
+ memcpy(data+dfpos,
+ tvb_get_ptr(fd_i->tvb_data, (dfpos-fd_i->offset), fraglen-(dfpos-fd_i->offset)),
+ fraglen-(dfpos-fd_i->offset));
+ dfpos=MAX(dfpos, (fd_i->offset + fraglen));
+ }
}
} else {
- if (fd_i->offset + fd_i->len < fd_i->offset) /* Integer overflow? */
- g_warning("Reassemble error in frame %u: offset %u + len %u < offset",
- pinfo->fd->num, fd_i->offset,
- fd_i->len);
+ if (fd_i->offset + fd_i->len < fd_i->offset) {
+ /* Integer overflow? */
+ fd_head->error = "offset + len < offset";
+ }
}
- if( fd_i->flags & FD_NOT_MALLOCED )
- fd_i->flags &= ~FD_NOT_MALLOCED;
- else
- g_free(fd_i->data);
- fd_i->data=NULL;
- dfpos=MAX(dfpos,(fd_i->offset+fd_i->len));
+ if (fd_i->flags & FD_SUBSET_TVB)
+ fd_i->flags &= ~FD_SUBSET_TVB;
+ else if (fd_i->tvb_data)
+ tvb_free(fd_i->tvb_data);
+
+ fd_i->tvb_data=NULL;
}
}
- g_free(old_data);
+ if (old_tvb_data)
+ tvb_add_to_chain(tvb, old_tvb_data);
/* mark this packet as defragmented.
- allows us to skip any trailing fragments */
+ allows us to skip any trailing fragments */
fd_head->flags |= FD_DEFRAGMENTED;
- fd_head->reassembled_in=pinfo->fd->num;
+ fd_head->reassembled_in=pinfo->num;
+ fd_head->reas_in_layer_num = pinfo->curr_layer_num;
+
+ /* we don't throw until here to avoid leaking old_data and others */
+ if (fd_head->error) {
+ THROW_MESSAGE(ReassemblyError, fd_head->error);
+ }
return TRUE;
}
-static fragment_data *
-fragment_add_common(tvbuff_t *tvb, const int offset, const packet_info *pinfo, const guint32 id,
- GHashTable *fragment_table, const guint32 frag_offset,
- const guint32 frag_data_len, const gboolean more_frags,
- const gboolean check_already_added)
+static fragment_head *
+fragment_add_common(reassembly_table *table, tvbuff_t *tvb, const int offset,
+ const packet_info *pinfo, const guint32 id,
+ const void *data, const guint32 frag_offset,
+ const guint32 frag_data_len, const gboolean more_frags,
+ const gboolean check_already_added)
{
- fragment_key key, *new_key;
- fragment_data *fd_head;
- fragment_data *fd_item;
- gboolean already_added=pinfo->fd->flags.visited;
+ fragment_head *fd_head;
+ fragment_item *fd_item;
+ gboolean already_added;
- /* dissector shouldn't give us garbage tvb info */
+ /*
+ * Dissector shouldn't give us garbage tvb info.
+ *
+ * XXX - should this code take responsibility for preventing
+ * reassembly if data is missing due to the packets being
+ * sliced, rather than leaving it up to dissectors?
+ */
DISSECTOR_ASSERT(tvb_bytes_exist(tvb, offset, frag_data_len));
- /* 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);
+ fd_head = lookup_fd_head(table, pinfo, id, data, NULL);
#if 0
/* debug output of associated fragments. */
/* leave it here for future debugging sessions */
if(strcmp(pinfo->current_proto, "DCERPC") == 0) {
printf("proto:%s num:%u id:%u offset:%u len:%u more:%u visited:%u\n",
- pinfo->current_proto, pinfo->fd->num, id, frag_offset, frag_data_len, more_frags, pinfo->fd->flags.visited);
+ pinfo->current_proto, pinfo->num, id, frag_offset, frag_data_len, more_frags, pinfo->fd->flags.visited);
if(fd_head != NULL) {
for(fd_item=fd_head->next;fd_item;fd_item=fd_item->next){
printf("fd_frame:%u fd_offset:%u len:%u datalen:%u\n",
#endif
/*
- * "already_added" is true if "pinfo->fd->flags.visited" is true;
- * if "pinfo->fd->flags.visited", this isn't the first pass, so
- * we've already done all the reassembly and added all the
- * fragments.
- *
- * If it's not true, but "check_already_added" is true, just check
- * if we have seen this fragment before, i.e., if we have already
- * added it to reassembly.
- * That can be true even if "pinfo->fd->flags.visited" is false
- * since we sometimes might call a subdissector multiple times.
- * As an additional check, just make sure we have not already added
- * this frame to the reassembly list, if there is a reassembly list;
- * note that the first item in the reassembly list is not a
- * fragment, it's a data structure for the reassembled packet.
- * We don't check it because its "frame" member isn't initialized
- * to anything, and because it doesn't count in any case.
- *
- * And as another additional check, make sure the fragment offsets are
- * the same, as otherwise we get into trouble if multiple fragments
- * are in one PDU.
+ * Is this the first pass through the capture?
*/
- if (!already_added && check_already_added && fd_head != NULL) {
- if (pinfo->fd->num <= fd_head->frame) {
- for(fd_item=fd_head->next;fd_item;fd_item=fd_item->next){
- if(pinfo->fd->num==fd_item->frame && frag_offset==fd_item->offset){
- already_added=TRUE;
+ if (!pinfo->fd->flags.visited) {
+ /*
+ * Yes, so we could be doing reassembly. If
+ * "check_already_added" is true, and fd_head is non-null,
+ * meaning that this fragment would be added to an
+ * in-progress reassembly, check if we have seen this
+ * fragment before, i.e., if we have already added it to
+ * that reassembly. That can be true even on the first pass
+ * since we sometimes might call a subdissector multiple
+ * times.
+ *
+ * We check both the frame number and the fragment offset,
+ * so that we support multiple fragments from the same
+ * frame being added to the same reassembled PDU.
+ */
+ if (check_already_added && fd_head != NULL) {
+ /*
+ * fd_head->frame is the maximum of the frame
+ * numbers of all the fragments added to this
+ * reassembly; if this frame is later than that
+ * frame, we know it hasn't been added yet.
+ */
+ if (pinfo->num <= fd_head->frame) {
+ already_added = FALSE;
+ /*
+ * The first item in the reassembly list
+ * is not a fragment, it's a data structure
+ * for the reassembled packet, so we
+ * start checking with the next item.
+ */
+ for (fd_item = fd_head->next; fd_item;
+ fd_item = fd_item->next) {
+ if (pinfo->num == fd_item->frame &&
+ frag_offset == fd_item->offset) {
+ already_added = TRUE;
+ break;
+ }
+ }
+ if (already_added) {
+ /*
+ * Have we already finished
+ * reassembling?
+ */
+ if (fd_head->flags & FD_DEFRAGMENTED) {
+ /*
+ * Yes.
+ * XXX - can this ever happen?
+ */
+ THROW_MESSAGE(ReassemblyError,
+ "Frame already added in first pass");
+ } else {
+ /*
+ * No.
+ */
+ return NULL;
+ }
}
}
}
- }
- /* have we already added this frame ?*/
- if (already_added) {
+ } else {
+ /*
+ * No, so we've already done all the reassembly and added
+ * all the fragments. Do we have a reassembly and, if so,
+ * have we finished reassembling?
+ */
if (fd_head != NULL && fd_head->flags & FD_DEFRAGMENTED) {
+ /*
+ * Yes. This is probably being done after the
+ * first pass, and we've already done the work
+ * on the first pass.
+ *
+ * If the reassembly got a fatal error, throw that
+ * error again.
+ */
+ if (fd_head->error)
+ THROW_MESSAGE(ReassemblyError, fd_head->error);
+
+ /*
+ * Is it later in the capture than all of the
+ * fragments in the reassembly?
+ */
+ if (pinfo->num > fd_head->frame) {
+ /*
+ * Yes, so report this as a problem,
+ * possibly a retransmission.
+ */
+ THROW_MESSAGE(ReassemblyError, "New fragment overlaps old data (retransmission?)");
+ }
+
+ /*
+ * Does this fragment go past the end of the
+ * results of that reassembly?
+ */
+ if (frag_offset + frag_data_len > fd_head->datalen) {
+ /*
+ * Yes.
+ */
+ if (frag_offset >= fd_head->datalen) {
+ /*
+ * The fragment starts past the
+ * end of the reassembled data.
+ */
+ THROW_MESSAGE(ReassemblyError, "New fragment past old data limits");
+ } else {
+ /*
+ * The fragment starts before the end
+ * of the reassembled data, but
+ * runs past the end. That could
+ * just be a retransmission.
+ */
+ THROW_MESSAGE(ReassemblyError, "New fragment overlaps old data (retransmission?)");
+ }
+ }
+
return fd_head;
} else {
+ /*
+ * No.
+ */
return NULL;
}
}
if (fd_head==NULL){
/* not found, this must be the first snooped fragment for this
- * packet. Create list-head.
+ * packet. Create list-head.
*/
fd_head = new_head(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.
+ * Insert it into the hash table.
*/
- new_key = g_slice_new(fragment_key);
- 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);
+ insert_fd_head(table, fd_head, pinfo, id, data);
}
if (fragment_add_work(fd_head, tvb, offset, pinfo, frag_offset,
}
}
-fragment_data *
-fragment_add(tvbuff_t *tvb, const int offset, const packet_info *pinfo, const guint32 id,
- GHashTable *fragment_table, const guint32 frag_offset,
- const guint32 frag_data_len, const gboolean more_frags)
+fragment_head *
+fragment_add(reassembly_table *table, tvbuff_t *tvb, const int offset,
+ const packet_info *pinfo, const guint32 id, const void *data,
+ const guint32 frag_offset, const guint32 frag_data_len,
+ const gboolean more_frags)
{
- return fragment_add_common(tvb, offset, pinfo, id, fragment_table,
+ return fragment_add_common(table, tvb, offset, pinfo, id, data,
frag_offset, frag_data_len, more_frags, TRUE);
}
* For use when you can have multiple fragments in the same frame added
* to the same reassembled PDU, e.g. with ONC RPC-over-TCP.
*/
-fragment_data *
-fragment_add_multiple_ok(tvbuff_t *tvb, const int offset, const packet_info *pinfo,
- const guint32 id, GHashTable *fragment_table,
- const guint32 frag_offset, const guint32 frag_data_len,
- const gboolean more_frags)
+fragment_head *
+fragment_add_multiple_ok(reassembly_table *table, tvbuff_t *tvb,
+ const int offset, const packet_info *pinfo,
+ const guint32 id, const void *data,
+ const guint32 frag_offset,
+ const guint32 frag_data_len, const gboolean more_frags)
{
- return fragment_add_common(tvb, offset, pinfo, id, fragment_table,
+ return fragment_add_common(table, tvb, offset, pinfo, id, data,
frag_offset, frag_data_len, more_frags, FALSE);
}
-fragment_data *
-fragment_add_check(tvbuff_t *tvb, const int offset, const packet_info *pinfo,
- const guint32 id, GHashTable *fragment_table,
- GHashTable *reassembled_table, const guint32 frag_offset,
- const guint32 frag_data_len, const gboolean more_frags)
+fragment_head *
+fragment_add_check(reassembly_table *table, tvbuff_t *tvb, const int offset,
+ const packet_info *pinfo, const guint32 id,
+ const void *data, const guint32 frag_offset,
+ const guint32 frag_data_len, const gboolean more_frags)
{
reassembled_key reass_key;
- fragment_key key, *new_key, *old_key;
- gpointer orig_key, value;
- fragment_data *fd_head;
+ fragment_head *fd_head;
+ gpointer orig_key;
/*
* If this isn't the first pass, look for this frame in the table
* of reassembled packets.
*/
if (pinfo->fd->flags.visited) {
- reass_key.frame = pinfo->fd->num;
+ reass_key.frame = pinfo->num;
reass_key.id = id;
- return g_hash_table_lookup(reassembled_table, &reass_key);
+ return (fragment_head *)g_hash_table_lookup(table->reassembled_table, &reass_key);
}
- /* create key to search hash with */
- key.src = pinfo->src;
- key.dst = pinfo->dst;
- key.id = id;
-
/* Looks up a key in the GHashTable, returning the original key and the associated value
* and a gboolean which is TRUE if the key was found. This is useful if you need to free
* the memory allocated for the original key, for example before calling g_hash_table_remove()
*/
- if (!g_hash_table_lookup_extended(fragment_table, &key,
- &orig_key, &value)) {
+ fd_head = lookup_fd_head(table, pinfo, id, data, &orig_key);
+ if (fd_head == NULL) {
/* not found, this must be the first snooped fragment for this
- * packet. Create list-head.
+ * packet. Create list-head.
*/
fd_head = new_head(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 = g_slice_new(fragment_key);
- 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 */
- } else {
- /*
- * We found it.
+ * Save the key, for unhashing it later.
*/
- fd_head = value;
+ orig_key = insert_fd_head(table, fd_head, pinfo, id, data);
}
/*
* If this is a short frame, then we can't, and don't, do
* reassembly on it. We just give up.
*/
- if (tvb_reported_length(tvb) > tvb_length(tvb))
+ if (tvb_reported_length(tvb) > tvb_captured_length(tvb))
return NULL;
if (fragment_add_work(fd_head, tvb, offset, pinfo, frag_offset,
* 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);
+ fragment_unhash(table, orig_key);
/*
* Add this item to the table of reassembled packets.
*/
- fragment_reassembled(fd_head, pinfo, reassembled_table, id);
+ fragment_reassembled(table, fd_head, pinfo, id);
return fd_head;
} else {
/*
}
static void
-fragment_defragment_and_free (fragment_data *fd_head, const packet_info *pinfo)
+fragment_defragment_and_free (fragment_head *fd_head, const packet_info *pinfo)
{
- fragment_data *fd_i = NULL;
- fragment_data *last_fd = NULL;
+ fragment_item *fd_i = NULL;
+ fragment_item *last_fd = NULL;
guint32 dfpos = 0, size = 0;
- void *old_data = NULL;
+ tvbuff_t *old_tvb_data = NULL;
+ guint8 *data;
for(fd_i=fd_head->next;fd_i;fd_i=fd_i->next) {
if(!last_fd || last_fd->offset!=fd_i->offset){
}
/* store old data in case the fd_i->data pointers refer to it */
- old_data=fd_head->data;
- fd_head->data = g_malloc(size);
+ old_tvb_data=fd_head->tvb_data;
+ data = (guint8 *) g_malloc(size);
+ fd_head->tvb_data = tvb_new_real_data(data, size, size);
+ tvb_set_free_cb(fd_head->tvb_data, g_free);
fd_head->len = size; /* record size for caller */
/* add all data fragments */
if (fd_i->len) {
if(!last_fd || last_fd->offset != fd_i->offset) {
/* First fragment or in-sequence fragment */
- memcpy(fd_head->data+dfpos, fd_i->data, fd_i->len);
+ memcpy(data+dfpos, tvb_get_ptr(fd_i->tvb_data, 0, fd_i->len), fd_i->len);
dfpos += fd_i->len;
} else {
/* duplicate/retransmission/overlap */
fd_i->flags |= FD_OVERLAP;
fd_head->flags |= FD_OVERLAP;
if(last_fd->len != fd_i->len
- || memcmp(last_fd->data, fd_i->data, last_fd->len) ) {
+ || tvb_memeql(last_fd->tvb_data, 0, tvb_get_ptr(fd_i->tvb_data, 0, last_fd->len), last_fd->len) ) {
fd_i->flags |= FD_OVERLAPCONFLICT;
fd_head->flags |= FD_OVERLAPCONFLICT;
}
/* 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->flags & FD_NOT_MALLOCED )
- fd_i->flags &= ~FD_NOT_MALLOCED;
- else
- g_free(fd_i->data);
- fd_i->data=NULL;
+ if (fd_i->flags & FD_SUBSET_TVB)
+ fd_i->flags &= ~FD_SUBSET_TVB;
+ else if (fd_i->tvb_data)
+ tvb_free(fd_i->tvb_data);
+ fd_i->tvb_data=NULL;
}
- g_free(old_data);
+ if (old_tvb_data)
+ tvb_free(old_tvb_data);
/* mark this packet as defragmented.
* allows us to skip any trailing fragments.
*/
fd_head->flags |= FD_DEFRAGMENTED;
- fd_head->reassembled_in=pinfo->fd->num;
+ fd_head->reassembled_in=pinfo->num;
+ fd_head->reas_in_layer_num = pinfo->curr_layer_num;
}
/*
* The bsn for the first block is 0.
*/
static gboolean
-fragment_add_seq_work(fragment_data *fd_head, tvbuff_t *tvb, const int offset,
+fragment_add_seq_work(fragment_head *fd_head, tvbuff_t *tvb, const int offset,
const packet_info *pinfo, const guint32 frag_number,
- const guint32 frag_data_len, const gboolean more_frags,
- const guint32 flags _U_)
+ const guint32 frag_data_len, const gboolean more_frags)
{
- fragment_data *fd;
- fragment_data *fd_i;
- fragment_data *last_fd;
+ fragment_item *fd;
+ fragment_item *fd_i;
+ fragment_item *last_fd;
guint32 max, dfpos;
+ guint32 frag_number_work;
+
+ /* Enables the use of fragment sequence numbers, which do not start with 0 */
+ frag_number_work = frag_number;
+ if ( fd_head->fragment_nr_offset != 0 )
+ if ( frag_number_work >= fd_head->fragment_nr_offset )
+ frag_number_work = frag_number - fd_head->fragment_nr_offset;
/* 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 &&
+ if(fd_head->flags & FD_DEFRAGMENTED && frag_number_work >= 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->tvb_data ) {
if( fd_i->flags & FD_OVERLAP ) {
/* this is a duplicate of the previous
* fragment. */
- fd_i->data = fd_head->data + lastdfpos;
+ fd_i->tvb_data = tvb_new_subset_remaining(fd_head->tvb_data, lastdfpos);
} else {
- fd_i->data = fd_head->data + dfpos;
+ fd_i->tvb_data = tvb_new_subset_remaining(fd_head->tvb_data, dfpos);
lastdfpos = dfpos;
dfpos += fd_i->len;
}
- fd_i->flags |= FD_NOT_MALLOCED;
+ fd_i->flags |= FD_SUBSET_TVB;
}
fd_i->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS);
}
fd_head->flags &= (~FD_TOOLONGFRAGMENT) & (~FD_MULTIPLETAILS);
fd_head->datalen=0;
fd_head->reassembled_in=0;
+ fd_head->reas_in_layer_num = 0;
}
/* create new fd describing this fragment */
- fd = g_slice_new(fragment_data);
+ fd = g_slice_new(fragment_item);
fd->next = NULL;
fd->flags = 0;
- fd->frame = pinfo->fd->num;
- fd->offset = frag_number;
+ fd->frame = pinfo->num;
+ fd->offset = frag_number_work;
fd->len = frag_data_len;
- fd->data = NULL;
+ fd->tvb_data = NULL;
+ fd->error = NULL;
+
+ /* fd_head->frame is the maximum of the frame numbers of all the
+ * fragments added to the reassembly. */
+ if (fd->frame > fd_head->frame)
+ fd_head->frame = fd->frame;
if (!more_frags) {
/*
return TRUE;
}
DISSECTOR_ASSERT(fd_head->len >= dfpos + fd->len);
- if ( memcmp(fd_head->data+dfpos,
+ if (tvb_memeql(fd_head->tvb_data, dfpos,
tvb_get_ptr(tvb,offset,fd->len),fd->len) ){
/*
* They have the same length, but the
/* If we have reached this point, the packet is not defragmented yet.
* Save all payload in a buffer until we can defragment.
- * XXX - what if we didn't capture the entire fragment due
- * to a too-short snapshot length?
*/
/* check len, there may be a fragment with 0 len, that is actually the tail */
if (fd->len) {
- fd->data = g_malloc(fd->len);
- tvb_memcpy(tvb, fd->data, offset, fd->len);
+ if (!tvb_bytes_exist(tvb, offset, fd->len)) {
+ /* abort if we didn't capture the entire fragment due
+ * to a too-short snapshot length */
+ g_slice_free(fragment_item, fd);
+ return FALSE;
+ }
+
+ fd->tvb_data = tvb_clone_offset_len(tvb, offset, fd->len);
}
LINK_FRAG(fd_head,fd);
if( !(fd_head->flags & FD_DATALEN_SET) ){
- /* if we dont know the sequence number of the last fragment,
+ /* if we don't know the sequence number of the last fragment,
* there are definitely still missing packets. Cheaper than
* the check below.
*/
* This function assumes frag_number being a block sequence number.
* The bsn for the first block is 0.
*/
-fragment_data *
-fragment_add_seq(tvbuff_t *tvb, const int offset, const packet_info *pinfo, const guint32 id,
- GHashTable *fragment_table, const guint32 frag_number,
- const guint32 frag_data_len, const gboolean more_frags)
-{
- fragment_key key;
-
- /* create key to search hash with */
- key.src = pinfo->src;
- key.dst = pinfo->dst;
- key.id = id;
-
- 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, const int offset, const packet_info *pinfo, const guint32 id,
- void *v_act_id,
- GHashTable *fragment_table, const guint32 frag_number,
- const guint32 frag_data_len, const gboolean more_frags)
+static fragment_head *
+fragment_add_seq_common(reassembly_table *table, tvbuff_t *tvb,
+ const int offset, const packet_info *pinfo,
+ const guint32 id, const void *data,
+ guint32 frag_number, const guint32 frag_data_len,
+ const gboolean more_frags, const guint32 flags,
+ gpointer *orig_keyp)
{
- e_uuid_t *act_id = (e_uuid_t *)v_act_id;
- dcerpc_fragment_key key;
+ fragment_head *fd_head;
+ gpointer orig_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, const int offset, const packet_info *pinfo,
- void *key, fragment_key_copier key_copier,
- GHashTable *fragment_table, guint32 frag_number,
- const guint32 frag_data_len, const gboolean more_frags,
- const guint32 flags)
-{
- fragment_data *fd_head;
- fd_head = g_hash_table_lookup(fragment_table, key);
+ fd_head = lookup_fd_head(table, pinfo, id, data, &orig_key);
/* have we already seen this frame ?*/
if (pinfo->fd->flags.visited) {
if (fd_head != NULL && fd_head->flags & FD_DEFRAGMENTED) {
+ if (orig_keyp != NULL)
+ *orig_keyp = orig_key;
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.
+ * packet. Create list-head.
*/
fd_head= new_head(FD_BLOCKSEQUENCE);
* fragment_add_seq_check will then add it to the table
* of reassembled packets.
*/
- fd_head->reassembled_in=pinfo->fd->num;
+ if (orig_keyp != NULL)
+ *orig_keyp = NULL;
+ fd_head->reassembled_in=pinfo->num;
+ fd_head->reas_in_layer_num = pinfo->curr_layer_num;
return fd_head;
}
- /*
- * We're going to use the key to insert the fragment,
- * so copy it to a long-term store.
- */
- if(key_copier != NULL)
- key = key_copier(key);
- g_hash_table_insert(fragment_table, key, fd_head);
+ orig_key = insert_fd_head(table, fd_head, pinfo, id, data);
+ if (orig_keyp != NULL)
+ *orig_keyp = orig_key;
/*
* If we weren't given an initial fragment number,
if (flags & REASSEMBLE_FLAGS_NO_FRAG_NUMBER)
frag_number = 0;
} else {
+ if (orig_keyp != NULL)
+ *orig_keyp = orig_key;
+
if (flags & REASSEMBLE_FLAGS_NO_FRAG_NUMBER) {
- fragment_data *fd;
+ fragment_item *fd;
/*
* If we weren't given an initial fragment number,
* use the next expected fragment number as the fragment
}
}
- /*
- * 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, flags)) {
+ frag_number, frag_data_len, more_frags)) {
/*
* Reassembly is complete.
*/
}
}
+fragment_head *
+fragment_add_seq(reassembly_table *table, tvbuff_t *tvb, const int offset,
+ const packet_info *pinfo, const guint32 id, const void *data,
+ const guint32 frag_number, const guint32 frag_data_len,
+ const gboolean more_frags, const guint32 flags)
+{
+ return fragment_add_seq_common(table, tvb, offset, pinfo, id, data,
+ frag_number, frag_data_len,
+ more_frags, flags, NULL);
+}
+
/*
* This does the work for "fragment_add_seq_check()" and
* "fragment_add_seq_next()".
* This function assumes frag_number being a block sequence number.
* The bsn for the first block is 0.
*
- * If "no_frag_number" is TRUE, it uses the next expected fragment number
+ * If REASSEMBLE_FLAGS_NO_FRAG_NUMBER, it uses the next expected fragment number
* as the fragment number if there is a reassembly in progress, otherwise
* it uses 0.
*
- * If "no_frag_number" is FALSE, it uses the "frag_number" argument as
+ * If not REASSEMBLE_FLAGS_NO_FRAG_NUMBER, it uses the "frag_number" argument as
* the fragment number.
*
* If this is the first fragment seen for this datagram, a new
- * "fragment_data" structure is allocated to refer to the reassembled
+ * "fragment_head" structure is allocated to refer to the reassembled
* packet.
*
* This fragment is added to the linked list of fragments for this packet.
*
* XXX - Should we simply return NULL for zero-length fragments?
*/
-static fragment_data *
-fragment_add_seq_check_work(tvbuff_t *tvb, const int offset,
- const packet_info *pinfo, const guint32 id,
- GHashTable *fragment_table,
- GHashTable *reassembled_table,
+static fragment_head *
+fragment_add_seq_check_work(reassembly_table *table, tvbuff_t *tvb,
+ const int offset, const packet_info *pinfo,
+ const guint32 id, const void *data,
const guint32 frag_number,
const guint32 frag_data_len,
const gboolean more_frags, const guint32 flags)
{
reassembled_key reass_key;
- fragment_key key;
- fragment_data *fd_head;
+ fragment_head *fd_head;
+ gpointer orig_key;
/*
* Have we already seen this frame?
* If so, look for it in the table of reassembled packets.
*/
if (pinfo->fd->flags.visited) {
- reass_key.frame = pinfo->fd->num;
+ reass_key.frame = pinfo->num;
reass_key.id = id;
- return g_hash_table_lookup(reassembled_table, &reass_key);
+ return (fragment_head *)g_hash_table_lookup(table->reassembled_table, &reass_key);
}
- /* create key to search hash with */
- key.src = pinfo->src;
- key.dst = pinfo->dst;
- key.id = id;
-
- 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);
+ fd_head = fragment_add_seq_common(table, tvb, offset, pinfo, id, data,
+ frag_number, frag_data_len,
+ more_frags,
+ flags,
+ &orig_key);
if (fd_head) {
- gpointer orig_key;
-
- 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;
- }
-
/*
* Reassembly is complete.
- * Remove this from the table of in-progress
- * reassemblies, add it to the table of
- * reassembled packets, and return it.
+ *
+ * If this is in the table of in-progress reassemblies,
+ * remove it from that table. (It could be that this
+ * was the first and last fragment, so that no
+ * reassembly was done.)
*/
- 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);
- }
+ if (orig_key != NULL)
+ fragment_unhash(table, orig_key);
/*
* Add this item to the table of reassembled packets.
*/
- fragment_reassembled(fd_head, pinfo, reassembled_table, id);
+ fragment_reassembled(table, fd_head, pinfo, id);
return fd_head;
} else {
/*
}
}
-fragment_data *
-fragment_add_seq_check(tvbuff_t *tvb, const int offset,
+fragment_head *
+fragment_add_seq_check(reassembly_table *table, tvbuff_t *tvb, const int offset,
const packet_info *pinfo, const guint32 id,
- GHashTable *fragment_table,
- GHashTable *reassembled_table, const guint32 frag_number,
- const guint32 frag_data_len, const gboolean more_frags)
+ const void *data,
+ const guint32 frag_number, const guint32 frag_data_len,
+ const gboolean more_frags)
{
- return fragment_add_seq_check_work(tvb, offset, pinfo, id,
- fragment_table, reassembled_table,
+ return fragment_add_seq_check_work(table, tvb, offset, pinfo, id, data,
frag_number, frag_data_len,
more_frags, 0);
}
-fragment_data *
-fragment_add_seq_802_11(tvbuff_t *tvb, const int offset,
- const packet_info *pinfo, const guint32 id,
- GHashTable *fragment_table,
- GHashTable *reassembled_table,
+fragment_head *
+fragment_add_seq_802_11(reassembly_table *table, tvbuff_t *tvb,
+ const int offset, const packet_info *pinfo,
+ const guint32 id, const void *data,
const guint32 frag_number, const guint32 frag_data_len,
const gboolean more_frags)
{
- return fragment_add_seq_check_work(tvb, offset, pinfo, id,
- fragment_table, reassembled_table,
+ return fragment_add_seq_check_work(table, tvb, offset, pinfo, id, data,
frag_number, frag_data_len,
more_frags,
REASSEMBLE_FLAGS_802_11_HACK);
}
-fragment_data *
-fragment_add_seq_next(tvbuff_t *tvb, const int offset, const packet_info *pinfo,
- const guint32 id, GHashTable *fragment_table,
- GHashTable *reassembled_table, const guint32 frag_data_len,
+fragment_head *
+fragment_add_seq_next(reassembly_table *table, tvbuff_t *tvb, const int offset,
+ const packet_info *pinfo, const guint32 id,
+ const void *data, const guint32 frag_data_len,
const gboolean more_frags)
{
- return fragment_add_seq_check_work(tvb, offset, pinfo, id,
- fragment_table, reassembled_table, 0,
- frag_data_len, more_frags,
+ /* Use a dummy frag_number (0), it is ignored since
+ * REASSEMBLE_FLAGS_NO_FRAG_NUMBER is set. */
+ return fragment_add_seq_check_work(table, tvb, offset, pinfo, id, data,
+ 0, frag_data_len, more_frags,
REASSEMBLE_FLAGS_NO_FRAG_NUMBER);
}
+static void
+fragment_add_seq_single_move(reassembly_table *table, const packet_info *pinfo,
+ const guint32 id, const void *data,
+ const guint32 offset)
+{
+ fragment_head *fh, *new_fh;
+ fragment_item *fd, *prev_fd;
+ tvbuff_t *old_tvb_data;
+ if (offset == 0) {
+ return;
+ }
+ fh = lookup_fd_head(table, pinfo, id, data, NULL);
+ if (fh == NULL) {
+ /* Shouldn't be called this way.
+ * Probably wouldn't hurt to just create fh in this case. */
+ g_assert_not_reached();
+ return;
+ }
+ if (fh->flags & FD_DATALEN_SET && fh->datalen <= offset) {
+ /* Don't take from past the end. <= because we don't
+ * want to take a First fragment from the next one
+ * either */
+ return;
+ }
+ new_fh = lookup_fd_head(table, pinfo, id+offset, data, NULL);
+ if (new_fh != NULL) {
+ /* Attach to the end of the sorted list. */
+ for(prev_fd = fh; prev_fd->next != NULL; prev_fd=prev_fd->next) {}
+ /* Don't take a reassembly starting with a First fragment. */
+ fd = new_fh->next;
+ if (fd && fd->offset != 0) {
+ prev_fd->next = fd;
+ for (; fd; fd=fd->next) {
+ fd->offset += offset;
+ if (fh->frame < fd->frame) {
+ fh->frame = fd->frame;
+ }
+ }
+ /* If previously found a Last fragment,
+ * transfer that info to the new one. */
+ if (new_fh->flags & FD_DATALEN_SET) {
+ fh->flags |= FD_DATALEN_SET;
+ fh->datalen = new_fh->datalen + offset;
+ }
+ /* Now remove and delete */
+ new_fh->next = NULL;
+ old_tvb_data = fragment_delete(table, pinfo, id+offset, data);
+ if (old_tvb_data)
+ tvb_free(old_tvb_data);
+ }
+ }
+}
+
+static fragment_head *
+fragment_add_seq_single_work(reassembly_table *table, tvbuff_t *tvb,
+ const int offset, const packet_info *pinfo,
+ const guint32 id, const void* data,
+ const guint32 frag_data_len,
+ const gboolean first, const gboolean last,
+ const guint32 max_frags, const guint32 max_age,
+ const guint32 flags)
+{
+ reassembled_key reass_key;
+ tvbuff_t *old_tvb_data;
+ gpointer orig_key;
+ fragment_head *fh, *new_fh;
+ fragment_item *fd, *prev_fd;
+ guint32 frag_number, tmp_offset;
+ /* Have we already seen this frame?
+ * If so, look for it in the table of reassembled packets.
+ * Note here we store in the reassembly table by the single sequence
+ * number rather than the sequence number of the First fragment. */
+ if (pinfo->fd->flags.visited) {
+ reass_key.frame = pinfo->num;
+ reass_key.id = id;
+ fh = (fragment_head *)g_hash_table_lookup(table->reassembled_table, &reass_key);
+ return fh;
+ }
+ /* First let's figure out where we want to add our new fragment */
+ fh = NULL;
+ if (first) {
+ frag_number = 0;
+ fh = lookup_fd_head(table, pinfo, id-frag_number, data, NULL);
+ if ((flags & REASSEMBLE_FLAGS_AGING) &&
+ fh && ((fh->frame + max_age) < pinfo->num)) {
+ old_tvb_data = fragment_delete(table, pinfo, id-frag_number, data);
+ if (old_tvb_data)
+ tvb_free(old_tvb_data);
+ fh = NULL;
+ }
+ if (fh == NULL) {
+ /* Not found. Create list-head. */
+ fh = new_head(FD_BLOCKSEQUENCE);
+ insert_fd_head(table, fh, pinfo, id-frag_number, data);
+ }
+ /* As this is the first fragment, we might have added segments
+ * for this reassembly to the previous one in-progress. */
+ fd = NULL;
+ for (frag_number=1; frag_number < max_frags; frag_number++) {
+ new_fh = lookup_fd_head(table, pinfo, id-frag_number, data, NULL);
+ if (new_fh != NULL) {
+ prev_fd = new_fh;
+ new_fh->frame = 0;
+ for (fd=new_fh->next; fd && fd->offset < frag_number; fd=fd->next) {
+ prev_fd = fd;
+ if (new_fh->frame < fd->frame) {
+ new_fh->frame = fd->frame;
+ }
+ }
+ prev_fd->next = NULL;
+ break;
+ }
+ }
+ if (fd != NULL) {
+ tmp_offset = 0;
+ for (prev_fd = fd; prev_fd; prev_fd = prev_fd->next) {
+ prev_fd->offset -= frag_number;
+ tmp_offset = prev_fd->offset;
+ if (fh->frame < prev_fd->frame) {
+ fh->frame = prev_fd->frame;
+ }
+ }
+ MERGE_FRAG(fh, fd);
+ if (new_fh != NULL) {
+ /* If we've moved a Last packet, change datalen.
+ * Second part of this test prob. redundant? */
+ if (new_fh->flags & FD_DATALEN_SET &&
+ new_fh->datalen >= frag_number) {
+ fh->flags |= FD_DATALEN_SET;
+ fh->datalen = new_fh->datalen - frag_number;
+ new_fh->flags &= ~FD_DATALEN_SET;
+ new_fh->datalen = 0;
+ }
+ /* If we've moved all the fragments,
+ * delete the old head */
+ if (new_fh->next == NULL) {
+ old_tvb_data = fragment_delete(table, pinfo, id-frag_number, data);
+ if (old_tvb_data)
+ tvb_free(old_tvb_data);
+ }
+ } else {
+ /* Look forward and take off the next (this is
+ * necessary in some edge cases where max_frags
+ * prevented some fragments from going on the
+ * previous First, but they can go on this one. */
+ fragment_add_seq_single_move(table, pinfo, id,
+ data, tmp_offset);
+ }
+ }
+ frag_number = 0; /* For the rest of the function */
+ } else {
+ for (frag_number=1; frag_number < max_frags; frag_number++) {
+ fh = lookup_fd_head(table, pinfo, id-frag_number, data, NULL);
+ if ((flags & REASSEMBLE_FLAGS_AGING) &&
+ fh && ((fh->frame + max_age) < pinfo->num)) {
+ old_tvb_data = fragment_delete(table, pinfo, id-frag_number, data);
+ if (old_tvb_data)
+ tvb_free(old_tvb_data);
+ fh = NULL;
+ }
+ if (fh != NULL) {
+ if (fh->flags & FD_DATALEN_SET &&
+ fh->datalen < frag_number) {
+ /* This fragment is after the Last
+ * fragment, so must go after here. */
+ fh = NULL;
+ }
+ break;
+ }
+ }
+ if (fh == NULL) { /* Didn't find location, use default */
+ frag_number = 1;
+ /* Already looked for frag_number 1, so just create */
+ fh = new_head(FD_BLOCKSEQUENCE);
+ insert_fd_head(table, fh, pinfo, id-frag_number, data);
+ }
+ }
+ if (last) {
+ /* Look for fragments past the end set by this Last fragment. */
+ prev_fd = fh;
+ for (fd=fh->next; fd && fd->offset <= frag_number; fd=fd->next) {
+ prev_fd = fd;
+ }
+ /* fd is now all fragments offset > frag_number (the Last).
+ * It shouldn't have a fragment with offset frag_number+1,
+ * as that would be a First fragment not marked as such.
+ * However, this can happen if we had unreassembled fragments
+ * (missing, or at the start of the capture) and we've also
+ * looped around on the sequence numbers. It can also happen
+ * if bit errors mess up Last or First. */
+ if (fd != NULL) {
+ prev_fd->next = NULL;
+ fh->frame = 0;
+ for (prev_fd=fh->next; prev_fd; prev_fd=prev_fd->next) {
+ if (fh->frame < prev_fd->frame) {
+ fh->frame = prev_fd->frame;
+ }
+ }
+ while (fd && fd->offset == frag_number+1) {
+ /* Definitely have bad data here. Best to
+ * delete these and leave unreassembled. */
+ fragment_item *tmp_fd;
+ tmp_fd=fd->next;
+
+ if (fd->tvb_data && !(fd->flags & FD_SUBSET_TVB))
+ tvb_free(fd->tvb_data);
+ g_slice_free(fragment_item, fd);
+ fd=tmp_fd;
+ }
+ }
+ if (fd != NULL) {
+ /* Move these onto the next frame. */
+ new_fh = lookup_fd_head(table, pinfo, id+1, data, NULL);
+ if (new_fh==NULL) {
+ /* Not found. Create list-head. */
+ new_fh = new_head(FD_BLOCKSEQUENCE);
+ insert_fd_head(table, new_fh, pinfo, id+1, data);
+ }
+ tmp_offset = 0;
+ for (prev_fd = fd; prev_fd; prev_fd = prev_fd->next) {
+ prev_fd->offset -= (frag_number+1);
+ tmp_offset = prev_fd->offset;
+ if (new_fh->frame < fd->frame) {
+ new_fh->frame = fd->frame;
+ }
+ }
+ MERGE_FRAG(new_fh, fd);
+ /* If we previously found a different Last fragment,
+ * transfer that information to the new reassembly. */
+ if (fh->flags & FD_DATALEN_SET &&
+ fh->datalen > frag_number) {
+ new_fh->flags |= FD_DATALEN_SET;
+ new_fh->datalen = fh->datalen - (frag_number+1);
+ fh->flags &= ~FD_DATALEN_SET;
+ fh->datalen = 0;
+ } else {
+ /* Look forward and take off the next (this is
+ * necessary in some edge cases where max_frags
+ * prevented some fragments from going on the
+ * previous First, but they can go on this one. */
+ fragment_add_seq_single_move(table, pinfo, id+1,
+ data, tmp_offset);
+ }
+ }
+ } else {
+ fragment_add_seq_single_move(table, pinfo, id-frag_number, data,
+ frag_number+1);
+ }
+ /* Having cleaned up everything, finally ready to add our new
+ * fragment. Note that only this will ever complete a reassembly. */
+ fh = fragment_add_seq_common(table, tvb, offset, pinfo,
+ id-frag_number, data,
+ frag_number, frag_data_len,
+ !last, 0, &orig_key);
+ if (fh) {
+ /*
+ * Reassembly is complete.
+ *
+ * If this is in the table of in-progress reassemblies,
+ * remove it from that table. (It could be that this
+ * was the first and last fragment, so that no
+ * reassembly was done.)
+ */
+ if (orig_key != NULL)
+ fragment_unhash(table, orig_key);
+
+ /*
+ * Add this item to the table of reassembled packets.
+ */
+ fragment_reassembled_single(table, fh, pinfo, id-frag_number);
+ return fh;
+ } else {
+ /*
+ * Reassembly isn't complete.
+ */
+ return NULL;
+ }
+}
+
+fragment_head *
+fragment_add_seq_single(reassembly_table *table, tvbuff_t *tvb,
+ const int offset, const packet_info *pinfo,
+ const guint32 id, const void* data,
+ const guint32 frag_data_len,
+ const gboolean first, const gboolean last,
+ const guint32 max_frags)
+{
+ return fragment_add_seq_single_work(table, tvb, offset, pinfo,
+ id, data, frag_data_len,
+ first, last, max_frags, 0, 0);
+}
+
+fragment_head *
+fragment_add_seq_single_aging(reassembly_table *table, tvbuff_t *tvb,
+ const int offset, const packet_info *pinfo,
+ const guint32 id, const void* data,
+ const guint32 frag_data_len,
+ const gboolean first, const gboolean last,
+ const guint32 max_frags, const guint32 max_age)
+{
+ return fragment_add_seq_single_work(table, tvb, offset, pinfo,
+ id, data, frag_data_len,
+ first, last, max_frags, max_age,
+ REASSEMBLE_FLAGS_AGING);
+}
+
void
-fragment_start_seq_check(const packet_info *pinfo, const guint32 id, GHashTable *fragment_table,
+fragment_start_seq_check(reassembly_table *table, const packet_info *pinfo,
+ const guint32 id, const void *data,
const guint32 tot_len)
{
- fragment_key key, *new_key;
- fragment_data *fd_head;
+ fragment_head *fd_head;
/* Have we already seen this frame ?*/
if (pinfo->fd->flags.visited) {
return;
}
- /* Create key to search hash with */
- key.src = pinfo->src;
- key.dst = pinfo->dst;
- key.id = id;
-
- /* Check if fragment data exist for this key */
- fd_head = g_hash_table_lookup(fragment_table, &key);
+ /* Check if fragment data exists */
+ fd_head = lookup_fd_head(table, pinfo, id, data, NULL);
if (fd_head == NULL) {
/* Create list-head. */
- fd_head = g_slice_new(fragment_data);
+ fd_head = g_slice_new(fragment_head);
fd_head->next = NULL;
- fd_head->datalen = tot_len;
+ fd_head->frame = 0;
fd_head->offset = 0;
fd_head->len = 0;
- fd_head->flags = FD_BLOCKSEQUENCE|FD_DATALEN_SET;
- fd_head->data = NULL;
+ fd_head->fragment_nr_offset = 0;
+ fd_head->datalen = tot_len;
fd_head->reassembled_in = 0;
- /*
- * We're going to use the key to insert the fragment,
- * so copy it to a long-term store.
- */
- new_key = fragment_key_copy(&key);
- g_hash_table_insert(fragment_table, new_key, fd_head);
+ fd_head->reas_in_layer_num = 0;
+ fd_head->flags = FD_BLOCKSEQUENCE|FD_DATALEN_SET;
+ fd_head->tvb_data = NULL;
+ fd_head->error = NULL;
+
+ insert_fd_head(table, fd_head, pinfo, id, data);
}
}
-fragment_data *
-fragment_end_seq_next(const packet_info *pinfo, const guint32 id, GHashTable *fragment_table,
- GHashTable *reassembled_table)
+fragment_head *
+fragment_end_seq_next(reassembly_table *table, const packet_info *pinfo,
+ const guint32 id, const void *data)
{
reassembled_key reass_key;
reassembled_key *new_key;
- fragment_key key;
- fragment_data *fd_head;
+ fragment_head *fd_head;
+ gpointer orig_key;
/*
* Have we already seen this frame?
* If so, look for it in the table of reassembled packets.
*/
if (pinfo->fd->flags.visited) {
- reass_key.frame = pinfo->fd->num;
+ reass_key.frame = pinfo->num;
reass_key.id = id;
- return g_hash_table_lookup(reassembled_table, &reass_key);
+ return (fragment_head *)g_hash_table_lookup(table->reassembled_table, &reass_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);
+ fd_head = lookup_fd_head(table, pinfo, id, data, &orig_key);
if (fd_head) {
- gpointer orig_key;
-
- if (fd_head->flags & FD_DATA_NOT_PRESENT) {
- /* No data added */
- return NULL;
- }
-
fd_head->datalen = fd_head->offset;
fd_head->flags |= FD_DATALEN_SET;
fragment_defragment_and_free (fd_head, pinfo);
/*
- * 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.
*/
- 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);
- }
+ fragment_unhash(table, orig_key);
/*
* Add this item to the table of reassembled packets.
*/
- fragment_reassembled(fd_head, pinfo, reassembled_table, id);
+ fragment_reassembled(table, fd_head, pinfo, id);
if (fd_head->next != NULL) {
new_key = g_slice_new(reassembled_key);
- new_key->frame = pinfo->fd->num;
+ new_key->frame = pinfo->num;
new_key->id = id;
- g_hash_table_insert(reassembled_table, new_key, fd_head);
+ g_hash_table_insert(table->reassembled_table, new_key, fd_head);
}
return fd_head;
*/
tvbuff_t *
process_reassembled_data(tvbuff_t *tvb, const int offset, packet_info *pinfo,
- const char *name, fragment_data *fd_head, const fragment_items *fit,
+ const char *name, fragment_head *fd_head, const fragment_items *fit,
gboolean *update_col_infop, proto_tree *tree)
{
tvbuff_t *next_tvb;
gboolean update_col_info;
proto_item *frag_tree_item;
- if (fd_head != NULL && pinfo->fd->num == fd_head->reassembled_in) {
+ if (fd_head != NULL && pinfo->num == fd_head->reassembled_in && pinfo->curr_layer_num == fd_head->reas_in_layer_num) {
/*
* OK, we've reassembled this.
* Is this something that's been reassembled from more
/*
* Yes.
* Allocate a new tvbuff, referring to the
- * reassembled payload.
- */
- if (fd_head->flags & FD_BLOCKSEQUENCE) {
- next_tvb = tvb_new_real_data(fd_head->data,
- fd_head->len, fd_head->len);
- } else {
- next_tvb = tvb_new_real_data(fd_head->data,
- fd_head->datalen, fd_head->datalen);
- }
-
- /*
- * Add the tvbuff to the list of tvbuffs to which
+ * reassembled payload, and set
+ * the tvbuff to the list of tvbuffs to which
* the tvbuff we were handed refers, so it'll get
* cleaned up when that tvbuff is cleaned up.
*/
- tvb_set_child_real_data_tvbuff(tvb, next_tvb);
+ next_tvb = tvb_new_chain(tvb, fd_head->tvb_data);
/* Add the defragmented data to the data source list. */
add_new_data_source(pinfo, next_tvb, name);
* it in the top-level item for that subtree.
*/
static void
-show_fragment(fragment_data *fd, const int offset, const fragment_items *fit,
+show_fragment(fragment_item *fd, const int offset, const fragment_items *fit,
proto_tree *ft, proto_item *fi, const gboolean first_frag,
const guint32 count, tvbuff_t *tvb, packet_info *pinfo)
{
} else {
name = g_strdup(proto_registrar_get_name(*(fit->hf_fragments)));
}
- proto_item_set_text(fi, "%u %s (%u byte%s): ", count, name, tvb_length(tvb),
- plurality(tvb_length(tvb), "", "s"));
+ proto_item_set_text(fi, "%u %s (%u byte%s): ", count, name, tvb_captured_length(tvb),
+ plurality(tvb_captured_length(tvb), "", "s"));
g_free(name);
} else {
proto_item_append_text(fi, ", ");
}
static gboolean
-show_fragment_errs_in_col(fragment_data *fd_head, const fragment_items *fit,
+show_fragment_errs_in_col(fragment_head *fd_head, const fragment_items *fit,
packet_info *pinfo)
{
if (fd_head->flags & (FD_OVERLAPCONFLICT
|FD_MULTIPLETAILS|FD_TOOLONGFRAGMENT) ) {
- if (check_col(pinfo->cinfo, COL_INFO)) {
- col_add_fstr(pinfo->cinfo, COL_INFO,
- "[Illegal %s]", fit->tag);
- return TRUE;
- }
+ col_add_fstr(pinfo->cinfo, COL_INFO, "[Illegal %s]", fit->tag);
+ return TRUE;
}
return FALSE;
or FALSE if fragmentation was ok.
*/
gboolean
-show_fragment_tree(fragment_data *fd_head, const fragment_items *fit,
+show_fragment_tree(fragment_head *fd_head, const fragment_items *fit,
proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, proto_item **fi)
{
- fragment_data *fd;
+ fragment_item *fd;
proto_tree *ft;
gboolean first_frag;
guint32 count = 0;
if (fit->hf_reassembled_length) {
proto_item *fli = proto_tree_add_uint(ft, *(fit->hf_reassembled_length),
- tvb, 0, 0, tvb_length (tvb));
+ tvb, 0, 0, tvb_captured_length (tvb));
PROTO_ITEM_SET_GENERATED(fli);
}
if (fit->hf_reassembled_data) {
proto_item *fli = proto_tree_add_item(ft, *(fit->hf_reassembled_data),
- tvb, 0, tvb_length(tvb), ENC_NA);
+ tvb, 0, tvb_captured_length(tvb), ENC_NA);
PROTO_ITEM_SET_GENERATED(fli);
}
or FALSE if fragmentation was ok.
*/
gboolean
-show_fragment_seq_tree(fragment_data *fd_head, const fragment_items *fit,
+show_fragment_seq_tree(fragment_head *fd_head, const fragment_items *fit,
proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, proto_item **fi)
{
guint32 offset, next_offset, count = 0;
- fragment_data *fd, *last_fd;
+ fragment_item *fd, *last_fd;
proto_tree *ft;
gboolean first_frag;
if (fit->hf_reassembled_length) {
proto_item *fli = proto_tree_add_uint(ft, *(fit->hf_reassembled_length),
- tvb, 0, 0, tvb_length (tvb));
+ tvb, 0, 0, tvb_captured_length (tvb));
+ PROTO_ITEM_SET_GENERATED(fli);
+ }
+
+ if (fit->hf_reassembled_data) {
+ proto_item *fli = proto_tree_add_item(ft, *(fit->hf_reassembled_data),
+ tvb, 0, tvb_captured_length(tvb), ENC_NA);
PROTO_ITEM_SET_GENERATED(fli);
}
return show_fragment_errs_in_col(fd_head, fit, pinfo);
}
+static void
+reassembly_table_init_reg_table(gpointer p, gpointer user_data _U_)
+{
+ register_reassembly_table_t* reg_table = (register_reassembly_table_t*)p;
+ reassembly_table_init(reg_table->table, reg_table->funcs);
+}
+
+static void
+reassembly_table_init_reg_tables(void)
+{
+ g_list_foreach(reassembly_table_list, reassembly_table_init_reg_table, NULL);
+}
+
+static void
+reassembly_table_cleanup_reg_table(gpointer p, gpointer user_data _U_)
+{
+ register_reassembly_table_t* reg_table = (register_reassembly_table_t*)p;
+ reassembly_table_destroy(reg_table->table);
+}
+
+static void
+reassembly_table_cleanup_reg_tables(void)
+{
+ g_list_foreach(reassembly_table_list, reassembly_table_cleanup_reg_table, NULL);
+}
+
+void reassembly_tables_init(void)
+{
+ register_init_routine(&reassembly_table_init_reg_tables);
+ register_cleanup_routine(&reassembly_table_cleanup_reg_tables);
+}
+
+static void
+reassembly_table_free(gpointer p, gpointer user_data _U_)
+{
+ register_reassembly_table_t* reg_table = (register_reassembly_table_t*)p;
+ reassembly_table_destroy(reg_table->table);
+ g_free(reg_table);
+}
+
+void
+reassembly_table_cleanup(void)
+{
+ g_list_foreach(reassembly_table_list, reassembly_table_free, NULL);
+ g_list_free(reassembly_table_list);
+}
+
/*
- * Local Variables:
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
* c-basic-offset: 8
- * indent-tabs-mode: t
* tab-width: 8
+ * indent-tabs-mode: t
* End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
*/