Coverage Report

Created: 2026-01-02 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-protobuf.c
Line
Count
Source
1
/* packet-protobuf.c
2
 * Routines for Google Protocol Buffers dissection
3
 * Copyright 2017-2022, Huang Qiangxiong <qiangxiong.huang@qq.com>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
/*
13
 * The information used comes from:
14
 * https://developers.google.com/protocol-buffers/docs/encoding
15
 *
16
 * This protobuf dissector may be invoked by GRPC dissector or other dissectors.
17
 * Other dissectors can give protobuf message type info by the data argument or private_table["pb_msg_type"]
18
 * before call protobuf dissector.
19
 * For GRPC dissector the data argument format is:
20
 *    "application/grpc" ["+proto"] "," "/" service-name "/" method-name "," ("request" / "response")
21
 * For example:
22
 *    application/grpc,/helloworld.Greeter/SayHello,request
23
 * In this format, we will try to get real protobuf message type by method (service-name.method-name)
24
 * and in/out type (request / response).
25
 * For other dissectors can specifies message type directly, like:
26
 *    "message," message_type_name
27
 * For example:
28
 *    message,helloworld.HelloRequest      (helloworld is package, HelloRequest is message type)
29
 */
30
31
#include "config.h"
32
33
#include <epan/packet.h>
34
#include <epan/expert.h>
35
#include <epan/prefs.h>
36
#include <epan/uat.h>
37
#include <epan/strutil.h>
38
#include <epan/proto_data.h>
39
#include <wsutil/filesystem.h>
40
#include <wsutil/file_util.h>
41
#include <wsutil/json_dumper.h>
42
#include <wsutil/pint.h>
43
#include <epan/ws_printf.h>
44
#include <wsutil/report_message.h>
45
46
#include "protobuf-helper.h"
47
#include "epan/dissectors/packet-http.h"
48
49
#define protobuf_wire_type_VALUE_STRING_LIST(XXX)    \
50
    XXX(PROTOBUF_WIRETYPE_VARINT, 0, "varint")  \
51
    XXX(PROTOBUF_WIRETYPE_FIXED64, 1, "64-bit")   \
52
    XXX(PROTOBUF_WIRETYPE_LENGTH_DELIMITED, 2, "Length-delimited") \
53
    XXX(PROTOBUF_WIRETYPE_START_GROUP, 3, "Start group (deprecated)") \
54
    XXX(PROTOBUF_WIRETYPE_END_GROUP, 4, "End group (deprecated)") \
55
    XXX(PROTOBUF_WIRETYPE_FIXED32, 5, "32-bit")
56
57
VALUE_STRING_ENUM(protobuf_wire_type);
58
59
60
/* converting */
61
static inline double
62
0
protobuf_uint64_to_double(uint64_t value) {
63
0
    union { double f; uint64_t i; } double_uint64_union;
64
65
0
    double_uint64_union.i = value;
66
0
    return double_uint64_union.f;
67
0
}
68
69
static inline float
70
0
protobuf_uint32_to_float(uint32_t value) {
71
0
    union { float f; uint32_t i; } float_uint32_union;
72
73
0
    float_uint32_union.i = value;
74
0
    return float_uint32_union.f;
75
0
}
76
77
static VALUE_STRING_ARRAY_GLOBAL_DEF(protobuf_wire_type);
78
79
/* which field type of each wire type could be */
80
static int protobuf_wire_to_field_type[6][9] = {
81
    /* PROTOBUF_WIRETYPE_VARINT, 0, "varint") */
82
    { PROTOBUF_TYPE_INT32, PROTOBUF_TYPE_INT64, PROTOBUF_TYPE_UINT32, PROTOBUF_TYPE_UINT64,
83
      PROTOBUF_TYPE_SINT32, PROTOBUF_TYPE_SINT64, PROTOBUF_TYPE_BOOL, PROTOBUF_TYPE_ENUM,
84
      PROTOBUF_TYPE_NONE },
85
86
    /* PROTOBUF_WIRETYPE_FIXED64, 1, "64-bit")   */
87
    { PROTOBUF_TYPE_FIXED64, PROTOBUF_TYPE_SFIXED64, PROTOBUF_TYPE_DOUBLE,
88
      PROTOBUF_TYPE_NONE },
89
90
    /* PROTOBUF_WIRETYPE_LENGTH_DELIMITED, 2, "Length-delimited") */
91
    { PROTOBUF_TYPE_STRING, PROTOBUF_TYPE_BYTES, PROTOBUF_TYPE_MESSAGE, PROTOBUF_TYPE_GROUP,
92
      PROTOBUF_TYPE_NONE },
93
94
    /* PROTOBUF_WIRETYPE_START_GROUP, 3, "Start group (deprecated)") */
95
    { PROTOBUF_TYPE_NONE },
96
97
    /* PROTOBUF_WIRETYPE_END_GROUP, 4, "End group (deprecated)") */
98
    { PROTOBUF_TYPE_NONE },
99
100
    /* PROTOBUF_WIRETYPE_FIXED32, 5, "32-bit") */
101
    { PROTOBUF_TYPE_FIXED32, PROTOBUF_TYPE_SINT32, PROTOBUF_TYPE_FLOAT,
102
      PROTOBUF_TYPE_NONE }
103
};
104
105
void proto_register_protobuf(void);
106
void proto_reg_handoff_protobuf(void);
107
108
14
#define PREFS_UPDATE_PROTOBUF_SEARCH_PATHS            1
109
56
#define PREFS_UPDATE_PROTOBUF_UDP_MESSAGE_TYPES       2
110
14
#define PREFS_UPDATE_PROTOBUF_URI_MESSAGE_TYPES       3
111
0
#define PREFS_UPDATE_ALL   (PREFS_UPDATE_PROTOBUF_SEARCH_PATHS | PREFS_UPDATE_PROTOBUF_UDP_MESSAGE_TYPES | PREFS_UPDATE_PROTOBUF_URI_MESSAGE_TYPES)
112
113
static void protobuf_reinit(int target);
114
115
static int proto_protobuf;
116
static int proto_protobuf_json_mapping;
117
118
static bool protobuf_dissector_called;
119
120
/* information get from *.proto files */
121
static int hf_protobuf_message_name;
122
static int hf_protobuf_field_name;
123
static int hf_protobuf_field_type;
124
125
/* field tag */
126
static int hf_protobuf_field_number;
127
static int hf_protobuf_wire_type;
128
129
/* field value */
130
static int hf_protobuf_value_length; /* only Length-delimited field has */
131
static int hf_protobuf_value_data;
132
static int hf_protobuf_value_double;
133
static int hf_protobuf_value_float;
134
static int hf_protobuf_value_int64;
135
static int hf_protobuf_value_uint64;
136
static int hf_protobuf_value_int32;
137
static int hf_protobuf_value_uint32;
138
static int hf_protobuf_value_bool;
139
static int hf_protobuf_value_string;
140
static int hf_protobuf_value_repeated;
141
static int hf_json_mapping_line;
142
143
/* expert */
144
static expert_field ei_protobuf_failed_parse_tag;
145
static expert_field ei_protobuf_failed_parse_length_delimited_field;
146
static expert_field ei_protobuf_failed_parse_field;
147
static expert_field ei_protobuf_wire_type_invalid;
148
static expert_field ei_protobuf_message_type_not_found;
149
static expert_field ei_protobuf_wire_type_not_support_packed_repeated;
150
static expert_field ei_protobuf_failed_parse_packed_repeated_field;
151
static expert_field ei_protobuf_missing_required_field;
152
static expert_field ei_protobuf_default_value_error;
153
154
/* trees */
155
static int ett_protobuf;
156
static int ett_protobuf_message;
157
static int ett_protobuf_field;
158
static int ett_protobuf_value;
159
static int ett_protobuf_packed_repeated;
160
static int ett_protobuf_json;
161
162
/* preferences */
163
static bool try_dissect_as_string;
164
static bool show_all_possible_field_types;
165
static bool dissect_bytes_as_string;
166
static bool old_dissect_bytes_as_string;
167
static bool show_details;
168
static bool pbf_as_hf; /* dissect protobuf fields as header fields of wireshark */
169
static bool preload_protos;
170
/* Show protobuf as JSON similar to https://developers.google.com/protocol-buffers/docs/proto3#json */
171
static bool display_json_mapping;
172
static bool use_utc_fmt;
173
static const char* default_message_type = "";
174
175
176
#define add_default_value_policy_vals_ENUM_VAL_T_LIST(XXX) \
177
14
    XXX(ADD_DEFAULT_VALUE_NONE,      0, "none", "None") \
178
14
    XXX(ADD_DEFAULT_VALUE_DECLARED,  1, "decl", "Only Explicitly-Declared (proto2)") \
179
14
    XXX(ADD_DEFAULT_VALUE_ENUM_BOOL, 2, "enbl", "Explicitly-Declared, ENUM and BOOL") \
180
14
    XXX(ADD_DEFAULT_VALUE_ALL,       3, "all",  "All")
181
182
typedef ENUM_VAL_T_ENUM(add_default_value_policy_vals) add_default_value_policy_t;
183
184
static int add_default_value = (int) ADD_DEFAULT_VALUE_NONE;
185
186
/* dynamic wireshark header fields for protobuf fields */
187
static hf_register_info *dynamic_hf;
188
static unsigned dynamic_hf_size;
189
/* the key is full name of protobuf fields, the value is header field id */
190
static GHashTable *pbf_hf_hash;
191
192
/* Protobuf field value subdissector table list.
193
 * Only valid for the value of PROTOBUF_TYPE_BYTES or PROTOBUF_TYPE_STRING fields.
194
 */
195
static dissector_table_t protobuf_field_subdissector_table;
196
197
static dissector_handle_t protobuf_handle;
198
199
/* store varint tvb info */
200
typedef struct {
201
    unsigned offset;
202
    unsigned length;
203
    uint64_t value;
204
} protobuf_varint_tvb_info_t;
205
206
static PbwDescriptorPool* pbw_pool;
207
208
/* protobuf source files search paths */
209
typedef struct {
210
    char* path; /* protobuf source files searching directory path */
211
    bool load_all; /* load all *.proto files in this directory and its sub directories */
212
} protobuf_search_path_t;
213
214
static protobuf_search_path_t* protobuf_search_paths;
215
static unsigned num_protobuf_search_paths;
216
217
int proto_http;
218
219
static void *
220
protobuf_search_paths_copy_cb(void* n, const void* o, size_t siz _U_)
221
0
{
222
0
    protobuf_search_path_t* new_rec = (protobuf_search_path_t*)n;
223
0
    const protobuf_search_path_t* old_rec = (const protobuf_search_path_t*)o;
224
225
    /* copy interval values like int */
226
0
    memcpy(new_rec, old_rec, sizeof(protobuf_search_path_t));
227
228
0
    if (old_rec->path)
229
0
        new_rec->path = g_strdup(old_rec->path);
230
231
0
    return new_rec;
232
0
}
233
234
static void
235
protobuf_search_paths_free_cb(void*r)
236
0
{
237
0
    protobuf_search_path_t* rec = (protobuf_search_path_t*)r;
238
239
0
    g_free(rec->path);
240
0
}
241
242
UAT_DIRECTORYNAME_CB_DEF(protobuf_search_paths, path, protobuf_search_path_t)
243
0
UAT_BOOL_CB_DEF(protobuf_search_paths, load_all, protobuf_search_path_t)
244
245
246
247
/* The protobuf message type of the data on certain udp ports */
248
typedef struct {
249
    range_t  *udp_port_range; /* dissect data on these udp ports as protobuf */
250
    char     *message_type; /* protobuf message type of data on these udp ports */
251
} protobuf_udp_message_type_t;
252
253
static protobuf_udp_message_type_t* protobuf_udp_message_types;
254
static unsigned num_protobuf_udp_message_types;
255
256
static void *
257
protobuf_udp_message_types_copy_cb(void* n, const void* o, size_t siz _U_)
258
0
{
259
0
    protobuf_udp_message_type_t* new_rec = (protobuf_udp_message_type_t*)n;
260
0
    const protobuf_udp_message_type_t* old_rec = (const protobuf_udp_message_type_t*)o;
261
262
    /* copy interval values like int */
263
0
    memcpy(new_rec, old_rec, sizeof(protobuf_udp_message_type_t));
264
265
0
    if (old_rec->udp_port_range)
266
0
        new_rec->udp_port_range = range_copy(NULL, old_rec->udp_port_range);
267
0
    if (old_rec->message_type)
268
0
        new_rec->message_type = g_strdup(old_rec->message_type);
269
270
0
    return new_rec;
271
0
}
272
273
static bool
274
protobuf_udp_message_types_update_cb(void *r, char **err)
275
0
{
276
0
    protobuf_udp_message_type_t* rec = (protobuf_udp_message_type_t*)r;
277
0
    static range_t *empty;
278
279
0
    empty = range_empty(NULL);
280
0
    if (ranges_are_equal(rec->udp_port_range, empty)) {
281
0
        *err = g_strdup("Must specify UDP port(s) (like 8000 or 8000,8008-8088)");
282
0
        wmem_free(NULL, empty);
283
0
        return false;
284
0
    }
285
286
0
    wmem_free(NULL, empty);
287
0
    return true;
288
0
}
289
290
static void
291
protobuf_udp_message_types_free_cb(void*r)
292
0
{
293
0
    protobuf_udp_message_type_t* rec = (protobuf_udp_message_type_t*)r;
294
295
0
    wmem_free(NULL, rec->udp_port_range);
296
0
    g_free(rec->message_type);
297
0
}
298
299
0
UAT_RANGE_CB_DEF(protobuf_udp_message_types, udp_port_range, protobuf_udp_message_type_t)
Unexecuted instantiation: packet-protobuf.c:protobuf_udp_message_types_udp_port_range_set_cb
Unexecuted instantiation: packet-protobuf.c:protobuf_udp_message_types_udp_port_range_tostr_cb
300
UAT_CSTRING_CB_DEF(protobuf_udp_message_types, message_type, protobuf_udp_message_type_t)
301
302
static GSList* old_udp_port_ranges;
303
304
305
306
/* The protobuf message type associated with a request URI */
307
typedef struct {
308
    char     *uri;          /* URI appearing in HTTP message */
309
    char     *message_type; /* associated protobuf message type */
310
} protobuf_uri_mapping_t;
311
312
static protobuf_uri_mapping_t* protobuf_uri_message_types;
313
static unsigned num_protobuf_uri_message_types;
314
315
static void *
316
protobuf_uri_message_type_copy_cb(void* n, const void* o, size_t siz _U_)
317
0
{
318
0
    protobuf_uri_mapping_t* new_rec = (protobuf_uri_mapping_t*)n;
319
0
    const protobuf_uri_mapping_t* old_rec = (const protobuf_uri_mapping_t*)o;
320
321
0
    if (old_rec->uri)
322
0
        new_rec->uri = g_strdup(old_rec->uri);
323
0
    if (old_rec->message_type)
324
0
        new_rec->message_type = g_strdup(old_rec->message_type);
325
326
0
    return new_rec;
327
0
}
328
329
static void
330
protobuf_uri_message_type_free_cb(void*r)
331
0
{
332
0
    protobuf_uri_mapping_t* rec = (protobuf_uri_mapping_t*)r;
333
334
0
    g_free(rec->uri);
335
0
    g_free(rec->message_type);
336
0
}
337
338
UAT_CSTRING_CB_DEF(protobuf_uri_message_type, uri,          protobuf_uri_mapping_t)
339
UAT_CSTRING_CB_DEF(protobuf_uri_message_type, message_type, protobuf_uri_mapping_t)
340
341
342
343
/* If you use int32 or int64 as the type for a negative number, the resulting varint is always
344
 * ten bytes long - it is, effectively, treated like a very large unsigned integer. If you use
345
 * one of the signed types, the resulting varint uses ZigZag encoding, which is much more efficient.
346
 * ZigZag encoding maps signed integers to unsigned integers so that numbers with a small absolute
347
 * value (for instance, -1) have a small varint encoded value too. (refers to protobuf spec)
348
 *      sint32 encoded using   (n << 1) ^ (n >> 31)
349
 */
350
static int32_t
351
0
sint32_decode(uint32_t sint32) {
352
0
    return (sint32 >> 1) ^ ((int32_t)sint32 << 31 >> 31);
353
0
}
354
355
/* sint64 encoded using   (n << 1) ^ (n >> 63) */
356
static int64_t
357
0
sint64_decode(uint64_t sint64) {
358
0
    return (sint64 >> 1) ^ ((int64_t)sint64 << 63 >> 63);
359
0
}
360
361
/* Try to get a protobuf field which has a varint value from the tvb.
362
 * The field number, wire type and uint64 value will be output.
363
 * @return the length of this field. Zero if failed.
364
 */
