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