/* packet-protobuf.c * Routines for Google Protocol Buffers dissection * Copyright 2017, Huang Qiangxiong * * Wireshark - Network traffic analyzer * By Gerald Combs * 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 #include #include #include #include #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 %u", prepend_text, (guint32)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: */