365
static unsigned
366
tvb_get_protobuf_field_uint(tvbuff_t* tvb, unsigned offset, unsigned maxlen,
367
    uint64_t* field_number, uint32_t* wire_type, uint64_t* value)
368
0
{
369
0
    unsigned tag_length, value_length;
370
0
    uint64_t tag_value;
371
372
    /* parsing the tag of the field */
373
0
    tag_length = tvb_get_varint(tvb, offset, maxlen, &tag_value, ENC_VARINT_PROTOBUF);
374
0
    if (tag_length == 0 || tag_length >= maxlen) {
375
0
        return 0;
376
0
    }
377
0
    *field_number = tag_value >> 3;
378
0
    *wire_type = tag_value & 0x07;
379
380
0
    if (*wire_type != PROTOBUF_WIRETYPE_VARINT) {
381
0
        return 0;
382
0
    }
383
    /* parsing the value of the field */
384
0
    value_length = tvb_get_varint(tvb, offset + tag_length, maxlen - tag_length, value, ENC_VARINT_PROTOBUF);
385
0
    return (value_length == 0) ? 0 : (tag_length + value_length);
386
0
}
387
388
/* Get Protobuf timestamp from the tvb according to the format of google.protobuf.Timestamp.
389
 * return the length parsed.
390
 */
391
static unsigned
392
tvb_get_protobuf_time(tvbuff_t* tvb, unsigned offset, unsigned maxlen, nstime_t* timestamp)
393
0
{
394
0
    unsigned field_length;
395
0
    uint64_t field_number, value;
396
0
    uint32_t wire_type;
397
0
    unsigned off = offset;
398
0
    unsigned len = maxlen; /* remain bytes */
399
400
    /* Get the seconds and nanos fields from google.protobuf.Timestamp message which defined:
401
     *
402
     * message Timestamp {
403
     *    int64 seconds = 1;
404
     *    int32 nanos = 2;
405
     * }
406
     */
407
0
    nstime_set_zero(timestamp);
408
409
0
    while (len > 0) {
410
0
        field_length = tvb_get_protobuf_field_uint(tvb, off, len, &field_number, &wire_type, &value);
411
0
        if (field_length == 0) {
412
0
            break;
413
0
        }
414
415
0
        if (field_number == 1) {
416
0
            timestamp->secs = (time_t)value;
417
0
        } else if (field_number == 2) {
418
0
            timestamp->nsecs = (int)value;
419
0
        }
420
421
0
        off += field_length;
422
0
        len -= field_length;
423
0
    }
424
425
0
    if (timestamp->nsecs < 0 || timestamp->nsecs > 999999999) {
426
0
        nstime_set_unset(timestamp);
427
0
    }
428
429
0
    return maxlen - len;
430
0
}
431
432
433
/* declare first because it will be called by dissect_packed_repeated_field_values */
434
static void
435
protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, unsigned offset, unsigned length, packet_info *pinfo,
436
    proto_item *ti_field, int field_type, const uint64_t value, const char* prepend_text, const PbwFieldDescriptor* field_desc,
437
    bool is_top_level, json_dumper *dumper);
438
439
static void
440
dissect_protobuf_message(tvbuff_t *tvb, unsigned offset, unsigned length, packet_info *pinfo, proto_tree *protobuf_tree,
441
    const PbwDescriptor* message_desc, int hf_msg, bool is_top_level, json_dumper *dumper, wmem_allocator_t* scope, char** retval);
442
443
/* Only repeated fields of primitive numeric types (types which use the varint, 32-bit, or 64-bit wire types) can
444
 * be declared "packed".
445
 * The format of a packed_repeated field likes: tag + varint + varint + varint ...
446
 * or likes: tag + fixed64 + fixed64 + fixed64 ...
447
 * Return consumed bytes
448
 */
449
static unsigned
450
// NOLINTNEXTLINE(misc-no-recursion)
451
dissect_packed_repeated_field_values(tvbuff_t *tvb, unsigned start, unsigned length, packet_info *pinfo,
452
    proto_item *ti_field, int field_type, const char* prepend_text, const PbwFieldDescriptor* field_desc,
453
    json_dumper *dumper)
454
0
{
455
0
    uint64_t sub_value;
456
0
    unsigned sub_value_length;
457
0
    unsigned offset = start;
458
0
    protobuf_varint_tvb_info_t *info;
459
0
    unsigned max_offset = offset + length;
460
0
    wmem_list_frame_t *lframe;
461
0
    wmem_list_t* varint_list;
462
0
    int value_size = 0;
463
464
0
    if (prepend_text == NULL) {
465
0
        prepend_text = "";
466
0
    }
467
468
    /* prepare subtree */
469
0
    proto_item_append_text(ti_field, "%s [", prepend_text);
470
0
    proto_item *ti = proto_tree_add_item(proto_item_get_subtree(ti_field), hf_protobuf_value_repeated, tvb, start, length, ENC_NA);
471
0
    proto_tree *subtree = proto_item_add_subtree(ti, ett_protobuf_packed_repeated);
472
473
0
    prepend_text = "";
474
475
0
    switch (field_type)
476
0
    {
477
    /* packed for Varint encoded types (int32, int64, uint32, uint64, sint32, sint64, bool, enum) */
478
    /* format: tag + varint + varint + varint ... */
479
0
    case PROTOBUF_TYPE_INT32:
480
0
    case PROTOBUF_TYPE_INT64:
481
0
    case PROTOBUF_TYPE_UINT32:
482
0
    case PROTOBUF_TYPE_UINT64:
483
0
    case PROTOBUF_TYPE_SINT32:
484
0
    case PROTOBUF_TYPE_SINT64:
485
0
    case PROTOBUF_TYPE_BOOL:
486
0
    case PROTOBUF_TYPE_ENUM:
487
0
        varint_list = wmem_list_new(pinfo->pool);
488
489
        /* try to test all can parsed as varint */
490
0
        while (offset < max_offset) {
491
0
            sub_value_length = tvb_get_varint(tvb, offset, max_offset - offset, &sub_value, ENC_VARINT_PROTOBUF);
492
0
            if (sub_value_length == 0) {
493
                /* not a valid packed repeated field */
494
0
                wmem_destroy_list(varint_list);
495
0
                return 0;
496
0
            }
497
498
            /* temporarily store varint info in the list */
499
0
            info = wmem_new(pinfo->pool, protobuf_varint_tvb_info_t);
500
0
            info->offset = offset;
501
0
            info->length = sub_value_length;
502
0
            info->value = sub_value;
503
0
            wmem_list_append(varint_list, info);
504
505
0
            offset += sub_value_length;
506
0
        }
507
508
        /* all parsed, we add varints into the packed-repeated subtree */
509
0
        for (lframe = wmem_list_head(varint_list); lframe != NULL; lframe = wmem_list_frame_next(lframe)) {
510
0
            info = (protobuf_varint_tvb_info_t*)wmem_list_frame_data(lframe);
511
0
            protobuf_dissect_field_value(subtree, tvb, info->offset, info->length, pinfo,
512
0
                ti_field, field_type, info->value, prepend_text, field_desc, false, dumper);
513
0
            prepend_text = ",";
514
0
        }
515
516
0
        wmem_destroy_list(varint_list);
517
0
        break;
518
519
    /* packed for 64-bit encoded types (fixed64, sfixed64, double) and 32-bit encoded types (fixed32, sfixed32, float) */
520
    /* format like: tag + sint32 + sint32 + sint32 ... */
521
0
    case PROTOBUF_TYPE_FIXED64:
522
0
    case PROTOBUF_TYPE_SFIXED64:
523
0
    case PROTOBUF_TYPE_DOUBLE:
524
0
        value_size = 8; /* 64-bit */
525
        /* FALLTHROUGH */
526
0
    case PROTOBUF_TYPE_FIXED32:
527
0
    case PROTOBUF_TYPE_SFIXED32:
528
0
    case PROTOBUF_TYPE_FLOAT:
529
0
        if (value_size == 0) {
530
0
            value_size = 4; /* 32-bit */
531
0
        }
532
533
0
        if (length % value_size != 0) {
534
0
            expert_add_info(pinfo, ti_field, &ei_protobuf_failed_parse_packed_repeated_field);
535
0
            return 0;
536
0
        }
537
538
0
        for (offset = start; offset < max_offset; offset += value_size) {
539
0
            protobuf_dissect_field_value(subtree, tvb, offset, value_size, pinfo, ti_field, field_type,
540
0
                (value_size == 4 ? tvb_get_uint32(tvb, offset, ENC_LITTLE_ENDIAN)
541
0
                    : tvb_get_uint64(tvb, offset, ENC_LITTLE_ENDIAN)),
542
0
                prepend_text, field_desc, false, dumper);
543
544
0
            prepend_text = ",";
545
0
        }
546
547
0
        break;
548
549
0
    default:
550
0
        expert_add_info(pinfo, ti_field, &ei_protobuf_wire_type_not_support_packed_repeated);
551
0
        return 0; /* prevent dead loop */
552
0
    }
553
554
0
    proto_item_append_text(ti_field, "]");
555
0
    return length;
556
0
}
557
558
/* The "google.protobuf.Timestamp" must be converted to rfc3339 format if mapping to JSON
559
 * according to https://developers.google.com/protocol-buffers/docs/proto3#json
560
 */
561
static char *
562
abs_time_to_rfc3339(wmem_allocator_t *scope, const nstime_t *nstime, bool use_utc)
563
0
{
564
0
    struct tm *tm;
565
0
    char datetime_format[128];
566
0
    int nsecs;
567
0
    int width;
568
0
    char nsecs_buf[32];
569
570
0
    if (use_utc) {
571
0
        tm = gmtime(&nstime->secs);
572
0
        if (tm != NULL)
573
0
            strftime(datetime_format, sizeof(datetime_format), "%Y-%m-%dT%H:%M:%S%%sZ", tm);
574
0
        else
575
0
            snprintf(datetime_format, sizeof(datetime_format), "Not representable");
576
0
    } else {
577
0
        tm = localtime(&nstime->secs);
578
0
        if (tm != NULL)
579
0
            strftime(datetime_format, sizeof(datetime_format), "%Y-%m-%dT%H:%M:%S%%s%z", tm);
580
0
        else
581
0
            snprintf(datetime_format, sizeof(datetime_format), "Not representable");
582
0
    }
583
584
0
    if (nstime->nsecs == 0)
585
0
        return wmem_strdup_printf(scope, datetime_format, "");
586
587
0
    nsecs = nstime->nsecs;
588
0
    width = 9;
589
0
    while (width > 0 && (nsecs % 1000) == 0) {
590
0
        nsecs /= 1000;
591
0
        width -= 3;
592
0
    }
593
0
    snprintf(nsecs_buf, sizeof(nsecs_buf), ".%0*d", width, nsecs);
594
595
0
    return wmem_strdup_printf(scope, datetime_format, nsecs_buf);
596
0
}
597
598
/* Dissect field value based on a specific type. */
599
static void
600
// NOLINTNEXTLINE(misc-no-recursion)
601
protobuf_dissect_field_value(proto_tree *value_tree, tvbuff_t *tvb, unsigned offset, unsigned length, packet_info *pinfo,
602
    proto_item *ti_field, int field_type, const uint64_t value, const char* prepend_text, const PbwFieldDescriptor* field_desc,
603
    bool is_top_level, json_dumper *dumper)
