from Frederic Peters: bring debian package generation .deb up to date
[obnox/wireshark/wip.git] / epan / circuit.c
1 /* circuit.c
2  * Routines for building lists of packets that are part of a "circuit"
3  *
4  * $Id$
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <stdio.h>
30
31 #include <string.h>
32 #include <glib.h>
33 #include "packet.h"
34 #include "circuit.h"
35
36 /*
37  * Hash table for circuits.
38  */
39 static GHashTable *circuit_hashtable = NULL;
40
41 static GMemChunk *circuit_key_chunk = NULL;
42 static GMemChunk *circuit_chunk = NULL;
43
44 static guint32 new_index;
45
46 static int circuit_init_count = 200;
47
48 /*
49  * Protocol-specific data attached to a circuit_t structure - protocol
50  * index and opaque pointer.
51  */
52 typedef struct _circuit_proto_data {
53         int     proto;
54         void    *proto_data;
55 } circuit_proto_data;
56
57 static GMemChunk *circuit_proto_data_area = NULL;
58
59 /*
60  * Compute the hash value for a circuit.
61  */
62 static guint
63 circuit_hash(gconstpointer v)
64 {
65         const circuit_key *key = (const circuit_key *)v;
66
67         return key->ctype ^ key->circuit_id;
68 }
69
70 /*
71  * Compare two circuit keys.
72  */
73 static gint
74 circuit_match(gconstpointer v, gconstpointer w)
75 {
76         const circuit_key *v1 = (const circuit_key *)v;
77         const circuit_key *v2 = (const circuit_key *)w;
78
79         return v1->ctype == v2->ctype && v1->circuit_id == v2->circuit_id;
80 }
81
82 /*
83  * Initialize some variables every time a file is loaded or re-loaded.
84  * Destroy all existing circuits, and create a new hash table
85  * for the circuits in the new file.
86  */
87 void
88 circuit_init(void)
89 {
90         if (circuit_hashtable != NULL)
91                 g_hash_table_destroy(circuit_hashtable);
92         if (circuit_key_chunk != NULL)
93                 g_mem_chunk_destroy(circuit_key_chunk);
94         if (circuit_chunk != NULL)
95                 g_mem_chunk_destroy(circuit_chunk);
96
97         /*
98          * Free up any space allocated for circuit protocol data
99          * areas.
100          *
101          * We can free the space, as the structures it contains are
102          * pointed to by circuit data structures that were freed
103          * above.
104          */
105         if (circuit_proto_data_area != NULL)
106                 g_mem_chunk_destroy(circuit_proto_data_area);
107
108         circuit_hashtable = g_hash_table_new(circuit_hash, circuit_match);
109         circuit_key_chunk = g_mem_chunk_new("circuit_key_chunk",
110             sizeof(circuit_key),
111             circuit_init_count * sizeof(struct circuit_key),
112             G_ALLOC_AND_FREE);
113         circuit_chunk = g_mem_chunk_new("circuit_chunk",
114             sizeof(circuit_t),
115             circuit_init_count * sizeof(circuit_t),
116             G_ALLOC_AND_FREE);
117
118         /*
119          * Allocate a new area for circuit protocol data items.
120          */
121         circuit_proto_data_area = g_mem_chunk_new("circuit_proto_data_area",
122             sizeof(circuit_proto_data), 20 * sizeof(circuit_proto_data), /* FIXME*/
123             G_ALLOC_ONLY);
124
125         /*
126          * Start the circuit indices over at 0.
127          */
128         new_index = 0;
129 }
130
131 /*
132  * Given a circuit type and circuit ID for a packet, create a new circuit
133  * to contain packets for that circuit.
134  */
135 circuit_t *
136 circuit_new(circuit_type ctype, guint32 circuit_id, guint32 first_frame)
137 {
138         circuit_t *circuit, *old_circuit;
139         circuit_key *new_key;
140
141         new_key = g_mem_chunk_alloc(circuit_key_chunk);
142         new_key->ctype = ctype;
143         new_key->circuit_id = circuit_id;
144
145         circuit = g_mem_chunk_alloc(circuit_chunk);
146         circuit->next = NULL;
147         circuit->first_frame = first_frame;
148         circuit->last_frame = 0;        /* not known yet */
149         circuit->index = new_index;
150         circuit->data_list = NULL;
151         circuit->dissector_handle = NULL;
152         circuit->key_ptr = new_key;
153
154         new_index++;
155
156         /*
157          * Is there already a circuit with this circuit ID?
158          */
159         old_circuit = g_hash_table_lookup(circuit_hashtable, new_key);
160         if (old_circuit != NULL) {
161                 /*
162                  * Yes.  Find the last circuit in the list of circuits
163                  * with this circuit ID, and if its last frame isn't
164                  * known, make it be the previous frame to this one.
165                  */
166                 while (old_circuit->next != NULL)
167                         old_circuit = old_circuit->next;
168                 if (old_circuit->last_frame == 0)
169                         old_circuit->last_frame = first_frame - 1;
170
171                 /*
172                  * Now put the new circuit after the last one in the
173                  * list.
174                  */
175                 old_circuit->next = circuit;
176         } else {
177                 /*
178                  * No.  This is the first one with this circuit ID; add
179                  * it to the hash table.
180                  */
181                 g_hash_table_insert(circuit_hashtable, new_key, circuit);
182         }
183
184         return circuit;
185 }
186
187 /*
188  * Given a circuit type and ID, and a frame number, search for a circuit with
189  * that type and ID whose range of frames includes that frame number.
190  * Returns NULL if not found.
191  */
192 circuit_t *
193 find_circuit(circuit_type ctype, guint32 circuit_id, guint32 frame)
194 {
195         circuit_key key;
196         circuit_t *circuit;
197
198         key.ctype = ctype;
199         key.circuit_id = circuit_id;
200
201         /*
202          * OK, search the list of circuits with that type and ID for
203          * a circuit whose range of frames includes that frame number.
204          */
205         for (circuit = g_hash_table_lookup(circuit_hashtable, &key);
206             circuit != NULL; circuit = circuit->next) {
207                 /*
208                  * The circuit includes that frame number if:
209                  *
210                  *      the circuit's first frame is unknown or is at or
211                  *      before that frame
212                  *
213                  * and
214                  *
215                  *      the circuit's last frame is unknown or is at or
216                  *      after that frame.
217                  */
218                 if ((circuit->first_frame == 0 || circuit->first_frame <= frame)
219                     && (circuit->last_frame == 0 || circuit->last_frame >= frame))
220                         break;
221         }
222         return circuit;
223 }
224
225 /*
226  * Set the last frame of a circuit, if it's not already known,
227  * "closing" the circuit.
228  */
229 void
230 close_circuit(circuit_t *circuit, guint32 last_frame)
231 {
232         if (circuit->last_frame == 0)
233                 circuit->last_frame = last_frame;
234 }
235
236 static gint
237 p_compare(gconstpointer a, gconstpointer b)
238 {
239         const circuit_proto_data *ap = (const circuit_proto_data *)a;
240         const circuit_proto_data *bp = (const circuit_proto_data *)b;
241
242         if (ap->proto > bp->proto)
243                 return 1;
244         else if (ap->proto == bp->proto)
245                 return 0;
246         else
247                 return -1;
248 }
249
250 void
251 circuit_add_proto_data(circuit_t *conv, int proto, void *proto_data)
252 {
253         circuit_proto_data *p1 = g_mem_chunk_alloc(circuit_proto_data_area);
254
255         p1->proto = proto;
256         p1->proto_data = proto_data;
257
258         /* Add it to the list of items for this circuit. */
259
260         conv->data_list = g_slist_insert_sorted(conv->data_list, (gpointer *)p1,
261             p_compare);
262 }
263
264 void *
265 circuit_get_proto_data(circuit_t *conv, int proto)
266 {
267         circuit_proto_data temp, *p1;
268         GSList *item;
269
270         temp.proto = proto;
271         temp.proto_data = NULL;
272
273         item = g_slist_find_custom(conv->data_list, (gpointer *)&temp,
274             p_compare);
275
276         if (item != NULL) {
277                 p1 = (circuit_proto_data *)item->data;
278                 return p1->proto_data;
279         }
280
281         return NULL;
282 }
283
284 void
285 circuit_delete_proto_data(circuit_t *conv, int proto)
286 {
287         circuit_proto_data temp;
288         GSList *item;
289
290         temp.proto = proto;
291         temp.proto_data = NULL;
292
293         item = g_slist_find_custom(conv->data_list, (gpointer *)&temp,
294             p_compare);
295
296         if (item != NULL)
297                 conv->data_list = g_slist_remove(conv->data_list, item);
298 }
299
300 void
301 circuit_set_dissector(circuit_t *circuit, dissector_handle_t handle)
302 {
303         circuit->dissector_handle = handle;
304 }
305
306 dissector_handle_t
307 circuit_get_dissector(circuit_t *circuit)
308 {
309         return circuit->dissector_handle;
310 }
311
312 /*
313  * Given a circuit type and ID for a packet, search for a matching
314  * circuit and, if found and it has a circuit dissector,
315  * call that dissector and return TRUE, otherwise return FALSE.
316  */
317 gboolean
318 try_circuit_dissector(circuit_type ctype, guint32 circuit_id, guint32 frame,
319     tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
320 {
321         circuit_t *circuit;
322
323         circuit = find_circuit(ctype, circuit_id, frame);
324
325         if (circuit != NULL) {
326                 if (circuit->dissector_handle == NULL)
327                         return FALSE;
328                 call_dissector(circuit->dissector_handle, tvb, pinfo,
329                     tree);
330                 return TRUE;
331         }
332         return FALSE;
333 }