Don't set the focus on the display filter entry when we're passed a
[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 #include "emem.h"
38
39 /* number of streams to allocate memory for at once */
40 #define MEMCHUNK_STREAM_COUNT 20
41
42 /* ditto pdus */
43 #define MEMCHUNK_PDU_COUNT 100
44
45 /* ditto fragments */
46 #define MEMCHUNK_FRAGMENT_COUNT 100
47
48
49 typedef struct {
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 */
53
54     /* id of this pdu (globally unique) */
55     guint32 id;
56 } stream_pdu_t;
57
58
59 struct stream_pdu_fragment
60 {
61     guint32 len;                     /* the length of this fragment */
62     stream_pdu_t *pdu;
63     gboolean final_fragment;
64 };
65
66 struct stream {
67     /* the key used to add this stream to stream_hash */
68     struct stream_key *key;
69
70     /* pdu to add the next fragment to, or NULL if we need to start
71      * a new PDU.
72      */
73     stream_pdu_t *current_pdu;
74
75     /* number of PDUs added to this stream so far */
76     guint32 pdu_counter;
77
78     /* the framenumber and offset of the last fragment added;
79        used for sanity-checking */
80     guint32 lastfrag_framenum;
81     guint32 lastfrag_offset;
82 };
83
84
85 /*****************************************************************************
86  *
87  * Stream hash
88  */
89
90 /* key */
91 typedef struct stream_key {
92     /* streams can be attached to circuits or conversations, and we note
93        that here */
94     gboolean is_circuit;
95     union {
96         const struct circuit *circuit;
97         const struct conversation *conv;
98     } circ;
99     int p2p_dir;
100 } stream_key_t;
101
102
103 /* hash func */
104 guint stream_hash_func(gconstpointer k)
105 {
106     const stream_key_t *key = (const stream_key_t *)k;
107
108     /* is_circuit is redundant to the circuit/conversation pointer */
109     return ((guint)key->circ.circuit) ^ key->p2p_dir;
110 }
111
112 /* compare func */
113 gboolean stream_compare_func(gconstpointer a,
114                              gconstpointer b)
115 {
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 )
120         return FALSE;
121     
122     if( key1 -> is_circuit )
123         return (key1 -> circ.circuit == key2 -> circ.circuit );
124     else
125         return (key1 -> circ.conv == key2 -> circ.conv );
126 }
127
128 /* memory pools */
129 static GMemChunk *stream_keys = NULL;
130 static GMemChunk *streams = NULL;
131
132
133 /* the hash table */
134 static GHashTable *stream_hash;
135
136
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 );
141         stream_hash = NULL;
142     }
143
144     if( stream_keys != NULL ) {
145         g_mem_chunk_destroy( stream_keys );
146         stream_keys = NULL;
147     }
148
149     if( streams != NULL ) {
150         g_mem_chunk_destroy( streams );
151         streams = NULL;
152     }
153
154     streams = g_mem_chunk_create(stream_t,
155                                  MEMCHUNK_STREAM_COUNT,
156                                  G_ALLOC_ONLY);
157
158     stream_keys = g_mem_chunk_create(stream_key_t,
159                                      MEMCHUNK_STREAM_COUNT,
160                                      G_ALLOC_ONLY);
161
162     stream_hash = g_hash_table_new(stream_hash_func,
163                                    stream_compare_func);
164 }
165
166
167 /* lookup function, returns null if not found */
168 static stream_t *stream_hash_lookup_circ( const struct circuit *circuit, int p2p_dir )
169 {
170     stream_key_t key;
171     key.is_circuit=TRUE;
172     key.circ.circuit=circuit;
173     key.p2p_dir=p2p_dir;
174     return (stream_t *)g_hash_table_lookup(stream_hash, &key);
175 }
176
177 static stream_t *stream_hash_lookup_conv( const struct conversation *conv, int p2p_dir )
178 {
179     stream_key_t key;
180     key.is_circuit=FALSE;
181     key.circ.conv = conv;
182     key.p2p_dir=p2p_dir;
183     return (stream_t *)g_hash_table_lookup(stream_hash, &key);
184 }
185
186
187 static stream_t *new_stream( stream_key_t *key )
188 {
189     stream_t *val;
190     
191     val = g_mem_chunk_alloc(streams);
192     val -> key = key;
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);
198
199     return val;
200 }
201
202
203 /* insert function */
204 static stream_t *stream_hash_insert_circ( const struct circuit *circuit, int p2p_dir )
205 {
206     stream_key_t *key;
207
208     key = g_mem_chunk_alloc(stream_keys);
209     key->is_circuit = TRUE;
210     key->circ.circuit = circuit;
211     key->p2p_dir = p2p_dir;
212
213     return new_stream(key);
214 }
215
216 static stream_t *stream_hash_insert_conv( const struct conversation *conv, int p2p_dir )
217 {
218     stream_key_t *key;
219
220     key = g_mem_chunk_alloc(stream_keys);
221     key->is_circuit = FALSE;
222     key->circ.conv = conv;
223     key->p2p_dir = p2p_dir;
224
225     return new_stream(key);
226 }
227
228
229 /******************************************************************************
230  *
231  * PDU data
232  */
233 static GMemChunk *pdus = NULL;
234
235 /* pdu counter, for generating unique pdu ids */
236 static guint32 pdu_counter;
237
238
239 static void stream_init_pdu_data(void)
240 {
241     if( pdus != NULL ) {
242         g_mem_chunk_destroy( pdus );
243         pdus = NULL;
244     }
245
246     pdus = g_mem_chunk_create(stream_pdu_t,
247                               MEMCHUNK_PDU_COUNT,
248                               G_ALLOC_ONLY);
249     pdu_counter = 0;
250 }
251
252
253 /* new pdu in this stream */
254 static stream_pdu_t *stream_new_pdu(stream_t *stream)
255 {
256     stream_pdu_t *pdu;
257     pdu = g_mem_chunk_alloc(pdus);
258     pdu -> fd_head = NULL;
259     pdu -> pdu_number = stream -> pdu_counter++;
260     pdu -> id = pdu_counter++;
261     return pdu;
262 }
263
264 /*****************************************************************************
265  *
266  * fragment hash
267  */
268
269 /* key */
270 typedef struct fragment_key {
271     const stream_t *stream;
272     guint32 framenum;
273     guint32 offset;
274 } fragment_key_t;
275
276
277 /* hash func */
278 guint fragment_hash_func(gconstpointer k)
279 {
280     const fragment_key_t *key = (const fragment_key_t *)k;
281     return ((guint)key->stream) + ((guint)key -> framenum) + ((guint)key->offset);
282 }
283
284 /* compare func */
285 gboolean fragment_compare_func(gconstpointer a,
286                                gconstpointer b)
287 {
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 );
293 }
294             
295 /* memory pools */
296 static GMemChunk *fragment_keys = NULL;
297 static GMemChunk *fragment_vals = NULL;
298
299 /* the hash table */
300 static GHashTable *fragment_hash;
301
302
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;
308     }
309
310     if( fragment_vals != NULL ) {
311         g_mem_chunk_destroy( fragment_vals );
312         fragment_vals = NULL;
313     }
314
315     if( fragment_keys != NULL ) {
316         g_mem_chunk_destroy( fragment_keys );
317         fragment_keys = NULL;
318     }
319
320     fragment_keys = g_mem_chunk_create(fragment_key_t,
321                                        MEMCHUNK_FRAGMENT_COUNT,
322                                        G_ALLOC_ONLY);
323     
324     fragment_vals = g_mem_chunk_create(stream_pdu_fragment_t,
325                                        MEMCHUNK_FRAGMENT_COUNT,
326                                        G_ALLOC_ONLY);
327
328     fragment_hash = g_hash_table_new(fragment_hash_func,
329                                      fragment_compare_func);
330 }
331
332
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 )
335 {
336     fragment_key_t key;
337     stream_pdu_fragment_t *val;
338
339     key.stream = stream;
340     key.framenum = framenum;
341     key.offset = offset;
342     val = g_hash_table_lookup(fragment_hash, &key);
343
344     return val;
345 }
346
347
348 /* insert function */
349 static stream_pdu_fragment_t *fragment_hash_insert( const stream_t *stream, guint32 framenum, guint32 offset,
350                                                     guint32 length)
351 {
352     fragment_key_t *key;
353     stream_pdu_fragment_t *val;
354
355     key = g_mem_chunk_alloc(fragment_keys);
356     key->stream = stream;
357     key->framenum = framenum;
358     key->offset = offset;
359
360     val = g_mem_chunk_alloc(fragment_vals);
361     val->len = length;
362     val->pdu = NULL;
363     val->final_fragment = FALSE;
364
365     g_hash_table_insert(fragment_hash, key, val);
366     return val;
367 }
368
369 /*****************************************************************************/
370
371 /* fragmentation hash tables */
372 static GHashTable *stream_fragment_table = NULL;
373 static GHashTable *stream_reassembled_table = NULL;
374
375 /* Initialise a new stream. Call this when you first identify a distinct
376  * stream. */
377 stream_t *stream_new_circ ( const struct circuit *circuit, int p2p_dir )
378 {
379     stream_t * stream;
380
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 );
385
386     stream = stream_hash_insert_circ(circuit, p2p_dir);
387     
388     return stream;
389 }
390
391 stream_t *stream_new_conv ( const struct conversation *conv, int p2p_dir )
392 {
393     stream_t * stream;
394
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 );
399
400     stream = stream_hash_insert_conv(conv, p2p_dir);
401     return stream;
402 }
403
404
405
406
407 /* retrieve a previously-created stream.
408  *
409  * Returns null if no matching stream was found.
410  */
411 stream_t *find_stream_circ ( const struct circuit *circuit, int p2p_dir )
412 {
413     return stream_hash_lookup_circ(circuit,p2p_dir);
414 }
415 stream_t *find_stream_conv ( const struct conversation *conv, int p2p_dir )
416 {
417     return stream_hash_lookup_conv(conv,p2p_dir);
418 }
419
420
421 /* initialise the stream routines */
422 void stream_init( void )
423 {
424     init_stream_hash();
425     init_fragment_hash();
426     stream_init_pdu_data();
427
428     fragment_table_init(&stream_fragment_table);
429     reassembled_table_init(&stream_reassembled_table);
430 }
431
432
433
434
435 /*****************************************************************************/
436
437 stream_pdu_fragment_t *stream_find_frag( stream_t *stream, guint32 framenum, guint32 offset )
438 {
439     return fragment_hash_lookup( stream, framenum, offset );
440 }
441     
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 )
444 {
445     fragment_data *fd_head;
446     stream_pdu_t *pdu;
447     stream_pdu_fragment_t *frag_data;
448
449     DISSECTOR_ASSERT(stream);
450
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));
454
455
456     pdu = stream->current_pdu;
457     if( pdu == NULL ) {
458         /* start a new pdu */
459         pdu = stream->current_pdu = stream_new_pdu(stream);
460     }
461         
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;
469
470     if( fd_head != NULL ) {
471         /* if this was the last fragment, update the pdu data.
472          */
473         pdu -> fd_head = fd_head;
474         
475         /* start a new pdu next time */
476         stream->current_pdu = NULL;
477
478         frag_data -> final_fragment = TRUE;
479     }
480
481     /* stashing the framenum and offset permit future sanity checks */
482     stream -> lastfrag_framenum = framenum;
483     stream -> lastfrag_offset = offset;
484
485     return frag_data;
486 }
487
488
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)
494 {
495     stream_pdu_t *pdu;
496     DISSECTOR_ASSERT(frag);
497     pdu = frag->pdu;
498
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);
506         }
507         return NULL;
508     }
509
510     return process_reassembled_data(tvb, offset, pinfo, name, pdu->fd_head,
511                                     fit, update_col_infop, tree);
512 }
513     
514 guint32 stream_get_frag_length( const stream_pdu_fragment_t *frag)
515 {
516     DISSECTOR_ASSERT( frag );
517     return frag->len;
518 }
519
520 fragment_data *stream_get_frag_data( const stream_pdu_fragment_t *frag)
521 {
522     DISSECTOR_ASSERT( frag );
523     return frag->pdu->fd_head;
524 }
525
526 guint32 stream_get_pdu_no( const stream_pdu_fragment_t *frag)
527 {
528     DISSECTOR_ASSERT( frag );
529     return frag->pdu->pdu_number;
530 }