604
0
{
605
0
    double double_value;
606
0
    float float_value;
607
0
    int64_t int64_value;
608
0
    int32_t int32_value;
609
0
    char* buf;
610
0
    bool add_datatype = true;
611
0
    proto_item* ti = NULL;
612
0
    proto_tree* subtree = NULL;
613
0
    const char* enum_value_name = NULL;
614
0
    const PbwDescriptor* sub_message_desc = NULL;
615
0
    const PbwEnumDescriptor* enum_desc = NULL;
616
0
    int* hf_id_ptr = NULL;
617
0
    const char* field_full_name = field_desc ? pbw_FieldDescriptor_full_name(field_desc) : NULL;
618
0
    proto_tree* field_tree = proto_item_get_subtree(ti_field);
619
0
    proto_tree* field_parent_tree = proto_tree_get_parent_tree(field_tree);
620
0
    proto_tree* pbf_tree = field_tree;
621
0
    dissector_handle_t field_dissector = field_full_name ? dissector_get_string_handle(protobuf_field_subdissector_table, field_full_name) : NULL;
622
623
0
    if (pbf_as_hf && field_full_name) {
624
0
        hf_id_ptr = (int*)g_hash_table_lookup(pbf_hf_hash, field_full_name);
625
0
        DISSECTOR_ASSERT_HINT(hf_id_ptr && (*hf_id_ptr) > 0, "hf must have been initialized properly");
626
0
    }
627
628
0
    if (pbf_as_hf && hf_id_ptr && !show_details) {
629
        /* set ti_field (Field(x)) item hidden if there is header_field */
630
0
        proto_item_set_hidden(ti_field);
631
0
        pbf_tree = field_parent_tree;
632
0
    }
633
634
0
    if (prepend_text == NULL) {
635
0
        prepend_text = "";
636
0
    }
637
638
0
    switch (field_type)
639
0
    {
640
0
    case PROTOBUF_TYPE_DOUBLE:
641
0
        double_value = protobuf_uint64_to_double(value);
642
0
        proto_tree_add_double(value_tree, hf_protobuf_value_double, tvb, offset, length, double_value);
643
0
        proto_item_append_text(ti_field, "%s %lf", prepend_text, double_value);
644
0
        if (is_top_level) {
645
0
            col_append_fstr(pinfo->cinfo, COL_INFO, "=%lf", double_value);
646
0
        }
647
0
        if (hf_id_ptr) {
648
0
            proto_tree_add_double(pbf_tree, *hf_id_ptr, tvb, offset, length, double_value);
649
0
        }
650
0
        if (field_desc && dumper) {
651
0
            json_dumper_value_double(dumper, double_value);
652
0
        }
653
0
        break;
654
655
0
    case PROTOBUF_TYPE_FLOAT:
656
0
        float_value = protobuf_uint32_to_float((uint32_t) value);
657
0
        proto_tree_add_float(value_tree, hf_protobuf_value_float, tvb, offset, length, float_value);
658
0
        proto_item_append_text(ti_field, "%s %f", prepend_text, float_value);
659
0
        if (is_top_level) {
660
0
            col_append_fstr(pinfo->cinfo, COL_INFO, "=%f", float_value);
661
0
        }
662
0
        if (hf_id_ptr) {
663
0
            proto_tree_add_float(pbf_tree, *hf_id_ptr, tvb, offset, length, float_value);
664
0
        }
665
0
        if (field_desc && dumper) {
666
0
            json_dumper_value_anyf(dumper, "%f", float_value);
667
0
        }
668
0
        break;
669
670
0
    case PROTOBUF_TYPE_INT64:
671
0
    case PROTOBUF_TYPE_SFIXED64:
672
0
        int64_value = (int64_t) value;
673
0
        proto_tree_add_int64(value_tree, hf_protobuf_value_int64, tvb, offset, length, int64_value);
674
0
        proto_item_append_text(ti_field, "%s %" PRId64, prepend_text, int64_value);
675
0
        if (is_top_level) {
676
0
            col_append_fstr(pinfo->cinfo, COL_INFO, "=%" PRId64, int64_value);
677
0
        }
678
0
        if (hf_id_ptr) {
679
0
            proto_tree_add_int64(pbf_tree, *hf_id_ptr, tvb, offset, length, int64_value);
680
0
        }
681
0
        if (field_desc && dumper) {
682
0
            json_dumper_value_anyf(dumper, "\"%" PRId64 "\"", int64_value);
683
0
        }
684
0
        break;
685
686
0
    case PROTOBUF_TYPE_UINT64:
687
0
    case PROTOBUF_TYPE_FIXED64: /* same as UINT64 */
688
0
        proto_tree_add_uint64(value_tree, hf_protobuf_value_uint64, tvb, offset, length, value);
689
0
        proto_item_append_text(ti_field, "%s %" PRIu64, prepend_text, value);
690
0
        if (is_top_level) {
691
0
            col_append_fstr(pinfo->cinfo, COL_INFO, "=%" PRIu64, value);
692
0
        }
693
0
        if (hf_id_ptr) {
694
0
            proto_tree_add_uint64(pbf_tree, *hf_id_ptr, tvb, offset, length, value);
695
0
        }
696
0
        if (field_desc && dumper) {
697
0
            json_dumper_value_anyf(dumper, "\"%" PRIu64 "\"", value);
698
0
        }
699
0
        break;
700
701
0
    case PROTOBUF_TYPE_INT32:
702
0
    case PROTOBUF_TYPE_SFIXED32:
703
0
        int32_value = (int32_t)value;
704
0
        proto_tree_add_int(value_tree, hf_protobuf_value_int32, tvb, offset, length, int32_value);
705
0
        proto_item_append_text(ti_field, "%s %d", prepend_text, int32_value);
706
0
        if (is_top_level) {
707
0
            col_append_fstr(pinfo->cinfo, COL_INFO, "=%d", int32_value);
708
0
        }
709
0
        if (hf_id_ptr) {
710
0
            proto_tree_add_int(pbf_tree, *hf_id_ptr, tvb, offset, length, int32_value);
711
0
        }
712
0
        if (field_desc && dumper) {
713
0
            json_dumper_value_anyf(dumper, "%d", int32_value);
714
0
        }
715
0
        break;
716
717
0
    case PROTOBUF_TYPE_ENUM:
718
0
        int32_value = (int32_t) value;
719
        /* get the name of enum value */
720
0
        if (field_desc) {
721
0
            enum_desc = pbw_FieldDescriptor_enum_type(field_desc);
722
0
            if (enum_desc) {
723
0
                const PbwEnumValueDescriptor* enum_value_desc = pbw_EnumDescriptor_FindValueByNumber(enum_desc, int32_value);
724
0
                if (enum_value_desc) {
725
0
                    enum_value_name = pbw_EnumValueDescriptor_name(enum_value_desc);
726
0
                }
727
0
            }
728
0
        }
729
0
        ti = proto_tree_add_int(value_tree, hf_protobuf_value_int32, tvb, offset, length, int32_value);
730
0
        if (enum_value_name) { /* show enum value name */
731
0
            proto_item_append_text(ti_field, "%s %s(%d)", prepend_text, enum_value_name, int32_value);
732
0
            proto_item_append_text(ti, " (%s)", enum_value_name);
733
0
            if (is_top_level) {
734
0
                col_append_fstr(pinfo->cinfo, COL_INFO, "=%s", enum_value_name);
735
0
            }
736
0
        } else {
737
0
            proto_item_append_text(ti_field, "%s %d", prepend_text, int32_value);
738
0
            if (is_top_level) {
739
0
                col_append_fstr(pinfo->cinfo, COL_INFO, "=%d", int32_value);
740
0
            }
741
742
0
        }
743
0
        if (hf_id_ptr) {
744
0
            proto_tree_add_int(pbf_tree, *hf_id_ptr, tvb, offset, length, int32_value);
745
0
        }
746
0
        if (field_desc && dumper) {
747
0
            if (enum_value_name) {
748
0
                json_dumper_value_string(dumper, enum_value_name);
749
0
            } else {
750
                /* The enum value is used if the name of the enum value is not specified in proto */
751
0
                json_dumper_value_anyf(dumper, "%d", int32_value);
752
0
            }
753
0
        }
754
0
        break;
755
756
0
    case PROTOBUF_TYPE_BOOL:
757
0
        if (length > 1) break; /* boolean should not use more than one bytes */
758
0
        proto_tree_add_boolean(value_tree, hf_protobuf_value_bool, tvb, offset, length, value);
759
0
        proto_item_append_text(ti_field, "%s %s", prepend_text, value ? "true" : "false");
760
0
        if (is_top_level) {
761
0
            col_append_fstr(pinfo->cinfo, COL_INFO, "=%s", value ? "true" : "false");
762
0
        }
763
0
        if (hf_id_ptr) {
764
0
            proto_tree_add_boolean(pbf_tree, *hf_id_ptr, tvb, offset, length, value);
765
0
        }
766
0
        if (field_desc && dumper) {
767
0
            json_dumper_value_anyf(dumper, value ? "true" : "false");
768
0
        }
769
0
        break;
770
771
0
    case PROTOBUF_TYPE_BYTES:
772
0
        if (field_desc && dumper) {
773
0
            json_dumper_begin_base64(dumper);
774
0
            buf = (char*) tvb_memdup(pinfo->pool, tvb, offset, length);
775
0
            if (buf) {
776
0
                json_dumper_write_base64(dumper, (uint8_t*)buf, length);
777
0
            }
778
0
            json_dumper_end_base64(dumper);
779
0
        }
780
0
        if (field_dissector) {
781
0
            if (!show_details) { /* don't show Value node if there is a subdissector for this field */
782
0
                proto_item_set_hidden(proto_tree_get_parent(value_tree));
783
0
            }
784
0
            if (dissect_bytes_as_string) { /* the type of *hf_id_ptr MUST be FT_STRING now */
785
0
                if (hf_id_ptr) {
786
0
                    ti = proto_tree_add_string_format_value(pbf_tree, *hf_id_ptr, tvb, offset, length, "", "(%u bytes)", length);
787
0
                }
788
                /* don't try to dissect bytes as string if there is a subdissector for this field */
789
0
                break;
790
0
            }
791
0
        }
792
0
        if (!dissect_bytes_as_string) {
793
            /* the type of *hf_id_ptr MUST be FT_BYTES now */
794
0
            if (hf_id_ptr) {
795
0
                ti = proto_tree_add_bytes_format_value(pbf_tree, *hf_id_ptr, tvb, offset, length, NULL, "(%u bytes)", length);
796
0
            }
797
0
            break;
798
0
        }
799
        /* or continue dissect BYTES as STRING */
800
0
        proto_item_append_text(ti_field, " =");
801
        /* FALLTHROUGH */
802
0
    case PROTOBUF_TYPE_STRING:
803
0
        proto_tree_add_item_ret_string(value_tree, hf_protobuf_value_string, tvb, offset, length, ENC_UTF_8|ENC_NA, pinfo->pool, (const uint8_t**)&buf);
804
0
        proto_item_append_text(ti_field, "%s %s", prepend_text, buf);
805
0
        if (is_top_level) {
806
0
            col_append_fstr(pinfo->cinfo, COL_INFO, "=%s", buf);
807
0
        }
808
0
        if (hf_id_ptr) {
809
0
            ti = proto_tree_add_item(pbf_tree, *hf_id_ptr, tvb, offset, length, ENC_UTF_8|ENC_NA);
810
0
        }
811
0
        if (field_desc && dumper && field_type == PROTOBUF_TYPE_STRING) {
812
            /* JSON view will ignore the dissect_bytes_as_string option */
813
0
            json_dumper_value_string(dumper, buf);
814
0
        }
815
0
        break;
816
817
0
    case PROTOBUF_TYPE_GROUP: /* This feature is deprecated. GROUP is identical to Nested MESSAGE. */
818
0
    case PROTOBUF_TYPE_MESSAGE:
819
0
        subtree = field_tree;
820
0
        if (field_desc) {
821
0
            sub_message_desc = pbw_FieldDescriptor_message_type(field_desc);
822
0
            if (sub_message_desc == NULL) {
823
0
                expert_add_info(pinfo, ti_field, &ei_protobuf_message_type_not_found);
824
0
            }
825
0
        }
826
0
        if (sub_message_desc) {
827
0
            dissect_protobuf_message(tvb, offset, length, pinfo, pbf_as_hf ? pbf_tree : subtree, sub_message_desc,
828
0
                                     hf_id_ptr ? *hf_id_ptr : -1,
829
0
                                     false,   // not top level
830
0
                                     dumper,
831
0
                                     pinfo->pool,
832
0
                                     &buf);
833
834
0
            if (buf) { /* append the value in string format to ti_field node */
835
0
                proto_item_append_text(ti_field, "= %s", buf);
836
0
            }
837
0
        } else if (hf_id_ptr) {
838
0
            proto_tree_add_bytes_format_value(pbf_tree, *hf_id_ptr, tvb, offset, length, NULL, "(%u bytes)", length);
839
0
        } else {
840
            /* we don't continue with unknown message type */
841
0
        }
842
0
        break;
843
844
0
    case PROTOBUF_TYPE_UINT32:
845
0
    case PROTOBUF_TYPE_FIXED32: /* same as UINT32 */
846
0
        proto_tree_add_uint(value_tree, hf_protobuf_value_uint32, tvb, offset, length, (uint32_t)value);
847
0
        proto_item_append_text(ti_field, "%s %u", prepend_text, (uint32_t)value);
848
0
        if (is_top_level) {
849
0
            col_append_fstr(pinfo->cinfo, COL_INFO, "=%u", (uint32_t)value);
850
0
        }
851
0
        if (hf_id_ptr) {
852
0
            proto_tree_add_uint(pbf_tree, *hf_id_ptr, tvb, offset, length, (uint32_t)value);
853
0
        }
854
0
        if (field_desc && dumper) {
855
0
            json_dumper_value_anyf(dumper, "%u", (uint32_t)value);
856
0
        }
857
0
        break;
858
859
0
    case PROTOBUF_TYPE_SINT32:
860
0
        int32_value = sint32_decode((uint32_t)value);
861
0
        proto_tree_add_int(value_tree, hf_protobuf_value_int32, tvb, offset, length, int32_value);
862
0
        proto_item_append_text(ti_field, "%s %d", prepend_text, int32_value);
863
0
        if (is_top_level) {
864
0
            col_append_fstr(pinfo->cinfo, COL_INFO, "=%d", int32_value);
865
0
        }
866
0
        if (hf_id_ptr) {
867
0
            proto_tree_add_int(pbf_tree, *hf_id_ptr, tvb, offset, length, int32_value);
868
0
        }
869
0
        if (field_desc && dumper) {
870
0
            json_dumper_value_anyf(dumper, "%d", int32_value);
871
0
        }
872
0
        break;
873
874
0
    case PROTOBUF_TYPE_SINT64:
875
0
        int64_value = sint64_decode(value);
876
0
        proto_tree_add_int64(value_tree, hf_protobuf_value_int64, tvb, offset, length, int64_value);
877
0
        proto_item_append_text(ti_field, "%s %" PRId64, prepend_text, int64_value);
878
0
        if (is_top_level) {
879
0
            col_append_fstr(pinfo->cinfo, COL_INFO, "=%" PRId64, int64_value);
880
0
        }
881
0
        if (hf_id_ptr) {
882
0
            proto_tree_add_int64(pbf_tree, *hf_id_ptr, tvb, offset, length, int64_value);
883
0
        }
884
0
        if (field_desc && dumper) {
885
0
            json_dumper_value_anyf(dumper, "%" PRId64, int64_value);
886
0
        }
887
0
        break;
888
889
0
    default:
890
        /* ignore unknown field type */
891
0
        add_datatype = false;
892
0
        break;
893
0
    }
894
895
    /* try dissect field value according to protobuf_field dissector table */
896
0
    if (field_dissector) {
897
        /* determine the tree passing to the subdissector */
898
0
        subtree = field_tree;
899
0
        if (ti) {
900
0
            subtree = proto_item_get_subtree(ti);
901
0
            if (!subtree) {
902
0
                subtree = proto_item_add_subtree(ti, ett_protobuf_value);
903
0
            }
904
0
        }
905
906
0
        call_dissector(field_dissector, tvb_new_subset_length(tvb, offset, length), pinfo, subtree);
907
0
    }
908
909
0
    if (add_datatype)
910
0
        proto_item_append_text(ti_field, " (%s)", val_to_str(pinfo->pool, field_type, protobuf_field_type, "Unknown type (%d)"));
911
912
0
}
913
914
/* add all possible values according to field types. */
915
static void
916
// NOLINTNEXTLINE(misc-no-recursion)
917
protobuf_try_dissect_field_value_on_multi_types(proto_tree *value_tree, tvbuff_t *tvb, unsigned offset, unsigned length,
918
    packet_info *pinfo, proto_item *ti_field, int* field_types, const uint64_t value, const char* prepend_text,
919
    json_dumper *dumper)
920
0
{
921
0
    int i;
922
923
0
    if (prepend_text == NULL) {
924
0
        prepend_text = "";
925
0
    }
926
927
0
    for (i = 0; field_types[i] != PROTOBUF_TYPE_NONE; ++i) {
928
0
        protobuf_dissect_field_value(value_tree, tvb, offset, length, pinfo, ti_field, field_types[i], value, prepend_text, NULL, false, dumper);
929
0
        prepend_text = ",";
930
0
    }
931
0
}
932
933
static bool
934
// NOLINTNEXTLINE(misc-no-recursion)
935
dissect_one_protobuf_field(tvbuff_t *tvb, unsigned* offset, unsigned maxlen, packet_info *pinfo, proto_tree *protobuf_tree,
936
    const PbwDescriptor* message_desc, bool is_top_level, const PbwFieldDescriptor** field_desc_ptr,
937
    const PbwFieldDescriptor* prev_field_desc, json_dumper *dumper)
938
0
{
939
0
    uint64_t tag_value; /* tag value = (field_number << 3) | wire_type */
940
0
    unsigned tag_length; /* how many bytes this tag has */
941
0
    uint64_t field_number;
942
0
    uint32_t wire_type;
943
0
    uint64_t value_uint64; /* uint64 value of numeric field (type of varint, 64-bit, 32-bit */
944
0
    unsigned value_length;
945
0
    unsigned value_length_size = 0; /* only Length-delimited field has it */
946
0
    proto_item *ti_field, *ti_field_number, *ti_wire, *ti_value_length = NULL;
947
0
    proto_item *ti_value, *ti_field_name, *ti_field_type = NULL;
948
0
    proto_tree *field_tree;
949
0
    proto_tree *value_tree;
950
0
    const char* field_name = NULL;
951
0
    int field_type = -1;
952
0
    bool is_packed = false;
953
0
    bool is_repeated = false;
954
0
    const PbwFieldDescriptor* field_desc = NULL;
955
0
    unsigned start_offset = *offset;
956
957
    /* A protocol buffer message is a series of key-value pairs. The binary version of a message just uses
958
     * the field's number as the key. a wire type that provides just enough information to find the length of
959
     * the following value.
960
     * Format of protobuf is:
961
     *       protobuf field -> tag value
962
     *       tag -> (field_number << 3) | wire_type  (the last three bits of the number store the wire type)
963
     *       value -> according to wiret_type, value may be
964
     *                 - varint (int32, int64, uint32, uint64, sint32, sint64, bool, enum),
965
     *                 - 64-bit number (fixed64, sfixed64, double)
966
     *                 - Length-delimited (string, bytes, embedded messages, packed repeated fields)
967
     *                 - deprecated 'Start group' or 'End group' (we stop dissecting when encountered them)
968
     *                 - 32-bit (fixed32, sfixed32, float)
969
     * All numbers in protobuf are stored in little-endian byte order.
970
     */
971
972
0
    field_tree = proto_tree_add_subtree(protobuf_tree, tvb, *offset, 0, ett_protobuf_field, &ti_field, "Field");
973
974
    /* parsing Tag */
975
0
    tag_length = tvb_get_varint(tvb, *offset, maxlen, &tag_value, ENC_VARINT_PROTOBUF);
976
977
0
    if (tag_length == 0) { /* not found a valid varint */
978
0
        expert_add_info(pinfo, ti_field, &ei_protobuf_failed_parse_tag);
979
0
        return false;
980
0
    }
981
982
0
    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);
983
0
    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);
984
0
    (*offset) += tag_length;
985
    /* try to find field_info first */
986
0
    if (message_desc) {
987
        /* find field descriptor according to field number from message descriptor */
988
0
        field_desc = pbw_Descriptor_FindFieldByNumber(message_desc, (int) field_number);
989
0
        if (field_desc) {
990
0
            *field_desc_ptr = field_desc;
991
0
            field_name = pbw_FieldDescriptor_name(field_desc);
992
0
            field_type = pbw_FieldDescriptor_type(field_desc);
993
0
            is_packed = pbw_FieldDescriptor_is_packed(field_desc);
994
0
            is_repeated = pbw_FieldDescriptor_is_repeated(field_desc);
995
0
        }
996
0
    }
997
998
0
    proto_item_append_text(ti_field, "(%" PRIu64 "):", field_number);
999
1000
    /* support filtering with field name */
1001
0
    ti_field_name = proto_tree_add_string(field_tree, hf_protobuf_field_name, tvb, start_offset, 0,
1002
0
        (field_name ? field_name : "<UNKNOWN>"));
1003
0
    proto_item_set_generated(ti_field_name);
1004
0
    if (field_name) {
1005
0
        proto_item_append_text(ti_field, " %s %s", field_name,
1006
0
            (field_type == PROTOBUF_TYPE_MESSAGE || field_type == PROTOBUF_TYPE_GROUP
1007
0
                || field_type == PROTOBUF_TYPE_BYTES)
1008
0
            ? "" : "="
1009
0
        );
1010
0
        if (field_type > 0) {
1011
0
            ti_field_type = proto_tree_add_int(field_tree, hf_protobuf_field_type, tvb, start_offset, 0, field_type);
1012
0
            proto_item_set_generated(ti_field_type);
1013
0
        }
1014
1015
0
        if (is_top_level) {
1016
            /* Show field name in Info column */
1017
0
            col_append_fstr(pinfo->cinfo, COL_INFO, " %s", field_name);
1018
0
        }
1019
0
    }
