include packet-cdt.h to declare prototypes
[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  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
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 #include "emem.h"
36
37 /*
38  * Hash table for circuits.
39  */
40 static GHashTable *circuit_hashtable = NULL;
41
42 static guint32 new_index;
43
44 /*
45  * Protocol-specific data attached to a circuit_t structure - protocol
46  * index and opaque pointer.
47  */
48 typedef struct _circuit_proto_data {
49         int     proto;
50         void    *proto_data;
51 } circuit_proto_data;
52
53 /*
54  * Compute the hash value for a circuit.
55  */
56 static guint
57 circuit_hash(gconstpointer v)
58 {
59         const circuit_key *key = (const circuit_key *)v;
60
61         return key->ctype ^ key->circuit_id;
62 }
63
64 /*
65  * Compare two circuit keys.
66  */
67 static gint
68 circuit_match(gconstpointer v, gconstpointer w)
69 {
70         const circuit_key *v1 = (const circuit_key *)v;
71         const circuit_key *v2 = (const circuit_key *)w;
72
73         return v1->ctype == v2->ctype && v1->circuit_id == v2->circuit_id;
74 }
75
76 /*
77  * Initialize some variables every time a file is loaded or re-loaded.
78  * Destroy all existing circuits, and create a new hash table
79  * for the circuits in the new file.
80  */
81 void
82 circuit_init(void)
83 {
84         if (circuit_hashtable != NULL)
85                 g_hash_table_destroy(circuit_hashtable);
86
87         /*
88          * Free up any space allocated for circuit protocol data
89          * areas.
90          *
91          * We can free the space, as the structures it contains are
92          * pointed to by circuit data structures that were freed
93          * above.
94          */
95
96         circuit_hashtable = g_hash_table_new(circuit_hash, circuit_match);
97
98         /*
99          * Start the circuit indices over at 0.
100          */
101         new_index = 0;
102 }
103
104 /*
105  * Given a circuit type and circuit ID for a packet, create a new circuit
106  * to contain packets for that circuit.
107  */
108 circuit_t *
109 circuit_new(circuit_type ctype, guint32 circuit_id, guint32 first_frame)
110 {
111         circuit_t *circuit, *old_circuit;
112         circuit_key *new_key;
113
114         new_key = se_alloc(sizeof(struct circuit_key));
115         new_key->ctype = ctype;
116         new_key->circuit_id = circuit_id;
117
118         circuit = se_alloc(sizeof(circuit_t));
119         circuit->next = NULL;
120         circuit->first_frame = first_frame;
121         circuit->last_frame = 0;        /* not known yet */
122         circuit->index = new_index;
123         circuit->data_list = NULL;
124         circuit->dissector_handle = NULL;
125         circuit->key_ptr = new_key;
126
127         new_index++;
128
129         /*
130          * Is there already a circuit with this circuit ID?
131          */
132         old_circuit = g_hash_table_lookup(circuit_hashtable, new_key);
133         if (old_circuit != NULL) {
134                 /*
135                  * Yes.  Find the last circuit in the list of circuits
136                  * with this circuit ID, and if its last frame isn't
137                  * known, make it be the previous frame to this one.
138                  */
139                 while (old_circuit->next != NULL)
140                         old_circuit = old_circuit->next;
141                 if (old_circuit->last_frame == 0)
142                         old_circuit->last_frame = first_frame - 1;
143
144                 /*
145                  * Now put the new circuit after the last one in the
146                  * list.
147                  */
148                 old_circuit->next = circuit;
149         } else {
150                 /*
151                  * No.  This is the first one with this circuit ID; add
152                  * it to the hash table.
153                  */
154                 g_hash_table_insert(circuit_hashtable, new_key, circuit);
155         }
156
157         return circuit;
158 }
159
160 /*
161  * Given a circuit type and ID, and a frame number, search for a circuit with
162  * that type and ID whose range of frames includes that frame number.
163  * Returns NULL if not found.
164  */
165 circuit_t *
166 find_circuit(circuit_type ctype, guint32 circuit_id, guint32 frame)
167 {
168         circuit_key key;
169         circuit_t *circuit;
170
171         key.ctype = ctype;
172         key.circuit_id = circuit_id;
173
174         /*
175          * OK, search the list of circuits with that type and ID for
176          * a circuit whose range of frames includes that frame number.
177          */
178         for (circuit = g_hash_table_lookup(circuit_hashtable, &key);
179             circuit != NULL; circuit = circuit->next) {
180                 /*
181                  * The circuit includes that frame number if:
182                  *
183                  *      the circuit's first frame is unknown or is at or
184                  *      before that frame
185                  *
186                  * and
187                  *
188                  *      the circuit's last frame is unknown or is at or
189                  *      after that frame.
190                  */
191                 if ((circuit->first_frame == 0 || circuit->first_frame <= frame)
192                     && (circuit->last_frame == 0 || circuit->last_frame >= frame))
193                         break;
194         }
195         return circuit;
196 }
197
198 /*
199  * Set the last frame of a circuit, if it's not already known,
200  * "closing" the circuit.
201  */
202 void
203 close_circuit(circuit_t *circuit, guint32 last_frame)
204 {
205         if (circuit->last_frame == 0)
206                 circuit->last_frame = last_frame;
207 }
208
209 static gint
210 p_compare(gconstpointer a, gconstpointer b)
211 {
212         const circuit_proto_data *ap = (const circuit_proto_data *)a;
213         const circuit_proto_data *bp = (const circuit_proto_data *)b;
214
215         if (ap->proto > bp->proto)
216                 return 1;
217         else if (ap->proto == bp->proto)
218                 return 0;
219         else
220                 return -1;
221 }
222
223 void
224 circuit_add_proto_data(circuit_t *conv, int proto, void *proto_data)
225 {
226         circuit_proto_data *p1 = se_alloc(sizeof(circuit_proto_data));
227
228         p1->proto = proto;
229         p1->proto_data = proto_data;
230
231         /* Add it to the list of items for this circuit. */
232
233         conv->data_list = g_slist_insert_sorted(conv->data_list, (gpointer *)p1,
234             p_compare);
235 }
236
237 void *
238 circuit_get_proto_data(circuit_t *conv, int proto)
239 {
240         circuit_proto_data temp, *p1;
241         GSList *item;
242
243         temp.proto = proto;
244         temp.proto_data = NULL;
245
246         item = g_slist_find_custom(conv->data_list, (gpointer *)&temp,
247             p_compare);
248
249         if (item != NULL) {
250                 p1 = (circuit_proto_data *)item->data;
251                 return p1->proto_data;
252         }
253
254         return NULL;
255 }
256
257 void
258 circuit_delete_proto_data(circuit_t *conv, int proto)
259 {
260         circuit_proto_data temp;
261         GSList *item;
262
263         temp.proto = proto;
264         temp.proto_data = NULL;
265
266         item = g_slist_find_custom(conv->data_list, (gpointer *)&temp,
267             p_compare);
268
269         if (item != NULL)
270                 conv->data_list = g_slist_remove(conv->data_list, item);
271 }
272
273 void
274 circuit_set_dissector(circuit_t *circuit, dissector_handle_t handle)
275 {
276         circuit->dissector_handle = handle;
277 }
278
279 dissector_handle_t
280 circuit_get_dissector(circuit_t *circuit)
281 {
282         return circuit->dissector_handle;
283 }
284
285 /*
286  * Given a circuit type and ID for a packet, search for a matching
287  * circuit and, if found and it has a circuit dissector,
288  * call that dissector and return TRUE, otherwise return FALSE.
289  */
290 gboolean
291 try_circuit_dissector(circuit_type ctype, guint32 circuit_id, guint32 frame,
292     tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
293 {
294         circuit_t *circuit;
295
296         circuit = find_circuit(ctype, circuit_id, frame);
297
298         if (circuit != NULL) {
299                 if (circuit->dissector_handle == NULL)
300                         return FALSE;
301                 call_dissector(circuit->dissector_handle, tvb, pinfo,
302                     tree);
303                 return TRUE;
304         }
305         return FALSE;
306 }