Minor updates to debug output.
[obnox/wireshark/wip.git] / epan / stream.c
1 /* stream.c
2  *
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
6  *
7  * $Id$
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald@wireshark.org>
11  * Copyright 1998 Gerald Combs
12  *
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.
17  *
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.
22  *
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.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <glib.h>
33 #include <epan/packet.h>
34 #include <epan/reassemble.h>
35 #include <epan/stream.h>
36 #include <epan/tvbuff.h>
37
38
39 typedef struct {
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 */
43
44     /* id of this pdu (globally unique) */
45     guint32 id;
46 } stream_pdu_t;
47
48
49 struct stream_pdu_fragment
50 {
51     guint32 len;                     /* the length of this fragment */
52     stream_pdu_t *pdu;
53     gboolean final_fragment;
54 };
55
56 struct stream {
57     /* the key used to add this stream to stream_hash */
58     struct stream_key *key;
59
60     /* pdu to add the next fragment to, or NULL if we need to start
61      * a new PDU.
62      */
63     stream_pdu_t *current_pdu;
64
65     /* number of PDUs added to this stream so far */
66     guint32 pdu_counter;
67
68     /* the framenumber and offset of the last fragment added;
69        used for sanity-checking */
70     guint32 lastfrag_framenum;
71     guint32 lastfrag_offset;
72 };
73
74
75 /*****************************************************************************
76  *
77  * Stream hash
78  */
79
80 /* key */
81 typedef struct stream_key {
82     /* streams can be attached to circuits or conversations, and we note
83        that here */
84     gboolean is_circuit;
85     union {
86         const struct circuit *circuit;
87         const struct conversation *conv;
88     } circ;
89     int p2p_dir;
90 } stream_key_t;
91
92
93 /* hash func */
94 static guint stream_hash_func(gconstpointer k)
95 {
96     const stream_key_t *key = (const stream_key_t *)k;
97
98     /* is_circuit is redundant to the circuit/conversation pointer */
99     return ((guint)(unsigned long)key->circ.circuit) ^ key->p2p_dir;
100 }
101
102 /* compare func */
103 static gboolean stream_compare_func(gconstpointer a,
104                              gconstpointer b)
105 {
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 )
110         return FALSE;
111
112     if( key1 -> is_circuit )
113         return (key1 -> circ.circuit == key2 -> circ.circuit );
114     else
115         return (key1 -> circ.conv == key2 -> circ.conv );
116 }
117
118 /* the hash table */
119 static GHashTable *stream_hash;
120
121
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 );
126         stream_hash = NULL;
127     }
128 }
129
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);
135 }
136
137 /* lookup function, returns null if not found */
138 static stream_t *stream_hash_lookup_circ( const struct circuit *circuit, int p2p_dir )
139 {
140     stream_key_t key;
141     key.is_circuit=TRUE;
142     key.circ.circuit=circuit;
143     key.p2p_dir=p2p_dir;
144     return (stream_t *)g_hash_table_lookup(stream_hash, &key);
145 }
146
147 static stream_t *stream_hash_lookup_conv( const struct conversation *conv, int p2p_dir )
148 {
149     stream_key_t key;
150     key.is_circuit=FALSE;
151     key.circ.conv = conv;
152     key.p2p_dir=p2p_dir;
153     return (stream_t *)g_hash_table_lookup(stream_hash, &key);
154 }
155
156
157 static stream_t *new_stream( stream_key_t *key )
158 {
159     stream_t *val;
160
161     val = se_alloc(sizeof(stream_t));
162     val -> key = key;
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);
168
169     return val;
170 }
171
172
173 /* insert function */
174 static stream_t *stream_hash_insert_circ( const struct circuit *circuit, int p2p_dir )
175 {
176     stream_key_t *key;
177
178     key = se_alloc(sizeof(stream_key_t));
179     key->is_circuit = TRUE;
180     key->circ.circuit = circuit;
181     key->p2p_dir = p2p_dir;
182
183     return new_stream(key);
184 }
185
186 static stream_t *stream_hash_insert_conv( const struct conversation *conv, int p2p_dir )
187 {
188     stream_key_t *key;
189
190     key = se_alloc(sizeof(stream_key_t));
191     key->is_circuit = FALSE;
192     key->circ.conv = conv;
193     key->p2p_dir = p2p_dir;
194
195     return new_stream(key);
196 }
197
198
199 /******************************************************************************
200  *
201  * PDU data
202  */
203
204 /* pdu counter, for generating unique pdu ids */
205 static guint32 pdu_counter;
206
207 static void stream_cleanup_pdu_data(void)
208 {
209 }
210
211 static void stream_init_pdu_data(void)
212 {
213     pdu_counter = 0;
214 }
215
216
217 /* new pdu in this stream */
218 static stream_pdu_t *stream_new_pdu(stream_t *stream)
219 {
220     stream_pdu_t *pdu;
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++;
225     return pdu;
226 }
227
228 /*****************************************************************************
229  *
230  * fragment hash
231  */
232
233 /* key */
234 typedef struct fragment_key {
235     const stream_t *stream;
236     guint32 framenum;
237     guint32 offset;
238 } fragment_key_t;
239
240
241 /* hash func */
242 static guint fragment_hash_func(gconstpointer k)
243 {
244     const fragment_key_t *key = (const fragment_key_t *)k;
245     return ((guint)(unsigned long)key->stream) + ((guint)key -> framenum) + ((guint)key->offset);
246 }
247
248 /* compare func */
249 static gboolean fragment_compare_func(gconstpointer a,
250                                gconstpointer b)
251 {
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 );
257 }
258
259 /* the hash table */
260 static GHashTable *fragment_hash;
261
262
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;
268     }
269 }
270
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);
276 }
277
278
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 )
281 {
282     fragment_key_t key;
283     stream_pdu_fragment_t *val;
284
285     key.stream = stream;
286     key.framenum = framenum;
287     key.offset = offset;
288     val = g_hash_table_lookup(fragment_hash, &key);
289
290     return val;
291 }
292
293
294 /* insert function */
295 static stream_pdu_fragment_t *fragment_hash_insert( const stream_t *stream, guint32 framenum, guint32 offset,
296                                                     guint32 length)
297 {
298     fragment_key_t *key;
299     stream_pdu_fragment_t *val;
300
301     key = se_alloc(sizeof(fragment_key_t));
302     key->stream = stream;
303     key->framenum = framenum;
304     key->offset = offset;
305
306     val = se_alloc(sizeof(stream_pdu_fragment_t));
307     val->len = length;
308     val->pdu = NULL;
309     val->final_fragment = FALSE;
310
311     g_hash_table_insert(fragment_hash, key, val);
312     return val;
313 }
314
315 /*****************************************************************************/
316
317 /* fragmentation hash tables */
318 static GHashTable *stream_fragment_table = NULL;
319 static GHashTable *stream_reassembled_table = NULL;
320
321 /* Initialise a new stream. Call this when you first identify a distinct
322  * stream. */
323 stream_t *stream_new_circ ( const struct circuit *circuit, int p2p_dir )
324 {
325     stream_t * stream;
326
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 );
331
332     stream = stream_hash_insert_circ(circuit, p2p_dir);
333
334     return stream;
335 }
336
337 stream_t *stream_new_conv ( const struct conversation *conv, int p2p_dir )
338 {
339     stream_t * stream;
340
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 );
345
346     stream = stream_hash_insert_conv(conv, p2p_dir);
347     return stream;
348 }
349
350
351 /* retrieve a previously-created stream.
352  *
353  * Returns null if no matching stream was found.
354  */
355 stream_t *find_stream_circ ( const struct circuit *circuit, int p2p_dir )
356 {
357     return stream_hash_lookup_circ(circuit,p2p_dir);
358 }
359 stream_t *find_stream_conv ( const struct conversation *conv, int p2p_dir )
360 {
361     return stream_hash_lookup_conv(conv,p2p_dir);
362 }
363
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 
367  *       se_alloc'd memory.
368  */
369 void stream_cleanup( void )
370 {
371     cleanup_stream_hash();
372     cleanup_fragment_hash();
373     stream_cleanup_pdu_data();
374 }
375
376 /* initialise the stream routines */
377 void stream_init( void )
378 {
379     init_stream_hash();
380     init_fragment_hash();
381     stream_init_pdu_data();
382
383     fragment_table_init(&stream_fragment_table);
384     reassembled_table_init(&stream_reassembled_table);
385 }
386
387 /*****************************************************************************/
388
389 stream_pdu_fragment_t *stream_find_frag( stream_t *stream, guint32 framenum, guint32 offset )
390 {
391     return fragment_hash_lookup( stream, framenum, offset );
392 }
393
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 )
396 {
397     fragment_data *fd_head;
398     stream_pdu_t *pdu;
399     stream_pdu_fragment_t *frag_data;
400
401     DISSECTOR_ASSERT(stream);
402
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));
406
407
408     pdu = stream->current_pdu;
409     if( pdu == NULL ) {
410         /* start a new pdu */
411         pdu = stream->current_pdu = stream_new_pdu(stream);
412     }
413
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;
421
422     if( fd_head != NULL ) {
423         /* if this was the last fragment, update the pdu data.
424          */
425         pdu -> fd_head = fd_head;
426
427         /* start a new pdu next time */
428         stream->current_pdu = NULL;
429
430         frag_data -> final_fragment = TRUE;
431     }
432
433     /* stashing the framenum and offset permit future sanity checks */
434     stream -> lastfrag_framenum = framenum;
435     stream -> lastfrag_offset = offset;
436
437     return frag_data;
438 }
439
440
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)
446 {
447     stream_pdu_t *pdu;
448     DISSECTOR_ASSERT(frag);
449     pdu = frag->pdu;
450
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);
458         }
459         return NULL;
460     }
461
462     return process_reassembled_data(tvb, offset, pinfo, name, pdu->fd_head,
463                                     fit, update_col_infop, tree);
464 }
465
466 guint32 stream_get_frag_length( const stream_pdu_fragment_t *frag)
467 {
468     DISSECTOR_ASSERT( frag );
469     return frag->len;
470 }
471
472 fragment_data *stream_get_frag_data( const stream_pdu_fragment_t *frag)
473 {
474     DISSECTOR_ASSERT( frag );
475     return frag->pdu->fd_head;
476 }
477
478 guint32 stream_get_pdu_no( const stream_pdu_fragment_t *frag)
479 {
480     DISSECTOR_ASSERT( frag );
481     return frag->pdu->pdu_number;
482 }