1020
1021
    /* move ti_field_number and ti_wire after ti_field_type (or field_type) for good look */
1022
0
    proto_tree_move_item(field_tree, (ti_field_type ? ti_field_type : ti_field_name), ti_wire);
1023
0
    proto_tree_move_item(field_tree, (ti_field_type ? ti_field_type : ti_field_name), ti_field_number);
1024
1025
    /* determine value_length, uint of numeric value and maybe value_length_size according to wire_type */
1026
0
    switch (wire_type)
1027
0
    {
1028
0
    case PROTOBUF_WIRETYPE_VARINT: /* varint, format: tag + varint */
1029
        /* get value length and real value */
1030
0
        value_length = tvb_get_varint(tvb, *offset, maxlen - tag_length, &value_uint64, ENC_VARINT_PROTOBUF);
1031
0
        if (value_length == 0) {
1032
0
            expert_add_info(pinfo, ti_wire, &ei_protobuf_failed_parse_field);
1033
0
            return false;
1034
0
        }
1035
0
        break;
1036
1037
0
    case PROTOBUF_WIRETYPE_FIXED64: /* fixed 64-bit type, format: tag + 64-bit-value */
1038
        /* get value length and real value */
1039
0
        value_length = 8;
1040
0
        value_uint64 = tvb_get_letoh64(tvb, *offset);
1041
0
        break;
1042
1043
0
    case PROTOBUF_WIRETYPE_FIXED32: /* fixed 32-bit type, format: tag + 32-bit-value */
1044
0
        value_length = 4;
1045
0
        value_uint64 = tvb_get_letohl(tvb, *offset);
1046
0
        break;
1047
1048
0
    case PROTOBUF_WIRETYPE_LENGTH_DELIMITED: /* Length-delimited, format: tag + length(varint) + bytes_value */
1049
        /* this time value_uint64 is the length of following value bytes */
1050
0
        value_length_size = tvb_get_varint(tvb, *offset, maxlen - tag_length, &value_uint64, ENC_VARINT_PROTOBUF);
1051
0
        if (value_length_size == 0) {
1052
0
            expert_add_info(pinfo, ti_field, &ei_protobuf_failed_parse_length_delimited_field);
1053
0
            return false;
1054
0
        }
1055
1056
0
        ti_value_length = proto_tree_add_uint64(field_tree, hf_protobuf_value_length, tvb, *offset, value_length_size, value_uint64);
1057
0
        (*offset) += value_length_size;
1058
1059
        /* we believe the length of following value will not be bigger than unsigned */
1060
0
        value_length = (unsigned) value_uint64;
1061
0
        break;
1062
1063
0
    default:
1064
0
        expert_add_info(pinfo, ti_wire, &ei_protobuf_wire_type_invalid);
1065
0
        return false;
1066
0
    }
1067
1068
0
    proto_item_set_len(ti_field, tag_length + value_length_size + value_length);
1069
0
    proto_item_set_len(ti_field_name, tag_length + value_length_size + value_length);
1070
0
    if (ti_field_type) {
1071
0
        proto_item_set_len(ti_field_type, tag_length + value_length_size + value_length);
1072
0
    }
1073
1074
    /* add value as bytes first */
1075
0
    ti_value = proto_tree_add_item(field_tree, hf_protobuf_value_data, tvb, *offset, value_length, ENC_NA);
1076
1077
    /* add value subtree. we add uint value for numeric field or string for length-delimited at least. */
1078
0
    value_tree = proto_item_add_subtree(ti_value, ett_protobuf_value);
1079
1080
0
    increment_dissection_depth(pinfo);
1081
0
    if (field_desc) {
1082
0
        if (dumper) {
1083
0
            if (prev_field_desc == NULL || pbw_FieldDescriptor_number(prev_field_desc) != (int) field_number) {
1084
                /* end JSON array if previous field is repeated field */
1085
0
                if (prev_field_desc && pbw_FieldDescriptor_is_repeated(prev_field_desc)) {
1086
0
                    json_dumper_end_array(dumper);
1087
0
                }
1088
1089
                /* set JSON name if it is the first of an unpacked repeated field, or an unrepeated field */
1090
0
                json_dumper_set_member_name(dumper, field_name);
1091
1092
                /* begin JSON array if it is the first of a repeated field */
1093
0
                if (is_repeated) {
1094
0
                    json_dumper_begin_array(dumper);
1095
0
                }
1096
0
            }
1097
0
        }
1098
0
        if (is_repeated && is_packed) {
1099
0
            dissect_packed_repeated_field_values(tvb, *offset, value_length, pinfo, ti_field,
1100
0
                field_type, "", field_desc, dumper);
1101
0
        } else {
1102
0
            protobuf_dissect_field_value(value_tree, tvb, *offset, value_length, pinfo, ti_field, field_type, value_uint64, "", field_desc,
1103
0
                                         is_top_level, dumper);
1104
0
        }
1105
0
    } else {
1106
        /* end JSON array if previous field is repeated field. We must end
1107
         * the array here even if we don't add this unknown field to the JSON,
1108
         * so that the array ends at the correct nested level.
1109
         */
1110
0
        if (dumper && prev_field_desc && pbw_FieldDescriptor_is_repeated(prev_field_desc)) {
1111
0
            json_dumper_end_array(dumper);
1112
0
        }
1113
0
        if (show_all_possible_field_types) {
1114
            /* try dissect every possible field type */
1115
0
            protobuf_try_dissect_field_value_on_multi_types(value_tree, tvb, *offset, value_length, pinfo,
1116
0
                ti_field, protobuf_wire_to_field_type[wire_type], value_uint64, "", dumper);
1117
0
        } else {
1118
0
            field_type = (wire_type == PROTOBUF_WIRETYPE_LENGTH_DELIMITED)
1119
                /* print string at least for length-delimited */
1120
0
                ? (try_dissect_as_string ? PROTOBUF_TYPE_STRING : PROTOBUF_TYPE_NONE)
1121
                /* use uint32 or uint64 */
1122
0
                : (value_uint64 <= 0xFFFFFFFF ? PROTOBUF_TYPE_UINT32 : PROTOBUF_TYPE_UINT64);
1123
0
            int field_types[] = { field_type, PROTOBUF_TYPE_NONE };
1124
1125
0
            protobuf_try_dissect_field_value_on_multi_types(value_tree, tvb, *offset, value_length, pinfo,
1126
0
                ti_field, field_types, value_uint64, "", dumper);
1127
0
        }
1128
0
    }
1129
0
    decrement_dissection_depth(pinfo);
1130
1131
0
    if (field_desc && !show_details) {
1132
0
        proto_item_set_hidden(ti_field_number);
1133
0
        proto_item_set_hidden(ti_wire);
1134
0
        proto_item_set_hidden(ti_value_length);
1135
0
        proto_item_set_hidden(ti_field_name);
1136
0
        proto_item_set_hidden(ti_field_type);
1137
0
        if (field_type != PROTOBUF_TYPE_BYTES && field_type != PROTOBUF_TYPE_GROUP) {
1138
0
            proto_item_set_hidden(ti_value);
1139
0
        }
1140
0
    }
1141
1142
0
    (*offset) += value_length;
1143
0
    return true;
1144
0
}
1145
1146
/* Make Protobuf fields that are not serialized on the wire (missing in capture files) to be displayed
1147
 * with default values. In 'proto2', default values can be explicitly declared. In 'proto3', if a
1148
 * field is set to its default, the value will *not* be serialized on the wire.
1149
 *
1150
 * The default value will be displayed according to following situations:
1151
 *  1. Explicitly-declared default values in 'proto2', for example:
1152
 *             optional int32 result_per_page = 3 [default = 10]; // default value is 10
1153
 *  2. For bools, the default value is false.
1154
 *  3. For enums, the default value is the first defined enum value, which must be 0 in 'proto3' (but
1155
 *     allowed to be other in 'proto2').
1156
 *  4. For numeric types, the default value is zero; for strings, the default value is the empty string;
1157
 *     and for bytes, the default value is empty bytes.
1158
 * There are no default values for fields 'repeated'.
1159
 * If the missing field is 'required' in a 'proto2' file, an expert warning item will be added to the tree.
1160
 *
1161
 * Which fields will be displayed is controlled by 'add_default_value' option:
1162
 *  - ADD_DEFAULT_VALUE_NONE      -- do not display any missing fields.
1163
 *  - ADD_DEFAULT_VALUE_DECLARED  -- only missing fields of situation (1) will be displayed.
1164
 *  - ADD_DEFAULT_VALUE_ENUM_BOOL -- missing fields of situantions (1, 2 and 3) will be displayed.
1165
 *  - ADD_DEFAULT_VALUE_ALL       -- missing fields of all situations (1, 2, 3, and 4) will be displayed.
1166
 */
1167
static void
1168
add_missing_fields_with_default_values(tvbuff_t* tvb, unsigned offset, packet_info* pinfo, proto_tree* message_tree,
1169
    const PbwDescriptor* message_desc, wmem_map_t* parsed_fields, json_dumper *dumper)
