Revert "LATER... ei_kerberos_kdc_session_key ..."
[wireshark-sm.git] / epan / sequence_analysis.c
bloba5de5b1ef8bc313d7794c88a966ac6dae6567984
1 /* sequence-analysis.c
2 * Flow sequence analysis
4 * Some code from gtk/flow_graph.c
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
13 #include "config.h"
14 #define WS_LOG_DOMAIN LOG_DOMAIN_EPAN
16 #include "sequence_analysis.h"
18 #include "addr_resolv.h"
19 #include "proto.h"
20 #include "color_filters.h"
21 #include <epan/column.h>
22 #include "tap.h"
23 #include <epan/wmem_scopes.h>
25 #define NODE_OVERFLOW MAX_NUM_NODES+1
27 struct register_analysis {
28 const char* name; /* Name (used for lookup) */
29 const char* ui_name; /* Name used for UI */
30 int proto_id; /* protocol id (0-indexed) */
31 const char* tap_listen_str; /* string used in register_tap_listener (NULL to use protocol name) */
32 unsigned tap_flags;
33 tap_packet_cb analysis_func; /* function to be called for new incoming packets for sequence analysis */
36 static wmem_tree_t *registered_seq_analysis;
38 void
39 register_seq_analysis(const char* name, const char* ui_name, const int proto_id, const char* tap_listener, unsigned tap_flags, tap_packet_cb tap_func)
41 register_analysis_t* analysis;
43 DISSECTOR_ASSERT(tap_func);
45 analysis = wmem_new0(wmem_epan_scope(), register_analysis_t);
47 analysis->name = name;
48 analysis->ui_name = ui_name;
49 analysis->proto_id = proto_id;
50 if (tap_listener != NULL)
51 analysis->tap_listen_str = tap_listener;
52 else
53 analysis->tap_listen_str = proto_get_protocol_filter_name(proto_id);
54 analysis->tap_flags = tap_flags;
55 analysis->analysis_func = tap_func;
57 if (registered_seq_analysis == NULL)
58 registered_seq_analysis = wmem_tree_new(wmem_epan_scope());
60 wmem_tree_insert_string(registered_seq_analysis, name, analysis, 0);
63 const char* sequence_analysis_get_name(register_analysis_t* analysis)
65 return analysis->name;
68 const char* sequence_analysis_get_ui_name(register_analysis_t* analysis)
70 return analysis->ui_name;
73 const char* sequence_analysis_get_tap_listener_name(register_analysis_t* analysis)
75 return analysis->tap_listen_str;
78 tap_packet_cb sequence_analysis_get_packet_func(register_analysis_t* analysis)
80 return analysis->analysis_func;
83 unsigned sequence_analysis_get_tap_flags(register_analysis_t* analysis)
85 return analysis->tap_flags;
89 register_analysis_t* sequence_analysis_find_by_name(const char* name)
91 return (register_analysis_t*)wmem_tree_lookup_string(registered_seq_analysis, name, 0);
94 void sequence_analysis_table_iterate_tables(wmem_foreach_func func, void *user_data)
96 wmem_tree_foreach(registered_seq_analysis, func, user_data);
99 seq_analysis_item_t* sequence_analysis_create_sai_with_addresses(packet_info *pinfo, seq_analysis_info_t *sainfo)
101 seq_analysis_item_t *sai = NULL;
102 char time_str[COL_MAX_LEN];
104 if (!sainfo->any_addr) {
105 if (pinfo->net_src.type!=AT_NONE && pinfo->net_dst.type!=AT_NONE) {
106 sai = g_new0(seq_analysis_item_t, 1);
107 copy_address(&(sai->src_addr),&(pinfo->net_src));
108 copy_address(&(sai->dst_addr),&(pinfo->net_dst));
110 } else {
111 if (pinfo->src.type!=AT_NONE && pinfo->dst.type!=AT_NONE) {
112 sai = g_new0(seq_analysis_item_t, 1);
113 copy_address(&(sai->src_addr),&(pinfo->src));
114 copy_address(&(sai->dst_addr),&(pinfo->dst));
118 if (sai) {
119 /* Fill in the timestamps */
120 set_fd_time(pinfo->epan, pinfo->fd, time_str);
121 sai->time_str = g_strdup(time_str);
124 return sai;
127 void sequence_analysis_use_color_filter(packet_info *pinfo, seq_analysis_item_t *sai)
129 if (pinfo->fd->color_filter) {
130 sai->bg_color = color_t_to_rgb(&pinfo->fd->color_filter->bg_color);
131 sai->fg_color = color_t_to_rgb(&pinfo->fd->color_filter->fg_color);
132 sai->has_color_filter = true;
136 void sequence_analysis_use_col_info_as_label_comment(packet_info *pinfo, seq_analysis_item_t *sai)
138 const char *protocol = NULL;
139 const char *colinfo = NULL;
141 if (pinfo->cinfo) {
142 colinfo = col_get_text(pinfo->cinfo, COL_INFO);
143 protocol = col_get_text(pinfo->cinfo, COL_PROTOCOL);
146 if (colinfo != NULL) {
147 sai->frame_label = g_strdup(colinfo);
148 if (protocol != NULL) {
149 sai->comment = ws_strdup_printf("%s: %s", protocol, colinfo);
150 } else {
151 sai->comment = g_strdup(colinfo);
153 } else {
154 /* This will probably never happen...*/
155 if (protocol != NULL) {
156 sai->frame_label = g_strdup(protocol);
157 sai->comment = g_strdup(protocol);
162 seq_analysis_info_t *
163 sequence_analysis_info_new(void)
165 seq_analysis_info_t *sainfo = g_new0(seq_analysis_info_t, 1);
167 ws_noisy("adding new item");
168 sainfo->items = g_queue_new();
169 sainfo->ht= g_hash_table_new(g_direct_hash, g_direct_equal);
170 return sainfo;
173 void sequence_analysis_info_free(seq_analysis_info_t *sainfo)
175 if (!sainfo) return;
177 ws_noisy("%d items", g_queue_get_length(sainfo->items));
178 sequence_analysis_list_free(sainfo);
180 g_queue_free(sainfo->items);
181 if (sainfo->ht != NULL)
182 g_hash_table_destroy(sainfo->ht);
184 g_free(sainfo);
187 static void sequence_analysis_item_free(void *data)
189 seq_analysis_item_t *seq_item = (seq_analysis_item_t *)data;
190 g_free(seq_item->frame_label);
191 g_free(seq_item->time_str);
192 g_free(seq_item->comment);
193 free_address(&seq_item->src_addr);
194 free_address(&seq_item->dst_addr);
195 if (seq_item->info_ptr) {
196 #if 0
197 /* XXX: If seq_item->info_type is GA_INFO_TYPE_RTP, then we need
198 * to free the data, but rtpstream_info_free_* is in libui and
199 * not exported. */
200 if (seq_item->info_type == GA_INFO_TYPE_RTP) {
201 rtpstream_info_free_data((rtpstream_info_t *)seq_item->info_ptr);
203 #endif
204 g_free(seq_item->info_ptr);
206 g_free(data);
210 /* compare two list entries by packet no */
211 static int
212 sequence_analysis_sort_compare(const void *a, const void *b, void *user_data _U_)
214 const seq_analysis_item_t *entry_a = (const seq_analysis_item_t *)a;
215 const seq_analysis_item_t *entry_b = (const seq_analysis_item_t *)b;
217 if(entry_a->frame_number < entry_b->frame_number)
218 return -1;
220 if(entry_a->frame_number > entry_b->frame_number)
221 return 1;
223 return 0;
227 void
228 sequence_analysis_list_sort(seq_analysis_info_t *sainfo)
230 if (!sainfo) return;
231 g_queue_sort(sainfo->items, sequence_analysis_sort_compare, NULL);
234 void
235 sequence_analysis_list_free(seq_analysis_info_t *sainfo)
237 if (!sainfo) return;
238 ws_noisy("%d items", g_queue_get_length(sainfo->items));
240 /* free the graph data items */
242 if (sainfo->items != NULL)
243 g_queue_free_full(sainfo->items, sequence_analysis_item_free);
244 sainfo->items = g_queue_new();
246 if (NULL != sainfo->ht) {
247 g_hash_table_remove_all(sainfo->ht);
249 sainfo->nconv = 0;
251 sequence_analysis_free_nodes(sainfo);
254 /* Return the index array if the node is in the array. Return -1 if there is room in the array
255 * and Return -2 if the array is full
257 /****************************************************************************/
258 static unsigned add_or_get_node(seq_analysis_info_t *sainfo, address *node) {
259 unsigned i;
261 if (node->type == AT_NONE) return NODE_OVERFLOW;
263 for (i=0; i<MAX_NUM_NODES && i < sainfo->num_nodes ; i++) {
264 if ( cmp_address(&(sainfo->nodes[i]), node) == 0 ) return i; /* it is in the array */
267 if (i >= MAX_NUM_NODES) {
268 return NODE_OVERFLOW;
269 } else {
270 sainfo->num_nodes++;
271 copy_address(&(sainfo->nodes[i]), node);
272 return i;
276 /* Same as add_or_get_node() but invoked for conversations where the same address is both used
277 * as src and dst.
278 * The occurrence number is tracking how many times this address was seen,
279 * value is 0 for the first occurrence, 1 for the second, and we never go higher to not have
280 * too many Y-Axis in the diagram.
282 static unsigned add_or_get_node_local(seq_analysis_info_t *sainfo, address *node, uint8_t occurrence) {
283 unsigned i;
285 if (node->type == AT_NONE) return NODE_OVERFLOW;
287 for (i=0; i<MAX_NUM_NODES && i < sainfo->num_nodes ; i++) {
288 if ( cmp_address(&(sainfo->nodes[i]), node) == 0 ) {
290 /* address is matching, go further by checking the occurrence indication */
291 if(sainfo->occurrence[i]==occurrence) {
292 return i;
297 if (i >= MAX_NUM_NODES) {
298 return NODE_OVERFLOW;
300 else { /* insert a new entry */
301 sainfo->num_nodes++;
302 copy_address(&(sainfo->nodes[i]), node);
303 sainfo->occurrence[i] = occurrence;
304 return i;
308 struct sainfo_counter {
309 seq_analysis_info_t *sainfo;
310 int num_items;
313 static void sequence_analysis_get_nodes_item_proc(void *data, void *user_data)
315 seq_analysis_item_t *gai = (seq_analysis_item_t *)data;
316 struct sainfo_counter *sc = (struct sainfo_counter *)user_data;
317 if (gai->display) {
318 (sc->num_items)++;
320 /* when both addresses are the same, look at the ports indications */
321 if( addresses_equal(&(gai->src_addr), &(gai->dst_addr)) ) {
322 if(gai->port_src < gai->port_dst) {
323 gai->src_node = add_or_get_node_local(sc->sainfo, &(gai->src_addr), 0 );
324 gai->dst_node = add_or_get_node_local(sc->sainfo, &(gai->dst_addr), 1 );
326 else {
327 gai->src_node = add_or_get_node_local(sc->sainfo, &(gai->src_addr), 1 );
328 gai->dst_node = add_or_get_node_local(sc->sainfo, &(gai->dst_addr), 0 );
331 else {
332 gai->src_node = add_or_get_node(sc->sainfo, &(gai->src_addr));
333 gai->dst_node = add_or_get_node(sc->sainfo, &(gai->dst_addr));
338 /* Get the nodes from the list */
339 /****************************************************************************/
341 sequence_analysis_get_nodes(seq_analysis_info_t *sainfo)
343 struct sainfo_counter sc = {sainfo, 0};
345 /* Fill the node array */
346 g_queue_foreach(sainfo->items, sequence_analysis_get_nodes_item_proc, &sc);
348 return sc.num_items;
351 /* Free the node address list */
352 /****************************************************************************/
353 void
354 sequence_analysis_free_nodes(seq_analysis_info_t *sainfo)
356 int i;
358 for (i=0; i<MAX_NUM_NODES; i++) {
359 free_address(&sainfo->nodes[i]);
361 sainfo->num_nodes = 0;
364 /* Writing analysis to file */
365 /****************************************************************************/
367 #define NODE_CHARS_WIDTH 20
368 #define CONV_TIME_HEADER "Conv.| Time "
369 #define TIME_HEADER "|Time "
370 #define CONV_TIME_EMPTY_HEADER " | "
371 #define TIME_EMPTY_HEADER "| "
372 #define CONV_TIME_HEADER_LENGTH 16
373 #define TIME_HEADER_LENGTH 10
375 /****************************************************************************/
376 /* Adds trailing characters to complete the requested length. */
377 /****************************************************************************/
379 static void enlarge_string(GString *gstr, uint32_t length, char pad) {
381 size_t i;
383 for (i = gstr->len; i < length; i++) {
384 g_string_append_c(gstr, pad);
388 /****************************************************************************/
389 /* overwrites the characters in a string, between positions p1 and p2, with */
390 /* the characters of text_to_insert */
391 /* NB: it does not check that p1 and p2 fit into string */
392 /****************************************************************************/
394 static void overwrite (GString *gstr, char *text_to_insert, uint32_t p1, uint32_t p2) {
396 glong len, ins_len;
397 size_t pos;
398 char *ins_str = NULL;
400 if (p1 == p2)
401 return;
403 if (p1 > p2) {
404 pos = p2;
405 len = p1 - p2;
407 else{
408 pos = p1;
409 len = p2 - p1;
412 ins_len = g_utf8_strlen(text_to_insert, -1);
413 if (len > ins_len) {
414 len = ins_len;
415 } else if (len < ins_len) {
416 ins_str = g_utf8_substring(text_to_insert, 0, len);
419 if (!ins_str) ins_str = g_strdup(text_to_insert);
421 if (pos > gstr->len)
422 pos = gstr->len;
424 g_string_erase(gstr, pos, len);
426 g_string_insert(gstr, pos, ins_str);
427 g_free(ins_str);
431 void
432 sequence_analysis_dump_to_file(FILE *of, seq_analysis_info_t *sainfo, unsigned int first_node)
434 uint32_t i, display_items, display_nodes;
435 uint32_t start_position, end_position, item_width, header_length;
436 seq_analysis_item_t *sai;
437 uint16_t first_conv_num = 0;
438 bool several_convs = false;
439 bool first_packet = true;
441 GString *label_string, *empty_line, *separator_line, *tmp_str, *tmp_str2;
442 const char *empty_header;
443 char src_port[8], dst_port[8];
444 GList *list = NULL;
445 char *addr_str;
447 display_items = 0;
448 if (sainfo->items != NULL)
449 list = g_queue_peek_nth_link(sainfo->items, 0);
451 while (list)
453 sai = (seq_analysis_item_t *)list->data;
454 list = g_list_next(list);
456 if (!sai->display)
457 continue;
459 display_items += 1;
460 if (first_packet) {
461 first_conv_num = sai->conv_num;
462 first_packet = false;
464 else if (sai->conv_num != first_conv_num) {
465 several_convs = true;
469 /* if not items to display */
470 if (display_items == 0) {
471 return;
474 label_string = g_string_new("");
475 empty_line = g_string_new("");
476 separator_line = g_string_new("");
477 tmp_str = g_string_new("");
478 tmp_str2 = g_string_new("");
480 display_nodes = sainfo->num_nodes;
482 /* Write the conv. and time headers */
483 if (several_convs) {
484 fprintf(of, CONV_TIME_HEADER);
485 empty_header = CONV_TIME_EMPTY_HEADER;
486 header_length = CONV_TIME_HEADER_LENGTH;
488 else{
489 fprintf(of, TIME_HEADER);
490 empty_header = TIME_EMPTY_HEADER;
491 header_length = TIME_HEADER_LENGTH;
494 /* Write the node names on top */
495 for (i=0; i<display_nodes; i+=2) {
496 /* print the node identifiers */
497 addr_str = address_to_display(NULL, &(sainfo->nodes[i+first_node]));
498 g_string_printf(label_string, "| %s", addr_str);
499 wmem_free(NULL, addr_str);
500 enlarge_string(label_string, NODE_CHARS_WIDTH*2, ' ');
501 fprintf(of, "%s", label_string->str);
502 g_string_printf(label_string, "| ");
503 enlarge_string(label_string, NODE_CHARS_WIDTH, ' ');
504 g_string_append(empty_line, label_string->str);
507 fprintf(of, "|\n%s", empty_header);
508 g_string_printf(label_string, "| ");
509 enlarge_string(label_string, NODE_CHARS_WIDTH, ' ');
510 fprintf(of, "%s", label_string->str);
512 /* Write the node names on top */
513 for (i=1; i<display_nodes; i+=2) {
514 /* print the node identifiers */
515 addr_str = address_to_display(NULL, &(sainfo->nodes[i+first_node]));
516 g_string_printf(label_string, "| %s", addr_str);
517 wmem_free(NULL, addr_str);
518 if (label_string->len < NODE_CHARS_WIDTH)
520 enlarge_string(label_string, NODE_CHARS_WIDTH, ' ');
521 g_string_append(label_string, "| ");
523 enlarge_string(label_string, NODE_CHARS_WIDTH*2, ' ');
524 fprintf(of, "%s", label_string->str);
525 g_string_printf(label_string, "| ");
526 enlarge_string(label_string, NODE_CHARS_WIDTH, ' ');
527 g_string_append(empty_line, label_string->str);
530 fprintf(of, "\n");
532 g_string_append_c(empty_line, '|');
534 enlarge_string(separator_line, (uint32_t) empty_line->len + header_length, '-');
537 * Draw the items
540 list = g_queue_peek_nth_link(sainfo->items, 0);
541 while (list)
543 sai = (seq_analysis_item_t *)list->data;
544 list = g_list_next(list);
546 if (!sai->display)
547 continue;
549 start_position = (sai->src_node-first_node)*NODE_CHARS_WIDTH+NODE_CHARS_WIDTH/2;
551 end_position = (sai->dst_node-first_node)*NODE_CHARS_WIDTH+NODE_CHARS_WIDTH/2;
553 if (start_position > end_position) {
554 item_width = start_position-end_position;
556 else if (start_position < end_position) {
557 item_width = end_position-start_position;
559 else{ /* same origin and destination address */
560 end_position = start_position+NODE_CHARS_WIDTH;
561 item_width = NODE_CHARS_WIDTH;
564 /* separator between conversations */
565 if (sai->conv_num != first_conv_num) {
566 fprintf(of, "%s\n", separator_line->str);
567 first_conv_num = sai->conv_num;
570 /* write the conversation number */
571 if (several_convs) {
572 g_string_printf(label_string, "%i", sai->conv_num);
573 enlarge_string(label_string, 5, ' ');
574 fprintf(of, "%s", label_string->str);
577 if (sai->time_str != NULL) {
578 g_string_printf(label_string, "|%s", sai->time_str);
579 enlarge_string(label_string, 10, ' ');
580 fprintf(of, "%s", label_string->str);
583 /* write the frame label */
585 g_string_printf(tmp_str, "%s", empty_line->str);
586 overwrite(tmp_str, sai->frame_label,
587 start_position,
588 end_position
590 fprintf(of, "%s", tmp_str->str);
592 /* write the comments */
593 fprintf(of, "%s\n", sai->comment);
595 /* write the arrow and frame label*/
596 fprintf(of, "%s", empty_header);
598 g_string_printf(tmp_str, "%s", empty_line->str);
600 g_string_truncate(tmp_str2, 0);
602 if (start_position<end_position) {
603 enlarge_string(tmp_str2, item_width-2, '-');
604 g_string_append_c(tmp_str2, '>');
606 else{
607 g_string_printf(tmp_str2, "<");
608 enlarge_string(tmp_str2, item_width-1, '-');
611 overwrite(tmp_str, tmp_str2->str,
612 start_position,
613 end_position
616 snprintf(src_port, sizeof(src_port), "(%i)", sai->port_src);
617 snprintf(dst_port, sizeof(dst_port), "(%i)", sai->port_dst);
619 if (start_position<end_position) {
620 overwrite(tmp_str, src_port, start_position-9, start_position-1);
621 overwrite(tmp_str, dst_port, end_position+1, end_position+9);
623 else{
624 overwrite(tmp_str, src_port, start_position+1, start_position+9);
625 overwrite(tmp_str, dst_port, end_position-9, end_position+1);
628 fprintf(of, "%s\n", tmp_str->str);
631 g_string_free(label_string, TRUE);
632 g_string_free(empty_line, TRUE);
633 g_string_free(separator_line, TRUE);
634 g_string_free(tmp_str, TRUE);
635 g_string_free(tmp_str2, TRUE);
639 * Editor modelines
641 * Local Variables:
642 * c-basic-offset: 4
643 * tab-width: 8
644 * indent-tabs-mode: nil
645 * End:
647 * ex: set shiftwidth=4 tabstop=8 expandtab:
648 * :indentSize=4:tabSize=8:noTabs=true: