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
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 #include <epan/packet.h>
32 #include <epan/emem.h>
33 #include <epan/reassemble.h>
34 #include <epan/stream.h>
35 #include <epan/tvbuff.h>
39 fragment_head
*fd_head
; /* the reassembled data, NULL
40 * until we add the last fragment */
41 guint32 pdu_number
; /* Number of this PDU within the stream */
43 /* id of this pdu (globally unique) */
48 struct stream_pdu_fragment
50 guint32 len
; /* the length of this fragment */
52 gboolean final_fragment
;
56 /* the key used to add this stream to stream_hash */
57 struct stream_key
*key
;
59 /* pdu to add the next fragment to, or NULL if we need to start
62 stream_pdu_t
*current_pdu
;
64 /* number of PDUs added to this stream so far */
67 /* the framenumber and offset of the last fragment added;
68 used for sanity-checking */
69 guint32 lastfrag_framenum
;
70 guint32 lastfrag_offset
;
74 /*****************************************************************************
80 typedef struct stream_key
{
81 /* streams can be attached to circuits or conversations, and we note
85 const struct circuit
*circuit
;
86 const struct conversation
*conv
;
93 static guint
stream_hash_func(gconstpointer k
)
95 const stream_key_t
*key
= (const stream_key_t
*)k
;
97 /* is_circuit is redundant to the circuit/conversation pointer */
98 return ((guint
)(unsigned long)key
->circ
.circuit
) ^ key
->p2p_dir
;
102 static gboolean
stream_compare_func(gconstpointer a
,
105 const stream_key_t
*key1
= (const stream_key_t
*)a
;
106 const stream_key_t
*key2
= (const stream_key_t
*)b
;
107 if( key1
-> p2p_dir
!= key2
-> p2p_dir
||
108 key1
-> is_circuit
!= key2
-> is_circuit
)
111 if( key1
-> is_circuit
)
112 return (key1
-> circ
.circuit
== key2
-> circ
.circuit
);
114 return (key1
-> circ
.conv
== key2
-> circ
.conv
);
118 static GHashTable
*stream_hash
;
121 /* cleanup reset function, call from stream_cleanup() */
122 static void cleanup_stream_hash( void ) {
123 if( stream_hash
!= NULL
) {
124 g_hash_table_destroy( stream_hash
);
129 /* init function, call from stream_init() */
130 static void init_stream_hash( void ) {
131 g_assert(stream_hash
==NULL
);
132 stream_hash
= g_hash_table_new(stream_hash_func
,
133 stream_compare_func
);
136 /* lookup function, returns null if not found */
137 static stream_t
*stream_hash_lookup_circ( const struct circuit
*circuit
, int p2p_dir
)
141 key
.circ
.circuit
=circuit
;
143 return (stream_t
*)g_hash_table_lookup(stream_hash
, &key
);
146 static stream_t
*stream_hash_lookup_conv( const struct conversation
*conv
, int p2p_dir
)
149 key
.is_circuit
=FALSE
;
150 key
.circ
.conv
= conv
;
152 return (stream_t
*)g_hash_table_lookup(stream_hash
, &key
);
156 static stream_t
*new_stream( stream_key_t
*key
)
160 val
= se_new(stream_t
);
162 val
-> pdu_counter
= 0;
163 val
-> current_pdu
= NULL
;
164 val
-> lastfrag_framenum
= 0;
165 val
-> lastfrag_offset
= 0;
166 g_hash_table_insert(stream_hash
, key
, val
);
172 /* insert function */
173 static stream_t
*stream_hash_insert_circ( const struct circuit
*circuit
, int p2p_dir
)
177 key
= se_new(stream_key_t
);
178 key
->is_circuit
= TRUE
;
179 key
->circ
.circuit
= circuit
;
180 key
->p2p_dir
= p2p_dir
;
182 return new_stream(key
);
185 static stream_t
*stream_hash_insert_conv( const struct conversation
*conv
, int p2p_dir
)
189 key
= se_new(stream_key_t
);
190 key
->is_circuit
= FALSE
;
191 key
->circ
.conv
= conv
;
192 key
->p2p_dir
= p2p_dir
;
194 return new_stream(key
);
198 /******************************************************************************
203 /* pdu counter, for generating unique pdu ids */
204 static guint32 pdu_counter
;
206 static void stream_cleanup_pdu_data(void)
210 static void stream_init_pdu_data(void)
216 /* new pdu in this stream */
217 static stream_pdu_t
*stream_new_pdu(stream_t
*stream
)
220 pdu
= se_new(stream_pdu_t
);
221 pdu
-> fd_head
= NULL
;
222 pdu
-> pdu_number
= stream
-> pdu_counter
++;
223 pdu
-> id
= pdu_counter
++;
227 /*****************************************************************************
233 typedef struct fragment_key
{
234 const stream_t
*stream
;
241 static guint
fragment_hash_func(gconstpointer k
)
243 const fragment_key_t
*key
= (const fragment_key_t
*)k
;
244 return ((guint
)(unsigned long)key
->stream
) + ((guint
)key
-> framenum
) + ((guint
)key
->offset
);
248 static gboolean
fragment_compare_func(gconstpointer a
,
251 const fragment_key_t
*key1
= (const fragment_key_t
*)a
;
252 const fragment_key_t
*key2
= (const fragment_key_t
*)b
;
253 return (key1
-> stream
== key2
-> stream
&&
254 key1
-> framenum
== key2
-> framenum
&&
255 key1
-> offset
== key2
-> offset
);
259 static GHashTable
*fragment_hash
;
262 /* cleanup function, call from stream_cleanup() */
263 static void cleanup_fragment_hash( void ) {
264 if( fragment_hash
!= NULL
) {
265 g_hash_table_destroy( fragment_hash
);
266 fragment_hash
= NULL
;
270 /* init function, call from stream_init() */
271 static void init_fragment_hash( void ) {
272 g_assert(fragment_hash
==NULL
);
273 fragment_hash
= g_hash_table_new(fragment_hash_func
,
274 fragment_compare_func
);
278 /* lookup function, returns null if not found */
279 static stream_pdu_fragment_t
*fragment_hash_lookup( const stream_t
*stream
, guint32 framenum
, guint32 offset
)
282 stream_pdu_fragment_t
*val
;
285 key
.framenum
= framenum
;
287 val
= (stream_pdu_fragment_t
*)g_hash_table_lookup(fragment_hash
, &key
);
293 /* insert function */
294 static stream_pdu_fragment_t
*fragment_hash_insert( const stream_t
*stream
, guint32 framenum
, guint32 offset
,
298 stream_pdu_fragment_t
*val
;
300 key
= se_new(fragment_key_t
);
301 key
->stream
= stream
;
302 key
->framenum
= framenum
;
303 key
->offset
= offset
;
305 val
= se_new(stream_pdu_fragment_t
);
308 val
->final_fragment
= FALSE
;
310 g_hash_table_insert(fragment_hash
, key
, val
);
314 /*****************************************************************************/
316 /* reassembly table */
317 static reassembly_table stream_reassembly_table
;
319 /* Initialise a new stream. Call this when you first identify a distinct
321 stream_t
*stream_new_circ ( const struct circuit
*circuit
, int p2p_dir
)
325 /* we don't want to replace the previous data if we get called twice on the
326 same circuit, so do a lookup first */
327 stream
= stream_hash_lookup_circ(circuit
, p2p_dir
);
328 DISSECTOR_ASSERT( stream
== NULL
);
330 stream
= stream_hash_insert_circ(circuit
, p2p_dir
);
335 stream_t
*stream_new_conv ( const struct conversation
*conv
, int p2p_dir
)
339 /* we don't want to replace the previous data if we get called twice on the
340 same conversation, so do a lookup first */
341 stream
= stream_hash_lookup_conv(conv
, p2p_dir
);
342 DISSECTOR_ASSERT( stream
== NULL
);
344 stream
= stream_hash_insert_conv(conv
, p2p_dir
);
349 /* retrieve a previously-created stream.
351 * Returns null if no matching stream was found.
353 stream_t
*find_stream_circ ( const struct circuit
*circuit
, int p2p_dir
)
355 return stream_hash_lookup_circ(circuit
,p2p_dir
);
357 stream_t
*find_stream_conv ( const struct conversation
*conv
, int p2p_dir
)
359 return stream_hash_lookup_conv(conv
,p2p_dir
);
362 /* cleanup the stream routines */
363 /* Note: stream_cleanup must only be called when seasonal memory
364 * is also freed since the hash tables countain pointers to
367 void stream_cleanup( void )
369 cleanup_stream_hash();
370 cleanup_fragment_hash();
371 stream_cleanup_pdu_data();
374 /* initialise the stream routines */
375 void stream_init( void )
378 init_fragment_hash();
379 stream_init_pdu_data();
381 reassembly_table_init(&stream_reassembly_table
,
382 &addresses_reassembly_table_functions
);
385 /*****************************************************************************/
387 stream_pdu_fragment_t
*stream_find_frag( stream_t
*stream
, guint32 framenum
, guint32 offset
)
389 return fragment_hash_lookup( stream
, framenum
, offset
);
392 stream_pdu_fragment_t
*stream_add_frag( stream_t
*stream
, guint32 framenum
, guint32 offset
,
393 tvbuff_t
*tvb
, packet_info
*pinfo
, gboolean more_frags
)
395 fragment_head
*fd_head
;
397 stream_pdu_fragment_t
*frag_data
;
399 DISSECTOR_ASSERT(stream
);
401 /* check that this fragment is at the end of the stream */
402 DISSECTOR_ASSERT( framenum
> stream
->lastfrag_framenum
||
403 (framenum
== stream
->lastfrag_framenum
&& offset
> stream
->lastfrag_offset
));
406 pdu
= stream
->current_pdu
;
408 /* start a new pdu */
409 pdu
= stream
->current_pdu
= stream_new_pdu(stream
);
412 /* add it to the reassembly tables */
413 fd_head
= fragment_add_seq_next(&stream_reassembly_table
,
414 tvb
, 0, pinfo
, pdu
->id
, NULL
,
415 tvb_reported_length(tvb
), more_frags
);
416 /* add it to our hash */
417 frag_data
= fragment_hash_insert( stream
, framenum
, offset
, tvb_reported_length(tvb
));
418 frag_data
-> pdu
= pdu
;
420 if( fd_head
!= NULL
) {
421 /* if this was the last fragment, update the pdu data.
423 pdu
-> fd_head
= fd_head
;
425 /* start a new pdu next time */
426 stream
->current_pdu
= NULL
;
428 frag_data
-> final_fragment
= TRUE
;
431 /* stashing the framenum and offset permit future sanity checks */
432 stream
-> lastfrag_framenum
= framenum
;
433 stream
-> lastfrag_offset
= offset
;
439 tvbuff_t
*stream_process_reassembled(
440 tvbuff_t
*tvb
, int offset
, packet_info
*pinfo
,
441 const char *name
, const stream_pdu_fragment_t
*frag
,
442 const struct _fragment_items
*fit
,
443 gboolean
*update_col_infop
, proto_tree
*tree
)
446 DISSECTOR_ASSERT(frag
);
449 /* we handle non-terminal fragments ourselves, because
450 reassemble.c messes them up */
451 if(!frag
->final_fragment
) {
452 if (pdu
->fd_head
!= NULL
&& fit
->hf_reassembled_in
!= NULL
) {
453 proto_tree_add_uint(tree
,
454 *(fit
->hf_reassembled_in
), tvb
,
455 0, 0, pdu
->fd_head
->reassembled_in
);
460 return process_reassembled_data(tvb
, offset
, pinfo
, name
, pdu
->fd_head
,
461 fit
, update_col_infop
, tree
);
464 guint32
stream_get_frag_length( const stream_pdu_fragment_t
*frag
)
466 DISSECTOR_ASSERT( frag
);
470 fragment_head
*stream_get_frag_data( const stream_pdu_fragment_t
*frag
)
472 DISSECTOR_ASSERT( frag
);
473 return frag
->pdu
->fd_head
;
476 guint32
stream_get_pdu_no( const stream_pdu_fragment_t
*frag
)
478 DISSECTOR_ASSERT( frag
);
479 return frag
->pdu
->pdu_number
;