1170
0
{
1171
0
    const PbwFieldDescriptor* field_desc;
1172
0
    const char* field_name, * field_full_name, * enum_value_name, * string_value;
1173
0
    int field_count = pbw_Descriptor_field_count(message_desc);
1174
0
    int field_type, i;
1175
0
    uint64_t field_number;
1176
0
    bool is_required;
1177
0
    bool is_repeated;
1178
0
    bool has_default_value; /* explicitly-declared default value */
1179
0
    proto_item* ti_message = proto_tree_get_parent(message_tree);
1180
0
    proto_item* ti_field, * ti_field_number, * ti_field_name, * ti_field_type, * ti_value, * ti_pbf;
1181
0
    proto_tree* field_tree, * pbf_tree;
1182
0
    int* hf_id_ptr;
1183
0
    double double_value;
1184
0
    float float_value;
1185
0
    int64_t int64_value;
1186
0
    int32_t int32_value;
1187
0
    uint64_t uint64_value;
1188
0
    uint32_t uint32_value;
1189
0
    bool bool_value;
1190
0
    int size;
1191
0
    const PbwEnumValueDescriptor* enum_value_desc;
1192
1193
0
    for (i = 0; i < field_count; i++) {
1194
0
        field_desc = pbw_Descriptor_field(message_desc, i);
1195
0
        field_number = (uint64_t) pbw_FieldDescriptor_number(field_desc);
1196
0
        field_type = pbw_FieldDescriptor_type(field_desc);
1197
0
        is_required = pbw_FieldDescriptor_is_required(field_desc);
1198
0
        is_repeated = pbw_FieldDescriptor_is_repeated(field_desc);
1199
0
        has_default_value = pbw_FieldDescriptor_has_default_value(field_desc);
1200
1201
0
        if (!is_required && add_default_value == ADD_DEFAULT_VALUE_DECLARED && !has_default_value) {
1202
            /* ignore this field if default value is not explicitly-declared */
1203
0
            continue;
1204
0
        }
1205
1206
0
        if (!is_required && add_default_value == ADD_DEFAULT_VALUE_ENUM_BOOL && !has_default_value
1207
0
            && field_type != PROTOBUF_TYPE_ENUM && field_type != PROTOBUF_TYPE_BOOL) {
1208
            /* ignore this field if default value is not explicitly-declared, or it is not enum or bool */
1209
0
            continue;
1210
0
        }
1211
1212
        /* ignore repeated fields, or optional fields of message/group.
1213
         */
1214
0
        if (is_repeated || (!is_required && (field_type == PROTOBUF_TYPE_NONE
1215
0
            || field_type == PROTOBUF_TYPE_MESSAGE
1216
0
            || field_type == PROTOBUF_TYPE_GROUP
1217
0
            ))) {
1218
0
            continue;
1219
0
        }
1220
1221
        /* check if it is parsed */
1222
0
        if (wmem_map_lookup(parsed_fields, GINT_TO_POINTER((int)field_number))) {
1223
0
            continue; /* this field is parsed */
1224
0
        }
1225
1226
0
        field_name = pbw_FieldDescriptor_name(field_desc);
1227
1228
        /* this field is not found in message payload */
1229
0
        if (is_required) {
1230
0
            expert_add_info_format(pinfo, ti_message, &ei_protobuf_missing_required_field, "missing required field '%s'", field_name);
1231
0
            continue;
1232
0
        }
1233
1234
0
        field_full_name = pbw_FieldDescriptor_full_name(field_desc);
1235
1236
        /* add common tree item for this field */
1237
0
        field_tree = proto_tree_add_subtree_format(message_tree, tvb, offset, 0, ett_protobuf_field, &ti_field,
1238
0
            "Field(%" PRIu64 "): %s %s", field_number, field_name, "=");
1239
0
        proto_item_set_generated(ti_field);
1240
1241
        /* support filtering with the name, type or number of the field  */
1242
0
        ti_field_name = proto_tree_add_string(field_tree, hf_protobuf_field_name, tvb, offset, 0, field_name);
1243
0
        proto_item_set_generated(ti_field_name);
1244
0
        ti_field_type = proto_tree_add_int(field_tree, hf_protobuf_field_type, tvb, offset, 0, field_type);
1245
0
        proto_item_set_generated(ti_field_type);
1246
        // XXX - Why multiply the field_number by 8 here?
1247
0
        ti_field_number = proto_tree_add_uint64_format_value(field_tree, hf_protobuf_field_number, tvb, offset, 0, field_number << 3, "%" PRIu64, field_number);
1248
0
        proto_item_set_generated(ti_field_number);
1249
1250
0
        hf_id_ptr = NULL;
1251
0
        if (pbf_as_hf && field_full_name) {
1252
0
            hf_id_ptr = (int*)g_hash_table_lookup(pbf_hf_hash, field_full_name);
1253
0
            DISSECTOR_ASSERT_HINT(hf_id_ptr && (*hf_id_ptr) > 0, "hf must have been initialized properly");
1254
0
        }
1255
1256
0
        pbf_tree = field_tree;
1257
0
        if (pbf_as_hf && hf_id_ptr && !show_details) {
1258
            /* set ti_field (Field(x)) item hidden if there is header_field */
1259
0
            proto_item_set_hidden(ti_field);
1260
0
            pbf_tree = message_tree;
1261
0
        }
1262
1263
0
        ti_value = ti_pbf = NULL;
1264
0
        string_value = NULL;
1265
0
        size = 0;
1266
1267
0
        if (dumper) {
1268
0
            json_dumper_set_member_name(dumper, field_name);
1269
0
        }
1270
1271
0
        switch (field_type)
1272
0
        {
1273
0
        case PROTOBUF_TYPE_INT32:
1274
0
        case PROTOBUF_TYPE_SINT32:
1275
0
        case PROTOBUF_TYPE_SFIXED32:
1276
0
            int32_value = pbw_FieldDescriptor_default_value_int32(field_desc);
1277
0
            ti_value = proto_tree_add_int(field_tree, hf_protobuf_value_int32, tvb, offset, 0, int32_value);
1278
0
            proto_item_append_text(ti_field, " %d", int32_value);
1279
0
            if (hf_id_ptr) {
1280
0
                ti_pbf = proto_tree_add_int(pbf_tree, *hf_id_ptr, tvb, offset, 0, int32_value);
1281
0
            }
1282
0
            if (dumper) {
1283
0
                json_dumper_value_anyf(dumper, "%d", int32_value);
1284
0
            }
1285
0
            break;
1286
1287
0
        case PROTOBUF_TYPE_INT64:
1288
0
        case PROTOBUF_TYPE_SINT64:
1289
0
        case PROTOBUF_TYPE_SFIXED64:
1290
0
            int64_value = pbw_FieldDescriptor_default_value_int64(field_desc);
1291
0
            ti_value = proto_tree_add_int64(field_tree, hf_protobuf_value_int64, tvb, offset, 0, int64_value);
1292
0
            proto_item_append_text(ti_field, " %" PRId64, int64_value);
1293
0
            if (hf_id_ptr) {
1294
0
                ti_pbf = proto_tree_add_int64(pbf_tree, *hf_id_ptr, tvb, offset, 0, int64_value);
1295
0
            }
1296
0
            if (dumper) {
1297
0
                json_dumper_value_anyf(dumper, "\"%" PRId64 "\"", int64_value);
1298
0
            }
1299
0
            break;
1300
1301
0
        case PROTOBUF_TYPE_UINT32:
1302
0
        case PROTOBUF_TYPE_FIXED32:
1303
0
            uint32_value = pbw_FieldDescriptor_default_value_uint32(field_desc);
1304
0
            ti_value = proto_tree_add_uint(field_tree, hf_protobuf_value_uint32, tvb, offset, 0, uint32_value);
1305
0
            proto_item_append_text(ti_field, " %u", uint32_value);
1306
0
            if (hf_id_ptr) {
1307
0
                ti_pbf = proto_tree_add_uint(pbf_tree, *hf_id_ptr, tvb, offset, 0, uint32_value);
1308
0
            }
1309
0
            if (dumper) {
1310
0
                json_dumper_value_anyf(dumper, "%u", uint32_value);
1311
0
            }
1312
0
            break;
1313
1314
0
        case PROTOBUF_TYPE_UINT64:
1315
0
        case PROTOBUF_TYPE_FIXED64:
1316
0
            uint64_value = pbw_FieldDescriptor_default_value_uint64(field_desc);
1317
0
            ti_value = proto_tree_add_uint64(field_tree, hf_protobuf_value_uint64, tvb, offset, 0, uint64_value);
1318
0
            proto_item_append_text(ti_field, " %" PRIu64, uint64_value);
1319
0
            if (hf_id_ptr) {
1320
0
                ti_pbf = proto_tree_add_uint64(pbf_tree, *hf_id_ptr, tvb, offset, 0, uint64_value);
1321
0
            }
1322
0
            if (dumper) {
1323
0
                json_dumper_value_anyf(dumper, "\"%" PRIu64 "\"", uint64_value);
1324
0
            }
1325
0
            break;
1326
1327
0
        case PROTOBUF_TYPE_BOOL:
1328
0
            bool_value = pbw_FieldDescriptor_default_value_bool(field_desc);
1329
0
            ti_value = proto_tree_add_boolean(field_tree, hf_protobuf_value_bool, tvb, offset, 0, bool_value);
1330
0
            proto_item_append_text(ti_field, " %s", bool_value ? "true" : "false");
1331
0
            if (hf_id_ptr) {
1332
0
                ti_pbf = proto_tree_add_boolean(pbf_tree, *hf_id_ptr, tvb, offset, 0, bool_value);
1333
0
            }
1334
0
            if (dumper) {
1335
0
                json_dumper_value_anyf(dumper, bool_value ? "true" : "false");
1336
0
            }
1337
0
            break;
1338
1339
0
        case PROTOBUF_TYPE_DOUBLE:
1340
0
            double_value = pbw_FieldDescriptor_default_value_double(field_desc);
1341
0
            ti_value = proto_tree_add_double(field_tree, hf_protobuf_value_double, tvb, offset, 0, double_value);
1342
0
            proto_item_append_text(ti_field, " %lf", double_value);
1343
0
            if (hf_id_ptr) {
1344
0
                ti_pbf = proto_tree_add_double(pbf_tree, *hf_id_ptr, tvb, offset, 0, double_value);
1345
0
            }
1346
0
            if (dumper) {
1347
0
                json_dumper_value_double(dumper, double_value);
1348
0
            }
1349
0
            break;
1350
1351
0
        case PROTOBUF_TYPE_FLOAT:
1352
0
            float_value = pbw_FieldDescriptor_default_value_float(field_desc);
1353
0
            ti_value = proto_tree_add_float(field_tree, hf_protobuf_value_float, tvb, offset, 0, float_value);
1354
0
            proto_item_append_text(ti_field, " %f", float_value);
1355
0
            if (hf_id_ptr) {
1356
0
                ti_pbf = proto_tree_add_float(pbf_tree, *hf_id_ptr, tvb, offset, 0, float_value);
1357
0
            }
1358
0
            if (dumper) {
1359
0
                json_dumper_value_anyf(dumper, "%f", float_value);
1360
0
            }
1361
0
            break;
1362
1363
0
        case PROTOBUF_TYPE_BYTES:
1364
0
            string_value = pbw_FieldDescriptor_default_value_string(field_desc, &size);
1365
0
            if (dumper) {
1366
0
                if (string_value == NULL) {
1367
0
                    json_dumper_value_string(dumper, "");
1368
0
                } else {
1369
0
                    json_dumper_begin_base64(dumper);
1370
0
                    json_dumper_write_base64(dumper, (const unsigned char *)string_value, size);
1371
0
                    json_dumper_end_base64(dumper);
1372
0
                }
1373
0
            }
1374
0
            if (!dissect_bytes_as_string) {
1375
0
                if (string_value == NULL) {
1376
0
                    ti_value = proto_tree_add_bytes_format_value(field_tree, hf_protobuf_value_data, tvb, offset, 0, NULL, "%s", "");
1377
0
                } else {
1378
0
                    ti_value = proto_tree_add_bytes_with_length(field_tree, hf_protobuf_value_data, tvb, offset, 0, (const uint8_t*)string_value, size);
1379
0
                }
1380
0
                proto_item_append_text(ti_field, " (%d bytes)", size);
1381
                /* the type of *hf_id_ptr MUST be FT_BYTES now */
1382
0
                if (hf_id_ptr) {
1383
0
                    if (string_value == NULL) {
1384
0
                        ti_pbf = proto_tree_add_bytes_format_value(pbf_tree, *hf_id_ptr, tvb, offset, 0, NULL, "%s", "");
1385
0
                    } else {
1386
0
                        ti_pbf = proto_tree_add_bytes_with_length(pbf_tree, *hf_id_ptr, tvb, offset, 0, (const uint8_t*)string_value, size);
1387
0
                    }
1388
0
                }
1389
0
                break;
1390
0
            }
1391
            /* or continue dissect BYTES as STRING */
1392
            /* FALLTHROUGH */
1393
0
        case PROTOBUF_TYPE_STRING:
1394
0
            if (string_value == NULL) {
1395
0
                string_value = pbw_FieldDescriptor_default_value_string(field_desc, &size);
1396
0
            }
1397
0
            if (string_value == NULL) {
1398
0
                string_value = "";
1399
0
            }
1400
0
            ti_value = proto_tree_add_string(field_tree, hf_protobuf_value_string, tvb, offset, 0, string_value);
1401
0
            proto_item_append_text(ti_field, " %s", string_value);
1402
0
            if (hf_id_ptr) {
1403
0
                ti_pbf = proto_tree_add_string(pbf_tree, *hf_id_ptr, tvb, offset, 0, string_value);
1404
0
            }
1405
0
            if (dumper && field_type == PROTOBUF_TYPE_STRING) {
1406
                /* JSON view will ignore the dissect_bytes_as_string option */
1407
0
                json_dumper_value_string(dumper, string_value);
1408
0
            }
1409
0
            break;
1410
1411
0
        case PROTOBUF_TYPE_ENUM:
1412
0
            enum_value_desc = pbw_FieldDescriptor_default_value_enum(field_desc);
1413
0
            if (enum_value_desc) {
1414
0
                int32_value = pbw_EnumValueDescriptor_number(enum_value_desc);
1415
0
                enum_value_name = pbw_EnumValueDescriptor_name(enum_value_desc);
1416
0
                ti_value = proto_tree_add_int(field_tree, hf_protobuf_value_int32, tvb, offset, 0, int32_value);
1417
0
                if (enum_value_name) { /* show enum value name */
1418
0
                    proto_item_append_text(ti_field, " %s(%d)", enum_value_name, int32_value);
1419
0
                    proto_item_append_text(ti_value, " (%s)", enum_value_name);
1420
0
                } else {
1421
0
                    proto_item_append_text(ti_field, " %d", int32_value);
1422
0
                }
1423
0
                if (hf_id_ptr) {
1424
0
                    ti_pbf = proto_tree_add_int(pbf_tree, *hf_id_ptr, tvb, offset, 0, int32_value);
1425
0
                }
1426
0
                if (dumper) {
1427
0
                    json_dumper_value_string(dumper, enum_value_name);
1428
0
                }
1429
0
                break;
1430
0
            } else {
1431
0
                expert_add_info_format(pinfo, ti_message, &ei_protobuf_default_value_error, "enum value of field '%s' not found in *.proto!", field_name);
1432
0
            }
1433
0
            break;
1434
1435
0
        default:
1436
            /* should not get here */
1437
0
            break;
1438
0
        }
1439
1440
0
        proto_item_append_text(ti_field, " (%s)", val_to_str(pinfo->pool, field_type, protobuf_field_type, "Unknown type (%d)"));
1441
1442
0
        if (ti_value) {
1443
0
            proto_item_set_generated(ti_value);
1444
0
        }
1445
0
        if (ti_pbf) {
1446
0
            proto_item_set_generated(ti_pbf);
1447
0
        }
1448
1449
0
        if (!show_details) {
1450
0
            proto_item_set_hidden(ti_field_name);
1451
0
            proto_item_set_hidden(ti_field_type);
1452
0
            proto_item_set_hidden(ti_field_number);
1453
0
            if (ti_value && (field_type != PROTOBUF_TYPE_BYTES || dissect_bytes_as_string)) {
1454
0
                proto_item_set_hidden(ti_value);
1455
0
            }
1456
0
        }
1457
0
    }
1458
0
}
1459
1460
static void
1461
// NOLINTNEXTLINE(misc-no-recursion)
1462
dissect_protobuf_message(tvbuff_t *tvb, unsigned offset, unsigned length, packet_info *pinfo, proto_tree *protobuf_tree,
1463
    const PbwDescriptor* message_desc, int hf_msg, bool is_top_level, json_dumper *dumper, wmem_allocator_t* scope, char** retval)
