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 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 #include <epan/packet.h>
30 #include <epan/emem.h>
31 #include <epan/reassemble.h>
32 #include <epan/stream.h>
33 #include <epan/tvbuff.h>
37 fragment_head *fd_head; /* the reassembled data, NULL
38 * until we add the last fragment */
39 guint32 pdu_number; /* Number of this PDU within the stream */
41 /* id of this pdu (globally unique) */
46 struct stream_pdu_fragment
48 guint32 len; /* the length of this fragment */
50 gboolean final_fragment;
54 /* the key used to add this stream to stream_hash */
55 struct stream_key *key;
57 /* pdu to add the next fragment to, or NULL if we need to start
60 stream_pdu_t *current_pdu;
62 /* number of PDUs added to this stream so far */
65 /* the framenumber and offset of the last fragment added;
66 used for sanity-checking */
67 guint32 lastfrag_framenum;
68 guint32 lastfrag_offset;
72 /*****************************************************************************
78 typedef struct stream_key {
79 /* streams can be attached to circuits or conversations, and we note
83 const struct circuit *circuit;
84 const struct conversation *conv;
91 static guint stream_hash_func(gconstpointer k)
93 const stream_key_t *key = (const stream_key_t *)k;
95 /* is_circuit is redundant to the circuit/conversation pointer */
96 return ((guint)(unsigned long)key->circ.circuit) ^ key->p2p_dir;
100 static gboolean stream_compare_func(gconstpointer a,
103 const stream_key_t *key1 = (const stream_key_t *)a;
104 const stream_key_t *key2 = (const stream_key_t *)b;
105 if( key1 -> p2p_dir != key2 -> p2p_dir ||
106 key1-> is_circuit != key2 -> is_circuit )
109 if( key1 -> is_circuit )
110 return (key1 -> circ.circuit == key2 -> circ.circuit );
112 return (key1 -> circ.conv == key2 -> circ.conv );
116 static GHashTable *stream_hash;
119 /* cleanup reset function, call from stream_cleanup() */
120 static void cleanup_stream_hash( void ) {
121 if( stream_hash != NULL ) {
122 g_hash_table_destroy( stream_hash );
127 /* init function, call from stream_init() */
128 static void init_stream_hash( void ) {
129 g_assert(stream_hash==NULL);
130 stream_hash = g_hash_table_new(stream_hash_func,
131 stream_compare_func);
134 /* lookup function, returns null if not found */
135 static stream_t *stream_hash_lookup_circ( const struct circuit *circuit, int p2p_dir )
139 key.circ.circuit=circuit;
141 return (stream_t *)g_hash_table_lookup(stream_hash, &key);
144 static stream_t *stream_hash_lookup_conv( const struct conversation *conv, int p2p_dir )
147 key.is_circuit=FALSE;
148 key.circ.conv = conv;
150 return (stream_t *)g_hash_table_lookup(stream_hash, &key);
154 static stream_t *new_stream( stream_key_t *key )
158 val = se_new(stream_t);
160 val -> pdu_counter = 0;
161 val -> current_pdu = NULL;
162 val -> lastfrag_framenum = 0;
163 val -> lastfrag_offset = 0;
164 g_hash_table_insert(stream_hash, key, val);
170 /* insert function */
171 static stream_t *stream_hash_insert_circ( const struct circuit *circuit, int p2p_dir )
175 key = se_new(stream_key_t);
176 key->is_circuit = TRUE;
177 key->circ.circuit = circuit;
178 key->p2p_dir = p2p_dir;
180 return new_stream(key);
183 static stream_t *stream_hash_insert_conv( const struct conversation *conv, int p2p_dir )
187 key = se_new(stream_key_t);
188 key->is_circuit = FALSE;
189 key->circ.conv = conv;
190 key->p2p_dir = p2p_dir;
192 return new_stream(key);
196 /******************************************************************************
201 /* pdu counter, for generating unique pdu ids */
202 static guint32 pdu_counter;
204 static void stream_cleanup_pdu_data(void)
208 static void stream_init_pdu_data(void)
214 /* new pdu in this stream */
215 static stream_pdu_t *stream_new_pdu(stream_t *stream)
218 pdu = se_new(stream_pdu_t);
219 pdu -> fd_head = NULL;
220 pdu -> pdu_number = stream -> pdu_counter++;
221 pdu -> id = pdu_counter++;
225 /*****************************************************************************
231 typedef struct fragment_key {
232 const stream_t *stream;
239 static guint fragment_hash_func(gconstpointer k)
241 const fragment_key_t *key = (const fragment_key_t *)k;
242 return ((guint)(unsigned long)key->stream) + ((guint)key -> framenum) + ((guint)key->offset);
246 static gboolean fragment_compare_func(gconstpointer a,
249 const fragment_key_t *key1 = (const fragment_key_t *)a;
250 const fragment_key_t *key2 = (const fragment_key_t *)b;
251 return (key1 -> stream == key2 -> stream &&
252 key1 -> framenum == key2 -> framenum &&
253 key1 -> offset == key2 -> offset );
257 static GHashTable *fragment_hash;
260 /* cleanup function, call from stream_cleanup() */
261 static void cleanup_fragment_hash( void ) {
262 if( fragment_hash != NULL ) {
263 g_hash_table_destroy( fragment_hash );
264 fragment_hash = NULL;
268 /* init function, call from stream_init() */
269 static void init_fragment_hash( void ) {
270 g_assert(fragment_hash==NULL);
271 fragment_hash = g_hash_table_new(fragment_hash_func,
272 fragment_compare_func);
276 /* lookup function, returns null if not found */
277 static stream_pdu_fragment_t *fragment_hash_lookup( const stream_t *stream, guint32 framenum, guint32 offset )
280 stream_pdu_fragment_t *val;
283 key.framenum = framenum;
285 val = (stream_pdu_fragment_t *)g_hash_table_lookup(fragment_hash, &key);
291 /* insert function */
292 static stream_pdu_fragment_t *fragment_hash_insert( const stream_t *stream, guint32 framenum, guint32 offset,
296 stream_pdu_fragment_t *val;
298 key = se_new(fragment_key_t);
299 key->stream = stream;
300 key->framenum = framenum;
301 key->offset = offset;
303 val = se_new(stream_pdu_fragment_t);
306 val->final_fragment = FALSE;
308 g_hash_table_insert(fragment_hash, key, val);
312 /*****************************************************************************/
314 /* reassembly table */
315 static reassembly_table stream_reassembly_table;
317 /* Initialise a new stream. Call this when you first identify a distinct
319 stream_t *stream_new_circ ( const struct circuit *circuit, int p2p_dir )
323 /* we don't want to replace the previous data if we get called twice on the
324 same circuit, so do a lookup first */
325 stream = stream_hash_lookup_circ(circuit, p2p_dir);
326 DISSECTOR_ASSERT( stream == NULL );
328 stream = stream_hash_insert_circ(circuit, p2p_dir);
333 stream_t *stream_new_conv ( const struct conversation *conv, int p2p_dir )
337 /* we don't want to replace the previous data if we get called twice on the
338 same conversation, so do a lookup first */
339 stream = stream_hash_lookup_conv(conv, p2p_dir);
340 DISSECTOR_ASSERT( stream == NULL );
342 stream = stream_hash_insert_conv(conv, p2p_dir);
347 /* retrieve a previously-created stream.
349 * Returns null if no matching stream was found.
351 stream_t *find_stream_circ ( const struct circuit *circuit, int p2p_dir )
353 return stream_hash_lookup_circ(circuit,p2p_dir);
355 stream_t *find_stream_conv ( const struct conversation *conv, int p2p_dir )
357 return stream_hash_lookup_conv(conv,p2p_dir);
360 /* cleanup the stream routines */
361 /* Note: stream_cleanup must only be called when seasonal memory
362 * is also freed since the hash tables countain pointers to
365 void stream_cleanup( void )
367 cleanup_stream_hash();
368 cleanup_fragment_hash();
369 stream_cleanup_pdu_data();
372 /* initialise the stream routines */
373 void stream_init( void )
376 init_fragment_hash();
377 stream_init_pdu_data();
379 reassembly_table_init(&stream_reassembly_table,
380 &addresses_reassembly_table_functions);
383 /*****************************************************************************/
385 stream_pdu_fragment_t *stream_find_frag( stream_t *stream, guint32 framenum, guint32 offset )
387 return fragment_hash_lookup( stream, framenum, offset );
390 stream_pdu_fragment_t *stream_add_frag( stream_t *stream, guint32 framenum, guint32 offset,
391 tvbuff_t *tvb, packet_info *pinfo, gboolean more_frags )
393 fragment_head *fd_head;
395 stream_pdu_fragment_t *frag_data;
397 DISSECTOR_ASSERT(stream);
399 /* check that this fragment is at the end of the stream */
400 DISSECTOR_ASSERT( framenum > stream->lastfrag_framenum ||
401 (framenum == stream->lastfrag_framenum && offset > stream->lastfrag_offset));
404 pdu = stream->current_pdu;
406 /* start a new pdu */
407 pdu = stream->current_pdu = stream_new_pdu(stream);
410 /* add it to the reassembly tables */
411 fd_head = fragment_add_seq_next(&stream_reassembly_table,
412 tvb, 0, pinfo, pdu->id, NULL,
413 tvb_reported_length(tvb), more_frags);
414 /* add it to our hash */
415 frag_data = fragment_hash_insert( stream, framenum, offset, tvb_reported_length(tvb));
416 frag_data -> pdu = pdu;
418 if( fd_head != NULL ) {
419 /* if this was the last fragment, update the pdu data.
421 pdu -> fd_head = fd_head;
423 /* start a new pdu next time */
424 stream->current_pdu = NULL;
426 frag_data -> final_fragment = TRUE;
429 /* stashing the framenum and offset permit future sanity checks */
430 stream -> lastfrag_framenum = framenum;
431 stream -> lastfrag_offset = offset;
437 tvbuff_t *stream_process_reassembled(
438 tvbuff_t *tvb, int offset, packet_info *pinfo,
439 const char *name, const stream_pdu_fragment_t *frag,
440 const struct _fragment_items *fit,
441 gboolean *update_col_infop, proto_tree *tree)
444 DISSECTOR_ASSERT(frag);
447 /* we handle non-terminal fragments ourselves, because
448 reassemble.c messes them up */
449 if(!frag->final_fragment) {
450 if (pdu->fd_head != NULL && fit->hf_reassembled_in != NULL) {
451 proto_tree_add_uint(tree,
452 *(fit->hf_reassembled_in), tvb,
453 0, 0, pdu->fd_head->reassembled_in);
458 return process_reassembled_data(tvb, offset, pinfo, name, pdu->fd_head,
459 fit, update_col_infop, tree);
462 guint32 stream_get_frag_length( const stream_pdu_fragment_t *frag)
464 DISSECTOR_ASSERT( frag );
468 fragment_head *stream_get_frag_data( const stream_pdu_fragment_t *frag)
470 DISSECTOR_ASSERT( frag );
471 return frag->pdu->fd_head;
474 guint32 stream_get_pdu_no( const stream_pdu_fragment_t *frag)
476 DISSECTOR_ASSERT( frag );
477 return frag->pdu->pdu_number;