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