1464
0
{
1465
0
    proto_tree *message_tree;
1466
0
    proto_item *ti_message, *ti;
1467
0
    const char* message_name = "<UNKNOWN>";
1468
0
    unsigned max_offset = offset + length;
1469
0
    const PbwFieldDescriptor* field_desc;
1470
0
    const PbwFieldDescriptor* prev_field_desc = NULL;
1471
0
    wmem_map_t* parsed_fields = NULL; /* store parsed field numbers. */
1472
0
    nstime_t timestamp = { 0 };
1473
0
    char* value_label = NULL; /* The label representing the value of some wellknown message, such as google.protobuf.Timestamp */
1474
1475
0
    if (message_desc) {
1476
0
        message_name = pbw_Descriptor_full_name(message_desc);
1477
1478
0
        if (add_default_value) {
1479
0
            parsed_fields = wmem_map_new(pinfo->pool, g_direct_hash, g_direct_equal);
1480
0
        }
1481
1482
0
        if (strcmp(message_name, "google.protobuf.Timestamp") == 0) {
1483
            /* parse this message as timestamp */
1484
0
            tvb_get_protobuf_time(tvb, offset, length, &timestamp);
1485
0
            if (!nstime_is_unset(&timestamp)) {
1486
0
                value_label = abs_time_to_rfc3339(scope ? scope : pinfo->pool, &timestamp, use_utc_fmt);
1487
0
                if (hf_msg > 0) {
1488
0
                    ti = proto_tree_add_time_format_value(protobuf_tree, hf_msg, tvb, offset, length, &timestamp, "%s", value_label);
1489
0
                    protobuf_tree = proto_item_add_subtree(ti, ett_protobuf_message);
1490
0
                }
1491
0
                if (dumper) {
1492
0
                    json_dumper_value_string(dumper, value_label);
1493
0
                    dumper = NULL; /* this message will not dump as JSON object */
1494
0
                }
1495
0
            } else {
1496
0
                expert_add_info(pinfo, proto_tree_get_parent(protobuf_tree), &ei_protobuf_failed_parse_field);
1497
0
            }
1498
0
        } else if (hf_msg > 0) {
1499
0
            ti = proto_tree_add_bytes_format_value(protobuf_tree, hf_msg, tvb, offset, length, NULL, "(%u bytes)", length);
1500
0
            protobuf_tree = proto_item_add_subtree(ti, ett_protobuf_message);
1501
0
        }
1502
0
    }
1503
1504
0
    if (pbf_as_hf && message_desc) {
1505
        /* support filtering with message name as wireshark field name */
1506
0
        int *hf_id_ptr = (int*)g_hash_table_lookup(pbf_hf_hash, message_name);
1507
0
        DISSECTOR_ASSERT_HINT(hf_id_ptr && (*hf_id_ptr) > 0, "hf of message should initialized properly");
1508
0
        ti_message = proto_tree_add_item(protobuf_tree, *hf_id_ptr, tvb, offset, length, ENC_NA);
1509
0
        proto_item_set_text(ti_message, "Message: %s", message_name);
1510
1511
0
        if (show_details) {
1512
            /* show "Message" item and add its fields under this item */
1513
0
            message_tree = proto_item_add_subtree(ti_message, ett_protobuf_message);
1514
0
        } else {
1515
            /* hidden "Message" item (but still can be filtered by wireshark field name with "pbm.xxx" prefix),
1516
             * and add its fields under the parent (field or protobuf protocol) item directly */
1517
0
            proto_item_set_hidden(ti_message);
1518
0
            message_tree = protobuf_tree;
1519
0
            ti_message = proto_tree_get_parent(message_tree);
1520
0
            proto_item_append_text(ti_message, " (Message: %s)", message_name);
1521
0
        }
1522
0
    } else {
1523
0
        message_tree = proto_tree_add_subtree_format(protobuf_tree, tvb, offset, length, ett_protobuf_message,
1524
0
            &ti_message, "Message: %s", message_name);
1525
0
    }
1526
1527
0
    if (is_top_level) {
1528
0
        col_append_sep_fstr(pinfo->cinfo, COL_PROTOCOL, "/", "PB(%s)", message_name);
1529
0
    }
1530
1531
    /* support filtering with message name */
1532
0
    ti = proto_tree_add_string(message_tree, hf_protobuf_message_name, tvb, offset, length, message_name);
1533
0
    proto_item_set_generated(ti);
1534
0
    if (!show_details) {
1535
0
        proto_item_set_hidden(ti);
1536
0
    }
1537
1538
    /* create object for json */
1539
0
    if (message_desc && dumper) {
1540
0
        json_dumper_begin_object(dumper);
1541
0
    }
1542
1543
    /* each time we dissect one protobuf field. */
1544
0
    increment_dissection_depth(pinfo);
1545
0
    while (offset < max_offset)
1546
0
    {
1547
0
        field_desc = NULL;
1548
0
        if (!dissect_one_protobuf_field(tvb, &offset, max_offset - offset, pinfo, message_tree, message_desc,
1549
0
            is_top_level, &field_desc, prev_field_desc, dumper)) {
1550
0
            break;
1551
0
        }
1552
1553
0
        if (parsed_fields && field_desc) {
1554
0
            wmem_map_insert(parsed_fields, GINT_TO_POINTER(pbw_FieldDescriptor_number(field_desc)), GINT_TO_POINTER(1));
1555
0
        }
1556
1557
        /* Only set this on success - if we didn't dissect a field, we
1558
         * may still need to close the JSON array associated with the
1559
         * last successfully dissected field. */
1560
0
        prev_field_desc = field_desc;
1561
0
    }
1562
0
    decrement_dissection_depth(pinfo);
1563
1564
0
    if (dumper && prev_field_desc && pbw_FieldDescriptor_is_repeated(prev_field_desc)) {
1565
        /* The last field is repeated field, we close the JSON array */
1566
0
        json_dumper_end_array(dumper);
1567
0
    }
1568
1569
    /* add default values for missing fields */
1570
0
    if (add_default_value && parsed_fields) {
1571
0
        add_missing_fields_with_default_values(tvb, offset, pinfo, message_tree, message_desc, parsed_fields, dumper);
1572
0
    }
1573
1574
0
    if (message_desc && dumper) {
1575
0
        json_dumper_end_object(dumper);
1576
0
    }
1577
1578
0
    if (value_label) {
1579
0
        ti = proto_tree_add_item(message_tree, hf_text_only, tvb, offset, length, ENC_NA);
1580
0
        proto_item_set_text(ti, "[Message Value: %s]", value_label);
1581
0
    }
1582
1583
0
    if (retval) {
1584
0
        *retval = value_label;
1585
0
    }
1586
0
}
1587
1588
/* try to find message type by UDP port */
1589
static const PbwDescriptor*
1590
find_message_type_by_udp_port(packet_info *pinfo)
1591
0
{
1592
0
    range_t* udp_port_range;
1593
0
    const char* message_type;
1594
0
    unsigned i;
1595
0
    for (i = 0; i < num_protobuf_udp_message_types; ++i) {
1596
0
        udp_port_range = protobuf_udp_message_types[i].udp_port_range;
1597
0
        if (value_is_in_range(udp_port_range, pinfo->srcport)
1598
0
            || value_is_in_range(udp_port_range, pinfo->destport))
1599
0
        {
1600
0
            message_type = protobuf_udp_message_types[i].message_type;
1601
0
            if (message_type && strlen(message_type) > 0) {
1602
0
                return pbw_DescriptorPool_FindMessageTypeByName(pbw_pool, message_type);
1603
0
            }
1604
0
        }
1605
0
    }
1606
0
    return NULL;
1607
0
}
1608
1609
static bool
1610
// NOLINTNEXTLINE(misc-no-recursion)
1611
uri_matches_pattern(const char *request_uri, const char *uri_pattern, int depth)
1612
0
{
1613
    /* Arbitrary recursion depth limit.. */
1614
0
    if (depth > 32) {
1615
0
        return false;
1616
0
    }
1617
1618
    /* Exact match */
1619
0
    if (strcmp(request_uri, uri_pattern)==0) {
1620
0
        return true;
1621
0
    }
1622
1623
    /* Match if both strings now empty */
1624
0
    if (strlen(uri_pattern)==0 && strlen(request_uri)==0) {
1625
0
        return true;
1626
0
    }
1627
1628
    /* Fail if remaining, unmatched pattern but reached end of uri */
1629
0
    if (strlen(uri_pattern)>0 && strlen(request_uri)==0) {
1630
0
        return false;
1631
0
    }
1632
1633
    /* If remainder of pattern is just '*', it matches */
1634
0
    if (strlen(uri_pattern)==1 && uri_pattern[0] == '*') {
1635
0
        return true;
1636
0
    }
1637
1638
    /* If next uri_pattern char is not '*', needs to match exactly */
1639
0
    if (strlen(uri_pattern) && uri_pattern[0] != '*') {
1640
1641
        /* Skip identical characters */
1642
0
        int n;
1643
0
        for (n=0; strlen(request_uri+n) && strlen(request_uri+n) && uri_pattern[n] != '*'; n++) {
1644
0
            if (request_uri[n] == uri_pattern[n]) {
1645
0
                continue;
1646
0
            }
1647
0
            else {
1648
                /* Fail if non-wildcarded comparison fails */
1649
0
                return false;
1650
0
            }
1651
0
        }
1652
1653
        /* Recursively call n characters along */
1654
0
        return uri_matches_pattern(request_uri+n, uri_pattern+n, depth+1);
1655
0
    }
1656
1657
0
    if (strlen(uri_pattern) && uri_pattern[0] == '*') {
1658
        /* We are at a '*'. Test with/without moving past it now */
1659
0
        return (uri_matches_pattern(request_uri+1, uri_pattern,   depth+1) ||
1660
0
                uri_matches_pattern(request_uri+1, uri_pattern+1, depth+1));
1661
0
    }
1662
1663
0
    return false;
1664
0
}
1665
1666
1667
static int
1668
dissect_protobuf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
1669
0
{
1670
0
    proto_item *ti;
1671
0
    proto_tree *protobuf_tree, *protobuf_json_tree;
1672
0
    unsigned offset = 0;
1673
0
    unsigned i;
1674
0
    const PbwDescriptor* message_desc = NULL;
1675
0
    const char* data_str = NULL;
1676
0
    char *json_str, *p;
1677
1678
    /* initialize only the first time the protobuf dissector is called */
1679
0
    if (!protobuf_dissector_called) {
1680
0
        protobuf_dissector_called = true;
1681
0
        protobuf_reinit(PREFS_UPDATE_ALL);
1682
0
    }
1683
1684
    /* may set col_set_str(pinfo->cinfo, COL_PROTOCOL, "PROTOBUF"); */
1685
0
    col_append_str(pinfo->cinfo, COL_INFO, " (PROTOBUF)");
1686
1687
0
    ti = proto_tree_add_item(tree, proto_protobuf, tvb, 0, -1, ENC_NA);
1688
0
    protobuf_tree = proto_item_add_subtree(ti, ett_protobuf);
1689
1690
    /* The dissectors written in Lua are not able to specify the message type by data
1691
       parameter when calling protobuf dissector. But they can tell Protobuf dissector
1692
       the message type by the value of pinfo->private_table["pb_msg_type"]. */
1693
0
    if (data) {
1694
0
        data_str = (const char*)data;
1695
0
    } else if (pinfo->private_table) {
1696
0
        data_str = (const char*)g_hash_table_lookup(pinfo->private_table, "pb_msg_type");
1697
0
    }
1698
1699
0
    if (data_str) {
1700
        /* The data_str has two formats:
1701
        * (1) Come from GRPC dissector like:
1702
        *    http2_content_type "," http2_path "," ("request" / "response")
1703
        * According to grpc wire format guide, it will be:
1704
        *    "application/grpc" ["+proto"] "," "/" service-name "/" method-name "," ("request" / "response")
1705
        * For example:
1706
        *    application/grpc,/helloworld.Greeter/SayHello,request
1707
        * In this format, we will try to get real protobuf message type by method (service-name.method-name)
1708
        * and in/out type (request / response).
1709
        * (2) Come from other dissector which specifies message type directly, like:
1710
        *    "message," message_type_name
1711
        * For example:
1712
        *    message,helloworld.HelloRequest      (helloworld is package, HelloRequest is message type)
1713
        */
1714
0
        const char* message_info = strchr(data_str, ',');
1715
1716
0
        if (message_info) {
1717
0
            message_info++; /* ignore ',' */
1718
0
            proto_item_append_text(ti, ": %s", message_info);  /* append to proto item */
1719
1720
0
            if (g_str_has_prefix(data_str, "message,")) {
1721
                /* find message type by name directly */
1722
0
                message_desc = pbw_DescriptorPool_FindMessageTypeByName(pbw_pool, message_info);
1723
0
            } else /* if (g_str_has_prefix(data_str, "application/grpc,") */ {
1724
                /* get long method-name like: helloworld.Greeter.SayHello */
1725
0
                if (message_info[0] == '/') {
1726
0
                    message_info++; /* ignore first '/' */
1727
0
                }
1728
1729
0
                char** tmp_names = wmem_strsplit(pinfo->pool, message_info, ",", 2);
1730
0
                char* method_name = (tmp_names[0]) ? tmp_names[0] : NULL;
1731
0
                char* direction_type = (method_name && tmp_names[1]) ? tmp_names[1] : NULL;
1732
1733
                /* replace all '/' to '.', so helloworld.Greeter/SayHello converted to helloworld.Greeter.SayHello */
1734
0
                if (method_name) {
1735
0
                    for (i = 0; method_name[i] != 0; i++) {
1736
0
                        if (method_name[i] == '/') {
1737
0
                            method_name[i] = '.';
1738
0
                        }
1739
0
                    }
1740
0
                }
1741
1742
                /* find message type according to method descriptor */
1743
0
                if (direction_type) {
1744
0
                    const PbwMethodDescriptor* method_desc = pbw_DescriptorPool_FindMethodByName(pbw_pool, method_name);
1745
0
                    if (method_desc) {
1746
0
                        message_desc = strcmp(direction_type, "request") == 0
1747
0
                            ? pbw_MethodDescriptor_input_type(method_desc)
1748
0
                            : pbw_MethodDescriptor_output_type(method_desc);
1749
0
                    }
1750
0
                }
1751
0
            }
1752
1753
0
            if (message_desc) {
1754
0
                const char* message_full_name = pbw_Descriptor_full_name(message_desc);
1755
0
                if (message_full_name) {
1756
0
                    col_append_fstr(pinfo->cinfo, COL_INFO, " %s", message_full_name);
1757
0
                }
1758
0
            }
1759
0
        }
1760
1761
0
    } else if (pinfo->ptype == PT_UDP) {
1762
0
        message_desc = find_message_type_by_udp_port(pinfo);
1763
0
    }
1764
1765
0
    if (!message_desc) {
1766
        /* If this was inside an HTTP request, do we have a message type assigned to this URI? */
1767
0
        http_req_res_t  *curr = (http_req_res_t *)p_get_proto_data(wmem_file_scope(), pinfo,
1768
0
                                                                   proto_http, HTTP_PROTO_DATA_REQRES);
1769
0
        if (curr) {
1770
0
            if (curr->request_uri) {
1771
0
                for (unsigned n=0; n < num_protobuf_uri_message_types; n++) {
1772
0
                    if (uri_matches_pattern(curr->request_uri, protobuf_uri_message_types[n].uri, 1 /* depth */)) {
1773
0
                        if (strlen(protobuf_uri_message_types[n].message_type)) {
1774
                            /* Lookup message type for matching URI */
1775
0
                            message_desc = pbw_DescriptorPool_FindMessageTypeByName(pbw_pool,
1776
0
                                                                                    protobuf_uri_message_types[n].message_type);
1777
0
                        }
1778
                        /* Found a matched URI, so stop looking */
1779
0
                        break;
1780
0
                    }
1781
0
                }
1782
0
            }
1783
0
        }
1784
0
    }
1785
1786
    /* If *still* have no schema and a default is configured, try to use that */
1787
0
    if (!message_desc && strlen(default_message_type)) {
1788
0
        message_desc = pbw_DescriptorPool_FindMessageTypeByName(pbw_pool,
1789
0
                                                                default_message_type);
1790
0
    }
1791
1792
0
    if (display_json_mapping && message_desc) {
1793
0
        json_dumper dumper = {
1794
0
            .output_string = g_string_new(NULL),
1795
0
            .flags = JSON_DUMPER_FLAGS_PRETTY_PRINT | JSON_DUMPER_FLAGS_NO_DEBUG,
1796
0
        };
1797
1798
        /* Dissecting can throw an exception, ideally CLEANUP_PUSH and _POP
1799
         * should be used to free the GString to avoid a leak.
1800
         */
1801
0
        dissect_protobuf_message(tvb, offset, tvb_reported_length_remaining(tvb, offset), pinfo,
1802
0
                                 protobuf_tree, message_desc,
1803
0
                                 -1,  // no hf item
1804
0
                                 pinfo->ptype == PT_UDP, // is_top_level
1805
0
                                 &dumper,
1806
0
                                 NULL,  // scope
1807
0
                                 NULL); // retval
1808
1809
0
        DISSECTOR_ASSERT_HINT(json_dumper_finish(&dumper), "Bad json_dumper state");
1810
0
        ti = proto_tree_add_item(tree, proto_protobuf_json_mapping, tvb, 0, -1, ENC_NA);
1811
0
        protobuf_json_tree = proto_item_add_subtree(ti, ett_protobuf_json);
1812
1813
0
        json_str = g_string_free(dumper.output_string, false);
1814
0
        if (json_str != NULL) {
1815
0
            p = json_str;
1816
            /* add each line of json to the protobuf_json_tree */
1817
0
            do {
1818
0
                char *q = strchr(p, '\n');
1819
0
                if (q != NULL) {
1820
0
                    *(q++) = '\0'; /* replace the '\n' to '\0' */
1821
0
                } /* else (q == NULL) means this is the last line of the JSON */
1822
0
                proto_tree_add_string_format(protobuf_json_tree, hf_json_mapping_line, tvb, 0, -1, p, "%s", p);
1823
0
                p = q;
1824
0
            } while (p);
1825
1826
0
            g_free(json_str);
1827
0
        }
1828
0
    } else {
1829
0
        dissect_protobuf_message(tvb, offset, tvb_reported_length_remaining(tvb, offset), pinfo,
1830
0
                                 protobuf_tree, message_desc,
1831
0
                                 -1, // no hf item
1832
0
                                 true,   // is_top_level
1833
0
                                 NULL,   // dumper
1834
0
                                 NULL,   // scope
1835
0
                                 NULL);  // retval
1836
0
    }
1837
1838
0
    return tvb_captured_length(tvb);
1839
0
}
1840
1841
static bool
1842
// NOLINTNEXTLINE(misc-no-recursion)
1843
load_all_files_in_dir(PbwDescriptorPool* pool, const char* dir_path, unsigned depth)
1844
0
{
1845
0
    WS_DIR        *dir;             /* scanned directory */
1846
0
    WS_DIRENT     *file;            /* current file */
1847
0
    const char    *dot;
1848
0
    const char    *name;            /* current file or dir name (without parent dir path) */
1849
0
    char          *path;            /* sub file or dir path of dir_path */
1850
1851
0
    if (depth > prefs.gui_max_tree_depth) {
1852
0
        return false;
1853
0
    }
1854
1855
0
    if (g_file_test(dir_path, G_FILE_TEST_IS_DIR)) {
1856
0
        if ((dir = ws_dir_open(dir_path, 0, NULL)) != NULL) {
1857
0
            while ((file = ws_dir_read_name(dir)) != NULL) {
1858
                /* load all files with '.proto' suffix */
1859
0
                name = ws_dir_get_name(file);
1860
0
                path = g_build_filename(dir_path, name, NULL);
1861
0
                dot = strrchr(name, '.');
1862
0
                if (dot && g_ascii_strcasecmp(dot + 1, "proto") == 0) {
1863
                    /* Note: pbw_load_proto_file support absolute or relative (to one of search paths) path */
1864
0
                    if (pbw_load_proto_file(pool, path) != 0) {
1865
0
                        g_free(path);
1866
0
                        ws_dir_close(dir);
1867
0
                        return false;
1868
0
                    }
1869
0
                } else {
1870
0
                    if (!load_all_files_in_dir(pool, path, depth + 1)) {
1871
0
                        g_free(path);
1872
0
                        ws_dir_close(dir);
1873
0
                        return false;
1874
0
                    }
1875
0
                }
1876
0
                g_free(path);
1877
0
            }
1878
0
            ws_dir_close(dir);
1879
0
        }
1880
0
    }
1881
0
    return true;
1882
0
}
1883
1884
/* There might be a lot of errors to be found during parsing .proto files.
1885
   We buffer the errors first, and print them in one list finally. */
1886
static wmem_strbuf_t* err_msg_buf;
1887
0
#define MIN_ERR_STR_BUF_SIZE 512
1888
#define MAX_ERR_STR_BUF_SIZE 1024
1889
1890
static void
1891
buffer_error(const char *fmt, ...)
1892
0
{
1893
0
    va_list ap;
1894
0
    va_start(ap, fmt);
1895
1896
0
    if (err_msg_buf == NULL)
1897
0
        err_msg_buf = wmem_strbuf_new_sized(wmem_epan_scope(), MIN_ERR_STR_BUF_SIZE);
1898
1899
0
    wmem_strbuf_append_vprintf(err_msg_buf, fmt, ap);
1900
1901
0
    va_end(ap);
1902
0
}
1903
1904
static void
1905
flush_and_report_error(void)
1906
0
{
1907
0
    char* str;
1908
0
    if (err_msg_buf) {
1909
0
        str = wmem_strbuf_finalize(err_msg_buf);
1910
0
        err_msg_buf = NULL;
1911
0
        report_failure("Protobuf: Error(s):\n%s", str);
1912
0
        wmem_free(wmem_epan_scope(), str);
1913
0
    }
1914
0
}
1915
1916
static void
1917
update_protobuf_search_paths(void)
1918
14
{
1919
14
    protobuf_reinit(PREFS_UPDATE_PROTOBUF_SEARCH_PATHS);
1920
14
}
1921
1922
static void
1923
update_protobuf_udp_message_types(void)
1924
14
{
1925
14
    protobuf_reinit(PREFS_UPDATE_PROTOBUF_UDP_MESSAGE_TYPES);
1926
14
}
1927
1928
static void
1929
update_protobuf_uri_message_types(void)
1930
14
{
1931
14
    protobuf_reinit(PREFS_UPDATE_PROTOBUF_URI_MESSAGE_TYPES);
1932
14
}
1933
1934
1935
static void
1936
deregister_header_fields(void)
1937
0
{
1938
0
    if (dynamic_hf) {
1939
        /* Deregister all fields */
1940
0
        for (unsigned i = 0; i < dynamic_hf_size; i++) {
1941
0
            proto_deregister_field(proto_protobuf, *(dynamic_hf[i].p_id));
1942
0
            g_free(dynamic_hf[i].p_id);
1943
            /* dynamic_hf[i].name and .abbrev will be freed by proto_add_deregistered_data */
1944
0
        }
1945
1946
0
        proto_add_deregistered_data(dynamic_hf);
1947
0
        dynamic_hf = NULL;
1948
0
        dynamic_hf_size = 0;
1949
0
    }
1950
1951
0
    if (pbf_hf_hash) {
1952
0
        g_hash_table_destroy(pbf_hf_hash);
1953
0
        pbf_hf_hash = NULL;
1954
0
    }
1955
0
}
1956
1957
/* convert the names of the enum's values to value_string array */
1958
static value_string*
1959
enum_to_value_string(const PbwEnumDescriptor* enum_desc)
1960
0
{
1961
0
    value_string* vals;
1962
0
    int i, value_count;
1963
0
    if (enum_desc == NULL || (value_count = pbw_EnumDescriptor_value_count(enum_desc)) == 0) {
1964
0
        return NULL;
1965
0
    }
1966
1967
0
    vals = g_new0(value_string, value_count + 1);
1968
0
    for (i = 0; i < value_count; i++) {
1969
0
        const PbwEnumValueDescriptor* enum_value_desc = pbw_EnumDescriptor_value(enum_desc, i);
1970
0
        vals[i].value = pbw_EnumValueDescriptor_number(enum_value_desc);
1971
0
        vals[i].strptr = g_strdup(pbw_EnumValueDescriptor_name(enum_value_desc));
1972
0
    }
1973
    /* the strptr of last element of vals must be NULL */
1974
0
    return vals;
1975
0
}
1976
1977
/* create wireshark header fields according to each message's fields
1978
 * and add them into pbf_as_hf hash table */
