/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 | | */ |