aboutsummaryrefslogtreecommitdiffstats
path: root/epan/packet.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/packet.c')
-rw-r--r--epan/packet.c668
1 files changed, 505 insertions, 163 deletions
diff --git a/epan/packet.c b/epan/packet.c
index c087659657..322b4f491d 100644
--- a/epan/packet.c
+++ b/epan/packet.c
@@ -32,6 +32,7 @@
#include <epan/wmem_scopes.h>
+#include <epan/column-info.h>
#include <epan/exceptions.h>
#include <epan/reassemble.h>
#include <epan/stream.h>
@@ -43,7 +44,7 @@
#include <wsutil/wslog.h>
#include <wsutil/ws_assert.h>
-static gint proto_malformed = -1;
+static gint proto_malformed;
static dissector_handle_t frame_handle = NULL;
static dissector_handle_t file_handle = NULL;
static dissector_handle_t data_handle = NULL;
@@ -157,6 +158,7 @@ destroy_depend_dissector_list(void *data)
* A heuristics dissector list.
*/
struct heur_dissector_list {
+ const char *ui_name;
protocol_t *protocol;
GSList *dissectors;
};
@@ -235,8 +237,32 @@ packet_cache_proto_handles(void)
/* 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);
@@ -316,7 +342,7 @@ init_dissection(void)
{
/*
* Reinitialize resolution information. Don't leak host entries from
- * one file to another (e.g. embarassing-host-name.example.com from
+ * one file to another (e.g. embarrassing-host-name.example.com from
* file1.pcapng into a name resolution block in file2.pcapng).
*/
host_name_lookup_reset();
@@ -422,7 +448,7 @@ 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;
+ struct data_source *this_source = (struct data_source *)source->data;
if (this_source->name && strcmp(this_source->name, name) == 0) {
return this_source->tvb;
}
@@ -444,11 +470,17 @@ free_data_sources(packet_info *pinfo)
}
void
-mark_frame_as_depended_upon(packet_info *pinfo, guint32 frame_num)
+mark_frame_as_depended_upon(frame_data *fd, guint32 frame_num)
{
/* Don't mark a frame as dependent on itself */
- if (frame_num != pinfo->num) {
- pinfo->dependent_frames = g_slist_prepend(pinfo->dependent_frames, GUINT_TO_POINTER(frame_num));
+ 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));
}
}
@@ -589,15 +621,23 @@ dissect_record(epan_dissect_t *edt, int file_type_subtype,
clear_address(&edt->pi.dst);
edt->pi.noreassembly_reason = "";
edt->pi.ptype = PT_NONE;
- edt->pi.use_endpoint = FALSE;
- edt->pi.conv_endpoint = NULL;
+ 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.
@@ -660,8 +700,9 @@ dissect_file(epan_dissect_t *edt, wtap_rec *rec,
clear_address(&edt->pi.dst);
edt->pi.noreassembly_reason = "";
edt->pi.ptype = PT_NONE;
- edt->pi.use_endpoint = FALSE;
- edt->pi.conv_endpoint = NULL;
+ 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);
@@ -721,12 +762,74 @@ enum dissector_e {
*/
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
@@ -786,15 +889,16 @@ call_dissector_work_error(dissector_handle_t handle, tvbuff_t *tvb,
#define PINFO_LAYER_MAX_RECURSION_DEPTH 500
static int
-call_dissector_work(dissector_handle_t handle, tvbuff_t *tvb, packet_info *pinfo_arg,
+call_dissector_work(dissector_handle_t handle, tvbuff_t *tvb, packet_info *pinfo,
proto_tree *tree, gboolean add_proto_name, void *data)
{
- packet_info *pinfo = pinfo_arg;
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;
+ unsigned saved_desegment_len = pinfo->desegment_len;
+ bool consumed_none;
if (handle->protocol != NULL &&
!proto_is_protocol_enabled(handle->protocol)) {
@@ -812,7 +916,7 @@ call_dissector_work(dissector_handle_t handle, tvbuff_t *tvb, packet_info *pinfo
/*
* can_desegment is set to 2 by anyone which offers the
* desegmentation api/service.
- * Then everytime a subdissector is called it is decremented
+ * Then every time a subdissector is called it is decremented
* by one.
* Thus only the subdissector immediately on top of whoever
* offers this service can use it.
@@ -834,8 +938,7 @@ call_dissector_work(dissector_handle_t handle, tvbuff_t *tvb, packet_info *pinfo
*/
/* XXX Should we check for a duplicate layer here? */
if (add_proto_name) {
- pinfo->curr_layer_num++;
- wmem_list_append(pinfo->layers, GINT_TO_POINTER(proto_get_id(handle->protocol)));
+ add_layer(pinfo, proto_get_id(handle->protocol));
}
}
@@ -847,24 +950,35 @@ call_dissector_work(dissector_handle_t handle, tvbuff_t *tvb, packet_info *pinfo
*/
len = call_dissector_through_handle(handle, tvb, pinfo, tree, data);
}
+ consumed_none = len == 0 || (pinfo->desegment_len != saved_desegment_len && pinfo->desegment_offset == 0);
+ /* If len == 0, then the dissector didn't accept the packet.
+ * In the latter case, the dissector accepted the packet, but didn't
+ * consume any bytes because they all belong in a later segment.
+ * In the latter case, we probably won't call a dissector here again
+ * on the next pass, so removing the layer keeps any *further* layers
+ * past this one the same on subsequent passes.
+ *
+ * XXX: DISSECTOR_ASSERT that the tree count didn't change? If the
+ * dissector didn't consume any bytes but added items to the tree,
+ * that's improper behavior and needs a rethink. We could also move the
+ * test that the packet didn't change desegment_offset and desegment_len
+ * while rejecting the packet from packet-tcp.c decode_tcp_ports to here.
+ */
if (handle->protocol != NULL && !proto_is_pino(handle->protocol) && add_proto_name &&
- (len == 0 || (tree && saved_tree_count == tree->tree_data->count))) {
+ (consumed_none || (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
+ * consume any data or we didn't add any items to the
* tree. Remove it.
*/
while (wmem_list_count(pinfo->layers) > saved_layers_len) {
- if (len == 0) {
- /*
- * 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.
- */
- pinfo->curr_layer_num--;
- }
- wmem_list_remove_frame(pinfo->layers, wmem_list_tail(pinfo->layers));
+ /*
+ * Only reduce the layer number if the dissector didn't
+ * consume any 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, consumed_none);
}
}
pinfo->current_proto = saved_proto;
@@ -1155,43 +1269,8 @@ void dissector_add_uint_range(const char *name, range_t *range,
}
}
-static void
-dissector_add_preference(const char *name, dissector_handle_t handle, guint init_value)
-{
- guint* uint_var;
- module_t *module;
- gchar *description, *title;
- dissector_table_t pref_dissector_table = find_dissector_table(name);
- int proto_id = proto_get_id(handle->protocol);
-
- uint_var = wmem_new(wmem_epan_scope(), guint);
- *uint_var = init_value;
-
- /* 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);
- }
-
- description = wmem_strdup_printf(wmem_epan_scope(), "Set the %s for %s (if other than the default of %u)",
- pref_dissector_table->ui_name, proto_get_protocol_short_name(handle->protocol), *uint_var);
- title = wmem_strdup_printf(wmem_epan_scope(), "%s %s", proto_get_protocol_short_name(handle->protocol),
- pref_dissector_table->ui_name);
-
- prefs_register_decode_as_preference(module, name, title, description, uint_var);
-}
-
-void dissector_add_uint_with_preference(const char *name, const guint32 pattern,
- dissector_handle_t handle)
-{
- dissector_add_preference(name, handle, pattern);
- dissector_add_uint(name, pattern, handle);
-}
-
-void dissector_add_uint_range_with_preference(const char *name, const char* range_str,
- dissector_handle_t 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;
@@ -1216,8 +1295,13 @@ void dissector_add_uint_range_with_preference(const char *name, const char* rang
routine to apply preferences, which could duplicate the
registration of a preference. Check for that here */
if (prefs_find_preference(module, name) == NULL) {
- description = wmem_strdup_printf(wmem_epan_scope(), "%s %s(s)",
+ 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 */
@@ -1245,14 +1329,34 @@ void dissector_add_uint_range_with_preference(const char *name, const char* rang
prefs_register_decode_as_range_preference(module, name, title, description, range, max_value);
}
- dissector_add_uint_range(name, *range, handle);
+ 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 */
+/* be consistent 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. */
@@ -1294,6 +1398,36 @@ void dissector_delete_uint_range(const char *name, range_t *range,
}
}
+/* Remove an entry from a guid dissector table. */
+void dissector_delete_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);
+
+ /* sanity check */
+ ws_assert(sub_dissectors);
+
+ /* Find the table entry */
+ dtbl_entry = (dtbl_entry_t *)g_hash_table_lookup(sub_dissectors->hash_table, guid_val);
+
+ if (dtbl_entry == NULL) {
+ fprintf(stderr, "OOPS: guid not found in dissector table \"%s\"\n", name);
+ return;
+ }
+
+ /* Make sure the handles match */
+ if (dtbl_entry->current != handle) {
+ fprintf(stderr, "OOPS: handle does not match for guid in dissector table \"%s\"\n", name);
+ return;
+ }
+
+ /* Remove the table entry */
+ g_hash_table_remove(sub_dissectors->hash_table, guid_val);
+}
+
+
static gboolean
dissector_delete_all_check (gpointer key _U_, gpointer value, gpointer user_data)
{
@@ -1403,6 +1537,22 @@ dissector_reset_uint(const char *name, const guint32 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. */
@@ -1525,7 +1675,7 @@ find_string_dtbl_entry(dissector_table_t const sub_dissectors, const gchar *patt
ws_assert_not_reached();
}
- if (sub_dissectors->param == TRUE) {
+ if (sub_dissectors->param == STRING_CASE_INSENSITIVE) {
key = g_ascii_strdown(pattern, -1);
} else {
key = g_strdup(pattern);
@@ -1593,7 +1743,7 @@ dissector_add_string(const char *name, const gchar *pattern,
dtbl_entry->current = handle;
dtbl_entry->initial = dtbl_entry->current;
- if (sub_dissectors->param == TRUE) {
+ if (sub_dissectors->param == STRING_CASE_INSENSITIVE) {
key = g_ascii_strdown(pattern, -1);
} else {
key = g_strdup(pattern);
@@ -1616,7 +1766,7 @@ dissector_add_string(const char *name, const gchar *pattern,
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 consistent 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 */
@@ -1710,6 +1860,22 @@ dissector_reset_string(const char *name, const gchar *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. */
@@ -1963,7 +2129,7 @@ int dissector_try_guid_new(dissector_table_t sub_dissectors,
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);
+ 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,
@@ -2108,8 +2274,10 @@ dissector_add_for_decode_as(const char *name, dissector_handle_t handle)
return;
}
- /* Ensure the protocol is unique. This prevents confusion when
- using Decode As with duplicative entries.
+ /* 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. */
@@ -2118,7 +2286,8 @@ dissector_add_for_decode_as(const char *name, dissector_handle_t handle)
for (entry = sub_dissectors->dissector_handles; entry != NULL; entry = g_slist_next(entry))
{
dup_handle = (dissector_handle_t)entry->data;
- if (dup_handle->protocol == handle->protocol)
+ if (dup_handle->description != NULL &&
+ strcmp(dup_handle->description, handle->description) == 0)
{
const char *dissector_name, *dup_dissector_name;
@@ -2128,10 +2297,9 @@ dissector_add_for_decode_as(const char *name, dissector_handle_t handle)
dup_dissector_name = dissector_handle_get_dissector_name(dup_handle);
if (dup_dissector_name == NULL)
dup_dissector_name = "(anonymous)";
- fprintf(stderr, "Duplicate dissectors %s and %s for protocol %s in dissector table %s\n",
+ fprintf(stderr, "Dissectors %s and %s in dissector table %s have same dissector name %s\n",
dissector_name, dup_dissector_name,
- proto_get_protocol_short_name(handle->protocol),
- name);
+ name, handle->description);
if (wireshark_abort_on_dissector_bug)
abort();
}
@@ -2150,7 +2318,7 @@ void dissector_add_for_decode_as_with_preference(const char *name,
table value would default to 0.
Set up a preference value with that information
*/
- dissector_add_preference(name, handle, 0);
+ dissector_add_range_preference(name, handle, "");
dissector_add_for_decode_as(name, handle);
}
@@ -2173,7 +2341,7 @@ dissector_table_get_dissector_handles(dissector_table_t dissector_table) {
* Data structure used as user data when iterating dissector handles
*/
typedef struct lookup_entry {
- const gchar* dissector_short_name;
+ const gchar* dissector_description;
dissector_handle_t handle;
} lookup_entry_t;
@@ -2186,17 +2354,17 @@ 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 *proto_short_name = dissector_handle_get_short_name(handle);
- if (proto_short_name && strcmp(lookup->dissector_short_name, proto_short_name) == 0) {
+ 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* short_name)
+dissector_handle_t dissector_table_get_dissector_handle(dissector_table_t dissector_table, const gchar* description)
{
lookup_entry_t lookup;
- lookup.dissector_short_name = short_name;
+ lookup.dissector_description = description;
lookup.handle = NULL;
g_slist_foreach(dissector_table->dissector_handles, find_dissector_in_table, &lookup);
@@ -2215,22 +2383,27 @@ 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));
+ 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;
+ 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;
}
/**************************************************/
@@ -2345,7 +2518,7 @@ dissector_table_foreach_handle(const char *table_name,
for (tmp = sub_dissectors->dissector_handles; tmp != NULL;
tmp = g_slist_next(tmp))
- func(table_name, tmp->data, user_data);
+ func(table_name, tmp->data, user_data);
}
/*
@@ -2538,14 +2711,15 @@ register_dissector_table(const char *name, const char *ui_name, const int proto,
sub_dissectors->ui_name = ui_name;
sub_dissectors->type = type;
sub_dissectors->param = param;
- sub_dissectors->protocol = find_protocol_by_id(proto);
+ 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)
+ const char *ui_name, const int proto, GHashFunc hash_func, GEqualFunc key_equal_func,
+ GDestroyNotify key_destroy_func)
{
dissector_table_t sub_dissectors;
@@ -2560,14 +2734,14 @@ dissector_table_t register_custom_dissector_table(const char *name,
sub_dissectors->hash_func = hash_func;
sub_dissectors->hash_table = g_hash_table_new_full(hash_func,
key_equal_func,
- &g_free,
+ 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 = find_protocol_by_id(proto);
+ 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;
@@ -2639,19 +2813,7 @@ get_dissector_table_param(const char *name)
static void
check_valid_heur_name_or_fail(const char *heur_name)
{
- gboolean found_invalid = proto_check_field_name(heur_name);
-
- /* Additionally forbid upper case characters. */
- if (!found_invalid) {
- for (guint i = 0; heur_name[i]; i++) {
- if (g_ascii_isupper(heur_name[i])) {
- found_invalid = TRUE;
- break;
- }
- }
- }
-
- if (found_invalid) {
+ 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);
@@ -2736,6 +2898,7 @@ heur_dissector_add(const char *name, heur_dissector_t dissector, const char *dis
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);
@@ -2804,10 +2967,12 @@ dissector_try_heuristic(heur_dissector_list_t sub_dissectors, tvbuff_t *tvb,
heur_dtbl_entry_t *hdtbl_entry;
int proto_id;
int len;
+ bool consumed_none;
+ unsigned saved_desegment_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.
+ then every time 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
@@ -2853,34 +3018,36 @@ dissector_try_heuristic(heur_dissector_list_t sub_dissectors, tvbuff_t *tvb,
* Add the protocol name to the layers; we'll remove it
* if the dissector fails.
*/
- pinfo->curr_layer_num++;
- wmem_list_append(pinfo->layers, GINT_TO_POINTER(proto_id));
+ add_layer(pinfo, proto_id);
}
pinfo->heur_list_name = hdtbl_entry->list_name;
+ saved_desegment_len = pinfo->desegment_len;
len = (hdtbl_entry->dissector)(tvb, pinfo, tree, data);
+ consumed_none = len == 0 || (pinfo->desegment_len != saved_desegment_len && pinfo->desegment_offset == 0);
if (hdtbl_entry->protocol != NULL &&
- (len == 0 || (tree && saved_tree_count == tree->tree_data->count))) {
+ (consumed_none || (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
+ * didn't consume any data 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) {
- if (len == 0) {
- /*
- * 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.
- */
- pinfo->curr_layer_num--;
- }
- wmem_list_remove_frame(pinfo->layers, wmem_list_tail(pinfo->layers));
+ /*
+ * Only reduce the layer number if the dissector
+ * didn't consume 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, consumed_none);
}
}
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. */
@@ -2960,7 +3127,7 @@ dissector_all_heur_tables_foreach_table_func (gpointer key, gpointer value, gpoi
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);
+ (*info->caller_func)((gchar *)key, (struct heur_dissector_list *)value, info->caller_data);
}
/*
@@ -2970,10 +3137,10 @@ dissector_all_heur_tables_foreach_table_func (gpointer key, gpointer value, gpoi
static void
dissector_all_heur_tables_foreach_list_func (gpointer key, gpointer user_data)
{
- struct heur_dissector_list *list;
+ 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);
+ 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);
}
@@ -3010,10 +3177,13 @@ 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\n",
+ 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(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);
}
}
@@ -3034,7 +3204,7 @@ dissector_dump_heur_decodes(void)
heur_dissector_list_t
-register_heur_dissector_list(const char *name, const int proto)
+register_heur_dissector_list_with_description(const char *name, const char *ui_name, const int proto)
{
heur_dissector_list_t sub_dissectors;
@@ -3046,13 +3216,26 @@ register_heur_dissector_list(const char *name, const int proto)
/* 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 = find_protocol_by_id(proto);
+ sub_dissectors->protocol = (proto == -1) ? NULL : find_protocol_by_id(proto);
+ sub_dissectors->ui_name = ui_name;
sub_dissectors->dissectors = NULL; /* initially empty */
g_hash_table_insert(heur_dissector_lists, (gpointer)name,
(gpointer) sub_dissectors);
return sub_dissectors;
}
+heur_dissector_list_t
+register_heur_dissector_list(const char *name, const int proto)
+{
+ return register_heur_dissector_list_with_description(name, NULL, proto);
+}
+
+const char *
+heur_dissector_list_get_description(heur_dissector_list_t list)
+{
+ return list ? list->ui_name : NULL;
+}
+
/*
* Register dissectors by name; used if one dissector always calls a
* particular dissector, or if it bases the decision of which dissector
@@ -3063,7 +3246,7 @@ register_heur_dissector_list(const char *name, const int proto)
/* Get the long name of the protocol for a dissector handle, if it has
a protocol. */
const char *
-dissector_handle_get_long_name(const dissector_handle_t handle)
+dissector_handle_get_protocol_long_name(const dissector_handle_t handle)
{
if (handle == NULL || handle->protocol == NULL) {
return NULL;
@@ -3074,20 +3257,29 @@ dissector_handle_get_long_name(const dissector_handle_t handle)
/* Get the short name of the protocol for a dissector handle, if it has
a protocol. */
const char *
-dissector_handle_get_short_name(const dissector_handle_t handle)
+dissector_handle_get_protocol_short_name(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).
- */
+ 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
@@ -3111,6 +3303,10 @@ dissector_handle_get_protocol_index(const dissector_handle_t handle)
GList*
get_dissector_names(void)
{
+ if (!registered_dissectors) {
+ return NULL;
+ }
+
return g_hash_table_get_keys(registered_dissectors);
}
@@ -3121,13 +3317,13 @@ 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*/
+/** Find a dissector by name and add parent protocol as a dependency*/
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_short_name(handle));
+ register_depend_dissector(proto_get_protocol_short_name(find_protocol_by_id(parent_proto)), dissector_handle_get_protocol_short_name(handle));
}
return handle;
@@ -3144,16 +3340,32 @@ dissector_handle_get_dissector_name(const dissector_handle_t handle)
}
static dissector_handle_t
-new_dissector_handle(enum dissector_e type, void *dissector, const int proto, const char *name, void *cb_data)
+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;
}
@@ -3161,14 +3373,29 @@ new_dissector_handle(enum dissector_e type, void *dissector, const int proto, co
dissector_handle_t
create_dissector_handle(dissector_t dissector, const int proto)
{
- return new_dissector_handle(DISSECTOR_TYPE_SIMPLE, dissector, proto, NULL, NULL);
+ 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);
+ 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. */
@@ -3182,13 +3409,30 @@ destroy_dissector_handle(dissector_handle_t 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)
{
- /* Make sure the registration is unique */
- ws_assert(g_hash_table_lookup(registered_dissectors, name) == NULL);
+ gboolean new_entry;
+
+ /* Make sure name is "parsing friendly" - descriptions should be
+ * used for complicated phrases. */
+ check_valid_dissector_name_or_fail(name);
- g_hash_table_insert(registered_dissectors, (gpointer)name, handle);
+ 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;
}
@@ -3199,7 +3443,17 @@ 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);
+ 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);
}
@@ -3209,7 +3463,7 @@ register_dissector_with_data(const char *name, dissector_cb_t dissector, const i
{
struct dissector_handle *handle;
- handle = new_dissector_handle(DISSECTOR_TYPE_CALLBACK, dissector, proto, name, cb_data);
+ handle = new_dissector_handle(DISSECTOR_TYPE_CALLBACK, dissector, proto, name, NULL, cb_data);
return register_dissector_handle(name, handle);
}
@@ -3318,7 +3572,7 @@ void call_heur_dissector_direct(heur_dtbl_entry_t *heur_dtbl_entry, tvbuff_t *tv
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.
+ then every time 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
@@ -3346,27 +3600,30 @@ void call_heur_dissector_direct(heur_dtbl_entry_t *heur_dtbl_entry, tvbuff_t *tv
/* 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);
- pinfo->curr_layer_num++;
- wmem_list_append(pinfo->layers, GINT_TO_POINTER(proto_get_id(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) {
- pinfo->curr_layer_num--;
- wmem_list_remove_frame(pinfo->layers, wmem_list_tail(pinfo->layers));
+ remove_last_layer(pinfo, TRUE);
}
+
+ call_dissector_work(data_handle, tvb, pinfo, tree, TRUE, NULL);
}
+ /* XXX: Remove layers if it was accepted but didn't actually consume
+ * data due to desegmentation? (Currently the only callers of this
+ * are UDP and exported PDUs, so not yet necessary.)
+ */
+
/* Restore info from caller */
pinfo->can_desegment = saved_can_desegment;
pinfo->current_proto = saved_curr_proto;
@@ -3484,17 +3741,22 @@ dissector_dump_decodes(void)
}
/*
- * Dumps the "layer type"/"decode as" associations to stdout, similar
- * to the proto_registrar_dump_*() routines.
+ * Dumps information about dissector tables to stdout.
*
* 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"
+ * 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_)
{
@@ -3550,6 +3812,30 @@ dissector_dump_dissector_tables_display (gpointer key, gpointer user_data _U_)
printf("\n");
}
+/** The output format of this function is meant to parallel
+ * that of dissector_dump_dissector_tables_display().
+ * Field 3 is shown as "heuristic".
+ * Field 4 is omitted, as it is for FT_STRING dissector tables above.
+ * Field 6 is omitted since "Decode As" doesn't apply.
+ */
+
+static void
+dissector_dump_heur_dissector_tables_display (gpointer key, gpointer user_data _U_)
+{
+ const char *list_name = (const char *)key;
+ heur_dissector_list_t list;
+
+ list = (heur_dissector_list_t)g_hash_table_lookup(heur_dissector_lists, key);
+ printf("%s\t%s\theuristic", list_name, list->ui_name ? list->ui_name : list_name);
+
+ if (list->protocol != NULL) {
+ printf("\t%s",
+ proto_get_protocol_short_name(list->protocol));
+ } else
+ printf("\t(no protocol)");
+ printf("\n");
+}
+
static gint
compare_dissector_key_name(gconstpointer dissector_a, gconstpointer dissector_b)
{
@@ -3565,6 +3851,62 @@ dissector_dump_dissector_tables(void)
list = g_list_sort(list, compare_dissector_key_name);
g_list_foreach(list, dissector_dump_dissector_tables_display, NULL);
g_list_free(list);
+
+ list = g_hash_table_get_keys(heur_dissector_lists);
+ list = g_list_sort(list, compare_dissector_key_name);
+ g_list_foreach(list, dissector_dump_heur_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