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