summaryrefslogtreecommitdiffstats
path: root/epan/stream.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
commite4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch)
tree68cb5ef9081156392f1dd62a00c6ccc1451b93df /epan/stream.c
parentInitial commit. (diff)
downloadwireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz
wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'epan/stream.c')
-rw-r--r--epan/stream.c429
1 files changed, 429 insertions, 0 deletions
diff --git a/epan/stream.c b/epan/stream.c
new file mode 100644
index 00000000..5bad6037
--- /dev/null
+++ b/epan/stream.c
@@ -0,0 +1,429 @@
+/* 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
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <epan/packet.h>
+#include <epan/reassemble.h>
+#include <epan/stream.h>
+#include <epan/tvbuff.h>
+#include <wsutil/ws_assert.h>
+
+
+typedef struct {
+ fragment_head *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 are attached to conversations */
+ const struct conversation *conv;
+ int p2p_dir;
+} stream_key_t;
+
+
+/* hash func */
+static guint stream_hash_func(gconstpointer k)
+{
+ const stream_key_t *key = (const stream_key_t *)k;
+
+ return (GPOINTER_TO_UINT(key->conv)) ^ key->p2p_dir;
+}
+
+/* compare func */
+static 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)
+ return FALSE;
+
+ return (key1 -> conv == key2 -> conv );
+}
+
+/* the hash table */
+static GHashTable *stream_hash;
+
+
+/* cleanup reset function, call from stream_cleanup() */
+static void cleanup_stream_hash( void ) {
+ if( stream_hash != NULL ) {
+ g_hash_table_destroy( stream_hash );
+ stream_hash = NULL;
+ }
+}
+
+/* init function, call from stream_init() */
+static void init_stream_hash( void ) {
+ ws_assert(stream_hash==NULL);
+ stream_hash = g_hash_table_new(stream_hash_func,
+ stream_compare_func);
+}
+
+/* lookup function, returns null if not found */
+static stream_t *stream_hash_lookup( const struct conversation *conv, int p2p_dir )
+{
+ stream_key_t key;
+ key.conv = conv;
+ key.p2p_dir=p2p_dir;
+ return (stream_t *)g_hash_table_lookup(stream_hash, &key);
+}
+
+
+static stream_t *new_stream( stream_key_t *key )
+{
+ stream_t *val;
+
+ val = wmem_new(wmem_file_scope(), stream_t);
+ 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( const struct conversation *conv, int p2p_dir )
+{
+ stream_key_t *key;
+
+ key = wmem_new(wmem_file_scope(), stream_key_t);
+ key->conv = conv;
+ key->p2p_dir = p2p_dir;
+
+ return new_stream(key);
+}
+
+
+/******************************************************************************
+ *
+ * PDU data
+ */
+
+/* pdu counter, for generating unique pdu ids */
+static guint32 pdu_counter;
+
+static void stream_cleanup_pdu_data(void)
+{
+}
+
+static void stream_init_pdu_data(void)
+{
+ pdu_counter = 0;
+}
+
+
+/* new pdu in this stream */
+static stream_pdu_t *stream_new_pdu(stream_t *stream)
+{
+ stream_pdu_t *pdu;
+ pdu = wmem_new(wmem_file_scope(), stream_pdu_t);
+ 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 */
+static guint fragment_hash_func(gconstpointer k)
+{
+ const fragment_key_t *key = (const fragment_key_t *)k;
+ return (GPOINTER_TO_UINT(key->stream)) + ((guint)key -> framenum) + ((guint)key->offset);
+}
+
+/* compare func */
+static 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 );
+}
+
+/* the hash table */
+static GHashTable *fragment_hash;
+
+
+/* cleanup function, call from stream_cleanup() */
+static void cleanup_fragment_hash( void ) {
+ if( fragment_hash != NULL ) {
+ g_hash_table_destroy( fragment_hash );
+ fragment_hash = NULL;
+ }
+}
+
+/* init function, call from stream_init() */
+static void init_fragment_hash( void ) {
+ ws_assert(fragment_hash==NULL);
+ 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_pdu_fragment_t *val;
+
+ key.stream = stream;
+ key.framenum = framenum;
+ key.offset = offset;
+ val = (stream_pdu_fragment_t *)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 = wmem_new(wmem_file_scope(), fragment_key_t);
+ key->stream = stream;
+ key->framenum = framenum;
+ key->offset = offset;
+
+ val = wmem_new(wmem_file_scope(), stream_pdu_fragment_t);
+ val->len = length;
+ val->pdu = NULL;
+ val->final_fragment = FALSE;
+
+ g_hash_table_insert(fragment_hash, key, val);
+ return val;
+}
+
+/*****************************************************************************/
+
+/* reassembly table */
+static reassembly_table stream_reassembly_table;
+
+/* Initialise a new stream. Call this when you first identify a distinct
+ * stream. */
+stream_t *stream_new ( 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, p2p_dir);
+ DISSECTOR_ASSERT( stream == NULL );
+
+ stream = stream_hash_insert(conv, p2p_dir);
+ return stream;
+}
+
+
+/* retrieve a previously-created stream.
+ *
+ * Returns null if no matching stream was found.
+ */
+stream_t *find_stream ( const struct conversation *conv, int p2p_dir )
+{
+ return stream_hash_lookup(conv,p2p_dir);
+}
+
+/* cleanup the stream routines */
+/* Note: stream_cleanup must only be called when seasonal memory
+ * is also freed since the hash tables countain pointers to
+ * wmem_file_scoped memory.
+ */
+void stream_cleanup( void )
+{
+ cleanup_stream_hash();
+ cleanup_fragment_hash();
+ stream_cleanup_pdu_data();
+ reassembly_table_destroy(&stream_reassembly_table);
+}
+
+/* initialise the stream routines */
+void stream_init( void )
+{
+ init_stream_hash();
+ init_fragment_hash();
+ stream_init_pdu_data();
+
+ reassembly_table_init(&stream_reassembly_table,
+ &addresses_reassembly_table_functions);
+}
+
+/*****************************************************************************/
+
+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_head *fd_head;
+ stream_pdu_t *pdu;
+ stream_pdu_fragment_t *frag_data;
+
+ DISSECTOR_ASSERT(stream);
+
+ /* check that this fragment is at the end of the stream */
+ DISSECTOR_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(&stream_reassembly_table,
+ tvb, 0, pinfo, pdu->id, NULL,
+ 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,
+ const char *name, const stream_pdu_fragment_t *frag,
+ const struct _fragment_items *fit,
+ gboolean *update_col_infop, proto_tree *tree)
+{
+ stream_pdu_t *pdu;
+ DISSECTOR_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)
+{
+ DISSECTOR_ASSERT( frag );
+ return frag->len;
+}
+
+fragment_head *stream_get_frag_data( const stream_pdu_fragment_t *frag)
+{
+ DISSECTOR_ASSERT( frag );
+ return frag->pdu->fd_head;
+}
+
+guint32 stream_get_pdu_no( const stream_pdu_fragment_t *frag)
+{
+ DISSECTOR_ASSERT( frag );
+ return frag->pdu->pdu_number;
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */