ISO14443: Fix Dead Store (Dead assignement/Dead increment) Warning found by Clang
[metze/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  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
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.
15  *
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.
20  *
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.
24  */
25
26 #include "config.h"
27
28 #include <glib.h>
29 #include <epan/packet.h>
30 #include <epan/reassemble.h>
31 #include <epan/stream.h>
32 #include <epan/tvbuff.h>
33
34
35 typedef struct {
36     fragment_head *fd_head;          /* the reassembled data, NULL
37                                       * until we add the last fragment */
38     guint32 pdu_number;              /* Number of this PDU within the stream */
39
40     /* id of this pdu (globally unique) */
41     guint32 id;
42 } stream_pdu_t;
43
44
45 struct stream_pdu_fragment
46 {
47     guint32 len;                     /* the length of this fragment */
48     stream_pdu_t *pdu;
49     gboolean final_fragment;
50 };
51
52 struct stream {
53     /* the key used to add this stream to stream_hash */
54     struct stream_key *key;
55
56     /* pdu to add the next fragment to, or NULL if we need to start
57      * a new PDU.
58      */
59     stream_pdu_t *current_pdu;
60
61     /* number of PDUs added to this stream so far */
62     guint32 pdu_counter;
63
64     /* the framenumber and offset of the last fragment added;
65        used for sanity-checking */
66     guint32 lastfrag_framenum;
67     guint32 lastfrag_offset;
68 };
69
70
71 /*****************************************************************************
72  *
73  * Stream hash
74  */
75
76 /* key */
77 typedef struct stream_key {
78     /* streams can be attached to circuits or conversations, and we note
79        that here */
80     gboolean is_circuit;
81     union {
82         const struct circuit *circuit;
83         const struct conversation *conv;
84     } circ;
85     int p2p_dir;
86 } stream_key_t;
87
88
89 /* hash func */
90 static guint stream_hash_func(gconstpointer k)
91 {
92     const stream_key_t *key = (const stream_key_t *)k;
93
94     /* is_circuit is redundant to the circuit/conversation pointer */
95     return (GPOINTER_TO_UINT(key->circ.circuit)) ^ key->p2p_dir;
96 }
97
98 /* compare func */
99 static gboolean stream_compare_func(gconstpointer a,
100                              gconstpointer b)
101 {
102     const stream_key_t *key1 = (const stream_key_t *)a;
103     const stream_key_t *key2 = (const stream_key_t *)b;
104     if( key1 -> p2p_dir != key2 -> p2p_dir ||
105         key1-> is_circuit != key2 -> is_circuit )
106         return FALSE;
107
108     if( key1 -> is_circuit )
109         return (key1 -> circ.circuit == key2 -> circ.circuit );
110     else
111         return (key1 -> circ.conv == key2 -> circ.conv );
112 }
113
114 /* the hash table */
115 static GHashTable *stream_hash;
116
117
118 /* cleanup reset function, call from stream_cleanup() */
119 static void cleanup_stream_hash( void ) {
120     if( stream_hash != NULL ) {
121         g_hash_table_destroy( stream_hash );
122         stream_hash = NULL;
123     }
124 }
125
126 /* init function, call from stream_init() */
127 static void init_stream_hash( void ) {
128     g_assert(stream_hash==NULL);
129     stream_hash = g_hash_table_new(stream_hash_func,
130                                    stream_compare_func);
131 }
132
133 /* lookup function, returns null if not found */
134 static stream_t *stream_hash_lookup_circ( const struct circuit *circuit, int p2p_dir )
135 {
136     stream_key_t key;
137     key.is_circuit=TRUE;
138     key.circ.circuit=circuit;
139     key.p2p_dir=p2p_dir;
140     return (stream_t *)g_hash_table_lookup(stream_hash, &key);
141 }
142
143 static stream_t *stream_hash_lookup_conv( const struct conversation *conv, int p2p_dir )
144 {
145     stream_key_t key;
146     key.is_circuit=FALSE;
147     key.circ.conv = conv;
148     key.p2p_dir=p2p_dir;
149     return (stream_t *)g_hash_table_lookup(stream_hash, &key);
150 }
151
152
153 static stream_t *new_stream( stream_key_t *key )
154 {
155     stream_t *val;
156
157     val = wmem_new(wmem_file_scope(), stream_t);
158     val -> key = key;
159     val -> pdu_counter = 0;
160     val -> current_pdu = NULL;
161     val -> lastfrag_framenum = 0;
162     val -> lastfrag_offset = 0;
163     g_hash_table_insert(stream_hash, key, val);
164
165     return val;
166 }
167
168
169 /* insert function */
170 static stream_t *stream_hash_insert_circ( const struct circuit *circuit, int p2p_dir )
171 {
172     stream_key_t *key;
173
174     key = wmem_new(wmem_file_scope(), stream_key_t);
175     key->is_circuit = TRUE;
176     key->circ.circuit = circuit;
177     key->p2p_dir = p2p_dir;
178
179     return new_stream(key);
180 }
181
182 static stream_t *stream_hash_insert_conv( const struct conversation *conv, int p2p_dir )
183 {
184     stream_key_t *key;
185
186     key = wmem_new(wmem_file_scope(), stream_key_t);
187     key->is_circuit = FALSE;
188     key->circ.conv = conv;
189     key->p2p_dir = p2p_dir;
190
191     return new_stream(key);
192 }
193
194
195 /******************************************************************************
196  *
197  * PDU data
198  */
199
200 /* pdu counter, for generating unique pdu ids */
201 static guint32 pdu_counter;
202
203 static void stream_cleanup_pdu_data(void)
204 {
205 }
206
207 static void stream_init_pdu_data(void)
208 {
209     pdu_counter = 0;
210 }
211
212
213 /* new pdu in this stream */
214 static stream_pdu_t *stream_new_pdu(stream_t *stream)
215 {
216     stream_pdu_t *pdu;
217     pdu = wmem_new(wmem_file_scope(), stream_pdu_t);
218     pdu -> fd_head = NULL;
219     pdu -> pdu_number = stream -> pdu_counter++;
220     pdu -> id = pdu_counter++;
221     return pdu;
222 }
223
224 /*****************************************************************************
225  *
226  * fragment hash
227  */
228
229 /* key */
230 typedef struct fragment_key {
231     const stream_t *stream;
232     guint32 framenum;
233     guint32 offset;
234 } fragment_key_t;
235
236
237 /* hash func */
238 static guint fragment_hash_func(gconstpointer k)
239 {
240     const fragment_key_t *key = (const fragment_key_t *)k;
241     return (GPOINTER_TO_UINT(key->stream)) + ((guint)key -> framenum) + ((guint)key->offset);
242 }
243
244 /* compare func */
245 static gboolean fragment_compare_func(gconstpointer a,
246                                gconstpointer b)
247 {
248     const fragment_key_t *key1 = (const fragment_key_t *)a;
249     const fragment_key_t *key2 = (const fragment_key_t *)b;
250     return (key1 -> stream == key2 -> stream &&
251             key1 -> framenum == key2 -> framenum &&
252             key1 -> offset == key2 -> offset );
253 }
254
255 /* the hash table */
256 static GHashTable *fragment_hash;
257
258
259 /* cleanup function, call from stream_cleanup() */
260 static void cleanup_fragment_hash( void ) {
261     if( fragment_hash != NULL ) {
262         g_hash_table_destroy( fragment_hash );
263         fragment_hash = NULL;
264     }
265 }
266
267 /* init function, call from stream_init() */
268 static void init_fragment_hash( void ) {
269     g_assert(fragment_hash==NULL);
270     fragment_hash = g_hash_table_new(fragment_hash_func,
271                                      fragment_compare_func);
272 }
273
274
275 /* lookup function, returns null if not found */
276 static stream_pdu_fragment_t *fragment_hash_lookup( const stream_t *stream, guint32 framenum, guint32 offset )
277 {
278     fragment_key_t key;
279     stream_pdu_fragment_t *val;
280
281     key.stream = stream;
282     key.framenum = framenum;
283     key.offset = offset;
284     val = (stream_pdu_fragment_t *)g_hash_table_lookup(fragment_hash, &key);
285
286     return val;
287 }
288
289
290 /* insert function */
291 static stream_pdu_fragment_t *fragment_hash_insert( const stream_t *stream, guint32 framenum, guint32 offset,
292                                                     guint32 length)
293 {
294     fragment_key_t *key;
295     stream_pdu_fragment_t *val;
296
297     key = wmem_new(wmem_file_scope(), fragment_key_t);
298     key->stream = stream;
299     key->framenum = framenum;
300     key->offset = offset;
301
302     val = wmem_new(wmem_file_scope(), stream_pdu_fragment_t);
303     val->len = length;
304     val->pdu = NULL;
305     val->final_fragment = FALSE;
306
307     g_hash_table_insert(fragment_hash, key, val);
308     return val;
309 }
310
311 /*****************************************************************************/
312
313 /* reassembly table */
314 static reassembly_table stream_reassembly_table;
315
316 /* Initialise a new stream. Call this when you first identify a distinct
317  * stream. */
318 stream_t *stream_new_circ ( const struct circuit *circuit, int p2p_dir )
319 {
320     stream_t * stream;
321
322     /* we don't want to replace the previous data if we get called twice on the
323        same circuit, so do a lookup first */
324     stream = stream_hash_lookup_circ(circuit, p2p_dir);
325     DISSECTOR_ASSERT( stream == NULL );
326
327     stream = stream_hash_insert_circ(circuit, p2p_dir);
328
329     return stream;
330 }
331
332 stream_t *stream_new_conv ( const struct conversation *conv, int p2p_dir )
333 {
334     stream_t * stream;
335
336     /* we don't want to replace the previous data if we get called twice on the
337        same conversation, so do a lookup first */
338     stream = stream_hash_lookup_conv(conv, p2p_dir);
339     DISSECTOR_ASSERT( stream == NULL );
340
341     stream = stream_hash_insert_conv(conv, p2p_dir);
342     return stream;
343 }
344
345
346 /* retrieve a previously-created stream.
347  *
348  * Returns null if no matching stream was found.
349  */
350 stream_t *find_stream_circ ( const struct circuit *circuit, int p2p_dir )
351 {
352     return stream_hash_lookup_circ(circuit,p2p_dir);
353 }
354 stream_t *find_stream_conv ( const struct conversation *conv, int p2p_dir )
355 {
356     return stream_hash_lookup_conv(conv,p2p_dir);
357 }
358
359 /* cleanup the stream routines */
360 /* Note: stream_cleanup must only be called when seasonal memory
361  *       is also freed since the hash tables countain pointers to
362  *       wmem_file_scoped memory.
363  */
364 void stream_cleanup( void )
365 {
366     cleanup_stream_hash();
367     cleanup_fragment_hash();
368     stream_cleanup_pdu_data();
369 }
370
371 /* initialise the stream routines */
372 void stream_init( void )
373 {
374     init_stream_hash();
375     init_fragment_hash();
376     stream_init_pdu_data();
377
378     reassembly_table_init(&stream_reassembly_table,
379                           &addresses_reassembly_table_functions);
380 }
381
382 /*****************************************************************************/
383
384 stream_pdu_fragment_t *stream_find_frag( stream_t *stream, guint32 framenum, guint32 offset )
385 {
386     return fragment_hash_lookup( stream, framenum, offset );
387 }
388
389 stream_pdu_fragment_t *stream_add_frag( stream_t *stream, guint32 framenum, guint32 offset,
390                                         tvbuff_t *tvb, packet_info *pinfo, gboolean more_frags )
391 {
392     fragment_head *fd_head;
393     stream_pdu_t *pdu;
394     stream_pdu_fragment_t *frag_data;
395
396     DISSECTOR_ASSERT(stream);
397
398     /* check that this fragment is at the end of the stream */
399     DISSECTOR_ASSERT( framenum > stream->lastfrag_framenum ||
400                       (framenum == stream->lastfrag_framenum && offset > stream->lastfrag_offset));
401
402
403     pdu = stream->current_pdu;
404     if( pdu == NULL ) {
405         /* start a new pdu */
406         pdu = stream->current_pdu = stream_new_pdu(stream);
407     }
408
409     /* add it to the reassembly tables */
410     fd_head = fragment_add_seq_next(&stream_reassembly_table,
411                                     tvb, 0, pinfo, pdu->id, NULL,
412                                     tvb_reported_length(tvb), more_frags);
413     /* add it to our hash */
414     frag_data = fragment_hash_insert( stream, framenum, offset, tvb_reported_length(tvb));
415     frag_data -> pdu = pdu;
416
417     if( fd_head != NULL ) {
418         /* if this was the last fragment, update the pdu data.
419          */
420         pdu -> fd_head = fd_head;
421
422         /* start a new pdu next time */
423         stream->current_pdu = NULL;
424
425         frag_data -> final_fragment = TRUE;
426     }
427
428     /* stashing the framenum and offset permit future sanity checks */
429     stream -> lastfrag_framenum = framenum;
430     stream -> lastfrag_offset = offset;
431
432     return frag_data;
433 }
434
435
436 tvbuff_t *stream_process_reassembled(
437     tvbuff_t *tvb, int offset, packet_info *pinfo,
438     const char *name, const stream_pdu_fragment_t *frag,
439     const struct _fragment_items *fit,
440     gboolean *update_col_infop, proto_tree *tree)
441 {
442     stream_pdu_t *pdu;
443     DISSECTOR_ASSERT(frag);
444     pdu = frag->pdu;
445
446     /* we handle non-terminal fragments ourselves, because
447        reassemble.c messes them up */
448     if(!frag->final_fragment) {
449         if (pdu->fd_head != NULL && fit->hf_reassembled_in != NULL) {
450             proto_tree_add_uint(tree,
451                                 *(fit->hf_reassembled_in), tvb,
452                                 0, 0, pdu->fd_head->reassembled_in);
453         }
454         return NULL;
455     }
456
457     return process_reassembled_data(tvb, offset, pinfo, name, pdu->fd_head,
458                                     fit, update_col_infop, tree);
459 }
460
461 guint32 stream_get_frag_length( const stream_pdu_fragment_t *frag)
462 {
463     DISSECTOR_ASSERT( frag );
464     return frag->len;
465 }
466
467 fragment_head *stream_get_frag_data( const stream_pdu_fragment_t *frag)
468 {
469     DISSECTOR_ASSERT( frag );
470     return frag->pdu->fd_head;
471 }
472
473 guint32 stream_get_pdu_no( const stream_pdu_fragment_t *frag)
474 {
475     DISSECTOR_ASSERT( frag );
476     return frag->pdu->pdu_number;
477 }
478
479 /*
480  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
481  *
482  * Local variables:
483  * c-basic-offset: 4
484  * tab-width: 8
485  * indent-tabs-mode: nil
486  * End:
487  *
488  * vi: set shiftwidth=4 tabstop=8 expandtab:
489  * :indentSize=4:tabSize=8:noTabs=true:
490  */