1979
static void
1980
collect_fields(const PbwDescriptor* message, void* userdata)
1981
0
{
1982
0
    wmem_list_t* hf_list = (wmem_list_t*) userdata;
1983
0
    hf_register_info* hf;
1984
0
    const PbwFieldDescriptor* field_desc;
1985
0
    const PbwEnumDescriptor* enum_desc;
1986
0
    const PbwDescriptor* sub_msg_desc;
1987
0
    int i, field_type, total_num = pbw_Descriptor_field_count(message);
1988
1989
    /* add message as field */
1990
0
    hf = g_new0(hf_register_info, 1);
1991
0
    hf->p_id = g_new(int, 1);
1992
0
    *(hf->p_id) = -1;
1993
0
    hf->hfinfo.name = g_strdup(pbw_Descriptor_name(message));
1994
0
    hf->hfinfo.abbrev = ws_strdup_printf("pbm.%s", pbw_Descriptor_full_name(message));
1995
0
    hf->hfinfo.type = FT_BYTES;
1996
0
    hf->hfinfo.display = BASE_NONE;
1997
0
    wmem_list_append(hf_list, hf);
1998
0
    g_hash_table_insert(pbf_hf_hash, g_strdup(pbw_Descriptor_full_name(message)), hf->p_id);
1999
2000
    /* add fields of this message as fields */
2001
0
    for (i = 0; i < total_num; i++) {
2002
0
        field_desc = pbw_Descriptor_field(message, i);
2003
0
        field_type = pbw_FieldDescriptor_type(field_desc);
2004
0
        if (field_type <= PROTOBUF_TYPE_NONE ||field_type > PROTOBUF_MAX_FIELD_TYPE) {
2005
            /* not a valid field type */
2006
0
            continue;
2007
0
        }
2008
0
        hf = g_new0(hf_register_info, 1);
2009
0
        hf->p_id = g_new(int, 1);
2010
0
        *(hf->p_id) = -1;
2011
2012
0
        hf->hfinfo.name = g_strdup(pbw_FieldDescriptor_name(field_desc));
2013
0
        hf->hfinfo.abbrev = ws_strdup_printf("pbf.%s", pbw_FieldDescriptor_full_name(field_desc));
2014
0
        switch (field_type) {
2015
0
        case PROTOBUF_TYPE_DOUBLE:
2016
0
            hf->hfinfo.type = FT_DOUBLE;
2017
0
            hf->hfinfo.display = BASE_NONE;
2018
0
            break;
2019
2020
0
        case PROTOBUF_TYPE_FLOAT:
2021
0
            hf->hfinfo.type = FT_FLOAT;
2022
0
            hf->hfinfo.display = BASE_NONE;
2023
0
            break;
2024
2025
0
        case PROTOBUF_TYPE_INT64:
2026
0
        case PROTOBUF_TYPE_SFIXED64:
2027
0
        case PROTOBUF_TYPE_SINT64:
2028
0
            hf->hfinfo.type = FT_INT64;
2029
0
            hf->hfinfo.display = BASE_DEC;
2030
0
            break;
2031
2032
0
        case PROTOBUF_TYPE_UINT64:
2033
0
        case PROTOBUF_TYPE_FIXED64:
2034
0
            hf->hfinfo.type = FT_UINT64;
2035
0
            hf->hfinfo.display = BASE_DEC;
2036
0
            break;
2037
2038
0
        case PROTOBUF_TYPE_INT32:
2039
0
        case PROTOBUF_TYPE_SFIXED32:
2040
0
        case PROTOBUF_TYPE_SINT32:
2041
0
            hf->hfinfo.type = FT_INT32;
2042
0
            hf->hfinfo.display = BASE_DEC;
2043
0
            break;
2044
2045
0
        case PROTOBUF_TYPE_UINT32:
2046
0
        case PROTOBUF_TYPE_FIXED32:
2047
0
            hf->hfinfo.type = FT_UINT32;
2048
0
            hf->hfinfo.display = BASE_DEC;
2049
0
            break;
2050
2051
0
        case PROTOBUF_TYPE_ENUM:
2052
0
            hf->hfinfo.type = FT_INT32;
2053
0
            hf->hfinfo.display = BASE_DEC;
2054
0
            enum_desc = pbw_FieldDescriptor_enum_type(field_desc);
2055
0
            if (enum_desc) {
2056
0
                hf->hfinfo.strings = enum_to_value_string(enum_desc);
2057
0
            }
2058
0
            break;
2059
2060
0
        case PROTOBUF_TYPE_BOOL:
2061
0
            hf->hfinfo.type = FT_BOOLEAN;
2062
0
            hf->hfinfo.display = BASE_NONE;
2063
0
            break;
2064
2065
0
        case PROTOBUF_TYPE_BYTES:
2066
0
            hf->hfinfo.type = dissect_bytes_as_string ? FT_STRING : FT_BYTES;
2067
0
            hf->hfinfo.display = BASE_NONE;
2068
0
            break;
2069
2070
0
        case PROTOBUF_TYPE_STRING:
2071
0
            hf->hfinfo.type = FT_STRING;
2072
0
            hf->hfinfo.display = BASE_NONE;
2073
0
            break;
2074
2075
0
        case PROTOBUF_TYPE_GROUP:
2076
0
        case PROTOBUF_TYPE_MESSAGE:
2077
0
            sub_msg_desc = pbw_FieldDescriptor_message_type(field_desc);
2078
0
            if (sub_msg_desc && strcmp(pbw_Descriptor_full_name(sub_msg_desc), "google.protobuf.Timestamp") == 0) {
2079
0
                hf->hfinfo.type = FT_ABSOLUTE_TIME;
2080
0
                hf->hfinfo.display = use_utc_fmt ? ABSOLUTE_TIME_NTP_UTC : ABSOLUTE_TIME_LOCAL;
2081
0
            } else {
2082
0
                hf->hfinfo.type = FT_BYTES;
2083
0
                hf->hfinfo.display = BASE_NONE;
2084
0
            }
2085
0
            break;
2086
2087
0
        default:
2088
            /* should not happen */
2089
0
            break;
2090
0
        }
2091
2092
0
        wmem_list_append(hf_list, hf);
2093
0
        g_hash_table_insert(pbf_hf_hash, g_strdup(pbw_FieldDescriptor_full_name(field_desc)), hf->p_id);
2094
0
    }
2095
0
}
2096
2097
static void
2098
update_header_fields(bool force_reload)
2099
0
{
2100
0
    if (!force_reload && pbf_as_hf && dynamic_hf) {
2101
        /* If initialized, do nothing. */
2102
0
        return;
2103
0
    }
2104
0
    deregister_header_fields();
2105
2106
0
    if (pbf_as_hf) {
2107
0
        int i;
2108
0
        wmem_list_frame_t *it;
2109
0
        wmem_list_t* hf_list = wmem_list_new(NULL);
2110
0
        pbf_hf_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
2111
0
        DISSECTOR_ASSERT(pbw_pool);
2112
0
        pbw_foreach_message(pbw_pool, collect_fields, hf_list);
2113
0
        dynamic_hf_size = wmem_list_count(hf_list);
2114
0
        if (dynamic_hf_size == 0) {
2115
0
            deregister_header_fields();
2116
0
            return;
2117
0
        }
2118
0
        dynamic_hf = g_new0(hf_register_info, dynamic_hf_size);
2119
2120
0
        for (it = wmem_list_head(hf_list), i = 0; it; it = wmem_list_frame_next(it), i++) {
2121
0
            hf_register_info* hf = (hf_register_info*) wmem_list_frame_data(it);
2122
            /* copy hf_register_info structure */
2123
0
            dynamic_hf[i] = *hf;
2124
0
            g_free(hf);
2125
0
            HFILL_INIT(dynamic_hf[i]);
2126
0
        }
2127
2128
0
        wmem_destroy_list(hf_list);
2129
0
        proto_register_field_array(proto_protobuf, dynamic_hf, dynamic_hf_size);
2130
0
    }
2131
0
}
2132
2133
static void
2134
protobuf_reinit(int target)
2135
42
{
2136
42
    unsigned i;
2137
42
    char **source_paths;
2138
42
    GSList* it;
2139
42
    range_t* udp_port_range;
2140
42
    const char* message_type;
2141
42
    bool loading_completed = true;
2142
42
    size_t num_proto_paths;
2143
2144
42
    if (target & PREFS_UPDATE_PROTOBUF_UDP_MESSAGE_TYPES) {
2145
        /* delete protobuf dissector from old udp ports */
2146
28
        for (it = old_udp_port_ranges; it; it = it->next) {
2147
0
            udp_port_range = (range_t*) it->data;
2148
0
            dissector_delete_uint_range("udp.port", udp_port_range, protobuf_handle);
2149
0
            wmem_free(NULL, udp_port_range);
2150
0
        }
2151
2152
28
        if (old_udp_port_ranges) {
2153
0
            g_slist_free(old_udp_port_ranges);
2154
0
            old_udp_port_ranges = NULL;
2155
0
        }
2156
2157
        /* add protobuf dissector to new udp ports */
2158
28
        for (i = 0; i < num_protobuf_udp_message_types; ++i) {
2159
0
            udp_port_range = protobuf_udp_message_types[i].udp_port_range;
2160
0
            if (udp_port_range) {
2161
0
                udp_port_range = range_copy(NULL, udp_port_range);
2162
0
                old_udp_port_ranges = g_slist_append(old_udp_port_ranges, udp_port_range);
2163
0
                dissector_add_uint_range("udp.port", udp_port_range, protobuf_handle);
2164
0
            }
2165
0
        }
2166
28
    }
2167
2168
    /* loading .proto files and checking message types of UDP port will be done only after dissector is called */
2169
42
    if (!protobuf_dissector_called) {
2170
42
        return;
2171
42
    }
2172
2173
0
    if (target & PREFS_UPDATE_PROTOBUF_SEARCH_PATHS) {
2174
        /* convert protobuf_search_path_t array to char* array. should release by g_free().
2175
           Add the global and profile protobuf dirs to the search list, add 1 for the terminating null entry */
2176
0
        num_proto_paths = (size_t)num_protobuf_search_paths + 2;
2177
0
        source_paths = g_new0(char *, num_proto_paths + 1);
2178
2179
        /* Load the files in the global and personal config dirs */
2180
0
        source_paths[0] = get_datafile_path("protobuf", epan_get_environment_prefix());
2181
0
        source_paths[1] = get_persconffile_path("protobuf", true, epan_get_environment_prefix());
2182
2183
0
        for (i = 0; i < num_protobuf_search_paths; ++i) {
2184
0
            source_paths[i + 2] = protobuf_search_paths[i].path;
2185
0
        }
2186
2187
        /* init DescriptorPool of protobuf */
2188
0
        pbw_reinit_DescriptorPool(&pbw_pool, (const char **)source_paths, buffer_error);
2189
2190
        /* load all .proto files in the marked search paths, we can invoke FindMethodByName etc later. */
2191
0
        for (i = 0; i < num_proto_paths; ++i) {
2192
0
            if ((i < 2) || protobuf_search_paths[i - 2].load_all) {
2193
0
                if (!load_all_files_in_dir(pbw_pool, source_paths[i], 0)) {
2194
0
                    buffer_error("Protobuf: Loading .proto files action stopped!\n");
2195
0
                    loading_completed = false;
2196
0
                    break; /* stop loading when error occurs */
2197
0
                }
2198
0
            }
2199
0
        }
2200
2201
0
        g_free(source_paths[0]);
2202
0
        g_free(source_paths[1]);
2203
0
        g_free(source_paths);
2204
0
        update_header_fields(true);
2205
0
    }
2206
2207
    /* check if the message types of UDP port exist */
2208
0
    for (i = 0; i < num_protobuf_udp_message_types; ++i) {
2209
0
        message_type = protobuf_udp_message_types[i].message_type;
2210
0
        if (loading_completed && message_type && strlen(message_type) > 0
2211
0
            && pbw_DescriptorPool_FindMessageTypeByName(pbw_pool, message_type) == NULL) {
2212
0
            buffer_error("Protobuf: the message type \"%s\" of UDP Message Type preferences does not exist!\n", message_type);
2213
0
        }
2214
0
    }
2215
2216
    /* report error if encountered */
2217
0
    flush_and_report_error();
2218
0
}
2219
2220
void
2221
proto_register_protobuf(void)
2222
14
{
2223
14
    static hf_register_info hf[] = {
2224
14
        { &hf_protobuf_message_name,
2225
14
            { "Message Name", "protobuf.message.name",
2226
14
               FT_STRING, BASE_NONE, NULL, 0x0,
2227
14
              "The name of the protobuf message", HFILL }
2228
14
        },
2229
14
        { &hf_protobuf_field_name,
2230
14
            { "Field Name", "protobuf.field.name",
2231
14
               FT_STRING, BASE_NONE, NULL, 0x0,
2232
14
              "The name of the field", HFILL }
2233
14
        },
2234
14
        { &hf_protobuf_field_type,
2235
14
            { "Field Type", "protobuf.field.type",
2236
14
               FT_INT32, BASE_DEC, VALS(protobuf_field_type), 0x0,
2237
14
              "The type of the field", HFILL }
2238
14
        },
2239
14
        { &hf_protobuf_field_number,
2240
14
            { "Field Number", "protobuf.field.number",
2241
14
               FT_UINT64, BASE_DEC, NULL, UINT64_C(0xFFFFFFFFFFFFFFF8),
2242
14
              "Field number encoded in varint", HFILL }
2243
14
        },
2244
14
        { &hf_protobuf_wire_type,
2245
14
            { "Wire Type", "protobuf.field.wiretype",
2246
14
               FT_UINT8, BASE_DEC, VALS(protobuf_wire_type), 0x07,
2247
14
              "The Wire Type of the field.", HFILL }
2248
14
        },
2249
14
        { &hf_protobuf_value_length,
2250
14
            { "Value Length", "protobuf.field.value.length",
2251
14
               FT_UINT64, BASE_DEC, NULL, 0x0,
2252
14
              "The length of length-delimited field value.", HFILL }
2253
14
        },
2254
14
        { &hf_protobuf_value_data,
2255
14
            { "Value", "protobuf.field.value",
2256
14
               FT_BYTES, BASE_NONE, NULL, 0x0,
2257
14
              "The wire type determines value format", HFILL }
2258
14
        },
2259
14
        { &hf_protobuf_value_double,
2260
14
            { "Double", "protobuf.field.value.double",
2261
14
               FT_DOUBLE, BASE_NONE, NULL, 0x0,
2262
14
              "Dissect value as double", HFILL }
2263
14
        },
2264
14
        { &hf_protobuf_value_float,
2265
14
            { "Float", "protobuf.field.value.float",
2266
14
               FT_FLOAT, BASE_NONE, NULL, 0x0,
2267
14
              "Dissect value as float", HFILL }
2268
14
        },
2269
14
        { &hf_protobuf_value_int64,
2270
14
            { "Int64", "protobuf.field.value.int64",
2271
14
               FT_INT64, BASE_DEC, NULL, 0x0,
2272
14
              "Dissect value as int64", HFILL }
2273
14
        },
2274
14
        { &hf_protobuf_value_uint64,
2275
14
            { "Uint64", "protobuf.field.value.uint64",
2276
14
               FT_UINT64, BASE_DEC, NULL, 0x0,
2277
14
              "Dissect value as uint64", HFILL }
2278
14
        },
2279
14
        { &hf_protobuf_value_int32,
2280
14
            { "Int32", "protobuf.field.value.int32",
2281
14
               FT_INT32, BASE_DEC, NULL, 0x0,
2282
14
              "Dissect value as int32", HFILL }
2283
14
        },
2284
14
        { &hf_protobuf_value_uint32,
2285
14
            { "Uint32", "protobuf.field.value.uint32",
2286
14
               FT_UINT32, BASE_DEC, NULL, 0x0,
2287
14
              "Dissect value as uint32", HFILL }
2288
14
        },
2289
14
        { &hf_protobuf_value_bool,
2290
14
            { "Bool", "protobuf.field.value.bool",
2291
14
               FT_BOOLEAN, BASE_NONE, NULL, 0x0,
2292
14
              "Dissect value as bool", HFILL }
2293
14
        },
2294
14
        { &hf_protobuf_value_string,
2295
14
            { "String", "protobuf.field.value.string",
2296
14
               FT_STRING, BASE_NONE, NULL, 0x0,
2297
14
              "Dissect value as string", HFILL }
2298
14
        },
2299
14
        { &hf_protobuf_value_repeated,
2300
14
            { "Repeated", "protobuf.field.value.repeated",
2301
14
               FT_BYTES, BASE_NONE, NULL, 0x0,
2302
14
              "Dissect value as repeated", HFILL }
2303
14
        }
2304
14
    };
2305
2306
14
    static hf_register_info json_hf[] = {
2307
14
        { &hf_json_mapping_line,
2308
14
            { "JSON Mapping Line", "protobuf_json.line",
2309
14
               FT_STRING, BASE_NONE, NULL, 0x0,
2310
14
              "One line of the protobuf json mapping", HFILL }
2311
14
        }
2312
14
    };
2313
2314
14
    static int *ett[] = {
2315
14
        &ett_protobuf,
2316
14
        &ett_protobuf_message,
2317
14
        &ett_protobuf_field,
2318
14
        &ett_protobuf_value,
2319
14
        &ett_protobuf_packed_repeated
2320
14
    };
2321
2322
14
    static int *ett_json[] = {
2323
14
        &ett_protobuf_json
2324
14
    };
2325
2326
    /* Setup protocol expert items */
2327
14
    static ei_register_info ei[] = {
2328
14
        { &ei_protobuf_failed_parse_tag,
2329
14
          { "protobuf.failed_parse_tag", PI_MALFORMED, PI_ERROR,
2330
14
            "Failed to parse tag field", EXPFILL }
2331
14
        },
2332
14
        { &ei_protobuf_wire_type_invalid,
2333
14
          { "protobuf.field.wiretype.invalid", PI_PROTOCOL, PI_WARN,
2334
14
            "Unknown or unsupported wiretype", EXPFILL }
2335
14
        },
2336
14
        { &ei_protobuf_failed_parse_length_delimited_field,
2337
14
          { "protobuf.field.failed_parse_length_delimited_field", PI_MALFORMED, PI_ERROR,
2338
14
            "Failed to parse length delimited field", EXPFILL }
2339
14
        },
2340
14
        { &ei_protobuf_failed_parse_field,
2341
14
          { "protobuf.field.failed_parse_field", PI_MALFORMED, PI_ERROR,
2342
14
            "Failed to parse value field", EXPFILL }
2343
14
        },
2344
14
        { &ei_protobuf_message_type_not_found,
2345
14
          { "protobuf.field.message_type_not_found", PI_PROTOCOL, PI_WARN,
2346
14
            "Failed to find message type of a field", EXPFILL }
2347
14
        },
2348
14
        { &ei_protobuf_wire_type_not_support_packed_repeated,
2349
14
          { "protobuf.field.wire_type_not_support_packed_repeated", PI_MALFORMED, PI_ERROR,
2350
14
            "The wire type does not support protobuf packed repeated field", EXPFILL }
2351
14
        },
2352
14
        { &ei_protobuf_failed_parse_packed_repeated_field,
2353
14
          { "protobuf.field.failed_parse_packed_repeated_field", PI_MALFORMED, PI_ERROR,
2354
14
            "Failed to parse packed repeated field", EXPFILL }
2355
14
        },
2356
14
        { &ei_protobuf_missing_required_field,
2357
14
          { "protobuf.message.missing_required_field", PI_PROTOCOL, PI_WARN,
2358
14
            "The required field is not found in message payload", EXPFILL }
2359
14
        },
2360
14
        { &ei_protobuf_default_value_error,
2361
14
          { "protobuf.message.default_value_error", PI_PROTOCOL, PI_WARN,
2362
14
            "Parsing default value of a field error", EXPFILL }
2363
14
        },
2364
14
    };
2365
2366
14
    ENUM_VAL_T_ARRAY_STATIC(add_default_value_policy_vals);
2367
2368
14
    module_t *protobuf_module;
2369
14
    expert_module_t *expert_protobuf;
2370
2371
14
    static uat_field_t protobuf_search_paths_table_columns[] = {
2372
14
        UAT_FLD_DIRECTORYNAME(protobuf_search_paths, path, "Protobuf source directory", "Directory of the root of protobuf source files"),
2373
14
        UAT_FLD_BOOL(protobuf_search_paths, load_all, "Load all files", "Load all .proto files from this directory and its subdirectories"),
2374
14
        UAT_END_FIELDS
2375
14
    };
2376
14
    uat_t* protobuf_search_paths_uat;
2377
2378
14
    static uat_field_t protobuf_udp_message_types_table_columns[] = {
2379
14
        UAT_FLD_RANGE(protobuf_udp_message_types, udp_port_range, "UDP Ports", 0xFFFF, "UDP ports on which data will be dissected as protobuf"),
2380
14
        UAT_FLD_CSTRING(protobuf_udp_message_types, message_type, "Message Type", "Protobuf message type of data on these udp ports"),
2381
14
        UAT_END_FIELDS
2382
14
    };
2383
14
    uat_t* protobuf_udp_message_types_uat;
2384
2385
14
    static uat_field_t protobuf_uri_message_types_table_columns[] = {
2386
14
        UAT_FLD_CSTRING(protobuf_uri_message_type, uri, "HTTP URI", "URI for HTTP request carrying protobuf contents"),
2387
14
        UAT_FLD_CSTRING(protobuf_uri_message_type, message_type, "Message Type", "Protobuf message type of data on these URIs"),
2388
14
        UAT_END_FIELDS
2389
14
    };
2390
14
    uat_t* protobuf_uri_message_types_uat;
2391
2392
2393
14
    proto_protobuf = proto_register_protocol("Protocol Buffers", "ProtoBuf", "protobuf");
2394
14
    proto_protobuf_json_mapping = proto_register_protocol("Protocol Buffers (as JSON Mapping View)", "ProtoBuf_JSON", "protobuf_json");
2395
2396
14
    proto_register_field_array(proto_protobuf, hf, array_length(hf));
2397
14
    proto_register_subtree_array(ett, array_length(ett));
2398
2399
14
    proto_register_field_array(proto_protobuf_json_mapping, json_hf, array_length(json_hf));
2400
14
    proto_register_subtree_array(ett_json, array_length(ett_json));
2401
2402
14
    protobuf_module = prefs_register_protocol(proto_protobuf, proto_reg_handoff_protobuf);
2403
2404
14
    prefs_register_bool_preference(protobuf_module, "preload_protos",
2405
14
        "Load .proto files on startup.",
2406
14
        "Load .proto files when Wireshark starts. By default, the .proto files are loaded only"
2407
14
        " when the Protobuf dissector is called for the first time.",
2408
14
        &preload_protos);
2409
2410
14
    protobuf_search_paths_uat = uat_new("Protobuf Search Paths",
2411
14
        sizeof(protobuf_search_path_t),
2412
14
        "protobuf_search_paths",
2413
14
        true,
2414
14
        &protobuf_search_paths,
2415
14
        &num_protobuf_search_paths,
2416
14
        UAT_AFFECTS_DISSECTION | UAT_AFFECTS_FIELDS,
2417
14
        "ChProtobufSearchPaths",
2418
14
        protobuf_search_paths_copy_cb,
2419
14
        NULL,
2420
14
        protobuf_search_paths_free_cb,
2421
14
        update_protobuf_search_paths,
2422
14
        NULL,
2423
14
        protobuf_search_paths_table_columns
2424
14
    );
2425
2426
14
    prefs_register_uat_preference(protobuf_module, "search_paths", "Protobuf search paths",
2427
14
        "Specify the directories where .proto files are recursively loaded from, or in which to search for imports.",
2428
14
        protobuf_search_paths_uat);
2429
2430
14
    prefs_register_bool_preference(protobuf_module, "pbf_as_hf",
2431
14
        "Dissect Protobuf fields as Wireshark fields.",
2432
14
        "If Protobuf messages and fields are defined in loaded .proto files,"
2433
14
        " they will be dissected as wireshark fields if this option is turned on."
2434
14
        " The names of all these wireshark fields will be prefixed with \"pbf.\" (for fields)"
2435
14
        " or \"pbm.\" (for messages) followed by their full names in the .proto files.",
2436
14
        &pbf_as_hf);
2437
2438
14
    prefs_set_preference_effect_fields(protobuf_module, "pbf_as_hf");
2439
2440
14
    prefs_register_bool_preference(protobuf_module, "show_details",
2441
14
        "Show details of message, fields and enums.",
2442
14
        "Show the names of message, field, enum and enum_value."
2443
14
        " Show the wire type and field number format of field."
2444
14
        " Show value nodes of field and enum_value.",
2445
14
        &show_details);
2446
2447
14
    prefs_register_bool_preference(protobuf_module, "bytes_as_string",
2448
14
        "Show all fields of bytes type as string.",
2449
14
        "Show all fields of bytes type as string. For example ETCD string",
2450
14
        &dissect_bytes_as_string);
2451
2452
14
    prefs_register_enum_preference(protobuf_module, "add_default_value",
2453
14
        "Add missing fields with default values.",
2454
14
        "Make Protobuf fields that are not serialized on the wire to be displayed with default values.\n"
2455
14
        "The default value will be one of the following: \n"
2456
14
        "  1) The value of the 'default' option of an optional field defined in 'proto2' file. (explicitly-declared)\n"
2457
14
        "  2) False for bools.\n"
2458
14
        "  3) First defined enum value for enums.\n"
2459
14
        "  4) Zero for numeric types; empty string for 'string'; and empty bytes for 'bytes'.\n"
2460
14
        "There are no default values for fields 'repeated'.\n"
2461
14
        "If the missing field is 'required' in a 'proto2' file, a warning item will be added to the tree.",
2462
14
        &add_default_value, add_default_value_policy_vals, false);
2463
2464
14
    protobuf_udp_message_types_uat = uat_new("Protobuf UDP Message Types",
2465
14
        sizeof(protobuf_udp_message_type_t),
2466
14
        "protobuf_udp_message_types",
2467
14
        true,
2468
14
        &protobuf_udp_message_types,
2469
14
        &num_protobuf_udp_message_types,
2470
14
        UAT_AFFECTS_DISSECTION | UAT_AFFECTS_FIELDS,
2471
14
        "ChProtobufUDPMessageTypes",
2472
14
        protobuf_udp_message_types_copy_cb,
2473
14
        protobuf_udp_message_types_update_cb,
2474
14
        protobuf_udp_message_types_free_cb,
2475
14
        update_protobuf_udp_message_types,
2476
14
        NULL,
2477
14
        protobuf_udp_message_types_table_columns
2478
14
    );
2479
2480
14
    prefs_register_uat_preference(protobuf_module, "udp_message_types", "Protobuf UDP message types",
2481
14
        "Specify the Protobuf message type of data on certain UDP ports.",
2482
14
        protobuf_udp_message_types_uat);
2483
2484
2485
14
    protobuf_uri_message_types_uat = uat_new("Protobuf URI Message Types",
2486
14
        sizeof(protobuf_uri_mapping_t),
2487
14
        "protobuf_uri_message_types",
2488
14
        true,
2489
14
        &protobuf_uri_message_types,
2490
14
        &num_protobuf_uri_message_types,
2491
14
        UAT_AFFECTS_DISSECTION | UAT_AFFECTS_FIELDS,
2492
14
        NULL, //"ChProtobufURIMessageTypes",
2493
14
        protobuf_uri_message_type_copy_cb,
2494
14
        NULL,
2495
14
        protobuf_uri_message_type_free_cb,
2496
14
        update_protobuf_uri_message_types,
2497
14
        NULL,
2498
14
        protobuf_uri_message_types_table_columns
2499
14
    );
2500
2501
14
    prefs_register_uat_preference(protobuf_module, "uri_message_types", "Protobuf URI message types",
2502
14
        "Specify the Protobuf message type of data on certain URIs. N.B., URI may contain '*'",
2503
14
        protobuf_uri_message_types_uat);
2504
2505
2506
14
    prefs_register_bool_preference(protobuf_module, "display_json_mapping",
2507
14
        "Display JSON mapping for Protobuf message",
2508
14
        "Specifies that the JSON text of the "
2509
14
        "Protobuf message should be displayed "
2510
14
        "in addition to the dissection tree",
2511
14
        &display_json_mapping);
2512
2513
14
    prefs_register_bool_preference(protobuf_module, "use_utc",
2514
14
        "Display time in UTC",
2515
14
        "Display timestamp in UTC format",
2516
14
        &use_utc_fmt);
2517
2518
    /* Following preferences are for undefined fields, that happened while message type is not specified
2519
       when calling dissect_protobuf(), or message type or field information is not found in search paths
2520
    */
2521
14
    prefs_register_bool_preference(protobuf_module, "try_dissect_as_string",
2522
14
        "Try to dissect all undefined length-delimited fields as string.",
2523
14
        "Try to dissect all undefined length-delimited fields as string.",
2524
14
        &try_dissect_as_string);
2525
2526
14
    prefs_register_bool_preference(protobuf_module, "show_all_types",
2527
14
        "Try to show all possible field types for each undefined field.",
2528
14
        "Try to show all possible field types for each undefined field according to wire type.",
2529
14
        &show_all_possible_field_types);
2530
2531
14
    prefs_register_string_preference(protobuf_module, "default_type",
2532
14
                                     "Message type to use if none set",
2533
14
                                     "Can be useful e.g. if dissector called through media type",
2534
14
                                     &default_message_type);
2535
2536
14
    prefs_register_static_text_preference(protobuf_module, "field_dissector_table_note",
2537
14
        "Subdissector can register itself in \"protobuf_field\" dissector table for parsing"
2538
14
        " the value of the field.",
2539
14
        "The key of \"protobuf_field\" table is the full name of field.");
2540
2541
14
    protobuf_field_subdissector_table =
2542
14
        register_dissector_table("protobuf_field", "Protobuf field subdissector table",
2543
14
            proto_protobuf, FT_STRING, STRING_CASE_SENSITIVE);
2544
2545
14
    expert_protobuf = expert_register_protocol(proto_protobuf);
2546
14
    expert_register_field_array(expert_protobuf, ei, array_length(ei));
2547
2548
14
    protobuf_handle = register_dissector("protobuf", dissect_protobuf, proto_protobuf);
2549
14
}
2550
2551
void
2552
proto_reg_handoff_protobuf(void)
2553
14
{
2554
14
    if (protobuf_dissector_called) {
2555
0
        update_header_fields( /* if bytes_as_string preferences changed, we force reload header fields */
2556
0
            (old_dissect_bytes_as_string && !dissect_bytes_as_string) || (!old_dissect_bytes_as_string && dissect_bytes_as_string)
2557
0
        );
2558
14
    } else if (preload_protos) {
2559
0
        protobuf_dissector_called = true;
2560
0
        protobuf_reinit(PREFS_UPDATE_ALL);
2561
0
    }
2562
14
    old_dissect_bytes_as_string = dissect_bytes_as_string;
2563
14
    dissector_add_string("grpc_message_type", "application/grpc", protobuf_handle);
2564
14
    dissector_add_string("grpc_message_type", "application/grpc+proto", protobuf_handle);
2565
14
    dissector_add_string("grpc_message_type", "application/grpc-web", protobuf_handle);
2566
14
    dissector_add_string("grpc_message_type", "application/grpc-web+proto", protobuf_handle);
2567
14
    dissector_add_string("grpc_message_type", "application/grpc-web-text", protobuf_handle);
2568
14
    dissector_add_string("grpc_message_type", "application/grpc-web-text+proto", protobuf_handle);
2569
2570
14
    dissector_add_string("media_type", "application/x-protobuf", protobuf_handle);
2571
2572
14
    proto_http = proto_get_id_by_filter_name("http");
2573
14
}
2574
2575
/*
2576
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
2577
 *
2578
 * Local variables:
2579
 * c-basic-offset: 4
2580
 * tab-width: 8
2581
 * indent-tabs-mode: nil
2582
 * End:
2583
 *
2584
 * vi: set shiftwidth=4 tabstop=8 expandtab:
2585
 * :indentSize=4:tabSize=8:noTabs=true:
2586
 */