Protobuf: Fix a google.protobuf.Timestamp displaying bug

1. Fix the bug that the timestamp of google.protobuf.Timestamp message
   type does not displayed while pbf_as_hf (Dissect Protobuf fields as
   Wireshark fields) is FALSE.

2. Add the use_utc preference for displaying google.protobuf.Timestamp
  in UTC or local zone format.
This commit is contained in:
Huang Qiangxiong 2022-08-05 07:36:12 +08:00 committed by AndersBroman
parent 11990f258d
commit 89452ef2fc
1 changed files with 53 additions and 27 deletions

View File

@ -157,6 +157,7 @@ static gboolean pbf_as_hf = FALSE; /* dissect protobuf fields as header fields o
static gboolean preload_protos = FALSE;
/* Show protobuf as JSON similar to https://developers.google.com/protocol-buffers/docs/proto3#json */
static gboolean display_json_mapping = FALSE;
static gboolean use_utc_fmt = FALSE;
enum add_default_value_policy_t {
ADD_DEFAULT_VALUE_NONE,
@ -376,7 +377,7 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset
static void
dissect_protobuf_message(tvbuff_t *tvb, guint offset, guint length, packet_info *pinfo, proto_tree *protobuf_tree,
const PbwDescriptor* message_desc, gboolean is_top_level, json_dumper *dumper);
const PbwDescriptor* message_desc, int hf_msg, gboolean is_top_level, json_dumper *dumper, wmem_allocator_t* scope, char** retval);
/* Only repeated fields of primitive numeric types (types which use the varint, 32-bit, or 64-bit wire types) can
* be declared "packed".
@ -493,7 +494,6 @@ dissect_packed_repeated_field_values(tvbuff_t *tvb, guint start, guint length, p
/* The "google.protobuf.Timestamp" must be converted to rfc3339 format if mapping to JSON
* according to https://developers.google.com/protocol-buffers/docs/proto3#json
* TODO: Displaying the timezone in 'Z' or local format could be made into a preference.
*/
static char *
abs_time_to_rfc3339(wmem_allocator_t *scope, const nstime_t *nstime, bool use_utc)
@ -551,7 +551,6 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset
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;
nstime_t timestamp = { 0 };
dissector_handle_t field_dissector = field_full_name ? dissector_get_string_handle(protobuf_field_subdissector_table, field_full_name) : NULL;
if (pbf_as_hf && field_full_name) {
@ -753,27 +752,18 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset
expert_add_info(pinfo, ti_field, &et_protobuf_message_type_not_found);
}
}
if (hf_id_ptr) {
if (sub_message_desc
&& strcmp(pbw_Descriptor_full_name(sub_message_desc), "google.protobuf.Timestamp") == 0)
{ /* parse this field directly as timestamp */
if (tvb_get_protobuf_time(tvb, offset, length, &timestamp)) {
ti = proto_tree_add_time(pbf_tree, *hf_id_ptr, tvb, offset, length, &timestamp);
subtree = proto_item_add_subtree(ti, ett_protobuf_message);
if (dumper) {
buf = abs_time_to_rfc3339(pinfo->pool, &timestamp, FALSE);
json_dumper_value_string(dumper, buf);
}
} else {
expert_add_info(pinfo, ti_field, &ei_protobuf_failed_parse_field);
}
} else {
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 (sub_message_desc) {
dissect_protobuf_message(tvb, offset, length, pinfo, subtree, sub_message_desc, FALSE, (timestamp.secs == 0 ? dumper : NULL));
dissect_protobuf_message(tvb, offset, length, pinfo, pbf_as_hf ? pbf_tree : subtree, sub_message_desc,
hf_id_ptr ? *hf_id_ptr : -1, FALSE, dumper, wmem_packet_scope(), &buf);
if (buf) { /* append the value in string format to ti_field node */
proto_item_append_text(ti_field, "= %s", buf);
}
} else 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);
} else {
/* we don't continue with unknown mssage type */
}
break;
@ -1380,10 +1370,10 @@ add_missing_fields_with_default_values(tvbuff_t* tvb, guint offset, packet_info*
static void
dissect_protobuf_message(tvbuff_t *tvb, guint offset, guint length, packet_info *pinfo, proto_tree *protobuf_tree,
const PbwDescriptor* message_desc, gboolean is_top_level, json_dumper *dumper)
const PbwDescriptor* message_desc, int hf_msg, gboolean is_top_level, json_dumper *dumper, wmem_allocator_t* scope, char** retval)
{
proto_tree *message_tree;
proto_item *ti_message, *ti;
proto_item *ti_message, *ti, *ti_parent = proto_tree_get_parent(protobuf_tree);
const gchar* message_name = "<UNKNOWN> Message Type";
guint max_offset = offset + length;
const PbwFieldDescriptor* field_desc;
@ -1391,6 +1381,8 @@ dissect_protobuf_message(tvbuff_t *tvb, guint offset, guint length, packet_info
int* parsed_fields = NULL; /* store parsed field numbers. end with NULL */
int parsed_fields_count = 0;
int field_count = 0;
nstime_t timestamp = { 0 };
gchar* value_label = NULL; /* The label representing the value of some wellknown message, such as google.protobuf.Timestamp */
if (message_desc) {
message_name = pbw_Descriptor_full_name(message_desc);
@ -1398,6 +1390,26 @@ dissect_protobuf_message(tvbuff_t *tvb, guint offset, guint length, packet_info
if (add_default_value && field_count > 0) {
parsed_fields = wmem_alloc0_array(pinfo->pool, int, field_count);
}
if (strcmp(message_name, "google.protobuf.Timestamp") == 0) {
/* parse this message as timestamp */
if (tvb_get_protobuf_time(tvb, offset, length, &timestamp)) {
value_label = abs_time_to_rfc3339(scope ? scope : pinfo->pool, &timestamp, use_utc_fmt);
if (hf_msg != -1) {
ti = proto_tree_add_time_format_value(protobuf_tree, hf_msg, tvb, offset, length, &timestamp, "%s", value_label);
protobuf_tree = proto_item_add_subtree(ti, ett_protobuf_message);
}
if (dumper) {
json_dumper_value_string(dumper, value_label);
dumper = NULL; /* this message will not dump as JSON object */
}
} else {
expert_add_info(pinfo, ti_parent, &ei_protobuf_failed_parse_field);
}
} else if (hf_msg != -1) {
ti = proto_tree_add_bytes_format_value(protobuf_tree, hf_msg, tvb, offset, length, NULL, "(%u bytes)", length);
protobuf_tree = proto_item_add_subtree(ti, ett_protobuf_message);
}
}
if (pbf_as_hf && message_desc) {
@ -1474,6 +1486,15 @@ dissect_protobuf_message(tvbuff_t *tvb, guint offset, guint length, packet_info
if (parsed_fields) {
wmem_free(pinfo->pool, parsed_fields);
}
if (value_label) {
ti = proto_tree_add_item(message_tree, hf_text_only, tvb, offset, length, ENC_NA);
proto_item_set_text(ti, "[Message Value: %s]", value_label);
}
if (retval) {
*retval = value_label;
}
}
/* try to find message type by UDP port */
@ -1600,7 +1621,7 @@ dissect_protobuf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data
}
dissect_protobuf_message(tvb, offset, tvb_reported_length_remaining(tvb, offset), pinfo,
protobuf_tree, message_desc, pinfo->ptype == PT_UDP, (message_desc ? &dumper : NULL));
protobuf_tree, message_desc, -1, pinfo->ptype == PT_UDP, (message_desc ? &dumper : NULL), NULL, NULL);
if (display_json_mapping && message_desc && json_dumper_finish(&dumper)) {
ti = proto_tree_add_item(tree, proto_protobuf_json_mapping, tvb, 0, -1, ENC_NA);
@ -1855,7 +1876,7 @@ collect_fields(const PbwDescriptor* message, void* userdata)
sub_msg_desc = pbw_FieldDescriptor_message_type(field_desc);
if (sub_msg_desc && strcmp(pbw_Descriptor_full_name(sub_msg_desc), "google.protobuf.Timestamp") == 0) {
hf->hfinfo.type = FT_ABSOLUTE_TIME;
hf->hfinfo.display = ABSOLUTE_TIME_LOCAL;
hf->hfinfo.display = use_utc_fmt ? ABSOLUTE_TIME_NTP_UTC : ABSOLUTE_TIME_LOCAL;
} else {
hf->hfinfo.type = FT_BYTES;
hf->hfinfo.display = BASE_NONE;
@ -2264,6 +2285,11 @@ proto_register_protobuf(void)
"in addition to the dissection tree",
&display_json_mapping);
prefs_register_bool_preference(protobuf_module, "use_utc",
"Display time in UTC",
"Display timestamp in UTC format",
&use_utc_fmt);
/* Following preferences are for undefined fields, that happened while message type is not specified
when calling dissect_protobuf(), or message type or field information is not found in search paths
*/