summaryrefslogtreecommitdiffstats
path: root/epan/packet.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/packet.c')
-rw-r--r--epan/packet.c3931
1 files changed, 3931 insertions, 0 deletions
diff --git a/epan/packet.c b/epan/packet.c
new file mode 100644
index 0000000..c4bcb4d
--- /dev/null
+++ b/epan/packet.c
@@ -0,0 +1,3931 @@
+/* packet.c
+ * Routines for packet disassembly
+ *
+ * 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"
+#define WS_LOG_DOMAIN LOG_DOMAIN_EPAN
+
+#include <glib.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+
+#include "packet.h"
+#include "timestamp.h"
+
+#include "osi-utils.h"
+#include "to_str.h"
+
+#include "addr_resolv.h"
+#include "tvbuff.h"
+#include "epan_dissect.h"
+
+#include <epan/wmem_scopes.h>
+
+#include <epan/column-info.h>
+#include <epan/exceptions.h>
+#include <epan/reassemble.h>
+#include <epan/stream.h>
+#include <epan/expert.h>
+#include <epan/prefs.h>
+#include <epan/range.h>
+
+#include <wsutil/str_util.h>
+#include <wsutil/wslog.h>
+#include <wsutil/ws_assert.h>
+
+static gint proto_malformed = -1;
+static dissector_handle_t frame_handle = NULL;
+static dissector_handle_t file_handle = NULL;
+static dissector_handle_t data_handle = NULL;
+
+/**
+ * A data source.
+ * Has a tvbuff and a name.
+ */
+struct data_source {
+ tvbuff_t *tvb;
+ char *name;
+};
+
+/*
+ * A dissector table.
+ *
+ * "hash_table" is a hash table, indexed by port number, supplying
+ * a "struct dtbl_entry"; it records what dissector is assigned to
+ * that uint or string value in that table.
+ *
+ * "dissector_handles" is a list of all dissectors that *could* be
+ * used in that table; not all of them are necessarily in the table,
+ * as they may be for protocols that don't have a fixed uint value,
+ * e.g. for TCP or UDP port number tables and protocols with no fixed
+ * port number.
+ *
+ * "ui_name" is the name the dissector table has in the user interface.
+ *
+ * "type" is a field type giving the width of the uint value for that
+ * dissector table, if it's a uint dissector table.
+ *
+ * "param" is the base in which to display the uint value for that
+ * dissector table, if it's a uint dissector table, or if it's a string
+ * table, TRUE/FALSE to indicate case-insensitive or not.
+ *
+ * "protocol" is the protocol associated with the dissector table. Used
+ * for determining dependencies.
+ */
+struct dissector_table {
+ GHashTable *hash_table;
+ GSList *dissector_handles;
+ const char *ui_name;
+ ftenum_t type;
+ int param;
+ protocol_t *protocol;
+ GHashFunc hash_func;
+ gboolean supports_decode_as;
+};
+
+/*
+ * Dissector tables. const char * -> dissector_table *
+ */
+static GHashTable *dissector_tables = NULL;
+
+/*
+ * Dissector table aliases. const char * -> const char *
+ */
+static GHashTable *dissector_table_aliases = NULL;
+
+/*
+ * List of registered dissectors.
+ */
+static GHashTable *registered_dissectors = NULL;
+
+/*
+ * A dissector dependency list.
+ */
+struct depend_dissector_list {
+ GSList *dissectors;
+};
+
+/* Maps char *dissector_name to depend_dissector_list_t */
+static GHashTable *depend_dissector_lists = NULL;
+
+/* Allow protocols to register a "cleanup" routine to be
+ * run after the initial sequential run through the packets.
+ * Note that the file can still be open after this; this is not
+ * the final cleanup. */
+static GSList *postseq_cleanup_routines;
+
+/*
+ * Post-dissector information - handle for the dissector and a list
+ * of hfids for the fields the post-dissector wants.
+ */
+typedef struct {
+ dissector_handle_t handle;
+ GArray *wanted_hfids;
+} postdissector;
+
+/*
+ * Array of all postdissectors.
+ */
+static GArray *postdissectors = NULL;
+
+/*
+ * i-th element of that array.
+ */
+#define POSTDISSECTORS(i) g_array_index(postdissectors, postdissector, i)
+
+static void
+destroy_depend_dissector_list(void *data)
+{
+ depend_dissector_list_t dissector_list = (depend_dissector_list_t)data;
+ GSList **list = &(dissector_list->dissectors);
+
+ g_slist_free_full(*list, g_free);
+ g_slice_free(struct depend_dissector_list, dissector_list);
+}
+
+/*
+ * A heuristics dissector list.
+ */
+struct heur_dissector_list {
+ protocol_t *protocol;
+ GSList *dissectors;
+};
+
+static GHashTable *heur_dissector_lists = NULL;
+
+/* Name hashtables for fast detection of duplicate names */
+static GHashTable* heuristic_short_names = NULL;
+
+static void
+destroy_heuristic_dissector_entry(gpointer data)
+{
+ heur_dtbl_entry_t *hdtbl_entry = (heur_dtbl_entry_t *)data;
+ g_free(hdtbl_entry->list_name);
+ g_free(hdtbl_entry->short_name);
+ g_slice_free(heur_dtbl_entry_t, data);
+}
+
+static void
+destroy_heuristic_dissector_list(void *data)
+{
+ heur_dissector_list_t dissector_list = (heur_dissector_list_t)data;
+ GSList **list = &(dissector_list->dissectors);
+
+ g_slist_free_full(*list, destroy_heuristic_dissector_entry);
+ g_slice_free(struct heur_dissector_list, dissector_list);
+}
+
+static void
+destroy_dissector_table(void *data)
+{
+ struct dissector_table *table = (struct dissector_table *)data;
+
+ g_hash_table_destroy(table->hash_table);
+ g_slist_free(table->dissector_handles);
+ g_slice_free(struct dissector_table, data);
+}
+
+void
+packet_init(void)
+{
+ dissector_tables = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, destroy_dissector_table);
+
+ dissector_table_aliases = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, NULL);
+
+ registered_dissectors = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, NULL);
+
+ depend_dissector_lists = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, destroy_depend_dissector_list);
+
+ heur_dissector_lists = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, destroy_heuristic_dissector_list);
+
+ heuristic_short_names = g_hash_table_new(g_str_hash, g_str_equal);
+}
+
+void
+packet_cache_proto_handles(void)
+{
+ frame_handle = find_dissector("frame");
+ ws_assert(frame_handle != NULL);
+
+ file_handle = find_dissector("file");
+ ws_assert(file_handle != NULL);
+
+ data_handle = find_dissector("data");
+ ws_assert(data_handle != NULL);
+
+ proto_malformed = proto_get_id_by_filter_name("_ws.malformed");
+ ws_assert(proto_malformed != -1);
+}
+
+/* List of routines that are called before we make a pass through a capture file
+ * and dissect all its packets. See register_init_routine, register_cleanup_routine
+ * and register_shutdown_routine in packet.h */
+/**
+ * List of "init" routines, which are called before we make a pass through
+ * a capture file and dissect all its packets (e.g., when we read in a
+ * new capture file, or run a "filter packets" or "colorize packets"
+ * pass over the current capture file or when the preferences are changed).
+ *
+ * See register_init_routine().
+ */
+static GSList *init_routines = NULL;
+
+/**
+ * List of "cleanup" routines, which are called after closing a capture
+ * file (or when preferences are changed; in that case these routines
+ * are called before the init routines are executed). They can be used
+ * to release resources that are allocated in an "init" routine.
+ *
+ * See register_cleanup_routine().
+ */
+static GSList *cleanup_routines = NULL;
+
+/*
+ * List of "shutdown" routines, which are called once, just before
+ * program exit.
+ *
+ * See register_shutdown_routine().
+ */
+static GSList *shutdown_routines = NULL;
+
+typedef void (*void_func_t)(void);
+
+/* Initialize all data structures used for dissection. */
+static void
+call_routine(gpointer routine, gpointer dummy _U_)
+{
+ void_func_t func = (void_func_t)routine;
+ (*func)();
+}
+
+void
+packet_cleanup(void)
+{
+ g_slist_free(init_routines);
+ g_slist_free(cleanup_routines);
+ g_slist_free(postseq_cleanup_routines);
+ g_hash_table_destroy(dissector_tables);
+ g_hash_table_destroy(dissector_table_aliases);
+ g_hash_table_destroy(registered_dissectors);
+ g_hash_table_destroy(depend_dissector_lists);
+ g_hash_table_destroy(heur_dissector_lists);
+ g_hash_table_destroy(heuristic_short_names);
+ g_slist_foreach(shutdown_routines, &call_routine, NULL);
+ g_slist_free(shutdown_routines);
+ if (postdissectors) {
+ for (guint i = 0; i < postdissectors->len; i++) {
+ if (POSTDISSECTORS(i).wanted_hfids) {
+ g_array_free(POSTDISSECTORS(i).wanted_hfids, TRUE);
+ }
+ }
+ g_array_free(postdissectors, TRUE);
+ }
+}
+
+/*
+ * Given a tvbuff, and a length from a packet header, adjust the length
+ * of the tvbuff to reflect the specified length.
+ */
+void
+set_actual_length(tvbuff_t *tvb, const guint specified_len)
+{
+ if (specified_len < tvb_reported_length(tvb)) {
+ /* Adjust the length of this tvbuff to include only the specified
+ payload length.
+
+ The dissector above the one calling us (the dissector above is
+ probably us) may use that to determine how much of its packet
+ was padding. */
+ tvb_set_reported_length(tvb, specified_len);
+ }
+}
+
+void
+register_init_routine(void (*func)(void))
+{
+ init_routines = g_slist_prepend(init_routines, (gpointer)func);
+}
+
+void
+register_cleanup_routine(void (*func)(void))
+{
+ cleanup_routines = g_slist_prepend(cleanup_routines, (gpointer)func);
+}
+
+/* register a new shutdown routine */
+void
+register_shutdown_routine(void (*func)(void))
+{
+ shutdown_routines = g_slist_prepend(shutdown_routines, (gpointer)func);
+}
+
+/* Initialize all data structures used for dissection. */
+void
+init_dissection(void)
+{
+ /*
+ * Reinitialize resolution information. Don't leak host entries from
+ * one file to another (e.g. embarassing-host-name.example.com from
+ * file1.pcapng into a name resolution block in file2.pcapng).
+ */
+ host_name_lookup_reset();
+
+ wmem_enter_file_scope();
+
+ /* Initialize the table of conversations. */
+ epan_conversation_init();
+
+ /* Initialize protocol-specific variables. */
+ g_slist_foreach(init_routines, &call_routine, NULL);
+
+ /* Initialize the stream-handling tables */
+ stream_init();
+
+ /* Initialize the expert infos */
+ expert_packet_init();
+}
+
+void
+cleanup_dissection(void)
+{
+ /* Cleanup protocol-specific variables. */
+ g_slist_foreach(cleanup_routines, &call_routine, NULL);
+
+ /* Cleanup the stream-handling tables */
+ stream_cleanup();
+
+ /* Cleanup the expert infos */
+ expert_packet_cleanup();
+
+ wmem_leave_file_scope();
+
+ /*
+ * Keep the name resolution info around until we start the next
+ * dissection. Lua scripts may potentially do name resolution at
+ * any time, even if we're not dissecting and have no capture
+ * file open.
+ */
+}
+
+void
+register_postseq_cleanup_routine(void_func_t func)
+{
+ postseq_cleanup_routines = g_slist_prepend(postseq_cleanup_routines,
+ (gpointer)func);
+}
+
+/* Call all the registered "postseq_cleanup" routines. */
+void
+postseq_cleanup_all_protocols(void)
+{
+ g_slist_foreach(postseq_cleanup_routines,
+ &call_routine, NULL);
+}
+
+/*
+ * Add a new data source to the list of data sources for a frame, given
+ * the tvbuff for the data source and its name.
+ */
+void
+add_new_data_source(packet_info *pinfo, tvbuff_t *tvb, const char *name)
+{
+ struct data_source *src;
+
+ src = wmem_new(pinfo->pool, struct data_source);
+ src->tvb = tvb;
+ src->name = wmem_strdup(pinfo->pool, name);
+ /* This could end up slow, but we should never have that many data
+ * sources so it probably doesn't matter */
+ pinfo->data_src = g_slist_append(pinfo->data_src, src);
+}
+
+void
+remove_last_data_source(packet_info *pinfo)
+{
+ GSList *last;
+
+ last = g_slist_last(pinfo->data_src);
+ pinfo->data_src = g_slist_delete_link(pinfo->data_src, last);
+}
+
+char*
+get_data_source_name(const struct data_source *src)
+{
+ guint length = tvb_captured_length(src->tvb);
+
+ return wmem_strdup_printf(NULL, "%s (%u byte%s)", src->name, length,
+ plurality(length, "", "s"));
+}
+
+tvbuff_t *
+get_data_source_tvb(const struct data_source *src)
+{
+ return src->tvb;
+}
+
+/*
+ * Find and return the tvb associated with the given data source name
+ */
+tvbuff_t *
+get_data_source_tvb_by_name(packet_info *pinfo, const char *name)
+{
+ GSList *source;
+ for (source = pinfo->data_src; source; source = source->next) {
+ struct data_source *this_source = (struct data_source *)source->data;
+ if (this_source->name && strcmp(this_source->name, name) == 0) {
+ return this_source->tvb;
+ }
+ }
+ return NULL;
+}
+
+
+/*
+ * Free up a frame's list of data sources.
+ */
+void
+free_data_sources(packet_info *pinfo)
+{
+ if (pinfo->data_src) {
+ g_slist_free(pinfo->data_src);
+ pinfo->data_src = NULL;
+ }
+}
+
+void
+mark_frame_as_depended_upon(frame_data *fd, guint32 frame_num)
+{
+ /* Don't mark a frame as dependent on itself */
+ if (frame_num != fd->num) {
+ /* ws_assert(frame_num < fd->num) - we assume in several other
+ * places in the code that frames don't depend on future
+ * frames. */
+ if (fd->dependent_frames == NULL) {
+ fd->dependent_frames = g_hash_table_new(g_direct_hash, g_direct_equal);
+ }
+ g_hash_table_add(fd->dependent_frames, GUINT_TO_POINTER(frame_num));
+ }
+}
+
+/* Allow dissectors to register a "final_registration" routine
+ * that is run like the proto_register_XXX() routine, but at the
+ * end of the epan_init() function; that is, *after* all other
+ * subsystems, like dfilters, have finished initializing. This is
+ * useful for dissector registration routines which need to compile
+ * display filters. dfilters can't initialize itself until all protocols
+ * have registered themselves. */
+static GSList *final_registration_routines;
+
+void
+register_final_registration_routine(void (*func)(void))
+{
+ final_registration_routines = g_slist_prepend(final_registration_routines,
+ (gpointer)func);
+}
+
+/* Call all the registered "final_registration" routines. */
+void
+final_registration_all_protocols(void)
+{
+ g_slist_foreach(final_registration_routines,
+ &call_routine, NULL);
+}
+
+
+/* Creates the top-most tvbuff and calls dissect_frame() */
+void
+dissect_record(epan_dissect_t *edt, int file_type_subtype,
+ wtap_rec *rec, tvbuff_t *tvb, frame_data *fd, column_info *cinfo)
+{
+ const char *volatile record_type;
+ frame_data_t frame_dissector_data;
+
+ switch (rec->rec_type) {
+
+ case REC_TYPE_PACKET:
+ record_type = "Frame";
+ break;
+
+ case REC_TYPE_FT_SPECIFIC_EVENT:
+ record_type = "Event";
+ break;
+
+ case REC_TYPE_FT_SPECIFIC_REPORT:
+ record_type = "Report";
+ break;
+
+ case REC_TYPE_SYSCALL:
+ record_type = "System Call";
+ break;
+
+ case REC_TYPE_SYSTEMD_JOURNAL_EXPORT:
+ record_type = "Systemd Journal Entry";
+ break;
+
+ case REC_TYPE_CUSTOM_BLOCK:
+ switch (rec->rec_header.custom_block_header.pen) {
+ case PEN_NFLX:
+ record_type = "Black Box Log Block";
+ break;
+ default:
+ record_type = "PCAPNG Custom Block";
+ break;
+ }
+ break;
+
+ default:
+ /*
+ * XXX - if we add record types that shouldn't be
+ * dissected and displayed, but that need to at
+ * least be processed somewhere, we need to somehow
+ * indicate that to our caller.
+ */
+ ws_assert_not_reached();
+ break;
+ }
+
+ if (cinfo != NULL)
+ col_init(cinfo, edt->session);
+ edt->pi.epan = edt->session;
+ /* edt->pi.pool created in epan_dissect_init() */
+ edt->pi.current_proto = "<Missing Protocol Name>";
+ edt->pi.cinfo = cinfo;
+ edt->pi.presence_flags = 0;
+ edt->pi.num = fd->num;
+ /*
+ * XXX - this doesn't check the wtap_rec because, for
+ * some capture files, time stamps are supplied only
+ * when reading sequentially, so we keep the time stamp
+ * in the frame_data structure.
+ */
+ if (fd->has_ts) {
+ edt->pi.presence_flags |= PINFO_HAS_TS;
+ edt->pi.abs_ts = fd->abs_ts;
+ }
+ switch (rec->rec_type) {
+
+ case REC_TYPE_PACKET:
+ edt->pi.pseudo_header = &rec->rec_header.packet_header.pseudo_header;
+ break;
+
+ case REC_TYPE_FT_SPECIFIC_EVENT:
+ case REC_TYPE_FT_SPECIFIC_REPORT:
+ edt->pi.pseudo_header = NULL;
+ break;
+
+ case REC_TYPE_SYSCALL:
+ edt->pi.pseudo_header = NULL;
+ break;
+
+ case REC_TYPE_SYSTEMD_JOURNAL_EXPORT:
+ edt->pi.pseudo_header = NULL;
+ break;
+
+ case REC_TYPE_CUSTOM_BLOCK:
+ switch (rec->rec_header.custom_block_header.pen) {
+ case PEN_NFLX:
+ edt->pi.pseudo_header = NULL;
+ break;
+ default:
+ edt->pi.pseudo_header = NULL;
+ break;
+ }
+ break;
+
+ }
+
+ edt->pi.fd = fd;
+ edt->pi.rec = rec;
+ clear_address(&edt->pi.dl_src);
+ clear_address(&edt->pi.dl_dst);
+ clear_address(&edt->pi.net_src);
+ clear_address(&edt->pi.net_dst);
+ clear_address(&edt->pi.src);
+ clear_address(&edt->pi.dst);
+ edt->pi.noreassembly_reason = "";
+ edt->pi.ptype = PT_NONE;
+ edt->pi.use_conv_addr_port_endpoints = FALSE;
+ edt->pi.conv_addr_port_endpoints = NULL;
+ edt->pi.conv_elements = NULL;
+ edt->pi.p2p_dir = P2P_DIR_UNKNOWN;
+ edt->pi.link_dir = LINK_DIR_UNKNOWN;
+ edt->pi.src_win_scale = -1; /* unknown Rcv.Wind.Shift */
+ edt->pi.dst_win_scale = -1; /* unknown Rcv.Wind.Shift */
+ edt->pi.layers = wmem_list_new(edt->pi.pool);
+ edt->tvb = tvb;
+
+ frame_delta_abs_time(edt->session, fd, fd->frame_ref_num, &edt->pi.rel_ts);
+
+ if (rec->ts_rel_cap_valid) {
+ nstime_copy(&edt->pi.rel_cap_ts, &rec->ts_rel_cap);
+ edt->pi.rel_cap_ts_present = true;
+ }
+
+ /*
+ * If the block has been modified, use the modified block,
+ * otherwise use the block from the file.
+ */
+ if (fd->has_modified_block) {
+ frame_dissector_data.pkt_block = epan_get_modified_block(edt->session, fd);
+ }
+ else {
+ frame_dissector_data.pkt_block = rec->block;
+ }
+ frame_dissector_data.file_type_subtype = file_type_subtype;
+ frame_dissector_data.color_edt = edt; /* Used strictly for "coloring rules" */
+
+ TRY {
+ /* Add this tvbuffer into the data_src list */
+ add_new_data_source(&edt->pi, edt->tvb, record_type);
+
+ /* Even though dissect_frame() catches all the exceptions a
+ * sub-dissector can throw, dissect_frame() itself may throw
+ * a ReportedBoundsError in bizarre cases. Thus, we catch the exception
+ * in this function. */
+ call_dissector_with_data(frame_handle, edt->tvb, &edt->pi, edt->tree, &frame_dissector_data);
+ }
+ CATCH(BoundsError) {
+ ws_assert_not_reached();
+ }
+ CATCH2(FragmentBoundsError, ReportedBoundsError) {
+ proto_tree_add_protocol_format(edt->tree, proto_malformed, edt->tvb, 0, 0,
+ "[Malformed %s: Packet Length]",
+ record_type);
+ }
+ ENDTRY;
+ wtap_block_unref(rec->block);
+ rec->block = NULL;
+
+ fd->visited = 1;
+}
+
+/* Creates the top-most tvbuff and calls dissect_file() */
+void
+dissect_file(epan_dissect_t *edt, wtap_rec *rec,
+ tvbuff_t *tvb, frame_data *fd, column_info *cinfo)
+{
+ file_data_t file_dissector_data;
+
+ if (cinfo != NULL)
+ col_init(cinfo, edt->session);
+ edt->pi.epan = edt->session;
+ /* edt->pi.pool created in epan_dissect_init() */
+ edt->pi.current_proto = "<Missing Filetype Name>";
+ edt->pi.cinfo = cinfo;
+ edt->pi.fd = fd;
+ edt->pi.rec = rec;
+ edt->pi.pseudo_header = NULL;
+ clear_address(&edt->pi.dl_src);
+ clear_address(&edt->pi.dl_dst);
+ clear_address(&edt->pi.net_src);
+ clear_address(&edt->pi.net_dst);
+ clear_address(&edt->pi.src);
+ clear_address(&edt->pi.dst);
+ edt->pi.noreassembly_reason = "";
+ edt->pi.ptype = PT_NONE;
+ edt->pi.use_conv_addr_port_endpoints = FALSE;
+ edt->pi.conv_addr_port_endpoints = NULL;
+ edt->pi.conv_elements = NULL;
+ edt->pi.p2p_dir = P2P_DIR_UNKNOWN;
+ edt->pi.link_dir = LINK_DIR_UNKNOWN;
+ edt->pi.layers = wmem_list_new(edt->pi.pool);
+ edt->tvb = tvb;
+
+
+ frame_delta_abs_time(edt->session, fd, fd->frame_ref_num, &edt->pi.rel_ts);
+
+
+ TRY {
+ /*
+ * If the block has been modified, use the modified block,
+ * otherwise use the block from the file.
+ */
+ if (fd->has_modified_block) {
+ file_dissector_data.pkt_block = epan_get_modified_block(edt->session, fd);
+ }
+ else {
+ file_dissector_data.pkt_block = rec->block;
+ }
+ file_dissector_data.color_edt = edt; /* Used strictly for "coloring rules" */
+
+
+ /* Add this tvbuffer into the data_src list */
+ add_new_data_source(&edt->pi, edt->tvb, "File");
+
+ /* Even though dissect_file() catches all the exceptions a
+ * sub-dissector can throw, dissect_frame() itself may throw
+ * a ReportedBoundsError in bizarre cases. Thus, we catch the exception
+ * in this function. */
+ call_dissector_with_data(file_handle, edt->tvb, &edt->pi, edt->tree, &file_dissector_data);
+
+ }
+ CATCH(BoundsError) {
+ ws_assert_not_reached();
+ }
+ CATCH3(FragmentBoundsError, ContainedBoundsError, ReportedBoundsError) {
+ proto_tree_add_protocol_format(edt->tree, proto_malformed, edt->tvb, 0, 0,
+ "[Malformed Record: Packet Length]");
+ }
+ ENDTRY;
+ wtap_block_unref(rec->block);
+ rec->block = NULL;
+
+ fd->visited = 1;
+}
+
+/*********************** code added for sub-dissector lookup *********************/
+
+enum dissector_e {
+ DISSECTOR_TYPE_SIMPLE,
+ DISSECTOR_TYPE_CALLBACK
+};
+
+/*
+ * A dissector handle.
+ */
+struct dissector_handle {
+ const char *name; /* dissector name */
+ const char *description; /* dissector description */
+ enum dissector_e dissector_type;
+ void *dissector_func;
+ void *dissector_data;
+ protocol_t *protocol;
+};
+
+static void
+add_layer(packet_info *pinfo, int proto_id)
+{
+ int *proto_layer_num_ptr;
+
+ pinfo->curr_layer_num++;
+ wmem_list_append(pinfo->layers, GINT_TO_POINTER(proto_id));
+
+ /* Increment layer number for this proto id. */
+ if (pinfo->proto_layers == NULL) {
+ pinfo->proto_layers = wmem_map_new(pinfo->pool, g_direct_hash, g_direct_equal);
+ }
+
+ proto_layer_num_ptr = wmem_map_lookup(pinfo->proto_layers, GINT_TO_POINTER(proto_id));
+ if (proto_layer_num_ptr == NULL) {
+ /* Insert new layer */
+ proto_layer_num_ptr = wmem_new(pinfo->pool, int);
+ *proto_layer_num_ptr = 1;
+ wmem_map_insert(pinfo->proto_layers, GINT_TO_POINTER(proto_id), proto_layer_num_ptr);
+ }
+ else {
+ /* Increment layer number */
+ (*proto_layer_num_ptr)++;
+ }
+ pinfo->curr_proto_layer_num = *proto_layer_num_ptr;
+}
+
+static void
+remove_last_layer(packet_info *pinfo, gboolean reduce_count)
+{
+ int *proto_layer_num_ptr;
+ wmem_list_frame_t *frame;
+ int proto_id;
+
+ if (reduce_count) {
+ pinfo->curr_layer_num--;
+ }
+
+ frame = wmem_list_tail(pinfo->layers);
+ proto_id = GPOINTER_TO_INT(wmem_list_frame_data(frame));
+ wmem_list_remove_frame(pinfo->layers, frame);
+
+ if (reduce_count) {
+ /* Reduce count for removed protocol layer. */
+ proto_layer_num_ptr = wmem_map_lookup(pinfo->proto_layers, GINT_TO_POINTER(proto_id));
+ if (proto_layer_num_ptr && *proto_layer_num_ptr > 0) {
+ (*proto_layer_num_ptr)--;
+ }
+ }
+
+ /* Restore count for new last (protocol) layer. */
+ frame = wmem_list_tail(pinfo->layers);
+ if (frame) {
+ proto_id = GPOINTER_TO_INT(wmem_list_frame_data(frame));
+ proto_layer_num_ptr = wmem_map_lookup(pinfo->proto_layers, GINT_TO_POINTER(proto_id));
+ ws_assert(proto_layer_num_ptr);
+ pinfo->curr_proto_layer_num = *proto_layer_num_ptr;
+ }
+}
+
+
+/* This function will return
+ * old style dissector :
+ * length of the payload or 1 of the payload is empty
+ * new dissector :
+ * >0 this protocol was successfully dissected and this was this protocol.
+ * 0 this packet did not match this protocol.
+ *
+ * The only time this function will return 0 is if it is a new style dissector
+ * and if the dissector rejected the packet.
+ */
+static int
+call_dissector_through_handle(dissector_handle_t handle, tvbuff_t *tvb,
+ packet_info *pinfo, proto_tree *tree, void *data)
+{
+ const char *saved_proto;
+ int len;
+
+ saved_proto = pinfo->current_proto;
+
+ if ((handle->protocol != NULL) && (!proto_is_pino(handle->protocol))) {
+ pinfo->current_proto =
+ proto_get_protocol_short_name(handle->protocol);
+ }
+
+ if (handle->dissector_type == DISSECTOR_TYPE_SIMPLE) {
+ len = ((dissector_t)handle->dissector_func)(tvb, pinfo, tree, data);
+ }
+ else if (handle->dissector_type == DISSECTOR_TYPE_CALLBACK) {
+ len = ((dissector_cb_t)handle->dissector_func)(tvb, pinfo, tree, data, handle->dissector_data);
+ }
+ else {
+ ws_assert_not_reached();
+ }
+ pinfo->current_proto = saved_proto;
+
+ return len;
+}
+
+/*
+ * Call a dissector through a handle.
+ * If the protocol for that handle isn't enabled, return 0 without
+ * calling the dissector.
+ * Otherwise, if the handle refers to a new-style dissector, call the
+ * dissector and return its return value, otherwise call it and return
+ * the length of the tvbuff pointed to by the argument.
+ */
+
+static int
+call_dissector_work_error(dissector_handle_t handle, tvbuff_t *tvb,
+ packet_info *pinfo_arg, proto_tree *tree, void *);
+
+/*
+ * XXX packet_info.curr_layer_num is a guint8 and *_MAX_RECURSION_DEPTH is
+ * 100 elsewhere in the code. We should arguably use the same value here,
+ * but using that makes suite_wslua.case_wslua.test_wslua_dissector_fpm fail.
+ */
+#define PINFO_LAYER_MAX_RECURSION_DEPTH 500
+
+static int
+call_dissector_work(dissector_handle_t handle, tvbuff_t *tvb, packet_info *pinfo,
+ proto_tree *tree, gboolean add_proto_name, void *data)
+{
+ const char *saved_proto;
+ guint16 saved_can_desegment;
+ int len;
+ guint saved_layers_len = 0;
+ guint saved_tree_count = tree ? tree->tree_data->count : 0;
+
+ if (handle->protocol != NULL &&
+ !proto_is_protocol_enabled(handle->protocol)) {
+ /*
+ * The protocol isn't enabled.
+ */
+ return 0;
+ }
+
+ saved_proto = pinfo->current_proto;
+ saved_can_desegment = pinfo->can_desegment;
+ saved_layers_len = wmem_list_count(pinfo->layers);
+ DISSECTOR_ASSERT(saved_layers_len < PINFO_LAYER_MAX_RECURSION_DEPTH);
+
+ /*
+ * can_desegment is set to 2 by anyone which offers the
+ * desegmentation api/service.
+ * Then everytime a subdissector is called it is decremented
+ * by one.
+ * Thus only the subdissector immediately on top of whoever
+ * offers this service can use it.
+ * We save the current value of "can_desegment" for the
+ * benefit of TCP proxying dissectors such as SOCKS, so they
+ * can restore it and allow the dissectors they call to use
+ * the desegmentation service.
+ */
+ pinfo->saved_can_desegment = saved_can_desegment;
+ pinfo->can_desegment = saved_can_desegment-(saved_can_desegment>0);
+ if ((handle->protocol != NULL) && (!proto_is_pino(handle->protocol))) {
+ pinfo->current_proto =
+ proto_get_protocol_short_name(handle->protocol);
+
+ /*
+ * Add the protocol name to the layers only if told to
+ * do so. Asn2wrs generated dissectors may be added
+ * multiple times otherwise.
+ */
+ /* XXX Should we check for a duplicate layer here? */
+ if (add_proto_name) {
+ add_layer(pinfo, proto_get_id(handle->protocol));
+ }
+ }
+
+ if (pinfo->flags.in_error_pkt) {
+ len = call_dissector_work_error(handle, tvb, pinfo, tree, data);
+ } else {
+ /*
+ * Just call the subdissector.
+ */
+ len = call_dissector_through_handle(handle, tvb, pinfo, tree, data);
+ }
+ if (handle->protocol != NULL && !proto_is_pino(handle->protocol) && add_proto_name &&
+ (len == 0 || (tree && saved_tree_count == tree->tree_data->count))) {
+ /*
+ * We've added a layer and either the dissector didn't
+ * accept the packet or we didn't add any items to the
+ * tree. Remove it.
+ */
+ while (wmem_list_count(pinfo->layers) > saved_layers_len) {
+ /*
+ * Only reduce the layer number if the dissector
+ * rejected the data. Since tree can be NULL on
+ * the first pass, we cannot check it or it will
+ * break dissectors that rely on a stable value.
+ */
+ remove_last_layer(pinfo, len == 0);
+ }
+ }
+ pinfo->current_proto = saved_proto;
+ pinfo->can_desegment = saved_can_desegment;
+ return len;
+}
+
+
+static int
+call_dissector_work_error(dissector_handle_t handle, tvbuff_t *tvb,
+ packet_info *pinfo_arg, proto_tree *tree, void *data)
+{
+ packet_info *pinfo = pinfo_arg;
+ const char *saved_proto;
+ guint16 saved_can_desegment;
+ volatile int len = 0;
+ gboolean save_writable;
+ address save_dl_src;
+ address save_dl_dst;
+ address save_net_src;
+ address save_net_dst;
+ address save_src;
+ address save_dst;
+
+ /*
+ * This isn't a packet being transported inside
+ * the protocol whose dissector is calling us,
+ * it's a copy of a packet that caused an error
+ * in some protocol included in a packet that
+ * reports the error (e.g., an ICMP Unreachable
+ * packet).
+ */
+
+ /*
+ * Save the current state of the writability of
+ * the columns, and restore them after the
+ * dissector returns, so that the columns
+ * don't reflect the packet that got the error,
+ * they reflect the packet that reported the
+ * error.
+ */
+ saved_proto = pinfo->current_proto;
+ saved_can_desegment = pinfo->can_desegment;
+
+ save_writable = col_get_writable(pinfo->cinfo, -1);
+ col_set_writable(pinfo->cinfo, -1, FALSE);
+ copy_address_shallow(&save_dl_src, &pinfo->dl_src);
+ copy_address_shallow(&save_dl_dst, &pinfo->dl_dst);
+ copy_address_shallow(&save_net_src, &pinfo->net_src);
+ copy_address_shallow(&save_net_dst, &pinfo->net_dst);
+ copy_address_shallow(&save_src, &pinfo->src);
+ copy_address_shallow(&save_dst, &pinfo->dst);
+
+ /* Dissect the contained packet. */
+ TRY {
+ len = call_dissector_through_handle(handle, tvb,pinfo, tree, data);
+ }
+ CATCH(BoundsError) {
+ /*
+ * Restore the column writability and addresses.
+ */
+ col_set_writable(pinfo->cinfo, -1, save_writable);
+ copy_address_shallow(&pinfo->dl_src, &save_dl_src);
+ copy_address_shallow(&pinfo->dl_dst, &save_dl_dst);
+ copy_address_shallow(&pinfo->net_src, &save_net_src);
+ copy_address_shallow(&pinfo->net_dst, &save_net_dst);
+ copy_address_shallow(&pinfo->src, &save_src);
+ copy_address_shallow(&pinfo->dst, &save_dst);
+
+ /*
+ * Restore the current protocol, so any
+ * "Short Frame" indication reflects that
+ * protocol, not the protocol for the
+ * packet that got the error.
+ */
+ pinfo->current_proto = saved_proto;
+
+ /*
+ * Restore the desegmentability state.
+ */
+ pinfo->can_desegment = saved_can_desegment;
+
+ /*
+ * Rethrow the exception, so this will be
+ * reported as a short frame.
+ */
+ RETHROW;
+ }
+ CATCH3(FragmentBoundsError, ContainedBoundsError, ReportedBoundsError) {
+ /*
+ * "ret" wasn't set because an exception was thrown
+ * before "call_dissector_through_handle()" returned.
+ * As it called something, at least one dissector
+ * accepted the packet, and, as an exception was
+ * thrown, not only was all the tvbuff dissected,
+ * a dissector tried dissecting past the end of
+ * the data in some tvbuff, so we'll assume that
+ * the entire tvbuff was dissected.
+ */
+ len = tvb_captured_length(tvb);
+ }
+ ENDTRY;
+
+ col_set_writable(pinfo->cinfo, -1, save_writable);
+ copy_address_shallow(&pinfo->dl_src, &save_dl_src);
+ copy_address_shallow(&pinfo->dl_dst, &save_dl_dst);
+ copy_address_shallow(&pinfo->net_src, &save_net_src);
+ copy_address_shallow(&pinfo->net_dst, &save_net_dst);
+ copy_address_shallow(&pinfo->src, &save_src);
+ copy_address_shallow(&pinfo->dst, &save_dst);
+ pinfo->want_pdu_tracking = 0;
+ return len;
+}
+
+/*
+ * An entry in the hash table portion of a dissector table.
+ */
+struct dtbl_entry {
+ dissector_handle_t initial;
+ dissector_handle_t current;
+};
+
+/* Finds a dissector table by table name. */
+dissector_table_t
+find_dissector_table(const char *name)
+{
+ dissector_table_t dissector_table = (dissector_table_t) g_hash_table_lookup(dissector_tables, name);
+ if (! dissector_table) {
+ const char *new_name = (const char *) g_hash_table_lookup(dissector_table_aliases, name);
+ if (new_name) {
+ dissector_table = (dissector_table_t) g_hash_table_lookup(dissector_tables, new_name);
+ }
+ if (dissector_table) {
+ ws_warning("%s is now %s", name, new_name);
+ }
+ }
+ return dissector_table;
+}
+
+/* Find an entry in a uint dissector table. */
+static dtbl_entry_t *
+find_uint_dtbl_entry(dissector_table_t sub_dissectors, const guint32 pattern)
+{
+ switch (sub_dissectors->type) {
+
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ /*
+ * You can do a uint lookup in these tables.
+ */
+ break;
+ case FT_NONE:
+ /* For now treat as uint */
+ break;
+
+ default:
+ /*
+ * But you can't do a uint lookup in any other types
+ * of tables.
+ */
+ ws_assert_not_reached();
+ }
+
+ /*
+ * Find the entry.
+ */
+ return (dtbl_entry_t *)g_hash_table_lookup(sub_dissectors->hash_table,
+ GUINT_TO_POINTER(pattern));
+}
+
+#if 0
+static void
+dissector_add_uint_sanity_check(const char *name, guint32 pattern, dissector_handle_t handle, dissector_table_t sub_dissectors)
+{
+ dtbl_entry_t *dtbl_entry;
+
+ if (pattern == 0) {
+ ws_warning("%s: %s registering using a pattern of 0",
+ name, proto_get_protocol_filter_name(proto_get_id(handle->protocol)));
+ }
+
+ dtbl_entry = g_hash_table_lookup(sub_dissectors->hash_table, GUINT_TO_POINTER(pattern));
+ if (dtbl_entry != NULL) {
+ ws_warning("%s: %s registering using pattern %d already registered by %s",
+ name, proto_get_protocol_filter_name(proto_get_id(handle->protocol)),
+ pattern, proto_get_protocol_filter_name(proto_get_id(dtbl_entry->initial->protocol)));
+ }
+}
+#endif
+
+/* Add an entry to a uint dissector table. */
+void
+dissector_add_uint(const char *name, const guint32 pattern, dissector_handle_t handle)
+{
+ dissector_table_t sub_dissectors;
+ dtbl_entry_t *dtbl_entry;
+
+ sub_dissectors = find_dissector_table(name);
+
+ /*
+ * Make sure the handle and the dissector table exist.
+ */
+ if (handle == NULL) {
+ fprintf(stderr, "OOPS: handle to register \"%s\" to doesn't exist\n",
+ name);
+ if (wireshark_abort_on_dissector_bug)
+ abort();
+ return;
+ }
+ if (sub_dissectors == NULL) {
+ fprintf(stderr, "OOPS: dissector table \"%s\" doesn't exist\n",
+ name);
+ fprintf(stderr, "Protocol being registered is \"%s\"\n",
+ proto_get_protocol_long_name(handle->protocol));
+ if (wireshark_abort_on_dissector_bug)
+ abort();
+ return;
+ }
+
+ switch (sub_dissectors->type) {
+
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ /*
+ * You can do a uint lookup in these tables.
+ */
+ break;
+
+ default:
+ /*
+ * But you can't do a uint lookup in any other types
+ * of tables.
+ */
+ ws_assert_not_reached();
+ }
+
+#if 0
+ dissector_add_uint_sanity_check(name, pattern, handle, sub_dissectors);
+#endif
+
+ dtbl_entry = g_new(dtbl_entry_t, 1);
+ dtbl_entry->current = handle;
+ dtbl_entry->initial = dtbl_entry->current;
+
+ /* do the table insertion */
+ g_hash_table_insert(sub_dissectors->hash_table,
+ GUINT_TO_POINTER(pattern), (gpointer)dtbl_entry);
+
+ /*
+ * Now, if this table supports "Decode As", add this handle
+ * to the list of handles that could be used for "Decode As"
+ * with this table, because it *is* being used with this table.
+ */
+ if (sub_dissectors->supports_decode_as)
+ dissector_add_for_decode_as(name, handle);
+}
+
+
+
+void dissector_add_uint_range(const char *name, range_t *range,
+ dissector_handle_t handle)
+{
+ dissector_table_t sub_dissectors;
+ guint32 i, j;
+
+ if (range) {
+ if (range->nranges == 0) {
+ /*
+ * Even an empty range would want a chance for
+ * Decode As, if the dissector table supports
+ * it.
+ */
+ sub_dissectors = find_dissector_table(name);
+ if (sub_dissectors->supports_decode_as)
+ dissector_add_for_decode_as(name, handle);
+ }
+ else {
+ for (i = 0; i < range->nranges; i++) {
+ for (j = range->ranges[i].low; j < range->ranges[i].high; j++)
+ dissector_add_uint(name, j, handle);
+ dissector_add_uint(name, range->ranges[i].high, handle);
+ }
+ }
+ }
+}
+
+static range_t*
+dissector_add_range_preference(const char *name, dissector_handle_t handle, const char* range_str)
+{
+ range_t** range;
+ module_t *module;
+ gchar *description, *title;
+ dissector_table_t pref_dissector_table = find_dissector_table(name);
+ int proto_id = proto_get_id(handle->protocol);
+ guint32 max_value = 0;
+
+ /* If a dissector is added for Decode As only, it's dissector
+ table value would default to 0.
+ Set up a preference value with that information
+ */
+ range = wmem_new0(wmem_epan_scope(), range_t*);
+
+ /* If the dissector already has a preference module, use it */
+ module = prefs_find_module(proto_get_protocol_filter_name(proto_id));
+ if (module == NULL) {
+ /* Otherwise create a new one */
+ module = prefs_register_protocol(proto_id, NULL);
+ }
+ /* Some preference callback functions use the proto_reg_handoff_
+ routine to apply preferences, which could duplicate the
+ registration of a preference. Check for that here */
+ if (prefs_find_preference(module, name) == NULL) {
+ if (g_strcmp0(range_str, "") > 0) {
+ description = wmem_strdup_printf(wmem_epan_scope(), "%s %s(s) (default: %s)",
+ proto_get_protocol_short_name(handle->protocol), pref_dissector_table->ui_name, range_str);
+ } else {
+ description = wmem_strdup_printf(wmem_epan_scope(), "%s %s(s)",
+ proto_get_protocol_short_name(handle->protocol), pref_dissector_table->ui_name);
+ }
+ title = wmem_strdup_printf(wmem_epan_scope(), "%s(s)", pref_dissector_table->ui_name);
+
+ /* Max value is based on datatype of dissector table */
+ switch (pref_dissector_table->type) {
+
+ case FT_UINT8:
+ max_value = 0xFF;
+ break;
+ case FT_UINT16:
+ max_value = 0xFFFF;
+ break;
+ case FT_UINT24:
+ max_value = 0xFFFFFF;
+ break;
+ case FT_UINT32:
+ max_value = 0xFFFFFFFF;
+ break;
+
+ default:
+ ws_error("The dissector table %s (%s) is not an integer type - are you using a buggy plugin?", name, pref_dissector_table->ui_name);
+ ws_assert_not_reached();
+ }
+
+ range_convert_str(wmem_epan_scope(), range, range_str, max_value);
+ prefs_register_decode_as_range_preference(module, name, title, description, range, max_value);
+ }
+
+ return *range;
+}
+
+void dissector_add_uint_with_preference(const char *name, const guint32 pattern,
+ dissector_handle_t handle)
+{
+ char* range_str;
+
+ range_str = wmem_strdup_printf(NULL, "%d", pattern);
+ dissector_add_range_preference(name, handle, range_str);
+ wmem_free(NULL, range_str);
+ dissector_add_uint(name, pattern, handle);
+}
+
+void dissector_add_uint_range_with_preference(const char *name, const char* range_str,
+ dissector_handle_t handle)
+{
+ range_t* range;
+
+ range = dissector_add_range_preference(name, handle, range_str);
+ dissector_add_uint_range(name, range, handle);
+}
+
+/* Delete the entry for a dissector in a uint dissector table
+ with a particular pattern. */
+
+/* NOTE: this doesn't use the dissector call variable. It is included to */
+/* be consistant with the dissector_add_uint and more importantly to be used */
+/* if the technique of adding a temporary dissector is implemented. */
+/* If temporary dissectors are deleted, then the original dissector must */
+/* be available. */
+void
+dissector_delete_uint(const char *name, const guint32 pattern,
+ dissector_handle_t handle _U_)
+{
+ dissector_table_t sub_dissectors = find_dissector_table(name);
+ dtbl_entry_t *dtbl_entry;
+
+ /* sanity check */
+ ws_assert(sub_dissectors);
+
+ /*
+ * Find the entry.
+ */
+ dtbl_entry = find_uint_dtbl_entry(sub_dissectors, pattern);
+
+ if (dtbl_entry != NULL) {
+ /*
+ * Found - remove it.
+ */
+ g_hash_table_remove(sub_dissectors->hash_table,
+ GUINT_TO_POINTER(pattern));
+ }
+}
+
+void dissector_delete_uint_range(const char *name, range_t *range,
+ dissector_handle_t handle)
+{
+ guint32 i, j;
+
+ if (range) {
+ for (i = 0; i < range->nranges; i++) {
+ for (j = range->ranges[i].low; j < range->ranges[i].high; j++)
+ dissector_delete_uint(name, j, handle);
+ dissector_delete_uint(name, range->ranges[i].high, handle);
+ }
+ }
+}
+
+static gboolean
+dissector_delete_all_check (gpointer key _U_, gpointer value, gpointer user_data)
+{
+ dtbl_entry_t *dtbl_entry = (dtbl_entry_t *) value;
+ dissector_handle_t handle = (dissector_handle_t) user_data;
+
+ if (!dtbl_entry->current->protocol) {
+ /*
+ * Not all dissectors are registered with a protocol, so we need this
+ * check when running from dissector_delete_from_all_tables.
+ */
+ return FALSE;
+ }
+
+ return (proto_get_id (dtbl_entry->current->protocol) == proto_get_id (handle->protocol));
+}
+
+/* Delete all entries from a dissector table. */
+void dissector_delete_all(const char *name, dissector_handle_t handle)
+{
+ dissector_table_t sub_dissectors = find_dissector_table(name);
+ ws_assert (sub_dissectors);
+
+ g_hash_table_foreach_remove (sub_dissectors->hash_table, dissector_delete_all_check, handle);
+}
+
+static void
+dissector_delete_from_table(gpointer key _U_, gpointer value, gpointer user_data)
+{
+ dissector_table_t sub_dissectors = (dissector_table_t) value;
+ ws_assert (sub_dissectors);
+
+ g_hash_table_foreach_remove(sub_dissectors->hash_table, dissector_delete_all_check, user_data);
+ sub_dissectors->dissector_handles = g_slist_remove(sub_dissectors->dissector_handles, user_data);
+}
+
+/* Delete handle from all tables and dissector_handles lists */
+static void
+dissector_delete_from_all_tables(dissector_handle_t handle)
+{
+ g_hash_table_foreach(dissector_tables, dissector_delete_from_table, handle);
+}
+
+/* Change the entry for a dissector in a uint dissector table
+ with a particular pattern to use a new dissector handle. */
+void
+dissector_change_uint(const char *name, const guint32 pattern, dissector_handle_t handle)
+{
+ dissector_table_t sub_dissectors = find_dissector_table(name);
+ dtbl_entry_t *dtbl_entry;
+
+ /* sanity check */
+ ws_assert(sub_dissectors);
+
+ /*
+ * See if the entry already exists. If so, reuse it.
+ */
+ dtbl_entry = find_uint_dtbl_entry(sub_dissectors, pattern);
+ if (dtbl_entry != NULL) {
+ dtbl_entry->current = handle;
+ return;
+ }
+
+ /*
+ * Don't create an entry if there is no dissector handle - I.E. the
+ * user said not to decode something that wasn't being decoded
+ * in the first place.
+ */
+ if (handle == NULL)
+ return;
+
+ dtbl_entry = g_new(dtbl_entry_t, 1);
+ dtbl_entry->initial = NULL;
+ dtbl_entry->current = handle;
+
+ /* do the table insertion */
+ g_hash_table_insert(sub_dissectors->hash_table,
+ GUINT_TO_POINTER(pattern), (gpointer)dtbl_entry);
+}
+
+/* Reset an entry in a uint dissector table to its initial value. */
+void
+dissector_reset_uint(const char *name, const guint32 pattern)
+{
+ dissector_table_t sub_dissectors = find_dissector_table(name);
+ dtbl_entry_t *dtbl_entry;
+
+ /* sanity check */
+ ws_assert(sub_dissectors);
+
+ /*
+ * Find the entry.
+ */
+ dtbl_entry = find_uint_dtbl_entry(sub_dissectors, pattern);
+
+ if (dtbl_entry == NULL)
+ return;
+
+ /*
+ * Found - is there an initial value?
+ */
+ if (dtbl_entry->initial != NULL) {
+ dtbl_entry->current = dtbl_entry->initial;
+ } else {
+ g_hash_table_remove(sub_dissectors->hash_table,
+ GUINT_TO_POINTER(pattern));
+ }
+}
+
+/* Return TRUE if an entry in a uint dissector table is found and has been
+ * changed (i.e. dissector_change_uint() has been called, such as from
+ * Decode As, prefs registered via dissector_add_uint_[range_]with_preference),
+ * etc.), otherwise return FALSE.
+ */
+gboolean
+dissector_is_uint_changed(dissector_table_t const sub_dissectors, const guint32 uint_val)
+{
+ if (sub_dissectors != NULL) {
+ dtbl_entry_t *dtbl_entry = find_uint_dtbl_entry(sub_dissectors, uint_val);
+ if (dtbl_entry != NULL)
+ return (dtbl_entry->current != dtbl_entry->initial);
+ }
+ return FALSE;
+}
+
+/* Look for a given value in a given uint dissector table and, if found,
+ call the dissector with the arguments supplied, and return the number
+ of bytes consumed by the dissector, otherwise return 0. */
+
+int
+dissector_try_uint_new(dissector_table_t sub_dissectors, const guint32 uint_val,
+ tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ const gboolean add_proto_name, void *data)
+{
+ dtbl_entry_t *dtbl_entry;
+ struct dissector_handle *handle;
+ guint32 saved_match_uint;
+ int len;
+
+ dtbl_entry = find_uint_dtbl_entry(sub_dissectors, uint_val);
+ if (dtbl_entry == NULL) {
+ /*
+ * There's no entry in the table for our value.
+ */
+ return 0;
+ }
+
+ /*
+ * Is there currently a dissector handle for this entry?
+ */
+ handle = dtbl_entry->current;
+ if (handle == NULL) {
+ /*
+ * No - pretend this dissector didn't exist,
+ * so that other dissectors might have a chance
+ * to dissect this packet.
+ */
+ return 0;
+ }
+
+ /*
+ * Save the current value of "pinfo->match_uint",
+ * set it to the uint_val that matched, call the
+ * dissector, and restore "pinfo->match_uint".
+ */
+ saved_match_uint = pinfo->match_uint;
+ pinfo->match_uint = uint_val;
+ len = call_dissector_work(handle, tvb, pinfo, tree, add_proto_name, data);
+ pinfo->match_uint = saved_match_uint;
+
+ /*
+ * If a new-style dissector returned 0, it means that
+ * it didn't think this tvbuff represented a packet for
+ * its protocol, and didn't dissect anything.
+ *
+ * Old-style dissectors can't reject the packet.
+ *
+ * 0 is also returned if the protocol wasn't enabled.
+ *
+ * If the packet was rejected, we return 0, so that
+ * other dissectors might have a chance to dissect this
+ * packet, otherwise we return the dissected length.
+ */
+ return len;
+}
+
+int
+dissector_try_uint(dissector_table_t sub_dissectors, const guint32 uint_val,
+ tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+
+ return dissector_try_uint_new(sub_dissectors, uint_val, tvb, pinfo, tree, TRUE, NULL);
+}
+
+/* Look for a given value in a given uint dissector table and, if found,
+ return the dissector handle for that value. */
+dissector_handle_t
+dissector_get_uint_handle(dissector_table_t const sub_dissectors, const guint32 uint_val)
+{
+ dtbl_entry_t *dtbl_entry;
+
+ dtbl_entry = find_uint_dtbl_entry(sub_dissectors, uint_val);
+ if (dtbl_entry != NULL)
+ return dtbl_entry->current;
+ else
+ return NULL;
+}
+
+dissector_handle_t
+dissector_get_default_uint_handle(const char *name, const guint32 uint_val)
+{
+ dissector_table_t sub_dissectors = find_dissector_table(name);
+
+ if (sub_dissectors != NULL) {
+ dtbl_entry_t *dtbl_entry = find_uint_dtbl_entry(sub_dissectors, uint_val);
+ if (dtbl_entry != NULL)
+ return dtbl_entry->initial;
+ }
+ return NULL;
+}
+
+/* Find an entry in a string dissector table. */
+static dtbl_entry_t *
+find_string_dtbl_entry(dissector_table_t const sub_dissectors, const gchar *pattern)
+{
+ dtbl_entry_t *ret;
+ char *key;
+
+ switch (sub_dissectors->type) {
+
+ case FT_STRING:
+ case FT_STRINGZ:
+ case FT_STRINGZPAD:
+ case FT_STRINGZTRUNC:
+ /*
+ * You can do a string lookup in these tables.
+ */
+ break;
+
+ default:
+ /*
+ * But you can't do a string lookup in any other types
+ * of tables.
+ */
+ ws_assert_not_reached();
+ }
+
+ if (sub_dissectors->param == STRING_CASE_INSENSITIVE) {
+ key = g_ascii_strdown(pattern, -1);
+ } else {
+ key = g_strdup(pattern);
+ }
+
+ /*
+ * Find the entry.
+ */
+ ret = (dtbl_entry_t *)g_hash_table_lookup(sub_dissectors->hash_table, key);
+
+ g_free(key);
+
+ return ret;
+}
+
+/* Add an entry to a string dissector table. */
+void
+dissector_add_string(const char *name, const gchar *pattern,
+ dissector_handle_t handle)
+{
+ dissector_table_t sub_dissectors = find_dissector_table(name);
+ dtbl_entry_t *dtbl_entry;
+ char *key;
+
+ /*
+ * Make sure the handle and the dissector table exist.
+ */
+ if (handle == NULL) {
+ fprintf(stderr, "OOPS: handle to register \"%s\" to doesn't exist\n",
+ name);
+ if (wireshark_abort_on_dissector_bug)
+ abort();
+ return;
+ }
+ if (sub_dissectors == NULL) {
+ fprintf(stderr, "OOPS: dissector table \"%s\" doesn't exist\n",
+ name);
+ fprintf(stderr, "Protocol being registered is \"%s\"\n",
+ proto_get_protocol_long_name(handle->protocol));
+ if (wireshark_abort_on_dissector_bug)
+ abort();
+ return;
+ }
+
+ switch (sub_dissectors->type) {
+
+ case FT_STRING:
+ case FT_STRINGZ:
+ case FT_STRINGZPAD:
+ case FT_STRINGZTRUNC:
+ /*
+ * You can do a string lookup in these tables.
+ */
+ break;
+
+ default:
+ /*
+ * But you can't do a string lookup in any other types
+ * of tables.
+ */
+ ws_assert_not_reached();
+ }
+
+ dtbl_entry = g_new(dtbl_entry_t, 1);
+ dtbl_entry->current = handle;
+ dtbl_entry->initial = dtbl_entry->current;
+
+ if (sub_dissectors->param == STRING_CASE_INSENSITIVE) {
+ key = g_ascii_strdown(pattern, -1);
+ } else {
+ key = g_strdup(pattern);
+ }
+
+ /* do the table insertion */
+ g_hash_table_insert(sub_dissectors->hash_table, (gpointer)key,
+ (gpointer)dtbl_entry);
+
+ /*
+ * Now, if this table supports "Decode As", add this handle
+ * to the list of handles that could be used for "Decode As"
+ * with this table, because it *is* being used with this table.
+ */
+ if (sub_dissectors->supports_decode_as)
+ dissector_add_for_decode_as(name, handle);
+}
+
+/* Delete the entry for a dissector in a string dissector table
+ with a particular pattern. */
+
+/* NOTE: this doesn't use the dissector call variable. It is included to */
+/* be consistant with the dissector_add_string and more importantly to */
+/* be used if the technique of adding a temporary dissector is */
+/* implemented. */
+/* If temporary dissectors are deleted, then the original dissector must */
+/* be available. */
+void
+dissector_delete_string(const char *name, const gchar *pattern,
+ dissector_handle_t handle _U_)
+{
+ dissector_table_t sub_dissectors = find_dissector_table(name);
+ dtbl_entry_t *dtbl_entry;
+
+ /* sanity check */
+ ws_assert(sub_dissectors);
+
+ /*
+ * Find the entry.
+ */
+ dtbl_entry = find_string_dtbl_entry(sub_dissectors, pattern);
+
+ if (dtbl_entry != NULL) {
+ /*
+ * Found - remove it.
+ */
+ g_hash_table_remove(sub_dissectors->hash_table, pattern);
+ }
+}
+
+/* Change the entry for a dissector in a string dissector table
+ with a particular pattern to use a new dissector handle. */
+void
+dissector_change_string(const char *name, const gchar *pattern,
+ dissector_handle_t handle)
+{
+ dissector_table_t sub_dissectors = find_dissector_table(name);
+ dtbl_entry_t *dtbl_entry;
+
+ /* sanity check */
+ ws_assert(sub_dissectors);
+
+ /*
+ * See if the entry already exists. If so, reuse it.
+ */
+ dtbl_entry = find_string_dtbl_entry(sub_dissectors, pattern);
+ if (dtbl_entry != NULL) {
+ dtbl_entry->current = handle;
+ return;
+ }
+
+ /*
+ * Don't create an entry if there is no dissector handle - I.E. the
+ * user said not to decode something that wasn't being decoded
+ * in the first place.
+ */
+ if (handle == NULL)
+ return;
+
+ dtbl_entry = g_new(dtbl_entry_t, 1);
+ dtbl_entry->initial = NULL;
+ dtbl_entry->current = handle;
+
+ /* do the table insertion */
+ g_hash_table_insert(sub_dissectors->hash_table, (gpointer)g_strdup(pattern),
+ (gpointer)dtbl_entry);
+}
+
+/* Reset an entry in a string sub-dissector table to its initial value. */
+void
+dissector_reset_string(const char *name, const gchar *pattern)
+{
+ dissector_table_t sub_dissectors = find_dissector_table(name);
+ dtbl_entry_t *dtbl_entry;
+
+ /* sanity check */
+ ws_assert(sub_dissectors);
+
+ /*
+ * Find the entry.
+ */
+ dtbl_entry = find_string_dtbl_entry(sub_dissectors, pattern);
+
+ if (dtbl_entry == NULL)
+ return;
+
+ /*
+ * Found - is there an initial value?
+ */
+ if (dtbl_entry->initial != NULL) {
+ dtbl_entry->current = dtbl_entry->initial;
+ } else {
+ g_hash_table_remove(sub_dissectors->hash_table, pattern);
+ }
+}
+
+/* Return TRUE if an entry in a uint dissector table is found and has been
+ * changed (i.e. dissector_change_uint() has been called, such as from
+ * Decode As, prefs registered via dissector_add_uint_[range_]with_preference),
+ * etc.), otherwise return FALSE.
+ */
+gboolean
+dissector_is_string_changed(dissector_table_t const sub_dissectors, const gchar *string)
+{
+ if (sub_dissectors != NULL) {
+ dtbl_entry_t *dtbl_entry = find_string_dtbl_entry(sub_dissectors, string);
+ if (dtbl_entry != NULL)
+ return (dtbl_entry->current != dtbl_entry->initial);
+ }
+ return FALSE;
+}
+
+/* Look for a given string in a given dissector table and, if found, call
+ the dissector with the arguments supplied, and return length of dissected data,
+ otherwise return 0. */
+int
+dissector_try_string_new(dissector_table_t sub_dissectors, const gchar *string,
+ tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, const gboolean add_proto_name, void *data)
+{
+ dtbl_entry_t *dtbl_entry;
+ struct dissector_handle *handle;
+ int len;
+ const gchar *saved_match_string;
+
+ /* XXX ASSERT instead ? */
+ if (!string) return 0;
+ dtbl_entry = find_string_dtbl_entry(sub_dissectors, string);
+ if (dtbl_entry != NULL) {
+ /*
+ * Is there currently a dissector handle for this entry?
+ */
+ handle = dtbl_entry->current;
+ if (handle == NULL) {
+ /*
+ * No - pretend this dissector didn't exist,
+ * so that other dissectors might have a chance
+ * to dissect this packet.
+ */
+ return 0;
+ }
+
+ /*
+ * Save the current value of "pinfo->match_string",
+ * set it to the string that matched, call the
+ * dissector, and restore "pinfo->match_string".
+ */
+ saved_match_string = pinfo->match_string;
+ pinfo->match_string = string;
+ len = call_dissector_work(handle, tvb, pinfo, tree, add_proto_name, data);
+ pinfo->match_string = saved_match_string;
+
+ /*
+ * If a new-style dissector returned 0, it means that
+ * it didn't think this tvbuff represented a packet for
+ * its protocol, and didn't dissect anything.
+ *
+ * Old-style dissectors can't reject the packet.
+ *
+ * 0 is also returned if the protocol wasn't enabled.
+ *
+ * If the packet was rejected, we return 0, so that
+ * other dissectors might have a chance to dissect this
+ * packet, otherwise we return the dissected length.
+ */
+ return len;
+ }
+ return 0;
+}
+
+int
+dissector_try_string(dissector_table_t sub_dissectors, const gchar *string,
+ tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
+{
+ return dissector_try_string_new(sub_dissectors, string, tvb, pinfo, tree, TRUE, data);
+}
+
+/* Look for a given value in a given string dissector table and, if found,
+ return the dissector handle for that value. */
+dissector_handle_t
+dissector_get_string_handle(dissector_table_t sub_dissectors,
+ const gchar *string)
+{
+ dtbl_entry_t *dtbl_entry;
+
+ /* XXX ASSERT instead ? */
+ if (!string) return NULL;
+ dtbl_entry = find_string_dtbl_entry(sub_dissectors, string);
+ if (dtbl_entry != NULL)
+ return dtbl_entry->current;
+ else
+ return NULL;
+}
+
+dissector_handle_t
+dissector_get_default_string_handle(const char *name, const gchar *string)
+{
+ dissector_table_t sub_dissectors;
+
+ /* XXX ASSERT instead ? */
+ if (!string) return NULL;
+ sub_dissectors = find_dissector_table(name);
+ if (sub_dissectors != NULL) {
+ dtbl_entry_t *dtbl_entry = find_string_dtbl_entry(sub_dissectors, string);
+ if (dtbl_entry != NULL)
+ return dtbl_entry->initial;
+ }
+ return NULL;
+}
+
+/* Add an entry to a "custom" dissector table. */
+void dissector_add_custom_table_handle(const char *name, void *pattern, dissector_handle_t handle)
+{
+ dissector_table_t sub_dissectors = find_dissector_table(name);
+ dtbl_entry_t *dtbl_entry;
+
+ /*
+ * Make sure the handle and the dissector table exist.
+ */
+ if (handle == NULL) {
+ fprintf(stderr, "OOPS: handle to register \"%s\" to doesn't exist\n",
+ name);
+ if (wireshark_abort_on_dissector_bug)
+ abort();
+ return;
+ }
+ if (sub_dissectors == NULL) {
+ fprintf(stderr, "OOPS: dissector table \"%s\" doesn't exist\n",
+ name);
+ fprintf(stderr, "Protocol being registered is \"%s\"\n",
+ proto_get_protocol_long_name(handle->protocol));
+ if (wireshark_abort_on_dissector_bug)
+ abort();
+ return;
+ }
+
+ ws_assert(sub_dissectors->type == FT_BYTES);
+
+ dtbl_entry = g_new(dtbl_entry_t, 1);
+ dtbl_entry->current = handle;
+ dtbl_entry->initial = dtbl_entry->current;
+
+ /* do the table insertion */
+ g_hash_table_insert(sub_dissectors->hash_table, (gpointer)pattern,
+ (gpointer)dtbl_entry);
+
+ /*
+ * Now, if this table supports "Decode As", add this handle
+ * to the list of handles that could be used for "Decode As"
+ * with this table, because it *is* being used with this table.
+ */
+ if (sub_dissectors->supports_decode_as)
+ dissector_add_for_decode_as(name, handle);
+}
+
+dissector_handle_t dissector_get_custom_table_handle(dissector_table_t sub_dissectors, void *key)
+{
+ dtbl_entry_t *dtbl_entry = (dtbl_entry_t *)g_hash_table_lookup(sub_dissectors->hash_table, key);
+
+ if (dtbl_entry != NULL)
+ return dtbl_entry->current;
+
+ return NULL;
+}
+/* Add an entry to a guid dissector table. */
+void dissector_add_guid(const char *name, guid_key* guid_val, dissector_handle_t handle)
+{
+ dissector_table_t sub_dissectors;
+ dtbl_entry_t *dtbl_entry;
+
+ sub_dissectors = find_dissector_table(name);
+
+ /*
+ * Make sure the handle and the dissector table exist.
+ */
+ if (handle == NULL) {
+ fprintf(stderr, "OOPS: handle to register \"%s\" to doesn't exist\n",
+ name);
+ if (wireshark_abort_on_dissector_bug)
+ abort();
+ return;
+ }
+ if (sub_dissectors == NULL) {
+ fprintf(stderr, "OOPS: dissector table \"%s\" doesn't exist\n",
+ name);
+ fprintf(stderr, "Protocol being registered is \"%s\"\n",
+ proto_get_protocol_long_name(handle->protocol));
+ if (wireshark_abort_on_dissector_bug)
+ abort();
+ return;
+ }
+
+ if (sub_dissectors->type != FT_GUID) {
+ ws_assert_not_reached();
+ }
+
+ dtbl_entry = g_new(dtbl_entry_t, 1);
+ dtbl_entry->current = handle;
+ dtbl_entry->initial = dtbl_entry->current;
+
+ /* do the table insertion */
+ g_hash_table_insert(sub_dissectors->hash_table,
+ guid_val, (gpointer)dtbl_entry);
+
+ /*
+ * Now, if this table supports "Decode As", add this handle
+ * to the list of handles that could be used for "Decode As"
+ * with this table, because it *is* being used with this table.
+ */
+ if (sub_dissectors->supports_decode_as)
+ dissector_add_for_decode_as(name, handle);
+}
+
+/* Look for a given value in a given guid dissector table and, if found,
+ call the dissector with the arguments supplied, and return TRUE,
+ otherwise return FALSE. */
+int dissector_try_guid_new(dissector_table_t sub_dissectors,
+ guid_key* guid_val, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, const gboolean add_proto_name, void *data)
+{
+ dtbl_entry_t *dtbl_entry;
+ struct dissector_handle *handle;
+ int len;
+
+ dtbl_entry = (dtbl_entry_t *)g_hash_table_lookup(sub_dissectors->hash_table, guid_val);
+ if (dtbl_entry != NULL) {
+ /*
+ * Is there currently a dissector handle for this entry?
+ */
+ handle = dtbl_entry->current;
+ if (handle == NULL) {
+ /*
+ * No - pretend this dissector didn't exist,
+ * so that other dissectors might have a chance
+ * to dissect this packet.
+ */
+ return 0;
+ }
+
+ /*
+ * Save the current value of "pinfo->match_uint",
+ * set it to the uint_val that matched, call the
+ * dissector, and restore "pinfo->match_uint".
+ */
+ len = call_dissector_work(handle, tvb, pinfo, tree, add_proto_name, data);
+
+ /*
+ * If a new-style dissector returned 0, it means that
+ * it didn't think this tvbuff represented a packet for
+ * its protocol, and didn't dissect anything.
+ *
+ * Old-style dissectors can't reject the packet.
+ *
+ * 0 is also returned if the protocol wasn't enabled.
+ *
+ * If the packet was rejected, we return 0, so that
+ * other dissectors might have a chance to dissect this
+ * packet, otherwise we return the dissected length.
+ */
+ return len;
+ }
+ return 0;
+}
+
+int dissector_try_guid(dissector_table_t sub_dissectors,
+ guid_key* guid_val, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ return dissector_try_guid_new(sub_dissectors, guid_val, tvb, pinfo, tree, TRUE, NULL);
+}
+
+/** Look for a given value in a given guid dissector table and, if found,
+ * return the current dissector handle for that value.
+ *
+ * @param[in] sub_dissectors Dissector table to search.
+ * @param[in] guid_val Value to match.
+ * @return The matching dissector handle on success, NULL if no match is found.
+ */
+dissector_handle_t dissector_get_guid_handle(
+ dissector_table_t const sub_dissectors, guid_key* guid_val)
+{
+ dtbl_entry_t *dtbl_entry;
+
+ dtbl_entry = (dtbl_entry_t *)g_hash_table_lookup(sub_dissectors->hash_table, guid_val);
+ if (dtbl_entry != NULL)
+ return dtbl_entry->current;
+ else
+ return NULL;
+}
+
+/* Use the currently assigned payload dissector for the dissector table and,
+ if any, call the dissector with the arguments supplied, and return the
+ number of bytes consumed, otherwise return 0. */
+int dissector_try_payload(dissector_table_t sub_dissectors,
+ tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ return dissector_try_uint(sub_dissectors, 0, tvb, pinfo, tree);
+}
+
+/* Use the currently assigned payload dissector for the dissector table and,
+ if any, call the dissector with the arguments supplied, and return the
+ number of bytes consumed, otherwise return 0. */
+int dissector_try_payload_new(dissector_table_t sub_dissectors,
+ tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, const gboolean add_proto_name, void *data)
+{
+ return dissector_try_uint_new(sub_dissectors, 0, tvb, pinfo, tree, add_proto_name, data);
+}
+
+/* Change the entry for a dissector in a payload (FT_NONE) dissector table
+ with a particular pattern to use a new dissector handle. */
+void dissector_change_payload(const char *name, dissector_handle_t handle)
+{
+ dissector_change_uint(name, 0, handle);
+}
+
+/* Reset payload (FT_NONE) dissector table to its initial value. */
+void dissector_reset_payload(const char *name)
+{
+ dissector_reset_uint(name, 0);
+}
+
+/* Given a payload dissector table (type FT_NONE), return the handle of
+ the dissector that is currently active, i.e. that was selected via
+ Decode As. */
+dissector_handle_t
+dissector_get_payload_handle(dissector_table_t const dissector_table)
+{
+ return dissector_get_uint_handle(dissector_table, 0);
+}
+
+dissector_handle_t
+dtbl_entry_get_handle (dtbl_entry_t *dtbl_entry)
+{
+ return dtbl_entry->current;
+}
+
+static gint
+dissector_compare_filter_name(gconstpointer dissector_a, gconstpointer dissector_b)
+{
+ const struct dissector_handle *a = (const struct dissector_handle *)dissector_a;
+ const struct dissector_handle *b = (const struct dissector_handle *)dissector_b;
+ const char *a_name, *b_name;
+ gint ret;
+
+ if (a->protocol == NULL)
+ a_name = "";
+ else
+ a_name = proto_get_protocol_filter_name(proto_get_id(a->protocol));
+
+ if (b->protocol == NULL)
+ b_name = "";
+ else
+ b_name = proto_get_protocol_filter_name(proto_get_id(b->protocol));
+
+ ret = strcmp(a_name, b_name);
+ return ret;
+}
+
+/* Add a handle to the list of handles that *could* be used with this
+ table. That list is used by the "Decode As"/"-d" code in the UI. */
+void
+dissector_add_for_decode_as(const char *name, dissector_handle_t handle)
+{
+ dissector_table_t sub_dissectors = find_dissector_table(name);
+ GSList *entry;
+ dissector_handle_t dup_handle;
+
+ /*
+ * Make sure the dissector table exists.
+ */
+ if (sub_dissectors == NULL) {
+ fprintf(stderr, "OOPS: dissector table \"%s\" doesn't exist\n",
+ name);
+ fprintf(stderr, "Protocol being registered is \"%s\"\n",
+ proto_get_protocol_long_name(handle->protocol));
+ if (wireshark_abort_on_dissector_bug)
+ abort();
+ return;
+ }
+
+ /*
+ * Make sure it supports Decode As.
+ */
+ if (!sub_dissectors->supports_decode_as) {
+ const char *dissector_name;
+
+ dissector_name = dissector_handle_get_dissector_name(handle);
+ if (dissector_name == NULL)
+ dissector_name = "(anonymous)";
+ fprintf(stderr, "Registering dissector %s for protocol %s in dissector table %s, which doesn't support Decode As\n",
+ dissector_name,
+ proto_get_protocol_short_name(handle->protocol),
+ name);
+ if (wireshark_abort_on_dissector_bug)
+ abort();
+ return;
+ }
+
+ /* Add the dissector as a dependency
+ (some dissector tables don't have protocol association, so there is
+ the need for the NULL check */
+ if (sub_dissectors->protocol != NULL)
+ register_depend_dissector(proto_get_protocol_short_name(sub_dissectors->protocol), proto_get_protocol_short_name(handle->protocol));
+
+ /* Is it already in this list? */
+ entry = g_slist_find(sub_dissectors->dissector_handles, (gpointer)handle);
+ if (entry != NULL) {
+ /*
+ * Yes - don't insert it again.
+ */
+ return;
+ }
+
+ /* Ensure the dissector's description is unique. This prevents
+ confusion when using Decode As; duplicate descriptions would
+ make it impossible to distinguish between the dissectors
+ with the same descriptions.
+
+ FT_STRING can at least show the string value in the dialog,
+ so we don't do the check for them. */
+ if (sub_dissectors->type != FT_STRING)
+ {
+ for (entry = sub_dissectors->dissector_handles; entry != NULL; entry = g_slist_next(entry))
+ {
+ dup_handle = (dissector_handle_t)entry->data;
+ if (dup_handle->description != NULL &&
+ strcmp(dup_handle->description, handle->description) == 0)
+ {
+ const char *dissector_name, *dup_dissector_name;
+
+ dissector_name = dissector_handle_get_dissector_name(handle);
+ if (dissector_name == NULL)
+ dissector_name = "(anonymous)";
+ dup_dissector_name = dissector_handle_get_dissector_name(dup_handle);
+ if (dup_dissector_name == NULL)
+ dup_dissector_name = "(anonymous)";
+ fprintf(stderr, "Dissectors %s and %s in dissector table %s have same dissector name %s\n",
+ dissector_name, dup_dissector_name,
+ name, handle->description);
+ if (wireshark_abort_on_dissector_bug)
+ abort();
+ }
+ }
+ }
+
+ /* Add it to the list. */
+ sub_dissectors->dissector_handles =
+ g_slist_insert_sorted(sub_dissectors->dissector_handles, (gpointer)handle, (GCompareFunc)dissector_compare_filter_name);
+}
+
+void dissector_add_for_decode_as_with_preference(const char *name,
+ dissector_handle_t handle)
+{
+ /* If a dissector is added for Decode As only, it's dissector
+ table value would default to 0.
+ Set up a preference value with that information
+ */
+ dissector_add_range_preference(name, handle, "");
+
+ dissector_add_for_decode_as(name, handle);
+}
+
+dissector_handle_t
+dtbl_entry_get_initial_handle (dtbl_entry_t *dtbl_entry)
+{
+ return dtbl_entry->initial;
+}
+
+GSList *
+dissector_table_get_dissector_handles(dissector_table_t dissector_table) {
+ if (!dissector_table)
+ return NULL;
+
+ return dissector_table->dissector_handles;
+}
+
+/*
+ * Data structure used as user data when iterating dissector handles
+ */
+typedef struct lookup_entry {
+ const gchar* dissector_description;
+ dissector_handle_t handle;
+} lookup_entry_t;
+
+/*
+ * A callback function to changed a dissector_handle if matched
+ * This is used when iterating a dissector table
+ */
+static void
+find_dissector_in_table(gpointer item, gpointer user_data)
+{
+ dissector_handle_t handle = (dissector_handle_t)item;
+ lookup_entry_t * lookup = (lookup_entry_t *)user_data;
+ const gchar *description = dissector_handle_get_description(handle);
+ if (description && strcmp(lookup->dissector_description, description) == 0) {
+ lookup->handle = handle;
+ }
+}
+
+dissector_handle_t dissector_table_get_dissector_handle(dissector_table_t dissector_table, const gchar* description)
+{
+ lookup_entry_t lookup;
+
+ lookup.dissector_description = description;
+ lookup.handle = NULL;
+
+ g_slist_foreach(dissector_table->dissector_handles, find_dissector_in_table, &lookup);
+ return lookup.handle;
+}
+
+ftenum_t
+dissector_table_get_type(dissector_table_t dissector_table) {
+ if (!dissector_table) return FT_NONE;
+ return dissector_table->type;
+}
+
+void
+dissector_table_allow_decode_as(dissector_table_t dissector_table)
+{
+ dissector_table->supports_decode_as = TRUE;
+}
+
+gboolean
+dissector_table_supports_decode_as(dissector_table_t dissector_table)
+{
+ return dissector_table->supports_decode_as;
+}
+
+static gint
+uuid_equal(gconstpointer k1, gconstpointer k2)
+{
+ const guid_key *key1 = (const guid_key *)k1;
+ const guid_key *key2 = (const guid_key *)k2;
+ return ((memcmp(&key1->guid, &key2->guid, sizeof (e_guid_t)) == 0)
+ && (key1->ver == key2->ver));
+}
+
+static guint
+uuid_hash(gconstpointer k)
+{
+ const guid_key *key = (const guid_key *)k;
+ /* This isn't perfect, but the Data1 part of these is almost always unique. */
+ return key->guid.data1;
+}
+
+/**************************************************/
+/* */
+/* Routines to walk dissector tables */
+/* */
+/**************************************************/
+
+typedef struct dissector_foreach_info {
+ gpointer caller_data;
+ DATFunc caller_func;
+ GHFunc next_func;
+ const gchar *table_name;
+ ftenum_t selector_type;
+} dissector_foreach_info_t;
+
+/*
+ * Called for each entry in a dissector table.
+ */
+static void
+dissector_table_foreach_func (gpointer key, gpointer value, gpointer user_data)
+{
+ dissector_foreach_info_t *info;
+ dtbl_entry_t *dtbl_entry;
+
+ ws_assert(value);
+ ws_assert(user_data);
+
+ dtbl_entry = (dtbl_entry_t *)value;
+ if (dtbl_entry->current == NULL ||
+ dtbl_entry->current->protocol == NULL) {
+ /*
+ * Either there is no dissector for this entry, or
+ * the dissector doesn't have a protocol associated
+ * with it.
+ *
+ * XXX - should the latter check be done?
+ */
+ return;
+ }
+
+ info = (dissector_foreach_info_t *)user_data;
+ info->caller_func(info->table_name, info->selector_type, key, value,
+ info->caller_data);
+}
+
+/*
+ * Called for each entry in the table of all dissector tables.
+ */
+static void
+dissector_all_tables_foreach_func (gpointer key, gpointer value, gpointer user_data)
+{
+ dissector_table_t sub_dissectors;
+ dissector_foreach_info_t *info;
+
+ ws_assert(value);
+ ws_assert(user_data);
+
+ sub_dissectors = (dissector_table_t)value;
+ info = (dissector_foreach_info_t *)user_data;
+ info->table_name = (gchar*) key;
+ info->selector_type = get_dissector_table_selector_type(info->table_name);
+ g_hash_table_foreach(sub_dissectors->hash_table, info->next_func, info);
+}
+
+/*
+ * Walk all dissector tables calling a user supplied function on each
+ * entry.
+ */
+static void
+dissector_all_tables_foreach (DATFunc func,
+ gpointer user_data)
+{
+ dissector_foreach_info_t info;
+
+ info.caller_data = user_data;
+ info.caller_func = func;
+ info.next_func = dissector_table_foreach_func;
+ g_hash_table_foreach(dissector_tables, dissector_all_tables_foreach_func, &info);
+}
+
+/*
+ * Walk one dissector table's hash table calling a user supplied function
+ * on each entry.
+ */
+void
+dissector_table_foreach (const char *table_name,
+ DATFunc func,
+ gpointer user_data)
+{
+ dissector_foreach_info_t info;
+ dissector_table_t sub_dissectors = find_dissector_table(table_name);
+
+ info.table_name = table_name;
+ info.selector_type = sub_dissectors->type;
+ info.caller_func = func;
+ info.caller_data = user_data;
+ g_hash_table_foreach(sub_dissectors->hash_table, dissector_table_foreach_func, &info);
+}
+
+/*
+ * Walk one dissector table's list of handles calling a user supplied
+ * function on each entry.
+ */
+void
+dissector_table_foreach_handle(const char *table_name,
+ DATFunc_handle func,
+ gpointer user_data)
+{
+ dissector_table_t sub_dissectors = find_dissector_table(table_name);
+ GSList *tmp;
+
+ for (tmp = sub_dissectors->dissector_handles; tmp != NULL;
+ tmp = g_slist_next(tmp))
+ func(table_name, tmp->data, user_data);
+}
+
+/*
+ * Called for each entry in a dissector table.
+ */
+static void
+dissector_table_foreach_changed_func (gpointer key, gpointer value, gpointer user_data)
+{
+ dtbl_entry_t *dtbl_entry;
+ dissector_foreach_info_t *info;
+
+ ws_assert(value);
+ ws_assert(user_data);
+
+ dtbl_entry = (dtbl_entry_t *)value;
+ if (dtbl_entry->initial == dtbl_entry->current) {
+ /*
+ * Entry hasn't changed - don't call the function.
+ */
+ return;
+ }
+
+ info = (dissector_foreach_info_t *)user_data;
+ info->caller_func(info->table_name, info->selector_type, key, value,
+ info->caller_data);
+}
+
+/*
+ * Walk all dissector tables calling a user supplied function only on
+ * any entry that has been changed from its original state.
+ */
+void
+dissector_all_tables_foreach_changed (DATFunc func,
+ gpointer user_data)
+{
+ dissector_foreach_info_t info;
+
+ info.caller_data = user_data;
+ info.caller_func = func;
+ info.next_func = dissector_table_foreach_changed_func;
+ g_hash_table_foreach(dissector_tables, dissector_all_tables_foreach_func, &info);
+}
+
+/*
+ * Walk one dissector table calling a user supplied function only on
+ * any entry that has been changed from its original state.
+ */
+void
+dissector_table_foreach_changed (const char *table_name,
+ DATFunc func,
+ gpointer user_data)
+{
+ dissector_foreach_info_t info;
+ dissector_table_t sub_dissectors = find_dissector_table(table_name);
+
+ info.table_name = table_name;
+ info.selector_type = sub_dissectors->type;
+ info.caller_func = func;
+ info.caller_data = user_data;
+ g_hash_table_foreach(sub_dissectors->hash_table,
+ dissector_table_foreach_changed_func, &info);
+}
+
+typedef struct dissector_foreach_table_info {
+ gpointer caller_data;
+ DATFunc_table caller_func;
+} dissector_foreach_table_info_t;
+
+/*
+ * Called for each entry in the table of all dissector tables.
+ * This is used if we directly process the hash table.
+ */
+static void
+dissector_all_tables_foreach_table_func (gpointer key, gpointer value, gpointer user_data)
+{
+ dissector_table_t table;
+ dissector_foreach_table_info_t *info;
+
+ table = (dissector_table_t)value;
+ info = (dissector_foreach_table_info_t *)user_data;
+ (*info->caller_func)((gchar *)key, table->ui_name, info->caller_data);
+}
+
+/*
+ * Called for each key in the table of all dissector tables.
+ * This is used if we get a list of table names, sort it, and process the list.
+ */
+static void
+dissector_all_tables_foreach_list_func (gpointer key, gpointer user_data)
+{
+ dissector_table_t table;
+ dissector_foreach_table_info_t *info;
+
+ table = (dissector_table_t)g_hash_table_lookup(dissector_tables, key);
+ info = (dissector_foreach_table_info_t *)user_data;
+ (*info->caller_func)((gchar*)key, table->ui_name, info->caller_data);
+}
+
+/*
+ * Walk all dissector tables calling a user supplied function on each
+ * table.
+ */
+void
+dissector_all_tables_foreach_table (DATFunc_table func,
+ gpointer user_data,
+ GCompareFunc compare_key_func)
+{
+ dissector_foreach_table_info_t info;
+ GList *list;
+
+ info.caller_data = user_data;
+ info.caller_func = func;
+ if (compare_key_func != NULL)
+ {
+ list = g_hash_table_get_keys(dissector_tables);
+ list = g_list_sort(list, compare_key_func);
+ g_list_foreach(list, dissector_all_tables_foreach_list_func, &info);
+ g_list_free(list);
+ }
+ else
+ {
+ g_hash_table_foreach(dissector_tables, dissector_all_tables_foreach_table_func, &info);
+ }
+}
+
+dissector_table_t
+register_dissector_table(const char *name, const char *ui_name, const int proto, const ftenum_t type,
+ const int param)
+{
+ dissector_table_t sub_dissectors;
+
+ /* Make sure the registration is unique */
+ if (g_hash_table_lookup(dissector_tables, name)) {
+ ws_error("The dissector table %s (%s) is already registered - are you using a buggy plugin?", name, ui_name);
+ }
+
+ /* Create and register the dissector table for this name; returns */
+ /* a pointer to the dissector table. */
+ sub_dissectors = g_slice_new(struct dissector_table);
+ switch (type) {
+
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ /*
+ * XXX - there's no "g_uint_hash()" or "g_uint_equal()",
+ * so we use "g_direct_hash()" and "g_direct_equal()".
+ */
+ sub_dissectors->hash_func = g_direct_hash;
+ sub_dissectors->hash_table = g_hash_table_new_full(g_direct_hash,
+ g_direct_equal,
+ NULL,
+ &g_free);
+ break;
+
+ case FT_STRING:
+ case FT_STRINGZ:
+ case FT_STRINGZPAD:
+ case FT_STRINGZTRUNC:
+ sub_dissectors->hash_func = g_str_hash;
+ sub_dissectors->hash_table = g_hash_table_new_full(g_str_hash,
+ g_str_equal,
+ &g_free,
+ &g_free);
+ break;
+ case FT_GUID:
+ sub_dissectors->hash_table = g_hash_table_new_full(uuid_hash,
+ uuid_equal,
+ NULL,
+ &g_free);
+ break;
+
+ case FT_NONE:
+ /* Dissector tables with FT_NONE don't have values associated with
+ dissectors so this will always be a hash table size of 1 just
+ to store the single dtbl_entry_t */
+ sub_dissectors->hash_func = g_direct_hash;
+ sub_dissectors->hash_table = g_hash_table_new_full(g_direct_hash,
+ g_direct_equal,
+ NULL,
+ &g_free);
+ break;
+
+ default:
+ ws_error("The dissector table %s (%s) is registering an unsupported type - are you using a buggy plugin?", name, ui_name);
+ ws_assert_not_reached();
+ }
+ sub_dissectors->dissector_handles = NULL;
+ sub_dissectors->ui_name = ui_name;
+ sub_dissectors->type = type;
+ sub_dissectors->param = param;
+ sub_dissectors->protocol = (proto == -1) ? NULL : find_protocol_by_id(proto);
+ sub_dissectors->supports_decode_as = FALSE;
+ g_hash_table_insert(dissector_tables, (gpointer)name, (gpointer) sub_dissectors);
+ return sub_dissectors;
+}
+
+dissector_table_t register_custom_dissector_table(const char *name,
+ const char *ui_name, const int proto, GHashFunc hash_func, GEqualFunc key_equal_func,
+ GDestroyNotify key_destroy_func)
+{
+ dissector_table_t sub_dissectors;
+
+ /* Make sure the registration is unique */
+ if (g_hash_table_lookup(dissector_tables, name)) {
+ ws_error("The dissector table %s (%s) is already registered - are you using a buggy plugin?", name, ui_name);
+ }
+
+ /* Create and register the dissector table for this name; returns */
+ /* a pointer to the dissector table. */
+ sub_dissectors = g_slice_new(struct dissector_table);
+ sub_dissectors->hash_func = hash_func;
+ sub_dissectors->hash_table = g_hash_table_new_full(hash_func,
+ key_equal_func,
+ key_destroy_func,
+ &g_free);
+
+ sub_dissectors->dissector_handles = NULL;
+ sub_dissectors->ui_name = ui_name;
+ sub_dissectors->type = FT_BYTES; /* Consider key a "blob" of data, no need to really create new type */
+ sub_dissectors->param = BASE_NONE;
+ sub_dissectors->protocol = (proto == -1) ? NULL : find_protocol_by_id(proto);
+ sub_dissectors->supports_decode_as = FALSE;
+ g_hash_table_insert(dissector_tables, (gpointer)name, (gpointer) sub_dissectors);
+ return sub_dissectors;
+}
+
+void
+register_dissector_table_alias(dissector_table_t dissector_table, const char *alias_name) {
+ if (!dissector_table || !alias_name) return;
+
+ const char *name = NULL;
+ GList *list = g_hash_table_get_keys(dissector_tables);
+ for (GList *cur = list; cur; cur = cur->next) {
+ if (g_hash_table_lookup(dissector_tables, cur->data) == dissector_table) {
+ name = (const char *) cur->data;
+ break;
+ }
+ }
+ g_list_free(list);
+ if (!name) return;
+
+ g_hash_table_insert(dissector_table_aliases, (gpointer) alias_name, (gpointer) name);
+}
+
+void
+deregister_dissector_table(const char *name)
+{
+ dissector_table_t sub_dissectors = (dissector_table_t) g_hash_table_lookup(dissector_tables, name);
+ if (!sub_dissectors) return;
+
+ g_hash_table_remove(dissector_tables, name);
+
+ GList *list = g_hash_table_get_keys(dissector_table_aliases);
+ for (GList *cur = list; cur; cur = cur->next) {
+ gpointer alias_name = cur->data;
+ if (g_hash_table_lookup(dissector_table_aliases, alias_name) == name) {
+ g_hash_table_remove(dissector_table_aliases, alias_name);
+ }
+ }
+ g_list_free(list);
+}
+
+const char *
+get_dissector_table_ui_name(const char *name)
+{
+ dissector_table_t sub_dissectors = find_dissector_table(name);
+ if (!sub_dissectors) return NULL;
+
+ return sub_dissectors->ui_name;
+}
+
+ftenum_t
+get_dissector_table_selector_type(const char *name)
+{
+ dissector_table_t sub_dissectors = find_dissector_table(name);
+ if (!sub_dissectors) return FT_NONE;
+
+ return sub_dissectors->type;
+}
+
+int
+get_dissector_table_param(const char *name)
+{
+ dissector_table_t sub_dissectors = find_dissector_table(name);
+ if (!sub_dissectors) return 0;
+
+ return sub_dissectors->param;
+}
+
+static void
+check_valid_heur_name_or_fail(const char *heur_name)
+{
+ if (proto_check_field_name_lower(heur_name)) {
+ ws_error("Heuristic Protocol internal name \"%s\" has one or more invalid characters."
+ " Allowed are lowercase, digits, '-', '_' and non-repeating '.'."
+ " This might be caused by an inappropriate plugin or a development error.", heur_name);
+ }
+}
+
+/* Finds a heuristic dissector table by table name. */
+heur_dissector_list_t
+find_heur_dissector_list(const char *name)
+{
+ return (heur_dissector_list_t)g_hash_table_lookup(heur_dissector_lists, name);
+}
+
+gboolean
+has_heur_dissector_list(const gchar *name) {
+ return (find_heur_dissector_list(name) != NULL);
+}
+
+heur_dtbl_entry_t* find_heur_dissector_by_unique_short_name(const char *short_name)
+{
+ return (heur_dtbl_entry_t*)g_hash_table_lookup(heuristic_short_names, short_name);
+}
+
+void
+heur_dissector_add(const char *name, heur_dissector_t dissector, const char *display_name, const char *internal_name, const int proto, heuristic_enable_e enable)
+{
+ heur_dissector_list_t sub_dissectors = find_heur_dissector_list(name);
+ const char *proto_name;
+ heur_dtbl_entry_t *hdtbl_entry;
+ guint i, list_size;
+ GSList *list_entry;
+
+ /*
+ * Make sure the dissector table exists.
+ */
+ if (sub_dissectors == NULL) {
+ fprintf(stderr, "OOPS: dissector table \"%s\" doesn't exist\n",
+ name);
+ proto_name = proto_get_protocol_name(proto);
+ if (proto_name != NULL) {
+ fprintf(stderr, "Protocol being registered is \"%s\"\n",
+ proto_name);
+ }
+ if (wireshark_abort_on_dissector_bug)
+ abort();
+ return;
+ }
+
+ /* Verify that sub-dissector is not already in the list */
+ list_size = g_slist_length(sub_dissectors->dissectors);
+ for (i = 0; i < list_size; i++)
+ {
+ list_entry = g_slist_nth(sub_dissectors->dissectors, i);
+ hdtbl_entry = (heur_dtbl_entry_t *)list_entry->data;
+ if ((hdtbl_entry->dissector == dissector) &&
+ (hdtbl_entry->protocol == find_protocol_by_id(proto)))
+ {
+ proto_name = proto_get_protocol_name(proto);
+ if (proto_name != NULL) {
+ fprintf(stderr, "Protocol %s is already registered in \"%s\" table\n",
+ proto_name, name);
+ }
+ if (wireshark_abort_on_dissector_bug)
+ abort();
+ return;
+ }
+ }
+
+ /* Make sure short_name is "parsing friendly" since it should only be used internally */
+ check_valid_heur_name_or_fail(internal_name);
+
+ /* Ensure short_name is unique */
+ if (g_hash_table_lookup(heuristic_short_names, internal_name) != NULL) {
+ ws_error("Duplicate heuristic short_name \"%s\"!"
+ " This might be caused by an inappropriate plugin or a development error.", internal_name);
+ }
+
+ hdtbl_entry = g_slice_new(heur_dtbl_entry_t);
+ hdtbl_entry->dissector = dissector;
+ hdtbl_entry->protocol = find_protocol_by_id(proto);
+ hdtbl_entry->display_name = display_name;
+ hdtbl_entry->short_name = g_strdup(internal_name);
+ hdtbl_entry->list_name = g_strdup(name);
+ hdtbl_entry->enabled = (enable == HEURISTIC_ENABLE);
+ hdtbl_entry->enabled_by_default = (enable == HEURISTIC_ENABLE);
+
+ /* do the table insertion */
+ g_hash_table_insert(heuristic_short_names, (gpointer)hdtbl_entry->short_name, hdtbl_entry);
+
+ sub_dissectors->dissectors = g_slist_prepend(sub_dissectors->dissectors,
+ (gpointer)hdtbl_entry);
+
+ /* XXX - could be optimized to pass hdtbl_entry directly */
+ proto_add_heuristic_dissector(hdtbl_entry->protocol, hdtbl_entry->short_name);
+
+ /* Add the dissector as a dependency
+ (some heuristic tables don't have protocol association, so there is
+ the need for the NULL check */
+ if (sub_dissectors->protocol != NULL)
+ register_depend_dissector(proto_get_protocol_short_name(sub_dissectors->protocol), proto_get_protocol_short_name(hdtbl_entry->protocol));
+}
+
+
+
+static int
+find_matching_heur_dissector(gconstpointer a, gconstpointer b) {
+ const heur_dtbl_entry_t *hdtbl_entry_a = (const heur_dtbl_entry_t *) a;
+ const heur_dtbl_entry_t *hdtbl_entry_b = (const heur_dtbl_entry_t *) b;
+
+ return (hdtbl_entry_a->dissector == hdtbl_entry_b->dissector) &&
+ (hdtbl_entry_a->protocol == hdtbl_entry_b->protocol) ? 0 : 1;
+}
+
+void
+heur_dissector_delete(const char *name, heur_dissector_t dissector, const int proto) {
+ heur_dissector_list_t sub_dissectors = find_heur_dissector_list(name);
+ heur_dtbl_entry_t hdtbl_entry;
+ GSList *found_entry;
+
+ /* sanity check */
+ ws_assert(sub_dissectors != NULL);
+
+ hdtbl_entry.dissector = dissector;
+ hdtbl_entry.protocol = find_protocol_by_id(proto);
+
+ found_entry = g_slist_find_custom(sub_dissectors->dissectors,
+ (gpointer) &hdtbl_entry, find_matching_heur_dissector);
+
+ if (found_entry) {
+ heur_dtbl_entry_t *found_hdtbl_entry = (heur_dtbl_entry_t *)(found_entry->data);
+ proto_add_deregistered_data(found_hdtbl_entry->list_name);
+ g_hash_table_remove(heuristic_short_names, found_hdtbl_entry->short_name);
+ proto_add_deregistered_data(found_hdtbl_entry->short_name);
+ proto_add_deregistered_slice(sizeof(heur_dtbl_entry_t), found_hdtbl_entry);
+ sub_dissectors->dissectors = g_slist_delete_link(sub_dissectors->dissectors,
+ found_entry);
+ }
+}
+
+gboolean
+dissector_try_heuristic(heur_dissector_list_t sub_dissectors, tvbuff_t *tvb,
+ packet_info *pinfo, proto_tree *tree, heur_dtbl_entry_t **heur_dtbl_entry, void *data)
+{
+ gboolean status;
+ const char *saved_curr_proto;
+ const char *saved_heur_list_name;
+ GSList *entry;
+ GSList *prev_entry = NULL;
+ guint16 saved_can_desegment;
+ guint saved_layers_len = 0;
+ heur_dtbl_entry_t *hdtbl_entry;
+ int proto_id;
+ int len;
+ guint saved_tree_count = tree ? tree->tree_data->count : 0;
+
+ /* can_desegment is set to 2 by anyone which offers this api/service.
+ then everytime a subdissector is called it is decremented by one.
+ thus only the subdissector immediately ontop of whoever offers this
+ service can use it.
+ We save the current value of "can_desegment" for the
+ benefit of TCP proxying dissectors such as SOCKS, so they
+ can restore it and allow the dissectors they call to use
+ the desegmentation service.
+ */
+ saved_can_desegment = pinfo->can_desegment;
+ pinfo->saved_can_desegment = saved_can_desegment;
+ pinfo->can_desegment = saved_can_desegment-(saved_can_desegment>0);
+
+ status = FALSE;
+ saved_curr_proto = pinfo->current_proto;
+ saved_heur_list_name = pinfo->heur_list_name;
+
+ saved_layers_len = wmem_list_count(pinfo->layers);
+ *heur_dtbl_entry = NULL;
+
+ DISSECTOR_ASSERT(saved_layers_len < PINFO_LAYER_MAX_RECURSION_DEPTH);
+
+ for (entry = sub_dissectors->dissectors; entry != NULL;
+ entry = g_slist_next(entry)) {
+ /* XXX - why set this now and above? */
+ pinfo->can_desegment = saved_can_desegment-(saved_can_desegment>0);
+ hdtbl_entry = (heur_dtbl_entry_t *)entry->data;
+
+ if (hdtbl_entry->protocol != NULL &&
+ (!proto_is_protocol_enabled(hdtbl_entry->protocol)||(hdtbl_entry->enabled==FALSE))) {
+ /*
+ * No - don't try this dissector.
+ */
+ continue;
+ }
+
+ if (hdtbl_entry->protocol != NULL) {
+ proto_id = proto_get_id(hdtbl_entry->protocol);
+ /* do NOT change this behavior - wslua uses the protocol short name set here in order
+ to determine which Lua-based heurisitc dissector to call */
+ pinfo->current_proto =
+ proto_get_protocol_short_name(hdtbl_entry->protocol);
+
+ /*
+ * Add the protocol name to the layers; we'll remove it
+ * if the dissector fails.
+ */
+ add_layer(pinfo, proto_id);
+ }
+
+ pinfo->heur_list_name = hdtbl_entry->list_name;
+
+ len = (hdtbl_entry->dissector)(tvb, pinfo, tree, data);
+ if (hdtbl_entry->protocol != NULL &&
+ (len == 0 || (tree && saved_tree_count == tree->tree_data->count))) {
+ /*
+ * We added a protocol layer above. The dissector
+ * didn't accept the packet or it didn't add any
+ * items to the tree so remove it from the list.
+ */
+ while (wmem_list_count(pinfo->layers) > saved_layers_len) {
+ /*
+ * Only reduce the layer number if the dissector
+ * rejected the data. Since tree can be NULL on
+ * the first pass, we cannot check it or it will
+ * break dissectors that rely on a stable value.
+ */
+ remove_last_layer(pinfo, len == 0);
+ }
+ }
+ if (len) {
+ if (ws_log_msg_is_active(WS_LOG_DOMAIN, LOG_LEVEL_DEBUG)) {
+ ws_debug("Frame: %d | Layers: %s | Dissector: %s\n", pinfo->num, proto_list_layers(pinfo), hdtbl_entry->short_name);
+ }
+
+ *heur_dtbl_entry = hdtbl_entry;
+
+ /* Bubble the matched entry to the top for faster search next time. */
+ if (prev_entry != NULL) {
+ sub_dissectors->dissectors = g_slist_remove_link(sub_dissectors->dissectors, entry);
+ sub_dissectors->dissectors = g_slist_concat(entry, sub_dissectors->dissectors);
+ }
+ status = TRUE;
+ break;
+ }
+ prev_entry = entry;
+ }
+
+ pinfo->current_proto = saved_curr_proto;
+ pinfo->heur_list_name = saved_heur_list_name;
+ pinfo->can_desegment = saved_can_desegment;
+ return status;
+}
+
+typedef struct heur_dissector_foreach_info {
+ gpointer caller_data;
+ DATFunc_heur caller_func;
+ GHFunc next_func;
+ const gchar *table_name;
+} heur_dissector_foreach_info_t;
+
+/*
+ * Called for each entry in a heuristic dissector table.
+ */
+static void
+heur_dissector_table_foreach_func (gpointer data, gpointer user_data)
+{
+ heur_dissector_foreach_info_t *info;
+
+ ws_assert(data);
+ ws_assert(user_data);
+
+ info = (heur_dissector_foreach_info_t *)user_data;
+ info->caller_func(info->table_name, (heur_dtbl_entry_t *)data,
+ info->caller_data);
+}
+
+/*
+ * Walk one heuristic dissector table's list calling a user supplied function
+ * on each entry.
+ */
+void
+heur_dissector_table_foreach (const char *table_name,
+ DATFunc_heur func,
+ gpointer user_data)
+{
+ heur_dissector_foreach_info_t info;
+ heur_dissector_list_t sub_dissectors = find_heur_dissector_list(table_name);
+
+ info.table_name = table_name;
+ info.caller_func = func;
+ info.caller_data = user_data;
+ g_slist_foreach(sub_dissectors->dissectors,
+ heur_dissector_table_foreach_func, &info);
+}
+
+/*
+ * Called for each entry in the table of all heuristic dissector tables.
+ */
+typedef struct heur_dissector_foreach_table_info {
+ gpointer caller_data;
+ DATFunc_heur_table caller_func;
+} heur_dissector_foreach_table_info_t;
+
+/*
+ * Called for each entry in the table of all heuristic dissector tables.
+ * This is used if we directly process the hash table.
+ */
+static void
+dissector_all_heur_tables_foreach_table_func (gpointer key, gpointer value, gpointer user_data)
+{
+ heur_dissector_foreach_table_info_t *info;
+
+ info = (heur_dissector_foreach_table_info_t *)user_data;
+ (*info->caller_func)((gchar *)key, (struct heur_dissector_list *)value, info->caller_data);
+}
+
+/*
+ * Called for each key in the table of all dissector tables.
+ * This is used if we get a list of table names, sort it, and process the list.
+ */
+static void
+dissector_all_heur_tables_foreach_list_func (gpointer key, gpointer user_data)
+{
+ struct heur_dissector_list *list;
+ heur_dissector_foreach_table_info_t *info;
+
+ list = (struct heur_dissector_list *)g_hash_table_lookup(heur_dissector_lists, key);
+ info = (heur_dissector_foreach_table_info_t *)user_data;
+ (*info->caller_func)((gchar*)key, list, info->caller_data);
+}
+
+/*
+ * Walk all heuristic dissector tables calling a user supplied function on each
+ * table.
+ */
+void
+dissector_all_heur_tables_foreach_table (DATFunc_heur_table func,
+ gpointer user_data,
+ GCompareFunc compare_key_func)
+{
+ heur_dissector_foreach_table_info_t info;
+ GList *list;
+
+ info.caller_data = user_data;
+ info.caller_func = func;
+ if (compare_key_func != NULL)
+ {
+ list = g_hash_table_get_keys(dissector_tables);
+ list = g_list_sort(list, compare_key_func);
+ g_list_foreach(list, dissector_all_heur_tables_foreach_list_func, &info);
+ g_list_free(list);
+ }
+ else
+ {
+ g_hash_table_foreach(heur_dissector_lists, dissector_all_heur_tables_foreach_table_func, &info);
+ }
+}
+
+static void
+display_heur_dissector_table_entries(const char *table_name,
+ heur_dtbl_entry_t *hdtbl_entry, gpointer user_data _U_)
+{
+ if (hdtbl_entry->protocol != NULL) {
+ printf("%s\t%s\t%c\t%c\t%s\t%s\n",
+ table_name,
+ proto_get_protocol_filter_name(proto_get_id(hdtbl_entry->protocol)),
+ (proto_is_protocol_enabled(hdtbl_entry->protocol) && hdtbl_entry->enabled) ? 'T' : 'F',
+ (proto_is_protocol_enabled_by_default(hdtbl_entry->protocol) && hdtbl_entry->enabled_by_default) ? 'T' : 'F',
+ hdtbl_entry->short_name,
+ hdtbl_entry->display_name);
+ }
+}
+
+static void
+dissector_dump_heur_decodes_display(const gchar *table_name, struct heur_dissector_list *listptr _U_, gpointer user_data _U_)
+{
+ heur_dissector_table_foreach(table_name, display_heur_dissector_table_entries, NULL);
+}
+
+/*
+ * For each heuristic dissector table, dump list of dissectors (filter_names) for that table
+ */
+void
+dissector_dump_heur_decodes(void)
+{
+ dissector_all_heur_tables_foreach_table(dissector_dump_heur_decodes_display, NULL, NULL);
+}
+
+
+heur_dissector_list_t
+register_heur_dissector_list(const char *name, const int proto)
+{
+ heur_dissector_list_t sub_dissectors;
+
+ /* Make sure the registration is unique */
+ if (g_hash_table_lookup(heur_dissector_lists, name) != NULL) {
+ ws_error("The heuristic dissector list %s is already registered - are you using a buggy plugin?", name);
+ }
+
+ /* Create and register the dissector table for this name; returns */
+ /* a pointer to the dissector table. */
+ sub_dissectors = g_slice_new(struct heur_dissector_list);
+ sub_dissectors->protocol = (proto == -1) ? NULL : find_protocol_by_id(proto);
+ sub_dissectors->dissectors = NULL; /* initially empty */
+ g_hash_table_insert(heur_dissector_lists, (gpointer)name,
+ (gpointer) sub_dissectors);
+ return sub_dissectors;
+}
+
+/*
+ * Register dissectors by name; used if one dissector always calls a
+ * particular dissector, or if it bases the decision of which dissector
+ * to call on something other than a numerical value or on "try a bunch
+ * of dissectors until one likes the packet".
+ */
+
+/* Get the long name of the protocol for a dissector handle, if it has
+ a protocol. */
+const char *
+dissector_handle_get_protocol_long_name(const dissector_handle_t handle)
+{
+ if (handle == NULL || handle->protocol == NULL) {
+ return NULL;
+ }
+ return proto_get_protocol_long_name(handle->protocol);
+}
+
+/* Get the short name of the protocol for a dissector handle, if it has
+ a protocol. */
+const char *
+dissector_handle_get_protocol_short_name(const dissector_handle_t handle)
+{
+ if (handle == NULL || handle->protocol == NULL) {
+ return NULL;
+ }
+ return proto_get_protocol_short_name(handle->protocol);
+}
+
+/* For backwards source and binary compatibility */
+const char *
+dissector_handle_get_short_name(const dissector_handle_t handle)
+{
+ return dissector_handle_get_protocol_short_name(handle);
+}
+
+/* Get the description for what the dissector in the dissector handle
+ dissects, if it has one. */
+const char *
+dissector_handle_get_description(const dissector_handle_t handle)
+{
+ return handle->description;
+}
+
+/* Get the index of the protocol for a dissector handle, if it has
+ a protocol. */
+int
+dissector_handle_get_protocol_index(const dissector_handle_t handle)
+{
+ if (handle->protocol == NULL) {
+ /*
+ * No protocol (see, for example, the handle for
+ * dissecting the set of protocols where the first
+ * octet of the payload is an OSI network layer protocol
+ * ID).
+ */
+ return -1;
+ }
+ return proto_get_id(handle->protocol);
+}
+
+/* Get a GList of all registered dissector names. The content of the list
+ is owned by the hash table and should not be modified or freed.
+ Use g_list_free() when done using the list. */
+GList*
+get_dissector_names(void)
+{
+ return g_hash_table_get_keys(registered_dissectors);
+}
+
+/* Find a registered dissector by name. */
+dissector_handle_t
+find_dissector(const char *name)
+{
+ return (dissector_handle_t)g_hash_table_lookup(registered_dissectors, name);
+}
+
+/** Find a dissector by name and add parent protocol as a depedency*/
+dissector_handle_t find_dissector_add_dependency(const char *name, const int parent_proto)
+{
+ dissector_handle_t handle = (dissector_handle_t)g_hash_table_lookup(registered_dissectors, name);
+ if ((handle != NULL) && (parent_proto > 0))
+ {
+ register_depend_dissector(proto_get_protocol_short_name(find_protocol_by_id(parent_proto)), dissector_handle_get_protocol_short_name(handle));
+ }
+
+ return handle;
+}
+
+/* Get a dissector name from handle. */
+const char *
+dissector_handle_get_dissector_name(const dissector_handle_t handle)
+{
+ if (handle == NULL) {
+ return NULL;
+ }
+ return handle->name;
+}
+
+static dissector_handle_t
+new_dissector_handle(enum dissector_e type, void *dissector, const int proto, const char *name, const char *description, void *cb_data)
+{
+ struct dissector_handle *handle;
+
+ handle = wmem_new(wmem_epan_scope(), struct dissector_handle);
+ handle->name = name;
+ handle->description = description;
+ handle->dissector_type = type;
+ handle->dissector_func = dissector;
+ handle->dissector_data = cb_data;
+ handle->protocol = find_protocol_by_id(proto);
+
+ if (handle->description == NULL) {
+ /*
+ * No description for what this dissector dissects
+ * was supplied; use the short name for the protocol,
+ * if we have the protocol.
+ *
+ * (We may have no protocol; see, for example, the handle
+ * for dissecting the set of protocols where the first
+ * octet of the payload is an OSI network layer protocol
+ * ID.)
+ */
+ if (handle->protocol != NULL)
+ handle->description = proto_get_protocol_short_name(handle->protocol);
+ }
+ return handle;
+}
+
+/* Create an anonymous handle for a new dissector. */
+dissector_handle_t
+create_dissector_handle(dissector_t dissector, const int proto)
+{
+ return new_dissector_handle(DISSECTOR_TYPE_SIMPLE, dissector, proto, NULL, NULL, NULL);
+}
+
+dissector_handle_t
+create_dissector_handle_with_name(dissector_t dissector,
+ const int proto, const char* name)
+{
+ return new_dissector_handle(DISSECTOR_TYPE_SIMPLE, dissector, proto, name, NULL, NULL);
+}
+
+dissector_handle_t
+create_dissector_handle_with_name_and_description(dissector_t dissector,
+ const int proto,
+ const char* name,
+ const char* description)
+{
+ return new_dissector_handle(DISSECTOR_TYPE_SIMPLE, dissector, proto, name, description, NULL);
+}
+
+dissector_handle_t
+create_dissector_handle_with_data(dissector_cb_t dissector, const int proto, void* cb_data)
+{
+ return new_dissector_handle(DISSECTOR_TYPE_CALLBACK, dissector, proto, NULL, NULL, cb_data);
+}
+
+/* Destroy an anonymous handle for a dissector. */
+static void
+destroy_dissector_handle(dissector_handle_t handle)
+{
+ if (handle == NULL) return;
+
+ dissector_delete_from_all_tables(handle);
+ deregister_postdissector(handle);
+ wmem_free(wmem_epan_scope(), handle);
+}
+
+static void
+check_valid_dissector_name_or_fail(const char *name)
+{
+ if (proto_check_field_name(name)) {
+ ws_error("Dissector name \"%s\" has one or more invalid characters."
+ " Allowed are letters, digits, '-', '_' and non-repeating '.'."
+ " This might be caused by an inappropriate plugin or a development error.", name);
+ }
+}
+
+static dissector_handle_t
+register_dissector_handle(const char *name, dissector_handle_t handle)
+{
+ gboolean new_entry;
+
+ /* Make sure name is "parsing friendly" - descriptions should be
+ * used for complicated phrases. */
+ check_valid_dissector_name_or_fail(name);
+
+ new_entry = g_hash_table_insert(registered_dissectors, (gpointer)name, handle);
+ if (!new_entry) {
+ /* Make sure the registration is unique */
+ ws_error("dissector handle name \"%s\" is already registered", name);
+ }
+
+ return handle;
+}
+
+/* Register a new dissector by name. */
+dissector_handle_t
+register_dissector(const char *name, dissector_t dissector, const int proto)
+{
+ struct dissector_handle *handle;
+
+ handle = new_dissector_handle(DISSECTOR_TYPE_SIMPLE, dissector, proto, name, NULL, NULL);
+
+ return register_dissector_handle(name, handle);
+}
+
+dissector_handle_t
+register_dissector_with_description(const char *name, const char *description, dissector_t dissector, const int proto)
+{
+ struct dissector_handle *handle;
+
+ handle = new_dissector_handle(DISSECTOR_TYPE_SIMPLE, dissector, proto, name, description, NULL);
+
+ return register_dissector_handle(name, handle);
+}
+
+dissector_handle_t
+register_dissector_with_data(const char *name, dissector_cb_t dissector, const int proto, void *cb_data)
+{
+ struct dissector_handle *handle;
+
+ handle = new_dissector_handle(DISSECTOR_TYPE_CALLBACK, dissector, proto, name, NULL, cb_data);
+
+ return register_dissector_handle(name, handle);
+}
+
+static gboolean
+remove_depend_dissector_from_list(depend_dissector_list_t sub_dissectors, const char *dependent)
+{
+ GSList *found_entry;
+
+ found_entry = g_slist_find_custom(sub_dissectors->dissectors,
+ dependent, (GCompareFunc)strcmp);
+
+ if (found_entry) {
+ g_free(found_entry->data);
+ sub_dissectors->dissectors = g_slist_delete_link(sub_dissectors->dissectors, found_entry);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+remove_depend_dissector_ghfunc(gpointer key _U_, gpointer value, gpointer user_data)
+{
+ depend_dissector_list_t sub_dissectors = (depend_dissector_list_t) value;
+ const char *dependent = (const char *)user_data;
+
+ remove_depend_dissector_from_list(sub_dissectors, dependent);
+}
+
+/* Deregister a dissector by name. */
+void
+deregister_dissector(const char *name)
+{
+ dissector_handle_t handle = find_dissector(name);
+ if (handle == NULL) return;
+
+ g_hash_table_remove(registered_dissectors, name);
+ g_hash_table_remove(depend_dissector_lists, name);
+ g_hash_table_foreach(depend_dissector_lists, remove_depend_dissector_ghfunc, (gpointer)name);
+ g_hash_table_remove(heur_dissector_lists, name);
+
+ destroy_dissector_handle(handle);
+}
+
+/* Call a dissector through a handle but if the dissector rejected it
+ * return 0.
+ */
+int
+call_dissector_only(dissector_handle_t handle, tvbuff_t *tvb,
+ packet_info *pinfo, proto_tree *tree, void *data)
+{
+ int ret;
+
+ DISSECTOR_ASSERT(handle != NULL);
+ ret = call_dissector_work(handle, tvb, pinfo, tree, TRUE, data);
+ return ret;
+}
+
+/* Call a dissector through a handle and if this fails call the "data"
+ * dissector.
+ */
+int
+call_dissector_with_data(dissector_handle_t handle, tvbuff_t *tvb,
+ packet_info *pinfo, proto_tree *tree, void *data)
+{
+ int ret;
+
+ ret = call_dissector_only(handle, tvb, pinfo, tree, data);
+ if (ret == 0) {
+ /*
+ * The protocol was disabled, or the dissector rejected
+ * it. Just dissect this packet as data.
+ */
+ DISSECTOR_ASSERT(data_handle->protocol != NULL);
+ call_dissector_work(data_handle, tvb, pinfo, tree, TRUE, NULL);
+ return tvb_captured_length(tvb);
+ }
+ return ret;
+}
+
+int
+call_dissector(dissector_handle_t handle, tvbuff_t *tvb,
+ packet_info *pinfo, proto_tree *tree)
+{
+ return call_dissector_with_data(handle, tvb, pinfo, tree, NULL);
+}
+
+int
+call_data_dissector(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ return call_dissector_work(data_handle, tvb, pinfo, tree, TRUE, NULL);
+}
+
+/*
+ * Call a heuristic dissector through a heur_dtbl_entry
+ */
+void call_heur_dissector_direct(heur_dtbl_entry_t *heur_dtbl_entry, tvbuff_t *tvb,
+ packet_info *pinfo, proto_tree *tree, void *data)
+{
+ const char *saved_curr_proto;
+ const char *saved_heur_list_name;
+ guint16 saved_can_desegment;
+ guint saved_layers_len = 0;
+
+ DISSECTOR_ASSERT(heur_dtbl_entry);
+
+ /* can_desegment is set to 2 by anyone which offers this api/service.
+ then everytime a subdissector is called it is decremented by one.
+ thus only the subdissector immediately ontop of whoever offers this
+ service can use it.
+ We save the current value of "can_desegment" for the
+ benefit of TCP proxying dissectors such as SOCKS, so they
+ can restore it and allow the dissectors they call to use
+ the desegmentation service.
+ */
+ saved_can_desegment = pinfo->can_desegment;
+ pinfo->saved_can_desegment = saved_can_desegment;
+ pinfo->can_desegment = saved_can_desegment-(saved_can_desegment>0);
+
+ saved_curr_proto = pinfo->current_proto;
+ saved_heur_list_name = pinfo->heur_list_name;
+
+ saved_layers_len = wmem_list_count(pinfo->layers);
+
+ if (!heur_dtbl_entry->enabled ||
+ (heur_dtbl_entry->protocol != NULL && !proto_is_protocol_enabled(heur_dtbl_entry->protocol))) {
+ DISSECTOR_ASSERT(data_handle->protocol != NULL);
+ call_dissector_work(data_handle, tvb, pinfo, tree, TRUE, NULL);
+ return;
+ }
+
+ if (heur_dtbl_entry->protocol != NULL) {
+ /* do NOT change this behavior - wslua uses the protocol short name set here in order
+ to determine which Lua-based heuristic dissector to call */
+ pinfo->current_proto = proto_get_protocol_short_name(heur_dtbl_entry->protocol);
+ add_layer(pinfo, proto_get_id(heur_dtbl_entry->protocol));
+ }
+
+ pinfo->heur_list_name = heur_dtbl_entry->list_name;
+
+ /* call the dissector, in case of failure call data handle (might happen with exported PDUs) */
+ if (!(*heur_dtbl_entry->dissector)(tvb, pinfo, tree, data)) {
+ call_dissector_work(data_handle, tvb, pinfo, tree, TRUE, NULL);
+
+ /*
+ * We added a protocol layer above. The dissector
+ * didn't accept the packet or it didn't add any
+ * items to the tree so remove it from the list.
+ */
+ while (wmem_list_count(pinfo->layers) > saved_layers_len) {
+ remove_last_layer(pinfo, TRUE);
+ }
+ }
+
+ /* Restore info from caller */
+ pinfo->can_desegment = saved_can_desegment;
+ pinfo->current_proto = saved_curr_proto;
+ pinfo->heur_list_name = saved_heur_list_name;
+
+}
+
+static gint
+find_matching_proto_name(gconstpointer arg1, gconstpointer arg2)
+{
+ const char *protocol_name = (const char*)arg1;
+ const gchar *name = (const gchar *)arg2;
+
+ return strcmp(protocol_name, name);
+}
+
+gboolean register_depend_dissector(const char* parent, const char* dependent)
+{
+ GSList *list_entry;
+ depend_dissector_list_t sub_dissectors;
+
+ if ((parent == NULL) || (dependent == NULL))
+ {
+ /* XXX - assert on parent? */
+ return FALSE;
+ }
+
+ sub_dissectors = find_depend_dissector_list(parent);
+ if (sub_dissectors == NULL) {
+ /* parent protocol doesn't exist, create it */
+ sub_dissectors = g_slice_new(struct depend_dissector_list);
+ sub_dissectors->dissectors = NULL; /* initially empty */
+ g_hash_table_insert(depend_dissector_lists, (gpointer)g_strdup(parent), (gpointer) sub_dissectors);
+ }
+
+ /* Verify that sub-dissector is not already in the list */
+ list_entry = g_slist_find_custom(sub_dissectors->dissectors, (gpointer)dependent, find_matching_proto_name);
+ if (list_entry != NULL)
+ return TRUE; /* Dependency already exists */
+
+ sub_dissectors->dissectors = g_slist_prepend(sub_dissectors->dissectors, (gpointer)g_strdup(dependent));
+ return TRUE;
+}
+
+gboolean deregister_depend_dissector(const char* parent, const char* dependent)
+{
+ depend_dissector_list_t sub_dissectors = find_depend_dissector_list(parent);
+
+ /* sanity check */
+ ws_assert(sub_dissectors != NULL);
+
+ return remove_depend_dissector_from_list(sub_dissectors, dependent);
+}
+
+depend_dissector_list_t find_depend_dissector_list(const char* name)
+{
+ return (depend_dissector_list_t)g_hash_table_lookup(depend_dissector_lists, name);
+}
+
+/*
+ * Dumps the "layer type"/"decode as" associations to stdout, similar
+ * to the proto_registrar_dump_*() routines.
+ *
+ * There is one record per line. The fields are tab-delimited.
+ *
+ * Field 1 = layer type, e.g. "tcp.port"
+ * Field 2 = selector in decimal
+ * Field 3 = "decode as" name, e.g. "http"
+ */
+
+
+static void
+dissector_dump_decodes_display(const gchar *table_name,
+ ftenum_t selector_type _U_, gpointer key, gpointer value,
+ gpointer user_data _U_)
+{
+ guint32 selector = GPOINTER_TO_UINT (key);
+ dissector_table_t sub_dissectors = find_dissector_table(table_name);
+ dtbl_entry_t *dtbl_entry;
+ dissector_handle_t handle;
+ gint proto_id;
+ const gchar *decode_as;
+
+ ws_assert(sub_dissectors);
+ switch (sub_dissectors->type) {
+
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ dtbl_entry = (dtbl_entry_t *)value;
+ ws_assert(dtbl_entry);
+
+ handle = dtbl_entry->current;
+ ws_assert(handle);
+
+ proto_id = dissector_handle_get_protocol_index(handle);
+
+ if (proto_id != -1) {
+ decode_as = proto_get_protocol_filter_name(proto_id);
+ ws_assert(decode_as != NULL);
+ printf("%s\t%u\t%s\n", table_name, selector, decode_as);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void
+dissector_dump_decodes(void)
+{
+ dissector_all_tables_foreach(dissector_dump_decodes_display, NULL);
+}
+
+/*
+ * Dumps information about dissector tables to stdout.
+ *
+ * There is one record per line. The fields are tab-delimited.
+ *
+ * Field 1 = dissector table name, e.g. "tcp.port"
+ * Field 2 = name used for the dissector table in the GUI
+ * Field 3 = type (textual representation of the ftenum type)
+ * Field 4 = base for display (for integer types)
+ * Field 5 = protocol name
+ * Field 6 = "decode as" support
+ *
+ * This does not dump the *individual entries* in the dissector tables,
+ * i.e. it doesn't show what dissector handles what particular value
+ * of the key in the dissector table.
+ */
+
+static void
+dissector_dump_dissector_tables_display (gpointer key, gpointer user_data _U_)
+{
+ const char *table_name = (const char *)key;
+ dissector_table_t table;
+
+ table = (dissector_table_t)g_hash_table_lookup(dissector_tables, key);
+ printf("%s\t%s\t%s", table_name, table->ui_name, ftype_name(table->type));
+ switch (table->type) {
+
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ switch(table->param) {
+
+ case BASE_NONE:
+ printf("\tBASE_NONE");
+ break;
+
+ case BASE_DEC:
+ printf("\tBASE_DEC");
+ break;
+
+ case BASE_HEX:
+ printf("\tBASE_HEX");
+ break;
+
+ case BASE_DEC_HEX:
+ printf("\tBASE_DEC_HEX");
+ break;
+
+ case BASE_HEX_DEC:
+ printf("\tBASE_HEX_DEC");
+ break;
+
+ default:
+ printf("\t%d", table->param);
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ if (table->protocol != NULL) {
+ printf("\t%s",
+ proto_get_protocol_short_name(table->protocol));
+ } else
+ printf("\t(no protocol)");
+ printf("\tDecode As %ssupported",
+ table->supports_decode_as ? "" : "not ");
+ printf("\n");
+}
+
+static gint
+compare_dissector_key_name(gconstpointer dissector_a, gconstpointer dissector_b)
+{
+ return strcmp((const char*)dissector_a, (const char*)dissector_b);
+}
+
+void
+dissector_dump_dissector_tables(void)
+{
+ GList *list;
+
+ list = g_hash_table_get_keys(dissector_tables);
+ list = g_list_sort(list, compare_dissector_key_name);
+ g_list_foreach(list, dissector_dump_dissector_tables_display, NULL);
+ g_list_free(list);
+}
+
+/*
+ * Dumps the entries in the table of registered dissectors.
+ *
+ * There is one record per line. The fields are tab-delimited.
+ *
+ * Field 1 = dissector name
+ * Field 2 = dissector description
+ */
+
+struct dissector_info {
+ const char *name;
+ const char *description;
+};
+
+static int
+compare_dissector_info_names(const void *arg1, const void *arg2)
+{
+ const struct dissector_info *info1 = (const struct dissector_info *) arg1;
+ const struct dissector_info *info2 = (const struct dissector_info *) arg2;
+
+ return strcmp(info1->name, info2->name);
+}
+
+void
+dissector_dump_dissectors(void)
+{
+ GHashTableIter iter;
+ struct dissector_info *dissectors_info;
+ guint num_protocols;
+ gpointer key, value;
+ guint proto_index;
+
+ g_hash_table_iter_init(&iter, registered_dissectors);
+ num_protocols = g_hash_table_size(registered_dissectors);
+ dissectors_info = g_new(struct dissector_info, num_protocols);
+ proto_index = 0;
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ dissectors_info[proto_index].name = (const char *)key;
+ dissectors_info[proto_index].description =
+ ((dissector_handle_t) value)->description;
+ proto_index++;
+ }
+ qsort(dissectors_info, num_protocols, sizeof(struct dissector_info),
+ compare_dissector_info_names);
+ for (proto_index = 0; proto_index < num_protocols; proto_index++) {
+ printf("%s\t%s\n", dissectors_info[proto_index].name,
+ dissectors_info[proto_index].description);
+ }
+ g_free(dissectors_info);
+}
+
+void
+register_postdissector(dissector_handle_t handle)
+{
+ postdissector p;
+
+ if (!postdissectors)
+ postdissectors = g_array_sized_new(FALSE, FALSE, (guint)sizeof(postdissector), 1);
+
+ p.handle = handle;
+ p.wanted_hfids = NULL;
+ postdissectors = g_array_append_val(postdissectors, p);
+}
+
+void
+set_postdissector_wanted_hfids(dissector_handle_t handle, GArray *wanted_hfids)
+{
+ guint i;
+
+ if (!postdissectors) return;
+
+ for (i = 0; i < postdissectors->len; i++) {
+ if (POSTDISSECTORS(i).handle == handle) {
+ if (POSTDISSECTORS(i).wanted_hfids) {
+ g_array_free(POSTDISSECTORS(i).wanted_hfids, TRUE);
+ }
+ POSTDISSECTORS(i).wanted_hfids = wanted_hfids;
+ break;
+ }
+ }
+}
+
+void
+deregister_postdissector(dissector_handle_t handle)
+{
+ guint i;
+
+ if (!postdissectors) return;
+
+ for (i = 0; i < postdissectors->len; i++) {
+ if (POSTDISSECTORS(i).handle == handle) {
+ if (POSTDISSECTORS(i).wanted_hfids) {
+ g_array_free(POSTDISSECTORS(i).wanted_hfids, TRUE);
+ }
+ postdissectors = g_array_remove_index_fast(postdissectors, i);
+ break;
+ }
+ }
+}
+
+gboolean
+have_postdissector(void)
+{
+ guint i;
+ dissector_handle_t handle;
+
+ for (i = 0; i < postdissectors->len; i++) {
+ handle = POSTDISSECTORS(i).handle;
+
+ if (handle->protocol != NULL
+ && proto_is_protocol_enabled(handle->protocol)) {
+ /* We have at least one enabled postdissector */
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+void
+call_all_postdissectors(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ guint i;
+
+ for (i = 0; i < postdissectors->len; i++) {
+ call_dissector_only(POSTDISSECTORS(i).handle,
+ tvb, pinfo, tree, NULL);
+ }
+}
+
+gboolean
+postdissectors_want_hfids(void)
+{
+ guint i;
+
+ for (i = 0; i < postdissectors->len; i++) {
+ if (POSTDISSECTORS(i).wanted_hfids != NULL &&
+ POSTDISSECTORS(i).wanted_hfids->len != 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void
+prime_epan_dissect_with_postdissector_wanted_hfids(epan_dissect_t *edt)
+{
+ guint i;
+
+ if (postdissectors == NULL) {
+ /*
+ * No postdissector expressed an interest in any hfids.
+ */
+ return;
+ }
+ for (i = 0; i < postdissectors->len; i++) {
+ if (POSTDISSECTORS(i).wanted_hfids != NULL &&
+ POSTDISSECTORS(i).wanted_hfids->len != 0)
+ epan_dissect_prime_with_hfid_array(edt,
+ POSTDISSECTORS(i).wanted_hfids);
+ }
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */