wireshark/epan/dissectors/packet-protobuf.c

693 lines
27 KiB
C
Raw Normal View History

/* packet-protobuf.c
* Routines for Google Protocol Buffers dissection
* Copyright 2017, Huang Qiangxiong <qiangxiong.huang@qq.com>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/*
* The information used comes from:
* https://developers.google.cn/protocol-buffers/docs/encoding
*
* This protobuf dissector may be invoked by GRPC dissector.
*
* TODO
* Support custom preference settings for embedded messages.
* Dissect message according to '*.proto' files.
*/
#include "config.h"
#include <epan/packet.h>
#include <epan/expert.h>
#include <epan/prefs.h>
#include <epan/strutil.h>
#include <epan/proto_data.h>
#include "packet-protobuf.h"
#include "wsutil/pint.h"
/* converting */
static inline gdouble
protobuf_uint64_to_double(guint64 value) {
union { gdouble f; guint64 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;
float_uint32_union.i = value;
return float_uint32_union.f;
}
VALUE_STRING_ARRAY_GLOBAL_DEF(protobuf_wire_type);
/* Protobuf field type. Must be kept in sync with FieldType of protobuf wire_format_lite.h */
#define protobuf_field_type_VALUE_STRING_LIST(XXX) \
XXX(PROTOBUF_TYPE_NONE, 0, "") \
XXX(PROTOBUF_TYPE_DOUBLE, 1, "double") \
XXX(PROTOBUF_TYPE_FLOAT, 2, "float") \
XXX(PROTOBUF_TYPE_INT64, 3, "int64") \
XXX(PROTOBUF_TYPE_UINT64, 4, "uint64") \
XXX(PROTOBUF_TYPE_INT32, 5, "int32") \
XXX(PROTOBUF_TYPE_FIXED64, 6, "fixed64") \
XXX(PROTOBUF_TYPE_FIXED32, 7, "fixed32") \
XXX(PROTOBUF_TYPE_BOOL, 8, "bool") \
XXX(PROTOBUF_TYPE_STRING, 9, "string") \
XXX(PROTOBUF_TYPE_GROUP, 10, "group(packed_repeated)") \
XXX(PROTOBUF_TYPE_MESSAGE, 11, "message") \
XXX(PROTOBUF_TYPE_BYTES, 12, "bytes") \
XXX(PROTOBUF_TYPE_UINT32, 13, "uint32") \
XXX(PROTOBUF_TYPE_ENUM, 14, "enum") \
XXX(PROTOBUF_TYPE_SFIXED32, 15, "sfixed32") \
XXX(PROTOBUF_TYPE_SFIXED64, 16, "sfixed64") \
XXX(PROTOBUF_TYPE_SINT32, 17, "sint32") \
XXX(PROTOBUF_TYPE_SINT64, 18, "sint64")
#define PROTOBUF_MAX_FIELD_TYPE 18
VALUE_STRING_ENUM(protobuf_field_type);
VALUE_STRING_ARRAY(protobuf_field_type);
/* which field type of each wire type could be */
static int protobuf_wire_to_field_type[6][9] = {
/* PROTOBUF_WIRETYPE_VARINT, 0, "varint") */
{ PROTOBUF_TYPE_INT32, PROTOBUF_TYPE_INT64, PROTOBUF_TYPE_UINT32, PROTOBUF_TYPE_UINT64,
PROTOBUF_TYPE_SINT32, PROTOBUF_TYPE_SINT64, PROTOBUF_TYPE_BOOL, PROTOBUF_TYPE_ENUM,
PROTOBUF_TYPE_NONE },
/* PROTOBUF_WIRETYPE_FIXED64, 1, "64-bit") */
{ PROTOBUF_TYPE_FIXED64, PROTOBUF_TYPE_SFIXED64, PROTOBUF_TYPE_DOUBLE,
PROTOBUF_TYPE_NONE },
/* PROTOBUF_WIRETYPE_LENGTH_DELIMITED, 2, "Length-delimited") */
{ PROTOBUF_TYPE_STRING, PROTOBUF_TYPE_BYTES, PROTOBUF_TYPE_MESSAGE, PROTOBUF_TYPE_GROUP,
PROTOBUF_TYPE_NONE },
/* PROTOBUF_WIRETYPE_START_GROUP, 3, "Start group (deprecated)") */
{ PROTOBUF_TYPE_NONE },
/* PROTOBUF_WIRETYPE_END_GROUP, 4, "End group (deprecated)") */
{ PROTOBUF_TYPE_NONE },
/* PROTOBUF_WIRETYPE_FIXED32, 5, "32-bit") */
{ PROTOBUF_TYPE_FIXED32, PROTOBUF_TYPE_SINT32, PROTOBUF_TYPE_FLOAT,
PROTOBUF_TYPE_NONE }
};
void proto_register_protobuf(void);
void proto_reg_handoff_protobuf(void);
static int proto_protobuf = -1;
/* field tag */
static int hf_protobuf_field_number = -1;
static int hf_protobuf_wire_type = -1;
/* 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;
/* 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;
/* trees */
static int ett_protobuf = -1;
static int ett_protobuf_field = -1;
static int ett_protobuf_value = -1;
static int ett_protobuf_packed_repeated = -1;
/* preferences */
gboolean try_dissect_as_string = FALSE;
gboolean try_dissect_as_repeated = FALSE;
gboolean show_all_possible_field_types = FALSE;
static dissector_handle_t protobuf_handle;
/* Stuff for generation/handling of fields */
typedef struct {
gchar* call_path; /* equals to grpc :path, for example: "/helloworld.Greeter/SayHello" */
guint direction_type; /* 0: request, 1: response */
guint field_type; /* type of field, refer to protobuf_field_type vals. */
gchar* field_name; /* field name, will display in tree */
guint field_number; /* field number in .proto file*/
} protobuf_field_t;
static GHashTable* protobuf_fields_hash = NULL;
/* get field info according to call_path, direction_type and field_number
* user should free the returned struct. */
static protobuf_field_t*
protobuf_find_field_info(const gchar* call_path_direction_type, int field_number)
{
gchar* key = wmem_strdup_printf(wmem_packet_scope(), "%s,%d", call_path_direction_type, field_number);
protobuf_field_t* p = (protobuf_field_t*)g_hash_table_lookup(protobuf_fields_hash, key);
return p;
}
/* 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
* one of the signed types, the resulting varint uses ZigZag encoding, which is much more efficient.
* ZigZag encoding maps signed integers to unsigned integers so that numbers with a small absolute
* 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);
}
/* sint64 encoded using (n << 1) ^ (n >> 63) */
static gint64
sint64_decode(guint64 sint64) {
return (sint64 >> 1) ^ ((gint64)sint64 << 63 >> 63);
}
/* declare first because it will be called by dissect_packed_repeated_field_values */
static void
protobuf_try_dissect_field_value_on_multi_types(proto_tree *value_tree, tvbuff_t *tvb, guint offset, guint length,
packet_info *pinfo, void *data, proto_item *ti_field, int wire_type, int* field_types, const guint64 value);
/* format tag + varint + varint + varint ...
return consumed bytes */
static guint
dissect_packed_repeated_field_values(proto_tree *value_tree, tvbuff_t *tvb, guint start, guint length, packet_info *pinfo,
void *data, proto_item *ti_field, int wire_type, int* field_types, const gchar* prepend_text)
{
guint64 sub_value;
guint sub_value_length;
guint offset = start;
guint max_offset = offset + length;
int i;
for (i = 0; field_types[i] != PROTOBUF_TYPE_NONE; ++i) {
if (field_types[i] == PROTOBUF_TYPE_GROUP) {
return 0; /* prevent dead loop */
}
}
proto_item_append_text(ti_field, "%s Repeated: [", (prepend_text ? prepend_text : ""));
proto_item *ti = proto_tree_add_item(value_tree, hf_protobuf_value_repeated, tvb, start, length, ENC_NA);
proto_tree *subtree = proto_item_add_subtree(ti, ett_protobuf_packed_repeated);
/* add varints into the packed-repeated subtree */
while (offset < max_offset) {
sub_value_length = tvb_get_varint(tvb, offset, max_offset - offset, &sub_value);
if (sub_value_length == 0) {
/* not a valid packed repeated field */
return 0;
}
protobuf_try_dissect_field_value_on_multi_types(subtree, tvb, offset, sub_value_length, pinfo, data,
ti_field, wire_type, field_types, sub_value);
offset += sub_value_length;
}
proto_item_append_text(ti_field, "]");
return length;
}
/* dissect field value based on a specific type. if pfield_info is given, we use pfield_info->field_type
* insteadof field_type parameter and we will show field_name.
*/
static void
protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, guint offset, guint length, packet_info *pinfo,
void *data, proto_item *ti_field, int wire_type, int field_type, const guint64 value, protobuf_field_t *pfield_info)
{
gdouble double_value;
gfloat float_value;
gint64 int64_value;
gint32 int32_value;
guint8* buf;
gboolean add_datatype = TRUE;
gchar* prepend_text;
if (pfield_info && pfield_info->field_name && strlen(pfield_info->field_name) >0) {
field_type = pfield_info->field_type;
prepend_text = wmem_strdup_printf(wmem_packet_scope(), ", %s =", pfield_info->field_name);
} else {
prepend_text = ",";
}
switch (field_type)
{
case PROTOBUF_TYPE_DOUBLE:
double_value = protobuf_uint64_to_double(value);
proto_tree_add_double(value_tree, hf_protobuf_value_double, tvb, offset, length, double_value);
proto_item_append_text(ti_field, "%s %lf", prepend_text, double_value);
break;
case PROTOBUF_TYPE_FLOAT:
float_value = protobuf_uint32_to_float((guint32) 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);
break;
case PROTOBUF_TYPE_INT64:
case PROTOBUF_TYPE_SFIXED64:
int64_value = (gint64) value;
proto_tree_add_int64(value_tree, hf_protobuf_value_int64, tvb, offset, length, int64_value);
proto_item_append_text(ti_field, "%s %" G_GINT64_MODIFIER "d", prepend_text, int64_value);
break;
case PROTOBUF_TYPE_UINT64:
case PROTOBUF_TYPE_FIXED64: /* same as UINT64 */
proto_tree_add_uint64(value_tree, hf_protobuf_value_uint64, tvb, offset, length, value);
proto_item_append_text(ti_field, "%s %" G_GINT64_MODIFIER "u", prepend_text, value);
break;
case PROTOBUF_TYPE_INT32:
case PROTOBUF_TYPE_SFIXED32:
int32_value = (gint32) 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);
break;
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_item_append_text(ti_field, "%s %s", prepend_text, value ? "true" : "false");
break;
case PROTOBUF_TYPE_STRING:
buf = (guint8*) wmem_alloc(wmem_packet_scope(), length + 1);
tvb_get_nstringz0(tvb, offset, length + 1, buf);
proto_tree_add_string(value_tree, hf_protobuf_value_string, tvb, offset, length, buf);
proto_item_append_text(ti_field, "%s %s", prepend_text, buf);
break;
case PROTOBUF_TYPE_GROUP:
if (try_dissect_as_repeated) {
int field_types[] = { PROTOBUF_TYPE_UINT64, PROTOBUF_TYPE_NONE };
dissect_packed_repeated_field_values(value_tree, tvb, offset, length, pinfo, data, ti_field, wire_type,
field_types, prepend_text);
}
add_datatype = FALSE;
break;
case PROTOBUF_TYPE_MESSAGE:
/* may call dissect_protobuf(tvb, pinfo, value_tree, data); */
add_datatype = FALSE;
break;
case PROTOBUF_TYPE_BYTES:
/* do nothing */
add_datatype = FALSE;
break;
case PROTOBUF_TYPE_ENUM:
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 %lu", prepend_text, value);
break;
case PROTOBUF_TYPE_SINT32:
int32_value = sint32_decode((guint32)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);
break;
case PROTOBUF_TYPE_SINT64:
int64_value = sint64_decode(value);
proto_tree_add_int64(value_tree, hf_protobuf_value_int64, tvb, offset, length, int64_value);
proto_item_append_text(ti_field, "%s %" G_GINT64_MODIFIER "d", prepend_text, int64_value);
break;
default:
/* ignore unknown field type */
add_datatype = FALSE;
break;
}
if (add_datatype)
proto_item_append_text(ti_field, " (%s)", val_to_str(field_type, protobuf_field_type, "Unknown type (%d)"));
}
/* add all possible values according to field types. */
static void
protobuf_try_dissect_field_value_on_multi_types(proto_tree *value_tree, tvbuff_t *tvb, guint offset, guint length,
packet_info *pinfo, void *data, proto_item *ti_field, int wire_type, int* field_types, const guint64 value)
{
int i;
for (i = 0; field_types[i] != PROTOBUF_TYPE_NONE; ++i) {
protobuf_dissect_field_value(value_tree, tvb, offset, length, pinfo, data, ti_field, wire_type, field_types[i],
value, NULL);
}
}
static gboolean
dissect_one_protobuf_field(tvbuff_t *tvb, guint* offset, guint maxlen, packet_info *pinfo,
proto_tree *protobuf_tree, void *data)
{
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 */
proto_item *ti_field, *ti_wire;
proto_item *ti_value;
proto_tree *field_tree;
proto_tree *value_tree;
protobuf_field_t* pfield_info = NULL;
/* 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
* the following value.
* Format of protobuf is:
* protobuf field -> tag value
* tag -> (field_number << 3) | wire_type (the last three bits of the number store the wire type)
* value -> according to wiret_type, value may be
* - varint (int32, int64, uint32, uint64, sint32, sint64, bool, enum),
* - 64-bit number (fixed64, sfixed64, double)
* - Length-delimited (string, bytes, embedded messages, packed repeated fields)
* - deprecated 'Start group' or 'End group' (we stop dissecting when encountered them)
* - 32-bit (fixed32, sfixed32, float)
* All numbers in protobuf are stored in little-endian byte order.
*/
field_tree = proto_tree_add_subtree(protobuf_tree, tvb, *offset, 0, ett_protobuf_field, &ti_field, "Field");
/* parsing Tag */
tag_length = tvb_get_varint(tvb, *offset, maxlen, &tag_value);
if (tag_length == 0) { /* not found a valid varint */
expert_add_info(pinfo, ti_field, &ei_protobuf_failed_parse_tag);
return FALSE;
}
proto_tree_add_item_ret_uint64(field_tree, hf_protobuf_field_number, tvb, *offset, tag_length, ENC_LITTLE_ENDIAN|ENC_VARINT_PROTOBUF, &field_number);
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);
(*offset) += tag_length;
proto_item_append_text(ti_field, "[%" G_GINT64_MODIFIER "u]", field_number);
/* determine value_length, uint of numeric value and maybe value_length_size according to wire_type */
switch (wire_type)
{
case PROTOBUF_WIRETYPE_VARINT: /* varint, format: tag + varint */
/* get value length and real value */
value_length = tvb_get_varint(tvb, *offset, maxlen - tag_length, &value_uint64);
if (value_length == 0) {
expert_add_info(pinfo, ti_wire, &ei_protobuf_failed_parse_field);
return FALSE;
}
break;
case PROTOBUF_WIRETYPE_FIXED64: /* fixed 64-bit type, format: tag + 64-bit-value */
/* get value length and real value */
value_length = 8;
value_uint64 = tvb_get_letoh64(tvb, *offset);
break;
case PROTOBUF_WIRETYPE_FIXED32: /* fixed 32-bit type, format: tag + 32-bit-value */
value_length = 4;
value_uint64 = tvb_get_letohl(tvb, *offset);
break;
case PROTOBUF_WIRETYPE_LENGTH_DELIMITED: /* Length-delimited, format: tag + length(varint) + bytes_value */
/* this time value_uint64 is the length of following value bytes */
value_length_size = tvb_get_varint(tvb, *offset, maxlen - tag_length, &value_uint64);
if (value_length_size == 0) {
expert_add_info(pinfo, ti_field, &ei_protobuf_failed_parse_length_delimited_field);
return FALSE;
}
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;
break;
default:
expert_add_info(pinfo, ti_wire, &ei_protobuf_wire_type_invalid);
return FALSE;
}
proto_item_set_len(ti_field, tag_length + value_length_size + value_length);
/* add value as bytes first */
ti_value = proto_tree_add_item(field_tree, hf_protobuf_value_data, tvb, *offset, value_length, ENC_NA);
/* add value subtree. we add uint value for numeric field or string for length-delimited at least. */
value_tree = proto_item_add_subtree(ti_value, ett_protobuf_value);
/* try to find field_info first */
if (data) {
const gchar* message_info = (const gchar*)data;
/* find call_path + request or reponse part from format:
* http2_content_type "," http2_path "," ("request" / "response")
* Acording to grpc wire format guide, it will be:
* "application/grpc" [("+proto" / "+json" / {custom})] "," "/" service-name "/" method-name "/" "," ("request" / "response")
* For example:
* application/grpc,/helloworld.Greeter/SayHello,request
*/
message_info = strchr(message_info, ',');
if (message_info) {
message_info++;
}
pfield_info = protobuf_find_field_info(message_info, (gint) field_number);
}
if (pfield_info) {
protobuf_dissect_field_value(value_tree, tvb, *offset, value_length, pinfo, data, ti_field, wire_type,
pfield_info->field_type, value_uint64, pfield_info);
} else {
if (show_all_possible_field_types) {
/* try dissect every possible field type */
protobuf_try_dissect_field_value_on_multi_types(value_tree, tvb, *offset, value_length, pinfo, data,
ti_field, wire_type, protobuf_wire_to_field_type[wire_type], value_uint64);
} else {
int field_type = (wire_type == PROTOBUF_WIRETYPE_LENGTH_DELIMITED)
/* print string at least for length-delimited */
? (try_dissect_as_string ? PROTOBUF_TYPE_STRING : PROTOBUF_TYPE_NONE)
/* use uint32 or uint64 */
: (value_uint64 <= 0xFFFFFFFF ? PROTOBUF_TYPE_UINT32 : PROTOBUF_TYPE_UINT64);
int field_types[] = { field_type, PROTOBUF_TYPE_NONE };
protobuf_try_dissect_field_value_on_multi_types(value_tree, tvb, *offset, value_length, pinfo, data,
ti_field, wire_type, field_types, value_uint64);
}
}
(*offset) += value_length;
return TRUE;
}
static int
dissect_protobuf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
proto_item *ti;
proto_tree *protobuf_tree;
guint offset = 0;
/* may set col_set_str(pinfo->cinfo, COL_PROTOCOL, "PROTOBUF"); */
col_append_str(pinfo->cinfo, COL_INFO, " (PROTOBUF)");
ti = proto_tree_add_item(tree, proto_protobuf, tvb, 0, -1, ENC_NA);
protobuf_tree = proto_item_add_subtree(ti, ett_protobuf);
if (data) {
/* append to proto item */
proto_item_append_text(ti, ": %s", (const gchar*)data);
}
/* each time we dissec one protobuf field. */
while (tvb_reported_length_remaining(tvb, offset) > 0)
{
if (!dissect_one_protobuf_field(tvb, &offset, tvb_reported_length_remaining(tvb, offset), pinfo, protobuf_tree, data))
break;
}
return tvb_captured_length(tvb);
}
void
proto_register_protobuf(void)
{
static hf_register_info hf[] = {
{ &hf_protobuf_field_number,
{ "Field Number", "protobuf.field.number",
FT_UINT64, BASE_DEC, NULL, G_GUINT64_CONSTANT(0xFFFFFFFFFFFFFFF8),
"Field number encoded in varint", HFILL }
},
{ &hf_protobuf_wire_type,
{ "Wire Type", "protobuf.field.wiretype",
FT_UINT8, BASE_DEC, VALS(protobuf_wire_type), 0x07,
"The Wire Type of the field.", HFILL }
},
{ &hf_protobuf_value_length,
{ "Value Length", "protobuf.field.value.length",
FT_UINT64, BASE_DEC, NULL, 0x0,
"The length of length-delimited field value.", HFILL }
},
{ &hf_protobuf_value_data,
{ "Value", "protobuf.field.value",
FT_BYTES, BASE_NONE, NULL, 0x0,
"The wire type determines value format", HFILL }
},
{ &hf_protobuf_value_double,
{ "Double", "protobuf.field.value.double",
FT_DOUBLE, BASE_NONE, NULL, 0x0,
"Dissect value as double", HFILL }
},
{ &hf_protobuf_value_float,
{ "Float", "protobuf.field.value.float",
FT_FLOAT, BASE_NONE, NULL, 0x0,
"Dissect value as float", HFILL }
},
{ &hf_protobuf_value_int64,
{ "Int64", "protobuf.field.value.int64",
FT_INT64, BASE_DEC, NULL, 0x0,
"Dissect value as int64", HFILL }
},
{ &hf_protobuf_value_uint64,
{ "Uint64", "protobuf.field.value.uint64",
FT_UINT64, BASE_DEC, NULL, 0x0,
"Dissect value as uint64", HFILL }
},
{ &hf_protobuf_value_int32,
{ "Int32", "protobuf.field.value.int32",
FT_INT32, BASE_DEC, NULL, 0x0,
"Dissect value as int32", HFILL }
},
{ &hf_protobuf_value_uint32,
{ "Uint32", "protobuf.field.value.uint32",
FT_UINT32, BASE_DEC, NULL, 0x0,
"Dissect value as uint32", HFILL }
},
{ &hf_protobuf_value_bool,
{ "Bool", "protobuf.field.value.bool",
FT_BOOLEAN, BASE_DEC, NULL, 0x0,
"Dissect value as bool", HFILL }
},
{ &hf_protobuf_value_string,
{ "String", "protobuf.field.value.string",
FT_STRING, BASE_NONE, NULL, 0x0,
"Dissect value as string", HFILL }
},
{ &hf_protobuf_value_repeated,
{ "Repeated", "protobuf.field.value.repeated",
FT_BYTES, BASE_NONE, NULL, 0x0,
"Dissect value as repeated", HFILL }
}
};
static gint *ett[] = {
&ett_protobuf,
&ett_protobuf_field,
&ett_protobuf_value,
&ett_protobuf_packed_repeated
};
/* Setup protocol expert items */
static ei_register_info ei[] = {
{ &ei_protobuf_failed_parse_tag,
{ "protobuf.failed_parse_tag", PI_MALFORMED, PI_ERROR,
"Failed to parse tag field", EXPFILL }
},
{ &ei_protobuf_wire_type_invalid,
{ "protobuf.field.wiretype.invalid", PI_PROTOCOL, PI_WARN,
"Unknown or unsupported wiretype", EXPFILL }
},
{ &ei_protobuf_failed_parse_length_delimited_field,
{ "protobuf.field.failed_parse_length_delimited_field", PI_MALFORMED, PI_ERROR,
"Failed to parse length delimited field", EXPFILL }
},
{ &ei_protobuf_failed_parse_field,
{ "protobuf.field.failed_parse_field", PI_MALFORMED, PI_ERROR,
"Failed to parse value field", EXPFILL }
},
};
module_t *protobuf_module;
expert_module_t *expert_protobuf;
proto_protobuf = proto_register_protocol("Protocol Buffers", "ProtoBuf", "protobuf");
proto_register_field_array(proto_protobuf, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
protobuf_module = prefs_register_protocol(proto_protobuf, NULL);
prefs_register_bool_preference(protobuf_module, "try_dissect_all_length_delimited_field_as_string",
"Try to dissect all length-delimited field as string.",
"Try to dissect all length-delimited field as string.",
&try_dissect_as_string);
prefs_register_bool_preference(protobuf_module, "try_dissect_length_delimited_field_as_repeated",
"Try to dissect length-delimited field as repeated.",
"Try to dissect length-delimited field as repeated.",
&try_dissect_as_repeated);
prefs_register_bool_preference(protobuf_module, "show_all_possible_field_types",
"Try to show all possible field types for each field.",
"Try to show all possible field types for each field according to wire type.",
&show_all_possible_field_types);
expert_protobuf = expert_register_protocol(proto_protobuf);
expert_register_field_array(expert_protobuf, ei, array_length(ei));
protobuf_handle = register_dissector("protobuf", dissect_protobuf, proto_protobuf);
}
void
proto_reg_handoff_protobuf(void)
{
dissector_add_string("grpc_message_type", "application/grpc", protobuf_handle);
dissector_add_string("grpc_message_type", "application/grpc+proto", protobuf_handle);
}
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/