diff options
Diffstat (limited to '')
-rw-r--r-- | epan/dissectors/packet-protobuf.c | 756 |
1 files changed, 470 insertions, 286 deletions
diff --git a/epan/dissectors/packet-protobuf.c b/epan/dissectors/packet-protobuf.c index e6f892e6..b1f3b993 100644 --- a/epan/dissectors/packet-protobuf.c +++ b/epan/dissectors/packet-protobuf.c @@ -21,7 +21,7 @@ * For example: * application/grpc,/helloworld.Greeter/SayHello,request * In this format, we will try to get real protobuf message type by method (service-name.method-name) - * and in/out type (request / reponse). + * and in/out type (request / response). * For other dissectors can specifies message type directly, like: * "message," message_type_name * For example: @@ -45,19 +45,20 @@ #include "protobuf-helper.h" #include "packet-protobuf.h" +#include "epan/dissectors/packet-http.h" /* converting */ -static inline gdouble -protobuf_uint64_to_double(guint64 value) { - union { gdouble f; guint64 i; } double_uint64_union; +static inline double +protobuf_uint64_to_double(uint64_t value) { + union { double f; uint64_t i; } double_uint64_union; double_uint64_union.i = value; return double_uint64_union.f; } -static inline gfloat -protobuf_uint32_to_float(guint32 value) { - union { gfloat f; guint32 i; } float_uint32_union; +static inline float +protobuf_uint32_to_float(uint32_t value) { + union { float f; uint32_t i; } float_uint32_union; float_uint32_union.i = value; return float_uint32_union.f; @@ -96,68 +97,71 @@ void proto_reg_handoff_protobuf(void); #define PREFS_UPDATE_PROTOBUF_SEARCH_PATHS 1 #define PREFS_UPDATE_PROTOBUF_UDP_MESSAGE_TYPES 2 -#define PREFS_UPDATE_ALL (PREFS_UPDATE_PROTOBUF_SEARCH_PATHS | PREFS_UPDATE_PROTOBUF_UDP_MESSAGE_TYPES) +#define PREFS_UPDATE_PROTOBUF_URI_MESSAGE_TYPES 3 +#define PREFS_UPDATE_ALL (PREFS_UPDATE_PROTOBUF_SEARCH_PATHS | PREFS_UPDATE_PROTOBUF_UDP_MESSAGE_TYPES | PREFS_UPDATE_PROTOBUF_URI_MESSAGE_TYPES) static void protobuf_reinit(int target); -static int proto_protobuf = -1; -static int proto_protobuf_json_mapping = -1; +static int proto_protobuf; +static int proto_protobuf_json_mapping; -static gboolean protobuf_dissector_called = FALSE; +static bool protobuf_dissector_called; /* information get from *.proto files */ -static int hf_protobuf_message_name = -1; -static int hf_protobuf_field_name = -1; -static int hf_protobuf_field_type = -1; +static int hf_protobuf_message_name; +static int hf_protobuf_field_name; +static int hf_protobuf_field_type; /* field tag */ -static int hf_protobuf_field_number = -1; -static int hf_protobuf_wire_type = -1; +static int hf_protobuf_field_number; +static int hf_protobuf_wire_type; /* field value */ -static int hf_protobuf_value_length = -1; /* only Length-delimited field has */ -static int hf_protobuf_value_data = -1; -static int hf_protobuf_value_double = -1; -static int hf_protobuf_value_float = -1; -static int hf_protobuf_value_int64 = -1; -static int hf_protobuf_value_uint64 = -1; -static int hf_protobuf_value_int32 = -1; -static int hf_protobuf_value_uint32 = -1; -static int hf_protobuf_value_bool = -1; -static int hf_protobuf_value_string = -1; -static int hf_protobuf_value_repeated = -1; -static int hf_json_mapping_line = -1; +static int hf_protobuf_value_length; /* only Length-delimited field has */ +static int hf_protobuf_value_data; +static int hf_protobuf_value_double; +static int hf_protobuf_value_float; +static int hf_protobuf_value_int64; +static int hf_protobuf_value_uint64; +static int hf_protobuf_value_int32; +static int hf_protobuf_value_uint32; +static int hf_protobuf_value_bool; +static int hf_protobuf_value_string; +static int hf_protobuf_value_repeated; +static int hf_json_mapping_line; /* expert */ -static expert_field ei_protobuf_failed_parse_tag = EI_INIT; -static expert_field ei_protobuf_failed_parse_length_delimited_field = EI_INIT; -static expert_field ei_protobuf_failed_parse_field = EI_INIT; -static expert_field ei_protobuf_wire_type_invalid = EI_INIT; -static expert_field et_protobuf_message_type_not_found = EI_INIT; -static expert_field et_protobuf_wire_type_not_support_packed_repeated = EI_INIT; -static expert_field et_protobuf_failed_parse_packed_repeated_field = EI_INIT; -static expert_field et_protobuf_missing_required_field = EI_INIT; -static expert_field et_protobuf_default_value_error = EI_INIT; +static expert_field ei_protobuf_failed_parse_tag; +static expert_field ei_protobuf_failed_parse_length_delimited_field; +static expert_field ei_protobuf_failed_parse_field; +static expert_field ei_protobuf_wire_type_invalid; +static expert_field ei_protobuf_message_type_not_found; +static expert_field ei_protobuf_wire_type_not_support_packed_repeated; +static expert_field ei_protobuf_failed_parse_packed_repeated_field; +static expert_field ei_protobuf_missing_required_field; +static expert_field ei_protobuf_default_value_error; /* trees */ -static int ett_protobuf = -1; -static int ett_protobuf_message = -1; -static int ett_protobuf_field = -1; -static int ett_protobuf_value = -1; -static int ett_protobuf_packed_repeated = -1; -static int ett_protobuf_json = -1; +static int ett_protobuf; +static int ett_protobuf_message; +static int ett_protobuf_field; +static int ett_protobuf_value; +static int ett_protobuf_packed_repeated; +static int ett_protobuf_json; /* preferences */ -static gboolean try_dissect_as_string = FALSE; -static gboolean show_all_possible_field_types = FALSE; -static gboolean dissect_bytes_as_string = FALSE; -static gboolean old_dissect_bytes_as_string = FALSE; -static gboolean show_details = FALSE; -static gboolean pbf_as_hf = FALSE; /* dissect protobuf fields as header fields of wireshark */ -static gboolean preload_protos = FALSE; +static bool try_dissect_as_string; +static bool show_all_possible_field_types; +static bool dissect_bytes_as_string; +static bool old_dissect_bytes_as_string; +static bool show_details; +static bool pbf_as_hf; /* dissect protobuf fields as header fields of wireshark */ +static bool preload_protos; /* 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; +static bool display_json_mapping; +static bool use_utc_fmt; +static const char* default_message_type = ""; + #define add_default_value_policy_vals_ENUM_VAL_T_LIST(XXX) \ XXX(ADD_DEFAULT_VALUE_NONE, 0, "none", "None") \ @@ -167,13 +171,13 @@ static gboolean use_utc_fmt = FALSE; typedef ENUM_VAL_T_ENUM(add_default_value_policy_vals) add_default_value_policy_t; -static gint add_default_value = (gint) ADD_DEFAULT_VALUE_NONE; +static int add_default_value = (int) ADD_DEFAULT_VALUE_NONE; /* dynamic wireshark header fields for protobuf fields */ -static hf_register_info *dynamic_hf = NULL; -static guint dynamic_hf_size = 0; +static hf_register_info *dynamic_hf; +static unsigned dynamic_hf_size; /* the key is full name of protobuf fields, the value is header field id */ -static GHashTable *pbf_hf_hash = NULL; +static GHashTable *pbf_hf_hash; /* Protobuf field value subdissector table list. * Only valid for the value of PROTOBUF_TYPE_BYTES or PROTOBUF_TYPE_STRING fields. @@ -184,21 +188,23 @@ static dissector_handle_t protobuf_handle; /* store varint tvb info */ typedef struct { - guint offset; - guint length; - guint64 value; + unsigned offset; + unsigned length; + uint64_t value; } protobuf_varint_tvb_info_t; -static PbwDescriptorPool* pbw_pool = NULL; +static PbwDescriptorPool* pbw_pool; /* protobuf source files search paths */ typedef struct { char* path; /* protobuf source files searching directory path */ - gboolean load_all; /* load all *.proto files in this directory and its sub directories */ + bool load_all; /* load all *.proto files in this directory and its sub directories */ } protobuf_search_path_t; -static protobuf_search_path_t* protobuf_search_paths = NULL; -static guint num_protobuf_search_paths = 0; +static protobuf_search_path_t* protobuf_search_paths; +static unsigned num_protobuf_search_paths; + +int proto_http; static void * protobuf_search_paths_copy_cb(void* n, const void* o, size_t siz _U_) @@ -206,7 +212,7 @@ protobuf_search_paths_copy_cb(void* n, const void* o, size_t siz _U_) protobuf_search_path_t* new_rec = (protobuf_search_path_t*)n; const protobuf_search_path_t* old_rec = (const protobuf_search_path_t*)o; - /* copy interval values like gint */ + /* copy interval values like int */ memcpy(new_rec, old_rec, sizeof(protobuf_search_path_t)); if (old_rec->path) @@ -226,14 +232,16 @@ protobuf_search_paths_free_cb(void*r) UAT_DIRECTORYNAME_CB_DEF(protobuf_search_paths, path, protobuf_search_path_t) UAT_BOOL_CB_DEF(protobuf_search_paths, load_all, protobuf_search_path_t) -/* the protobuf message type of the data on certain udp ports */ + + +/* The protobuf message type of the data on certain udp ports */ typedef struct { range_t *udp_port_range; /* dissect data on these udp ports as protobuf */ - gchar *message_type; /* protobuf message type of data on these udp ports */ + char *message_type; /* protobuf message type of data on these udp ports */ } protobuf_udp_message_type_t; -static protobuf_udp_message_type_t* protobuf_udp_message_types = NULL; -static guint num_protobuf_udp_message_types = 0; +static protobuf_udp_message_type_t* protobuf_udp_message_types; +static unsigned num_protobuf_udp_message_types; static void * protobuf_udp_message_types_copy_cb(void* n, const void* o, size_t siz _U_) @@ -241,7 +249,7 @@ protobuf_udp_message_types_copy_cb(void* n, const void* o, size_t siz _U_) protobuf_udp_message_type_t* new_rec = (protobuf_udp_message_type_t*)n; const protobuf_udp_message_type_t* old_rec = (const protobuf_udp_message_type_t*)o; - /* copy interval values like gint */ + /* copy interval values like int */ memcpy(new_rec, old_rec, sizeof(protobuf_udp_message_type_t)); if (old_rec->udp_port_range) @@ -262,11 +270,11 @@ protobuf_udp_message_types_update_cb(void *r, char **err) if (ranges_are_equal(rec->udp_port_range, empty)) { *err = g_strdup("Must specify UDP port(s) (like 8000 or 8000,8008-8088)"); wmem_free(NULL, empty); - return FALSE; + return false; } wmem_free(NULL, empty); - return TRUE; + return true; } static void @@ -281,7 +289,46 @@ protobuf_udp_message_types_free_cb(void*r) UAT_RANGE_CB_DEF(protobuf_udp_message_types, udp_port_range, protobuf_udp_message_type_t) UAT_CSTRING_CB_DEF(protobuf_udp_message_types, message_type, protobuf_udp_message_type_t) -static GSList* old_udp_port_ranges = NULL; +static GSList* old_udp_port_ranges; + + + +/* The protobuf message type associated with a request URI */ +typedef struct { + char *uri; /* URI appearing in HTTP message */ + char *message_type; /* associated protobuf message type */ +} protobuf_uri_mapping_t; + +static protobuf_uri_mapping_t* protobuf_uri_message_types; +static unsigned num_protobuf_uri_message_types; + +static void * +protobuf_uri_message_type_copy_cb(void* n, const void* o, size_t siz _U_) +{ + protobuf_uri_mapping_t* new_rec = (protobuf_uri_mapping_t*)n; + const protobuf_uri_mapping_t* old_rec = (const protobuf_uri_mapping_t*)o; + + if (old_rec->uri) + new_rec->uri = g_strdup(old_rec->uri); + if (old_rec->message_type) + new_rec->message_type = g_strdup(old_rec->message_type); + + return new_rec; +} + +static void +protobuf_uri_message_type_free_cb(void*r) +{ + protobuf_uri_mapping_t* rec = (protobuf_uri_mapping_t*)r; + + g_free(rec->uri); + g_free(rec->message_type); +} + +UAT_CSTRING_CB_DEF(protobuf_uri_message_type, uri, protobuf_uri_mapping_t) +UAT_CSTRING_CB_DEF(protobuf_uri_message_type, message_type, protobuf_uri_mapping_t) + + /* If you use int32 or int64 as the type for a negative number, the resulting varint is always * ten bytes long - it is, effectively, treated like a very large unsigned integer. If you use @@ -290,27 +337,27 @@ static GSList* old_udp_port_ranges = NULL; * value (for instance, -1) have a small varint encoded value too. (refers to protobuf spec) * sint32 encoded using (n << 1) ^ (n >> 31) */ -static gint32 -sint32_decode(guint32 sint32) { - return (sint32 >> 1) ^ ((gint32)sint32 << 31 >> 31); +static int32_t +sint32_decode(uint32_t sint32) { + return (sint32 >> 1) ^ ((int32_t)sint32 << 31 >> 31); } /* sint64 encoded using (n << 1) ^ (n >> 63) */ -static gint64 -sint64_decode(guint64 sint64) { - return (sint64 >> 1) ^ ((gint64)sint64 << 63 >> 63); +static int64_t +sint64_decode(uint64_t sint64) { + return (sint64 >> 1) ^ ((int64_t)sint64 << 63 >> 63); } /* Try to get a protobuf field which has a varint value from the tvb. * The field number, wire type and uint64 value will be output. * @return the length of this field. Zero if failed. */ -static guint -tvb_get_protobuf_field_uint(tvbuff_t* tvb, guint offset, guint maxlen, - guint64* field_number, guint32* wire_type, guint64* value) +static unsigned +tvb_get_protobuf_field_uint(tvbuff_t* tvb, unsigned offset, unsigned maxlen, + uint64_t* field_number, uint32_t* wire_type, uint64_t* value) { - guint tag_length, value_length; - guint64 tag_value; + unsigned tag_length, value_length; + uint64_t tag_value; /* parsing the tag of the field */ tag_length = tvb_get_varint(tvb, offset, maxlen, &tag_value, ENC_VARINT_PROTOBUF); @@ -331,14 +378,14 @@ tvb_get_protobuf_field_uint(tvbuff_t* tvb, guint offset, guint maxlen, /* Get Protobuf timestamp from the tvb according to the format of google.protobuf.Timestamp. * return the length parsed. */ -static guint -tvb_get_protobuf_time(tvbuff_t* tvb, guint offset, guint maxlen, nstime_t* timestamp) +static unsigned +tvb_get_protobuf_time(tvbuff_t* tvb, unsigned offset, unsigned maxlen, nstime_t* timestamp) { - guint field_length; - guint64 field_number, value; - guint32 wire_type; - guint off = offset; - guint len = maxlen; /* remain bytes */ + unsigned field_length; + uint64_t field_number, value; + uint32_t wire_type; + unsigned off = offset; + unsigned len = maxlen; /* remain bytes */ /* Get the seconds and nanos fields from google.protobuf.Timestamp message which defined: * @@ -347,8 +394,7 @@ tvb_get_protobuf_time(tvbuff_t* tvb, guint offset, guint maxlen, nstime_t* times * int32 nanos = 2; * } */ - timestamp->secs = 0; - timestamp->nsecs = 0; + nstime_set_zero(timestamp); while (len > 0) { field_length = tvb_get_protobuf_field_uint(tvb, off, len, &field_number, &wire_type, &value); @@ -357,28 +403,32 @@ tvb_get_protobuf_time(tvbuff_t* tvb, guint offset, guint maxlen, nstime_t* times } if (field_number == 1) { - timestamp->secs = (gint64)value; + timestamp->secs = (time_t)value; } else if (field_number == 2) { - timestamp->nsecs = (gint32)value; + timestamp->nsecs = (int)value; } off += field_length; len -= field_length; } + if (timestamp->nsecs < 0 || timestamp->nsecs > 999999999) { + nstime_set_unset(timestamp); + } + return maxlen - len; } /* declare first because it will be called by dissect_packed_repeated_field_values */ static void -protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset, guint length, packet_info *pinfo, - proto_item *ti_field, int field_type, const guint64 value, const gchar* prepend_text, const PbwFieldDescriptor* field_desc, - gboolean is_top_level, json_dumper *dumper); +protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, unsigned offset, unsigned length, packet_info *pinfo, + proto_item *ti_field, int field_type, const uint64_t value, const char* prepend_text, const PbwFieldDescriptor* field_desc, + bool is_top_level, json_dumper *dumper); static void -dissect_protobuf_message(tvbuff_t *tvb, guint offset, guint length, packet_info *pinfo, proto_tree *protobuf_tree, - const PbwDescriptor* message_desc, int hf_msg, gboolean is_top_level, json_dumper *dumper, wmem_allocator_t* scope, char** retval); +dissect_protobuf_message(tvbuff_t *tvb, unsigned offset, unsigned length, packet_info *pinfo, proto_tree *protobuf_tree, + const PbwDescriptor* message_desc, int hf_msg, bool 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". @@ -386,17 +436,17 @@ dissect_protobuf_message(tvbuff_t *tvb, guint offset, guint length, packet_info * or likes: tag + fixed64 + fixed64 + fixed64 ... * Return consumed bytes */ -static guint +static unsigned // NOLINTNEXTLINE(misc-no-recursion) -dissect_packed_repeated_field_values(tvbuff_t *tvb, guint start, guint length, packet_info *pinfo, - proto_item *ti_field, int field_type, const gchar* prepend_text, const PbwFieldDescriptor* field_desc, +dissect_packed_repeated_field_values(tvbuff_t *tvb, unsigned start, unsigned length, packet_info *pinfo, + proto_item *ti_field, int field_type, const char* prepend_text, const PbwFieldDescriptor* field_desc, json_dumper *dumper) { - guint64 sub_value; - guint sub_value_length; - guint offset = start; + uint64_t sub_value; + unsigned sub_value_length; + unsigned offset = start; protobuf_varint_tvb_info_t *info; - guint max_offset = offset + length; + unsigned max_offset = offset + length; wmem_list_frame_t *lframe; wmem_list_t* varint_list; int value_size = 0; @@ -449,7 +499,7 @@ dissect_packed_repeated_field_values(tvbuff_t *tvb, guint start, guint length, p for (lframe = wmem_list_head(varint_list); lframe != NULL; lframe = wmem_list_frame_next(lframe)) { info = (protobuf_varint_tvb_info_t*)wmem_list_frame_data(lframe); protobuf_dissect_field_value(subtree, tvb, info->offset, info->length, pinfo, - ti_field, field_type, info->value, prepend_text, field_desc, FALSE, dumper); + ti_field, field_type, info->value, prepend_text, field_desc, false, dumper); prepend_text = ","; } @@ -471,15 +521,15 @@ dissect_packed_repeated_field_values(tvbuff_t *tvb, guint start, guint length, p } if (length % value_size != 0) { - expert_add_info(pinfo, ti_field, &et_protobuf_failed_parse_packed_repeated_field); + expert_add_info(pinfo, ti_field, &ei_protobuf_failed_parse_packed_repeated_field); return 0; } for (offset = start; offset < max_offset; offset += value_size) { protobuf_dissect_field_value(subtree, tvb, offset, value_size, pinfo, ti_field, field_type, - (value_size == 4 ? tvb_get_guint32(tvb, offset, ENC_LITTLE_ENDIAN) - : tvb_get_guint64(tvb, offset, ENC_LITTLE_ENDIAN)), - prepend_text, field_desc, FALSE, dumper); + (value_size == 4 ? tvb_get_uint32(tvb, offset, ENC_LITTLE_ENDIAN) + : tvb_get_uint64(tvb, offset, ENC_LITTLE_ENDIAN)), + prepend_text, field_desc, false, dumper); prepend_text = ","; } @@ -487,7 +537,7 @@ dissect_packed_repeated_field_values(tvbuff_t *tvb, guint start, guint length, p break; default: - expert_add_info(pinfo, ti_field, &et_protobuf_wire_type_not_support_packed_repeated); + expert_add_info(pinfo, ti_field, &ei_protobuf_wire_type_not_support_packed_repeated); return 0; /* prevent dead loop */ } @@ -504,6 +554,7 @@ abs_time_to_rfc3339(wmem_allocator_t *scope, const nstime_t *nstime, bool use_ut struct tm *tm; char datetime_format[128]; int nsecs; + int width; char nsecs_buf[32]; if (use_utc) { @@ -524,10 +575,12 @@ abs_time_to_rfc3339(wmem_allocator_t *scope, const nstime_t *nstime, bool use_ut return wmem_strdup_printf(scope, datetime_format, ""); nsecs = nstime->nsecs; - while (nsecs > 0 && (nsecs % 10) == 0) { - nsecs /= 10; + width = 9; + while (width > 0 && (nsecs % 1000) == 0) { + nsecs /= 1000; + width -= 3; } - snprintf(nsecs_buf, sizeof(nsecs_buf), ".%d", nsecs); + snprintf(nsecs_buf, sizeof(nsecs_buf), ".%0*d", width, nsecs); return wmem_strdup_printf(scope, datetime_format, nsecs_buf); } @@ -535,23 +588,23 @@ abs_time_to_rfc3339(wmem_allocator_t *scope, const nstime_t *nstime, bool use_ut /* Dissect field value based on a specific type. */ static void // NOLINTNEXTLINE(misc-no-recursion) -protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset, guint length, packet_info *pinfo, - proto_item *ti_field, int field_type, const guint64 value, const gchar* prepend_text, const PbwFieldDescriptor* field_desc, - gboolean is_top_level, json_dumper *dumper) +protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, unsigned offset, unsigned length, packet_info *pinfo, + proto_item *ti_field, int field_type, const uint64_t value, const char* prepend_text, const PbwFieldDescriptor* field_desc, + bool is_top_level, json_dumper *dumper) { - gdouble double_value; - gfloat float_value; - gint64 int64_value; - gint32 int32_value; + double double_value; + float float_value; + int64_t int64_value; + int32_t int32_value; char* buf; - gboolean add_datatype = TRUE; + bool add_datatype = true; proto_item* ti = NULL; proto_tree* subtree = NULL; const char* enum_value_name = NULL; const PbwDescriptor* sub_message_desc = NULL; const PbwEnumDescriptor* enum_desc = NULL; int* hf_id_ptr = NULL; - const gchar* field_full_name = field_desc ? pbw_FieldDescriptor_full_name(field_desc) : NULL; + const char* field_full_name = field_desc ? pbw_FieldDescriptor_full_name(field_desc) : NULL; proto_tree* field_tree = proto_item_get_subtree(ti_field); proto_tree* field_parent_tree = proto_tree_get_parent_tree(field_tree); proto_tree* pbf_tree = field_tree; @@ -590,7 +643,7 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset break; case PROTOBUF_TYPE_FLOAT: - float_value = protobuf_uint32_to_float((guint32) value); + float_value = protobuf_uint32_to_float((uint32_t) value); proto_tree_add_float(value_tree, hf_protobuf_value_float, tvb, offset, length, float_value); proto_item_append_text(ti_field, "%s %f", prepend_text, float_value); if (is_top_level) { @@ -606,7 +659,7 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset case PROTOBUF_TYPE_INT64: case PROTOBUF_TYPE_SFIXED64: - int64_value = (gint64) value; + int64_value = (int64_t) value; proto_tree_add_int64(value_tree, hf_protobuf_value_int64, tvb, offset, length, int64_value); proto_item_append_text(ti_field, "%s %" PRId64, prepend_text, int64_value); if (is_top_level) { @@ -637,7 +690,7 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset case PROTOBUF_TYPE_INT32: case PROTOBUF_TYPE_SFIXED32: - int32_value = (gint32)value; + int32_value = (int32_t)value; proto_tree_add_int(value_tree, hf_protobuf_value_int32, tvb, offset, length, int32_value); proto_item_append_text(ti_field, "%s %d", prepend_text, int32_value); if (is_top_level) { @@ -652,7 +705,7 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset break; case PROTOBUF_TYPE_ENUM: - int32_value = (gint32) value; + int32_value = (int32_t) value; /* get the name of enum value */ if (field_desc) { enum_desc = pbw_FieldDescriptor_enum_type(field_desc); @@ -687,13 +740,13 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset case PROTOBUF_TYPE_BOOL: if (length > 1) break; /* boolean should not use more than one bytes */ - proto_tree_add_boolean(value_tree, hf_protobuf_value_bool, tvb, offset, length, (guint32)value); + proto_tree_add_boolean(value_tree, hf_protobuf_value_bool, tvb, offset, length, value); proto_item_append_text(ti_field, "%s %s", prepend_text, value ? "true" : "false"); if (is_top_level) { col_append_fstr(pinfo->cinfo, COL_INFO, "=%s", value ? "true" : "false"); } if (hf_id_ptr) { - proto_tree_add_boolean(pbf_tree, *hf_id_ptr, tvb, offset, length, (guint32)value); + proto_tree_add_boolean(pbf_tree, *hf_id_ptr, tvb, offset, length, value); } if (field_desc && dumper) { json_dumper_value_anyf(dumper, value ? "true" : "false"); @@ -753,12 +806,16 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset if (field_desc) { sub_message_desc = pbw_FieldDescriptor_message_type(field_desc); if (sub_message_desc == NULL) { - expert_add_info(pinfo, ti_field, &et_protobuf_message_type_not_found); + expert_add_info(pinfo, ti_field, &ei_protobuf_message_type_not_found); } } if (sub_message_desc) { 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); + hf_id_ptr ? *hf_id_ptr : -1, + false, // not top level + dumper, + pinfo->pool, + &buf); if (buf) { /* append the value in string format to ti_field node */ proto_item_append_text(ti_field, "= %s", buf); @@ -772,21 +829,21 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset case PROTOBUF_TYPE_UINT32: case PROTOBUF_TYPE_FIXED32: /* same as UINT32 */ - proto_tree_add_uint(value_tree, hf_protobuf_value_uint32, tvb, offset, length, (guint32)value); - proto_item_append_text(ti_field, "%s %u", prepend_text, (guint32)value); + proto_tree_add_uint(value_tree, hf_protobuf_value_uint32, tvb, offset, length, (uint32_t)value); + proto_item_append_text(ti_field, "%s %u", prepend_text, (uint32_t)value); if (is_top_level) { - col_append_fstr(pinfo->cinfo, COL_INFO, "=%u", (guint32)value); + col_append_fstr(pinfo->cinfo, COL_INFO, "=%u", (uint32_t)value); } if (hf_id_ptr) { - proto_tree_add_uint(pbf_tree, *hf_id_ptr, tvb, offset, length, (guint32)value); + proto_tree_add_uint(pbf_tree, *hf_id_ptr, tvb, offset, length, (uint32_t)value); } if (field_desc && dumper) { - json_dumper_value_anyf(dumper, "%u", (guint32)value); + json_dumper_value_anyf(dumper, "%u", (uint32_t)value); } break; case PROTOBUF_TYPE_SINT32: - int32_value = sint32_decode((guint32)value); + int32_value = sint32_decode((uint32_t)value); proto_tree_add_int(value_tree, hf_protobuf_value_int32, tvb, offset, length, int32_value); proto_item_append_text(ti_field, "%s %d", prepend_text, int32_value); if (is_top_level) { @@ -817,7 +874,7 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset default: /* ignore unknown field type */ - add_datatype = FALSE; + add_datatype = false; break; } @@ -843,8 +900,8 @@ protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset /* add all possible values according to field types. */ static void // NOLINTNEXTLINE(misc-no-recursion) -protobuf_try_dissect_field_value_on_multi_types(proto_tree *value_tree, tvbuff_t *tvb, guint offset, guint length, - packet_info *pinfo, proto_item *ti_field, int* field_types, const guint64 value, const gchar* prepend_text, +protobuf_try_dissect_field_value_on_multi_types(proto_tree *value_tree, tvbuff_t *tvb, unsigned offset, unsigned length, + packet_info *pinfo, proto_item *ti_field, int* field_types, const uint64_t value, const char* prepend_text, json_dumper *dumper) { int i; @@ -854,34 +911,34 @@ protobuf_try_dissect_field_value_on_multi_types(proto_tree *value_tree, tvbuff_t } for (i = 0; field_types[i] != PROTOBUF_TYPE_NONE; ++i) { - protobuf_dissect_field_value(value_tree, tvb, offset, length, pinfo, ti_field, field_types[i], value, prepend_text, NULL, FALSE, dumper); + protobuf_dissect_field_value(value_tree, tvb, offset, length, pinfo, ti_field, field_types[i], value, prepend_text, NULL, false, dumper); prepend_text = ","; } } -static gboolean +static bool // NOLINTNEXTLINE(misc-no-recursion) -dissect_one_protobuf_field(tvbuff_t *tvb, guint* offset, guint maxlen, packet_info *pinfo, proto_tree *protobuf_tree, - const PbwDescriptor* message_desc, gboolean is_top_level, const PbwFieldDescriptor** field_desc_ptr, +dissect_one_protobuf_field(tvbuff_t *tvb, unsigned* offset, unsigned maxlen, packet_info *pinfo, proto_tree *protobuf_tree, + const PbwDescriptor* message_desc, bool is_top_level, const PbwFieldDescriptor** field_desc_ptr, const PbwFieldDescriptor* prev_field_desc, json_dumper *dumper) { - guint64 tag_value; /* tag value = (field_number << 3) | wire_type */ - guint tag_length; /* how many bytes this tag has */ - guint64 field_number; - guint32 wire_type; - guint64 value_uint64; /* uint64 value of numeric field (type of varint, 64-bit, 32-bit */ - guint value_length; - guint value_length_size = 0; /* only Length-delimited field has it */ + uint64_t tag_value; /* tag value = (field_number << 3) | wire_type */ + unsigned tag_length; /* how many bytes this tag has */ + uint64_t field_number; + uint32_t wire_type; + uint64_t value_uint64; /* uint64 value of numeric field (type of varint, 64-bit, 32-bit */ + unsigned value_length; + unsigned value_length_size = 0; /* only Length-delimited field has it */ proto_item *ti_field, *ti_field_number, *ti_wire, *ti_value_length = NULL; proto_item *ti_value, *ti_field_name, *ti_field_type = NULL; proto_tree *field_tree; proto_tree *value_tree; - const gchar* field_name = NULL; + const char* field_name = NULL; int field_type = -1; - gboolean is_packed = FALSE; - gboolean is_repeated = FALSE; + bool is_packed = false; + bool is_repeated = false; const PbwFieldDescriptor* field_desc = NULL; - guint start_offset = *offset; + unsigned start_offset = *offset; /* A protocol buffer message is a series of key-value pairs. The binary version of a message just uses * the field's number as the key. a wire type that provides just enough information to find the length of @@ -905,7 +962,7 @@ dissect_one_protobuf_field(tvbuff_t *tvb, guint* offset, guint maxlen, packet_in if (tag_length == 0) { /* not found a valid varint */ expert_add_info(pinfo, ti_field, &ei_protobuf_failed_parse_tag); - return FALSE; + return false; } 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); @@ -959,7 +1016,7 @@ dissect_one_protobuf_field(tvbuff_t *tvb, guint* offset, guint maxlen, packet_in value_length = tvb_get_varint(tvb, *offset, maxlen - tag_length, &value_uint64, ENC_VARINT_PROTOBUF); if (value_length == 0) { expert_add_info(pinfo, ti_wire, &ei_protobuf_failed_parse_field); - return FALSE; + return false; } break; @@ -979,19 +1036,19 @@ dissect_one_protobuf_field(tvbuff_t *tvb, guint* offset, guint maxlen, packet_in value_length_size = tvb_get_varint(tvb, *offset, maxlen - tag_length, &value_uint64, ENC_VARINT_PROTOBUF); if (value_length_size == 0) { expert_add_info(pinfo, ti_field, &ei_protobuf_failed_parse_length_delimited_field); - return FALSE; + return false; } ti_value_length = proto_tree_add_uint64(field_tree, hf_protobuf_value_length, tvb, *offset, value_length_size, value_uint64); (*offset) += value_length_size; - /* we believe the length of following value will not be bigger than guint */ - value_length = (guint) value_uint64; + /* we believe the length of following value will not be bigger than unsigned */ + value_length = (unsigned) value_uint64; break; default: expert_add_info(pinfo, ti_wire, &ei_protobuf_wire_type_invalid); - return FALSE; + return false; } proto_item_set_len(ti_field, tag_length + value_length_size + value_length); @@ -1062,7 +1119,7 @@ dissect_one_protobuf_field(tvbuff_t *tvb, guint* offset, guint maxlen, packet_in } (*offset) += value_length; - return TRUE; + return true; } /* Make Protobuf fields that are not serialized on the wire (missing in capture files) to be displayed @@ -1086,34 +1143,34 @@ dissect_one_protobuf_field(tvbuff_t *tvb, guint* offset, guint maxlen, packet_in * - ADD_DEFAULT_VALUE_ALL -- missing fields of all situations (1, 2, 3, and 4) will be displayed. */ static void -add_missing_fields_with_default_values(tvbuff_t* tvb, guint offset, packet_info* pinfo, proto_tree* message_tree, - const PbwDescriptor* message_desc, int* parsed_fields, int parsed_fields_count, json_dumper *dumper) +add_missing_fields_with_default_values(tvbuff_t* tvb, unsigned offset, packet_info* pinfo, proto_tree* message_tree, + const PbwDescriptor* message_desc, wmem_map_t* parsed_fields, json_dumper *dumper) { const PbwFieldDescriptor* field_desc; - const gchar* field_name, * field_full_name, * enum_value_name, * string_value; + const char* field_name, * field_full_name, * enum_value_name, * string_value; int field_count = pbw_Descriptor_field_count(message_desc); - int field_type, i, j; - guint64 field_number; - gboolean is_required; - gboolean is_repeated; - gboolean has_default_value; /* explicitly-declared default value */ + int field_type, i; + uint64_t field_number; + bool is_required; + bool is_repeated; + bool has_default_value; /* explicitly-declared default value */ proto_item* ti_message = proto_tree_get_parent(message_tree); proto_item* ti_field, * ti_field_number, * ti_field_name, * ti_field_type, * ti_value, * ti_pbf; proto_tree* field_tree, * pbf_tree; int* hf_id_ptr; - gdouble double_value; - gfloat float_value; - gint64 int64_value; - gint32 int32_value; - guint64 uint64_value; - guint32 uint32_value; - gboolean bool_value; - gint size; + double double_value; + float float_value; + int64_t int64_value; + int32_t int32_value; + uint64_t uint64_value; + uint32_t uint32_value; + bool bool_value; + int size; const PbwEnumValueDescriptor* enum_value_desc; for (i = 0; i < field_count; i++) { field_desc = pbw_Descriptor_field(message_desc, i); - field_number = (guint64) pbw_FieldDescriptor_number(field_desc); + field_number = (uint64_t) pbw_FieldDescriptor_number(field_desc); field_type = pbw_FieldDescriptor_type(field_desc); is_required = pbw_FieldDescriptor_is_required(field_desc); is_repeated = pbw_FieldDescriptor_is_repeated(field_desc); @@ -1143,22 +1200,15 @@ add_missing_fields_with_default_values(tvbuff_t* tvb, guint offset, packet_info* } /* check if it is parsed */ - if (parsed_fields && parsed_fields_count > 0) { - for (j = 0; j < parsed_fields_count; j++) { - if ((guint64) parsed_fields[j] == field_number) { - break; - } - } - if (j < parsed_fields_count) { - continue; /* this field is parsed */ - } + if (wmem_map_lookup(parsed_fields, GINT_TO_POINTER(field_number))) { + continue; /* this field is parsed */ } field_name = pbw_FieldDescriptor_name(field_desc); /* this field is not found in message payload */ if (is_required) { - expert_add_info_format(pinfo, ti_message, &et_protobuf_missing_required_field, "missing required field '%s'", field_name); + expert_add_info_format(pinfo, ti_message, &ei_protobuf_missing_required_field, "missing required field '%s'", field_name); continue; } @@ -1295,15 +1345,15 @@ add_missing_fields_with_default_values(tvbuff_t* tvb, guint offset, packet_info* DISSECTOR_ASSERT_HINT(has_default_value && string_value, "Bytes field must have default value!"); if (dumper) { json_dumper_begin_base64(dumper); - json_dumper_write_base64(dumper, (const guchar *)string_value, size); + json_dumper_write_base64(dumper, (const unsigned char *)string_value, size); json_dumper_end_base64(dumper); } if (!dissect_bytes_as_string) { - ti_value = proto_tree_add_bytes_with_length(field_tree, hf_protobuf_value_data, tvb, offset, 0, (const guint8*) string_value, size); + ti_value = proto_tree_add_bytes_with_length(field_tree, hf_protobuf_value_data, tvb, offset, 0, (const uint8_t*) string_value, size); proto_item_append_text(ti_field, " (%d bytes)", size); /* the type of *hf_id_ptr MUST be FT_BYTES now */ if (hf_id_ptr) { - ti_pbf = proto_tree_add_bytes_with_length(pbf_tree, *hf_id_ptr, tvb, offset, 0, (const guint8*)string_value, size); + ti_pbf = proto_tree_add_bytes_with_length(pbf_tree, *hf_id_ptr, tvb, offset, 0, (const uint8_t*)string_value, size); } break; } @@ -1345,7 +1395,7 @@ add_missing_fields_with_default_values(tvbuff_t* tvb, guint offset, packet_info* } break; } else { - expert_add_info_format(pinfo, ti_message, &et_protobuf_default_value_error, "enum value of field '%s' not found in *.proto!", field_name); + expert_add_info_format(pinfo, ti_message, &ei_protobuf_default_value_error, "enum value of field '%s' not found in *.proto!", field_name); } break; @@ -1376,43 +1426,43 @@ add_missing_fields_with_default_values(tvbuff_t* tvb, guint offset, packet_info* static void // NOLINTNEXTLINE(misc-no-recursion) -dissect_protobuf_message(tvbuff_t *tvb, guint offset, guint length, packet_info *pinfo, proto_tree *protobuf_tree, - const PbwDescriptor* message_desc, int hf_msg, gboolean is_top_level, json_dumper *dumper, wmem_allocator_t* scope, char** retval) +dissect_protobuf_message(tvbuff_t *tvb, unsigned offset, unsigned length, packet_info *pinfo, proto_tree *protobuf_tree, + const PbwDescriptor* message_desc, int hf_msg, bool is_top_level, json_dumper *dumper, wmem_allocator_t* scope, char** retval) { proto_tree *message_tree; proto_item *ti_message, *ti; - const gchar* message_name = "<UNKNOWN> Message Type"; - guint max_offset = offset + length; + const char* message_name = "<UNKNOWN>"; + unsigned max_offset = offset + length; const PbwFieldDescriptor* field_desc; const PbwFieldDescriptor* prev_field_desc = NULL; - int* parsed_fields = NULL; /* store parsed field numbers. end with NULL */ - int parsed_fields_count = 0; - int field_count = 0; + wmem_map_t* parsed_fields = NULL; /* store parsed field numbers. */ nstime_t timestamp = { 0 }; - gchar* value_label = NULL; /* The label representing the value of some wellknown message, such as google.protobuf.Timestamp */ + char* 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); - /* N.B. extra entries are needed because of possibly repeated items within message. - TODO: use dynamic wmem_array_t? Don't fancy void* interface... */ - field_count = pbw_Descriptor_field_count(message_desc) + 256; - if (add_default_value && field_count > 0) { - parsed_fields = wmem_alloc0_array(pinfo->pool, int, field_count); + + if (add_default_value) { + parsed_fields = wmem_map_new(pinfo->pool, g_direct_hash, g_direct_equal); } if (strcmp(message_name, "google.protobuf.Timestamp") == 0) { /* parse this message as timestamp */ tvb_get_protobuf_time(tvb, offset, length, ×tamp); - value_label = abs_time_to_rfc3339(scope ? scope : pinfo->pool, ×tamp, use_utc_fmt); - if (hf_msg != -1) { - ti = proto_tree_add_time_format_value(protobuf_tree, hf_msg, tvb, offset, length, ×tamp, "%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 */ + if (!nstime_is_unset(×tamp)) { + value_label = abs_time_to_rfc3339(scope ? scope : pinfo->pool, ×tamp, use_utc_fmt); + if (hf_msg > 0) { + ti = proto_tree_add_time_format_value(protobuf_tree, hf_msg, tvb, offset, length, ×tamp, "%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, proto_tree_get_parent(protobuf_tree), &ei_protobuf_failed_parse_field); } - } else if (hf_msg != -1) { + } else if (hf_msg > 0) { 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); } @@ -1442,10 +1492,14 @@ dissect_protobuf_message(tvbuff_t *tvb, guint offset, guint length, packet_info } if (is_top_level) { - col_clear(pinfo->cinfo, COL_PROTOCOL); + if (col_get_text(pinfo->cinfo, COL_PROTOCOL) && strlen(col_get_text(pinfo->cinfo, COL_PROTOCOL))) { + col_append_str(pinfo->cinfo, COL_PROTOCOL, "/"); + } + else { + col_clear(pinfo->cinfo, COL_PROTOCOL); + col_clear(pinfo->cinfo, COL_INFO); + } col_append_fstr(pinfo->cinfo, COL_PROTOCOL, "PB(%s)", message_name); - /* Top-level info will get written into Info column. */ - col_clear(pinfo->cinfo, COL_INFO); } /* support filtering with message name */ @@ -1471,12 +1525,7 @@ dissect_protobuf_message(tvbuff_t *tvb, guint offset, guint length, packet_info } if (parsed_fields && field_desc) { - if (parsed_fields_count < field_count) { - parsed_fields[parsed_fields_count++] = pbw_FieldDescriptor_number(field_desc); - } - else { - /* TODO: error? Means default values may not be set/shown.. */ - } + wmem_map_insert(parsed_fields, GINT_TO_POINTER(pbw_FieldDescriptor_number(field_desc)), GINT_TO_POINTER(1)); } prev_field_desc = field_desc; @@ -1489,18 +1538,14 @@ dissect_protobuf_message(tvbuff_t *tvb, guint offset, guint length, packet_info } /* add default values for missing fields */ - if (add_default_value && field_count > 0) { - add_missing_fields_with_default_values(tvb, offset, pinfo, message_tree, message_desc, parsed_fields, parsed_fields_count, dumper); + if (add_default_value && parsed_fields) { + add_missing_fields_with_default_values(tvb, offset, pinfo, message_tree, message_desc, parsed_fields, dumper); } if (message_desc && dumper) { json_dumper_end_object(dumper); } - 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); @@ -1516,8 +1561,8 @@ static const PbwDescriptor* find_message_type_by_udp_port(packet_info *pinfo) { range_t* udp_port_range; - const gchar* message_type; - guint i; + const char* message_type; + unsigned i; for (i = 0; i < num_protobuf_udp_message_types; ++i) { udp_port_range = protobuf_udp_message_types[i].udp_port_range; if (value_is_in_range(udp_port_range, pinfo->srcport) @@ -1532,20 +1577,78 @@ find_message_type_by_udp_port(packet_info *pinfo) return NULL; } +static bool +// NOLINTNEXTLINE(misc-no-recursion) +uri_matches_pattern(const char *request_uri, const char *uri_pattern, int depth) +{ + /* Arbitrary recursion depth limit.. */ + if (depth > 32) { + return false; + } + + /* Exact match */ + if (strcmp(request_uri, uri_pattern)==0) { + return true; + } + + /* Match if both strings now empty */ + if (strlen(uri_pattern)==0 && strlen(request_uri)==0) { + return true; + } + + /* Fail if remaining, unmatched pattern but reached end of uri */ + if (strlen(uri_pattern)>0 && strlen(request_uri)==0) { + return false; + } + + /* If remainder of pattern is just '*', it matches */ + if (strlen(uri_pattern)==1 && uri_pattern[0] == '*') { + return true; + } + + /* If next uri_pattern char is not '*', needs to match exactly */ + if (strlen(uri_pattern) && uri_pattern[0] != '*') { + + /* Skip identical characters */ + int n; + for (n=0; strlen(request_uri+n) && strlen(request_uri+n) && uri_pattern[n] != '*'; n++) { + if (request_uri[n] == uri_pattern[n]) { + continue; + } + else { + /* Fail if non-wildcarded comparison fails */ + return false; + } + } + + /* Recursively call n characters along */ + return uri_matches_pattern(request_uri+n, uri_pattern+n, depth+1); + } + + if (strlen(uri_pattern) && uri_pattern[0] == '*') { + /* We are at a '*'. Test with/without moving past it now */ + return (uri_matches_pattern(request_uri+1, uri_pattern, depth+1) || + uri_matches_pattern(request_uri+1, uri_pattern+1, depth+1)); + } + + return false; +} + + static int dissect_protobuf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { proto_item *ti; proto_tree *protobuf_tree, *protobuf_json_tree; - guint offset = 0; - guint i; + unsigned offset = 0; + unsigned i; const PbwDescriptor* message_desc = NULL; - const gchar* data_str = NULL; - gchar *json_str, *p, *q; + const char* data_str = NULL; + char *json_str, *p; /* initialize only the first time the protobuf dissector is called */ if (!protobuf_dissector_called) { - protobuf_dissector_called = TRUE; + protobuf_dissector_called = true; protobuf_reinit(PREFS_UPDATE_ALL); } @@ -1559,27 +1662,27 @@ dissect_protobuf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data parameter when calling protobuf dissector. But they can tell Protobuf dissector the message type by the value of pinfo->private_table["pb_msg_type"]. */ if (data) { - data_str = (const gchar*)data; + data_str = (const char*)data; } else if (pinfo->private_table) { - data_str = (const gchar*)g_hash_table_lookup(pinfo->private_table, "pb_msg_type"); + data_str = (const char*)g_hash_table_lookup(pinfo->private_table, "pb_msg_type"); } if (data_str) { /* The data_str has two formats: * (1) Come from GRPC dissector like: * http2_content_type "," http2_path "," ("request" / "response") - * Acording to grpc wire format guide, it will be: + * According to grpc wire format guide, it will be: * "application/grpc" ["+proto"] "," "/" service-name "/" method-name "," ("request" / "response") * For example: * application/grpc,/helloworld.Greeter/SayHello,request * In this format, we will try to get real protobuf message type by method (service-name.method-name) - * and in/out type (request / reponse). + * and in/out type (request / response). * (2) Come from other dissector which specifies message type directly, like: * "message," message_type_name * For example: * message,helloworld.HelloRequest (helloworld is package, HelloRequest is message type) */ - const gchar* message_info = strchr(data_str, ','); + const char* message_info = strchr(data_str, ','); if (message_info) { message_info++; /* ignore ',' */ @@ -1594,9 +1697,9 @@ dissect_protobuf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data message_info++; /* ignore first '/' */ } - gchar** tmp_names = wmem_strsplit(pinfo->pool, message_info, ",", 2); - gchar* method_name = (tmp_names[0]) ? tmp_names[0] : NULL; - gchar* direction_type = (method_name && tmp_names[1]) ? tmp_names[1] : NULL; + char** tmp_names = wmem_strsplit(pinfo->pool, message_info, ",", 2); + char* method_name = (tmp_names[0]) ? tmp_names[0] : NULL; + char* direction_type = (method_name && tmp_names[1]) ? tmp_names[1] : NULL; /* replace all '/' to '.', so helloworld.Greeter/SayHello converted to helloworld.Greeter.SayHello */ if (method_name) { @@ -1630,6 +1733,33 @@ dissect_protobuf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data message_desc = find_message_type_by_udp_port(pinfo); } + if (!message_desc) { + /* If this was inside an HTTP request, do we have a message type assigned to this URI? */ + http_req_res_t *curr = (http_req_res_t *)p_get_proto_data(wmem_file_scope(), pinfo, + proto_http, HTTP_PROTO_DATA_REQRES); + if (curr) { + if (curr->request_uri) { + for (unsigned n=0; n < num_protobuf_uri_message_types; n++) { + if (uri_matches_pattern(curr->request_uri, protobuf_uri_message_types[n].uri, 1 /* depth */)) { + if (strlen(protobuf_uri_message_types[n].message_type)) { + /* Lookup message type for matching URI */ + message_desc = pbw_DescriptorPool_FindMessageTypeByName(pbw_pool, + protobuf_uri_message_types[n].message_type); + } + /* Found a matched URI, so stop looking */ + break; + } + } + } + } + } + + /* If *still* have no schema and a default is configured, try to use that */ + if (!message_desc && strlen(default_message_type)) { + message_desc = pbw_DescriptorPool_FindMessageTypeByName(pbw_pool, + default_message_type); + } + if (display_json_mapping && message_desc) { json_dumper dumper = { .output_string = g_string_new(NULL), @@ -1640,49 +1770,57 @@ dissect_protobuf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data * should be used to free the GString to avoid a leak. */ dissect_protobuf_message(tvb, offset, tvb_reported_length_remaining(tvb, offset), pinfo, - protobuf_tree, message_desc, -1, pinfo->ptype == PT_UDP, &dumper, NULL, NULL); + protobuf_tree, message_desc, + -1, // no hf item + pinfo->ptype == PT_UDP, // is_top_level + &dumper, + NULL, // scope + NULL); // retval DISSECTOR_ASSERT_HINT(json_dumper_finish(&dumper), "Bad json_dumper state"); ti = proto_tree_add_item(tree, proto_protobuf_json_mapping, tvb, 0, -1, ENC_NA); protobuf_json_tree = proto_item_add_subtree(ti, ett_protobuf_json); - json_str = g_string_free(dumper.output_string, FALSE); + json_str = g_string_free(dumper.output_string, false); if (json_str != NULL) { p = json_str; - q = NULL; /* add each line of json to the protobuf_json_tree */ do { - q = strchr(p, '\n'); + char *q = strchr(p, '\n'); if (q != NULL) { *(q++) = '\0'; /* replace the '\n' to '\0' */ } /* else (q == NULL) means this is the last line of the JSON */ proto_tree_add_string_format(protobuf_json_tree, hf_json_mapping_line, tvb, 0, -1, p, "%s", p); p = q; - q = NULL; } while (p); g_free(json_str); } } else { dissect_protobuf_message(tvb, offset, tvb_reported_length_remaining(tvb, offset), pinfo, - protobuf_tree, message_desc, -1, pinfo->ptype == PT_UDP, NULL, NULL, NULL); + protobuf_tree, message_desc, + -1, // no hf item + true, // is_top_level + NULL, // dumper + NULL, // scope + NULL); // retval } return tvb_captured_length(tvb); } -static gboolean +static bool // NOLINTNEXTLINE(misc-no-recursion) -load_all_files_in_dir(PbwDescriptorPool* pool, const gchar* dir_path, unsigned depth) +load_all_files_in_dir(PbwDescriptorPool* pool, const char* dir_path, unsigned depth) { WS_DIR *dir; /* scanned directory */ WS_DIRENT *file; /* current file */ - const gchar *dot; - const gchar *name; /* current file or dir name (without parent dir path) */ - gchar *path; /* sub file or dir path of dir_path */ + const char *dot; + const char *name; /* current file or dir name (without parent dir path) */ + char *path; /* sub file or dir path of dir_path */ if (depth > prefs.gui_max_tree_depth) { - return FALSE; + return false; } if (g_file_test(dir_path, G_FILE_TEST_IS_DIR)) { @@ -1697,13 +1835,13 @@ load_all_files_in_dir(PbwDescriptorPool* pool, const gchar* dir_path, unsigned d if (pbw_load_proto_file(pool, path) != 0) { g_free(path); ws_dir_close(dir); - return FALSE; + return false; } } else { if (!load_all_files_in_dir(pool, path, depth + 1)) { g_free(path); ws_dir_close(dir); - return FALSE; + return false; } } g_free(path); @@ -1711,17 +1849,17 @@ load_all_files_in_dir(PbwDescriptorPool* pool, const gchar* dir_path, unsigned d ws_dir_close(dir); } } - return TRUE; + return true; } /* There might be a lot of errors to be found during parsing .proto files. We buffer the errors first, and print them in one list finally. */ -static wmem_strbuf_t* err_msg_buf = NULL; +static wmem_strbuf_t* err_msg_buf; #define MIN_ERR_STR_BUF_SIZE 512 #define MAX_ERR_STR_BUF_SIZE 1024 static void -buffer_error(const gchar *fmt, ...) +buffer_error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -1759,11 +1897,18 @@ update_protobuf_udp_message_types(void) } static void +update_protobuf_uri_message_types(void) +{ + protobuf_reinit(PREFS_UPDATE_PROTOBUF_URI_MESSAGE_TYPES); +} + + +static void deregister_header_fields(void) { if (dynamic_hf) { /* Deregister all fields */ - for (guint i = 0; i < dynamic_hf_size; i++) { + for (unsigned i = 0; i < dynamic_hf_size; i++) { proto_deregister_field(proto_protobuf, *(dynamic_hf[i].p_id)); g_free(dynamic_hf[i].p_id); /* dynamic_hf[i].name and .abbrev will be freed by proto_add_deregistered_data */ @@ -1814,7 +1959,7 @@ collect_fields(const PbwDescriptor* message, void* userdata) /* add message as field */ hf = g_new0(hf_register_info, 1); - hf->p_id = g_new(gint, 1); + hf->p_id = g_new(int, 1); *(hf->p_id) = -1; hf->hfinfo.name = g_strdup(pbw_Descriptor_name(message)); hf->hfinfo.abbrev = ws_strdup_printf("pbm.%s", pbw_Descriptor_full_name(message)); @@ -1832,7 +1977,7 @@ collect_fields(const PbwDescriptor* message, void* userdata) continue; } hf = g_new0(hf_register_info, 1); - hf->p_id = g_new(gint, 1); + hf->p_id = g_new(int, 1); *(hf->p_id) = -1; hf->hfinfo.name = g_strdup(pbw_FieldDescriptor_name(field_desc)); @@ -1921,7 +2066,7 @@ collect_fields(const PbwDescriptor* message, void* userdata) } static void -update_header_fields(gboolean force_reload) +update_header_fields(bool force_reload) { if (!force_reload && pbf_as_hf && dynamic_hf) { /* If initialized, do nothing. */ @@ -1959,12 +2104,12 @@ update_header_fields(gboolean force_reload) static void protobuf_reinit(int target) { - guint i; + unsigned i; char **source_paths; GSList* it; range_t* udp_port_range; - const gchar* message_type; - gboolean loading_completed = TRUE; + const char* message_type; + bool loading_completed = true; size_t num_proto_paths; if (target & PREFS_UPDATE_PROTOBUF_UDP_MESSAGE_TYPES) { @@ -2004,7 +2149,7 @@ protobuf_reinit(int target) /* Load the files in the global and personal config dirs */ source_paths[0] = get_datafile_path("protobuf"); - source_paths[1] = get_persconffile_path("protobuf", TRUE); + source_paths[1] = get_persconffile_path("protobuf", true); for (i = 0; i < num_protobuf_search_paths; ++i) { source_paths[i + 2] = protobuf_search_paths[i].path; @@ -2018,7 +2163,7 @@ protobuf_reinit(int target) if ((i < 2) || protobuf_search_paths[i - 2].load_all) { if (!load_all_files_in_dir(pbw_pool, source_paths[i], 0)) { buffer_error("Protobuf: Loading .proto files action stopped!\n"); - loading_completed = FALSE; + loading_completed = false; break; /* stop loading when error occurs */ } } @@ -2027,7 +2172,7 @@ protobuf_reinit(int target) g_free(source_paths[0]); g_free(source_paths[1]); g_free(source_paths); - update_header_fields(TRUE); + update_header_fields(true); } /* check if the message types of UDP port exist */ @@ -2064,7 +2209,7 @@ proto_register_protobuf(void) }, { &hf_protobuf_field_number, { "Field Number", "protobuf.field.number", - FT_UINT64, BASE_DEC, NULL, G_GUINT64_CONSTANT(0xFFFFFFFFFFFFFFF8), + FT_UINT64, BASE_DEC, NULL, UINT64_C(0xFFFFFFFFFFFFFFF8), "Field number encoded in varint", HFILL } }, { &hf_protobuf_wire_type, @@ -2114,7 +2259,7 @@ proto_register_protobuf(void) }, { &hf_protobuf_value_bool, { "Bool", "protobuf.field.value.bool", - FT_BOOLEAN, BASE_DEC, NULL, 0x0, + FT_BOOLEAN, BASE_NONE, NULL, 0x0, "Dissect value as bool", HFILL } }, { &hf_protobuf_value_string, @@ -2137,7 +2282,7 @@ proto_register_protobuf(void) } }; - static gint *ett[] = { + static int *ett[] = { &ett_protobuf, &ett_protobuf_message, &ett_protobuf_field, @@ -2145,7 +2290,7 @@ proto_register_protobuf(void) &ett_protobuf_packed_repeated }; - static gint *ett_json[] = { + static int *ett_json[] = { &ett_protobuf_json }; @@ -2167,23 +2312,23 @@ proto_register_protobuf(void) { "protobuf.field.failed_parse_field", PI_MALFORMED, PI_ERROR, "Failed to parse value field", EXPFILL } }, - { &et_protobuf_message_type_not_found, + { &ei_protobuf_message_type_not_found, { "protobuf.field.message_type_not_found", PI_PROTOCOL, PI_WARN, "Failed to find message type of a field", EXPFILL } }, - { &et_protobuf_wire_type_not_support_packed_repeated, + { &ei_protobuf_wire_type_not_support_packed_repeated, { "protobuf.field.wire_type_not_support_packed_repeated", PI_MALFORMED, PI_ERROR, "The wire type does not support protobuf packed repeated field", EXPFILL } }, - { &et_protobuf_failed_parse_packed_repeated_field, + { &ei_protobuf_failed_parse_packed_repeated_field, { "protobuf.field.failed_parse_packed_repeated_field", PI_MALFORMED, PI_ERROR, "Failed to parse packed repeated field", EXPFILL } }, - { &et_protobuf_missing_required_field, + { &ei_protobuf_missing_required_field, { "protobuf.message.missing_required_field", PI_PROTOCOL, PI_WARN, "The required field is not found in message payload", EXPFILL } }, - { &et_protobuf_default_value_error, + { &ei_protobuf_default_value_error, { "protobuf.message.default_value_error", PI_PROTOCOL, PI_WARN, "Parsing default value of a field error", EXPFILL } }, @@ -2208,6 +2353,14 @@ proto_register_protobuf(void) }; uat_t* protobuf_udp_message_types_uat; + static uat_field_t protobuf_uri_message_types_table_columns[] = { + UAT_FLD_CSTRING(protobuf_uri_message_type, uri, "HTTP URI", "URI for HTTP request carrying protobuf contents"), + UAT_FLD_CSTRING(protobuf_uri_message_type, message_type, "Message Type", "Protobuf message type of data on these URIs"), + UAT_END_FIELDS + }; + uat_t* protobuf_uri_message_types_uat; + + proto_protobuf = proto_register_protocol("Protocol Buffers", "ProtoBuf", "protobuf"); proto_protobuf_json_mapping = proto_register_protocol("Protocol Buffers (as JSON Mapping View)", "ProtoBuf_JSON", "protobuf_json"); @@ -2228,7 +2381,7 @@ proto_register_protobuf(void) protobuf_search_paths_uat = uat_new("Protobuf Search Paths", sizeof(protobuf_search_path_t), "protobuf_search_paths", - TRUE, + true, &protobuf_search_paths, &num_protobuf_search_paths, UAT_AFFECTS_DISSECTION | UAT_AFFECTS_FIELDS, @@ -2277,12 +2430,12 @@ proto_register_protobuf(void) " 4) Zero for numeric types.\n" "There are no default values for fields 'repeated' or 'bytes' and 'string' without default value declared.\n" "If the missing field is 'required' in a 'proto2' file, a warning item will be added to the tree.", - &add_default_value, add_default_value_policy_vals, FALSE); + &add_default_value, add_default_value_policy_vals, false); protobuf_udp_message_types_uat = uat_new("Protobuf UDP Message Types", sizeof(protobuf_udp_message_type_t), "protobuf_udp_message_types", - TRUE, + true, &protobuf_udp_message_types, &num_protobuf_udp_message_types, UAT_AFFECTS_DISSECTION | UAT_AFFECTS_FIELDS, @@ -2299,6 +2452,28 @@ proto_register_protobuf(void) "Specify the Protobuf message type of data on certain UDP ports.", protobuf_udp_message_types_uat); + + protobuf_uri_message_types_uat = uat_new("Protobuf URI Message Types", + sizeof(protobuf_uri_mapping_t), + "protobuf_uri_message_types", + true, + &protobuf_uri_message_types, + &num_protobuf_uri_message_types, + UAT_AFFECTS_DISSECTION | UAT_AFFECTS_FIELDS, + NULL, //"ChProtobufURIMessageTypes", + protobuf_uri_message_type_copy_cb, + NULL, + protobuf_uri_message_type_free_cb, + update_protobuf_uri_message_types, + NULL, + protobuf_uri_message_types_table_columns + ); + + prefs_register_uat_preference(protobuf_module, "uri_message_types", "Protobuf URI message types", + "Specify the Protobuf message type of data on certain URIs. N.B., URI may contain '*'", + protobuf_uri_message_types_uat); + + prefs_register_bool_preference(protobuf_module, "display_json_mapping", "Display JSON mapping for Protobuf message", "Specifies that the JSON text of the " @@ -2324,6 +2499,11 @@ proto_register_protobuf(void) "Try to show all possible field types for each undefined field according to wire type.", &show_all_possible_field_types); + prefs_register_string_preference(protobuf_module, "default_type", + "Message type to use if none set", + "Can be useful e.g. if dissector called through media type", + &default_message_type); + prefs_register_static_text_preference(protobuf_module, "field_dissector_table_note", "Subdissector can register itself in \"protobuf_field\" dissector table for parsing" " the value of the field.", @@ -2347,7 +2527,7 @@ proto_reg_handoff_protobuf(void) (old_dissect_bytes_as_string && !dissect_bytes_as_string) || (!old_dissect_bytes_as_string && dissect_bytes_as_string) ); } else if (preload_protos) { - protobuf_dissector_called = TRUE; + protobuf_dissector_called = true; protobuf_reinit(PREFS_UPDATE_ALL); } old_dissect_bytes_as_string = dissect_bytes_as_string; @@ -2357,6 +2537,10 @@ proto_reg_handoff_protobuf(void) dissector_add_string("grpc_message_type", "application/grpc-web+proto", protobuf_handle); dissector_add_string("grpc_message_type", "application/grpc-web-text", protobuf_handle); dissector_add_string("grpc_message_type", "application/grpc-web-text+proto", protobuf_handle); + + dissector_add_string("media_type", "application/x-protobuf", protobuf_handle); + + proto_http = proto_get_id_by_filter_name("http"); } /* |