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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
33 #include <epan/packet.h>
34 #include <epan/reassemble.h>
35 #include <epan/stream.h>
36 #include <epan/tvbuff.h>
40 fragment_data *fd_head; /* the reassembled data, NULL
41 * until we add the last fragment */
42 guint32 pdu_number; /* Number of this PDU within the stream */
44 /* id of this pdu (globally unique) */
49 struct stream_pdu_fragment
51 guint32 len; /* the length of this fragment */
53 gboolean final_fragment;
57 /* the key used to add this stream to stream_hash */
58 struct stream_key *key;
60 /* pdu to add the next fragment to, or NULL if we need to start
63 stream_pdu_t *current_pdu;
65 /* number of PDUs added to this stream so far */
68 /* the framenumber and offset of the last fragment added;
69 used for sanity-checking */
70 guint32 lastfrag_framenum;
71 guint32 lastfrag_offset;
75 /*****************************************************************************
81 typedef struct stream_key {
82 /* streams can be attached to circuits or conversations, and we note
86 const struct circuit *circuit;
87 const struct conversation *conv;
94 static guint stream_hash_func(gconstpointer k)
96 const stream_key_t *key = (const stream_key_t *)k;
98 /* is_circuit is redundant to the circuit/conversation pointer */
99 return ((guint)(unsigned long)key->circ.circuit) ^ key->p2p_dir;
103 static gboolean stream_compare_func(gconstpointer a,
106 const stream_key_t *key1 = (const stream_key_t *)a;
107 const stream_key_t *key2 = (const stream_key_t *)b;
108 if( key1 -> p2p_dir != key2 -> p2p_dir ||
109 key1-> is_circuit != key2 -> is_circuit )
112 if( key1 -> is_circuit )
113 return (key1 -> circ.circuit == key2 -> circ.circuit );
115 return (key1 -> circ.conv == key2 -> circ.conv );
119 static GHashTable *stream_hash;
122 /* cleanup reset function, call from stream_cleanup() */
123 static void cleanup_stream_hash( void ) {
124 if( stream_hash != NULL ) {
125 g_hash_table_destroy( stream_hash );
130 /* init function, call from stream_init() */
131 static void init_stream_hash( void ) {
132 g_assert(stream_hash==NULL);
133 stream_hash = g_hash_table_new(stream_hash_func,
134 stream_compare_func);
137 /* lookup function, returns null if not found */
138 static stream_t *stream_hash_lookup_circ( const struct circuit *circuit, int p2p_dir )
142 key.circ.circuit=circuit;
144 return (stream_t *)g_hash_table_lookup(stream_hash, &key);
147 static stream_t *stream_hash_lookup_conv( const struct conversation *conv, int p2p_dir )
150 key.is_circuit=FALSE;
151 key.circ.conv = conv;
153 return (stream_t *)g_hash_table_lookup(stream_hash, &key);
157 static stream_t *new_stream( stream_key_t *key )
161 val = se_alloc(sizeof(stream_t));
163 val -> pdu_counter = 0;
164 val -> current_pdu = NULL;
165 val -> lastfrag_framenum = 0;
166 val -> lastfrag_offset = 0;
167 g_hash_table_insert(stream_hash, key, val);
173 /* insert function */
174 static stream_t *stream_hash_insert_circ( const struct circuit *circuit, int p2p_dir )
178 key = se_alloc(sizeof(stream_key_t));
179 key->is_circuit = TRUE;
180 key->circ.circuit = circuit;
181 key->p2p_dir = p2p_dir;
183 return new_stream(key);
186 static stream_t *stream_hash_insert_conv( const struct conversation *conv, int p2p_dir )
190 key = se_alloc(sizeof(stream_key_t));
191 key->is_circuit = FALSE;
192 key->circ.conv = conv;
193 key->p2p_dir = p2p_dir;
195 return new_stream(key);
199 /******************************************************************************
204 /* pdu counter, for generating unique pdu ids */
205 static guint32 pdu_counter;
207 static void stream_cleanup_pdu_data(void)
211 static void stream_init_pdu_data(void)
217 /* new pdu in this stream */
218 static stream_pdu_t *stream_new_pdu(stream_t *stream)
221 pdu = se_alloc(sizeof(stream_pdu_t));
222 pdu -> fd_head = NULL;
223 pdu -> pdu_number = stream -> pdu_counter++;
224 pdu -> id = pdu_counter++;
228 /*****************************************************************************
234 typedef struct fragment_key {
235 const stream_t *stream;
242 static guint fragment_hash_func(gconstpointer k)
244 const fragment_key_t *key = (const fragment_key_t *)k;
245 return ((guint)(unsigned long)key->stream) + ((guint)key -> framenum) + ((guint)key->offset);
249 static gboolean fragment_compare_func(gconstpointer a,
252 const fragment_key_t *key1 = (const fragment_key_t *)a;
253 const fragment_key_t *key2 = (const fragment_key_t *)b;
254 return (key1 -> stream == key2 -> stream &&
255 key1 -> framenum == key2 -> framenum &&
256 key1 -> offset == key2 -> offset );
260 static GHashTable *fragment_hash;
263 /* cleanup function, call from stream_cleanup() */
264 static void cleanup_fragment_hash( void ) {
265 if( fragment_hash != NULL ) {
266 g_hash_table_destroy( fragment_hash );
267 fragment_hash = NULL;
271 /* init function, call from stream_init() */
272 static void init_fragment_hash( void ) {
273 g_assert(fragment_hash==NULL);
274 fragment_hash = g_hash_table_new(fragment_hash_func,
275 fragment_compare_func);
279 /* lookup function, returns null if not found */
280 static stream_pdu_fragment_t *fragment_hash_lookup( const stream_t *stream, guint32 framenum, guint32 offset )
283 stream_pdu_fragment_t *val;
286 key.framenum = framenum;
288 val = g_hash_table_lookup(fragment_hash, &key);
294 /* insert function */
295 static stream_pdu_fragment_t *fragment_hash_insert( const stream_t *stream, guint32 framenum, guint32 offset,
299 stream_pdu_fragment_t *val;
301 key = se_alloc(sizeof(fragment_key_t));
302 key->stream = stream;
303 key->framenum = framenum;
304 key->offset = offset;
306 val = se_alloc(sizeof(stream_pdu_fragment_t));
309 val->final_fragment = FALSE;
311 g_hash_table_insert(fragment_hash, key, val);
315 /*****************************************************************************/
317 /* fragmentation hash tables */
318 static GHashTable *stream_fragment_table = NULL;
319 static GHashTable *stream_reassembled_table = NULL;
321 /* Initialise a new stream. Call this when you first identify a distinct
323 stream_t *stream_new_circ ( const struct circuit *circuit, int p2p_dir )
327 /* we don't want to replace the previous data if we get called twice on the
328 same circuit, so do a lookup first */
329 stream = stream_hash_lookup_circ(circuit, p2p_dir);
330 DISSECTOR_ASSERT( stream == NULL );
332 stream = stream_hash_insert_circ(circuit, p2p_dir);
337 stream_t *stream_new_conv ( const struct conversation *conv, int p2p_dir )
341 /* we don't want to replace the previous data if we get called twice on the
342 same conversation, so do a lookup first */
343 stream = stream_hash_lookup_conv(conv, p2p_dir);
344 DISSECTOR_ASSERT( stream == NULL );
346 stream = stream_hash_insert_conv(conv, p2p_dir);
351 /* retrieve a previously-created stream.
353 * Returns null if no matching stream was found.
355 stream_t *find_stream_circ ( const struct circuit *circuit, int p2p_dir )
357 return stream_hash_lookup_circ(circuit,p2p_dir);
359 stream_t *find_stream_conv ( const struct conversation *conv, int p2p_dir )
361 return stream_hash_lookup_conv(conv,p2p_dir);
364 /* cleanup the stream routines */
365 /* Note: stream_cleanup must only be called when seasonal memory
366 * is also freed since the hash tables countain pointers to
369 void stream_cleanup( void )
371 cleanup_stream_hash();
372 cleanup_fragment_hash();
373 stream_cleanup_pdu_data();
376 /* initialise the stream routines */
377 void stream_init( void )
380 init_fragment_hash();
381 stream_init_pdu_data();
383 fragment_table_init(&stream_fragment_table);
384 reassembled_table_init(&stream_reassembled_table);
387 /*****************************************************************************/
389 stream_pdu_fragment_t *stream_find_frag( stream_t *stream, guint32 framenum, guint32 offset )
391 return fragment_hash_lookup( stream, framenum, offset );
394 stream_pdu_fragment_t *stream_add_frag( stream_t *stream, guint32 framenum, guint32 offset,
395 tvbuff_t *tvb, packet_info *pinfo, gboolean more_frags )
397 fragment_data *fd_head;
399 stream_pdu_fragment_t *frag_data;
401 DISSECTOR_ASSERT(stream);
403 /* check that this fragment is at the end of the stream */
404 DISSECTOR_ASSERT( framenum > stream->lastfrag_framenum ||
405 (framenum == stream->lastfrag_framenum && offset > stream->lastfrag_offset));
408 pdu = stream->current_pdu;
410 /* start a new pdu */
411 pdu = stream->current_pdu = stream_new_pdu(stream);
414 /* add it to the reassembly tables */
415 fd_head = fragment_add_seq_next(tvb, 0, pinfo, pdu->id,
416 stream_fragment_table, stream_reassembled_table,
417 tvb_reported_length(tvb), more_frags);
418 /* add it to our hash */
419 frag_data = fragment_hash_insert( stream, framenum, offset, tvb_reported_length(tvb));
420 frag_data -> pdu = pdu;
422 if( fd_head != NULL ) {
423 /* if this was the last fragment, update the pdu data.
425 pdu -> fd_head = fd_head;
427 /* start a new pdu next time */
428 stream->current_pdu = NULL;
430 frag_data -> final_fragment = TRUE;
433 /* stashing the framenum and offset permit future sanity checks */
434 stream -> lastfrag_framenum = framenum;
435 stream -> lastfrag_offset = offset;
441 tvbuff_t *stream_process_reassembled(
442 tvbuff_t *tvb, int offset, packet_info *pinfo,
443 const char *name, const stream_pdu_fragment_t *frag,
444 const struct _fragment_items *fit,
445 gboolean *update_col_infop, proto_tree *tree)
448 DISSECTOR_ASSERT(frag);
451 /* we handle non-terminal fragments ourselves, because
452 reassemble.c messes them up */
453 if(!frag->final_fragment) {
454 if (pdu->fd_head != NULL && fit->hf_reassembled_in != NULL) {
455 proto_tree_add_uint(tree,
456 *(fit->hf_reassembled_in), tvb,
457 0, 0, pdu->fd_head->reassembled_in);
462 return process_reassembled_data(tvb, offset, pinfo, name, pdu->fd_head,
463 fit, update_col_infop, tree);
466 guint32 stream_get_frag_length( const stream_pdu_fragment_t *frag)
468 DISSECTOR_ASSERT( frag );
472 fragment_data *stream_get_frag_data( const stream_pdu_fragment_t *frag)
474 DISSECTOR_ASSERT( frag );
475 return frag->pdu->fd_head;
478 guint32 stream_get_pdu_no( const stream_pdu_fragment_t *frag)
480 DISSECTOR_ASSERT( frag );
481 return frag->pdu->pdu_number;