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     guint32 pdu_number;              /* Number of this PDU within the stream */
28 
29     /* id of this pdu (globally unique) */
30     guint32 id;
31 } stream_pdu_t;
32 
33 
34 struct stream_pdu_fragment
35 {
36     guint32 len;                     /* the length of this fragment */
37     stream_pdu_t *pdu;
38     gboolean 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     guint32 pdu_counter;
52 
53     /* the framenumber and offset of the last fragment added;
54        used for sanity-checking */
55     guint32 lastfrag_framenum;
56     guint32 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 */
stream_hash_func(gconstpointer k)74 static guint stream_hash_func(gconstpointer k)
75 {
76     const stream_key_t *key = (const stream_key_t *)k;
77 
78     return (GPOINTER_TO_UINT(key->conv)) ^ key->p2p_dir;
79 }
80 
81 /* compare func */
stream_compare_func(gconstpointer a,gconstpointer b)82 static gboolean stream_compare_func(gconstpointer a,
83                              gconstpointer b)
84 {
85     const stream_key_t *key1 = (const stream_key_t *)a;
86     const stream_key_t *key2 = (const stream_key_t *)b;
87     if( key1 -> p2p_dir != key2 -> p2p_dir)
88         return FALSE;
89 
90     return (key1 -> conv == key2 -> conv );
91 }
92 
93 /* the hash table */
94 static GHashTable *stream_hash;
95 
96 
97 /* cleanup reset function, call from stream_cleanup() */
cleanup_stream_hash(void)98 static void cleanup_stream_hash( void ) {
99     if( stream_hash != NULL ) {
100         g_hash_table_destroy( stream_hash );
101         stream_hash = NULL;
102     }
103 }
104 
105 /* init function, call from stream_init() */
init_stream_hash(void)106 static void init_stream_hash( void ) {
107     ws_assert(stream_hash==NULL);
108     stream_hash = g_hash_table_new(stream_hash_func,
109                                    stream_compare_func);
110 }
111 
112 /* lookup function, returns null if not found */
stream_hash_lookup(const struct conversation * conv,int p2p_dir)113 static stream_t *stream_hash_lookup( const struct conversation *conv, int p2p_dir )
114 {
115     stream_key_t key;
116     key.conv = conv;
117     key.p2p_dir=p2p_dir;
118     return (stream_t *)g_hash_table_lookup(stream_hash, &key);
119 }
120 
121 
new_stream(stream_key_t * key)122 static stream_t *new_stream( stream_key_t *key )
123 {
124     stream_t *val;
125 
126     val = wmem_new(wmem_file_scope(), stream_t);
127     val -> key = key;
128     val -> pdu_counter = 0;
129     val -> current_pdu = NULL;
130     val -> lastfrag_framenum = 0;
131     val -> lastfrag_offset = 0;
132     g_hash_table_insert(stream_hash, key, val);
133 
134     return val;
135 }
136 
137 
138 /* insert function */
stream_hash_insert(const struct conversation * conv,int p2p_dir)139 static stream_t *stream_hash_insert( const struct conversation *conv, int p2p_dir )
140 {
141     stream_key_t *key;
142 
143     key = wmem_new(wmem_file_scope(), stream_key_t);
144     key->conv = conv;
145     key->p2p_dir = p2p_dir;
146 
147     return new_stream(key);
148 }
149 
150 
151 /******************************************************************************
152  *
153  * PDU data
154  */
155 
156 /* pdu counter, for generating unique pdu ids */
157 static guint32 pdu_counter;
158 
stream_cleanup_pdu_data(void)159 static void stream_cleanup_pdu_data(void)
160 {
161 }
162 
stream_init_pdu_data(void)163 static void stream_init_pdu_data(void)
164 {
165     pdu_counter = 0;
166 }
167 
168 
169 /* new pdu in this stream */
stream_new_pdu(stream_t * stream)170 static stream_pdu_t *stream_new_pdu(stream_t *stream)
171 {
172     stream_pdu_t *pdu;
173     pdu = wmem_new(wmem_file_scope(), stream_pdu_t);
174     pdu -> fd_head = NULL;
175     pdu -> pdu_number = stream -> pdu_counter++;
176     pdu -> id = pdu_counter++;
177     return pdu;
178 }
179 
180 /*****************************************************************************
181  *
182  * fragment hash
183  */
184 
185 /* key */
186 typedef struct fragment_key {
187     const stream_t *stream;
188     guint32 framenum;
189     guint32 offset;
190 } fragment_key_t;
191 
192 
193 /* hash func */
fragment_hash_func(gconstpointer k)194 static guint fragment_hash_func(gconstpointer k)
195 {
196     const fragment_key_t *key = (const fragment_key_t *)k;
197     return (GPOINTER_TO_UINT(key->stream)) + ((guint)key -> framenum) + ((guint)key->offset);
198 }
199 
200 /* compare func */
fragment_compare_func(gconstpointer a,gconstpointer b)201 static gboolean fragment_compare_func(gconstpointer a,
202                                gconstpointer b)
203 {
204     const fragment_key_t *key1 = (const fragment_key_t *)a;
205     const fragment_key_t *key2 = (const fragment_key_t *)b;
206     return (key1 -> stream == key2 -> stream &&
207             key1 -> framenum == key2 -> framenum &&
208             key1 -> offset == key2 -> offset );
209 }
210 
211 /* the hash table */
212 static GHashTable *fragment_hash;
213 
214 
215 /* cleanup function, call from stream_cleanup() */
cleanup_fragment_hash(void)216 static void cleanup_fragment_hash( void ) {
217     if( fragment_hash != NULL ) {
218         g_hash_table_destroy( fragment_hash );
219         fragment_hash = NULL;
220     }
221 }
222 
223 /* init function, call from stream_init() */
init_fragment_hash(void)224 static void init_fragment_hash( void ) {
225     ws_assert(fragment_hash==NULL);
226     fragment_hash = g_hash_table_new(fragment_hash_func,
227                                      fragment_compare_func);
228 }
229 
230 
231 /* lookup function, returns null if not found */
fragment_hash_lookup(const stream_t * stream,guint32 framenum,guint32 offset)232 static stream_pdu_fragment_t *fragment_hash_lookup( const stream_t *stream, guint32 framenum, guint32 offset )
233 {
234     fragment_key_t key;
235     stream_pdu_fragment_t *val;
236 
237     key.stream = stream;
238     key.framenum = framenum;
239     key.offset = offset;
240     val = (stream_pdu_fragment_t *)g_hash_table_lookup(fragment_hash, &key);
241 
242     return val;
243 }
244 
245 
246 /* insert function */
fragment_hash_insert(const stream_t * stream,guint32 framenum,guint32 offset,guint32 length)247 static stream_pdu_fragment_t *fragment_hash_insert( const stream_t *stream, guint32 framenum, guint32 offset,
248                                                     guint32 length)
249 {
250     fragment_key_t *key;
251     stream_pdu_fragment_t *val;
252 
253     key = wmem_new(wmem_file_scope(), fragment_key_t);
254     key->stream = stream;
255     key->framenum = framenum;
256     key->offset = offset;
257 
258     val = wmem_new(wmem_file_scope(), stream_pdu_fragment_t);
259     val->len = length;
260     val->pdu = NULL;
261     val->final_fragment = FALSE;
262 
263     g_hash_table_insert(fragment_hash, key, val);
264     return val;
265 }
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. */
stream_new(const struct conversation * conv,int p2p_dir)274 stream_t *stream_new ( const struct conversation *conv, int p2p_dir )
275 {
276     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     stream = stream_hash_lookup(conv, p2p_dir);
281     DISSECTOR_ASSERT( stream == NULL );
282 
283     stream = stream_hash_insert(conv, p2p_dir);
284     return stream;
285 }
286 
287 
288 /* retrieve a previously-created stream.
289  *
290  * Returns null if no matching stream was found.
291  */
find_stream(const struct conversation * conv,int p2p_dir)292 stream_t *find_stream ( const struct conversation *conv, int p2p_dir )
293 {
294     return stream_hash_lookup(conv,p2p_dir);
295 }
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  */
stream_cleanup(void)302 void stream_cleanup( void )
303 {
304     cleanup_stream_hash();
305     cleanup_fragment_hash();
306     stream_cleanup_pdu_data();
307     reassembly_table_destroy(&stream_reassembly_table);
308 }
309 
310 /* initialise the stream routines */
stream_init(void)311 void stream_init( void )
312 {
313     init_stream_hash();
314     init_fragment_hash();
315     stream_init_pdu_data();
316 
317     reassembly_table_init(&stream_reassembly_table,
318                           &addresses_reassembly_table_functions);
319 }
320 
321 /*****************************************************************************/
322 
stream_find_frag(stream_t * stream,guint32 framenum,guint32 offset)323 stream_pdu_fragment_t *stream_find_frag( stream_t *stream, guint32 framenum, guint32 offset )
324 {
325     return fragment_hash_lookup( stream, framenum, offset );
326 }
327 
stream_add_frag(stream_t * stream,guint32 framenum,guint32 offset,tvbuff_t * tvb,packet_info * pinfo,gboolean more_frags)328 stream_pdu_fragment_t *stream_add_frag( stream_t *stream, guint32 framenum, guint32 offset,
329                                         tvbuff_t *tvb, packet_info *pinfo, gboolean more_frags )
330 {
331     fragment_head *fd_head;
332     stream_pdu_t *pdu;
333     stream_pdu_fragment_t *frag_data;
334 
335     DISSECTOR_ASSERT(stream);
336 
337     /* check that this fragment is at the end of the stream */
338     DISSECTOR_ASSERT( framenum > stream->lastfrag_framenum ||
339                       (framenum == stream->lastfrag_framenum && offset > stream->lastfrag_offset));
340 
341 
342     pdu = stream->current_pdu;
343     if( pdu == NULL ) {
344         /* start a new pdu */
345         pdu = stream->current_pdu = stream_new_pdu(stream);
346     }
347 
348     /* add it to the reassembly tables */
349     fd_head = fragment_add_seq_next(&stream_reassembly_table,
350                                     tvb, 0, pinfo, pdu->id, NULL,
351                                     tvb_reported_length(tvb), more_frags);
352     /* add it to our hash */
353     frag_data = fragment_hash_insert( stream, framenum, offset, tvb_reported_length(tvb));
354     frag_data -> pdu = pdu;
355 
356     if( fd_head != NULL ) {
357         /* if this was the last fragment, update the pdu data.
358          */
359         pdu -> fd_head = fd_head;
360 
361         /* start a new pdu next time */
362         stream->current_pdu = NULL;
363 
364         frag_data -> final_fragment = TRUE;
365     }
366 
367     /* stashing the framenum and offset permit future sanity checks */
368     stream -> lastfrag_framenum = framenum;
369     stream -> lastfrag_offset = offset;
370 
371     return frag_data;
372 }
373 
374 
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)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     gboolean *update_col_infop, proto_tree *tree)
380 {
381     stream_pdu_t *pdu;
382     DISSECTOR_ASSERT(frag);
383     pdu = frag->pdu;
384 
385     /* we handle non-terminal fragments ourselves, because
386        reassemble.c messes them up */
387     if(!frag->final_fragment) {
388         if (pdu->fd_head != NULL && fit->hf_reassembled_in != NULL) {
389             proto_tree_add_uint(tree,
390                                 *(fit->hf_reassembled_in), tvb,
391                                 0, 0, pdu->fd_head->reassembled_in);
392         }
393         return NULL;
394     }
395 
396     return process_reassembled_data(tvb, offset, pinfo, name, pdu->fd_head,
397                                     fit, update_col_infop, tree);
398 }
399 
stream_get_frag_length(const stream_pdu_fragment_t * frag)400 guint32 stream_get_frag_length( const stream_pdu_fragment_t *frag)
401 {
402     DISSECTOR_ASSERT( frag );
403     return frag->len;
404 }
405 
stream_get_frag_data(const stream_pdu_fragment_t * frag)406 fragment_head *stream_get_frag_data( const stream_pdu_fragment_t *frag)
407 {
408     DISSECTOR_ASSERT( frag );
409     return frag->pdu->fd_head;
410 }
411 
stream_get_pdu_no(const stream_pdu_fragment_t * frag)412 guint32 stream_get_pdu_no( const stream_pdu_fragment_t *frag)
413 {
414     DISSECTOR_ASSERT( frag );
415     return frag->pdu->pdu_number;
416 }
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  */
430