Coverage Report

Created: 2025-08-04 07:15

/src/wireshark/epan/stream.c
Line
Count
Source (jump to first uncovered line)
1
/* stream.c
2
 *
3
 * Definititions for handling circuit-switched protocols
4
 * which are handled as streams, and don't have lengths
5
 * and IDs such as are required for reassemble.h
6
 *
7
 * Wireshark - Network traffic analyzer
8
 * By Gerald Combs <gerald@wireshark.org>
9
 * Copyright 1998 Gerald Combs
10
 *
11
 * SPDX-License-Identifier: GPL-2.0-or-later
12
 */
13
14
#include "config.h"
15
16
#include <glib.h>
17
#include <epan/packet.h>
18
#include <epan/reassemble.h>
19
#include <epan/stream.h>
20
#include <epan/tvbuff.h>
21
#include <wsutil/ws_assert.h>
22
23
24
typedef struct {
25
    fragment_head *fd_head;          /* the reassembled data, NULL
26
                                      * until we add the last fragment */
27
    uint32_t pdu_number;              /* Number of this PDU within the stream */
28
29
    /* id of this pdu (globally unique) */
30
    uint32_t id;
31
} stream_pdu_t;
32
33
34
struct stream_pdu_fragment
35
{
36
    uint32_t len;                     /* the length of this fragment */
37
    stream_pdu_t *pdu;
38
    bool final_fragment;
39
};
40
41
struct stream {
42
    /* the key used to add this stream to stream_hash */
43
    struct stream_key *key;
44
45
    /* pdu to add the next fragment to, or NULL if we need to start
46
     * a new PDU.
47
     */
48
    stream_pdu_t *current_pdu;
49
50
    /* number of PDUs added to this stream so far */
51
    uint32_t pdu_counter;
52
53
    /* the framenumber and offset of the last fragment added;
54
       used for sanity-checking */
55
    uint32_t lastfrag_framenum;
56
    uint32_t lastfrag_offset;
57
};
58
59
60
/*****************************************************************************
61
 *
62
 * Stream hash
63
 */
64
65
/* key */
66
typedef struct stream_key {
67
    /* streams are attached to conversations */
68
    const struct conversation *conv;
69
    int p2p_dir;
70
} stream_key_t;
71
72
73
/* hash func */
74
static unsigned stream_hash_func(const void *k)
75
0
{
76
0
    const stream_key_t *key = (const stream_key_t *)k;
77
78
0
    return (GPOINTER_TO_UINT(key->conv)) ^ key->p2p_dir;
79
0
}
80
81
/* compare func */
82
static gboolean stream_compare_func(const void *a,
83
                             const void *b)
84
0
{
85
0
    const stream_key_t *key1 = (const stream_key_t *)a;
86
0
    const stream_key_t *key2 = (const stream_key_t *)b;
87
0
    if( key1 -> p2p_dir != key2 -> p2p_dir)
88
0
        return FALSE;
89
90
0
    return (key1 -> conv == key2 -> conv );
91
0
}
92
93
/* the hash table */
94
static GHashTable *stream_hash;
95
96
97
/* cleanup reset function, call from stream_cleanup() */
98
0
static void cleanup_stream_hash( void ) {
99
0
    if( stream_hash != NULL ) {
100
0
        g_hash_table_destroy( stream_hash );
101
0
        stream_hash = NULL;
102
0
    }
103
0
}
104
105
/* init function, call from stream_init() */
106
14
static void init_stream_hash( void ) {
107
14
    ws_assert(stream_hash==NULL);
108
14
    stream_hash = g_hash_table_new(stream_hash_func,
109
14
                                   stream_compare_func);
110
14
}
111
112
/* lookup function, returns null if not found */
113
static stream_t *stream_hash_lookup( const struct conversation *conv, int p2p_dir )
114
0
{
115
0
    stream_key_t key;
116
0
    key.conv = conv;
117
0
    key.p2p_dir=p2p_dir;
118
0
    return (stream_t *)g_hash_table_lookup(stream_hash, &key);
119
0
}
120
121
122
static stream_t *new_stream( stream_key_t *key )
123
0
{
124
0
    stream_t *val;
125
126
0
    val = wmem_new(wmem_file_scope(), stream_t);
127
0
    val -> key = key;
128
0
    val -> pdu_counter = 0;
129
0
    val -> current_pdu = NULL;
130
0
    val -> lastfrag_framenum = 0;
131
0
    val -> lastfrag_offset = 0;
132
0
    g_hash_table_insert(stream_hash, key, val);
133
134
0
    return val;
135
0
}
136
137
138
/* insert function */
139
static stream_t *stream_hash_insert( const struct conversation *conv, int p2p_dir )
140
0
{
141
0
    stream_key_t *key;
142
143
0
    key = wmem_new(wmem_file_scope(), stream_key_t);
144
0
    key->conv = conv;
145
0
    key->p2p_dir = p2p_dir;
146
147
0
    return new_stream(key);
148
0
}
149
150
151
/******************************************************************************
152
 *
153
 * PDU data
154
 */
