summaryrefslogtreecommitdiffstats
path: root/epan/sequence_analysis.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
commite4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch)
tree68cb5ef9081156392f1dd62a00c6ccc1451b93df /epan/sequence_analysis.c
parentInitial commit. (diff)
downloadwireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz
wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'epan/sequence_analysis.c')
-rw-r--r--epan/sequence_analysis.c603
1 files changed, 603 insertions, 0 deletions
diff --git a/epan/sequence_analysis.c b/epan/sequence_analysis.c
new file mode 100644
index 00000000..d061e31b
--- /dev/null
+++ b/epan/sequence_analysis.c
@@ -0,0 +1,603 @@
+/* sequence-analysis.c
+ * Flow sequence analysis
+ *
+ * Some code from gtk/flow_graph.c
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include "sequence_analysis.h"
+
+#include "addr_resolv.h"
+#include "proto.h"
+#include "color_filters.h"
+#include <epan/column.h>
+#include "tap.h"
+#include <epan/wmem_scopes.h>
+
+#define NODE_OVERFLOW MAX_NUM_NODES+1
+
+struct register_analysis {
+ const char* name; /* Name (used for lookup) */
+ const char* ui_name; /* Name used for UI */
+ int proto_id; /* protocol id (0-indexed) */
+ const char* tap_listen_str; /* string used in register_tap_listener (NULL to use protocol name) */
+ guint tap_flags;
+ tap_packet_cb analysis_func; /* function to be called for new incoming packets for sequence analysis */
+};
+
+static wmem_tree_t *registered_seq_analysis = NULL;
+
+void
+register_seq_analysis(const char* name, const char* ui_name, const int proto_id, const char* tap_listener, guint tap_flags, tap_packet_cb tap_func)
+{
+ register_analysis_t* analysis;
+
+ DISSECTOR_ASSERT(tap_func);
+
+ analysis = wmem_new0(wmem_epan_scope(), register_analysis_t);
+
+ analysis->name = name;
+ analysis->ui_name = ui_name;
+ analysis->proto_id = proto_id;
+ if (tap_listener != NULL)
+ analysis->tap_listen_str = tap_listener;
+ else
+ analysis->tap_listen_str = proto_get_protocol_filter_name(proto_id);
+ analysis->tap_flags = tap_flags;
+ analysis->analysis_func = tap_func;
+
+ if (registered_seq_analysis == NULL)
+ registered_seq_analysis = wmem_tree_new(wmem_epan_scope());
+
+ wmem_tree_insert_string(registered_seq_analysis, name, analysis, 0);
+}
+
+const char* sequence_analysis_get_name(register_analysis_t* analysis)
+{
+ return analysis->name;
+}
+
+const char* sequence_analysis_get_ui_name(register_analysis_t* analysis)
+{
+ return analysis->ui_name;
+}
+
+const char* sequence_analysis_get_tap_listener_name(register_analysis_t* analysis)
+{
+ return analysis->tap_listen_str;
+}
+
+tap_packet_cb sequence_analysis_get_packet_func(register_analysis_t* analysis)
+{
+ return analysis->analysis_func;
+}
+
+guint sequence_analysis_get_tap_flags(register_analysis_t* analysis)
+{
+ return analysis->tap_flags;
+}
+
+
+register_analysis_t* sequence_analysis_find_by_name(const char* name)
+{
+ return (register_analysis_t*)wmem_tree_lookup_string(registered_seq_analysis, name, 0);
+}
+
+void sequence_analysis_table_iterate_tables(wmem_foreach_func func, gpointer user_data)
+{
+ wmem_tree_foreach(registered_seq_analysis, func, user_data);
+}
+
+seq_analysis_item_t* sequence_analysis_create_sai_with_addresses(packet_info *pinfo, seq_analysis_info_t *sainfo)
+{
+ seq_analysis_item_t *sai = NULL;
+ char time_str[COL_MAX_LEN];
+
+ if (sainfo->any_addr) {
+ if (pinfo->net_src.type!=AT_NONE && pinfo->net_dst.type!=AT_NONE) {
+ sai = g_new0(seq_analysis_item_t, 1);
+ copy_address(&(sai->src_addr),&(pinfo->net_src));
+ copy_address(&(sai->dst_addr),&(pinfo->net_dst));
+ }
+
+ } else {
+ if (pinfo->src.type!=AT_NONE && pinfo->dst.type!=AT_NONE) {
+ sai = g_new0(seq_analysis_item_t, 1);
+ copy_address(&(sai->src_addr),&(pinfo->src));
+ copy_address(&(sai->dst_addr),&(pinfo->dst));
+ }
+ }
+
+ if (sai) {
+ /* Fill in the timestamps */
+ set_fd_time(pinfo->epan, pinfo->fd, time_str);
+ sai->time_str = g_strdup(time_str);
+ }
+
+ return sai;
+}
+
+void sequence_analysis_use_color_filter(packet_info *pinfo, seq_analysis_item_t *sai)
+{
+ if (pinfo->fd->color_filter) {
+ sai->bg_color = color_t_to_rgb(&pinfo->fd->color_filter->bg_color);
+ sai->fg_color = color_t_to_rgb(&pinfo->fd->color_filter->fg_color);
+ sai->has_color_filter = TRUE;
+ }
+}
+
+void sequence_analysis_use_col_info_as_label_comment(packet_info *pinfo, seq_analysis_item_t *sai)
+{
+ const gchar *protocol = NULL;
+ const gchar *colinfo = NULL;
+
+ if (pinfo->cinfo) {
+ colinfo = col_get_text(pinfo->cinfo, COL_INFO);
+ protocol = col_get_text(pinfo->cinfo, COL_PROTOCOL);
+ }
+
+ if (colinfo != NULL) {
+ sai->frame_label = g_strdup(colinfo);
+ if (protocol != NULL) {
+ sai->comment = ws_strdup_printf("%s: %s", protocol, colinfo);
+ } else {
+ sai->comment = g_strdup(colinfo);
+ }
+ } else {
+ /* This will probably never happen...*/
+ if (protocol != NULL) {
+ sai->frame_label = g_strdup(protocol);
+ sai->comment = g_strdup(protocol);
+ }
+ }
+}
+
+seq_analysis_info_t *
+sequence_analysis_info_new(void)
+{
+ seq_analysis_info_t *sainfo = g_new0(seq_analysis_info_t, 1);
+
+ /* SEQ_ANALYSIS_DEBUG("adding new item"); */
+ sainfo->items = g_queue_new();
+ sainfo->ht= g_hash_table_new(g_direct_hash, g_direct_equal);
+ return sainfo;
+}
+
+void sequence_analysis_info_free(seq_analysis_info_t *sainfo)
+{
+ if (!sainfo) return;
+
+ /* SEQ_ANALYSIS_DEBUG("%d items", g_queue_get_length(sainfo->items)); */
+ sequence_analysis_list_free(sainfo);
+
+ g_queue_free(sainfo->items);
+ if (sainfo->ht != NULL)
+ g_hash_table_destroy(sainfo->ht);
+
+ g_free(sainfo);
+}
+
+static void sequence_analysis_item_free(gpointer data)
+{
+ seq_analysis_item_t *seq_item = (seq_analysis_item_t *)data;
+ g_free(seq_item->frame_label);
+ g_free(seq_item->time_str);
+ g_free(seq_item->comment);
+ free_address(&seq_item->src_addr);
+ free_address(&seq_item->dst_addr);
+ if (seq_item->info_ptr) {
+#if 0
+ /* XXX: If seq_item->info_type is GA_INFO_TYPE_RTP, then we need
+ * to free the data, but rtpstream_info_free_* is in libui and
+ * not exported. */
+ if (seq_item->info_type == GA_INFO_TYPE_RTP) {
+ rtpstream_info_free_data((rtpstream_info_t *)seq_item->info_ptr);
+ }
+#endif
+ g_free(seq_item->info_ptr);
+ }
+ g_free(data);
+}
+
+
+/* compare two list entries by packet no */
+static gint
+sequence_analysis_sort_compare(gconstpointer a, gconstpointer b, gpointer user_data _U_)
+{
+ const seq_analysis_item_t *entry_a = (const seq_analysis_item_t *)a;
+ const seq_analysis_item_t *entry_b = (const seq_analysis_item_t *)b;
+
+ if(entry_a->frame_number < entry_b->frame_number)
+ return -1;
+
+ if(entry_a->frame_number > entry_b->frame_number)
+ return 1;
+
+ return 0;
+}
+
+
+void
+sequence_analysis_list_sort(seq_analysis_info_t *sainfo)
+{
+ if (!sainfo) return;
+ g_queue_sort(sainfo->items, sequence_analysis_sort_compare, NULL);
+}
+
+void
+sequence_analysis_list_free(seq_analysis_info_t *sainfo)
+{
+ if (!sainfo) return;
+ /* SEQ_ANALYSIS_DEBUG("%d items", g_queue_get_length(sainfo->items)); */
+
+ /* free the graph data items */
+
+ if (sainfo->items != NULL)
+ g_queue_free_full(sainfo->items, sequence_analysis_item_free);
+ sainfo->items = g_queue_new();
+
+ if (NULL != sainfo->ht) {
+ g_hash_table_remove_all(sainfo->ht);
+ }
+ sainfo->nconv = 0;
+
+ sequence_analysis_free_nodes(sainfo);
+}
+
+/* Return the index array if the node is in the array. Return -1 if there is room in the array
+ * and Return -2 if the array is full
+ */
+/****************************************************************************/
+static guint add_or_get_node(seq_analysis_info_t *sainfo, address *node) {
+ guint i;
+
+ if (node->type == AT_NONE) return NODE_OVERFLOW;
+
+ for (i=0; i<MAX_NUM_NODES && i < sainfo->num_nodes ; i++) {
+ if ( cmp_address(&(sainfo->nodes[i]), node) == 0 ) return i; /* it is in the array */
+ }
+
+ if (i >= MAX_NUM_NODES) {
+ return NODE_OVERFLOW;
+ } else {
+ sainfo->num_nodes++;
+ copy_address(&(sainfo->nodes[i]), node);
+ return i;
+ }
+}
+
+struct sainfo_counter {
+ seq_analysis_info_t *sainfo;
+ int num_items;
+};
+
+static void sequence_analysis_get_nodes_item_proc(gpointer data, gpointer user_data)
+{
+ seq_analysis_item_t *gai = (seq_analysis_item_t *)data;
+ struct sainfo_counter *sc = (struct sainfo_counter *)user_data;
+ if (gai->display) {
+ (sc->num_items)++;
+ gai->src_node = add_or_get_node(sc->sainfo, &(gai->src_addr));
+ gai->dst_node = add_or_get_node(sc->sainfo, &(gai->dst_addr));
+ }
+}
+
+/* Get the nodes from the list */
+/****************************************************************************/
+int
+sequence_analysis_get_nodes(seq_analysis_info_t *sainfo)
+{
+ struct sainfo_counter sc = {sainfo, 0};
+
+ /* Fill the node array */
+ g_queue_foreach(sainfo->items, sequence_analysis_get_nodes_item_proc, &sc);
+
+ return sc.num_items;
+}
+
+/* Free the node address list */
+/****************************************************************************/
+void
+sequence_analysis_free_nodes(seq_analysis_info_t *sainfo)
+{
+ int i;
+
+ for (i=0; i<MAX_NUM_NODES; i++) {
+ free_address(&sainfo->nodes[i]);
+ }
+ sainfo->num_nodes = 0;
+}
+
+/* Writing analysis to file */
+/****************************************************************************/
+
+#define NODE_CHARS_WIDTH 20
+#define CONV_TIME_HEADER "Conv.| Time "
+#define TIME_HEADER "|Time "
+#define CONV_TIME_EMPTY_HEADER " | "
+#define TIME_EMPTY_HEADER "| "
+#define CONV_TIME_HEADER_LENGTH 16
+#define TIME_HEADER_LENGTH 10
+
+/****************************************************************************/
+/* Adds trailing characters to complete the requested length. */
+/****************************************************************************/
+
+static void enlarge_string(GString *gstr, guint32 length, char pad) {
+
+ gsize i;
+
+ for (i = gstr->len; i < length; i++) {
+ g_string_append_c(gstr, pad);
+ }
+}
+
+/****************************************************************************/
+/* overwrites the characters in a string, between positions p1 and p2, with */
+/* the characters of text_to_insert */
+/* NB: it does not check that p1 and p2 fit into string */
+/****************************************************************************/
+
+static void overwrite (GString *gstr, char *text_to_insert, guint32 p1, guint32 p2) {
+
+ glong len, ins_len;
+ gsize pos;
+ gchar *ins_str = NULL;
+
+ if (p1 == p2)
+ return;
+
+ if (p1 > p2) {
+ pos = p2;
+ len = p1 - p2;
+ }
+ else{
+ pos = p1;
+ len = p2 - p1;
+ }
+
+ ins_len = g_utf8_strlen(text_to_insert, -1);
+ if (len > ins_len) {
+ len = ins_len;
+ } else if (len < ins_len) {
+ ins_str = g_utf8_substring(text_to_insert, 0, len);
+ }
+
+ if (!ins_str) ins_str = g_strdup(text_to_insert);
+
+ if (pos > gstr->len)
+ pos = gstr->len;
+
+ g_string_erase(gstr, pos, len);
+
+ g_string_insert(gstr, pos, ins_str);
+ g_free(ins_str);
+}
+
+
+void
+sequence_analysis_dump_to_file(FILE *of, seq_analysis_info_t *sainfo, unsigned int first_node)
+{
+ guint32 i, display_items, display_nodes;
+ guint32 start_position, end_position, item_width, header_length;
+ seq_analysis_item_t *sai;
+ guint16 first_conv_num = 0;
+ gboolean several_convs = FALSE;
+ gboolean first_packet = TRUE;
+
+ GString *label_string, *empty_line, *separator_line, *tmp_str, *tmp_str2;
+ const char *empty_header;
+ char src_port[8], dst_port[8];
+ GList *list = NULL;
+ char *addr_str;
+
+ display_items = 0;
+ if (sainfo->items != NULL)
+ list = g_queue_peek_nth_link(sainfo->items, 0);
+
+ while (list)
+ {
+ sai = (seq_analysis_item_t *)list->data;
+ list = g_list_next(list);
+
+ if (!sai->display)
+ continue;
+
+ display_items += 1;
+ if (first_packet) {
+ first_conv_num = sai->conv_num;
+ first_packet = FALSE;
+ }
+ else if (sai->conv_num != first_conv_num) {
+ several_convs = TRUE;
+ }
+ }
+
+ /* if not items to display */
+ if (display_items == 0) {
+ return;
+ }
+
+ label_string = g_string_new("");
+ empty_line = g_string_new("");
+ separator_line = g_string_new("");
+ tmp_str = g_string_new("");
+ tmp_str2 = g_string_new("");
+
+ display_nodes = sainfo->num_nodes;
+
+ /* Write the conv. and time headers */
+ if (several_convs) {
+ fprintf(of, CONV_TIME_HEADER);
+ empty_header = CONV_TIME_EMPTY_HEADER;
+ header_length = CONV_TIME_HEADER_LENGTH;
+ }
+ else{
+ fprintf(of, TIME_HEADER);
+ empty_header = TIME_EMPTY_HEADER;
+ header_length = TIME_HEADER_LENGTH;
+ }
+
+ /* Write the node names on top */
+ for (i=0; i<display_nodes; i+=2) {
+ /* print the node identifiers */
+ addr_str = address_to_display(NULL, &(sainfo->nodes[i+first_node]));
+ g_string_printf(label_string, "| %s", addr_str);
+ wmem_free(NULL, addr_str);
+ enlarge_string(label_string, NODE_CHARS_WIDTH*2, ' ');
+ fprintf(of, "%s", label_string->str);
+ g_string_printf(label_string, "| ");
+ enlarge_string(label_string, NODE_CHARS_WIDTH, ' ');
+ g_string_append(empty_line, label_string->str);
+ }
+
+ fprintf(of, "|\n%s", empty_header);
+ g_string_printf(label_string, "| ");
+ enlarge_string(label_string, NODE_CHARS_WIDTH, ' ');
+ fprintf(of, "%s", label_string->str);
+
+ /* Write the node names on top */
+ for (i=1; i<display_nodes; i+=2) {
+ /* print the node identifiers */
+ addr_str = address_to_display(NULL, &(sainfo->nodes[i+first_node]));
+ g_string_printf(label_string, "| %s", addr_str);
+ wmem_free(NULL, addr_str);
+ if (label_string->len < NODE_CHARS_WIDTH)
+ {
+ enlarge_string(label_string, NODE_CHARS_WIDTH, ' ');
+ g_string_append(label_string, "| ");
+ }
+ enlarge_string(label_string, NODE_CHARS_WIDTH*2, ' ');
+ fprintf(of, "%s", label_string->str);
+ g_string_printf(label_string, "| ");
+ enlarge_string(label_string, NODE_CHARS_WIDTH, ' ');
+ g_string_append(empty_line, label_string->str);
+ }
+
+ fprintf(of, "\n");
+
+ g_string_append_c(empty_line, '|');
+
+ enlarge_string(separator_line, (guint32) empty_line->len + header_length, '-');
+
+ /*
+ * Draw the items
+ */
+
+ list = g_queue_peek_nth_link(sainfo->items, 0);
+ while (list)
+ {
+ sai = (seq_analysis_item_t *)list->data;
+ list = g_list_next(list);
+
+ if (!sai->display)
+ continue;
+
+ start_position = (sai->src_node-first_node)*NODE_CHARS_WIDTH+NODE_CHARS_WIDTH/2;
+
+ end_position = (sai->dst_node-first_node)*NODE_CHARS_WIDTH+NODE_CHARS_WIDTH/2;
+
+ if (start_position > end_position) {
+ item_width = start_position-end_position;
+ }
+ else if (start_position < end_position) {
+ item_width = end_position-start_position;
+ }
+ else{ /* same origin and destination address */
+ end_position = start_position+NODE_CHARS_WIDTH;
+ item_width = NODE_CHARS_WIDTH;
+ }
+
+ /* separator between conversations */
+ if (sai->conv_num != first_conv_num) {
+ fprintf(of, "%s\n", separator_line->str);
+ first_conv_num = sai->conv_num;
+ }
+
+ /* write the conversation number */
+ if (several_convs) {
+ g_string_printf(label_string, "%i", sai->conv_num);
+ enlarge_string(label_string, 5, ' ');
+ fprintf(of, "%s", label_string->str);
+ }
+
+ if (sai->time_str != NULL) {
+ g_string_printf(label_string, "|%s", sai->time_str);
+ enlarge_string(label_string, 10, ' ');
+ fprintf(of, "%s", label_string->str);
+ }
+
+ /* write the frame label */
+
+ g_string_printf(tmp_str, "%s", empty_line->str);
+ overwrite(tmp_str, sai->frame_label,
+ start_position,
+ end_position
+ );
+ fprintf(of, "%s", tmp_str->str);
+
+ /* write the comments */
+ fprintf(of, "%s\n", sai->comment);
+
+ /* write the arrow and frame label*/
+ fprintf(of, "%s", empty_header);
+
+ g_string_printf(tmp_str, "%s", empty_line->str);
+
+ g_string_truncate(tmp_str2, 0);
+
+ if (start_position<end_position) {
+ enlarge_string(tmp_str2, item_width-2, '-');
+ g_string_append_c(tmp_str2, '>');
+ }
+ else{
+ g_string_printf(tmp_str2, "<");
+ enlarge_string(tmp_str2, item_width-1, '-');
+ }
+
+ overwrite(tmp_str, tmp_str2->str,
+ start_position,
+ end_position
+ );
+
+ snprintf(src_port, sizeof(src_port), "(%i)", sai->port_src);
+ snprintf(dst_port, sizeof(dst_port), "(%i)", sai->port_dst);
+
+ if (start_position<end_position) {
+ overwrite(tmp_str, src_port, start_position-9, start_position-1);
+ overwrite(tmp_str, dst_port, end_position+1, end_position+9);
+ }
+ else{
+ overwrite(tmp_str, src_port, start_position+1, start_position+9);
+ overwrite(tmp_str, dst_port, end_position-9, end_position+1);
+ }
+
+ fprintf(of, "%s\n", tmp_str->str);
+ }
+
+ g_string_free(label_string, TRUE);
+ g_string_free(empty_line, TRUE);
+ g_string_free(separator_line, TRUE);
+ g_string_free(tmp_str, TRUE);
+ g_string_free(tmp_str2, TRUE);
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */