2 * Routines for Google Protocol Buffers dissection
3 * Copyright 2017-2022, Huang Qiangxiong <qiangxiong.huang@qq.com>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
13 * The information used comes from:
14 * https://developers.google.com/protocol-buffers/docs/encoding
16 * This protobuf dissector may be invoked by GRPC dissector or other dissectors.
17 * Other dissectors can give protobuf message type info by the data argument or private_table["pb_msg_type"]
18 * before call protobuf dissector.
19 * For GRPC dissector the data argument format is:
20 * "application/grpc" ["+proto"] "," "/" service-name "/" method-name "," ("request" / "response")
22 * application/grpc,/helloworld.Greeter/SayHello,request
23 * In this format, we will try to get real protobuf message type by method (service-name.method-name)
24 * and in/out type (request / response).
25 * For other dissectors can specifies message type directly, like:
26 * "message," message_type_name
28 * message,helloworld.HelloRequest (helloworld is package, HelloRequest is message type)
33 #include <epan/packet.h>
34 #include <epan/expert.h>
35 #include <epan/prefs.h>
37 #include <epan/strutil.h>
38 #include <epan/proto_data.h>
39 #include <wsutil/filesystem.h>
40 #include <wsutil/file_util.h>
41 #include <wsutil/json_dumper.h>
42 #include <wsutil/pint.h>
43 #include <epan/ws_printf.h>
44 #include <wsutil/report_message.h>
46 #include "protobuf-helper.h"
47 #include "packet-protobuf.h"
48 #include "epan/dissectors/packet-http.h"
52 protobuf_uint64_to_double(uint64_t value
) {
53 union { double f
; uint64_t i
; } double_uint64_union
;
55 double_uint64_union
.i
= value
;
56 return double_uint64_union
.f
;
60 protobuf_uint32_to_float(uint32_t value
) {
61 union { float f
; uint32_t i
; } float_uint32_union
;
63 float_uint32_union
.i
= value
;
64 return float_uint32_union
.f
;
67 VALUE_STRING_ARRAY_GLOBAL_DEF(protobuf_wire_type
);
69 /* which field type of each wire type could be */
70 static int protobuf_wire_to_field_type
[6][9] = {
71 /* PROTOBUF_WIRETYPE_VARINT, 0, "varint") */
72 { PROTOBUF_TYPE_INT32
, PROTOBUF_TYPE_INT64
, PROTOBUF_TYPE_UINT32
, PROTOBUF_TYPE_UINT64
,
73 PROTOBUF_TYPE_SINT32
, PROTOBUF_TYPE_SINT64
, PROTOBUF_TYPE_BOOL
, PROTOBUF_TYPE_ENUM
,
76 /* PROTOBUF_WIRETYPE_FIXED64, 1, "64-bit") */
77 { PROTOBUF_TYPE_FIXED64
, PROTOBUF_TYPE_SFIXED64
, PROTOBUF_TYPE_DOUBLE
,
80 /* PROTOBUF_WIRETYPE_LENGTH_DELIMITED, 2, "Length-delimited") */
81 { PROTOBUF_TYPE_STRING
, PROTOBUF_TYPE_BYTES
, PROTOBUF_TYPE_MESSAGE
, PROTOBUF_TYPE_GROUP
,
84 /* PROTOBUF_WIRETYPE_START_GROUP, 3, "Start group (deprecated)") */
85 { PROTOBUF_TYPE_NONE
},
87 /* PROTOBUF_WIRETYPE_END_GROUP, 4, "End group (deprecated)") */
88 { PROTOBUF_TYPE_NONE
},
90 /* PROTOBUF_WIRETYPE_FIXED32, 5, "32-bit") */
91 { PROTOBUF_TYPE_FIXED32
, PROTOBUF_TYPE_SINT32
, PROTOBUF_TYPE_FLOAT
,
95 void proto_register_protobuf(void);
96 void proto_reg_handoff_protobuf(void);
98 #define PREFS_UPDATE_PROTOBUF_SEARCH_PATHS 1
99 #define PREFS_UPDATE_PROTOBUF_UDP_MESSAGE_TYPES 2
100 #define PREFS_UPDATE_PROTOBUF_URI_MESSAGE_TYPES 3
101 #define PREFS_UPDATE_ALL (PREFS_UPDATE_PROTOBUF_SEARCH_PATHS | PREFS_UPDATE_PROTOBUF_UDP_MESSAGE_TYPES | PREFS_UPDATE_PROTOBUF_URI_MESSAGE_TYPES)
103 static void protobuf_reinit(int target
);
105 static int proto_protobuf
;
106 static int proto_protobuf_json_mapping
;
108 static bool protobuf_dissector_called
;
110 /* information get from *.proto files */
111 static int hf_protobuf_message_name
;
112 static int hf_protobuf_field_name
;
113 static int hf_protobuf_field_type
;
116 static int hf_protobuf_field_number
;
117 static int hf_protobuf_wire_type
;
120 static int hf_protobuf_value_length
; /* only Length-delimited field has */
121 static int hf_protobuf_value_data
;
122 static int hf_protobuf_value_double
;
123 static int hf_protobuf_value_float
;
124 static int hf_protobuf_value_int64
;
125 static int hf_protobuf_value_uint64
;
126 static int hf_protobuf_value_int32
;
127 static int hf_protobuf_value_uint32
;
128 static int hf_protobuf_value_bool
;
129 static int hf_protobuf_value_string
;
130 static int hf_protobuf_value_repeated
;
131 static int hf_json_mapping_line
;
134 static expert_field ei_protobuf_failed_parse_tag
;
135 static expert_field ei_protobuf_failed_parse_length_delimited_field
;
136 static expert_field ei_protobuf_failed_parse_field
;
137 static expert_field ei_protobuf_wire_type_invalid
;
138 static expert_field ei_protobuf_message_type_not_found
;
139 static expert_field ei_protobuf_wire_type_not_support_packed_repeated
;
140 static expert_field ei_protobuf_failed_parse_packed_repeated_field
;
141 static expert_field ei_protobuf_missing_required_field
;
142 static expert_field ei_protobuf_default_value_error
;
145 static int ett_protobuf
;
146 static int ett_protobuf_message
;
147 static int ett_protobuf_field
;
148 static int ett_protobuf_value
;
149 static int ett_protobuf_packed_repeated
;
150 static int ett_protobuf_json
;
153 static bool try_dissect_as_string
;
154 static bool show_all_possible_field_types
;
155 static bool dissect_bytes_as_string
;
156 static bool old_dissect_bytes_as_string
;
157 static bool show_details
;
158 static bool pbf_as_hf
; /* dissect protobuf fields as header fields of wireshark */
159 static bool preload_protos
;
160 /* Show protobuf as JSON similar to https://developers.google.com/protocol-buffers/docs/proto3#json */
161 static bool display_json_mapping
;
162 static bool use_utc_fmt
;
163 static const char* default_message_type
= "";
166 #define add_default_value_policy_vals_ENUM_VAL_T_LIST(XXX) \
167 XXX(ADD_DEFAULT_VALUE_NONE, 0, "none", "None") \
168 XXX(ADD_DEFAULT_VALUE_DECLARED, 1, "decl", "Only Explicitly-Declared (proto2)") \
169 XXX(ADD_DEFAULT_VALUE_ENUM_BOOL, 2, "enbl", "Explicitly-Declared, ENUM and BOOL") \
170 XXX(ADD_DEFAULT_VALUE_ALL, 3, "all", "All")
172 typedef ENUM_VAL_T_ENUM(add_default_value_policy_vals
) add_default_value_policy_t
;
174 static int add_default_value
= (int) ADD_DEFAULT_VALUE_NONE
;
176 /* dynamic wireshark header fields for protobuf fields */
177 static hf_register_info
*dynamic_hf
;
178 static unsigned dynamic_hf_size
;
179 /* the key is full name of protobuf fields, the value is header field id */
180 static GHashTable
*pbf_hf_hash
;
182 /* Protobuf field value subdissector table list.
183 * Only valid for the value of PROTOBUF_TYPE_BYTES or PROTOBUF_TYPE_STRING fields.
185 static dissector_table_t protobuf_field_subdissector_table
;
187 static dissector_handle_t protobuf_handle
;
189 /* store varint tvb info */
194 } protobuf_varint_tvb_info_t
;
196 static PbwDescriptorPool
* pbw_pool
;
198 /* protobuf source files search paths */
200 char* path
; /* protobuf source files searching directory path */
201 bool load_all
; /* load all *.proto files in this directory and its sub directories */
202 } protobuf_search_path_t
;
204 static protobuf_search_path_t
* protobuf_search_paths
;
205 static unsigned num_protobuf_search_paths
;
210 protobuf_search_paths_copy_cb(void* n
, const void* o
, size_t siz _U_
)
212 protobuf_search_path_t
* new_rec
= (protobuf_search_path_t
*)n
;
213 const protobuf_search_path_t
* old_rec
= (const protobuf_search_path_t
*)o
;
215 /* copy interval values like int */
216 memcpy(new_rec
, old_rec
, sizeof(protobuf_search_path_t
));
219 new_rec
->path
= g_strdup(old_rec
->path
);
225 protobuf_search_paths_free_cb(void*r
)
227 protobuf_search_path_t
* rec
= (protobuf_search_path_t
*)r
;
232 UAT_DIRECTORYNAME_CB_DEF(protobuf_search_paths
, path
, protobuf_search_path_t
)
233 UAT_BOOL_CB_DEF(protobuf_search_paths
, load_all
, protobuf_search_path_t
)
237 /* The protobuf message type of the data on certain udp ports */
239 range_t
*udp_port_range
; /* dissect data on these udp ports as protobuf */
240 char *message_type
; /* protobuf message type of data on these udp ports */
241 } protobuf_udp_message_type_t
;
243 static protobuf_udp_message_type_t
* protobuf_udp_message_types
;
244 static unsigned num_protobuf_udp_message_types
;
247 protobuf_udp_message_types_copy_cb(void* n
, const void* o
, size_t siz _U_
)
249 protobuf_udp_message_type_t
* new_rec
= (protobuf_udp_message_type_t
*)n
;
250 const protobuf_udp_message_type_t
* old_rec
= (const protobuf_udp_message_type_t
*)o
;
252 /* copy interval values like int */
253 memcpy(new_rec
, old_rec
, sizeof(protobuf_udp_message_type_t
));
255 if (old_rec
->udp_port_range
)
256 new_rec
->udp_port_range
= range_copy(NULL
, old_rec
->udp_port_range
);
257 if (old_rec
->message_type
)
258 new_rec
->message_type
= g_strdup(old_rec
->message_type
);
264 protobuf_udp_message_types_update_cb(void *r
, char **err
)
266 protobuf_udp_message_type_t
* rec
= (protobuf_udp_message_type_t
*)r
;
267 static range_t
*empty
;
269 empty
= range_empty(NULL
);
270 if (ranges_are_equal(rec
->udp_port_range
, empty
)) {
271 *err
= g_strdup("Must specify UDP port(s) (like 8000 or 8000,8008-8088)");
272 wmem_free(NULL
, empty
);
276 wmem_free(NULL
, empty
);
281 protobuf_udp_message_types_free_cb(void*r
)
283 protobuf_udp_message_type_t
* rec
= (protobuf_udp_message_type_t
*)r
;
285 wmem_free(NULL
, rec
->udp_port_range
);
286 g_free(rec
->message_type
);
289 UAT_RANGE_CB_DEF(protobuf_udp_message_types
, udp_port_range
, protobuf_udp_message_type_t
)
290 UAT_CSTRING_CB_DEF(protobuf_udp_message_types
, message_type
, protobuf_udp_message_type_t
)
292 static GSList
* old_udp_port_ranges
;
296 /* The protobuf message type associated with a request URI */
298 char *uri
; /* URI appearing in HTTP message */
299 char *message_type
; /* associated protobuf message type */
300 } protobuf_uri_mapping_t
;
302 static protobuf_uri_mapping_t
* protobuf_uri_message_types
;
303 static unsigned num_protobuf_uri_message_types
;
306 protobuf_uri_message_type_copy_cb(void* n
, const void* o
, size_t siz _U_
)
308 protobuf_uri_mapping_t
* new_rec
= (protobuf_uri_mapping_t
*)n
;
309 const protobuf_uri_mapping_t
* old_rec
= (const protobuf_uri_mapping_t
*)o
;
312 new_rec
->uri
= g_strdup(old_rec
->uri
);
313 if (old_rec
->message_type
)
314 new_rec
->message_type
= g_strdup(old_rec
->message_type
);
320 protobuf_uri_message_type_free_cb(void*r
)
322 protobuf_uri_mapping_t
* rec
= (protobuf_uri_mapping_t
*)r
;
325 g_free(rec
->message_type
);
328 UAT_CSTRING_CB_DEF(protobuf_uri_message_type
, uri
, protobuf_uri_mapping_t
)
329 UAT_CSTRING_CB_DEF(protobuf_uri_message_type
, message_type
, protobuf_uri_mapping_t
)
333 /* If you use int32 or int64 as the type for a negative number, the resulting varint is always
334 * ten bytes long - it is, effectively, treated like a very large unsigned integer. If you use
335 * one of the signed types, the resulting varint uses ZigZag encoding, which is much more efficient.
336 * ZigZag encoding maps signed integers to unsigned integers so that numbers with a small absolute
337 * value (for instance, -1) have a small varint encoded value too. (refers to protobuf spec)
338 * sint32 encoded using (n << 1) ^ (n >> 31)
341 sint32_decode(uint32_t sint32
) {
342 return (sint32
>> 1) ^ ((int32_t)sint32
<< 31 >> 31);
345 /* sint64 encoded using (n << 1) ^ (n >> 63) */
347 sint64_decode(uint64_t sint64
) {
348 return (sint64
>> 1) ^ ((int64_t)sint64
<< 63 >> 63);
351 /* Try to get a protobuf field which has a varint value from the tvb.
352 * The field number, wire type and uint64 value will be output.
353 * @return the length of this field. Zero if failed.
356 tvb_get_protobuf_field_uint(tvbuff_t
* tvb
, unsigned offset
, unsigned maxlen
,
357 uint64_t* field_number
, uint32_t* wire_type
, uint64_t* value
)
359 unsigned tag_length
, value_length
;
362 /* parsing the tag of the field */
363 tag_length
= tvb_get_varint(tvb
, offset
, maxlen
, &tag_value
, ENC_VARINT_PROTOBUF
);
364 if (tag_length
== 0 || tag_length
>= maxlen
) {
367 *field_number
= tag_value
>> 3;
368 *wire_type
= tag_value
& 0x07;
370 if (*wire_type
!= PROTOBUF_WIRETYPE_VARINT
) {
373 /* parsing the value of the field */
374 value_length
= tvb_get_varint(tvb
, offset
+ tag_length
, maxlen
- tag_length
, value
, ENC_VARINT_PROTOBUF
);
375 return (value_length
== 0) ? 0 : (tag_length
+ value_length
);
378 /* Get Protobuf timestamp from the tvb according to the format of google.protobuf.Timestamp.
379 * return the length parsed.
382 tvb_get_protobuf_time(tvbuff_t
* tvb
, unsigned offset
, unsigned maxlen
, nstime_t
* timestamp
)
384 unsigned field_length
;
385 uint64_t field_number
, value
;
387 unsigned off
= offset
;
388 unsigned len
= maxlen
; /* remain bytes */
390 /* Get the seconds and nanos fields from google.protobuf.Timestamp message which defined:
392 * message Timestamp {
397 nstime_set_zero(timestamp
);
400 field_length
= tvb_get_protobuf_field_uint(tvb
, off
, len
, &field_number
, &wire_type
, &value
);
401 if (field_length
== 0) {
405 if (field_number
== 1) {
406 timestamp
->secs
= (time_t)value
;
407 } else if (field_number
== 2) {
408 timestamp
->nsecs
= (int)value
;
415 if (timestamp
->nsecs
< 0 || timestamp
->nsecs
> 999999999) {
416 nstime_set_unset(timestamp
);
423 /* declare first because it will be called by dissect_packed_repeated_field_values */
425 protobuf_dissect_field_value(proto_tree
*value_tree
, tvbuff_t
*tvb
, unsigned offset
, unsigned length
, packet_info
*pinfo
,
426 proto_item
*ti_field
, int field_type
, const uint64_t value
, const char* prepend_text
, const PbwFieldDescriptor
* field_desc
,
427 bool is_top_level
, json_dumper
*dumper
);
430 dissect_protobuf_message(tvbuff_t
*tvb
, unsigned offset
, unsigned length
, packet_info
*pinfo
, proto_tree
*protobuf_tree
,
431 const PbwDescriptor
* message_desc
, int hf_msg
, bool is_top_level
, json_dumper
*dumper
, wmem_allocator_t
* scope
, char** retval
);
433 /* Only repeated fields of primitive numeric types (types which use the varint, 32-bit, or 64-bit wire types) can
434 * be declared "packed".
435 * The format of a packed_repeated field likes: tag + varint + varint + varint ...
436 * or likes: tag + fixed64 + fixed64 + fixed64 ...
437 * Return consumed bytes
440 // NOLINTNEXTLINE(misc-no-recursion)
441 dissect_packed_repeated_field_values(tvbuff_t
*tvb
, unsigned start
, unsigned length
, packet_info
*pinfo
,
442 proto_item
*ti_field
, int field_type
, const char* prepend_text
, const PbwFieldDescriptor
* field_desc
,
446 unsigned sub_value_length
;
447 unsigned offset
= start
;
448 protobuf_varint_tvb_info_t
*info
;
449 unsigned max_offset
= offset
+ length
;
450 wmem_list_frame_t
*lframe
;
451 wmem_list_t
* varint_list
;
454 if (prepend_text
== NULL
) {
458 /* prepare subtree */
459 proto_item_append_text(ti_field
, "%s [", prepend_text
);
460 proto_item
*ti
= proto_tree_add_item(proto_item_get_subtree(ti_field
), hf_protobuf_value_repeated
, tvb
, start
, length
, ENC_NA
);
461 proto_tree
*subtree
= proto_item_add_subtree(ti
, ett_protobuf_packed_repeated
);
467 /* packed for Varint encoded types (int32, int64, uint32, uint64, sint32, sint64, bool, enum) */
468 /* format: tag + varint + varint + varint ... */
469 case PROTOBUF_TYPE_INT32
:
470 case PROTOBUF_TYPE_INT64
:
471 case PROTOBUF_TYPE_UINT32
:
472 case PROTOBUF_TYPE_UINT64
:
473 case PROTOBUF_TYPE_SINT32
:
474 case PROTOBUF_TYPE_SINT64
:
475 case PROTOBUF_TYPE_BOOL
:
476 case PROTOBUF_TYPE_ENUM
:
477 varint_list
= wmem_list_new(pinfo
->pool
);
479 /* try to test all can parsed as varint */
480 while (offset
< max_offset
) {
481 sub_value_length
= tvb_get_varint(tvb
, offset
, max_offset
- offset
, &sub_value
, ENC_VARINT_PROTOBUF
);
482 if (sub_value_length
== 0) {
483 /* not a valid packed repeated field */
484 wmem_destroy_list(varint_list
);
488 /* temporarily store varint info in the list */
489 info
= wmem_new(pinfo
->pool
, protobuf_varint_tvb_info_t
);
490 info
->offset
= offset
;
491 info
->length
= sub_value_length
;
492 info
->value
= sub_value
;
493 wmem_list_append(varint_list
, info
);
495 offset
+= sub_value_length
;
498 /* all parsed, we add varints into the packed-repeated subtree */
499 for (lframe
= wmem_list_head(varint_list
); lframe
!= NULL
; lframe
= wmem_list_frame_next(lframe
)) {
500 info
= (protobuf_varint_tvb_info_t
*)wmem_list_frame_data(lframe
);
501 protobuf_dissect_field_value(subtree
, tvb
, info
->offset
, info
->length
, pinfo
,
502 ti_field
, field_type
, info
->value
, prepend_text
, field_desc
, false, dumper
);
506 wmem_destroy_list(varint_list
);
509 /* packed for 64-bit encoded types (fixed64, sfixed64, double) and 32-bit encoded types (fixed32, sfixed32, float) */
510 /* format like: tag + sint32 + sint32 + sint32 ... */
511 case PROTOBUF_TYPE_FIXED64
:
512 case PROTOBUF_TYPE_SFIXED64
:
513 case PROTOBUF_TYPE_DOUBLE
:
514 value_size
= 8; /* 64-bit */
516 case PROTOBUF_TYPE_FIXED32
:
517 case PROTOBUF_TYPE_SFIXED32
:
518 case PROTOBUF_TYPE_FLOAT
:
519 if (value_size
== 0) {
520 value_size
= 4; /* 32-bit */
523 if (length
% value_size
!= 0) {
524 expert_add_info(pinfo
, ti_field
, &ei_protobuf_failed_parse_packed_repeated_field
);
528 for (offset
= start
; offset
< max_offset
; offset
+= value_size
) {
529 protobuf_dissect_field_value(subtree
, tvb
, offset
, value_size
, pinfo
, ti_field
, field_type
,
530 (value_size
== 4 ? tvb_get_uint32(tvb
, offset
, ENC_LITTLE_ENDIAN
)
531 : tvb_get_uint64(tvb
, offset
, ENC_LITTLE_ENDIAN
)),
532 prepend_text
, field_desc
, false, dumper
);
540 expert_add_info(pinfo
, ti_field
, &ei_protobuf_wire_type_not_support_packed_repeated
);
541 return 0; /* prevent dead loop */
544 proto_item_append_text(ti_field
, "]");
548 /* The "google.protobuf.Timestamp" must be converted to rfc3339 format if mapping to JSON
549 * according to https://developers.google.com/protocol-buffers/docs/proto3#json
552 abs_time_to_rfc3339(wmem_allocator_t
*scope
, const nstime_t
*nstime
, bool use_utc
)
555 char datetime_format
[128];
561 tm
= gmtime(&nstime
->secs
);
563 strftime(datetime_format
, sizeof(datetime_format
), "%Y-%m-%dT%H:%M:%S%%sZ", tm
);
565 snprintf(datetime_format
, sizeof(datetime_format
), "Not representable");
567 tm
= localtime(&nstime
->secs
);
569 strftime(datetime_format
, sizeof(datetime_format
), "%Y-%m-%dT%H:%M:%S%%s%z", tm
);
571 snprintf(datetime_format
, sizeof(datetime_format
), "Not representable");
574 if (nstime
->nsecs
== 0)
575 return wmem_strdup_printf(scope
, datetime_format
, "");
577 nsecs
= nstime
->nsecs
;
579 while (width
> 0 && (nsecs
% 1000) == 0) {
583 snprintf(nsecs_buf
, sizeof(nsecs_buf
), ".%0*d", width
, nsecs
);
585 return wmem_strdup_printf(scope
, datetime_format
, nsecs_buf
);
588 /* Dissect field value based on a specific type. */
590 // NOLINTNEXTLINE(misc-no-recursion)
591 protobuf_dissect_field_value(proto_tree
*value_tree
, tvbuff_t
*tvb
, unsigned offset
, unsigned length
, packet_info
*pinfo
,
592 proto_item
*ti_field
, int field_type
, const uint64_t value
, const char* prepend_text
, const PbwFieldDescriptor
* field_desc
,
593 bool is_top_level
, json_dumper
*dumper
)
600 bool add_datatype
= true;
601 proto_item
* ti
= NULL
;
602 proto_tree
* subtree
= NULL
;
603 const char* enum_value_name
= NULL
;
604 const PbwDescriptor
* sub_message_desc
= NULL
;
605 const PbwEnumDescriptor
* enum_desc
= NULL
;
606 int* hf_id_ptr
= NULL
;
607 const char* field_full_name
= field_desc
? pbw_FieldDescriptor_full_name(field_desc
) : NULL
;
608 proto_tree
* field_tree
= proto_item_get_subtree(ti_field
);
609 proto_tree
* field_parent_tree
= proto_tree_get_parent_tree(field_tree
);
610 proto_tree
* pbf_tree
= field_tree
;
611 dissector_handle_t field_dissector
= field_full_name
? dissector_get_string_handle(protobuf_field_subdissector_table
, field_full_name
) : NULL
;
613 if (pbf_as_hf
&& field_full_name
) {
614 hf_id_ptr
= (int*)g_hash_table_lookup(pbf_hf_hash
, field_full_name
);
615 DISSECTOR_ASSERT_HINT(hf_id_ptr
&& (*hf_id_ptr
) > 0, "hf must have been initialized properly");
618 if (pbf_as_hf
&& hf_id_ptr
&& !show_details
) {
619 /* set ti_field (Field(x)) item hidden if there is header_field */
620 proto_item_set_hidden(ti_field
);
621 pbf_tree
= field_parent_tree
;
624 if (prepend_text
== NULL
) {
630 case PROTOBUF_TYPE_DOUBLE
:
631 double_value
= protobuf_uint64_to_double(value
);
632 proto_tree_add_double(value_tree
, hf_protobuf_value_double
, tvb
, offset
, length
, double_value
);
633 proto_item_append_text(ti_field
, "%s %lf", prepend_text
, double_value
);
635 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "=%lf", double_value
);
638 proto_tree_add_double(pbf_tree
, *hf_id_ptr
, tvb
, offset
, length
, double_value
);
640 if (field_desc
&& dumper
) {
641 json_dumper_value_double(dumper
, double_value
);
645 case PROTOBUF_TYPE_FLOAT
:
646 float_value
= protobuf_uint32_to_float((uint32_t) value
);
647 proto_tree_add_float(value_tree
, hf_protobuf_value_float
, tvb
, offset
, length
, float_value
);
648 proto_item_append_text(ti_field
, "%s %f", prepend_text
, float_value
);
650 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "=%f", float_value
);
653 proto_tree_add_float(pbf_tree
, *hf_id_ptr
, tvb
, offset
, length
, float_value
);
655 if (field_desc
&& dumper
) {
656 json_dumper_value_anyf(dumper
, "%f", float_value
);
660 case PROTOBUF_TYPE_INT64
:
661 case PROTOBUF_TYPE_SFIXED64
:
662 int64_value
= (int64_t) value
;
663 proto_tree_add_int64(value_tree
, hf_protobuf_value_int64
, tvb
, offset
, length
, int64_value
);
664 proto_item_append_text(ti_field
, "%s %" PRId64
, prepend_text
, int64_value
);
666 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "=%" PRId64
, int64_value
);
669 proto_tree_add_int64(pbf_tree
, *hf_id_ptr
, tvb
, offset
, length
, int64_value
);
671 if (field_desc
&& dumper
) {
672 json_dumper_value_anyf(dumper
, "\"%" PRId64
"\"", int64_value
);
676 case PROTOBUF_TYPE_UINT64
:
677 case PROTOBUF_TYPE_FIXED64
: /* same as UINT64 */
678 proto_tree_add_uint64(value_tree
, hf_protobuf_value_uint64
, tvb
, offset
, length
, value
);
679 proto_item_append_text(ti_field
, "%s %" PRIu64
, prepend_text
, value
);
681 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "=%" PRIu64
, value
);
684 proto_tree_add_uint64(pbf_tree
, *hf_id_ptr
, tvb
, offset
, length
, value
);
686 if (field_desc
&& dumper
) {
687 json_dumper_value_anyf(dumper
, "\"%" PRIu64
"\"", value
);
691 case PROTOBUF_TYPE_INT32
:
692 case PROTOBUF_TYPE_SFIXED32
:
693 int32_value
= (int32_t)value
;
694 proto_tree_add_int(value_tree
, hf_protobuf_value_int32
, tvb
, offset
, length
, int32_value
);
695 proto_item_append_text(ti_field
, "%s %d", prepend_text
, int32_value
);
697 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "=%d", int32_value
);
700 proto_tree_add_int(pbf_tree
, *hf_id_ptr
, tvb
, offset
, length
, int32_value
);
702 if (field_desc
&& dumper
) {
703 json_dumper_value_anyf(dumper
, "%d", int32_value
);
707 case PROTOBUF_TYPE_ENUM
:
708 int32_value
= (int32_t) value
;
709 /* get the name of enum value */
711 enum_desc
= pbw_FieldDescriptor_enum_type(field_desc
);
713 const PbwEnumValueDescriptor
* enum_value_desc
= pbw_EnumDescriptor_FindValueByNumber(enum_desc
, int32_value
);
714 if (enum_value_desc
) {
715 enum_value_name
= pbw_EnumValueDescriptor_name(enum_value_desc
);
719 ti
= proto_tree_add_int(value_tree
, hf_protobuf_value_int32
, tvb
, offset
, length
, int32_value
);
720 if (enum_value_name
) { /* show enum value name */
721 proto_item_append_text(ti_field
, "%s %s(%d)", prepend_text
, enum_value_name
, int32_value
);
722 proto_item_append_text(ti
, " (%s)", enum_value_name
);
724 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "=%s", enum_value_name
);
727 proto_item_append_text(ti_field
, "%s %d", prepend_text
, int32_value
);
729 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "=%d", int32_value
);
734 proto_tree_add_int(pbf_tree
, *hf_id_ptr
, tvb
, offset
, length
, int32_value
);
736 if (field_desc
&& dumper
) {
737 if (enum_value_name
) {
738 json_dumper_value_string(dumper
, enum_value_name
);
740 /* The enum value is used if the name of the enum value is not specified in proto */
741 json_dumper_value_anyf(dumper
, "%d", int32_value
);
746 case PROTOBUF_TYPE_BOOL
:
747 if (length
> 1) break; /* boolean should not use more than one bytes */
748 proto_tree_add_boolean(value_tree
, hf_protobuf_value_bool
, tvb
, offset
, length
, value
);
749 proto_item_append_text(ti_field
, "%s %s", prepend_text
, value
? "true" : "false");
751 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "=%s", value
? "true" : "false");
754 proto_tree_add_boolean(pbf_tree
, *hf_id_ptr
, tvb
, offset
, length
, value
);
756 if (field_desc
&& dumper
) {
757 json_dumper_value_anyf(dumper
, value
? "true" : "false");
761 case PROTOBUF_TYPE_BYTES
:
762 if (field_desc
&& dumper
) {
763 json_dumper_begin_base64(dumper
);
764 buf
= (char*) tvb_memdup(wmem_file_scope(), tvb
, offset
, length
);
766 json_dumper_write_base64(dumper
, buf
, length
);
767 wmem_free(wmem_file_scope(), buf
);
769 json_dumper_end_base64(dumper
);
771 if (field_dissector
) {
772 if (!show_details
) { /* don't show Value node if there is a subdissector for this field */
773 proto_item_set_hidden(proto_tree_get_parent(value_tree
));
775 if (dissect_bytes_as_string
) { /* the type of *hf_id_ptr MUST be FT_STRING now */
777 ti
= proto_tree_add_string_format_value(pbf_tree
, *hf_id_ptr
, tvb
, offset
, length
, "", "(%u bytes)", length
);
779 /* don't try to dissect bytes as string if there is a subdissector for this field */
783 if (!dissect_bytes_as_string
) {
784 /* the type of *hf_id_ptr MUST be FT_BYTES now */
786 ti
= proto_tree_add_bytes_format_value(pbf_tree
, *hf_id_ptr
, tvb
, offset
, length
, NULL
, "(%u bytes)", length
);
790 /* or continue dissect BYTES as STRING */
791 proto_item_append_text(ti_field
, " =");
793 case PROTOBUF_TYPE_STRING
:
794 proto_tree_add_item_ret_display_string(value_tree
, hf_protobuf_value_string
, tvb
, offset
, length
, ENC_UTF_8
|ENC_NA
, pinfo
->pool
, &buf
);
795 proto_item_append_text(ti_field
, "%s %s", prepend_text
, buf
);
797 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "=%s", buf
);
800 ti
= proto_tree_add_item_ret_display_string(pbf_tree
, *hf_id_ptr
, tvb
, offset
, length
, ENC_UTF_8
|ENC_NA
, pinfo
->pool
, &buf
);
802 if (field_desc
&& dumper
&& field_type
== PROTOBUF_TYPE_STRING
) {
803 /* JSON view will ignore the dissect_bytes_as_string option */
804 json_dumper_value_string(dumper
, buf
);
808 case PROTOBUF_TYPE_GROUP
: /* This feature is deprecated. GROUP is identical to Nested MESSAGE. */
809 case PROTOBUF_TYPE_MESSAGE
:
810 subtree
= field_tree
;
812 sub_message_desc
= pbw_FieldDescriptor_message_type(field_desc
);
813 if (sub_message_desc
== NULL
) {
814 expert_add_info(pinfo
, ti_field
, &ei_protobuf_message_type_not_found
);
817 if (sub_message_desc
) {
818 dissect_protobuf_message(tvb
, offset
, length
, pinfo
, pbf_as_hf
? pbf_tree
: subtree
, sub_message_desc
,
819 hf_id_ptr
? *hf_id_ptr
: -1,
820 false, // not top level
825 if (buf
) { /* append the value in string format to ti_field node */
826 proto_item_append_text(ti_field
, "= %s", buf
);
828 } else if (hf_id_ptr
) {
829 proto_tree_add_bytes_format_value(pbf_tree
, *hf_id_ptr
, tvb
, offset
, length
, NULL
, "(%u bytes)", length
);
831 /* we don't continue with unknown message type */
835 case PROTOBUF_TYPE_UINT32
:
836 case PROTOBUF_TYPE_FIXED32
: /* same as UINT32 */
837 proto_tree_add_uint(value_tree
, hf_protobuf_value_uint32
, tvb
, offset
, length
, (uint32_t)value
);
838 proto_item_append_text(ti_field
, "%s %u", prepend_text
, (uint32_t)value
);
840 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "=%u", (uint32_t)value
);
843 proto_tree_add_uint(pbf_tree
, *hf_id_ptr
, tvb
, offset
, length
, (uint32_t)value
);
845 if (field_desc
&& dumper
) {
846 json_dumper_value_anyf(dumper
, "%u", (uint32_t)value
);
850 case PROTOBUF_TYPE_SINT32
:
851 int32_value
= sint32_decode((uint32_t)value
);
852 proto_tree_add_int(value_tree
, hf_protobuf_value_int32
, tvb
, offset
, length
, int32_value
);
853 proto_item_append_text(ti_field
, "%s %d", prepend_text
, int32_value
);
855 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "=%d", int32_value
);
858 proto_tree_add_int(pbf_tree
, *hf_id_ptr
, tvb
, offset
, length
, int32_value
);
860 if (field_desc
&& dumper
) {
861 json_dumper_value_anyf(dumper
, "%d", int32_value
);
865 case PROTOBUF_TYPE_SINT64
:
866 int64_value
= sint64_decode(value
);
867 proto_tree_add_int64(value_tree
, hf_protobuf_value_int64
, tvb
, offset
, length
, int64_value
);
868 proto_item_append_text(ti_field
, "%s %" PRId64
, prepend_text
, int64_value
);
870 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "=%" PRId64
, int64_value
);
873 proto_tree_add_int64(pbf_tree
, *hf_id_ptr
, tvb
, offset
, length
, int64_value
);
875 if (field_desc
&& dumper
) {
876 json_dumper_value_anyf(dumper
, "%" PRId64
, int64_value
);
881 /* ignore unknown field type */
882 add_datatype
= false;
886 /* try dissect field value according to protobuf_field dissector table */
887 if (field_dissector
) {
888 /* determine the tree passing to the subdissector */
889 subtree
= field_tree
;
891 subtree
= proto_item_get_subtree(ti
);
893 subtree
= proto_item_add_subtree(ti
, ett_protobuf_value
);
897 call_dissector(field_dissector
, tvb_new_subset_length(tvb
, offset
, length
), pinfo
, subtree
);
901 proto_item_append_text(ti_field
, " (%s)", val_to_str(field_type
, protobuf_field_type
, "Unknown type (%d)"));
905 /* add all possible values according to field types. */
907 // NOLINTNEXTLINE(misc-no-recursion)
908 protobuf_try_dissect_field_value_on_multi_types(proto_tree
*value_tree
, tvbuff_t
*tvb
, unsigned offset
, unsigned length
,
909 packet_info
*pinfo
, proto_item
*ti_field
, int* field_types
, const uint64_t value
, const char* prepend_text
,
914 if (prepend_text
== NULL
) {
918 for (i
= 0; field_types
[i
] != PROTOBUF_TYPE_NONE
; ++i
) {
919 protobuf_dissect_field_value(value_tree
, tvb
, offset
, length
, pinfo
, ti_field
, field_types
[i
], value
, prepend_text
, NULL
, false, dumper
);
925 // NOLINTNEXTLINE(misc-no-recursion)
926 dissect_one_protobuf_field(tvbuff_t
*tvb
, unsigned* offset
, unsigned maxlen
, packet_info
*pinfo
, proto_tree
*protobuf_tree
,
927 const PbwDescriptor
* message_desc
, bool is_top_level
, const PbwFieldDescriptor
** field_desc_ptr
,
928 const PbwFieldDescriptor
* prev_field_desc
, json_dumper
*dumper
)
930 uint64_t tag_value
; /* tag value = (field_number << 3) | wire_type */
931 unsigned tag_length
; /* how many bytes this tag has */
932 uint64_t field_number
;
934 uint64_t value_uint64
; /* uint64 value of numeric field (type of varint, 64-bit, 32-bit */
935 unsigned value_length
;
936 unsigned value_length_size
= 0; /* only Length-delimited field has it */
937 proto_item
*ti_field
, *ti_field_number
, *ti_wire
, *ti_value_length
= NULL
;
938 proto_item
*ti_value
, *ti_field_name
, *ti_field_type
= NULL
;
939 proto_tree
*field_tree
;
940 proto_tree
*value_tree
;
941 const char* field_name
= NULL
;
943 bool is_packed
= false;
944 bool is_repeated
= false;
945 const PbwFieldDescriptor
* field_desc
= NULL
;
946 unsigned start_offset
= *offset
;
948 /* A protocol buffer message is a series of key-value pairs. The binary version of a message just uses
949 * the field's number as the key. a wire type that provides just enough information to find the length of
950 * the following value.
951 * Format of protobuf is:
952 * protobuf field -> tag value
953 * tag -> (field_number << 3) | wire_type (the last three bits of the number store the wire type)
954 * value -> according to wiret_type, value may be
955 * - varint (int32, int64, uint32, uint64, sint32, sint64, bool, enum),
956 * - 64-bit number (fixed64, sfixed64, double)
957 * - Length-delimited (string, bytes, embedded messages, packed repeated fields)
958 * - deprecated 'Start group' or 'End group' (we stop dissecting when encountered them)
959 * - 32-bit (fixed32, sfixed32, float)
960 * All numbers in protobuf are stored in little-endian byte order.
963 field_tree
= proto_tree_add_subtree(protobuf_tree
, tvb
, *offset
, 0, ett_protobuf_field
, &ti_field
, "Field");
966 tag_length
= tvb_get_varint(tvb
, *offset
, maxlen
, &tag_value
, ENC_VARINT_PROTOBUF
);
968 if (tag_length
== 0) { /* not found a valid varint */
969 expert_add_info(pinfo
, ti_field
, &ei_protobuf_failed_parse_tag
);
973 ti_field_number
= proto_tree_add_item_ret_uint64(field_tree
, hf_protobuf_field_number
, tvb
, *offset
, tag_length
, ENC_LITTLE_ENDIAN
|ENC_VARINT_PROTOBUF
, &field_number
);
974 ti_wire
= proto_tree_add_item_ret_uint(field_tree
, hf_protobuf_wire_type
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
|ENC_VARINT_PROTOBUF
, &wire_type
);
975 (*offset
) += tag_length
;
976 /* try to find field_info first */
978 /* find field descriptor according to field number from message descriptor */
979 field_desc
= pbw_Descriptor_FindFieldByNumber(message_desc
, (int) field_number
);
981 *field_desc_ptr
= field_desc
;
982 field_name
= pbw_FieldDescriptor_name(field_desc
);
983 field_type
= pbw_FieldDescriptor_type(field_desc
);
984 is_packed
= pbw_FieldDescriptor_is_packed(field_desc
);
985 is_repeated
= pbw_FieldDescriptor_is_repeated(field_desc
);
989 proto_item_append_text(ti_field
, "(%" PRIu64
"):", field_number
);
991 /* support filtering with field name */
992 ti_field_name
= proto_tree_add_string(field_tree
, hf_protobuf_field_name
, tvb
, start_offset
, 0,
993 (field_name
? field_name
: "<UNKNOWN>"));
994 proto_item_set_generated(ti_field_name
);
996 proto_item_append_text(ti_field
, " %s %s", field_name
,
997 (field_type
== PROTOBUF_TYPE_MESSAGE
|| field_type
== PROTOBUF_TYPE_GROUP
998 || field_type
== PROTOBUF_TYPE_BYTES
)
1001 if (field_type
> 0) {
1002 ti_field_type
= proto_tree_add_int(field_tree
, hf_protobuf_field_type
, tvb
, start_offset
, 0, field_type
);
1003 proto_item_set_generated(ti_field_type
);
1007 /* Show field name in Info column */
1008 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " %s", field_name
);
1012 /* move ti_field_number and ti_wire after ti_field_type (or field_type) for good look */
1013 proto_tree_move_item(field_tree
, (ti_field_type
? ti_field_type
: ti_field_name
), ti_wire
);
1014 proto_tree_move_item(field_tree
, (ti_field_type
? ti_field_type
: ti_field_name
), ti_field_number
);
1016 /* determine value_length, uint of numeric value and maybe value_length_size according to wire_type */
1019 case PROTOBUF_WIRETYPE_VARINT
: /* varint, format: tag + varint */
1020 /* get value length and real value */
1021 value_length
= tvb_get_varint(tvb
, *offset
, maxlen
- tag_length
, &value_uint64
, ENC_VARINT_PROTOBUF
);
1022 if (value_length
== 0) {
1023 expert_add_info(pinfo
, ti_wire
, &ei_protobuf_failed_parse_field
);
1028 case PROTOBUF_WIRETYPE_FIXED64
: /* fixed 64-bit type, format: tag + 64-bit-value */
1029 /* get value length and real value */
1031 value_uint64
= tvb_get_letoh64(tvb
, *offset
);
1034 case PROTOBUF_WIRETYPE_FIXED32
: /* fixed 32-bit type, format: tag + 32-bit-value */
1036 value_uint64
= tvb_get_letohl(tvb
, *offset
);
1039 case PROTOBUF_WIRETYPE_LENGTH_DELIMITED
: /* Length-delimited, format: tag + length(varint) + bytes_value */
1040 /* this time value_uint64 is the length of following value bytes */
1041 value_length_size
= tvb_get_varint(tvb
, *offset
, maxlen
- tag_length
, &value_uint64
, ENC_VARINT_PROTOBUF
);
1042 if (value_length_size
== 0) {
1043 expert_add_info(pinfo
, ti_field
, &ei_protobuf_failed_parse_length_delimited_field
);
1047 ti_value_length
= proto_tree_add_uint64(field_tree
, hf_protobuf_value_length
, tvb
, *offset
, value_length_size
, value_uint64
);
1048 (*offset
) += value_length_size
;
1050 /* we believe the length of following value will not be bigger than unsigned */
1051 value_length
= (unsigned) value_uint64
;
1055 expert_add_info(pinfo
, ti_wire
, &ei_protobuf_wire_type_invalid
);
1059 proto_item_set_len(ti_field
, tag_length
+ value_length_size
+ value_length
);
1060 proto_item_set_len(ti_field_name
, tag_length
+ value_length_size
+ value_length
);
1061 if (ti_field_type
) {
1062 proto_item_set_len(ti_field_type
, tag_length
+ value_length_size
+ value_length
);
1065 /* add value as bytes first */
1066 ti_value
= proto_tree_add_item(field_tree
, hf_protobuf_value_data
, tvb
, *offset
, value_length
, ENC_NA
);
1068 /* add value subtree. we add uint value for numeric field or string for length-delimited at least. */
1069 value_tree
= proto_item_add_subtree(ti_value
, ett_protobuf_value
);
1071 increment_dissection_depth(pinfo
);
1074 if (prev_field_desc
== NULL
|| pbw_FieldDescriptor_number(prev_field_desc
) != (int) field_number
) {
1075 /* end JSON array if previous field is repeated field */
1076 if (prev_field_desc
&& pbw_FieldDescriptor_is_repeated(prev_field_desc
)) {
1077 json_dumper_end_array(dumper
);
1080 /* set JSON name if it is the first of an unpacked repeated field, or an unrepeated field */
1081 json_dumper_set_member_name(dumper
, field_name
);
1083 /* begin JSON array if it is the first of a repeated field */
1085 json_dumper_begin_array(dumper
);
1089 if (is_repeated
&& is_packed
) {
1090 dissect_packed_repeated_field_values(tvb
, *offset
, value_length
, pinfo
, ti_field
,
1091 field_type
, "", field_desc
, dumper
);
1093 protobuf_dissect_field_value(value_tree
, tvb
, *offset
, value_length
, pinfo
, ti_field
, field_type
, value_uint64
, "", field_desc
,
1094 is_top_level
, dumper
);
1097 /* end JSON array if previous field is repeated field. We must end
1098 * the array here even if we don't add this unknown field to the JSON,
1099 * so that the array ends at the correct nested level.
1101 if (dumper
&& prev_field_desc
&& pbw_FieldDescriptor_is_repeated(prev_field_desc
)) {
1102 json_dumper_end_array(dumper
);
1104 if (show_all_possible_field_types
) {
1105 /* try dissect every possible field type */
1106 protobuf_try_dissect_field_value_on_multi_types(value_tree
, tvb
, *offset
, value_length
, pinfo
,
1107 ti_field
, protobuf_wire_to_field_type
[wire_type
], value_uint64
, "", dumper
);
1109 field_type
= (wire_type
== PROTOBUF_WIRETYPE_LENGTH_DELIMITED
)
1110 /* print string at least for length-delimited */
1111 ? (try_dissect_as_string
? PROTOBUF_TYPE_STRING
: PROTOBUF_TYPE_NONE
)
1112 /* use uint32 or uint64 */
1113 : (value_uint64
<= 0xFFFFFFFF ? PROTOBUF_TYPE_UINT32
: PROTOBUF_TYPE_UINT64
);
1114 int field_types
[] = { field_type
, PROTOBUF_TYPE_NONE
};
1116 protobuf_try_dissect_field_value_on_multi_types(value_tree
, tvb
, *offset
, value_length
, pinfo
,
1117 ti_field
, field_types
, value_uint64
, "", dumper
);
1120 decrement_dissection_depth(pinfo
);
1122 if (field_desc
&& !show_details
) {
1123 proto_item_set_hidden(ti_field_number
);
1124 proto_item_set_hidden(ti_wire
);
1125 proto_item_set_hidden(ti_value_length
);
1126 proto_item_set_hidden(ti_field_name
);
1127 proto_item_set_hidden(ti_field_type
);
1128 if (field_type
!= PROTOBUF_TYPE_BYTES
&& field_type
!= PROTOBUF_TYPE_GROUP
) {
1129 proto_item_set_hidden(ti_value
);
1133 (*offset
) += value_length
;
1137 /* Make Protobuf fields that are not serialized on the wire (missing in capture files) to be displayed
1138 * with default values. In 'proto2', default values can be explicitly declared. In 'proto3', if a
1139 * field is set to its default, the value will *not* be serialized on the wire.
1141 * The default value will be displayed according to following situations:
1142 * 1. Explicitly-declared default values in 'proto2', for example:
1143 * optional int32 result_per_page = 3 [default = 10]; // default value is 10
1144 * 2. For bools, the default value is false.
1145 * 3. For enums, the default value is the first defined enum value, which must be 0 in 'proto3' (but
1146 * allowed to be other in 'proto2').
1147 * 4. For numeric types, the default value is zero.
1148 * There are no default values for fields 'repeated' or 'bytes' and 'string' without default value declared.
1149 * If the missing field is 'required' in a 'proto2' file, an expert warning item will be added to the tree.
1151 * Which fields will be displayed is controlled by 'add_default_value' option:
1152 * - ADD_DEFAULT_VALUE_NONE -- do not display any missing fields.
1153 * - ADD_DEFAULT_VALUE_DECLARED -- only missing fields of situation (1) will be displayed.
1154 * - ADD_DEFAULT_VALUE_ENUM_BOOL -- missing fields of situantions (1, 2 and 3) will be displayed.
1155 * - ADD_DEFAULT_VALUE_ALL -- missing fields of all situations (1, 2, 3, and 4) will be displayed.
1158 add_missing_fields_with_default_values(tvbuff_t
* tvb
, unsigned offset
, packet_info
* pinfo
, proto_tree
* message_tree
,
1159 const PbwDescriptor
* message_desc
, wmem_map_t
* parsed_fields
, json_dumper
*dumper
)
1161 const PbwFieldDescriptor
* field_desc
;
1162 const char* field_name
, * field_full_name
, * enum_value_name
, * string_value
;
1163 int field_count
= pbw_Descriptor_field_count(message_desc
);
1165 uint64_t field_number
;
1168 bool has_default_value
; /* explicitly-declared default value */
1169 proto_item
* ti_message
= proto_tree_get_parent(message_tree
);
1170 proto_item
* ti_field
, * ti_field_number
, * ti_field_name
, * ti_field_type
, * ti_value
, * ti_pbf
;
1171 proto_tree
* field_tree
, * pbf_tree
;
1173 double double_value
;
1175 int64_t int64_value
;
1176 int32_t int32_value
;
1177 uint64_t uint64_value
;
1178 uint32_t uint32_value
;
1181 const PbwEnumValueDescriptor
* enum_value_desc
;
1183 for (i
= 0; i
< field_count
; i
++) {
1184 field_desc
= pbw_Descriptor_field(message_desc
, i
);
1185 field_number
= (uint64_t) pbw_FieldDescriptor_number(field_desc
);
1186 field_type
= pbw_FieldDescriptor_type(field_desc
);
1187 is_required
= pbw_FieldDescriptor_is_required(field_desc
);
1188 is_repeated
= pbw_FieldDescriptor_is_repeated(field_desc
);
1189 has_default_value
= pbw_FieldDescriptor_has_default_value(field_desc
);
1191 if (!is_required
&& add_default_value
== ADD_DEFAULT_VALUE_DECLARED
&& !has_default_value
) {
1192 /* ignore this field if default value is not explicitly-declared */
1196 if (!is_required
&& add_default_value
== ADD_DEFAULT_VALUE_ENUM_BOOL
&& !has_default_value
1197 && field_type
!= PROTOBUF_TYPE_ENUM
&& field_type
!= PROTOBUF_TYPE_BOOL
) {
1198 /* ignore this field if default value is not explicitly-declared, or it is not enum or bool */
1202 /* ignore repeated fields, or optional fields of message/group,
1203 * or string/bytes fields without explicitly-declared default value.
1205 if (is_repeated
|| (!is_required
&& (field_type
== PROTOBUF_TYPE_NONE
1206 || field_type
== PROTOBUF_TYPE_MESSAGE
1207 || field_type
== PROTOBUF_TYPE_GROUP
1208 || (field_type
== PROTOBUF_TYPE_BYTES
&& !has_default_value
)
1209 || (field_type
== PROTOBUF_TYPE_STRING
&& !has_default_value
)
1214 /* check if it is parsed */
1215 if (wmem_map_lookup(parsed_fields
, GINT_TO_POINTER(field_number
))) {
1216 continue; /* this field is parsed */
1219 field_name
= pbw_FieldDescriptor_name(field_desc
);
1221 /* this field is not found in message payload */
1223 expert_add_info_format(pinfo
, ti_message
, &ei_protobuf_missing_required_field
, "missing required field '%s'", field_name
);
1227 field_full_name
= pbw_FieldDescriptor_full_name(field_desc
);
1229 /* add common tree item for this field */
1230 field_tree
= proto_tree_add_subtree_format(message_tree
, tvb
, offset
, 0, ett_protobuf_field
, &ti_field
,
1231 "Field(%" PRIu64
"): %s %s", field_number
, field_name
, "=");
1232 proto_item_set_generated(ti_field
);
1234 /* support filtering with the name, type or number of the field */
1235 ti_field_name
= proto_tree_add_string(field_tree
, hf_protobuf_field_name
, tvb
, offset
, 0, field_name
);
1236 proto_item_set_generated(ti_field_name
);
1237 ti_field_type
= proto_tree_add_int(field_tree
, hf_protobuf_field_type
, tvb
, offset
, 0, field_type
);
1238 proto_item_set_generated(ti_field_type
);
1239 ti_field_number
= proto_tree_add_uint64_format(field_tree
, hf_protobuf_field_number
, tvb
, offset
, 0, field_number
<< 3, "Field Number: %" PRIu64
, field_number
);
1240 proto_item_set_generated(ti_field_number
);
1243 if (pbf_as_hf
&& field_full_name
) {
1244 hf_id_ptr
= (int*)g_hash_table_lookup(pbf_hf_hash
, field_full_name
);
1245 DISSECTOR_ASSERT_HINT(hf_id_ptr
&& (*hf_id_ptr
) > 0, "hf must have been initialized properly");
1248 pbf_tree
= field_tree
;
1249 if (pbf_as_hf
&& hf_id_ptr
&& !show_details
) {
1250 /* set ti_field (Field(x)) item hidden if there is header_field */
1251 proto_item_set_hidden(ti_field
);
1252 pbf_tree
= message_tree
;
1255 ti_value
= ti_pbf
= NULL
;
1256 string_value
= NULL
;
1260 json_dumper_set_member_name(dumper
, field_name
);
1265 case PROTOBUF_TYPE_INT32
:
1266 case PROTOBUF_TYPE_SINT32
:
1267 case PROTOBUF_TYPE_SFIXED32
:
1268 int32_value
= pbw_FieldDescriptor_default_value_int32(field_desc
);
1269 ti_value
= proto_tree_add_int(field_tree
, hf_protobuf_value_int32
, tvb
, offset
, 0, int32_value
);
1270 proto_item_append_text(ti_field
, " %d", int32_value
);
1272 ti_pbf
= proto_tree_add_int(pbf_tree
, *hf_id_ptr
, tvb
, offset
, 0, int32_value
);
1275 json_dumper_value_anyf(dumper
, "%d", int32_value
);
1279 case PROTOBUF_TYPE_INT64
:
1280 case PROTOBUF_TYPE_SINT64
:
1281 case PROTOBUF_TYPE_SFIXED64
:
1282 int64_value
= pbw_FieldDescriptor_default_value_int64(field_desc
);
1283 ti_value
= proto_tree_add_int64(field_tree
, hf_protobuf_value_int64
, tvb
, offset
, 0, int64_value
);
1284 proto_item_append_text(ti_field
, " %" PRId64
, int64_value
);
1286 ti_pbf
= proto_tree_add_int64(pbf_tree
, *hf_id_ptr
, tvb
, offset
, 0, int64_value
);
1289 json_dumper_value_anyf(dumper
, "\"%" PRId64
"\"", int64_value
);
1293 case PROTOBUF_TYPE_UINT32
:
1294 case PROTOBUF_TYPE_FIXED32
:
1295 uint32_value
= pbw_FieldDescriptor_default_value_uint32(field_desc
);
1296 ti_value
= proto_tree_add_uint(field_tree
, hf_protobuf_value_uint32
, tvb
, offset
, 0, uint32_value
);
1297 proto_item_append_text(ti_field
, " %u", uint32_value
);
1299 ti_pbf
= proto_tree_add_uint(pbf_tree
, *hf_id_ptr
, tvb
, offset
, 0, uint32_value
);
1302 json_dumper_value_anyf(dumper
, "%u", uint32_value
);
1306 case PROTOBUF_TYPE_UINT64
:
1307 case PROTOBUF_TYPE_FIXED64
:
1308 uint64_value
= pbw_FieldDescriptor_default_value_uint64(field_desc
);
1309 ti_value
= proto_tree_add_uint64(field_tree
, hf_protobuf_value_uint64
, tvb
, offset
, 0, uint64_value
);
1310 proto_item_append_text(ti_field
, " %" PRIu64
, uint64_value
);
1312 ti_pbf
= proto_tree_add_uint64(pbf_tree
, *hf_id_ptr
, tvb
, offset
, 0, uint64_value
);
1315 json_dumper_value_anyf(dumper
, "\"%" PRIu64
"\"", uint64_value
);
1319 case PROTOBUF_TYPE_BOOL
:
1320 bool_value
= pbw_FieldDescriptor_default_value_bool(field_desc
);
1321 ti_value
= proto_tree_add_boolean(field_tree
, hf_protobuf_value_bool
, tvb
, offset
, 0, bool_value
);
1322 proto_item_append_text(ti_field
, " %s", bool_value
? "true" : "false");
1324 ti_pbf
= proto_tree_add_boolean(pbf_tree
, *hf_id_ptr
, tvb
, offset
, 0, bool_value
);
1327 json_dumper_value_anyf(dumper
, bool_value
? "true" : "false");
1331 case PROTOBUF_TYPE_DOUBLE
:
1332 double_value
= pbw_FieldDescriptor_default_value_double(field_desc
);
1333 ti_value
= proto_tree_add_double(field_tree
, hf_protobuf_value_double
, tvb
, offset
, 0, double_value
);
1334 proto_item_append_text(ti_field
, " %lf", double_value
);
1336 ti_pbf
= proto_tree_add_double(pbf_tree
, *hf_id_ptr
, tvb
, offset
, 0, double_value
);
1339 json_dumper_value_double(dumper
, double_value
);
1343 case PROTOBUF_TYPE_FLOAT
:
1344 float_value
= pbw_FieldDescriptor_default_value_float(field_desc
);
1345 ti_value
= proto_tree_add_float(field_tree
, hf_protobuf_value_float
, tvb
, offset
, 0, float_value
);
1346 proto_item_append_text(ti_field
, " %f", float_value
);
1348 ti_pbf
= proto_tree_add_float(pbf_tree
, *hf_id_ptr
, tvb
, offset
, 0, float_value
);
1351 json_dumper_value_anyf(dumper
, "%f", float_value
);
1355 case PROTOBUF_TYPE_BYTES
:
1356 string_value
= pbw_FieldDescriptor_default_value_string(field_desc
, &size
);
1357 DISSECTOR_ASSERT_HINT(has_default_value
&& string_value
, "Bytes field must have default value!");
1359 json_dumper_begin_base64(dumper
);
1360 json_dumper_write_base64(dumper
, (const unsigned char *)string_value
, size
);
1361 json_dumper_end_base64(dumper
);
1363 if (!dissect_bytes_as_string
) {
1364 ti_value
= proto_tree_add_bytes_with_length(field_tree
, hf_protobuf_value_data
, tvb
, offset
, 0, (const uint8_t*) string_value
, size
);
1365 proto_item_append_text(ti_field
, " (%d bytes)", size
);
1366 /* the type of *hf_id_ptr MUST be FT_BYTES now */
1368 ti_pbf
= proto_tree_add_bytes_with_length(pbf_tree
, *hf_id_ptr
, tvb
, offset
, 0, (const uint8_t*)string_value
, size
);
1372 /* or continue dissect BYTES as STRING */
1374 case PROTOBUF_TYPE_STRING
:
1375 if (string_value
== NULL
) {
1376 string_value
= pbw_FieldDescriptor_default_value_string(field_desc
, &size
);
1378 DISSECTOR_ASSERT_HINT(has_default_value
&& string_value
, "String field must have default value!");
1379 ti_value
= proto_tree_add_string(field_tree
, hf_protobuf_value_string
, tvb
, offset
, 0, string_value
);
1380 proto_item_append_text(ti_field
, " %s", string_value
);
1382 ti_pbf
= proto_tree_add_string(pbf_tree
, *hf_id_ptr
, tvb
, offset
, 0, string_value
);
1384 if (dumper
&& field_type
== PROTOBUF_TYPE_STRING
) {
1385 /* JSON view will ignore the dissect_bytes_as_string option */
1386 json_dumper_value_string(dumper
, string_value
);
1390 case PROTOBUF_TYPE_ENUM
:
1391 enum_value_desc
= pbw_FieldDescriptor_default_value_enum(field_desc
);
1392 if (enum_value_desc
) {
1393 int32_value
= pbw_EnumValueDescriptor_number(enum_value_desc
);
1394 enum_value_name
= pbw_EnumValueDescriptor_name(enum_value_desc
);
1395 ti_value
= proto_tree_add_int(field_tree
, hf_protobuf_value_int32
, tvb
, offset
, 0, int32_value
);
1396 if (enum_value_name
) { /* show enum value name */
1397 proto_item_append_text(ti_field
, " %s(%d)", enum_value_name
, int32_value
);
1398 proto_item_append_text(ti_value
, " (%s)", enum_value_name
);
1400 proto_item_append_text(ti_field
, " %d", int32_value
);
1403 ti_pbf
= proto_tree_add_int(pbf_tree
, *hf_id_ptr
, tvb
, offset
, 0, int32_value
);
1406 json_dumper_value_string(dumper
, enum_value_name
);
1410 expert_add_info_format(pinfo
, ti_message
, &ei_protobuf_default_value_error
, "enum value of field '%s' not found in *.proto!", field_name
);
1415 /* should not get here */
1419 proto_item_append_text(ti_field
, " (%s)", val_to_str(field_type
, protobuf_field_type
, "Unknown type (%d)"));
1422 proto_item_set_generated(ti_value
);
1425 proto_item_set_generated(ti_pbf
);
1428 if (!show_details
) {
1429 proto_item_set_hidden(ti_field_name
);
1430 proto_item_set_hidden(ti_field_type
);
1431 proto_item_set_hidden(ti_field_number
);
1432 if (ti_value
&& (field_type
!= PROTOBUF_TYPE_BYTES
|| dissect_bytes_as_string
)) {
1433 proto_item_set_hidden(ti_value
);
1440 // NOLINTNEXTLINE(misc-no-recursion)
1441 dissect_protobuf_message(tvbuff_t
*tvb
, unsigned offset
, unsigned length
, packet_info
*pinfo
, proto_tree
*protobuf_tree
,
1442 const PbwDescriptor
* message_desc
, int hf_msg
, bool is_top_level
, json_dumper
*dumper
, wmem_allocator_t
* scope
, char** retval
)
1444 proto_tree
*message_tree
;
1445 proto_item
*ti_message
, *ti
;
1446 const char* message_name
= "<UNKNOWN>";
1447 unsigned max_offset
= offset
+ length
;
1448 const PbwFieldDescriptor
* field_desc
;
1449 const PbwFieldDescriptor
* prev_field_desc
= NULL
;
1450 wmem_map_t
* parsed_fields
= NULL
; /* store parsed field numbers. */
1451 nstime_t timestamp
= { 0 };
1452 char* value_label
= NULL
; /* The label representing the value of some wellknown message, such as google.protobuf.Timestamp */
1455 message_name
= pbw_Descriptor_full_name(message_desc
);
1457 if (add_default_value
) {
1458 parsed_fields
= wmem_map_new(pinfo
->pool
, g_direct_hash
, g_direct_equal
);
1461 if (strcmp(message_name
, "google.protobuf.Timestamp") == 0) {
1462 /* parse this message as timestamp */
1463 tvb_get_protobuf_time(tvb
, offset
, length
, ×tamp
);
1464 if (!nstime_is_unset(×tamp
)) {
1465 value_label
= abs_time_to_rfc3339(scope
? scope
: pinfo
->pool
, ×tamp
, use_utc_fmt
);
1467 ti
= proto_tree_add_time_format_value(protobuf_tree
, hf_msg
, tvb
, offset
, length
, ×tamp
, "%s", value_label
);
1468 protobuf_tree
= proto_item_add_subtree(ti
, ett_protobuf_message
);
1471 json_dumper_value_string(dumper
, value_label
);
1472 dumper
= NULL
; /* this message will not dump as JSON object */
1475 expert_add_info(pinfo
, proto_tree_get_parent(protobuf_tree
), &ei_protobuf_failed_parse_field
);
1477 } else if (hf_msg
> 0) {
1478 ti
= proto_tree_add_bytes_format_value(protobuf_tree
, hf_msg
, tvb
, offset
, length
, NULL
, "(%u bytes)", length
);
1479 protobuf_tree
= proto_item_add_subtree(ti
, ett_protobuf_message
);
1483 if (pbf_as_hf
&& message_desc
) {
1484 /* support filtering with message name as wireshark field name */
1485 int *hf_id_ptr
= (int*)g_hash_table_lookup(pbf_hf_hash
, message_name
);
1486 DISSECTOR_ASSERT_HINT(hf_id_ptr
&& (*hf_id_ptr
) > 0, "hf of message should initialized properly");
1487 ti_message
= proto_tree_add_item(protobuf_tree
, *hf_id_ptr
, tvb
, offset
, length
, ENC_NA
);
1488 proto_item_set_text(ti_message
, "Message: %s", message_name
);
1491 /* show "Message" item and add its fields under this item */
1492 message_tree
= proto_item_add_subtree(ti_message
, ett_protobuf_message
);
1494 /* hidden "Message" item (but still can be filtered by wireshark field name with "pbm.xxx" prefix),
1495 * and add its fields under the parent (field or protobuf protocol) item directly */
1496 proto_item_set_hidden(ti_message
);
1497 message_tree
= protobuf_tree
;
1498 ti_message
= proto_tree_get_parent(message_tree
);
1499 proto_item_append_text(ti_message
, " (Message: %s)", message_name
);
1502 message_tree
= proto_tree_add_subtree_format(protobuf_tree
, tvb
, offset
, length
, ett_protobuf_message
,
1503 &ti_message
, "Message: %s", message_name
);
1507 if (col_get_text(pinfo
->cinfo
, COL_PROTOCOL
) && strlen(col_get_text(pinfo
->cinfo
, COL_PROTOCOL
))) {
1508 col_append_str(pinfo
->cinfo
, COL_PROTOCOL
, "/");
1511 col_clear(pinfo
->cinfo
, COL_PROTOCOL
);
1512 col_clear(pinfo
->cinfo
, COL_INFO
);
1514 col_append_fstr(pinfo
->cinfo
, COL_PROTOCOL
, "PB(%s)", message_name
);
1517 /* support filtering with message name */
1518 ti
= proto_tree_add_string(message_tree
, hf_protobuf_message_name
, tvb
, offset
, length
, message_name
);
1519 proto_item_set_generated(ti
);
1520 if (!show_details
) {
1521 proto_item_set_hidden(ti
);
1524 /* create object for json */
1525 if (message_desc
&& dumper
) {
1526 json_dumper_begin_object(dumper
);
1529 /* each time we dissect one protobuf field. */
1530 increment_dissection_depth(pinfo
);
1531 while (offset
< max_offset
)
1534 if (!dissect_one_protobuf_field(tvb
, &offset
, max_offset
- offset
, pinfo
, message_tree
, message_desc
,
1535 is_top_level
, &field_desc
, prev_field_desc
, dumper
)) {
1539 if (parsed_fields
&& field_desc
) {
1540 wmem_map_insert(parsed_fields
, GINT_TO_POINTER(pbw_FieldDescriptor_number(field_desc
)), GINT_TO_POINTER(1));
1543 /* Only set this on success - if we didn't dissect a field, we
1544 * may still need to close the JSON array associated with the
1545 * last successfully dissected field. */
1546 prev_field_desc
= field_desc
;
1548 decrement_dissection_depth(pinfo
);
1550 if (dumper
&& prev_field_desc
&& pbw_FieldDescriptor_is_repeated(prev_field_desc
)) {
1551 /* The last field is repeated field, we close the JSON array */
1552 json_dumper_end_array(dumper
);
1555 /* add default values for missing fields */
1556 if (add_default_value
&& parsed_fields
) {
1557 add_missing_fields_with_default_values(tvb
, offset
, pinfo
, message_tree
, message_desc
, parsed_fields
, dumper
);
1560 if (message_desc
&& dumper
) {
1561 json_dumper_end_object(dumper
);
1565 ti
= proto_tree_add_item(message_tree
, hf_text_only
, tvb
, offset
, length
, ENC_NA
);
1566 proto_item_set_text(ti
, "[Message Value: %s]", value_label
);
1570 *retval
= value_label
;
1574 /* try to find message type by UDP port */
1575 static const PbwDescriptor
*
1576 find_message_type_by_udp_port(packet_info
*pinfo
)
1578 range_t
* udp_port_range
;
1579 const char* message_type
;
1581 for (i
= 0; i
< num_protobuf_udp_message_types
; ++i
) {
1582 udp_port_range
= protobuf_udp_message_types
[i
].udp_port_range
;
1583 if (value_is_in_range(udp_port_range
, pinfo
->srcport
)
1584 || value_is_in_range(udp_port_range
, pinfo
->destport
))
1586 message_type
= protobuf_udp_message_types
[i
].message_type
;
1587 if (message_type
&& strlen(message_type
) > 0) {
1588 return pbw_DescriptorPool_FindMessageTypeByName(pbw_pool
, message_type
);
1596 // NOLINTNEXTLINE(misc-no-recursion)
1597 uri_matches_pattern(const char *request_uri
, const char *uri_pattern
, int depth
)
1599 /* Arbitrary recursion depth limit.. */
1605 if (strcmp(request_uri
, uri_pattern
)==0) {
1609 /* Match if both strings now empty */
1610 if (strlen(uri_pattern
)==0 && strlen(request_uri
)==0) {
1614 /* Fail if remaining, unmatched pattern but reached end of uri */
1615 if (strlen(uri_pattern
)>0 && strlen(request_uri
)==0) {
1619 /* If remainder of pattern is just '*', it matches */
1620 if (strlen(uri_pattern
)==1 && uri_pattern
[0] == '*') {
1624 /* If next uri_pattern char is not '*', needs to match exactly */
1625 if (strlen(uri_pattern
) && uri_pattern
[0] != '*') {
1627 /* Skip identical characters */
1629 for (n
=0; strlen(request_uri
+n
) && strlen(request_uri
+n
) && uri_pattern
[n
] != '*'; n
++) {
1630 if (request_uri
[n
] == uri_pattern
[n
]) {
1634 /* Fail if non-wildcarded comparison fails */
1639 /* Recursively call n characters along */
1640 return uri_matches_pattern(request_uri
+n
, uri_pattern
+n
, depth
+1);
1643 if (strlen(uri_pattern
) && uri_pattern
[0] == '*') {
1644 /* We are at a '*'. Test with/without moving past it now */
1645 return (uri_matches_pattern(request_uri
+1, uri_pattern
, depth
+1) ||
1646 uri_matches_pattern(request_uri
+1, uri_pattern
+1, depth
+1));
1654 dissect_protobuf(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
1657 proto_tree
*protobuf_tree
, *protobuf_json_tree
;
1658 unsigned offset
= 0;
1660 const PbwDescriptor
* message_desc
= NULL
;
1661 const char* data_str
= NULL
;
1664 /* initialize only the first time the protobuf dissector is called */
1665 if (!protobuf_dissector_called
) {
1666 protobuf_dissector_called
= true;
1667 protobuf_reinit(PREFS_UPDATE_ALL
);
1670 /* may set col_set_str(pinfo->cinfo, COL_PROTOCOL, "PROTOBUF"); */
1671 col_append_str(pinfo
->cinfo
, COL_INFO
, " (PROTOBUF)");
1673 ti
= proto_tree_add_item(tree
, proto_protobuf
, tvb
, 0, -1, ENC_NA
);
1674 protobuf_tree
= proto_item_add_subtree(ti
, ett_protobuf
);
1676 /* The dissectors written in Lua are not able to specify the message type by data
1677 parameter when calling protobuf dissector. But they can tell Protobuf dissector
1678 the message type by the value of pinfo->private_table["pb_msg_type"]. */
1680 data_str
= (const char*)data
;
1681 } else if (pinfo
->private_table
) {
1682 data_str
= (const char*)g_hash_table_lookup(pinfo
->private_table
, "pb_msg_type");
1686 /* The data_str has two formats:
1687 * (1) Come from GRPC dissector like:
1688 * http2_content_type "," http2_path "," ("request" / "response")
1689 * According to grpc wire format guide, it will be:
1690 * "application/grpc" ["+proto"] "," "/" service-name "/" method-name "," ("request" / "response")
1692 * application/grpc,/helloworld.Greeter/SayHello,request
1693 * In this format, we will try to get real protobuf message type by method (service-name.method-name)
1694 * and in/out type (request / response).
1695 * (2) Come from other dissector which specifies message type directly, like:
1696 * "message," message_type_name
1698 * message,helloworld.HelloRequest (helloworld is package, HelloRequest is message type)
1700 const char* message_info
= strchr(data_str
, ',');
1703 message_info
++; /* ignore ',' */
1704 proto_item_append_text(ti
, ": %s", message_info
); /* append to proto item */
1706 if (g_str_has_prefix(data_str
, "message,")) {
1707 /* find message type by name directly */
1708 message_desc
= pbw_DescriptorPool_FindMessageTypeByName(pbw_pool
, message_info
);
1709 } else /* if (g_str_has_prefix(data_str, "application/grpc,") */ {
1710 /* get long method-name like: helloworld.Greeter.SayHello */
1711 if (message_info
[0] == '/') {
1712 message_info
++; /* ignore first '/' */
1715 char** tmp_names
= wmem_strsplit(pinfo
->pool
, message_info
, ",", 2);
1716 char* method_name
= (tmp_names
[0]) ? tmp_names
[0] : NULL
;
1717 char* direction_type
= (method_name
&& tmp_names
[1]) ? tmp_names
[1] : NULL
;
1719 /* replace all '/' to '.', so helloworld.Greeter/SayHello converted to helloworld.Greeter.SayHello */
1721 for (i
= 0; method_name
[i
] != 0; i
++) {
1722 if (method_name
[i
] == '/') {
1723 method_name
[i
] = '.';
1728 /* find message type according to method descriptor */
1729 if (direction_type
) {
1730 const PbwMethodDescriptor
* method_desc
= pbw_DescriptorPool_FindMethodByName(pbw_pool
, method_name
);
1732 message_desc
= strcmp(direction_type
, "request") == 0
1733 ? pbw_MethodDescriptor_input_type(method_desc
)
1734 : pbw_MethodDescriptor_output_type(method_desc
);
1740 const char* message_full_name
= pbw_Descriptor_full_name(message_desc
);
1741 if (message_full_name
) {
1742 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " %s", message_full_name
);
1747 } else if (pinfo
->ptype
== PT_UDP
) {
1748 message_desc
= find_message_type_by_udp_port(pinfo
);
1751 if (!message_desc
) {
1752 /* If this was inside an HTTP request, do we have a message type assigned to this URI? */
1753 http_req_res_t
*curr
= (http_req_res_t
*)p_get_proto_data(wmem_file_scope(), pinfo
,
1754 proto_http
, HTTP_PROTO_DATA_REQRES
);
1756 if (curr
->request_uri
) {
1757 for (unsigned n
=0; n
< num_protobuf_uri_message_types
; n
++) {
1758 if (uri_matches_pattern(curr
->request_uri
, protobuf_uri_message_types
[n
].uri
, 1 /* depth */)) {
1759 if (strlen(protobuf_uri_message_types
[n
].message_type
)) {
1760 /* Lookup message type for matching URI */
1761 message_desc
= pbw_DescriptorPool_FindMessageTypeByName(pbw_pool
,
1762 protobuf_uri_message_types
[n
].message_type
);
1764 /* Found a matched URI, so stop looking */
1772 /* If *still* have no schema and a default is configured, try to use that */
1773 if (!message_desc
&& strlen(default_message_type
)) {
1774 message_desc
= pbw_DescriptorPool_FindMessageTypeByName(pbw_pool
,
1775 default_message_type
);
1778 if (display_json_mapping
&& message_desc
) {
1779 json_dumper dumper
= {
1780 .output_string
= g_string_new(NULL
),
1781 .flags
= JSON_DUMPER_FLAGS_PRETTY_PRINT
| JSON_DUMPER_FLAGS_NO_DEBUG
,
1784 /* Dissecting can throw an exception, ideally CLEANUP_PUSH and _POP
1785 * should be used to free the GString to avoid a leak.
1787 dissect_protobuf_message(tvb
, offset
, tvb_reported_length_remaining(tvb
, offset
), pinfo
,
1788 protobuf_tree
, message_desc
,
1790 pinfo
->ptype
== PT_UDP
, // is_top_level
1795 DISSECTOR_ASSERT_HINT(json_dumper_finish(&dumper
), "Bad json_dumper state");
1796 ti
= proto_tree_add_item(tree
, proto_protobuf_json_mapping
, tvb
, 0, -1, ENC_NA
);
1797 protobuf_json_tree
= proto_item_add_subtree(ti
, ett_protobuf_json
);
1799 json_str
= g_string_free(dumper
.output_string
, false);
1800 if (json_str
!= NULL
) {
1802 /* add each line of json to the protobuf_json_tree */
1804 char *q
= strchr(p
, '\n');
1806 *(q
++) = '\0'; /* replace the '\n' to '\0' */
1807 } /* else (q == NULL) means this is the last line of the JSON */
1808 proto_tree_add_string_format(protobuf_json_tree
, hf_json_mapping_line
, tvb
, 0, -1, p
, "%s", p
);
1815 dissect_protobuf_message(tvb
, offset
, tvb_reported_length_remaining(tvb
, offset
), pinfo
,
1816 protobuf_tree
, message_desc
,
1818 true, // is_top_level
1824 return tvb_captured_length(tvb
);
1828 // NOLINTNEXTLINE(misc-no-recursion)
1829 load_all_files_in_dir(PbwDescriptorPool
* pool
, const char* dir_path
, unsigned depth
)
1831 WS_DIR
*dir
; /* scanned directory */
1832 WS_DIRENT
*file
; /* current file */
1834 const char *name
; /* current file or dir name (without parent dir path) */
1835 char *path
; /* sub file or dir path of dir_path */
1837 if (depth
> prefs
.gui_max_tree_depth
) {
1841 if (g_file_test(dir_path
, G_FILE_TEST_IS_DIR
)) {
1842 if ((dir
= ws_dir_open(dir_path
, 0, NULL
)) != NULL
) {
1843 while ((file
= ws_dir_read_name(dir
)) != NULL
) {
1844 /* load all files with '.proto' suffix */
1845 name
= ws_dir_get_name(file
);
1846 path
= g_build_filename(dir_path
, name
, NULL
);
1847 dot
= strrchr(name
, '.');
1848 if (dot
&& g_ascii_strcasecmp(dot
+ 1, "proto") == 0) {
1849 /* Note: pbw_load_proto_file support absolute or relative (to one of search paths) path */
1850 if (pbw_load_proto_file(pool
, path
) != 0) {
1856 if (!load_all_files_in_dir(pool
, path
, depth
+ 1)) {
1870 /* There might be a lot of errors to be found during parsing .proto files.
1871 We buffer the errors first, and print them in one list finally. */
1872 static wmem_strbuf_t
* err_msg_buf
;
1873 #define MIN_ERR_STR_BUF_SIZE 512
1874 #define MAX_ERR_STR_BUF_SIZE 1024
1877 buffer_error(const char *fmt
, ...)
1882 if (err_msg_buf
== NULL
)
1883 err_msg_buf
= wmem_strbuf_new_sized(wmem_epan_scope(), MIN_ERR_STR_BUF_SIZE
);
1885 wmem_strbuf_append_vprintf(err_msg_buf
, fmt
, ap
);
1891 flush_and_report_error(void)
1895 str
= wmem_strbuf_finalize(err_msg_buf
);
1897 report_failure("Protobuf: Error(s):\n%s", str
);
1898 wmem_free(wmem_epan_scope(), str
);
1903 update_protobuf_search_paths(void)
1905 protobuf_reinit(PREFS_UPDATE_PROTOBUF_SEARCH_PATHS
);
1909 update_protobuf_udp_message_types(void)
1911 protobuf_reinit(PREFS_UPDATE_PROTOBUF_UDP_MESSAGE_TYPES
);
1915 update_protobuf_uri_message_types(void)
1917 protobuf_reinit(PREFS_UPDATE_PROTOBUF_URI_MESSAGE_TYPES
);
1922 deregister_header_fields(void)
1925 /* Deregister all fields */
1926 for (unsigned i
= 0; i
< dynamic_hf_size
; i
++) {
1927 proto_deregister_field(proto_protobuf
, *(dynamic_hf
[i
].p_id
));
1928 g_free(dynamic_hf
[i
].p_id
);
1929 /* dynamic_hf[i].name and .abbrev will be freed by proto_add_deregistered_data */
1932 proto_add_deregistered_data(dynamic_hf
);
1934 dynamic_hf_size
= 0;
1938 g_hash_table_destroy(pbf_hf_hash
);
1943 /* convert the names of the enum's values to value_string array */
1944 static value_string
*
1945 enum_to_value_string(const PbwEnumDescriptor
* enum_desc
)
1949 if (enum_desc
== NULL
|| (value_count
= pbw_EnumDescriptor_value_count(enum_desc
)) == 0) {
1953 vals
= g_new0(value_string
, value_count
+ 1);
1954 for (i
= 0; i
< value_count
; i
++) {
1955 const PbwEnumValueDescriptor
* enum_value_desc
= pbw_EnumDescriptor_value(enum_desc
, i
);
1956 vals
[i
].value
= pbw_EnumValueDescriptor_number(enum_value_desc
);
1957 vals
[i
].strptr
= g_strdup(pbw_EnumValueDescriptor_name(enum_value_desc
));
1959 /* the strptr of last element of vals must be NULL */
1963 /* create wireshark header fields according to each message's fields
1964 * and add them into pbf_as_hf hash table */
1966 collect_fields(const PbwDescriptor
* message
, void* userdata
)
1968 wmem_list_t
* hf_list
= (wmem_list_t
*) userdata
;
1969 hf_register_info
* hf
;
1970 const PbwFieldDescriptor
* field_desc
;
1971 const PbwEnumDescriptor
* enum_desc
;
1972 const PbwDescriptor
* sub_msg_desc
;
1973 int i
, field_type
, total_num
= pbw_Descriptor_field_count(message
);
1975 /* add message as field */
1976 hf
= g_new0(hf_register_info
, 1);
1977 hf
->p_id
= g_new(int, 1);
1979 hf
->hfinfo
.name
= g_strdup(pbw_Descriptor_name(message
));
1980 hf
->hfinfo
.abbrev
= ws_strdup_printf("pbm.%s", pbw_Descriptor_full_name(message
));
1981 hf
->hfinfo
.type
= FT_BYTES
;
1982 hf
->hfinfo
.display
= BASE_NONE
;
1983 wmem_list_append(hf_list
, hf
);
1984 g_hash_table_insert(pbf_hf_hash
, g_strdup(pbw_Descriptor_full_name(message
)), hf
->p_id
);
1986 /* add fields of this message as fields */
1987 for (i
= 0; i
< total_num
; i
++) {
1988 field_desc
= pbw_Descriptor_field(message
, i
);
1989 field_type
= pbw_FieldDescriptor_type(field_desc
);
1990 if (field_type
<= PROTOBUF_TYPE_NONE
||field_type
> PROTOBUF_MAX_FIELD_TYPE
) {
1991 /* not a valid field type */
1994 hf
= g_new0(hf_register_info
, 1);
1995 hf
->p_id
= g_new(int, 1);
1998 hf
->hfinfo
.name
= g_strdup(pbw_FieldDescriptor_name(field_desc
));
1999 hf
->hfinfo
.abbrev
= ws_strdup_printf("pbf.%s", pbw_FieldDescriptor_full_name(field_desc
));
2000 switch (field_type
) {
2001 case PROTOBUF_TYPE_DOUBLE
:
2002 hf
->hfinfo
.type
= FT_DOUBLE
;
2003 hf
->hfinfo
.display
= BASE_NONE
;
2006 case PROTOBUF_TYPE_FLOAT
:
2007 hf
->hfinfo
.type
= FT_FLOAT
;
2008 hf
->hfinfo
.display
= BASE_NONE
;
2011 case PROTOBUF_TYPE_INT64
:
2012 case PROTOBUF_TYPE_SFIXED64
:
2013 case PROTOBUF_TYPE_SINT64
:
2014 hf
->hfinfo
.type
= FT_INT64
;
2015 hf
->hfinfo
.display
= BASE_DEC
;
2018 case PROTOBUF_TYPE_UINT64
:
2019 case PROTOBUF_TYPE_FIXED64
:
2020 hf
->hfinfo
.type
= FT_UINT64
;
2021 hf
->hfinfo
.display
= BASE_DEC
;
2024 case PROTOBUF_TYPE_INT32
:
2025 case PROTOBUF_TYPE_SFIXED32
:
2026 case PROTOBUF_TYPE_SINT32
:
2027 hf
->hfinfo
.type
= FT_INT32
;
2028 hf
->hfinfo
.display
= BASE_DEC
;
2031 case PROTOBUF_TYPE_UINT32
:
2032 case PROTOBUF_TYPE_FIXED32
:
2033 hf
->hfinfo
.type
= FT_UINT32
;
2034 hf
->hfinfo
.display
= BASE_DEC
;
2037 case PROTOBUF_TYPE_ENUM
:
2038 hf
->hfinfo
.type
= FT_INT32
;
2039 hf
->hfinfo
.display
= BASE_DEC
;
2040 enum_desc
= pbw_FieldDescriptor_enum_type(field_desc
);
2042 hf
->hfinfo
.strings
= enum_to_value_string(enum_desc
);
2046 case PROTOBUF_TYPE_BOOL
:
2047 hf
->hfinfo
.type
= FT_BOOLEAN
;
2048 hf
->hfinfo
.display
= BASE_NONE
;
2051 case PROTOBUF_TYPE_BYTES
:
2052 hf
->hfinfo
.type
= dissect_bytes_as_string
? FT_STRING
: FT_BYTES
;
2053 hf
->hfinfo
.display
= BASE_NONE
;
2056 case PROTOBUF_TYPE_STRING
:
2057 hf
->hfinfo
.type
= FT_STRING
;
2058 hf
->hfinfo
.display
= BASE_NONE
;
2061 case PROTOBUF_TYPE_GROUP
:
2062 case PROTOBUF_TYPE_MESSAGE
:
2063 sub_msg_desc
= pbw_FieldDescriptor_message_type(field_desc
);
2064 if (sub_msg_desc
&& strcmp(pbw_Descriptor_full_name(sub_msg_desc
), "google.protobuf.Timestamp") == 0) {
2065 hf
->hfinfo
.type
= FT_ABSOLUTE_TIME
;
2066 hf
->hfinfo
.display
= use_utc_fmt
? ABSOLUTE_TIME_NTP_UTC
: ABSOLUTE_TIME_LOCAL
;
2068 hf
->hfinfo
.type
= FT_BYTES
;
2069 hf
->hfinfo
.display
= BASE_NONE
;
2074 /* should not happen */
2078 wmem_list_append(hf_list
, hf
);
2079 g_hash_table_insert(pbf_hf_hash
, g_strdup(pbw_FieldDescriptor_full_name(field_desc
)), hf
->p_id
);
2084 update_header_fields(bool force_reload
)
2086 if (!force_reload
&& pbf_as_hf
&& dynamic_hf
) {
2087 /* If initialized, do nothing. */
2090 deregister_header_fields();
2094 wmem_list_frame_t
*it
;
2095 wmem_list_t
* hf_list
= wmem_list_new(NULL
);
2096 pbf_hf_hash
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
2097 DISSECTOR_ASSERT(pbw_pool
);
2098 pbw_foreach_message(pbw_pool
, collect_fields
, hf_list
);
2099 dynamic_hf_size
= wmem_list_count(hf_list
);
2100 if (dynamic_hf_size
== 0) {
2101 deregister_header_fields();
2104 dynamic_hf
= g_new0(hf_register_info
, dynamic_hf_size
);
2106 for (it
= wmem_list_head(hf_list
), i
= 0; it
; it
= wmem_list_frame_next(it
), i
++) {
2107 hf_register_info
* hf
= (hf_register_info
*) wmem_list_frame_data(it
);
2108 /* copy hf_register_info structure */
2109 dynamic_hf
[i
] = *hf
;
2111 HFILL_INIT(dynamic_hf
[i
]);
2114 wmem_destroy_list(hf_list
);
2115 proto_register_field_array(proto_protobuf
, dynamic_hf
, dynamic_hf_size
);
2120 protobuf_reinit(int target
)
2123 char **source_paths
;
2125 range_t
* udp_port_range
;
2126 const char* message_type
;
2127 bool loading_completed
= true;
2128 size_t num_proto_paths
;
2130 if (target
& PREFS_UPDATE_PROTOBUF_UDP_MESSAGE_TYPES
) {
2131 /* delete protobuf dissector from old udp ports */
2132 for (it
= old_udp_port_ranges
; it
; it
= it
->next
) {
2133 udp_port_range
= (range_t
*) it
->data
;
2134 dissector_delete_uint_range("udp.port", udp_port_range
, protobuf_handle
);
2135 wmem_free(NULL
, udp_port_range
);
2138 if (old_udp_port_ranges
) {
2139 g_slist_free(old_udp_port_ranges
);
2140 old_udp_port_ranges
= NULL
;
2143 /* add protobuf dissector to new udp ports */
2144 for (i
= 0; i
< num_protobuf_udp_message_types
; ++i
) {
2145 udp_port_range
= protobuf_udp_message_types
[i
].udp_port_range
;
2146 if (udp_port_range
) {
2147 udp_port_range
= range_copy(NULL
, udp_port_range
);
2148 old_udp_port_ranges
= g_slist_append(old_udp_port_ranges
, udp_port_range
);
2149 dissector_add_uint_range("udp.port", udp_port_range
, protobuf_handle
);
2154 /* loading .proto files and checking message types of UDP port will be done only after dissector is called */
2155 if (!protobuf_dissector_called
) {
2159 if (target
& PREFS_UPDATE_PROTOBUF_SEARCH_PATHS
) {
2160 /* convert protobuf_search_path_t array to char* array. should release by g_free().
2161 Add the global and profile protobuf dirs to the search list, add 1 for the terminating null entry */
2162 num_proto_paths
= (size_t)num_protobuf_search_paths
+ 2;
2163 source_paths
= g_new0(char *, num_proto_paths
+ 1);
2165 /* Load the files in the global and personal config dirs */
2166 source_paths
[0] = get_datafile_path("protobuf");
2167 source_paths
[1] = get_persconffile_path("protobuf", true);
2169 for (i
= 0; i
< num_protobuf_search_paths
; ++i
) {
2170 source_paths
[i
+ 2] = protobuf_search_paths
[i
].path
;
2173 /* init DescriptorPool of protobuf */
2174 pbw_reinit_DescriptorPool(&pbw_pool
, (const char **)source_paths
, buffer_error
);
2176 /* load all .proto files in the marked search paths, we can invoke FindMethodByName etc later. */
2177 for (i
= 0; i
< num_proto_paths
; ++i
) {
2178 if ((i
< 2) || protobuf_search_paths
[i
- 2].load_all
) {
2179 if (!load_all_files_in_dir(pbw_pool
, source_paths
[i
], 0)) {
2180 buffer_error("Protobuf: Loading .proto files action stopped!\n");
2181 loading_completed
= false;
2182 break; /* stop loading when error occurs */
2187 g_free(source_paths
[0]);
2188 g_free(source_paths
[1]);
2189 g_free(source_paths
);
2190 update_header_fields(true);
2193 /* check if the message types of UDP port exist */
2194 for (i
= 0; i
< num_protobuf_udp_message_types
; ++i
) {
2195 message_type
= protobuf_udp_message_types
[i
].message_type
;
2196 if (loading_completed
&& message_type
&& strlen(message_type
) > 0
2197 && pbw_DescriptorPool_FindMessageTypeByName(pbw_pool
, message_type
) == NULL
) {
2198 buffer_error("Protobuf: the message type \"%s\" of UDP Message Type preferences does not exist!\n", message_type
);
2202 /* report error if encountered */
2203 flush_and_report_error();
2207 proto_register_protobuf(void)
2209 static hf_register_info hf
[] = {
2210 { &hf_protobuf_message_name
,
2211 { "Message Name", "protobuf.message.name",
2212 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2213 "The name of the protobuf message", HFILL
}
2215 { &hf_protobuf_field_name
,
2216 { "Field Name", "protobuf.field.name",
2217 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2218 "The name of the field", HFILL
}
2220 { &hf_protobuf_field_type
,
2221 { "Field Type", "protobuf.field.type",
2222 FT_INT32
, BASE_DEC
, VALS(protobuf_field_type
), 0x0,
2223 "The type of the field", HFILL
}
2225 { &hf_protobuf_field_number
,
2226 { "Field Number", "protobuf.field.number",
2227 FT_UINT64
, BASE_DEC
, NULL
, UINT64_C(0xFFFFFFFFFFFFFFF8),
2228 "Field number encoded in varint", HFILL
}
2230 { &hf_protobuf_wire_type
,
2231 { "Wire Type", "protobuf.field.wiretype",
2232 FT_UINT8
, BASE_DEC
, VALS(protobuf_wire_type
), 0x07,
2233 "The Wire Type of the field.", HFILL
}
2235 { &hf_protobuf_value_length
,
2236 { "Value Length", "protobuf.field.value.length",
2237 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2238 "The length of length-delimited field value.", HFILL
}
2240 { &hf_protobuf_value_data
,
2241 { "Value", "protobuf.field.value",
2242 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
2243 "The wire type determines value format", HFILL
}
2245 { &hf_protobuf_value_double
,
2246 { "Double", "protobuf.field.value.double",
2247 FT_DOUBLE
, BASE_NONE
, NULL
, 0x0,
2248 "Dissect value as double", HFILL
}
2250 { &hf_protobuf_value_float
,
2251 { "Float", "protobuf.field.value.float",
2252 FT_FLOAT
, BASE_NONE
, NULL
, 0x0,
2253 "Dissect value as float", HFILL
}
2255 { &hf_protobuf_value_int64
,
2256 { "Int64", "protobuf.field.value.int64",
2257 FT_INT64
, BASE_DEC
, NULL
, 0x0,
2258 "Dissect value as int64", HFILL
}
2260 { &hf_protobuf_value_uint64
,
2261 { "Uint64", "protobuf.field.value.uint64",
2262 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2263 "Dissect value as uint64", HFILL
}
2265 { &hf_protobuf_value_int32
,
2266 { "Int32", "protobuf.field.value.int32",
2267 FT_INT32
, BASE_DEC
, NULL
, 0x0,
2268 "Dissect value as int32", HFILL
}
2270 { &hf_protobuf_value_uint32
,
2271 { "Uint32", "protobuf.field.value.uint32",
2272 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
2273 "Dissect value as uint32", HFILL
}
2275 { &hf_protobuf_value_bool
,
2276 { "Bool", "protobuf.field.value.bool",
2277 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
2278 "Dissect value as bool", HFILL
}
2280 { &hf_protobuf_value_string
,
2281 { "String", "protobuf.field.value.string",
2282 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2283 "Dissect value as string", HFILL
}
2285 { &hf_protobuf_value_repeated
,
2286 { "Repeated", "protobuf.field.value.repeated",
2287 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
2288 "Dissect value as repeated", HFILL
}
2292 static hf_register_info json_hf
[] = {
2293 { &hf_json_mapping_line
,
2294 { "JSON Mapping Line", "protobuf_json.line",
2295 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2296 "One line of the protobuf json mapping", HFILL
}
2300 static int *ett
[] = {
2302 &ett_protobuf_message
,
2303 &ett_protobuf_field
,
2304 &ett_protobuf_value
,
2305 &ett_protobuf_packed_repeated
2308 static int *ett_json
[] = {
2312 /* Setup protocol expert items */
2313 static ei_register_info ei
[] = {
2314 { &ei_protobuf_failed_parse_tag
,
2315 { "protobuf.failed_parse_tag", PI_MALFORMED
, PI_ERROR
,
2316 "Failed to parse tag field", EXPFILL
}
2318 { &ei_protobuf_wire_type_invalid
,
2319 { "protobuf.field.wiretype.invalid", PI_PROTOCOL
, PI_WARN
,
2320 "Unknown or unsupported wiretype", EXPFILL
}
2322 { &ei_protobuf_failed_parse_length_delimited_field
,
2323 { "protobuf.field.failed_parse_length_delimited_field", PI_MALFORMED
, PI_ERROR
,
2324 "Failed to parse length delimited field", EXPFILL
}
2326 { &ei_protobuf_failed_parse_field
,
2327 { "protobuf.field.failed_parse_field", PI_MALFORMED
, PI_ERROR
,
2328 "Failed to parse value field", EXPFILL
}
2330 { &ei_protobuf_message_type_not_found
,
2331 { "protobuf.field.message_type_not_found", PI_PROTOCOL
, PI_WARN
,
2332 "Failed to find message type of a field", EXPFILL
}
2334 { &ei_protobuf_wire_type_not_support_packed_repeated
,
2335 { "protobuf.field.wire_type_not_support_packed_repeated", PI_MALFORMED
, PI_ERROR
,
2336 "The wire type does not support protobuf packed repeated field", EXPFILL
}
2338 { &ei_protobuf_failed_parse_packed_repeated_field
,
2339 { "protobuf.field.failed_parse_packed_repeated_field", PI_MALFORMED
, PI_ERROR
,
2340 "Failed to parse packed repeated field", EXPFILL
}
2342 { &ei_protobuf_missing_required_field
,
2343 { "protobuf.message.missing_required_field", PI_PROTOCOL
, PI_WARN
,
2344 "The required field is not found in message payload", EXPFILL
}
2346 { &ei_protobuf_default_value_error
,
2347 { "protobuf.message.default_value_error", PI_PROTOCOL
, PI_WARN
,
2348 "Parsing default value of a field error", EXPFILL
}
2352 ENUM_VAL_T_ARRAY_STATIC(add_default_value_policy_vals
);
2354 module_t
*protobuf_module
;
2355 expert_module_t
*expert_protobuf
;
2357 static uat_field_t protobuf_search_paths_table_columns
[] = {
2358 UAT_FLD_DIRECTORYNAME(protobuf_search_paths
, path
, "Protobuf source directory", "Directory of the root of protobuf source files"),
2359 UAT_FLD_BOOL(protobuf_search_paths
, load_all
, "Load all files", "Load all .proto files from this directory and its subdirectories"),
2362 uat_t
* protobuf_search_paths_uat
;
2364 static uat_field_t protobuf_udp_message_types_table_columns
[] = {
2365 UAT_FLD_RANGE(protobuf_udp_message_types
, udp_port_range
, "UDP Ports", 0xFFFF, "UDP ports on which data will be dissected as protobuf"),
2366 UAT_FLD_CSTRING(protobuf_udp_message_types
, message_type
, "Message Type", "Protobuf message type of data on these udp ports"),
2369 uat_t
* protobuf_udp_message_types_uat
;
2371 static uat_field_t protobuf_uri_message_types_table_columns
[] = {
2372 UAT_FLD_CSTRING(protobuf_uri_message_type
, uri
, "HTTP URI", "URI for HTTP request carrying protobuf contents"),
2373 UAT_FLD_CSTRING(protobuf_uri_message_type
, message_type
, "Message Type", "Protobuf message type of data on these URIs"),
2376 uat_t
* protobuf_uri_message_types_uat
;
2379 proto_protobuf
= proto_register_protocol("Protocol Buffers", "ProtoBuf", "protobuf");
2380 proto_protobuf_json_mapping
= proto_register_protocol("Protocol Buffers (as JSON Mapping View)", "ProtoBuf_JSON", "protobuf_json");
2382 proto_register_field_array(proto_protobuf
, hf
, array_length(hf
));
2383 proto_register_subtree_array(ett
, array_length(ett
));
2385 proto_register_field_array(proto_protobuf_json_mapping
, json_hf
, array_length(json_hf
));
2386 proto_register_subtree_array(ett_json
, array_length(ett_json
));
2388 protobuf_module
= prefs_register_protocol(proto_protobuf
, proto_reg_handoff_protobuf
);
2390 prefs_register_bool_preference(protobuf_module
, "preload_protos",
2391 "Load .proto files on startup.",
2392 "Load .proto files when Wireshark starts. By default, the .proto files are loaded only"
2393 " when the Protobuf dissector is called for the first time.",
2396 protobuf_search_paths_uat
= uat_new("Protobuf Search Paths",
2397 sizeof(protobuf_search_path_t
),
2398 "protobuf_search_paths",
2400 &protobuf_search_paths
,
2401 &num_protobuf_search_paths
,
2402 UAT_AFFECTS_DISSECTION
| UAT_AFFECTS_FIELDS
,
2403 "ChProtobufSearchPaths",
2404 protobuf_search_paths_copy_cb
,
2406 protobuf_search_paths_free_cb
,
2407 update_protobuf_search_paths
,
2409 protobuf_search_paths_table_columns
2412 prefs_register_uat_preference(protobuf_module
, "search_paths", "Protobuf search paths",
2413 "Specify the directories where .proto files are recursively loaded from, or in which to search for imports.",
2414 protobuf_search_paths_uat
);
2416 prefs_register_bool_preference(protobuf_module
, "pbf_as_hf",
2417 "Dissect Protobuf fields as Wireshark fields.",
2418 "If Protobuf messages and fields are defined in loaded .proto files,"
2419 " they will be dissected as wireshark fields if this option is turned on."
2420 " The names of all these wireshark fields will be prefixed with \"pbf.\" (for fields)"
2421 " or \"pbm.\" (for messages) followed by their full names in the .proto files.",
2424 prefs_set_preference_effect_fields(protobuf_module
, "pbf_as_hf");
2426 prefs_register_bool_preference(protobuf_module
, "show_details",
2427 "Show details of message, fields and enums.",
2428 "Show the names of message, field, enum and enum_value."
2429 " Show the wire type and field number format of field."
2430 " Show value nodes of field and enum_value.",
2433 prefs_register_bool_preference(protobuf_module
, "bytes_as_string",
2434 "Show all fields of bytes type as string.",
2435 "Show all fields of bytes type as string. For example ETCD string",
2436 &dissect_bytes_as_string
);
2438 prefs_register_enum_preference(protobuf_module
, "add_default_value",
2439 "Add missing fields with default values.",
2440 "Make Protobuf fields that are not serialized on the wire to be displayed with default values.\n"
2441 "The default value will be one of the following: \n"
2442 " 1) The value of the 'default' option of an optional field defined in 'proto2' file. (explicitly-declared)\n"
2443 " 2) False for bools.\n"
2444 " 3) First defined enum value for enums.\n"
2445 " 4) Zero for numeric types.\n"
2446 "There are no default values for fields 'repeated' or 'bytes' and 'string' without default value declared.\n"
2447 "If the missing field is 'required' in a 'proto2' file, a warning item will be added to the tree.",
2448 &add_default_value
, add_default_value_policy_vals
, false);
2450 protobuf_udp_message_types_uat
= uat_new("Protobuf UDP Message Types",
2451 sizeof(protobuf_udp_message_type_t
),
2452 "protobuf_udp_message_types",
2454 &protobuf_udp_message_types
,
2455 &num_protobuf_udp_message_types
,
2456 UAT_AFFECTS_DISSECTION
| UAT_AFFECTS_FIELDS
,
2457 "ChProtobufUDPMessageTypes",
2458 protobuf_udp_message_types_copy_cb
,
2459 protobuf_udp_message_types_update_cb
,
2460 protobuf_udp_message_types_free_cb
,
2461 update_protobuf_udp_message_types
,
2463 protobuf_udp_message_types_table_columns
2466 prefs_register_uat_preference(protobuf_module
, "udp_message_types", "Protobuf UDP message types",
2467 "Specify the Protobuf message type of data on certain UDP ports.",
2468 protobuf_udp_message_types_uat
);
2471 protobuf_uri_message_types_uat
= uat_new("Protobuf URI Message Types",
2472 sizeof(protobuf_uri_mapping_t
),
2473 "protobuf_uri_message_types",
2475 &protobuf_uri_message_types
,
2476 &num_protobuf_uri_message_types
,
2477 UAT_AFFECTS_DISSECTION
| UAT_AFFECTS_FIELDS
,
2478 NULL
, //"ChProtobufURIMessageTypes",
2479 protobuf_uri_message_type_copy_cb
,
2481 protobuf_uri_message_type_free_cb
,
2482 update_protobuf_uri_message_types
,
2484 protobuf_uri_message_types_table_columns
2487 prefs_register_uat_preference(protobuf_module
, "uri_message_types", "Protobuf URI message types",
2488 "Specify the Protobuf message type of data on certain URIs. N.B., URI may contain '*'",
2489 protobuf_uri_message_types_uat
);
2492 prefs_register_bool_preference(protobuf_module
, "display_json_mapping",
2493 "Display JSON mapping for Protobuf message",
2494 "Specifies that the JSON text of the "
2495 "Protobuf message should be displayed "
2496 "in addition to the dissection tree",
2497 &display_json_mapping
);
2499 prefs_register_bool_preference(protobuf_module
, "use_utc",
2500 "Display time in UTC",
2501 "Display timestamp in UTC format",
2504 /* Following preferences are for undefined fields, that happened while message type is not specified
2505 when calling dissect_protobuf(), or message type or field information is not found in search paths
2507 prefs_register_bool_preference(protobuf_module
, "try_dissect_as_string",
2508 "Try to dissect all undefined length-delimited fields as string.",
2509 "Try to dissect all undefined length-delimited fields as string.",
2510 &try_dissect_as_string
);
2512 prefs_register_bool_preference(protobuf_module
, "show_all_types",
2513 "Try to show all possible field types for each undefined field.",
2514 "Try to show all possible field types for each undefined field according to wire type.",
2515 &show_all_possible_field_types
);
2517 prefs_register_string_preference(protobuf_module
, "default_type",
2518 "Message type to use if none set",
2519 "Can be useful e.g. if dissector called through media type",
2520 &default_message_type
);
2522 prefs_register_static_text_preference(protobuf_module
, "field_dissector_table_note",
2523 "Subdissector can register itself in \"protobuf_field\" dissector table for parsing"
2524 " the value of the field.",
2525 "The key of \"protobuf_field\" table is the full name of field.");
2527 protobuf_field_subdissector_table
=
2528 register_dissector_table("protobuf_field", "Protobuf field subdissector table",
2529 proto_protobuf
, FT_STRING
, STRING_CASE_SENSITIVE
);
2531 expert_protobuf
= expert_register_protocol(proto_protobuf
);
2532 expert_register_field_array(expert_protobuf
, ei
, array_length(ei
));
2534 protobuf_handle
= register_dissector("protobuf", dissect_protobuf
, proto_protobuf
);
2538 proto_reg_handoff_protobuf(void)
2540 if (protobuf_dissector_called
) {
2541 update_header_fields( /* if bytes_as_string preferences changed, we force reload header fields */
2542 (old_dissect_bytes_as_string
&& !dissect_bytes_as_string
) || (!old_dissect_bytes_as_string
&& dissect_bytes_as_string
)
2544 } else if (preload_protos
) {
2545 protobuf_dissector_called
= true;
2546 protobuf_reinit(PREFS_UPDATE_ALL
);
2548 old_dissect_bytes_as_string
= dissect_bytes_as_string
;
2549 dissector_add_string("grpc_message_type", "application/grpc", protobuf_handle
);
2550 dissector_add_string("grpc_message_type", "application/grpc+proto", protobuf_handle
);
2551 dissector_add_string("grpc_message_type", "application/grpc-web", protobuf_handle
);
2552 dissector_add_string("grpc_message_type", "application/grpc-web+proto", protobuf_handle
);
2553 dissector_add_string("grpc_message_type", "application/grpc-web-text", protobuf_handle
);
2554 dissector_add_string("grpc_message_type", "application/grpc-web-text+proto", protobuf_handle
);
2556 dissector_add_string("media_type", "application/x-protobuf", protobuf_handle
);
2558 proto_http
= proto_get_id_by_filter_name("http");
2562 * Editor modelines - https://www.wireshark.org/tools/modelines.html
2567 * indent-tabs-mode: nil
2570 * vi: set shiftwidth=4 tabstop=8 expandtab:
2571 * :indentSize=4:tabSize=8:noTabs=true: