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>
39 /* number of streams to allocate memory for at once */
40 #define MEMCHUNK_STREAM_COUNT 20
43 #define MEMCHUNK_PDU_COUNT 100
46 #define MEMCHUNK_FRAGMENT_COUNT 100
50 fragment_data *fd_head; /* the reassembled data, NULL
51 * until we add the last fragment */
52 guint32 pdu_number; /* Number of this PDU within the stream */
54 /* id of this pdu (globally unique) */
59 struct stream_pdu_fragment
61 guint32 len; /* the length of this fragment */
63 gboolean final_fragment;
67 /* the key used to add this stream to stream_hash */
68 struct stream_key *key;
70 /* pdu to add the next fragment to, or NULL if we need to start
73 stream_pdu_t *current_pdu;
75 /* number of PDUs added to this stream so far */
78 /* the framenumber and offset of the last fragment added;
79 used for sanity-checking */
80 guint32 lastfrag_framenum;
81 guint32 lastfrag_offset;
85 /*****************************************************************************
91 typedef struct stream_key {
92 /* streams can be attached to circuits or conversations, and we note
96 const struct circuit *circuit;
97 const struct conversation *conv;
104 guint stream_hash_func(gconstpointer k)
106 const stream_key_t *key = (const stream_key_t *)k;
108 /* is_circuit is redundant to the circuit/conversation pointer */
109 return ((guint)key->circ.circuit) ^ key->p2p_dir;
113 gboolean stream_compare_func(gconstpointer a,
116 const stream_key_t *key1 = (const stream_key_t *)a;
117 const stream_key_t *key2 = (const stream_key_t *)b;
118 if( key1 -> p2p_dir != key2 -> p2p_dir ||
119 key1-> is_circuit != key2 -> is_circuit )
122 if( key1 -> is_circuit )
123 return (key1 -> circ.circuit == key2 -> circ.circuit );
125 return (key1 -> circ.conv == key2 -> circ.conv );
129 static GMemChunk *stream_keys = NULL;
130 static GMemChunk *streams = NULL;
134 static GHashTable *stream_hash;
137 /* init/reset function, call from stream_init() */
138 static void init_stream_hash( void ) {
139 if( stream_hash != NULL ) {
140 g_hash_table_destroy( stream_hash );
144 if( stream_keys != NULL ) {
145 g_mem_chunk_destroy( stream_keys );
149 if( streams != NULL ) {
150 g_mem_chunk_destroy( streams );
154 streams = g_mem_chunk_create(stream_t,
155 MEMCHUNK_STREAM_COUNT,
158 stream_keys = g_mem_chunk_create(stream_key_t,
159 MEMCHUNK_STREAM_COUNT,
162 stream_hash = g_hash_table_new(stream_hash_func,
163 stream_compare_func);
167 /* lookup function, returns null if not found */
168 static stream_t *stream_hash_lookup_circ( const struct circuit *circuit, int p2p_dir )
172 key.circ.circuit=circuit;
174 return (stream_t *)g_hash_table_lookup(stream_hash, &key);
177 static stream_t *stream_hash_lookup_conv( const struct conversation *conv, int p2p_dir )
180 key.is_circuit=FALSE;
181 key.circ.conv = conv;
183 return (stream_t *)g_hash_table_lookup(stream_hash, &key);
187 static stream_t *new_stream( stream_key_t *key )
191 val = g_mem_chunk_alloc(streams);
193 val -> pdu_counter = 0;
194 val -> current_pdu = NULL;
195 val -> lastfrag_framenum = 0;
196 val -> lastfrag_offset = 0;
197 g_hash_table_insert(stream_hash, key, val);
203 /* insert function */
204 static stream_t *stream_hash_insert_circ( const struct circuit *circuit, int p2p_dir )
208 key = g_mem_chunk_alloc(stream_keys);
209 key->is_circuit = TRUE;
210 key->circ.circuit = circuit;
211 key->p2p_dir = p2p_dir;
213 return new_stream(key);
216 static stream_t *stream_hash_insert_conv( const struct conversation *conv, int p2p_dir )
220 key = g_mem_chunk_alloc(stream_keys);
221 key->is_circuit = FALSE;
222 key->circ.conv = conv;
223 key->p2p_dir = p2p_dir;
225 return new_stream(key);
229 /******************************************************************************
233 static GMemChunk *pdus = NULL;
235 /* pdu counter, for generating unique pdu ids */
236 static guint32 pdu_counter;
239 static void stream_init_pdu_data(void)
242 g_mem_chunk_destroy( pdus );
246 pdus = g_mem_chunk_create(stream_pdu_t,
253 /* new pdu in this stream */
254 static stream_pdu_t *stream_new_pdu(stream_t *stream)
257 pdu = g_mem_chunk_alloc(pdus);
258 pdu -> fd_head = NULL;
259 pdu -> pdu_number = stream -> pdu_counter++;
260 pdu -> id = pdu_counter++;
264 /*****************************************************************************
270 typedef struct fragment_key {
271 const stream_t *stream;
278 guint fragment_hash_func(gconstpointer k)
280 const fragment_key_t *key = (const fragment_key_t *)k;
281 return ((guint)key->stream) + ((guint)key -> framenum) + ((guint)key->offset);
285 gboolean fragment_compare_func(gconstpointer a,
288 const fragment_key_t *key1 = (const fragment_key_t *)a;
289 const fragment_key_t *key2 = (const fragment_key_t *)b;
290 return (key1 -> stream == key2 -> stream &&
291 key1 -> framenum == key2 -> framenum &&
292 key1 -> offset == key2 -> offset );
296 static GMemChunk *fragment_keys = NULL;
297 static GMemChunk *fragment_vals = NULL;
300 static GHashTable *fragment_hash;
303 /* init/reset function, call from stream_init() */
304 static void init_fragment_hash( void ) {
305 if( fragment_hash != NULL ) {
306 g_hash_table_destroy( fragment_hash );
307 fragment_hash = NULL;
310 if( fragment_vals != NULL ) {
311 g_mem_chunk_destroy( fragment_vals );
312 fragment_vals = NULL;
315 if( fragment_keys != NULL ) {
316 g_mem_chunk_destroy( fragment_keys );
317 fragment_keys = NULL;
320 fragment_keys = g_mem_chunk_create(fragment_key_t,
321 MEMCHUNK_FRAGMENT_COUNT,
324 fragment_vals = g_mem_chunk_create(stream_pdu_fragment_t,
325 MEMCHUNK_FRAGMENT_COUNT,
328 fragment_hash = g_hash_table_new(fragment_hash_func,
329 fragment_compare_func);
333 /* lookup function, returns null if not found */
334 static stream_pdu_fragment_t *fragment_hash_lookup( const stream_t *stream, guint32 framenum, guint32 offset )
337 stream_pdu_fragment_t *val;
340 key.framenum = framenum;
342 val = g_hash_table_lookup(fragment_hash, &key);
348 /* insert function */
349 static stream_pdu_fragment_t *fragment_hash_insert( const stream_t *stream, guint32 framenum, guint32 offset,
353 stream_pdu_fragment_t *val;
355 key = g_mem_chunk_alloc(fragment_keys);
356 key->stream = stream;
357 key->framenum = framenum;
358 key->offset = offset;
360 val = g_mem_chunk_alloc(fragment_vals);
363 val->final_fragment = FALSE;
365 g_hash_table_insert(fragment_hash, key, val);
369 /*****************************************************************************/
371 /* fragmentation hash tables */
372 static GHashTable *stream_fragment_table = NULL;
373 static GHashTable *stream_reassembled_table = NULL;
375 /* Initialise a new stream. Call this when you first identify a distinct
377 stream_t *stream_new_circ ( const struct circuit *circuit, int p2p_dir )
381 /* we don't want to replace the previous data if we get called twice on the
382 same circuit, so do a lookup first */
383 stream = stream_hash_lookup_circ(circuit, p2p_dir);
384 DISSECTOR_ASSERT( stream == NULL );
386 stream = stream_hash_insert_circ(circuit, p2p_dir);
391 stream_t *stream_new_conv ( const struct conversation *conv, int p2p_dir )
395 /* we don't want to replace the previous data if we get called twice on the
396 same conversation, so do a lookup first */
397 stream = stream_hash_lookup_conv(conv, p2p_dir);
398 DISSECTOR_ASSERT( stream == NULL );
400 stream = stream_hash_insert_conv(conv, p2p_dir);
407 /* retrieve a previously-created stream.
409 * Returns null if no matching stream was found.
411 stream_t *find_stream_circ ( const struct circuit *circuit, int p2p_dir )
413 return stream_hash_lookup_circ(circuit,p2p_dir);
415 stream_t *find_stream_conv ( const struct conversation *conv, int p2p_dir )
417 return stream_hash_lookup_conv(conv,p2p_dir);
421 /* initialise the stream routines */
422 void stream_init( void )
425 init_fragment_hash();
426 stream_init_pdu_data();
428 fragment_table_init(&stream_fragment_table);
429 reassembled_table_init(&stream_reassembled_table);
435 /*****************************************************************************/
437 stream_pdu_fragment_t *stream_find_frag( stream_t *stream, guint32 framenum, guint32 offset )
439 return fragment_hash_lookup( stream, framenum, offset );
442 stream_pdu_fragment_t *stream_add_frag( stream_t *stream, guint32 framenum, guint32 offset,
443 tvbuff_t *tvb, packet_info *pinfo, gboolean more_frags )
445 fragment_data *fd_head;
447 stream_pdu_fragment_t *frag_data;
449 DISSECTOR_ASSERT(stream);
451 /* check that this fragment is at the end of the stream */
452 DISSECTOR_ASSERT( framenum > stream->lastfrag_framenum ||
453 (framenum == stream->lastfrag_framenum && offset > stream->lastfrag_offset));
456 pdu = stream->current_pdu;
458 /* start a new pdu */
459 pdu = stream->current_pdu = stream_new_pdu(stream);
462 /* add it to the reassembly tables */
463 fd_head = fragment_add_seq_next(tvb, 0, pinfo, pdu->id,
464 stream_fragment_table, stream_reassembled_table,
465 tvb_reported_length(tvb), more_frags);
466 /* add it to our hash */
467 frag_data = fragment_hash_insert( stream, framenum, offset, tvb_reported_length(tvb));
468 frag_data -> pdu = pdu;
470 if( fd_head != NULL ) {
471 /* if this was the last fragment, update the pdu data.
473 pdu -> fd_head = fd_head;
475 /* start a new pdu next time */
476 stream->current_pdu = NULL;
478 frag_data -> final_fragment = TRUE;
481 /* stashing the framenum and offset permit future sanity checks */
482 stream -> lastfrag_framenum = framenum;
483 stream -> lastfrag_offset = offset;
489 tvbuff_t *stream_process_reassembled(
490 tvbuff_t *tvb, int offset, packet_info *pinfo,
491 char *name, const stream_pdu_fragment_t *frag,
492 const struct _fragment_items *fit,
493 gboolean *update_col_infop, proto_tree *tree)
496 DISSECTOR_ASSERT(frag);
499 /* we handle non-terminal fragments ourselves, because
500 reassemble.c messes them up */
501 if(!frag->final_fragment) {
502 if (pdu->fd_head != NULL && fit->hf_reassembled_in != NULL) {
503 proto_tree_add_uint(tree,
504 *(fit->hf_reassembled_in), tvb,
505 0, 0, pdu->fd_head->reassembled_in);
510 return process_reassembled_data(tvb, offset, pinfo, name, pdu->fd_head,
511 fit, update_col_infop, tree);
514 guint32 stream_get_frag_length( const stream_pdu_fragment_t *frag)
516 DISSECTOR_ASSERT( frag );
520 fragment_data *stream_get_frag_data( const stream_pdu_fragment_t *frag)
522 DISSECTOR_ASSERT( frag );
523 return frag->pdu->fd_head;
526 guint32 stream_get_pdu_no( const stream_pdu_fragment_t *frag)
528 DISSECTOR_ASSERT( frag );
529 return frag->pdu->pdu_number;