Replace tabs by spaces when editor modelines has "expandtab"
[metze/wireshark/wip.git] / epan / conversation_table.c
1 /* conversations_table.c
2  * conversations_table   2003 Ronnie Sahlberg
3  * Helper routines common to all endpoint conversations tap.
4  *
5  * Wireshark - Network traffic analyzer
6  * By Gerald Combs <gerald@wireshark.org>
7  * Copyright 1998 Gerald Combs
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23
24 #include "config.h"
25
26 #include <string.h>
27
28 #include "packet_info.h"
29 #include "proto.h"
30 #include "conversation_table.h"
31 #include "addr_resolv.h"
32
33 #include "stat_tap_ui.h"
34
35 GList *cmd_string_list_ = NULL;
36
37 struct register_ct {
38     gboolean hide_ports;       /* hide TCP / UDP port columns */
39     int proto_id;              /* protocol id (0-indexed) */
40     tap_packet_cb conv_func;   /* function to be called for new incoming packets for conversation*/
41     tap_packet_cb host_func;   /* function to be called for new incoming packets for hostlist */
42     conv_gui_init_cb conv_gui_init; /* GUI specific function to initialize conversation */
43     host_gui_init_cb host_gui_init; /* GUI specific function to initialize hostlist */
44 };
45
46 gboolean get_conversation_hide_ports(register_ct_t* ct)
47 {
48     return ct->hide_ports;
49 }
50
51 int get_conversation_proto_id(register_ct_t* ct)
52 {
53     if (!ct) {
54         return -1;
55     }
56     return ct->proto_id;
57 }
58
59 tap_packet_cb get_conversation_packet_func(register_ct_t* ct)
60 {
61     return ct->conv_func;
62 }
63
64 tap_packet_cb get_hostlist_packet_func(register_ct_t* ct)
65 {
66     return ct->host_func;
67 }
68
69 static GSList *registered_ct_tables = NULL;
70
71 void
72 dissector_conversation_init(const char *opt_arg, void* userdata)
73 {
74     register_ct_t *table = (register_ct_t*)userdata;
75     GString *cmd_str = g_string_new("conv,");
76     const char *filter=NULL;
77
78     g_string_append(cmd_str, proto_get_protocol_filter_name(table->proto_id));
79     if(!strncmp(opt_arg, cmd_str->str, cmd_str->len)){
80         if (opt_arg[cmd_str->len] == ',') {
81             filter = opt_arg + cmd_str->len + 1;
82         }
83     }
84     g_string_free(cmd_str, TRUE);
85
86     if (table->conv_gui_init)
87         table->conv_gui_init(table, filter);
88 }
89
90 void
91 dissector_hostlist_init(const char *opt_arg, void* userdata)
92 {
93     register_ct_t *table = (register_ct_t*)userdata;
94     GString *cmd_str = g_string_new("");
95     const char *filter=NULL;
96
97     g_string_printf(cmd_str, "%s,%s,", HOSTLIST_TAP_PREFIX, proto_get_protocol_filter_name(table->proto_id));
98     if(!strncmp(opt_arg, cmd_str->str, cmd_str->len)){
99         if (opt_arg[cmd_str->len] == ',') {
100             filter = opt_arg + cmd_str->len + 1;
101         }
102     } else {
103         filter=NULL;
104     }
105
106     g_string_free(cmd_str, TRUE);
107
108     if (table->host_gui_init)
109         table->host_gui_init(table, filter);
110 }
111 /** get conversation from protocol ID
112  *
113  * @param proto_id protocol ID
114  * @return tap function handler of conversation
115  */
116 register_ct_t* get_conversation_by_proto_id(int proto_id)
117 {
118     GSList *ct;
119     register_ct_t *table;
120
121     for(ct = registered_ct_tables; ct != NULL; ct = g_slist_next(ct)){
122         table = (register_ct_t*)ct->data;
123         if ((table) && (table->proto_id == proto_id))
124             return table;
125     }
126
127     return NULL;
128 }
129
130 static gint
131 insert_sorted_by_table_name(gconstpointer aparam, gconstpointer bparam)
132 {
133     const register_ct_t *a = (register_ct_t *)aparam;
134     const register_ct_t *b = (register_ct_t *)bparam;
135
136     return g_ascii_strcasecmp(proto_get_protocol_short_name(find_protocol_by_id(a->proto_id)), proto_get_protocol_short_name(find_protocol_by_id(b->proto_id)));
137 }
138
139 void
140 register_conversation_table(const int proto_id, gboolean hide_ports, tap_packet_cb conv_packet_func, tap_packet_cb hostlist_func)
141 {
142     register_ct_t *table;
143
144     table = g_new(register_ct_t,1);
145
146     table->hide_ports    = hide_ports;
147     table->proto_id      = proto_id;
148     table->conv_func     = conv_packet_func;
149     table->host_func     = hostlist_func;
150     table->conv_gui_init = NULL;
151     table->host_gui_init = NULL;
152
153     registered_ct_tables = g_slist_insert_sorted(registered_ct_tables, table, insert_sorted_by_table_name);
154 }
155
156 /* Set GUI fields for register_ct list */
157 static void
158 set_conv_gui_data(gpointer data, gpointer user_data)
159 {
160     GString *conv_cmd_str = g_string_new("conv,");
161     stat_tap_ui ui_info;
162     register_ct_t *table = (register_ct_t*)data;
163
164     table->conv_gui_init = (conv_gui_init_cb)user_data;
165
166     g_string_append(conv_cmd_str, proto_get_protocol_filter_name(table->proto_id));
167     cmd_string_list_ = g_list_append(cmd_string_list_, conv_cmd_str->str);
168     ui_info.group = REGISTER_STAT_GROUP_CONVERSATION_LIST;
169     ui_info.title = NULL;   /* construct this from the protocol info? */
170     ui_info.cli_string = g_string_free(conv_cmd_str, FALSE);
171     ui_info.tap_init_cb = dissector_conversation_init;
172     ui_info.index = -1;
173     ui_info.nparams = 0;
174     ui_info.params = NULL;
175     register_stat_tap_ui(&ui_info, table);
176 }
177
178 void conversation_table_set_gui_info(conv_gui_init_cb init_cb)
179 {
180     g_slist_foreach(registered_ct_tables, set_conv_gui_data, init_cb);
181 }
182
183 static void
184 set_host_gui_data(gpointer data, gpointer user_data)
185 {
186     GString *host_cmd_str = g_string_new("");
187     stat_tap_ui ui_info;
188     register_ct_t *table = (register_ct_t*)data;
189
190     table->host_gui_init = (host_gui_init_cb)user_data;
191
192     g_string_printf(host_cmd_str, "%s,%s", HOSTLIST_TAP_PREFIX, proto_get_protocol_filter_name(table->proto_id));
193     ui_info.group = REGISTER_STAT_GROUP_ENDPOINT_LIST;
194     ui_info.title = NULL;   /* construct this from the protocol info? */
195     ui_info.cli_string = g_string_free(host_cmd_str, FALSE);
196     ui_info.tap_init_cb = dissector_hostlist_init;
197     ui_info.index = -1;
198     ui_info.nparams = 0;
199     ui_info.params = NULL;
200     register_stat_tap_ui(&ui_info, table);
201 }
202
203 void hostlist_table_set_gui_info(host_gui_init_cb init_cb)
204 {
205     g_slist_foreach(registered_ct_tables, set_host_gui_data, init_cb);
206 }
207
208 void conversation_table_iterate_tables(GFunc func, gpointer user_data)
209 {
210     g_slist_foreach(registered_ct_tables, func, user_data);
211 }
212
213 guint conversation_table_get_num(void)
214 {
215     return g_slist_length(registered_ct_tables);
216 }
217
218
219 register_ct_t *get_conversation_table_by_num(guint table_num)
220 {
221     return (register_ct_t *) g_slist_nth_data(registered_ct_tables, table_num);
222 }
223
224 /** Compute the hash value for two given address/port pairs.
225  * (Parameter type is gconstpointer for GHashTable compatibility.)
226  *
227  * @param v Conversation Key. MUST point to a conv_key_t struct.
228  * @return Computed key hash.
229  */
230 static guint
231 conversation_hash(gconstpointer v)
232 {
233     const conv_key_t *key = (const conv_key_t *)v;
234     guint hash_val;
235
236     hash_val = 0;
237     ADD_ADDRESS_TO_HASH(hash_val, &key->addr1);
238     hash_val += key->port1;
239     ADD_ADDRESS_TO_HASH(hash_val, &key->addr2);
240     hash_val += key->port2;
241     hash_val ^= key->conv_id;
242
243     return hash_val;
244 }
245
246 /** Compare two conversation keys for an exact match.
247  * (Parameter types are gconstpointer for GHashTable compatibility.)
248  *
249  * @param key1 First conversation. MUST point to a conv_key_t struct.
250  * @param key2 Second conversation. MUST point to a conv_key_t struct.
251  * @return TRUE if conversations are equal, FALSE otherwise.
252  */
253 static gboolean
254 conversation_equal(gconstpointer key1, gconstpointer key2)
255 {
256     const conv_key_t *ck1 = (const conv_key_t *)key1;
257     const conv_key_t *ck2 = (const conv_key_t *)key2;
258
259     if (ck1->conv_id == ck2->conv_id)
260     {
261         if (ck1->port1 == ck2->port1 &&
262             ck1->port2 == ck2->port2 &&
263             ADDRESSES_EQUAL(&ck1->addr1, &ck2->addr1) &&
264             ADDRESSES_EQUAL(&ck1->addr2, &ck2->addr2)) {
265             return TRUE;
266         }
267
268         if (ck1->port2 == ck2->port1 &&
269             ck1->port1 == ck2->port2 &&
270             ADDRESSES_EQUAL(&ck1->addr2, &ck2->addr1) &&
271             ADDRESSES_EQUAL(&ck1->addr1, &ck2->addr2)) {
272             return TRUE;
273         }
274     }
275
276     /*
277      * The addresses, ports, or conversation IDs don't match.
278      */
279     return FALSE;
280 }
281
282 void
283 reset_conversation_table_data(conv_hash_t *ch)
284 {
285     if (!ch) {
286         return;
287     }
288
289     if (ch->conv_array != NULL) {
290         guint i;
291         for(i = 0; i < ch->conv_array->len; i++){
292             conv_item_t *conv = &g_array_index(ch->conv_array, conv_item_t, i);
293             g_free((gpointer)conv->src_address.data);
294             g_free((gpointer)conv->dst_address.data);
295         }
296
297         g_array_free(ch->conv_array, TRUE);
298     }
299
300     if (ch->hashtable != NULL) {
301         g_hash_table_destroy(ch->hashtable);
302     }
303
304     ch->conv_array=NULL;
305     ch->hashtable=NULL;
306 }
307
308 void reset_hostlist_table_data(conv_hash_t *ch)
309 {
310     if (!ch) {
311         return;
312     }
313
314     if (ch->conv_array != NULL) {
315         guint i;
316         for(i = 0; i < ch->conv_array->len; i++){
317             hostlist_talker_t *host = &g_array_index(ch->conv_array, hostlist_talker_t, i);
318             g_free((gpointer)host->myaddress.data);
319         }
320
321         g_array_free(ch->conv_array, TRUE);
322     }
323
324     if (ch->hashtable != NULL) {
325         g_hash_table_destroy(ch->hashtable);
326     }
327
328     ch->conv_array=NULL;
329     ch->hashtable=NULL;
330 }
331
332 const char *get_conversation_address(wmem_allocator_t *allocator, address *addr, gboolean resolve_names)
333 {
334     if (resolve_names) {
335         return address_to_display(allocator, addr);
336     } else {
337         return address_to_str(allocator, addr);
338     }
339 }
340
341 const char *get_conversation_port(wmem_allocator_t *allocator, guint32 port, port_type ptype, gboolean resolve_names)
342 {
343
344     if(!resolve_names) ptype = PT_NONE;
345
346     switch(ptype) {
347     case(PT_TCP):
348         return tcp_port_to_display(allocator, port);
349     case(PT_UDP):
350         return udp_port_to_display(allocator, port);
351     case(PT_SCTP):
352         return sctp_port_to_display(allocator, port);
353     default:
354         return wmem_strdup_printf(allocator, "%d", port);
355     }
356 }
357
358 /* given an address (to distinguish between ipv4 and ipv6 for tcp/udp),
359    a port_type and a name_type (FN_...)
360    return a string for the filter name.
361
362    Some addresses, like AT_ETHER may actually be any of multiple types
363    of protocols,   either ethernet, tokenring, fddi, wlan etc so we must be
364    more specific there;  that's why we need specific_addr_type.
365 */
366 static const char *
367 conversation_get_filter_name(conv_item_t *conv_item, conv_filter_type_e filter_type)
368 {
369
370     if ((conv_item == NULL) || (conv_item->dissector_info == NULL) || (conv_item->dissector_info->get_filter_type == NULL)) {
371         return CONV_FILTER_INVALID;
372     }
373
374     return conv_item->dissector_info->get_filter_type(conv_item, filter_type);
375 }
376
377 static const char *
378 hostlist_get_filter_name(hostlist_talker_t *host, conv_filter_type_e filter_type)
379 {
380
381     if ((host == NULL) || (host->dissector_info == NULL) || (host->dissector_info->get_filter_type == NULL)) {
382         return CONV_FILTER_INVALID;
383     }
384
385     return host->dissector_info->get_filter_type(host, filter_type);
386 }
387
388 /* Convert a port number into a string or NULL */
389 static char *
390 ct_port_to_str(port_type ptype, guint32 port)
391 {
392     switch(ptype){
393     case PT_TCP:
394     case PT_UDP:
395     case PT_SCTP:
396     case PT_NCP:
397         return g_strdup_printf("%d", port);
398     default:
399         break;
400     }
401     return NULL;
402 }
403
404 const char *get_conversation_filter(conv_item_t *conv_item, conv_direction_e direction)
405 {
406     char *sport, *dport, *src_addr, *dst_addr;
407     const char *str = "INVALID";
408
409     sport = ct_port_to_str(conv_item->ptype, conv_item->src_port);
410     dport = ct_port_to_str(conv_item->ptype, conv_item->dst_port);
411     src_addr = address_to_str(NULL, &conv_item->src_address);
412     dst_addr = address_to_str(NULL, &conv_item->dst_address);
413
414     if (conv_item->src_address.type == AT_STRINGZ || conv_item->src_address.type == AT_USB) {
415         char *new_addr;
416
417         new_addr = wmem_strdup_printf(NULL, "\"%s\"", src_addr);
418         wmem_free(NULL, src_addr);
419         src_addr = new_addr;
420     }
421     if (conv_item->dst_address.type == AT_STRINGZ || conv_item->dst_address.type == AT_USB) {
422         char *new_addr;
423
424         new_addr = wmem_strdup_printf(NULL, "\"%s\"", dst_addr);
425         wmem_free(NULL, dst_addr);
426         dst_addr = new_addr;
427     }
428
429     switch(direction){
430     case CONV_DIR_A_TO_FROM_B:
431         /* A <-> B */
432         str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s && %s==%s%s%s%s%s",
433                               conversation_get_filter_name(conv_item,  CONV_FT_ANY_ADDRESS),
434                               src_addr,
435                               sport?" && ":"",
436                               sport?conversation_get_filter_name(conv_item,  CONV_FT_ANY_PORT):"",
437                               sport?"==":"",
438                               sport?sport:"",
439                               conversation_get_filter_name(conv_item,  CONV_FT_ANY_ADDRESS),
440                               dst_addr,
441                               dport?" && ":"",
442                               dport?conversation_get_filter_name(conv_item,  CONV_FT_ANY_PORT):"",
443                               dport?"==":"",
444                               dport?dport:""
445             );
446         break;
447     case CONV_DIR_A_TO_B:
448         /* A --> B */
449         str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s && %s==%s%s%s%s%s",
450                               conversation_get_filter_name(conv_item,  CONV_FT_SRC_ADDRESS),
451                               src_addr,
452                               sport?" && ":"",
453                               sport?conversation_get_filter_name(conv_item,  CONV_FT_SRC_PORT):"",
454                               sport?"==":"",
455                               sport?sport:"",
456                               conversation_get_filter_name(conv_item,  CONV_FT_DST_ADDRESS),
457                               dst_addr,
458                               dport?" && ":"",
459                               dport?conversation_get_filter_name(conv_item,  CONV_FT_DST_PORT):"",
460                               dport?"==":"",
461                               dport?dport:""
462             );
463         break;
464     case CONV_DIR_A_FROM_B:
465         /* A <-- B */
466         str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s && %s==%s%s%s%s%s",
467                               conversation_get_filter_name(conv_item,  CONV_FT_DST_ADDRESS),
468                               src_addr,
469                               sport?" && ":"",
470                               sport?conversation_get_filter_name(conv_item,  CONV_FT_DST_PORT):"",
471                               sport?"==":"",
472                               sport?sport:"",
473                               conversation_get_filter_name(conv_item,  CONV_FT_SRC_ADDRESS),
474                               dst_addr,
475                               dport?" && ":"",
476                               dport?conversation_get_filter_name(conv_item,  CONV_FT_SRC_PORT):"",
477                               dport?"==":"",
478                               dport?dport:""
479             );
480         break;
481     case CONV_DIR_A_TO_FROM_ANY:
482         /* A <-> ANY */
483         str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s",
484                               conversation_get_filter_name(conv_item,  CONV_FT_ANY_ADDRESS),
485                               src_addr,
486                               sport?" && ":"",
487                               sport?conversation_get_filter_name(conv_item,  CONV_FT_ANY_PORT):"",
488                               sport?"==":"",
489                               sport?sport:""
490             );
491         break;
492     case CONV_DIR_A_TO_ANY:
493         /* A --> ANY */
494         str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s",
495                               conversation_get_filter_name(conv_item,  CONV_FT_SRC_ADDRESS),
496                               src_addr,
497                               sport?" && ":"",
498                               sport?conversation_get_filter_name(conv_item,  CONV_FT_SRC_PORT):"",
499                               sport?"==":"",
500                               sport?sport:""
501             );
502         break;
503     case CONV_DIR_A_FROM_ANY:
504         /* A <-- ANY */
505         str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s",
506                               conversation_get_filter_name(conv_item,  CONV_FT_DST_ADDRESS),
507                               src_addr,
508                               sport?" && ":"",
509                               sport?conversation_get_filter_name(conv_item,  CONV_FT_DST_PORT):"",
510                               sport?"==":"",
511                               sport?sport:""
512             );
513         break;
514     case CONV_DIR_ANY_TO_FROM_B:
515         /* ANY <-> B */
516         str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s",
517                               conversation_get_filter_name(conv_item,  CONV_FT_ANY_ADDRESS),
518                               dst_addr,
519                               dport?" && ":"",
520                               dport?conversation_get_filter_name(conv_item,  CONV_FT_ANY_PORT):"",
521                               dport?"==":"",
522                               dport?dport:""
523             );
524         break;
525     case CONV_DIR_ANY_FROM_B:
526         /* ANY <-- B */
527         str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s",
528                               conversation_get_filter_name(conv_item,  CONV_FT_SRC_ADDRESS),
529                               dst_addr,
530                               dport?" && ":"",
531                               dport?conversation_get_filter_name(conv_item,  CONV_FT_SRC_PORT):"",
532                               dport?"==":"",
533                               dport?dport:""
534             );
535         break;
536     case CONV_DIR_ANY_TO_B:
537         /* ANY --> B */
538         str = wmem_strdup_printf(NULL, "%s==%s%s%s%s%s",
539                               conversation_get_filter_name(conv_item,  CONV_FT_DST_ADDRESS),
540                               dst_addr,
541                               dport?" && ":"",
542                               dport?conversation_get_filter_name(conv_item,  CONV_FT_DST_PORT):"",
543                               dport?"==":"",
544                               dport?dport:""
545             );
546         break;
547     default:
548         break;
549     }
550     g_free(sport);
551     g_free(dport);
552     wmem_free(NULL, src_addr);
553     wmem_free(NULL, dst_addr);
554     return str;
555 }
556
557 const char *get_hostlist_filter(hostlist_talker_t *host)
558 {
559     char *sport, *src_addr;
560     const char *str;
561
562     sport=ct_port_to_str(host->ptype, host->port);
563     src_addr = address_to_str(NULL, &host->myaddress);
564     if (host->myaddress.type == AT_STRINGZ || host->myaddress.type == AT_USB) {
565         char *new_addr;
566
567         new_addr = wmem_strdup_printf(NULL, "\"%s\"", src_addr);
568         wmem_free(NULL, src_addr);
569         src_addr = new_addr;
570     }
571
572     str = g_strdup_printf("%s==%s%s%s%s%s",
573                           hostlist_get_filter_name(host, CONV_FT_ANY_ADDRESS),
574                           src_addr,
575                           sport?" && ":"",
576                           sport?hostlist_get_filter_name(host, CONV_FT_ANY_PORT):"",
577                           sport?"==":"",
578                           sport?sport:"");
579
580     g_free(sport);
581     wmem_free(NULL, src_addr);
582     return str;
583 }
584
585 void
586 add_conversation_table_data(conv_hash_t *ch, const address *src, const address *dst, guint32 src_port, guint32 dst_port, int num_frames, int num_bytes,
587         nstime_t *ts, nstime_t *abs_ts, ct_dissector_info_t *ct_info, port_type ptype)
588 {
589     add_conversation_table_data_with_conv_id(ch, src, dst, src_port, dst_port, CONV_ID_UNSET, num_frames, num_bytes, ts, abs_ts, ct_info, ptype);
590 }
591
592 void
593 add_conversation_table_data_with_conv_id(
594     conv_hash_t *ch,
595     const address *src,
596     const address *dst,
597     guint32 src_port,
598     guint32 dst_port,
599     conv_id_t conv_id,
600     int num_frames,
601     int num_bytes,
602     nstime_t *ts,
603     nstime_t *abs_ts,
604     ct_dissector_info_t *ct_info,
605     port_type ptype)
606 {
607     const address *addr1, *addr2;
608     guint32 port1, port2;
609     conv_item_t *conv_item = NULL;
610     unsigned int conversation_idx = 0;
611
612     if (src_port > dst_port) {
613         addr1 = src;
614         addr2 = dst;
615         port1 = src_port;
616         port2 = dst_port;
617     } else if (src_port < dst_port) {
618         addr2 = src;
619         addr1 = dst;
620         port2 = src_port;
621         port1 = dst_port;
622     } else if (CMP_ADDRESS(src, dst) < 0) {
623         addr1 = src;
624         addr2 = dst;
625         port1 = src_port;
626         port2 = dst_port;
627     } else {
628         addr2 = src;
629         addr1 = dst;
630         port2 = src_port;
631         port1 = dst_port;
632     }
633
634     /* if we don't have any entries at all yet */
635     if (ch->conv_array == NULL) {
636         ch->conv_array = g_array_sized_new(FALSE, FALSE, sizeof(conv_item_t), 10000);
637
638         ch->hashtable = g_hash_table_new_full(conversation_hash,
639                                               conversation_equal, /* key_equal_func */
640                                               g_free,             /* key_destroy_func */
641                                               NULL);              /* value_destroy_func */
642
643     } else {
644         /* try to find it among the existing known conversations */
645         conv_key_t existing_key;
646         gpointer conversation_idx_hash_val;
647
648         existing_key.addr1 = *addr1;
649         existing_key.addr2 = *addr2;
650         existing_key.port1 = port1;
651         existing_key.port2 = port2;
652         existing_key.conv_id = conv_id;
653         if (g_hash_table_lookup_extended(ch->hashtable, &existing_key, NULL, &conversation_idx_hash_val)) {
654             conv_item = &g_array_index(ch->conv_array, conv_item_t, GPOINTER_TO_UINT(conversation_idx_hash_val));
655         }
656     }
657
658     /* if we still don't know what conversation this is it has to be a new one
659        and we have to allocate it and append it to the end of the list */
660     if (conv_item == NULL) {
661         conv_key_t *new_key;
662         conv_item_t new_conv_item;
663
664         COPY_ADDRESS(&new_conv_item.src_address, addr1);
665         COPY_ADDRESS(&new_conv_item.dst_address, addr2);
666         new_conv_item.dissector_info = ct_info;
667         new_conv_item.ptype = ptype;
668         new_conv_item.src_port = port1;
669         new_conv_item.dst_port = port2;
670         new_conv_item.conv_id = conv_id;
671         new_conv_item.rx_frames = 0;
672         new_conv_item.tx_frames = 0;
673         new_conv_item.rx_bytes = 0;
674         new_conv_item.tx_bytes = 0;
675         new_conv_item.modified = TRUE;
676
677         if (ts) {
678             memcpy(&new_conv_item.start_time, ts, sizeof(new_conv_item.start_time));
679             memcpy(&new_conv_item.stop_time, ts, sizeof(new_conv_item.stop_time));
680             memcpy(&new_conv_item.start_abs_time, abs_ts, sizeof(new_conv_item.start_abs_time));
681         } else {
682             nstime_set_unset(&new_conv_item.start_abs_time);
683             nstime_set_unset(&new_conv_item.start_time);
684             nstime_set_unset(&new_conv_item.stop_time);
685         }
686         g_array_append_val(ch->conv_array, new_conv_item);
687         conversation_idx = ch->conv_array->len - 1;
688         conv_item = &g_array_index(ch->conv_array, conv_item_t, conversation_idx);
689
690         /* ct->conversations address is not a constant but src/dst_address.data are */
691         new_key = g_new(conv_key_t, 1);
692         SET_ADDRESS(&new_key->addr1, conv_item->src_address.type, conv_item->src_address.len, conv_item->src_address.data);
693         SET_ADDRESS(&new_key->addr2, conv_item->dst_address.type, conv_item->dst_address.len, conv_item->dst_address.data);
694         new_key->port1 = port1;
695         new_key->port2 = port2;
696         new_key->conv_id = conv_id;
697         g_hash_table_insert(ch->hashtable, new_key, GUINT_TO_POINTER(conversation_idx));
698     }
699
700     /* update the conversation struct */
701     conv_item->modified = TRUE;
702     if ( (!CMP_ADDRESS(src, addr1)) && (!CMP_ADDRESS(dst, addr2)) && (src_port==port1) && (dst_port==port2) ) {
703         conv_item->tx_frames += num_frames;
704         conv_item->tx_bytes += num_bytes;
705     } else {
706         conv_item->rx_frames += num_frames;
707         conv_item->rx_bytes += num_bytes;
708     }
709
710     if (ts) {
711         if (nstime_cmp(ts, &conv_item->stop_time) > 0) {
712             memcpy(&conv_item->stop_time, ts, sizeof(conv_item->stop_time));
713         } else if (nstime_cmp(ts, &conv_item->start_time) < 0) {
714             memcpy(&conv_item->start_time, ts, sizeof(conv_item->start_time));
715             memcpy(&conv_item->start_abs_time, abs_ts, sizeof(conv_item->start_abs_time));
716         }
717     }
718 }
719
720 /*
721  * Compute the hash value for a given address/port pairs if the match
722  * is to be exact.
723  */
724 static guint
725 host_hash(gconstpointer v)
726 {
727     const host_key_t *key = (const host_key_t *)v;
728     guint hash_val;
729
730     hash_val = 0;
731     ADD_ADDRESS_TO_HASH(hash_val, &key->myaddress);
732     hash_val += key->port;
733     return hash_val;
734 }
735
736 /*
737  * Compare two host keys for an exact match.
738  */
739 static gint
740 host_match(gconstpointer v, gconstpointer w)
741 {
742     const host_key_t *v1 = (const host_key_t *)v;
743     const host_key_t *v2 = (const host_key_t *)w;
744
745     if (v1->port == v2->port &&
746         ADDRESSES_EQUAL(&v1->myaddress, &v2->myaddress)) {
747         return 1;
748     }
749     /*
750      * The addresses or the ports don't match.
751      */
752     return 0;
753 }
754
755 void
756 add_hostlist_table_data(conv_hash_t *ch, const address *addr, guint32 port, gboolean sender, int num_frames, int num_bytes, hostlist_dissector_info_t *host_info, port_type port_type_val)
757 {
758     hostlist_talker_t *talker=NULL;
759     int talker_idx=0;
760
761     /* XXX should be optimized to allocate n extra entries at a time
762        instead of just one */
763     /* if we don't have any entries at all yet */
764     if(ch->conv_array==NULL){
765         ch->conv_array=g_array_sized_new(FALSE, FALSE, sizeof(hostlist_talker_t), 10000);
766         ch->hashtable = g_hash_table_new_full(host_hash,
767                                               host_match, /* key_equal_func */
768                                               g_free,     /* key_destroy_func */
769                                               NULL);      /* value_destroy_func */
770     }
771     else {
772         /* try to find it among the existing known conversations */
773         host_key_t existing_key;
774         gpointer talker_idx_hash_val;
775
776         existing_key.myaddress = *addr;
777         existing_key.port = port;
778
779         if (g_hash_table_lookup_extended(ch->hashtable, &existing_key, NULL, &talker_idx_hash_val)) {
780             talker = &g_array_index(ch->conv_array, hostlist_talker_t, GPOINTER_TO_UINT(talker_idx_hash_val));
781         }
782     }
783
784     /* if we still don't know what talker this is it has to be a new one
785        and we have to allocate it and append it to the end of the list */
786     if(talker==NULL){
787         host_key_t *new_key;
788         hostlist_talker_t host;
789
790         COPY_ADDRESS(&host.myaddress, addr);
791         host.dissector_info = host_info;
792         host.ptype=port_type_val;
793         host.port=port;
794         host.rx_frames=0;
795         host.tx_frames=0;
796         host.rx_bytes=0;
797         host.tx_bytes=0;
798         host.modified = TRUE;
799
800         g_array_append_val(ch->conv_array, host);
801         talker_idx= ch->conv_array->len - 1;
802         talker=&g_array_index(ch->conv_array, hostlist_talker_t, talker_idx);
803
804         /* hl->hosts address is not a constant but address.data is */
805         new_key = g_new(host_key_t,1);
806         SET_ADDRESS(&new_key->myaddress, talker->myaddress.type, talker->myaddress.len, talker->myaddress.data);
807         new_key->port = port;
808         g_hash_table_insert(ch->hashtable, new_key, GUINT_TO_POINTER(talker_idx));
809     }
810
811     /* if this is a new talker we need to initialize the struct */
812     talker->modified = TRUE;
813
814     /* update the talker struct */
815     if( sender ){
816         talker->tx_frames+=num_frames;
817         talker->tx_bytes+=num_bytes;
818     } else {
819         talker->rx_frames+=num_frames;
820         talker->rx_bytes+=num_bytes;
821     }
822 }
823
824 /*
825  * Editor modelines
826  *
827  * Local Variables:
828  * c-basic-offset: 4
829  * tab-width: 8
830  * indent-tabs-mode: nil
831  * End:
832  *
833  * ex: set shiftwidth=4 tabstop=8 expandtab:
834  * :indentSize=4:tabSize=8:noTabs=true:
835  */