Fix whitespace/indentation to match editor modelines.
[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_cmd_args.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 packet_func; /* function to be called for new incoming packets */
42     conv_gui_init_cb gui_init_cb; /* GUI specific function to initialize conversation */
43 };
44
45 gboolean get_conversation_hide_ports(register_ct_t* ct)
46 {
47     return ct->hide_ports;
48 }
49
50 int get_conversation_proto_id(register_ct_t* ct)
51 {
52     if (!ct) {
53         return -1;
54     }
55     return ct->proto_id;
56 }
57
58 tap_packet_cb get_conversation_packet_func(register_ct_t* ct)
59 {
60     return ct->packet_func;
61 }
62
63 static GSList *registered_ct_tables = NULL;
64
65 void
66 dissector_conversation_init(const char *opt_arg, void* userdata)
67 {
68     register_ct_t *table = (register_ct_t*)userdata;
69     GString *cmd_str = g_string_new("conv,");
70     const char *filter=NULL;
71
72     g_string_append(cmd_str, proto_get_protocol_filter_name(table->proto_id));
73     if(!strncmp(opt_arg, cmd_str->str, cmd_str->len)){
74         filter = opt_arg + cmd_str->len;
75     } else {
76         filter = NULL;
77     }
78     g_string_free(cmd_str, TRUE);
79
80     if (table->gui_init_cb)
81         table->gui_init_cb(table, filter);
82 }
83
84 /** get conversation from protocol ID
85  *
86  * @param proto_id protocol ID
87  * @return tap function handler of conversation
88  */
89 register_ct_t* get_conversation_by_proto_id(int proto_id)
90 {
91     GSList *ct;
92     register_ct_t *table;
93
94     for(ct = registered_ct_tables; ct != NULL; ct = g_slist_next(ct)){
95         table = (register_ct_t*)ct->data;
96         if ((table) && (table->proto_id == proto_id))
97             return table;
98     }
99
100     return NULL;
101 }
102
103 static gint
104 insert_sorted_by_table_name(gconstpointer aparam, gconstpointer bparam)
105 {
106     const register_ct_t *a = (register_ct_t *)aparam;
107     const register_ct_t *b = (register_ct_t *)bparam;
108
109     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)));
110 }
111
112 void
113 register_conversation_table(const int proto_id, gboolean hide_ports, tap_packet_cb packet_func)
114 {
115     register_ct_t *table;
116     GString *cmd_str = g_string_new("conv,");
117
118     table = g_new(register_ct_t,1);
119
120     table->hide_ports   = hide_ports;
121     table->proto_id     = proto_id;
122     table->packet_func  = packet_func;
123     table->gui_init_cb  = NULL;
124
125     registered_ct_tables = g_slist_insert_sorted(registered_ct_tables, table, insert_sorted_by_table_name);
126
127     g_string_append(cmd_str, proto_get_protocol_filter_name(table->proto_id));
128     cmd_string_list_ = g_list_append(cmd_string_list_, cmd_str->str);
129     register_stat_cmd_arg(cmd_str->str, dissector_conversation_init, table);
130     g_string_free(cmd_str, FALSE);
131 }
132
133 /* Set GUI fields for register_ct list */
134 static void
135 set_gui_data(gpointer data, gpointer user_data)
136 {
137     register_ct_t *table = (register_ct_t*)data;
138     table->gui_init_cb = (conv_gui_init_cb)user_data;
139 }
140
141 void conversation_table_set_gui_info(conv_gui_init_cb init_cb)
142 {
143     g_slist_foreach(registered_ct_tables, set_gui_data, init_cb);
144 }
145
146 void conversation_table_iterate_tables(GFunc func, gpointer user_data)
147 {
148     g_slist_foreach(registered_ct_tables, func, user_data);
149 }
150
151 guint conversation_table_get_num(void)
152 {
153     return g_slist_length(registered_ct_tables);
154 }
155
156
157 register_ct_t *get_conversation_table_by_num(guint table_num)
158 {
159     return (register_ct_t *) g_slist_nth_data(registered_ct_tables, table_num);
160 }
161
162 /** Compute the hash value for two given address/port pairs.
163  * (Parameter type is gconstpointer for GHashTable compatibility.)
164  *
165  * @param key Conversation. MUST point to a conv_key_t struct.
166  * @return Computed key hash.
167  */
168 static guint
169 conversation_hash(gconstpointer v)
170 {
171     const conv_key_t *key = (const conv_key_t *)v;
172     guint hash_val;
173
174     hash_val = 0;
175     ADD_ADDRESS_TO_HASH(hash_val, &key->addr1);
176     hash_val += key->port1;
177     ADD_ADDRESS_TO_HASH(hash_val, &key->addr2);
178     hash_val += key->port2;
179     hash_val ^= key->conv_id;
180
181     return hash_val;
182 }
183
184 /** Compare two conversation keys for an exact match.
185  * (Parameter types are gconstpointer for GHashTable compatibility.)
186  *
187  * @param key1 First conversation. MUST point to a conv_key_t struct.
188  * @param key2 Second conversation. MUST point to a conv_key_t struct.
189  * @return TRUE if conversations are equal, FALSE otherwise.
190  */
191 static gboolean
192 conversation_equal(gconstpointer key1, gconstpointer key2)
193 {
194     const conv_key_t *ck1 = (const conv_key_t *)key1;
195     const conv_key_t *ck2 = (const conv_key_t *)key2;
196
197     if (ck1->conv_id == ck2->conv_id)
198     {
199         if (ck1->port1 == ck2->port1 &&
200             ck1->port2 == ck2->port2 &&
201             ADDRESSES_EQUAL(&ck1->addr1, &ck2->addr1) &&
202             ADDRESSES_EQUAL(&ck1->addr2, &ck2->addr2)) {
203             return TRUE;
204         }
205
206         if (ck1->port2 == ck2->port1 &&
207             ck1->port1 == ck2->port2 &&
208             ADDRESSES_EQUAL(&ck1->addr2, &ck2->addr1) &&
209             ADDRESSES_EQUAL(&ck1->addr1, &ck2->addr2)) {
210             return TRUE;
211         }
212     }
213
214     /*
215      * The addresses, ports, or conversation IDs don't match.
216      */
217     return FALSE;
218 }
219
220 void
221 reset_conversation_table_data(conv_hash_t *ch)
222 {
223     if (!ch) {
224         return;
225     }
226
227     if (ch->conv_array != NULL) {
228         guint i;
229         for(i = 0; i < ch->conv_array->len; i++){
230             conv_item_t *conv = &g_array_index(ch->conv_array, conv_item_t, i);
231             g_free((gpointer)conv->src_address.data);
232             g_free((gpointer)conv->dst_address.data);
233         }
234
235         g_array_free(ch->conv_array, TRUE);
236     }
237
238     if (ch->hashtable != NULL) {
239         g_hash_table_destroy(ch->hashtable);
240     }
241
242     ch->conv_array=NULL;
243     ch->hashtable=NULL;
244 }
245
246 const char *get_conversation_address(address *addr, gboolean resolve_names)
247 {
248     if (resolve_names) {
249         return ep_address_to_display(addr);
250     } else {
251         return ep_address_to_str(addr);
252     }
253 }
254
255 const char *get_conversation_port(guint32 port, port_type ptype, gboolean resolve_names)
256 {
257
258     if(!resolve_names) ptype = PT_NONE;
259
260     switch(ptype) {
261     case(PT_TCP):
262         return ep_tcp_port_to_display(port);
263     case(PT_UDP):
264         return ep_udp_port_to_display(port);
265     case(PT_SCTP):
266         return ep_sctp_port_to_display(port);
267     default:
268         return ep_strdup_printf("%d", port);
269     }
270 }
271
272 /* given an address (to distinguish between ipv4 and ipv6 for tcp/udp),
273    a port_type and a name_type (FN_...)
274    return a string for the filter name.
275
276    Some addresses, like AT_ETHER may actually be any of multiple types
277    of protocols,   either ethernet, tokenring, fddi, wlan etc so we must be
278    more specific there;  that's why we need specific_addr_type.
279 */
280 static const char *
281 conversation_get_filter_name(conv_item_t *conv_item, conv_filter_type_e filter_type)
282 {
283
284     if ((conv_item == NULL) || (conv_item->dissector_info == NULL) || (conv_item->dissector_info->get_filter_type == NULL)) {
285         return CONV_FILTER_INVALID;
286     }
287
288     return conv_item->dissector_info->get_filter_type(conv_item, filter_type);
289 }
290
291 /* Convert a port number into a string or NULL */
292 static char *
293 ct_port_to_str(port_type ptype, guint32 port)
294 {
295     switch(ptype){
296     case PT_TCP:
297     case PT_UDP:
298     case PT_SCTP:
299     case PT_NCP:
300         return g_strdup_printf("%d", port);
301     default:
302         break;
303     }
304     return NULL;
305 }
306
307 const char *get_conversation_filter(conv_item_t *conv_item, conv_direction_e direction)
308 {
309     char *sport, *dport;
310     const char *str = "INVALID";
311
312     sport = ct_port_to_str(conv_item->ptype, conv_item->src_port);
313     dport = ct_port_to_str(conv_item->ptype, conv_item->dst_port);
314
315     switch(direction){
316     case CONV_DIR_A_TO_FROM_B:
317         /* A <-> B */
318         str = ep_strdup_printf("%s==%s%s%s%s%s && %s==%s%s%s%s%s",
319                               conversation_get_filter_name(conv_item,  CONV_FT_ANY_ADDRESS),
320                               ep_address_to_str(&conv_item->src_address),
321                               sport?" && ":"",
322                               sport?conversation_get_filter_name(conv_item,  CONV_FT_ANY_PORT):"",
323                               sport?"==":"",
324                               sport?sport:"",
325                               conversation_get_filter_name(conv_item,  CONV_FT_ANY_ADDRESS),
326                               ep_address_to_str(&conv_item->dst_address),
327                               dport?" && ":"",
328                               dport?conversation_get_filter_name(conv_item,  CONV_FT_ANY_PORT):"",
329                               dport?"==":"",
330                               dport?dport:""
331             );
332         break;
333     case CONV_DIR_A_TO_B:
334         /* A --> B */
335         str = ep_strdup_printf("%s==%s%s%s%s%s && %s==%s%s%s%s%s",
336                               conversation_get_filter_name(conv_item,  CONV_FT_SRC_ADDRESS),
337                               ep_address_to_str(&conv_item->src_address),
338                               sport?" && ":"",
339                               sport?conversation_get_filter_name(conv_item,  CONV_FT_SRC_PORT):"",
340                               sport?"==":"",
341                               sport?sport:"",
342                               conversation_get_filter_name(conv_item,  CONV_FT_DST_ADDRESS),
343                               ep_address_to_str(&conv_item->dst_address),
344                               dport?" && ":"",
345                               dport?conversation_get_filter_name(conv_item,  CONV_FT_DST_PORT):"",
346                               dport?"==":"",
347                               dport?dport:""
348             );
349         break;
350     case CONV_DIR_A_FROM_B:
351         /* A <-- B */
352         str = ep_strdup_printf("%s==%s%s%s%s%s && %s==%s%s%s%s%s",
353                               conversation_get_filter_name(conv_item,  CONV_FT_DST_ADDRESS),
354                               ep_address_to_str(&conv_item->src_address),
355                               sport?" && ":"",
356                               sport?conversation_get_filter_name(conv_item,  CONV_FT_DST_PORT):"",
357                               sport?"==":"",
358                               sport?sport:"",
359                               conversation_get_filter_name(conv_item,  CONV_FT_SRC_ADDRESS),
360                               ep_address_to_str(&conv_item->dst_address),
361                               dport?" && ":"",
362                               dport?conversation_get_filter_name(conv_item,  CONV_FT_SRC_PORT):"",
363                               dport?"==":"",
364                               dport?dport:""
365             );
366         break;
367     case CONV_DIR_A_TO_FROM_ANY:
368         /* A <-> ANY */
369         str = ep_strdup_printf("%s==%s%s%s%s%s",
370                               conversation_get_filter_name(conv_item,  CONV_FT_ANY_ADDRESS),
371                               ep_address_to_str(&conv_item->src_address),
372                               sport?" && ":"",
373                               sport?conversation_get_filter_name(conv_item,  CONV_FT_ANY_PORT):"",
374                               sport?"==":"",
375                               sport?sport:""
376             );
377         break;
378     case CONV_DIR_A_TO_ANY:
379         /* A --> ANY */
380         str = ep_strdup_printf("%s==%s%s%s%s%s",
381                               conversation_get_filter_name(conv_item,  CONV_FT_SRC_ADDRESS),
382                               ep_address_to_str(&conv_item->src_address),
383                               sport?" && ":"",
384                               sport?conversation_get_filter_name(conv_item,  CONV_FT_SRC_PORT):"",
385                               sport?"==":"",
386                               sport?sport:""
387             );
388         break;
389     case CONV_DIR_A_FROM_ANY:
390         /* A <-- ANY */
391         str = ep_strdup_printf("%s==%s%s%s%s%s",
392                               conversation_get_filter_name(conv_item,  CONV_FT_DST_ADDRESS),
393                               ep_address_to_str(&conv_item->src_address),
394                               sport?" && ":"",
395                               sport?conversation_get_filter_name(conv_item,  CONV_FT_DST_PORT):"",
396                               sport?"==":"",
397                               sport?sport:""
398             );
399         break;
400     case CONV_DIR_ANY_TO_FROM_B:
401         /* ANY <-> B */
402         str = ep_strdup_printf("%s==%s%s%s%s%s",
403                               conversation_get_filter_name(conv_item,  CONV_FT_ANY_ADDRESS),
404                               ep_address_to_str(&conv_item->dst_address),
405                               dport?" && ":"",
406                               dport?conversation_get_filter_name(conv_item,  CONV_FT_ANY_PORT):"",
407                               dport?"==":"",
408                               dport?dport:""
409             );
410         break;
411     case CONV_DIR_ANY_FROM_B:
412         /* ANY <-- B */
413         str = ep_strdup_printf("%s==%s%s%s%s%s",
414                               conversation_get_filter_name(conv_item,  CONV_FT_SRC_ADDRESS),
415                               ep_address_to_str(&conv_item->dst_address),
416                               dport?" && ":"",
417                               dport?conversation_get_filter_name(conv_item,  CONV_FT_SRC_PORT):"",
418                               dport?"==":"",
419                               dport?dport:""
420             );
421         break;
422     case CONV_DIR_ANY_TO_B:
423         /* ANY --> B */
424         str = ep_strdup_printf("%s==%s%s%s%s%s",
425                               conversation_get_filter_name(conv_item,  CONV_FT_DST_ADDRESS),
426                               ep_address_to_str(&conv_item->dst_address),
427                               dport?" && ":"",
428                               dport?conversation_get_filter_name(conv_item,  CONV_FT_DST_PORT):"",
429                               dport?"==":"",
430                               dport?dport:""
431             );
432         break;
433     default:
434         break;
435     }
436     g_free(sport);
437     g_free(dport);
438     return str;
439 }
440
441 void
442 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,
443         nstime_t *ts, nstime_t *abs_ts, ct_dissector_info_t *ct_info, port_type ptype)
444 {
445     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);
446 }
447
448 void
449 add_conversation_table_data_with_conv_id(
450     conv_hash_t *ch,
451     const address *src,
452     const address *dst,
453     guint32 src_port,
454     guint32 dst_port,
455     conv_id_t conv_id,
456     int num_frames,
457     int num_bytes,
458     nstime_t *ts,
459     nstime_t *abs_ts,
460     ct_dissector_info_t *ct_info,
461     port_type ptype)
462 {
463     const address *addr1, *addr2;
464     guint32 port1, port2;
465     conv_item_t *conv_item = NULL;
466     unsigned int conversation_idx = 0;
467
468     if (src_port > dst_port) {
469         addr1 = src;
470         addr2 = dst;
471         port1 = src_port;
472         port2 = dst_port;
473     } else if (src_port < dst_port) {
474         addr2 = src;
475         addr1 = dst;
476         port2 = src_port;
477         port1 = dst_port;
478     } else if (CMP_ADDRESS(src, dst) < 0) {
479         addr1 = src;
480         addr2 = dst;
481         port1 = src_port;
482         port2 = dst_port;
483     } else {
484         addr2 = src;
485         addr1 = dst;
486         port2 = src_port;
487         port1 = dst_port;
488     }
489
490     /* if we dont have any entries at all yet */
491     if (ch->conv_array == NULL) {
492         ch->conv_array = g_array_sized_new(FALSE, FALSE, sizeof(conv_item_t), 10000);
493
494         ch->hashtable = g_hash_table_new_full(conversation_hash,
495                                               conversation_equal, /* key_equal_func */
496                                               g_free,             /* key_destroy_func */
497                                               NULL);              /* value_destroy_func */
498
499     } else {
500         /* try to find it among the existing known conversations */
501         conv_key_t existing_key;
502
503         existing_key.addr1 = *addr1;
504         existing_key.addr2 = *addr2;
505         existing_key.port1 = port1;
506         existing_key.port2 = port2;
507         existing_key.conv_id = conv_id;
508         if (g_hash_table_lookup_extended(ch->hashtable, &existing_key, NULL, (gpointer *) &conversation_idx)) {
509             conv_item = &g_array_index(ch->conv_array, conv_item_t, conversation_idx);
510         }
511     }
512
513     /* if we still dont know what conversation this is it has to be a new one
514        and we have to allocate it and append it to the end of the list */
515     if (conv_item == NULL) {
516         conv_key_t *new_key;
517         conv_item_t new_conv_item;
518
519         COPY_ADDRESS(&new_conv_item.src_address, addr1);
520         COPY_ADDRESS(&new_conv_item.dst_address, addr2);
521         new_conv_item.dissector_info = ct_info;
522         new_conv_item.ptype = ptype;
523         new_conv_item.src_port = port1;
524         new_conv_item.dst_port = port2;
525         new_conv_item.conv_id = conv_id;
526         new_conv_item.rx_frames = 0;
527         new_conv_item.tx_frames = 0;
528         new_conv_item.rx_bytes = 0;
529         new_conv_item.tx_bytes = 0;
530         new_conv_item.modified = TRUE;
531
532         if (ts) {
533             memcpy(&new_conv_item.start_time, ts, sizeof(new_conv_item.start_time));
534             memcpy(&new_conv_item.stop_time, ts, sizeof(new_conv_item.stop_time));
535             memcpy(&new_conv_item.start_abs_time, abs_ts, sizeof(new_conv_item.start_abs_time));
536         } else {
537             nstime_set_unset(&new_conv_item.start_abs_time);
538             nstime_set_unset(&new_conv_item.start_time);
539             nstime_set_unset(&new_conv_item.stop_time);
540         }
541         g_array_append_val(ch->conv_array, new_conv_item);
542         conversation_idx = ch->conv_array->len - 1;
543         conv_item = &g_array_index(ch->conv_array, conv_item_t, conversation_idx);
544
545         /* ct->conversations address is not a constant but src/dst_address.data are */
546         new_key = g_new(conv_key_t, 1);
547         SET_ADDRESS(&new_key->addr1, conv_item->src_address.type, conv_item->src_address.len, conv_item->src_address.data);
548         SET_ADDRESS(&new_key->addr2, conv_item->dst_address.type, conv_item->dst_address.len, conv_item->dst_address.data);
549         new_key->port1 = port1;
550         new_key->port2 = port2;
551         new_key->conv_id = conv_id;
552         g_hash_table_insert(ch->hashtable, new_key, GUINT_TO_POINTER(conversation_idx));
553     }
554
555     /* update the conversation struct */
556     conv_item->modified = TRUE;
557     if ( (!CMP_ADDRESS(src, addr1)) && (!CMP_ADDRESS(dst, addr2)) && (src_port==port1) && (dst_port==port2) ) {
558         conv_item->tx_frames += num_frames;
559         conv_item->tx_bytes += num_bytes;
560     } else {
561         conv_item->rx_frames += num_frames;
562         conv_item->rx_bytes += num_bytes;
563     }
564
565     if (ts) {
566         if (nstime_cmp(ts, &conv_item->stop_time) > 0) {
567             memcpy(&conv_item->stop_time, ts, sizeof(conv_item->stop_time));
568         } else if (nstime_cmp(ts, &conv_item->start_time) < 0) {
569             memcpy(&conv_item->start_time, ts, sizeof(conv_item->start_time));
570             memcpy(&conv_item->start_abs_time, abs_ts, sizeof(conv_item->start_abs_time));
571         }
572     }
573 }
574
575 /*
576  * Editor modelines
577  *
578  * Local Variables:
579  * c-basic-offset: 4
580  * tab-width: 8
581  * indent-tabs-mode: nil
582  * End:
583  *
584  * ex: set shiftwidth=4 tabstop=8 expandtab:
585  * :indentSize=4:tabSize=8:noTabs=true:
586  */