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
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * SPDX-License-Identifier: GPL-2.0-or-later
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>
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 */
29 /* id of this pdu (globally unique) */
34 struct stream_pdu_fragment
36 uint32_t len
; /* the length of this fragment */
42 /* the key used to add this stream to stream_hash */
43 struct stream_key
*key
;
45 /* pdu to add the next fragment to, or NULL if we need to start
48 stream_pdu_t
*current_pdu
;
50 /* number of PDUs added to this stream so far */
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
;
60 /*****************************************************************************
66 typedef struct stream_key
{
67 /* streams are attached to conversations */
68 const struct conversation
*conv
;
74 static unsigned stream_hash_func(const void *k
)
76 const stream_key_t
*key
= (const stream_key_t
*)k
;
78 return (GPOINTER_TO_UINT(key
->conv
)) ^ key
->p2p_dir
;
82 static gboolean
stream_compare_func(const void *a
,
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
)
90 return (key1
-> conv
== key2
-> conv
);
94 static GHashTable
*stream_hash
;
97 /* cleanup reset function, call from stream_cleanup() */
98 static void cleanup_stream_hash( void ) {
99 if( stream_hash
!= NULL
) {
100 g_hash_table_destroy( stream_hash
);
105 /* init function, call from stream_init() */
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
);
112 /* lookup function, returns null if not found */
113 static stream_t
*stream_hash_lookup( const struct conversation
*conv
, int p2p_dir
)
118 return (stream_t
*)g_hash_table_lookup(stream_hash
, &key
);
122 static stream_t
*new_stream( stream_key_t
*key
)
126 val
= wmem_new(wmem_file_scope(), stream_t
);
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
);
138 /* insert function */
139 static stream_t
*stream_hash_insert( const struct conversation
*conv
, int p2p_dir
)
143 key
= wmem_new(wmem_file_scope(), stream_key_t
);
145 key
->p2p_dir
= p2p_dir
;
147 return new_stream(key
);
151 /******************************************************************************
156 /* pdu counter, for generating unique pdu ids */
157 static uint32_t pdu_counter
;
159 static void stream_cleanup_pdu_data(void)
163 static void stream_init_pdu_data(void)
169 /* new pdu in this stream */
170 static stream_pdu_t
*stream_new_pdu(stream_t
*stream
)
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
++;
180 /*****************************************************************************
186 typedef struct fragment_key
{
187 const stream_t
*stream
;
194 static unsigned fragment_hash_func(const void *k
)
196 const fragment_key_t
*key
= (const fragment_key_t
*)k
;
197 return (GPOINTER_TO_UINT(key
->stream
)) + ((unsigned)key
-> framenum
) + ((unsigned)key
->offset
);
201 static gboolean
fragment_compare_func(const void *a
,
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
);
212 static GHashTable
*fragment_hash
;
215 /* cleanup function, call from stream_cleanup() */
216 static void cleanup_fragment_hash( void ) {
217 if( fragment_hash
!= NULL
) {
218 g_hash_table_destroy( fragment_hash
);
219 fragment_hash
= NULL
;
223 /* init function, call from stream_init() */
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
);
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
)
235 stream_pdu_fragment_t
*val
;
238 key
.framenum
= framenum
;
240 val
= (stream_pdu_fragment_t
*)g_hash_table_lookup(fragment_hash
, &key
);
246 /* insert function */
247 static stream_pdu_fragment_t
*fragment_hash_insert( const stream_t
*stream
, uint32_t framenum
, uint32_t offset
,
251 stream_pdu_fragment_t
*val
;
253 key
= wmem_new(wmem_file_scope(), fragment_key_t
);
254 key
->stream
= stream
;
255 key
->framenum
= framenum
;
256 key
->offset
= offset
;
258 val
= wmem_new(wmem_file_scope(), stream_pdu_fragment_t
);
261 val
->final_fragment
= false;
263 g_hash_table_insert(fragment_hash
, key
, val
);
267 /*****************************************************************************/
269 /* reassembly table */
270 static reassembly_table stream_reassembly_table
;
272 /* Initialise a new stream. Call this when you first identify a distinct
274 stream_t
*stream_new ( const struct conversation
*conv
, int p2p_dir
)
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
);
283 stream
= stream_hash_insert(conv
, p2p_dir
);
288 /* retrieve a previously-created stream.
290 * Returns null if no matching stream was found.
292 stream_t
*find_stream ( const struct conversation
*conv
, int p2p_dir
)
294 return stream_hash_lookup(conv
,p2p_dir
);
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.
302 void stream_cleanup( void )
304 cleanup_stream_hash();
305 cleanup_fragment_hash();
306 stream_cleanup_pdu_data();
307 reassembly_table_destroy(&stream_reassembly_table
);
310 /* initialise the stream routines */
311 void stream_init( void )
314 init_fragment_hash();
315 stream_init_pdu_data();
317 reassembly_table_init(&stream_reassembly_table
,
318 &addresses_reassembly_table_functions
);
321 /*****************************************************************************/
323 stream_pdu_fragment_t
*stream_find_frag( stream_t
*stream
, uint32_t framenum
, uint32_t offset
)
325 return fragment_hash_lookup( stream
, framenum
, offset
);
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
)
331 fragment_head
*fd_head
;
333 stream_pdu_fragment_t
*frag_data
;
335 DISSECTOR_ASSERT(stream
);
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
));
342 pdu
= stream
->current_pdu
;
344 /* start a new pdu */
345 pdu
= stream
->current_pdu
= stream_new_pdu(stream
);
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
;
356 if( fd_head
!= NULL
) {
357 /* if this was the last fragment, update the pdu data.
359 pdu
-> fd_head
= fd_head
;
361 /* start a new pdu next time */
362 stream
->current_pdu
= NULL
;
364 frag_data
-> final_fragment
= true;
367 /* stashing the framenum and offset permit future sanity checks */
368 stream
-> lastfrag_framenum
= framenum
;
369 stream
-> lastfrag_offset
= offset
;
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
)
382 DISSECTOR_ASSERT(frag
);
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
);
396 return process_reassembled_data(tvb
, offset
, pinfo
, name
, pdu
->fd_head
,
397 fit
, update_col_infop
, tree
);
400 uint32_t stream_get_frag_length( const stream_pdu_fragment_t
*frag
)
402 DISSECTOR_ASSERT( frag
);
406 fragment_head
*stream_get_frag_data( const stream_pdu_fragment_t
*frag
)
408 DISSECTOR_ASSERT( frag
);
409 return frag
->pdu
->fd_head
;
412 uint32_t stream_get_pdu_no( const stream_pdu_fragment_t
*frag
)
414 DISSECTOR_ASSERT( frag
);
415 return frag
->pdu
->pdu_number
;
419 * Editor modelines - https://www.wireshark.org/tools/modelines.html
424 * indent-tabs-mode: nil
427 * vi: set shiftwidth=4 tabstop=8 expandtab:
428 * :indentSize=4:tabSize=8:noTabs=true: