From 623b347d1e863f7cd2d331926106321d3182d169 Mon Sep 17 00:00:00 2001 From: Huang Qiangxiong Date: Sun, 17 Nov 2019 20:25:15 +0800 Subject: [PATCH] Protobuf: add dissecting protobuf fields as wireshark fields preferences, etc. Two enhancements and one fixed bug: 1. Add dissecting protobuf fields as wireshark (header) fields preferences. User can input the full names of protobuf fields or messages in Filter toolbar for searching. 2. Add 'protobuf_field' dissector table. Dissector based on protobuf can register itself to 'protobuf_field' keyed with the full names of fields of BYETS or STRING types. 3. A bug about search MESSAGE or ENUM type in context is fixed. 4. Another small enhancement is adding prefs_set_preference_effect_fields() which can mark a preference that affects fields change (triggering FieldsChanged event). See the linked bug for sample capture file and .proto files. Ping-Bug: 16209 Change-Id: Ibc3c45a6d596a8bb983b0d847dd6a22801af7e04 Reviewed-on: https://code.wireshark.org/review/35111 Petri-Dish: Alexis La Goutte Tested-by: Petri Dish Buildbot Reviewed-by: Peter Wu Reviewed-by: Anders Broman --- debian/libwireshark0.symbols | 1 + docbook/release-notes.adoc | 4 + epan/dissectors/packet-protobuf.c | 341 +++++++++++++++++++++++++++- epan/prefs-int.h | 1 + epan/prefs.c | 9 + epan/prefs.h | 8 + epan/protobuf-helper.c | 27 ++- epan/protobuf-helper.h | 16 ++ epan/protobuf_lang_tree.c | 117 +++++++--- epan/protobuf_lang_tree.h | 17 ++ ui/qt/preference_editor_frame.cpp | 5 + ui/qt/preferences_dialog.cpp | 4 + ui/qt/protocol_preferences_menu.cpp | 7 + 13 files changed, 519 insertions(+), 38 deletions(-) mode change 100644 => 100755 docbook/release-notes.adoc diff --git a/debian/libwireshark0.symbols b/debian/libwireshark0.symbols index 2481df98d8..09a085b183 100644 --- a/debian/libwireshark0.symbols +++ b/debian/libwireshark0.symbols @@ -1077,6 +1077,7 @@ libwireshark.so.0 libwireshark0 #MINVER# prefs_set_gui_theme_is_dark@Base 2.5.0 prefs_set_module_effect_flags@Base 2.5.0 prefs_set_pref@Base 1.9.1 + prefs_set_preference_effect_fields@Base 3.3.0 prefs_set_range_value@Base 2.1.0 prefs_set_range_value_work@Base 2.3.0 prefs_set_stashed_range_value@Base 2.3.0 diff --git a/docbook/release-notes.adoc b/docbook/release-notes.adoc old mode 100644 new mode 100755 index 7eb41429bc..797749ee93 --- a/docbook/release-notes.adoc +++ b/docbook/release-notes.adoc @@ -35,6 +35,10 @@ since version 3.2.0: * Windows executables and installers are now https://support.microsoft.com/en-us/help/4472027/2019-sha-2-code-signing-support-requirement-for-windows-and-wsus[signed using SHA-2 only]. * Save RTP stream to .au supports any codec with 8000 Hz rate supported by Wireshark (shown in RTP player). If save of audio is not possible (unsupported codec or rate), silence of same length is saved and warning is shown. * C-ares is now a required dependency. +* Protobuf fields can be dissected as wireshark (header) fields that allows user input + the full names of Protobuf fields or messages in Filter toolbar for searching. +* Dissector based on Protobuf can register itself to a new 'protobuf_field' dissector table, + which is keyed with the full names of fields, for further parsing fields of BYETS or STRING type. // === Removed Features and Support diff --git a/epan/dissectors/packet-protobuf.c b/epan/dissectors/packet-protobuf.c index fe41a998df..bcae511841 100644 --- a/epan/dissectors/packet-protobuf.c +++ b/epan/dissectors/packet-protobuf.c @@ -145,7 +145,20 @@ static int ett_protobuf_packed_repeated = -1; static gboolean try_dissect_as_string = FALSE; static gboolean show_all_possible_field_types = FALSE; static gboolean dissect_bytes_as_string = FALSE; +static gboolean old_dissect_bytes_as_string = FALSE; static gboolean show_details = FALSE; +static gboolean pbf_as_hf = FALSE; /* dissect protobuf fields as header fields of wireshark */ + +/* dynamic wireshark header fields for protobuf fields */ +static hf_register_info *dynamic_hf = NULL; +static guint dynamic_hf_size = 0; +/* the key is full name of protobuf fields, the value is header field id */ +static GHashTable *pbf_hf_hash = NULL; + +/* Protobuf field value subdissector table list. + * Only valid for the value of PROTOBUF_TYPE_BYTES or PROTOBUF_TYPE_STRING fields. + */ +static dissector_table_t protobuf_field_subdissector_table; static dissector_handle_t protobuf_handle; @@ -400,10 +413,27 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset gint32 int32_value; char* buf; gboolean add_datatype = TRUE; - proto_item* ti; + proto_item* ti = NULL; + proto_tree* subtree = NULL; const char* enum_value_name = NULL; const PbwDescriptor* sub_message_desc = NULL; const PbwEnumDescriptor* enum_desc = NULL; + int* hf_id_ptr = NULL; + const gchar* field_full_name = field_desc ? pbw_FieldDescriptor_full_name(field_desc) : NULL; + proto_tree* field_tree = proto_item_get_subtree(ti_field); + proto_tree* field_parent_tree = proto_tree_get_parent_tree(field_tree); + proto_tree* pbf_tree = field_tree; + + if (pbf_as_hf && field_full_name) { + hf_id_ptr = (int*)g_hash_table_lookup(pbf_hf_hash, field_full_name); + DISSECTOR_ASSERT_HINT(hf_id_ptr && (*hf_id_ptr) > 0, "hf must have been initialized properly"); + } + + if (pbf_as_hf && hf_id_ptr && !show_details) { + /* set ti_field (Field(x)) item hidden if there is header_field */ + proto_item_set_hidden(ti_field); + pbf_tree = field_parent_tree; + } if (prepend_text == NULL) { prepend_text = ""; @@ -418,6 +448,9 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset if (is_top_level) { col_append_fstr(pinfo->cinfo, COL_INFO, "=%lf", double_value); } + if (hf_id_ptr) { + proto_tree_add_double(pbf_tree, *hf_id_ptr, tvb, offset, length, double_value); + } break; case PROTOBUF_TYPE_FLOAT: @@ -427,6 +460,9 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset if (is_top_level) { col_append_fstr(pinfo->cinfo, COL_INFO, "=%f", float_value); } + if (hf_id_ptr) { + proto_tree_add_float(pbf_tree, *hf_id_ptr, tvb, offset, length, float_value); + } break; case PROTOBUF_TYPE_INT64: @@ -437,6 +473,9 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset if (is_top_level) { col_append_fstr(pinfo->cinfo, COL_INFO, "=%" G_GINT64_MODIFIER "d", int64_value); } + if (hf_id_ptr) { + proto_tree_add_int64(pbf_tree, *hf_id_ptr, tvb, offset, length, int64_value); + } break; case PROTOBUF_TYPE_UINT64: @@ -446,6 +485,9 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset if (is_top_level) { col_append_fstr(pinfo->cinfo, COL_INFO, "=%" G_GINT64_MODIFIER "u", value); } + if (hf_id_ptr) { + proto_tree_add_uint64(pbf_tree, *hf_id_ptr, tvb, offset, length, value); + } break; case PROTOBUF_TYPE_INT32: @@ -456,7 +498,9 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset if (is_top_level) { col_append_fstr(pinfo->cinfo, COL_INFO, "=%d", int32_value); } - + if (hf_id_ptr) { + proto_tree_add_int(pbf_tree, *hf_id_ptr, tvb, offset, length, int32_value); + } break; case PROTOBUF_TYPE_ENUM: @@ -485,6 +529,9 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset } } + if (hf_id_ptr) { + proto_tree_add_int(pbf_tree, *hf_id_ptr, tvb, offset, length, int32_value); + } break; case PROTOBUF_TYPE_BOOL: @@ -494,29 +541,43 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset if (is_top_level) { col_append_fstr(pinfo->cinfo, COL_INFO, "=%s", value ? "true" : "false"); } + if (hf_id_ptr) { + proto_tree_add_boolean(pbf_tree, *hf_id_ptr, tvb, offset, length, (guint32)value); + } break; case PROTOBUF_TYPE_BYTES: if (!dissect_bytes_as_string) { + if (hf_id_ptr) { + ti = proto_tree_add_bytes_format_value(pbf_tree, *hf_id_ptr, tvb, offset, length, NULL, "(%u bytes)", length); + } break; } /* or continue dissect BYTES as STRING */ /* FALLTHROUGH */ case PROTOBUF_TYPE_STRING: - proto_tree_add_item_ret_display_string(value_tree, hf_protobuf_value_string, tvb, offset, length, ENC_UTF_8|ENC_NA, wmem_packet_scope(), &buf); + ti = proto_tree_add_item_ret_display_string(value_tree, hf_protobuf_value_string, tvb, offset, length, ENC_UTF_8|ENC_NA, wmem_packet_scope(), &buf); proto_item_append_text(ti_field, "%s %s", prepend_text, buf); if (is_top_level) { col_append_fstr(pinfo->cinfo, COL_INFO, "=%s", buf); } + if (hf_id_ptr) { + ti = proto_tree_add_item_ret_display_string(pbf_tree, *hf_id_ptr, tvb, offset, length, ENC_UTF_8|ENC_NA, wmem_packet_scope(), &buf); + } break; case PROTOBUF_TYPE_GROUP: /* This feature is deprecated. GROUP is identical to Nested MESSAGE. */ case PROTOBUF_TYPE_MESSAGE: + subtree = field_tree; + if (hf_id_ptr) { + ti = proto_tree_add_bytes_format_value(pbf_tree, *hf_id_ptr, tvb, offset, length, NULL, "(%u bytes)", length); + subtree = proto_item_add_subtree(ti, ett_protobuf_message); + } if (field_desc) { sub_message_desc = pbw_FieldDescriptor_message_type(field_desc); if (sub_message_desc) { dissect_protobuf_message(tvb, offset, length, pinfo, - proto_item_get_subtree(ti_field), sub_message_desc, FALSE); + subtree, sub_message_desc, FALSE); } else { expert_add_info(pinfo, ti_field, &et_protobuf_message_type_not_found); } @@ -532,6 +593,9 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset if (is_top_level) { col_append_fstr(pinfo->cinfo, COL_INFO, "=%u", (guint32)value); } + if (hf_id_ptr) { + proto_tree_add_uint(pbf_tree, *hf_id_ptr, tvb, offset, length, (guint32)value); + } break; case PROTOBUF_TYPE_SINT32: @@ -541,6 +605,9 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset if (is_top_level) { col_append_fstr(pinfo->cinfo, COL_INFO, "=%d", int32_value); } + if (hf_id_ptr) { + proto_tree_add_int(pbf_tree, *hf_id_ptr, tvb, offset, length, int32_value); + } break; case PROTOBUF_TYPE_SINT64: @@ -550,6 +617,9 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset if (is_top_level) { col_append_fstr(pinfo->cinfo, COL_INFO, "=%" G_GINT64_MODIFIER "d", int64_value); } + if (hf_id_ptr) { + proto_tree_add_int64(pbf_tree, *hf_id_ptr, tvb, offset, length, int64_value); + } break; default: @@ -558,6 +628,21 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset break; } + /* try dissect field value according to protobuf_field dissector table */ + if (field_full_name && (field_type == PROTOBUF_TYPE_BYTES || field_type == PROTOBUF_TYPE_STRING)) { + /* determine the tree passing to the subdissector */ + subtree = value_tree; + if (ti) { + subtree = proto_item_get_subtree(ti); + if (!subtree) { + subtree = proto_item_add_subtree(ti, ett_protobuf_value); + } + } + + dissector_try_string(protobuf_field_subdissector_table, field_full_name, + tvb_new_subset_length(tvb, offset, length), pinfo, subtree, NULL); + } + if (add_datatype) proto_item_append_text(ti_field, " (%s)", val_to_str(field_type, protobuf_field_type, "Unknown type (%d)")); @@ -664,6 +749,10 @@ dissect_one_protobuf_field(tvbuff_t *tvb, guint* offset, guint maxlen, packet_in } } + /* move ti_field_number and ti_wire after ti_field_type (or field_type) for good look */ + proto_tree_move_item(field_tree, (ti_field_type ? ti_field_type : ti_field_name), ti_wire); + proto_tree_move_item(field_tree, (ti_field_type ? ti_field_type : ti_field_name), ti_field_number); + /* determine value_length, uint of numeric value and maybe value_length_size according to wire_type */ switch (wire_type) { @@ -769,11 +858,33 @@ dissect_protobuf_message(tvbuff_t *tvb, guint offset, guint length, packet_info const gchar* message_name = " Message Type"; guint max_offset = offset + length; - message_tree = proto_tree_add_subtree(protobuf_tree, tvb, offset, length, ett_protobuf_message, &ti_message, "Message"); if (message_desc) { message_name = pbw_Descriptor_full_name(message_desc); } + if (pbf_as_hf && message_desc) { + /* support filtering with message name as wireshark field name */ + int *hf_id_ptr = (int*)g_hash_table_lookup(pbf_hf_hash, message_name); + DISSECTOR_ASSERT_HINT(hf_id_ptr && (*hf_id_ptr) > 0, "hf of message should initialized properly"); + ti_message = proto_tree_add_item(protobuf_tree, *hf_id_ptr, tvb, offset, length, ENC_NA); + proto_item_set_text(ti_message, "Message: %s", message_name); + + if (show_details) { + /* show "Message" item and add its fields under this item */ + message_tree = proto_item_add_subtree(ti_message, ett_protobuf_message); + } else { + /* hidden "Message" item (but still can be filtered by wireshark field name with "pbm.xxx" prefix), + * and add its fields under the parent (field or protobuf protocol) item directly */ + proto_item_set_hidden(ti_message); + message_tree = protobuf_tree; + ti_message = proto_tree_get_parent(message_tree); + proto_item_append_text(ti_message, " (Message: %s)", message_name); + } + } else { + message_tree = proto_tree_add_subtree_format(protobuf_tree, tvb, offset, length, ett_protobuf_message, + &ti_message, "Message: %s", message_name); + } + if (is_top_level) { col_clear(pinfo->cinfo, COL_PROTOCOL); col_append_fstr(pinfo->cinfo, COL_PROTOCOL, "PB(%s)", message_name); @@ -781,7 +892,6 @@ dissect_protobuf_message(tvbuff_t *tvb, guint offset, guint length, packet_info col_clear(pinfo->cinfo, COL_INFO); } - proto_item_append_text(ti_message, ": %s", message_name); /* support filtering with message name */ ti = proto_tree_add_string(message_tree, hf_protobuf_message_name, tvb, offset, length, message_name); proto_item_set_generated(ti); @@ -999,6 +1109,197 @@ update_protobuf_udp_message_types(void) protobuf_reinit(PREFS_UPDATE_PROTOBUF_UDP_MESSAGE_TYPES); } +static void +deregister_header_fields(void) +{ + if (dynamic_hf) { + /* Deregister all fields */ + for (guint i = 0; i < dynamic_hf_size; i++) { + proto_deregister_field(proto_protobuf, *(dynamic_hf[i].p_id)); + g_free(dynamic_hf[i].p_id); + /* dynamic_hf[i].name and .abbrev will be freed by proto_add_deregistered_data */ + } + + proto_add_deregistered_data(dynamic_hf); + dynamic_hf = NULL; + dynamic_hf_size = 0; + } + + if (pbf_hf_hash) { + g_hash_table_destroy(pbf_hf_hash); + pbf_hf_hash = NULL; + } +} + +/* convert the names of the enum's values to value_string array */ +static value_string* +enum_to_value_string(const PbwEnumDescriptor* enum_desc) +{ + value_string* vals; + int i, value_count; + if (enum_desc == NULL || (value_count = pbw_EnumDescriptor_value_count(enum_desc)) == 0) { + return NULL; + } + + vals = g_new0(value_string, value_count + 1); + for (i = 0; i < value_count; i++) { + const PbwEnumValueDescriptor* enum_value_desc = pbw_EnumDescriptor_value(enum_desc, i); + vals[i].value = pbw_EnumValueDescriptor_number(enum_value_desc); + vals[i].strptr = g_strdup(pbw_EnumValueDescriptor_name(enum_value_desc)); + } + /* the strptr of last element of vals must be NULL */ + return vals; +} + +/* create wireshark header fields according to each message's fields + * and add them into pbf_as_hf hash table */ +static void +collect_fields(const PbwDescriptor* message, void* userdata) +{ + wmem_list_t* hf_list = (wmem_list_t*) userdata; + hf_register_info* hf; + const PbwFieldDescriptor* field_desc; + const PbwEnumDescriptor* enum_desc; + int i, field_type, total_num = pbw_Descriptor_field_count(message); + + /* add message as field */ + hf = g_new0(hf_register_info, 1); + hf->p_id = g_new(gint, 1); + *(hf->p_id) = -1; + hf->hfinfo.name = g_strdup(pbw_Descriptor_name(message)); + hf->hfinfo.abbrev = g_strdup_printf("pbm.%s", pbw_Descriptor_full_name(message)); + hf->hfinfo.type = FT_BYTES; + hf->hfinfo.display = BASE_NONE; + wmem_list_append(hf_list, hf); + g_hash_table_insert(pbf_hf_hash, g_strdup(pbw_Descriptor_full_name(message)), hf->p_id); + + /* add fields of this message as fields */ + for (i = 0; i < total_num; i++) { + field_desc = pbw_Descriptor_field(message, i); + field_type = pbw_FieldDescriptor_type(field_desc); + if (field_type <= PROTOBUF_TYPE_NONE ||field_type > PROTOBUF_MAX_FIELD_TYPE) { + /* not a valid field type */ + continue; + } + hf = g_new0(hf_register_info, 1); + hf->p_id = g_new(gint, 1); + *(hf->p_id) = -1; + + hf->hfinfo.name = g_strdup(pbw_FieldDescriptor_name(field_desc)); + hf->hfinfo.abbrev = g_strdup_printf("pbf.%s", pbw_FieldDescriptor_full_name(field_desc)); + switch (field_type) { + case PROTOBUF_TYPE_DOUBLE: + hf->hfinfo.type = FT_DOUBLE; + hf->hfinfo.display = BASE_NONE; + break; + + case PROTOBUF_TYPE_FLOAT: + hf->hfinfo.type = FT_FLOAT; + hf->hfinfo.display = BASE_NONE; + break; + + case PROTOBUF_TYPE_INT64: + case PROTOBUF_TYPE_SFIXED64: + case PROTOBUF_TYPE_SINT64: + hf->hfinfo.type = FT_INT64; + hf->hfinfo.display = BASE_DEC; + break; + + case PROTOBUF_TYPE_UINT64: + case PROTOBUF_TYPE_FIXED64: + hf->hfinfo.type = FT_UINT64; + hf->hfinfo.display = BASE_DEC; + break; + + case PROTOBUF_TYPE_INT32: + case PROTOBUF_TYPE_SFIXED32: + case PROTOBUF_TYPE_SINT32: + hf->hfinfo.type = FT_INT32; + hf->hfinfo.display = BASE_DEC; + break; + + case PROTOBUF_TYPE_UINT32: + case PROTOBUF_TYPE_FIXED32: + hf->hfinfo.type = FT_UINT32; + hf->hfinfo.display = BASE_DEC; + break; + + case PROTOBUF_TYPE_ENUM: + hf->hfinfo.type = FT_INT32; + hf->hfinfo.display = BASE_DEC; + enum_desc = pbw_FieldDescriptor_enum_type(field_desc); + if (enum_desc) { + hf->hfinfo.strings = enum_to_value_string(enum_desc); + } + break; + + case PROTOBUF_TYPE_BOOL: + hf->hfinfo.type = FT_BOOLEAN; + hf->hfinfo.display = BASE_NONE; + break; + + case PROTOBUF_TYPE_BYTES: + hf->hfinfo.type = dissect_bytes_as_string ? FT_STRING : FT_BYTES; + hf->hfinfo.display = BASE_NONE; + break; + + case PROTOBUF_TYPE_STRING: + hf->hfinfo.type = FT_STRING; + hf->hfinfo.display = BASE_NONE; + break; + + case PROTOBUF_TYPE_GROUP: + case PROTOBUF_TYPE_MESSAGE: + hf->hfinfo.type = FT_BYTES; + hf->hfinfo.display = BASE_NONE; + break; + + default: + /* should not happen */ + break; + } + + wmem_list_append(hf_list, hf); + g_hash_table_insert(pbf_hf_hash, g_strdup(pbw_FieldDescriptor_full_name(field_desc)), hf->p_id); + } +} + +static void +update_header_fields(gboolean force_reload) +{ + if (!force_reload && pbf_as_hf && dynamic_hf) { + /* If initialized, do nothing. */ + return; + } + deregister_header_fields(); + + if (pbf_as_hf) { + int i; + wmem_list_frame_t *it; + wmem_list_t* hf_list = wmem_list_new(NULL); + pbf_hf_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + DISSECTOR_ASSERT(pbw_pool); + pbw_foreach_message(pbw_pool, collect_fields, hf_list); + dynamic_hf_size = wmem_list_count(hf_list); + if (dynamic_hf_size == 0) { + deregister_header_fields(); + return; + } + dynamic_hf = g_new0(hf_register_info, dynamic_hf_size); + + for (it = wmem_list_head(hf_list), i = 0; it; it = wmem_list_frame_next(it), i++) { + hf_register_info* hf = (hf_register_info*) wmem_list_frame_data(it); + /* copy hf_register_info structure */ + dynamic_hf[i] = *hf; + g_free(hf); + HFILL_INIT(dynamic_hf[i]); + } + + wmem_destroy_list(hf_list); + proto_register_field_array(proto_protobuf, dynamic_hf, dynamic_hf_size); + } +} + static void protobuf_reinit(int target) { @@ -1061,6 +1362,7 @@ protobuf_reinit(int target) } g_free(source_paths); + update_header_fields(TRUE); } /* check if the message types of UDP port exist */ @@ -1224,7 +1526,7 @@ proto_register_protobuf(void) proto_register_field_array(proto_protobuf, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); - protobuf_module = prefs_register_protocol(proto_protobuf, NULL); + protobuf_module = prefs_register_protocol(proto_protobuf, proto_reg_handoff_protobuf); protobuf_search_paths_uat = uat_new("Protobuf Search Paths", sizeof(protobuf_search_path_t), @@ -1246,6 +1548,16 @@ proto_register_protobuf(void) "Specify the directories where .proto files are recursively loaded from, or in which to search for imports.", protobuf_search_paths_uat); + prefs_register_bool_preference(protobuf_module, "pbf_as_hf", + "Dissect Protobuf fields as Wireshark fields.", + "If Protobuf messages and fields are defined in loaded .proto files," + " they will be dissected as wireshark fields if this option is turnned on." + " The names of all these wireshark fields will be prefixed with \"pbf.\" (for fields)" + " or \"pbm.\" (for messages) followed by their full names in the .proto files.", + &pbf_as_hf); + + prefs_set_preference_effect_fields(protobuf_module, "pbf_as_hf"); + prefs_register_bool_preference(protobuf_module, "show_details", "Show details of message, fields and enums.", "Show the names of message, field, enum and enum_value." @@ -1291,6 +1603,15 @@ proto_register_protobuf(void) "Try to show all possible field types for each undefined field according to wire type.", &show_all_possible_field_types); + prefs_register_static_text_preference(protobuf_module, "field_dissector_table_note", + "Subdissector can register itself in \"protobuf_field\" dissector table for parsing" + " the value of the field of bytes or string type.", + "The key of \"protobuf_field\" table is the full name of field."); + + protobuf_field_subdissector_table = + register_dissector_table("protobuf_field", "Protobuf field subdissector table", + proto_protobuf, FT_STRING, BASE_NONE); + expert_protobuf = expert_register_protocol(proto_protobuf); expert_register_field_array(expert_protobuf, ei, array_length(ei)); @@ -1300,6 +1621,12 @@ proto_register_protobuf(void) void proto_reg_handoff_protobuf(void) { + if (protobuf_dissector_called) { + update_header_fields( /* if bytes_as_string preferences changed, we force reload header fields */ + (old_dissect_bytes_as_string && !dissect_bytes_as_string) || (!old_dissect_bytes_as_string && dissect_bytes_as_string) + ); + } + old_dissect_bytes_as_string = dissect_bytes_as_string; dissector_add_string("grpc_message_type", "application/grpc", protobuf_handle); dissector_add_string("grpc_message_type", "application/grpc+proto", protobuf_handle); } diff --git a/epan/prefs-int.h b/epan/prefs-int.h index df391add94..bc42624b1c 100644 --- a/epan/prefs-int.h +++ b/epan/prefs-int.h @@ -142,6 +142,7 @@ WS_DLL_PUBLIC guint32 prefs_get_max_value(pref_t *pref); #define PREF_EFFECT_GUI (1u << 2) #define PREF_EFFECT_FONT (1u << 3) #define PREF_EFFECT_GUI_LAYOUT (1u << 4) +#define PREF_EFFECT_FIELDS (1u << 5) #define PREF_EFFECT_CUSTOM (1u << 31) /** Fetch flags that show the effect of the preference diff --git a/epan/prefs.c b/epan/prefs.c index a34134703c..cf5333d834 100644 --- a/epan/prefs.c +++ b/epan/prefs.c @@ -1964,6 +1964,15 @@ prefs_register_obsolete_preference(module_t *module, const char *name) register_preference(module, name, NULL, NULL, PREF_OBSOLETE); } +void +prefs_set_preference_effect_fields(module_t *module, const char *name) +{ + pref_t * pref = prefs_find_preference(module, name); + if (pref) { + prefs_set_effect_flags(pref, prefs_get_effect_flags(pref) | PREF_EFFECT_FIELDS); + } +} + /* * Check to see if a preference is obsolete. */ diff --git a/epan/prefs.h b/epan/prefs.h index 138d2fdf60..f9a657e362 100644 --- a/epan/prefs.h +++ b/epan/prefs.h @@ -513,6 +513,14 @@ void prefs_register_decode_as_preference(module_t *module, const char *name, WS_DLL_PUBLIC void prefs_register_obsolete_preference(module_t *module, const char *name); +/* + * Mark a preference that affects fields change. This works for bool, enum, + * int, string (containing filename), range preferences. UAT is not included, + * because you can specified UAT_AFFECTS_FIELDS at uat_new(). + */ +WS_DLL_PUBLIC void prefs_set_preference_effect_fields(module_t *module, + const char *name); + typedef guint (*pref_cb)(pref_t *pref, gpointer user_data); diff --git a/epan/protobuf-helper.c b/epan/protobuf-helper.c index 02a4470b28..3be033d9e6 100644 --- a/epan/protobuf-helper.c +++ b/epan/protobuf-helper.c @@ -183,6 +183,18 @@ pbw_EnumDescriptor_full_name(const PbwEnumDescriptor* anEnum) { return pbl_enum_descriptor_full_name((const pbl_enum_descriptor_t*) anEnum); } +/* like EnumDescriptor::value_count() */ +int +pbw_EnumDescriptor_value_count(const PbwEnumDescriptor* anEnum) { + return pbl_enum_descriptor_value_count((const pbl_enum_descriptor_t*) anEnum); +} + +/* like EnumDescriptor::value() */ +const PbwEnumValueDescriptor* +pbw_EnumDescriptor_value(const PbwEnumDescriptor* anEnum, int value_index) { + return (const PbwEnumValueDescriptor*) pbl_enum_descriptor_value((const pbl_enum_descriptor_t*) anEnum, value_index); +} + /* like EnumDescriptor::FindValueByNumber() */ const PbwEnumValueDescriptor* pbw_EnumDescriptor_FindValueByNumber(const PbwEnumDescriptor* anEnum, int number) { @@ -198,7 +210,20 @@ pbw_EnumValueDescriptor_name(const PbwEnumValueDescriptor* enumValue) { /* like EnumValueDescriptor::full_name() */ const char* pbw_EnumValueDescriptor_full_name(const PbwEnumValueDescriptor* enumValue) { - return pbl_enum_value_descriptor_full_name((const pbl_enum_value_descriptor_t*) enumValue); + return pbl_enum_value_descriptor_full_name((const pbl_enum_value_descriptor_t*) enumValue); +} + +/* like EnumValueDescriptor::number() */ +int +pbw_EnumValueDescriptor_number(const PbwEnumValueDescriptor* enumValue) { + return pbl_enum_value_descriptor_number((const pbl_enum_value_descriptor_t*) enumValue); +} + +/* visit all messages of this pool */ +void +pbw_foreach_message(const PbwDescriptorPool* pool, void (*cb)(const PbwDescriptor* message, void* userdata), void* userdata) +{ + pbl_foreach_message((const pbl_descriptor_pool_t*) pool, (void (*)(const pbl_message_descriptor_t*, void*)) cb, userdata); } /* diff --git a/epan/protobuf-helper.h b/epan/protobuf-helper.h index 3dada793eb..c92e7f6d0b 100644 --- a/epan/protobuf-helper.h +++ b/epan/protobuf-helper.h @@ -177,6 +177,14 @@ pbw_EnumDescriptor_name(const PbwEnumDescriptor* anEnum); const char* pbw_EnumDescriptor_full_name(const PbwEnumDescriptor* anEnum); +/* like EnumDescriptor::value_count() */ +int +pbw_EnumDescriptor_value_count(const PbwEnumDescriptor* anEnum); + +/* like EnumDescriptor::value() */ +const PbwEnumValueDescriptor* +pbw_EnumDescriptor_value(const PbwEnumDescriptor* anEnum, int value_index); + /* like EnumDescriptor::FindValueByNumber() */ const PbwEnumValueDescriptor* pbw_EnumDescriptor_FindValueByNumber(const PbwEnumDescriptor* anEnum, int number); @@ -189,6 +197,14 @@ pbw_EnumValueDescriptor_name(const PbwEnumValueDescriptor* enumValue); const char* pbw_EnumValueDescriptor_full_name(const PbwEnumValueDescriptor* enumValue); +/* like EnumValueDescriptor::number() */ +int +pbw_EnumValueDescriptor_number(const PbwEnumValueDescriptor* enumValue); + +/* visit all messages of this pool */ +void +pbw_foreach_message(const PbwDescriptorPool* pool, void (*cb)(const PbwDescriptor* message, void* userdata), void* userdata); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/epan/protobuf_lang_tree.c b/epan/protobuf_lang_tree.c index ae38dd7315..849129ce7c 100644 --- a/epan/protobuf_lang_tree.c +++ b/epan/protobuf_lang_tree.c @@ -248,14 +248,13 @@ pbl_get_node_full_name(pbl_node_t* node) return node->full_name; } -/* try to find node globally or in the package of given context */ +/* try to find node globally or in the context or parents (message or package) of the context */ static const pbl_node_t* pbl_find_node_in_context(const pbl_node_t* context, const char* name, pbl_node_type_t nodetype) { - pbl_node_t* package = NULL; const pbl_node_t* node = NULL; pbl_descriptor_pool_t* pool = NULL; - const char* pack_name; + char* parent_name; char* full_name; if (context == NULL || name == NULL) { @@ -271,39 +270,36 @@ pbl_find_node_in_context(const pbl_node_t* context, const char* name, pbl_node_t } } - /* try find node in context first */ - if (context->children_by_name) { - node = (pbl_node_t*) g_hash_table_lookup(context->children_by_name, name); - if (node && node->nodetype == nodetype) { - return node; - } - } - - /* find package node */ - for (node = context; node; node = node->parent) { - if (node->nodetype == PBL_PACKAGE) { - package = (pbl_node_t*)node; - break; - } - } /* find pool */ if (context->file) { pool = context->file->pool; } - /* try find node in package */ - if (package && pool) { - pack_name = pbl_get_node_full_name(package); - full_name = (pack_name[0] == 0) ? g_strdup(name) : g_strconcat(pack_name, ".", name, NULL); - node = pbl_find_node_in_pool(pool, full_name, nodetype); - g_free(full_name); - if (node) { - return node; - } - } - - /* try find node in pool directly */ + /* try find node in the context or parents (message or package) of the context */ if (pool) { + int remaining; + parent_name = g_strdup(pbl_get_node_full_name((pbl_node_t*) context)); + remaining = (int)strlen(parent_name); + while (remaining > 0) { + full_name = g_strconcat(parent_name, ".", name, NULL); + node = pbl_find_node_in_pool(pool, full_name, nodetype); + g_free(full_name); + if (node) { + g_free(parent_name); + return node; + } + /* scan from end to begin, and replace first '.' to '\0' */ + for (remaining--; remaining > 0; remaining--) { + if (parent_name[remaining] == '.') { + /* found a potential parent node name */ + parent_name[remaining] = '\0'; + break; /* break from the 'for' loop, continue 'while' loop */ + } + } + } + g_free(parent_name); + + /* try find node in pool directly */ return pbl_find_node_in_pool(pool, name, nodetype); } @@ -538,6 +534,20 @@ pbl_enum_descriptor_full_name(const pbl_enum_descriptor_t* anEnum) return pbl_get_node_full_name((pbl_node_t*)anEnum); } +/* like EnumDescriptor::value_count() */ +int +pbl_enum_descriptor_value_count(const pbl_enum_descriptor_t* anEnum) +{ + return (anEnum && anEnum->values) ? g_slist_length(anEnum->values) : 0; +} + +/* like EnumDescriptor::value() */ +const pbl_enum_value_descriptor_t* +pbl_enum_descriptor_value(const pbl_enum_descriptor_t* anEnum, int value_index) +{ + return (anEnum && anEnum->values) ? (pbl_enum_value_descriptor_t*) g_slist_nth_data(anEnum->values, value_index) : NULL; +} + /* like EnumDescriptor::FindValueByNumber() */ const pbl_enum_value_descriptor_t* pbl_enum_descriptor_FindValueByNumber(const pbl_enum_descriptor_t* anEnum, int number) @@ -563,6 +573,45 @@ pbl_enum_value_descriptor_full_name(const pbl_enum_value_descriptor_t* enumValue return pbl_get_node_full_name((pbl_node_t*)enumValue); } +/* like EnumValueDescriptor::number() */ +int +pbl_enum_value_descriptor_number(const pbl_enum_value_descriptor_t* enumValue) +{ + return GPOINTER_TO_INT(enumValue->number); +} + +static void +pbl_traverse_sub_tree(const pbl_node_t* node, void (*cb)(const pbl_message_descriptor_t*, void*), void* userdata) +{ + GSList* it; + if (node == NULL) { + return; + } + + if (node->nodetype == PBL_MESSAGE) { + (*cb)((const pbl_message_descriptor_t*) node, userdata); + } + + if (node->children) { + for (it = node->children; it; it = it->next) { + pbl_traverse_sub_tree((const pbl_node_t*) it->data, cb, userdata); + } + } +} + +/* visit all message in this pool */ +void +pbl_foreach_message(const pbl_descriptor_pool_t* pool, void (*cb)(const pbl_message_descriptor_t*, void*), void* userdata) +{ + GHashTableIter it; + gpointer key, value; + g_hash_table_iter_init (&it, pool->packages); + while (g_hash_table_iter_next (&it, &key, &value)) { + pbl_traverse_sub_tree((const pbl_node_t*)value, cb, userdata); + } +} + + /* * Following are tree building functions that should only be invoked by protobuf_lang parser. */ @@ -746,6 +795,7 @@ pbl_add_child(pbl_node_t* parent, pbl_node_t* child) } } else if (parent->nodetype == PBL_ENUM && child->nodetype == PBL_ENUM_VALUE) { pbl_enum_descriptor_t* anEnum = (pbl_enum_descriptor_t*) parent; + anEnum->values = g_slist_append(anEnum->values, child); /* add child to values_by_number table */ if (anEnum->values_by_number == NULL) { anEnum->values_by_number = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); @@ -793,6 +843,10 @@ pbl_merge_children(pbl_node_t* to, pbl_node_t* from) } } else if (from->nodetype == PBL_ENUM) { pbl_enum_descriptor_t* anEnum = (pbl_enum_descriptor_t*) from; + if (anEnum->values) { + g_slist_free(anEnum->values); + anEnum->values = NULL; + } if (anEnum->values_by_number) { g_hash_table_destroy(anEnum->values_by_number); anEnum->values_by_number = NULL; @@ -841,6 +895,9 @@ pbl_free_node(gpointer anode) break; case PBL_ENUM: enum_node = (pbl_enum_descriptor_t*) node; + if (enum_node->values) { + g_slist_free(enum_node->values); + } if (enum_node->values_by_number) { g_hash_table_destroy(enum_node->values_by_number); } diff --git a/epan/protobuf_lang_tree.h b/epan/protobuf_lang_tree.h index e4883fb6ef..0079db7732 100644 --- a/epan/protobuf_lang_tree.h +++ b/epan/protobuf_lang_tree.h @@ -101,6 +101,7 @@ typedef struct { /* like google::protobuf::EnumDescriptor of protobuf cpp library */ typedef struct { pbl_node_t basic_info; + GSList* values; GHashTable* values_by_number; } pbl_enum_descriptor_t; @@ -256,6 +257,14 @@ pbl_enum_descriptor_name(const pbl_enum_descriptor_t* anEnum); const char* pbl_enum_descriptor_full_name(const pbl_enum_descriptor_t* anEnum); +/* like EnumDescriptor::value_count() */ +int +pbl_enum_descriptor_value_count(const pbl_enum_descriptor_t* anEnum); + +/* like EnumDescriptor::value() */ +const pbl_enum_value_descriptor_t* +pbl_enum_descriptor_value(const pbl_enum_descriptor_t* anEnum, int value_index); + /* like EnumDescriptor::FindValueByNumber() */ const pbl_enum_value_descriptor_t* pbl_enum_descriptor_FindValueByNumber(const pbl_enum_descriptor_t* anEnum, int number); @@ -268,6 +277,14 @@ pbl_enum_value_descriptor_name(const pbl_enum_value_descriptor_t* enumValue); const char* pbl_enum_value_descriptor_full_name(const pbl_enum_value_descriptor_t* enumValue); +/* like EnumValueDescriptor::number() */ +int +pbl_enum_value_descriptor_number(const pbl_enum_value_descriptor_t* enumValue); + +/* visit all message in this pool */ +void +pbl_foreach_message(const pbl_descriptor_pool_t* pool, void (*cb)(const pbl_message_descriptor_t*, void*), void* userdata); + /* * Following are tree building functions. */ diff --git a/ui/qt/preference_editor_frame.cpp b/ui/qt/preference_editor_frame.cpp index eeac708db5..557f180069 100644 --- a/ui/qt/preference_editor_frame.cpp +++ b/ui/qt/preference_editor_frame.cpp @@ -181,6 +181,7 @@ void PreferenceEditorFrame::on_preferenceLineEdit_returnPressed() void PreferenceEditorFrame::on_buttonBox_accepted() { + unsigned int changed_flags = 0; unsigned int apply = 0; switch(prefs_get_type(pref_)) { case PREF_UINT: @@ -199,6 +200,7 @@ void PreferenceEditorFrame::on_buttonBox_accepted() } if (apply && module_) { + changed_flags = module_->prefs_changed_flags; pref_unstash_data_t unstashed_data; unstashed_data.module = module_; @@ -218,6 +220,9 @@ void PreferenceEditorFrame::on_buttonBox_accepted() on_buttonBox_rejected(); // Emit signals once UI is hidden if (apply) { + if (changed_flags & PREF_EFFECT_FIELDS) { + wsApp->emitAppSignal(WiresharkApplication::FieldsChanged); + } wsApp->emitAppSignal(WiresharkApplication::PacketDissectionChanged); wsApp->emitAppSignal(WiresharkApplication::PreferencesChanged); } diff --git a/ui/qt/preferences_dialog.cpp b/ui/qt/preferences_dialog.cpp index ee1a95bff0..b764ed39e6 100644 --- a/ui/qt/preferences_dialog.cpp +++ b/ui/qt/preferences_dialog.cpp @@ -264,6 +264,10 @@ void PreferencesDialog::on_buttonBox_accepted() wsApp->setMonospaceFont(prefs.gui_qt_font_name); + if (redissect_flags & PREF_EFFECT_FIELDS) { + wsApp->queueAppSignal(WiresharkApplication::FieldsChanged); + } + if (redissect_flags & PREF_EFFECT_DISSECTION) { /* Redissect all the packets, and re-evaluate the display filter. */ wsApp->queueAppSignal(WiresharkApplication::PacketDissectionChanged); diff --git a/ui/qt/protocol_preferences_menu.cpp b/ui/qt/protocol_preferences_menu.cpp index dad7932c08..0df5d651ab 100644 --- a/ui/qt/protocol_preferences_menu.cpp +++ b/ui/qt/protocol_preferences_menu.cpp @@ -274,10 +274,14 @@ void ProtocolPreferencesMenu::boolPreferenceTriggered() if (!bpa) return; module_->prefs_changed_flags |= bpa->setBoolValue(); + unsigned int changed_flags = module_->prefs_changed_flags; prefs_apply(module_); prefs_main_write(); + if (changed_flags & PREF_EFFECT_FIELDS) { + wsApp->emitAppSignal(WiresharkApplication::FieldsChanged); + } /* Protocol preference changes almost always affect dissection, so don't bother checking flags */ wsApp->emitAppSignal(WiresharkApplication::PacketDissectionChanged); @@ -294,6 +298,9 @@ void ProtocolPreferencesMenu::enumPreferenceTriggered() prefs_apply(module_); prefs_main_write(); + if (changed_flags & PREF_EFFECT_FIELDS) { + wsApp->emitAppSignal(WiresharkApplication::FieldsChanged); + } /* Protocol preference changes almost always affect dissection, so don't bother checking flags */ wsApp->emitAppSignal(WiresharkApplication::PacketDissectionChanged);