diff --git a/debian/libwireshark0.symbols b/debian/libwireshark0.symbols index 92ac448880..bf0edfc64a 100644 --- a/debian/libwireshark0.symbols +++ b/debian/libwireshark0.symbols @@ -1017,6 +1017,7 @@ libwireshark.so.0 libwireshark0 #MINVER# proto_is_protocol_enabled@Base 1.9.1 proto_is_protocol_enabled_by_default@Base 2.3.0 proto_is_frame_protocol@Base 1.99.1 + proto_is_pino@Base 2.3.0 proto_item_add_subtree@Base 1.9.1 proto_item_append_text@Base 1.9.1 proto_item_fill_label@Base 1.9.1 @@ -1034,6 +1035,7 @@ libwireshark.so.0 libwireshark0 #MINVER# proto_register_fields_section@Base 1.12.0~rc1 proto_register_prefix@Base 1.9.1 proto_register_protocol@Base 1.9.1 + proto_register_protocol_in_name_only@Base 2.3.0 proto_register_subtree_array@Base 1.9.1 proto_registrar_dump_fieldcount@Base 2.0.0 proto_registrar_dump_fields@Base 1.9.1 diff --git a/doc/README.dissector b/doc/README.dissector index 911fd78889..39600c515e 100644 --- a/doc/README.dissector +++ b/doc/README.dissector @@ -3345,7 +3345,32 @@ The arguments to udp_dissect_pdus are: a void pointer to user data that is passed to the length-determining routine, and the dissector routine referenced in the previous parameter. -2.9 Creating Decode As functionality. +2.9 PINOs (Protocols in name only) + +For the typical dissector there is a 1-1 relationship between it and it's +protocol. However, there are times when a protocol needs multiple "names" +because it has multiple dissection functions going into the same dissector +table. The muliple names removes confusion when picking dissection through +Decode As functionality. + +Once the "main" protocol name has been created through proto_register_protocol, +additional "pinos" can be created with proto_register_protocol_in_name_only. +These pinos have all of the naming conventions of a protocol, but are stored +separately as to remove confusion from real protocols. "pinos" the main +protocol's properties for things like enable/disable. i.e. If the "main" +protocol has been disabled, all of its pinos will be disabled as well. +Pinos should not have any fields registered with them or heuristic tables +associated with them. + +Another use case for pinos is when a protocol contains a TLV design and it +wants to create a dissector table to handle dissection of the "V". Dissector +tables require a "protocol", but the dissection functions for that table +typically aren't a protocol. In this case proto_register_protocol_in_name_only +creates the necessary placeholder for the dissector table. In addition, because +a dissector table exists, "V"s of the TLVs can be dissected outside of the +original dissector file. + +2.10 Creating Decode As functionality. While the Decode As functionality is available through the GUI, the underlying functionality is controlled by dissectors themselves. To create Decode As @@ -3381,7 +3406,11 @@ to the dissector table through Decode As GUI functionality. For dissector tables that are an integer or string type, the provided "default" handling functions shown in the example should suffice. -2.10 ptvcursors. +All entries into a dissector table that use Decode As must have a unique +protocol ID. If a protocol wants multiple entries into a dissector table, +a pino should be used (see section 2.9) + +2.11 ptvcursors. The ptvcursor API allows a simpler approach to writing dissectors for simple protocols. The ptvcursor API works best for protocols whose fields @@ -3414,7 +3443,7 @@ To use the ptvcursor API, include the "ptvcursor.h" file. The PGM dissector is an example of how to use it. You don't need to look at it as a guide; instead, the API description here should be good enough. -2.10.1 ptvcursor API. +2.11.1 ptvcursor API. ptvcursor_t* ptvcursor_new(proto_tree* tree, tvbuff_t* tvb, gint offset) @@ -3470,7 +3499,7 @@ If the length is unknown, length may be defined as SUBTREE_UNDEFINED_LENGTH. In this case, at the next pop, the item length will be equal to the advancement of the cursor since the creation of the subtree. -2.9.2 Miscellaneous functions. +2.11.2 Miscellaneous functions. tvbuff_t* ptvcursor_tvbuff(ptvcursor_t* ptvc) @@ -3493,7 +3522,7 @@ ptvcursor_set_subtree(ptvcursor_t* ptvc, proto_item* it, gint ett_subtree); Creates a subtree and adds it to the cursor as the working tree but does not save the old working tree. -2.11 Optimizations +2.12 Optimizations A protocol dissector may be called in 2 different ways - with, or without a non-null "tree" argument. diff --git a/epan/packet.c b/epan/packet.c index 5a0b2e115a..67ebb65f4b 100644 --- a/epan/packet.c +++ b/epan/packet.c @@ -642,7 +642,7 @@ call_dissector_through_handle(dissector_handle_t handle, tvbuff_t *tvb, saved_proto = pinfo->current_proto; - if (handle->protocol != NULL) { + if ((handle->protocol != NULL) && (!proto_is_pino(handle->protocol))) { pinfo->current_proto = proto_get_protocol_short_name(handle->protocol); } @@ -702,7 +702,7 @@ call_dissector_work(dissector_handle_t handle, tvbuff_t *tvb, packet_info *pinfo */ pinfo->saved_can_desegment = saved_can_desegment; pinfo->can_desegment = saved_can_desegment-(saved_can_desegment>0); - if (handle->protocol != NULL) { + if ((handle->protocol != NULL) && (!proto_is_pino(handle->protocol))) { pinfo->current_proto = proto_get_protocol_short_name(handle->protocol); diff --git a/epan/proto.c b/epan/proto.c index ceb74c6f1e..d42ac90a7a 100644 --- a/epan/proto.c +++ b/epan/proto.c @@ -315,11 +315,16 @@ struct _protocol { gboolean is_enabled; /* TRUE if protocol is enabled */ gboolean enabled_by_default; /* TRUE if protocol is enabled by default */ gboolean can_toggle; /* TRUE if is_enabled can be changed */ + int parent_proto_id; /* Used to identify "pino"s (Protocol In Name Only). + For dissectors that need a protocol name so they + can be added to a dissector table, but use the + parent_proto_id for things like enable/disable */ GList *heur_list; /* Heuristic dissectors associated with this protocol */ }; /* List of all protocols */ static GList *protocols = NULL; +static GList *pino_protocols = NULL; /* Deregistered fields */ static GPtrArray *deregistered_fields = NULL; @@ -568,6 +573,9 @@ proto_init(void (register_all_protocols_func)(register_cb cb, gpointer client_da void proto_cleanup(void) { + protocol_t *protocol; + header_field_info *hfinfo; + /* Free the abbrev/ID hash table */ if (gpa_name_map) { g_hash_table_destroy(gpa_name_map); @@ -577,8 +585,7 @@ proto_cleanup(void) last_field_name = NULL; while (protocols) { - protocol_t *protocol = (protocol_t *)protocols->data; - header_field_info *hfinfo; + protocol = (protocol_t *)protocols->data; PROTO_REGISTRAR_GET_NTH(protocol->proto_id, hfinfo); DISSECTOR_ASSERT(protocol->proto_id == hfinfo->id); @@ -591,6 +598,17 @@ proto_cleanup(void) g_free(protocol); } + while (pino_protocols) { + protocol = (protocol_t *)pino_protocols->data; + PROTO_REGISTRAR_GET_NTH(protocol->proto_id, hfinfo); + DISSECTOR_ASSERT(protocol->proto_id == hfinfo->id); + DISSECTOR_ASSERT(protocol->fields == NULL); //helpers should not have any registered fields + g_slice_free(header_field_info, hfinfo); + DISSECTOR_ASSERT(protocol->heur_list == NULL); //helpers should not have a heuristic list + pino_protocols = g_list_remove(pino_protocols, protocol); + g_free(protocol); + } + if (proto_names) { g_hash_table_destroy(proto_names); proto_names = NULL; @@ -5821,6 +5839,7 @@ proto_register_protocol(const char *name, const char *short_name, protocol->is_enabled = TRUE; /* protocol is enabled by default */ protocol->enabled_by_default = TRUE; /* see previous comment */ protocol->can_toggle = TRUE; + protocol->parent_proto_id = -1; protocol->heur_list = NULL; /* list will be sorted later by name, when all protocols completed registering */ protocols = g_list_prepend(protocols, protocol); @@ -5844,6 +5863,77 @@ proto_register_protocol(const char *name, const char *short_name, return proto_id; } +int +proto_register_protocol_in_name_only(const char *name, const char *short_name, const char *filter_name, int parent_proto, enum ftenum field_type) +{ + protocol_t *protocol; + header_field_info *hfinfo; + guint i; + gchar c; + gboolean found_invalid = FALSE; + + /* + * Helper protocols don't need the strict rules as a "regular" protocol + * Just register it in a list and make a hf_ field from it + */ + if ((field_type != FT_PROTOCOL) && (field_type != FT_BYTES)) { + g_error("Pino \"%s\" must be of type FT_PROTOCOL or FT_BYTES.", name); + } + + if (parent_proto < 0) { + g_error("Must have a valid parent protocol for helper protocol \"%s\"!" + " This might be caused by an inappropriate plugin or a development error.", name); + } + + for (i = 0; filter_name[i]; i++) { + c = filter_name[i]; + if (!(g_ascii_islower(c) || g_ascii_isdigit(c) || c == '-' || c == '_' || c == '.')) { + found_invalid = TRUE; + } + } + if (found_invalid) { + g_error("Protocol filter name \"%s\" has one or more invalid characters." + " Allowed are lower characters, digits, '-', '_' and '.'." + " This might be caused by an inappropriate plugin or a development error.", filter_name); + } + + /* Add this protocol to the list of helper protocols (just so it can be properly freed) */ + protocol = g_new(protocol_t, 1); + protocol->name = name; + protocol->short_name = short_name; + protocol->filter_name = filter_name; + protocol->fields = NULL; + + /* Enabling and toggling is really determined by parent protocol, + but provide default values here */ + protocol->is_enabled = TRUE; + protocol->enabled_by_default = TRUE; + protocol->can_toggle = TRUE; + + protocol->parent_proto_id = parent_proto; + protocol->heur_list = NULL; + /* list will be sorted later by name, when all protocols completed registering */ + pino_protocols = g_list_prepend(pino_protocols, protocol); + + /* Here we allocate a new header_field_info struct */ + hfinfo = g_slice_new(header_field_info); + hfinfo->name = name; + hfinfo->abbrev = filter_name; + hfinfo->type = field_type; + hfinfo->display = BASE_NONE; + if (field_type == FT_BYTES) { + hfinfo->display |= (BASE_NO_DISPLAY_VALUE|BASE_PROTOCOL_INFO); + } + hfinfo->strings = protocol; + hfinfo->bitmask = 0; + hfinfo->ref_type = HF_REF_TYPE_NONE; + hfinfo->blurb = NULL; + hfinfo->parent = -1; /* this field differentiates protos and fields */ + + protocol->proto_id = proto_register_field_init(hfinfo, hfinfo->parent); + return protocol->proto_id; +} + gboolean proto_deregister_protocol(const char *short_name) { @@ -5972,7 +6062,9 @@ find_protocol_by_id(const int proto_id) return NULL; PROTO_REGISTRAR_GET_NTH(proto_id, hfinfo); - DISSECTOR_ASSERT_FIELD_TYPE(hfinfo, FT_PROTOCOL); + if (hfinfo->type != FT_PROTOCOL) { + DISSECTOR_ASSERT(hfinfo->display & BASE_PROTOCOL_INFO); + } return (protocol_t *)hfinfo->strings; } @@ -6150,16 +6242,29 @@ proto_is_frame_protocol(const wmem_list_t *layers, const char* proto_name) return FALSE; } +gboolean +proto_is_pino(const protocol_t *protocol) +{ + return (protocol->parent_proto_id != -1); +} gboolean proto_is_protocol_enabled(const protocol_t *protocol) { + //parent protocol determines enable/disable for helper dissectors + if (proto_is_pino(protocol)) + return proto_is_protocol_enabled(find_protocol_by_id(protocol->parent_proto_id)); + return protocol->is_enabled; } gboolean proto_is_protocol_enabled_by_default(const protocol_t *protocol) { + //parent protocol determines enable/disable for helper dissectors + if (proto_is_pino(protocol)) + return proto_is_protocol_enabled_by_default(find_protocol_by_id(protocol->parent_proto_id)); + return protocol->enabled_by_default; } @@ -6169,6 +6274,10 @@ proto_can_toggle_protocol(const int proto_id) protocol_t *protocol; protocol = find_protocol_by_id(proto_id); + //parent protocol determines toggling for helper dissectors + if (proto_is_pino(protocol)) + return proto_can_toggle_protocol(protocol->parent_proto_id); + return protocol->can_toggle; } @@ -6179,6 +6288,7 @@ proto_disable_by_default(const int proto_id) protocol = find_protocol_by_id(proto_id); DISSECTOR_ASSERT(protocol->can_toggle); + DISSECTOR_ASSERT(proto_is_pino(protocol) == FALSE); protocol->is_enabled = FALSE; protocol->enabled_by_default = FALSE; } @@ -6190,6 +6300,7 @@ proto_set_decoding(const int proto_id, const gboolean enabled) protocol = find_protocol_by_id(proto_id); DISSECTOR_ASSERT(protocol->can_toggle); + DISSECTOR_ASSERT(proto_is_pino(protocol) == FALSE); protocol->is_enabled = enabled; } @@ -6576,6 +6687,12 @@ tmp_fld_check_assert(header_field_info *hfinfo) if (hfinfo->display & BASE_UNIT_STRING) break; + //fallthrough + case FT_BYTES: + //allowed to support string if its a protocol (for pinos) + if (hfinfo->display & BASE_PROTOCOL_INFO) + break; + //fallthrough default: g_error("Field '%s' (%s) has a 'strings' value but is of type %s" @@ -6790,7 +6907,8 @@ tmp_fld_check_assert(header_field_info *hfinfo) g_error("Field '%s' (%s) is an %s but has a bitmask\n", hfinfo->name, hfinfo->abbrev, ftype_name(hfinfo->type)); - if (hfinfo->strings != NULL) + //allowed to support string if its a protocol (for pinos) + if ((hfinfo->strings != NULL) && (!(hfinfo->display & BASE_PROTOCOL_INFO))) g_error("Field '%s' (%s) is an %s but has a strings value\n", hfinfo->name, hfinfo->abbrev, ftype_name(hfinfo->type)); diff --git a/epan/proto.h b/epan/proto.h index ed4b6971e6..80756281c5 100644 --- a/epan/proto.h +++ b/epan/proto.h @@ -554,6 +554,7 @@ typedef enum { #define BASE_UNIT_STRING 0x1000 /**< Add unit text to the field value */ #define BASE_NO_DISPLAY_VALUE 0x2000 /**< Just display the field name with no value. Intended for byte arrays or header fields above a subtree */ +#define BASE_PROTOCOL_INFO 0x4000 /**< protocol_t in [FIELDCONVERT]. Internal use only. */ /** BASE_ values that cause the field value to be displayed twice */ #define IS_BASE_DUAL(b) ((b)==BASE_DEC_HEX||(b)==BASE_HEX_DEC) @@ -582,7 +583,7 @@ struct _header_field_info { int display; /**< [FIELDDISPLAY] one of BASE_, or field bit-width if FT_BOOLEAN and non-zero bitmask */ const void *strings; /**< [FIELDCONVERT] value_string, val64_string, range_string or true_false_string, typically converted by VALS(), RVALS() or TFS(). - If this is an FT_PROTOCOL then it points to the + If this is an FT_PROTOCOL or BASE_PROTOCOL_INFO then it points to the associated protocol_t structure */ guint64 bitmask; /**< [BITMASK] bitmask of interesting bits */ const char *blurb; /**< [FIELDDESCR] Brief description of field */ @@ -2096,6 +2097,23 @@ proto_item_fill_label(field_info *fi, gchar *label_str); WS_DLL_PUBLIC int proto_register_protocol(const char *name, const char *short_name, const char *filter_name); +/** Register a "helper" protocol (pino - protocol in name only). + This is for dissectors that need distinguishing names and don't need the other + features (like enable/disable). One use case is a protocol with multiple dissection + functions in a single dissector table needing unique "dissector names" to remove + confusion with Decode As dialog. Another use case is for a dissector table set + up to handle TLVs within a single protocol (and allow "external" TLVs being + registered through the dissector table). + @param name the full name of the new protocol + @param short_name abbreviated name of the new protocol + @param filter_name protocol name used for a display filter string + @param parent_proto the "real" protocol for the helper. The parent decides enable/disable + @param field_type FT_PROTOCOL or FT_BYTES. Allows removal of "protocol highlighting" (FT_BYTES) + if pino is part of TLV. + @return the new protocol handle */ +WS_DLL_PUBLIC int +proto_register_protocol_in_name_only(const char *name, const char *short_name, const char *filter_name, int parent_proto, enum ftenum field_type); + /** Deregister a protocol. @param short_name abbreviated name of the protocol @return TRUE if protocol is removed */ @@ -2256,6 +2274,10 @@ WS_DLL_PUBLIC gboolean proto_is_protocol_enabled(const protocol_t *protocol); @return TRUE if decoding is enabled by default, FALSE if not */ WS_DLL_PUBLIC gboolean proto_is_protocol_enabled_by_default(const protocol_t *protocol); +/** Is this a protocol in name only (i.e. not a real one)? + @return TRUE if helper, FALSE if not */ +WS_DLL_PUBLIC gboolean proto_is_pino(const protocol_t *protocol); + /** Get a protocol's filter name by its item number. @param proto_id protocol id (0-indexed) @return its filter name. */