Squelch a complaint from Visual C++ 6.0 (the code was OK beforehand, at
[obnox/wireshark/wip.git] / conversation.c
1 /* conversation.c
2  * Routines for building lists of packets that are part of a "conversation"
3  *
4  * $Id: conversation.c,v 1.6 2000/01/05 21:48:16 gram Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
8  * Copyright 1998 Gerald Combs
9  *
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <stdio.h>
31
32 #ifdef HAVE_SYS_TYPES_H
33 # include <sys/types.h>
34 #endif
35
36 #ifdef HAVE_NETINET_IN_H
37 # include <netinet/in.h>
38 #endif
39
40 #include <string.h>
41 #include <glib.h>
42 #include "packet.h"
43 #include "conversation.h"
44
45 static GHashTable *conversation_hashtable = NULL;
46 static GMemChunk *conversation_key_chunk = NULL;
47 static GMemChunk *conversation_chunk = NULL;
48
49 typedef struct conversation_key {
50         struct conversation_key *next;
51         address src;
52         address dst;
53         port_type ptype;
54         guint32 port_src;
55         guint32 port_dst;
56 } conversation_key;
57
58 /*
59  * Linked list of conversation keys, so we can, before freeing them all,
60  * free the address data allocations associated with them.
61  */
62 static conversation_key *conversation_keys;
63
64 static guint32 new_index;
65
66 static int conversation_init_count = 200;
67
68 /*
69  * Compare two conversation keys.
70  */
71 static gint
72 conversation_equal(gconstpointer v, gconstpointer w)
73 {
74         conversation_key *v1 = (conversation_key *)v;
75         conversation_key *v2 = (conversation_key *)w;
76
77         if (v1->ptype != v2->ptype)
78                 return 0;       /* different types of port */
79
80         /*
81          * Are the first and second source ports the same, the first and
82          * second destination ports the same, the first and second source
83          * addresses the same, and the first and second destination
84          * addresses the same?
85          */
86         if (v1->port_src == v2->port_src &&
87             v1->port_dst == v2->port_dst &&
88             v1->src.type == v2->src.type &&
89             v1->src.len == v2->src.len &&
90             memcmp(v1->src.data, v2->src.data, v1->src.len) == 0 &&
91             v1->dst.type == v2->dst.type &&
92             v1->dst.len == v2->dst.len &&
93             memcmp(v1->dst.data, v2->dst.data, v1->dst.len) == 0) {
94                 /*
95                  * Yes.  It's the same conversation, and the two
96                  * address/port pairs are going in the same direction.
97                  */
98                 return 1;
99         }
100
101         /*
102          * Is the first source port the same as the second destination
103          * port, the first destination port the same as the first
104          * source port, the first source address the same as the second
105          * destination address, and the first destination address the
106          * same as the second source address?
107          */
108         if (v1->port_src == v2->port_dst &&
109             v1->port_dst == v2->port_src &&
110             v1->src.type == v2->dst.type &&
111             v1->src.len == v2->dst.len &&
112             memcmp(v1->src.data, v2->dst.data, v1->src.len) == 0 &&
113             v1->dst.type == v2->src.type &&
114             v1->dst.len == v2->src.len &&
115             memcmp(v1->dst.data, v2->src.data, v1->dst.len) == 0) {
116                 /*
117                  * Yes.  It's the same conversation, and the two
118                  * address/port pairs are going in opposite directions.
119                  */
120                 return 1;
121         }
122
123         /*
124          * The addresses or the ports don't match.
125          */     
126         return 0;
127 }
128
129 /*
130  * Compute the hash value for a given set of source and destination
131  * addresses and ports.
132  */
133 static guint 
134 conversation_hash(gconstpointer v)
135 {
136         conversation_key *key = (conversation_key *)v;
137         guint hash_val;
138         int i;
139
140         hash_val = 0;
141         for (i = 0; i < key->src.len; i++)
142                 hash_val += key->src.data[i];
143         for (i = 0; i < key->dst.len; i++)
144                 hash_val += key->dst.data[i];
145         hash_val += key->port_src + key->port_dst;
146
147         return hash_val;
148 }
149
150 /*
151  * Initialize some variables every time a file is loaded or re-loaded.
152  * Destroy all existing conversations, and create a new hash table
153  * for the conversations in the new file.
154  */
155 void
156 conversation_init(void)
157 {
158         conversation_key *key;
159
160         /*
161          * Free the addresses associated with the conversation keys.
162          */
163         for (key = conversation_keys; key != NULL; key = key->next) {
164                 /*
165                  * Grr.  I guess the theory here is that freeing
166                  * something sure as heck modifies it, so you
167                  * want to ban attempts to free it, but, alas,
168                  * if we make the "data" field of an "address"
169                  * structure not a "const", the compiler whines if
170                  * we try to make it point into the data for a packet,
171                  * as that's a "const" array (and should be, as dissectors
172                  * shouldn't trash it).
173                  *
174                  * So we cast the complaint into oblivion, and rely on
175                  * the fact that these addresses are known to have had
176                  * their data mallocated, i.e. they don't point into,
177                  * say, the middle of the data for a packet.
178                  */
179                 g_free((gpointer)key->src.data);
180                 g_free((gpointer)key->dst.data);
181         }
182         conversation_keys = NULL;
183         if (conversation_hashtable != NULL)
184                 g_hash_table_destroy(conversation_hashtable);
185         if (conversation_key_chunk != NULL)
186                 g_mem_chunk_destroy(conversation_key_chunk);
187         if (conversation_chunk != NULL)
188                 g_mem_chunk_destroy(conversation_chunk);
189
190         conversation_hashtable = g_hash_table_new(conversation_hash,
191             conversation_equal);
192         conversation_key_chunk = g_mem_chunk_new("conversation_key_chunk",
193             sizeof(conversation_key),
194             conversation_init_count * sizeof(struct conversation_key),
195             G_ALLOC_AND_FREE);
196         conversation_chunk = g_mem_chunk_new("conversation_chunk",
197             sizeof(conversation_t),
198             conversation_init_count * sizeof(conversation_t),
199             G_ALLOC_AND_FREE);
200
201         /*
202          * Start the conversation indices over at 0.
203          */
204         new_index = 0;
205 }
206
207 /*
208  * Copy an address, allocating a new buffer for the address data.
209  */
210 static void
211 copy_address(address *to, address *from)
212 {
213         guint8 *data;
214
215         to->type = from->type;
216         to->len = from->len;
217         data = g_malloc(from->len);
218         memcpy(data, from->data, from->len);
219         to->data = data;
220 }
221
222 /*
223  * Given source and destination addresses and ports for a packet,
224  * create a new conversation to contain packets between those address/port
225  * pairs.
226  */
227 conversation_t *
228 conversation_new(address *src, address *dst, port_type ptype,
229     guint32 src_port, guint32 dst_port, void *data)
230 {
231         conversation_t *conversation;
232         conversation_key *new_key;
233
234         new_key = g_mem_chunk_alloc(conversation_key_chunk);
235         new_key->next = conversation_keys;
236         conversation_keys = new_key;
237         copy_address(&new_key->src, src);
238         copy_address(&new_key->dst, dst);
239         new_key->ptype = ptype;
240         new_key->port_src = src_port;
241         new_key->port_dst = dst_port;
242
243         conversation = g_mem_chunk_alloc(conversation_chunk);
244         conversation->index = new_index;
245         conversation->data = data;
246         new_index++;
247
248         g_hash_table_insert(conversation_hashtable, new_key, conversation);
249         return conversation;
250 }
251
252 /*
253  * Given source and destination addresses and ports for a packet,
254  * search for a conversation containing packets between those address/port
255  * pairs.  Returns NULL if not found.
256  */
257 conversation_t *
258 find_conversation(address *src, address *dst, port_type ptype,
259     guint32 src_port, guint32 dst_port)
260 {
261         conversation_key key;
262
263         /*
264          * We don't make a copy of the address data, we just copy the
265          * pointer to it, as "key" disappears when we return.
266          */
267         key.src = *src;
268         key.dst = *dst;
269         key.ptype = ptype;
270         key.port_src = src_port;
271         key.port_dst = dst_port;
272         return g_hash_table_lookup(conversation_hashtable, &key);
273 }