155
156
/* pdu counter, for generating unique pdu ids */
157
static uint32_t pdu_counter;
158
159
static void stream_cleanup_pdu_data(void)
160
0
{
161
0
}
162
163
static void stream_init_pdu_data(void)
164
14
{
165
14
    pdu_counter = 0;
166
14
}
167
168
169
/* new pdu in this stream */
170
static stream_pdu_t *stream_new_pdu(stream_t *stream)
171
0
{
172
0
    stream_pdu_t *pdu;
173
0
    pdu = wmem_new(wmem_file_scope(), stream_pdu_t);
174
0
    pdu -> fd_head = NULL;
175
0
    pdu -> pdu_number = stream -> pdu_counter++;
176
0
    pdu -> id = pdu_counter++;
177
0
    return pdu;
178
0
}
179
180
/*****************************************************************************
181
 *
182
 * fragment hash
183
 */
184
185
/* key */
186
typedef struct fragment_key {
187
    const stream_t *stream;
188
    uint32_t framenum;
189
    uint32_t offset;
190
} fragment_key_t;
191
192
193
/* hash func */
194
static unsigned fragment_hash_func(const void *k)
195
0
{
196
0
    const fragment_key_t *key = (const fragment_key_t *)k;
197
0
    return (GPOINTER_TO_UINT(key->stream)) + ((unsigned)key -> framenum) + ((unsigned)key->offset);
198
0
}
199
200
/* compare func */
201
static gboolean fragment_compare_func(const void *a,
202
                               const void *b)
203
0
{
204
0
    const fragment_key_t *key1 = (const fragment_key_t *)a;
205
0
    const fragment_key_t *key2 = (const fragment_key_t *)b;
206
0
    return (key1 -> stream == key2 -> stream &&
207
0
            key1 -> framenum == key2 -> framenum &&
208
0
            key1 -> offset == key2 -> offset );
209
0
}
210
211
/* the hash table */
212
static GHashTable *fragment_hash;
213
214
215
/* cleanup function, call from stream_cleanup() */
216
0
static void cleanup_fragment_hash( void ) {
217
0
    if( fragment_hash != NULL ) {
218
0
        g_hash_table_destroy( fragment_hash );
219
0
        fragment_hash = NULL;
220
0
    }
221
0
}
222
223
/* init function, call from stream_init() */
224
14
static void init_fragment_hash( void ) {
225
14
    ws_assert(fragment_hash==NULL);
226
14
    fragment_hash = g_hash_table_new(fragment_hash_func,
227
14
                                     fragment_compare_func);
228
14
}
229
230
231
/* lookup function, returns null if not found */
232
static stream_pdu_fragment_t *fragment_hash_lookup( const stream_t *stream, uint32_t framenum, uint32_t offset )
233
0
{
234
0
    fragment_key_t key;
235
0
    stream_pdu_fragment_t *val;
236
237
0
    key.stream = stream;
238
0
    key.framenum = framenum;
239
0
    key.offset = offset;
240
0
    val = (stream_pdu_fragment_t *)g_hash_table_lookup(fragment_hash, &key);
241
242
0
    return val;
243
0
}
244
245
246
/* insert function */
247
static stream_pdu_fragment_t *fragment_hash_insert( const stream_t *stream, uint32_t framenum, uint32_t offset,
248
                                                    uint32_t length)
249
0
{
250
0
    fragment_key_t *key;
251
0
    stream_pdu_fragment_t *val;
252
253
0
    key = wmem_new(wmem_file_scope(), fragment_key_t);
254
0
    key->stream = stream;
255
0
    key->framenum = framenum;
256
0
    key->offset = offset;
257
258
0
    val = wmem_new(wmem_file_scope(), stream_pdu_fragment_t);
259
0
    val->len = length;
260
0
    val->pdu = NULL;
261
0
    val->final_fragment = false;
262
263
0
    g_hash_table_insert(fragment_hash, key, val);
264
0
    return val;
265
0
}
266
267
/*****************************************************************************/
268
269
/* reassembly table */
270
static reassembly_table stream_reassembly_table;
271
272
/* Initialise a new stream. Call this when you first identify a distinct
273
 * stream. */
274
stream_t *stream_new ( const struct conversation *conv, int p2p_dir )
275
0
{
276
0
    stream_t * stream;
277
278
    /* we don't want to replace the previous data if we get called twice on the
279
       same conversation, so do a lookup first */
280
0
    stream = stream_hash_lookup(conv, p2p_dir);
281
0
    DISSECTOR_ASSERT( stream == NULL );
282
283
0
    stream = stream_hash_insert(conv, p2p_dir);
284
0
    return stream;
285
0
}
286
287
288
/* retrieve a previously-created stream.
289
 *
290
 * Returns null if no matching stream was found.
291
 */
292
stream_t *find_stream ( const struct conversation *conv, int p2p_dir )
293
0
{
294
0
    return stream_hash_lookup(conv,p2p_dir);
295
0
}
296
297
/* cleanup the stream routines */
298
/* Note: stream_cleanup must only be called when seasonal memory
299
 *       is also freed since the hash tables countain pointers to
300
 *       wmem_file_scoped memory.
301
 */
