Coverage Report

Created: 2026-07-12 07:10

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