diff --git a/epan/Makefile.common b/epan/Makefile.common index fb1adb9dfa..760e544105 100644 --- a/epan/Makefile.common +++ b/epan/Makefile.common @@ -76,6 +76,7 @@ LIBETHEREAL_SRC = \ stat_cmd_args.c \ stats_tree.c \ strutil.c \ + stream.c \ t35.c \ tap.c \ timestamp.c \ @@ -165,6 +166,7 @@ LIBETHEREAL_INCLUDES = \ stat_cmd_args.h \ stats_tree.h \ stats_tree_priv.h \ + stream.h \ strutil.h \ t35.h \ tap.h \ diff --git a/epan/packet.c b/epan/packet.c index 45b073f1fb..e3c62828bd 100644 --- a/epan/packet.c +++ b/epan/packet.c @@ -54,6 +54,7 @@ #include "emem.h" #include +#include static gint proto_malformed = -1; static dissector_handle_t frame_handle = NULL; @@ -146,6 +147,9 @@ init_dissection(void) may free up space for fragments, which they find by using the data structures that "reassemble_init()" frees. */ reassemble_init(); + + /* Initialise the stream-handling tables */ + stream_init(); } void diff --git a/epan/stream.c b/epan/stream.c new file mode 100644 index 0000000000..3e72efe8d5 --- /dev/null +++ b/epan/stream.c @@ -0,0 +1,532 @@ +/* stream.c + * + * Definititions for handling circuit-switched protocols + * which are handled as streams, and don't have lengths + * and IDs such as are required for reassemble.h + * + * $Id: stream.c,v 1.9 2005/07/27 22:47:55 richardv Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include + +/* number of streams to allocate memory for at once */ +#define MEMCHUNK_STREAM_COUNT 20 + +/* ditto pdus */ +#define MEMCHUNK_PDU_COUNT 100 + +/* ditto fragments */ +#define MEMCHUNK_FRAGMENT_COUNT 100 + + +typedef struct { + fragment_data *fd_head; /* the reassembled data, NULL + * until we add the last fragment */ + guint32 pdu_number; /* Number of this PDU within the stream */ + + /* id of this pdu (globally unique) */ + guint32 id; +} stream_pdu_t; + + +struct stream_pdu_fragment +{ + guint32 len; /* the length of this fragment */ + stream_pdu_t *pdu; + gboolean final_fragment; +}; + +struct stream { + /* the key used to add this stream to stream_hash */ + struct stream_key *key; + + /* pdu to add the next fragment to, or NULL if we need to start + * a new PDU. + */ + stream_pdu_t *current_pdu; + + /* number of PDUs added to this stream so far */ + guint32 pdu_counter; + + /* the framenumber and offset of the last fragment added; + used for sanity-checking */ + guint32 lastfrag_framenum; + guint32 lastfrag_offset; +}; + + +/***************************************************************************** + * + * Stream hash + */ + +/* key */ +typedef struct stream_key { + /* streams can be attached to circuits or conversations, and we note + that here */ + gboolean is_circuit; + union { + const struct circuit *circuit; + const struct conversation *conv; + } circ; + int p2p_dir; +} stream_key_t; + + +/* hash func */ +guint stream_hash_func(gconstpointer k) +{ + const stream_key_t *key = (const stream_key_t *)k; + + /* is_circuit is redundant to the circuit/conversation pointer */ + return ((guint)key->circ.circuit) ^ key->p2p_dir; +} + +/* compare func */ +gboolean stream_compare_func(gconstpointer a, + gconstpointer b) +{ + const stream_key_t *key1 = (const stream_key_t *)a; + const stream_key_t *key2 = (const stream_key_t *)b; + if( key1 -> p2p_dir != key2 -> p2p_dir || + key1-> is_circuit != key2 -> is_circuit ) + return FALSE; + + if( key1 -> is_circuit ) + return (key1 -> circ.circuit == key2 -> circ.circuit ); + else + return (key1 -> circ.conv == key2 -> circ.conv ); +} + +/* value destroy func */ +void stream_value_destroy_func(gpointer v) +{ + stream_t *val = (stream_t *)v; + + /* this is only called when the entire hash (and hence the entire + * "streams" GMemChunk is being freed, so there is no need to free + * v. + */ +} + +/* memory pools */ +static GMemChunk *stream_keys = NULL; +static GMemChunk *streams = NULL; + + +/* the hash table */ +static GHashTable *stream_hash; + + +/* init/reset function, call from stream_init() */ +static void init_stream_hash( void ) { + if( stream_hash != NULL ) { + g_hash_table_destroy( stream_hash ); + stream_hash = NULL; + } + + if( stream_keys != NULL ) { + g_mem_chunk_destroy( stream_keys ); + stream_keys = NULL; + } + + if( streams != NULL ) { + g_mem_chunk_destroy( streams ); + streams = NULL; + } + + streams = g_mem_chunk_create(stream_t, + MEMCHUNK_STREAM_COUNT, + G_ALLOC_ONLY); + + stream_keys = g_mem_chunk_create(stream_key_t, + MEMCHUNK_STREAM_COUNT, + G_ALLOC_ONLY); + + stream_hash = g_hash_table_new_full(stream_hash_func, + stream_compare_func, + NULL, + stream_value_destroy_func); +} + + +/* lookup function, returns null if not found */ +static stream_t *stream_hash_lookup_circ( const struct circuit *circuit, int p2p_dir ) +{ + stream_key_t key = {TRUE,{circuit}, p2p_dir}; + return (stream_t *)g_hash_table_lookup(stream_hash, &key); +} + +static stream_t *stream_hash_lookup_conv( const struct conversation *conv, int p2p_dir ) +{ + stream_key_t key = {FALSE,{NULL}, p2p_dir}; + key.circ.conv = conv; + return (stream_t *)g_hash_table_lookup(stream_hash, &key); +} + + +static stream_t *new_stream( stream_key_t *key ) +{ + stream_t *val; + + val = g_mem_chunk_alloc(streams); + val -> key = key; + val -> pdu_counter = 0; + val -> current_pdu = NULL; + val -> lastfrag_framenum = 0; + val -> lastfrag_offset = 0; + g_hash_table_insert(stream_hash, key, val); + + return val; +} + + +/* insert function */ +static stream_t *stream_hash_insert_circ( const struct circuit *circuit, int p2p_dir ) +{ + stream_key_t *key; + + key = g_mem_chunk_alloc(stream_keys); + key->is_circuit = TRUE; + key->circ.circuit = circuit; + key->p2p_dir = p2p_dir; + + return new_stream(key); +} + +static stream_t *stream_hash_insert_conv( const struct conversation *conv, int p2p_dir ) +{ + stream_key_t *key; + + key = g_mem_chunk_alloc(stream_keys); + key->is_circuit = FALSE; + key->circ.conv = conv; + key->p2p_dir = p2p_dir; + + return new_stream(key); +} + + +/****************************************************************************** + * + * PDU data + */ +static GMemChunk *pdus = NULL; + +/* pdu counter, for generating unique pdu ids */ +static guint32 pdu_counter; + + +static void stream_init_pdu_data(void) +{ + if( pdus != NULL ) { + g_mem_chunk_destroy( pdus ); + pdus = NULL; + } + + pdus = g_mem_chunk_create(stream_pdu_t, + MEMCHUNK_PDU_COUNT, + G_ALLOC_ONLY); + pdu_counter = 0; +} + + +/* new pdu in this stream */ +static stream_pdu_t *stream_new_pdu(stream_t *stream) +{ + stream_pdu_t *pdu; + pdu = g_mem_chunk_alloc(pdus); + pdu -> fd_head = NULL; + pdu -> pdu_number = stream -> pdu_counter++; + pdu -> id = pdu_counter++; + return pdu; +} + +/***************************************************************************** + * + * fragment hash + */ + +/* key */ +typedef struct fragment_key { + const stream_t *stream; + guint32 framenum; + guint32 offset; +} fragment_key_t; + + +/* hash func */ +guint fragment_hash_func(gconstpointer k) +{ + const fragment_key_t *key = (const fragment_key_t *)k; + return ((guint)key->stream) + ((guint)key -> framenum) + ((guint)key->offset); +} + +/* compare func */ +gboolean fragment_compare_func(gconstpointer a, + gconstpointer b) +{ + const fragment_key_t *key1 = (const fragment_key_t *)a; + const fragment_key_t *key2 = (const fragment_key_t *)b; + return (key1 -> stream == key2 -> stream && + key1 -> framenum == key2 -> framenum && + key1 -> offset == key2 -> offset ); +} + +/* memory pools */ +static GMemChunk *fragment_keys = NULL; +static GMemChunk *fragment_vals = NULL; + +/* the hash table */ +static GHashTable *fragment_hash; + + +/* init/reset function, call from stream_init() */ +static void init_fragment_hash( void ) { + if( fragment_hash != NULL ) { + g_hash_table_destroy( fragment_hash ); + fragment_hash = NULL; + } + + if( fragment_vals != NULL ) { + g_mem_chunk_destroy( fragment_vals ); + fragment_vals = NULL; + } + + if( fragment_keys != NULL ) { + g_mem_chunk_destroy( fragment_keys ); + fragment_keys = NULL; + } + + fragment_keys = g_mem_chunk_create(fragment_key_t, + MEMCHUNK_FRAGMENT_COUNT, + G_ALLOC_ONLY); + + fragment_vals = g_mem_chunk_create(stream_pdu_fragment_t, + MEMCHUNK_FRAGMENT_COUNT, + G_ALLOC_ONLY); + + fragment_hash = g_hash_table_new(fragment_hash_func, + fragment_compare_func); +} + + +/* lookup function, returns null if not found */ +static stream_pdu_fragment_t *fragment_hash_lookup( const stream_t *stream, guint32 framenum, guint32 offset ) +{ + fragment_key_t key = {stream, framenum, offset}; + stream_pdu_fragment_t *val = g_hash_table_lookup(fragment_hash, &key); + + return val; +} + + +/* insert function */ +static stream_pdu_fragment_t *fragment_hash_insert( const stream_t *stream, guint32 framenum, guint32 offset, + guint32 length) +{ + fragment_key_t *key; + stream_pdu_fragment_t *val; + + key = g_mem_chunk_alloc(fragment_keys); + key->stream = stream; + key->framenum = framenum; + key->offset = offset; + + val = g_mem_chunk_alloc(fragment_vals); + val->len = length; + val->pdu = NULL; + val->final_fragment = FALSE; + + g_hash_table_insert(fragment_hash, key, val); + return val; +} + +/*****************************************************************************/ + +/* fragmentation hash tables */ +static GHashTable *stream_fragment_table = NULL; +static GHashTable *stream_reassembled_table = NULL; + +/* Initialise a new stream. Call this when you first identify a distinct + * stream. */ +stream_t *stream_new_circ ( const struct circuit *circuit, int p2p_dir ) +{ + stream_t * stream; + + /* we don't want to replace the previous data if we get called twice on the + same circuit, so do a lookup first */ + stream = stream_hash_lookup_circ(circuit, p2p_dir); + g_assert( stream == NULL ); + + stream = stream_hash_insert_circ(circuit, p2p_dir); + + return stream; +} + +stream_t *stream_new_conv ( const struct conversation *conv, int p2p_dir ) +{ + stream_t * stream; + + /* we don't want to replace the previous data if we get called twice on the + same conversation, so do a lookup first */ + stream = stream_hash_lookup_conv(conv, p2p_dir); + g_assert( stream == NULL ); + + stream = stream_hash_insert_conv(conv, p2p_dir); + return stream; +} + + + + +/* retrieve a previously-created stream. + * + * Returns null if no matching stream was found. + */ +stream_t *find_stream_circ ( const struct circuit *circuit, int p2p_dir ) +{ + return stream_hash_lookup_circ(circuit,p2p_dir); +} +stream_t *find_stream_conv ( const struct conversation *conv, int p2p_dir ) +{ + return stream_hash_lookup_conv(conv,p2p_dir); +} + + +/* initialise the stream routines */ +void stream_init( void ) +{ + init_stream_hash(); + init_fragment_hash(); + stream_init_pdu_data(); + + fragment_table_init(&stream_fragment_table); + reassembled_table_init(&stream_reassembled_table); +} + + + + +/*****************************************************************************/ + +stream_pdu_fragment_t *stream_find_frag( stream_t *stream, guint32 framenum, guint32 offset ) +{ + return fragment_hash_lookup( stream, framenum, offset ); +} + +stream_pdu_fragment_t *stream_add_frag( stream_t *stream, guint32 framenum, guint32 offset, + tvbuff_t *tvb, packet_info *pinfo, gboolean more_frags ) +{ + fragment_data *fd_head; + stream_pdu_t *pdu; + stream_pdu_fragment_t *frag_data; + + g_assert(stream); + + /* check that this fragment is at the end of the stream */ + g_assert( framenum > stream->lastfrag_framenum || + (framenum == stream->lastfrag_framenum && offset > stream->lastfrag_offset)); + + + pdu = stream->current_pdu; + if( pdu == NULL ) { + /* start a new pdu */ + pdu = stream->current_pdu = stream_new_pdu(stream); + } + + /* add it to the reassembly tables */ + fd_head = fragment_add_seq_next(tvb, 0, pinfo, pdu->id, + stream_fragment_table, stream_reassembled_table, + tvb_reported_length(tvb), more_frags); + /* add it to our hash */ + frag_data = fragment_hash_insert( stream, framenum, offset, tvb_reported_length(tvb)); + frag_data -> pdu = pdu; + + if( fd_head != NULL ) { + /* if this was the last fragment, update the pdu data. + */ + pdu -> fd_head = fd_head; + + /* start a new pdu next time */ + stream->current_pdu = NULL; + + frag_data -> final_fragment = TRUE; + } + + /* stashing the framenum and offset permit future sanity checks */ + stream -> lastfrag_framenum = framenum; + stream -> lastfrag_offset = offset; + + return frag_data; +} + + +tvbuff_t *stream_process_reassembled( + tvbuff_t *tvb, int offset, packet_info *pinfo, + char *name, const stream_pdu_fragment_t *frag, + const struct _fragment_items *fit, + gboolean *update_col_infop, proto_tree *tree) +{ + stream_pdu_t *pdu; + g_assert(frag); + pdu = frag->pdu; + + /* we handle non-terminal fragments ourselves, because + reassemble.c messes them up */ + if(!frag->final_fragment) { + if (pdu->fd_head != NULL && fit->hf_reassembled_in != NULL) { + proto_tree_add_uint(tree, + *(fit->hf_reassembled_in), tvb, + 0, 0, pdu->fd_head->reassembled_in); + } + return NULL; + } + + return process_reassembled_data(tvb, offset, pinfo, name, pdu->fd_head, + fit, update_col_infop, tree); +} + +guint32 stream_get_frag_length( const stream_pdu_fragment_t *frag) +{ + g_assert( frag ); + return frag->len; +} + +fragment_data *stream_get_frag_data( const stream_pdu_fragment_t *frag) +{ + g_assert( frag ); + return frag->pdu->fd_head; +} + +guint32 stream_get_pdu_no( const stream_pdu_fragment_t *frag) +{ + g_assert( frag ); + return frag->pdu->pdu_number; +} diff --git a/epan/stream.h b/epan/stream.h new file mode 100644 index 0000000000..ee868e1e37 --- /dev/null +++ b/epan/stream.h @@ -0,0 +1,138 @@ +/* stream.h + * + * Definititions for handling circuit-switched protocols + * which are handled as streams, and don't have lengths + * and IDs such as are required for reassemble.h + * + * $Id$ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef STREAM_H +#define STREAM_H + +#include + +extern struct _fragment_items; + +/* A stream represents the concept of an arbitrary stream of data, + divided up into frames for transmission, where the frames have + little or no correspondence to the PDUs of the protocol being + streamed, and those PDUs are just delineated by a magic number. + + For example, we stream H.223 over IAX2. IAX2 has no concept of + H.223 PDUs and just divides the H.223 stream into 160-byte + frames. H.223 PDUs are delineated by two-byte magic numbers (which + may, of course, straddle an IAX2 frame boundary). + + Essentially we act as a wrapper to reassemble.h, by making up + PDU ids and keeping some additional data on fragments to allow the + PDUs to be defragmented again. +*/ + + +/* A stream_t represents a stream. There might be one or two streams + in a circuit, depending on whether that circuit is mono- or bi-directional. +*/ +typedef struct stream stream_t; + +/* Fragments in a PDU are represented using a stream_pdu_fragment_t, + and placed in a linked-list with other fragments in the PDU. + + (They're also placed in a hash so we can find them again later) +*/ +typedef struct stream_pdu_fragment stream_pdu_fragment_t; + + + +struct circuit; +struct conversation; + +/* initialise a new stream. Call this when you first identify a distinct + * stream. The circit pointer is just used as a key to look up the stream. */ +extern stream_t *stream_new_circ ( const struct circuit *circuit, int p2p_dir ); +extern stream_t *stream_new_conv ( const struct conversation *conv, int p2p_dir ); + +/* retrieve a previously-created stream. + * + * Returns null if no matching stream was found. + */ +extern stream_t *find_stream_circ ( const struct circuit *circuit, int p2p_dir ); +extern stream_t *find_stream_conv ( const struct conversation *conv, int p2p_dir ); + + + +/* see if we've seen this fragment before. + + The framenum and offset are just hash keys, so can be any values unique + to this frame, but the idea is that you use the number of the frame being + disassembled, and the byte-offset within that frame. +*/ +extern stream_pdu_fragment_t *stream_find_frag( stream_t *stream, guint32 framenum, guint32 offset ); + +/* add a new fragment to the fragment tables for the stream. The framenum and + * offset are keys allowing future access with stream_find_frag(), tvb is the + * fragment to be added, and pinfo is the information for the frame containing + * this fragment. more_frags should be set if this is the final fragment in the + * PDU. + * + * * the fragment must be later in the stream than any previous fragment + * (ie, framenum.offset must be greater than those passed on the previous + * call) + * + * This essentially means that you can only add fragments on the first pass + * through the stream. + */ +extern stream_pdu_fragment_t *stream_add_frag( stream_t *stream, guint32 framenum, guint32 offset, + tvbuff_t *tvb, packet_info *pinfo, gboolean more_frags ); + +/* Get the length of a fragment previously found by stream_find_frag(). + */ +extern guint32 stream_get_frag_length( const stream_pdu_fragment_t *frag); + +/* Get a handle on the top of the chain of fragment_datas underlying this PDU + * frag can be any fragment within a PDU, and it will always return the head of + * the chain + * + * Returns NULL until the last fragment is added. + */ +extern struct _fragment_data *stream_get_frag_data( const stream_pdu_fragment_t *frag); + +/* + * Process reassembled data; if this is the last fragment, put the fragment + * information into the protocol tree, and construct a tvbuff with the + * reassembled data, otherwise just put a "reassembled in" item into the + * protocol tree. + */ +extern tvbuff_t *stream_process_reassembled( + tvbuff_t *tvb, int offset, packet_info *pinfo, + char *name, const stream_pdu_fragment_t *frag, + const struct _fragment_items *fit, + gboolean *update_col_infop, proto_tree *tree); + +/* Get the PDU number. PDUs are numbered from zero within a stream. + * frag can be any fragment within a PDU. + */ +extern guint32 stream_get_pdu_no( const stream_pdu_fragment_t *frag); + +/* initialise the stream routines */ +void stream_init( void ); + +#endif /* STREAM_H */