302
void stream_cleanup( void )
303
0
{
304
0
    cleanup_stream_hash();
305
0
    cleanup_fragment_hash();
306
0
    stream_cleanup_pdu_data();
307
0
    reassembly_table_destroy(&stream_reassembly_table);
308
0
}
309
310
/* initialise the stream routines */
311
void stream_init( void )
312
14
{
313
14
    init_stream_hash();
314
14
    init_fragment_hash();
315
14
    stream_init_pdu_data();
316
317
14
    reassembly_table_init(&stream_reassembly_table,
318
14
                          &addresses_reassembly_table_functions);
319
14
}
320
321
/*****************************************************************************/
322
323
stream_pdu_fragment_t *stream_find_frag( stream_t *stream, uint32_t framenum, uint32_t offset )
324
0
{
325
0
    return fragment_hash_lookup( stream, framenum, offset );
326
0
}
327
328
stream_pdu_fragment_t *stream_add_frag( stream_t *stream, uint32_t framenum, uint32_t offset,
329
                                        tvbuff_t *tvb, packet_info *pinfo, bool more_frags )
330
0
{
331
0
    fragment_head *fd_head;
332
0
    stream_pdu_t *pdu;
333
0
    stream_pdu_fragment_t *frag_data;
334
335
0
    DISSECTOR_ASSERT(stream);
336
337
    /* check that this fragment is at the end of the stream */
338
0
    DISSECTOR_ASSERT( framenum > stream->lastfrag_framenum ||
339
0
                      (framenum == stream->lastfrag_framenum && offset > stream->lastfrag_offset));
340
341
342
0
    pdu = stream->current_pdu;
343
0
    if( pdu == NULL ) {
344
        /* start a new pdu */
345
0
        pdu = stream->current_pdu = stream_new_pdu(stream);
346
0
    }
347
348
    /* add it to the reassembly tables */
349
0
    fd_head = fragment_add_seq_next(&stream_reassembly_table,
350
0
                                    tvb, 0, pinfo, pdu->id, NULL,
351
0
                                    tvb_reported_length(tvb), more_frags);
352
    /* add it to our hash */
353
0
    frag_data = fragment_hash_insert( stream, framenum, offset, tvb_reported_length(tvb));
354
0
    frag_data -> pdu = pdu;
355
356
0
    if( fd_head != NULL ) {
357
        /* if this was the last fragment, update the pdu data.
358
         */
359
0
        pdu -> fd_head = fd_head;
360
361
        /* start a new pdu next time */
362
0
        stream->current_pdu = NULL;
363
364
0
        frag_data -> final_fragment = true;
365
0
    }
366
367
    /* stashing the framenum and offset permit future sanity checks */
368
0
    stream -> lastfrag_framenum = framenum;
369
0
    stream -> lastfrag_offset = offset;
370
371
0
    return frag_data;
372
0
}
373
374
375
tvbuff_t *stream_process_reassembled(
376
    tvbuff_t *tvb, int offset, packet_info *pinfo,
377
    const char *name, const stream_pdu_fragment_t *frag,
378
    const struct _fragment_items *fit,
379
    bool *update_col_infop, proto_tree *tree)
380
0
{
381
0
    stream_pdu_t *pdu;
382
0
    DISSECTOR_ASSERT(frag);
383
0
    pdu = frag->pdu;
384
385
    /* we handle non-terminal fragments ourselves, because
386
       reassemble.c messes them up */
387
0
    if(!frag->final_fragment) {
388
0
        if (pdu->fd_head != NULL && fit->hf_reassembled_in != NULL) {
389
0
            proto_tree_add_uint(tree,
390
0
                                *(fit->hf_reassembled_in), tvb,
391
0
                                0, 0, pdu->fd_head->reassembled_in);
392
0
        }
393
0
        return NULL;
394
0
    }
395
396
0
    return process_reassembled_data(tvb, offset, pinfo, name, pdu->fd_head,
397
0
                                    fit, update_col_infop, tree);
398
0
}
399
400
uint32_t stream_get_frag_length( const stream_pdu_fragment_t *frag)
401
0
{
402
0
    DISSECTOR_ASSERT( frag );
403
0
    return frag->len;
404
0
}
405
406
fragment_head *stream_get_frag_data( const stream_pdu_fragment_t *frag)
407
0
{
408
0
    DISSECTOR_ASSERT( frag );
409
0
    return frag->pdu->fd_head;
410
0
}
411
412
uint32_t stream_get_pdu_no( const stream_pdu_fragment_t *frag)
413
0
{
414
0
    DISSECTOR_ASSERT( frag );
415
0
    return frag->pdu->pdu_number;
416
0
}
417
418
/*
419
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
420
 *
421
 * Local variables:
422
 * c-basic-offset: 4
423
 * tab-width: 8
424
 * indent-tabs-mode: nil
425
 * End:
426
 *
427
 * vi: set shiftwidth=4 tabstop=8 expandtab:
428
 * :indentSize=4:tabSize=8:noTabs=true:
429
 */