Coverage Report

Created: 2025-12-27 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-thrift.c
Line
Count
Source
1
/* packet-thrift.c
2
 * Routines for thrift protocol dissection.
3
 * Based on work by John Song <jsong@facebook.com> and
4
 * Bill Fumerola <bill@facebook.com>
5
 *
6
 * https://github.com/andrewcox/wireshark-with-thrift-plugin/blob/wireshark-1.8.6-with-thrift-plugin/plugins/thrift/packet-thrift.cpp
7
 *
8
 * Copyright 2015, Anders Broman <anders.broman[at]ericsson.com>
9
 * Copyright 2021, Richard van der Hoff <richard[at]matrix.org>
10
 * Copyright 2019-2024, Triton Circonflexe <triton[at]kumal.info>
11
 *
12
 * Wireshark - Network traffic analyzer
13
 * By Gerald Combs <gerald@wireshark.org>
14
 * Copyright 1998 Gerald Combs
15
 *
16
 * SPDX-License-Identifier: GPL-2.0-or-later
17
 */
18
/* Ref https://thrift.apache.org/developers
19
 *     https://thrift.apache.org/docs/idl.html
20
 *     https://diwakergupta.github.io/thrift-missing-guide/
21
 *     https://erikvanoosten.github.io/thrift-missing-specification/
22
 *     https://github.com/apache/thrift/blob/master/doc/specs/thrift-compact-protocol.md
23
 */
24
25
#include "config.h"
26
27
#include <stdint.h>
28
#include <epan/packet.h>
29
#include <epan/prefs.h>
30
#include <epan/proto_data.h>
31
#include <wsutil/array.h>
32
#include "packet-tcp.h"
33
#include "packet-tls.h"
34
#include "packet-thrift.h"
35
36
/* Line   40: Constants and macros declarations. */
37
/* Line  200: Protocol data structure and early declarations. */
38
/* Line  340: Generic helper functions for various purposes. */
39
/* Line  900: Helper functions to use within custom sub-dissectors (with some mutualization function). */
40
/* Line 2100: Generic functions to dissect TBinaryProtocol message content. */
41
/* Line 2420: Generic functions to dissect TCompactProtocol message content. */
42
/* Line 2900: Generic functions to dissect Thrift message header. */
43
44
/* ==== Note about the use of THRIFT_REQUEST_REASSEMBLY and THRIFT_SUBDISSECTOR_ERROR. ==== */
45
/* From the sub-dissection code, only the return value gives an information about the type of error.
46
 * In this case, THRIFT_REQUEST_REASSEMBLY is used for reassembly and THRIFT_SUBDISSECTOR_ERROR for everything else.
47
 *
48
 * From the generic dissection code (dissect_thrift_binary_* and dissect_thrift_compact_*) the functions also update
49
 * the offset passed as a reference. In this case, THRIFT_REQUEST_REASSEMBLY is the only error code used in order to
50
 * simplify propagation and the reference offset indicates the type of issue:
51
 * - THRIFT_REQUEST_REASSEMBLY indicates reassembly is required.
52
 * - Any positive value indicates where the non-reassembly error happened
53
 *   and the Thrift dissector consumes all the data available until now.
54
 */
55
56
57
void proto_register_thrift(void);
58
void proto_reg_handoff_thrift(void);
59
60
84
#define THRIFT_BINARY_VERSION_VALUE_MASK   0x7fff
61
7.21k
#define THRIFT_BINARY_VERSION_MASK     0xffff00f8
62
179
#define THRIFT_BINARY_MESSAGE_MASK     0x00000007
63
7.21k
#define THRIFT_BINARY_VERSION_1        0x80010000
64
65
1.84k
#define THRIFT_COMPACT_VERSION_VALUE_MASK   0x001f
66
7.46k
#define THRIFT_COMPACT_VERSION_MASK         0xff1f
67
2.47k
#define THRIFT_COMPACT_MESSAGE_MASK         0x00e0
68
7.46k
#define THRIFT_COMPACT_VERSION_1            0x8201
69
2.47k
#define THRIFT_COMPACT_MESSAGE_SHIFT             5
70
71
0
#define NOT_A_VALID_PDU (0)
72
73
10.1k
#define ABORT_SUBDISSECTION_ON_ISSUE(offset) do { if (offset < 0) return offset; } while (0)
74
75
24.3k
#define ABORT_ON_INCOMPLETE_PDU(len) do {\
76
24.3k
    if (tvb_reported_length_remaining(tvb, *offset) < (len)) {\
77
243
        /* Do not indicate the incomplete data if we know the above dissector is able to reassemble. */\
78
243
        if (pinfo->can_desegment <= 0) \
79
243
            expert_add_info(pinfo, NULL, &ei_thrift_not_enough_data);\
80
243
        /* Do not consume more than available for the reassembly to work. */\
81
243
        thrift_opt->reassembly_offset = *offset;\
82
243
        thrift_opt->reassembly_length = len;\
83
243
        *offset = THRIFT_REQUEST_REASSEMBLY;\
84
243
        return THRIFT_REQUEST_REASSEMBLY;\
85
24.1k
    } } while (0)
86
87
static dissector_handle_t thrift_handle;
88
static dissector_handle_t thrift_http_handle;
89
static bool framed_desegment = true;
90
static unsigned thrift_tls_port;
91
92
static bool show_internal_thrift_fields;
93
static bool try_generic_if_sub_dissector_fails;
94
static unsigned nested_type_depth = 25;
95
96
static dissector_table_t thrift_method_name_dissector_table;
97
98
/* TBinaryProtocol elements length. */
99
static const int TBP_THRIFT_TYPE_LEN = 1;
100
static const int TBP_THRIFT_FID_LEN = 2;
101
static const int TBP_THRIFT_BOOL_LEN = 1;
102
static const int TBP_THRIFT_I8_LEN = 1;
103
static const int TBP_THRIFT_DOUBLE_LEN = 8;
104
static const int TBP_THRIFT_I16_LEN = 2;
105
static const int TBP_THRIFT_I32_LEN = 4;
106
static const int TBP_THRIFT_I64_LEN = 8;
107
static const int TBP_THRIFT_UUID_LEN = 16;
108
static const int TBP_THRIFT_MTYPE_OFFSET = 3;
109
static const int TBP_THRIFT_MTYPE_LEN = 1;
110
static const unsigned TBP_THRIFT_VERSION_LEN = 4; /* (Version + method type) is explicitly passed as an int32 in libthrift */
111
static const int TBP_THRIFT_LENGTH_LEN = 4;
112
static const int TBP_THRIFT_SEQ_ID_LEN = 4;
113
static const int TBP_THRIFT_STRICT_HEADER_LEN = 8; /* (Protocol id + Version + Method type) + Name length = (4) + 4. */
114
                                    /* Old encoding: Name length [ + Name] + Message type      + Sequence Identifier   + T_STOP */
115
static const unsigned TBP_THRIFT_MIN_MESSAGE_LEN = 10; /* TBP_THRIFT_LENGTH_LEN + TBP_THRIFT_I8_LEN + TBP_THRIFT_SEQ_ID_LEN + TBP_THRIFT_TYPE_LEN; */
116
static const unsigned TBP_THRIFT_STRICT_MIN_MESSAGE_LEN = 13; /* TBP_THRIFT_STRICT_HEADER_LEN       + TBP_THRIFT_SEQ_ID_LEN + TBP_THRIFT_TYPE_LEN; */
117
static const int TBP_THRIFT_BINARY_LEN = 4; /* Length (even with empty content). */
118
static const int TBP_THRIFT_STRUCT_LEN = 1; /* Empty struct still contains T_STOP. */
119
static const int TBP_THRIFT_LINEAR_LEN = 5; /* Elements type + number of elements for list & set. */
120
121
/* TCompactProtocol elements length when different from TBinaryProtocol.
122
 * Are identical:
123
 * - Field Type (although Compact squeezes in the high nibble the field id delta or list/set length).
124
 * - T_BOOL (in linear containers, not structs)
125
 * - T_I8
126
 * - T_DOUBLE (endianness is inverted, though)
127
 */
128
static const int TCP_THRIFT_DELTA_NOT_SET;
129
static const int TCP_THRIFT_LENGTH_LARGER = 0xf;
130
static const int TCP_THRIFT_MAP_TYPES_LEN = 1;      /* High nibble = key type, low nibble = value type. */
131
static const int TCP_THRIFT_NIBBLE_SHIFT = 4;
132
static const int TCP_THRIFT_VERSION_LEN = 2;     /* Protocol id + (Method type + Version) */
133
static const int TCP_THRIFT_MIN_VARINT_LEN = 1;
134
/* Those cannot be define as static const int since they are used within a switch. */
135
/* Maximum length in bytes for 16, 32, and 64 bits integers encoded as varint. */
136
4.73k
#define TCP_THRIFT_MAX_I16_LEN (3)
137
15.4k
#define TCP_THRIFT_MAX_I32_LEN (5)
138
4.40k
#define TCP_THRIFT_MAX_I64_LEN (10)
139
static const int TCP_THRIFT_STRUCT_LEN = 1; /* Empty struct still contains T_STOP. */
140
static const unsigned TCP_THRIFT_MIN_MESSAGE_LEN = 5; /* Protocol id + (Method type + Version) + Name length [+ Name] + Sequence Identifier + T_STOP */
141
142
static const uint32_t TCP_THRIFT_NIBBLE_MASK = 0xf;
143
144
static const int OCTETS_TO_BITS_SHIFT = 3;   /* 8 bits per octets = 3 shifts left. */
145
static const int DISABLE_SUBTREE = -1;
146
147
static int proto_thrift;
148
static int hf_thrift_frame_length;
149
static int hf_thrift_protocol_id;
150
static int hf_thrift_version;
151
static int hf_thrift_mtype;
152
static int hf_thrift_str_len;
153
static int hf_thrift_method;
154
static int hf_thrift_seq_id;
155
static int hf_thrift_type;
156
static int hf_thrift_key_type;
157
static int hf_thrift_value_type;
158
static int hf_thrift_compact_struct_type;
159
static int hf_thrift_fid;
160
static int hf_thrift_fid_delta;
161
static int hf_thrift_bool;
162
static int hf_thrift_i8;
163
static int hf_thrift_i16;
164
static int hf_thrift_i32;
165
static int hf_thrift_i64;
166
static int hf_thrift_u64;
167
static int hf_thrift_uuid;
168
static int hf_thrift_binary;
169
static int hf_thrift_string;
170
static int hf_thrift_struct;
171
static int hf_thrift_list;
172
static int hf_thrift_set;
173
static int hf_thrift_map;
174
static int hf_thrift_num_list_item;
175
static int hf_thrift_num_list_pos;
176
static int hf_thrift_num_set_item;
177
static int hf_thrift_num_set_pos;
178
static int hf_thrift_num_map_item;
179
static int hf_thrift_large_container;
180
static int hf_thrift_double;
181
static int hf_thrift_exception;
182
static int hf_thrift_exception_message;
183
static int hf_thrift_exception_type;
184
185
static int ett_thrift;
186
static int ett_thrift_header;
187
static int ett_thrift_params;
188
static int ett_thrift_field;
189
static int ett_thrift_struct;
190
static int ett_thrift_list;
191
static int ett_thrift_set;
192
static int ett_thrift_map;
193
static int ett_thrift_error; /* Error while reading the header. */
194
static int ett_thrift_exception; /* ME_THRIFT_T_EXCEPTION */
195
196
static expert_field ei_thrift_wrong_type;
197
static expert_field ei_thrift_wrong_field_id;
198
static expert_field ei_thrift_negative_length;
199
static expert_field ei_thrift_wrong_proto_version;
200
static expert_field ei_thrift_struct_fid_not_in_seq;
201
static expert_field ei_thrift_frame_too_short;
202
static expert_field ei_thrift_not_enough_data;
203
static expert_field ei_thrift_frame_too_long;
204
static expert_field ei_thrift_varint_too_large;
205
static expert_field ei_thrift_undefined_field_id;
206
static expert_field ei_thrift_negative_field_id;
207
static expert_field ei_thrift_unordered_field_id;
208
static expert_field ei_thrift_application_exception;
209
static expert_field ei_thrift_protocol_exception;
210
static expert_field ei_thrift_too_many_subtypes;
211
212
static const thrift_member_t thrift_exception[] = {
213
    { &hf_thrift_exception_message, 1, true, DE_THRIFT_T_BINARY, NULL, { .encoding = ENC_UTF_8 }, NULL },
214
    { &hf_thrift_exception_type, 2, false, DE_THRIFT_T_I32, TMFILL },
215
    { NULL, 0, false, DE_THRIFT_T_STOP, TMFILL }
216
};
217
218
typedef enum {
219
    DE_THRIFT_C_STOP = DE_THRIFT_T_STOP,
220
    DE_THRIFT_C_BOOL_TRUE,
221
    DE_THRIFT_C_BOOL_FALSE,
222
    DE_THRIFT_C_I8,
223
    DE_THRIFT_C_I16,
224
    DE_THRIFT_C_I32,
225
    DE_THRIFT_C_I64,
226
    DE_THRIFT_C_DOUBLE,
227
    DE_THRIFT_C_BINARY,
228
    DE_THRIFT_C_LIST,
229
    DE_THRIFT_C_SET,
230
    DE_THRIFT_C_MAP,
231
    DE_THRIFT_C_STRUCT,
232
    DE_THRIFT_C_UUID,
233
} thrift_compact_type_enum_t;
234
235
typedef struct _thrift_field_header_t {
236
    union {
237
        thrift_type_enum_t binary;
238
        thrift_compact_type_enum_t compact;
239
    } type;
240
    int type_offset;
241
    int64_t field_id;
242
    int fid_offset;
243
    int fid_length;
244
    proto_item *type_pi;
245
    proto_item *fid_pi;
246
    proto_tree *fh_tree;
247
} thrift_field_header_t;
248
249
static const value_string thrift_type_vals[] = {
250
    { DE_THRIFT_T_STOP, "T_STOP" },
251
    { DE_THRIFT_T_VOID, "T_VOID" },
252
    { DE_THRIFT_T_BOOL, "T_BOOL" },
253
    { DE_THRIFT_T_I8, "T_I8" },
254
    { DE_THRIFT_T_DOUBLE, "T_DOUBLE" },
255
    { DE_THRIFT_T_I16, "T_I16" },
256
    { DE_THRIFT_T_I32, "T_I32" },
257
    { DE_THRIFT_T_I64, "T_I64" },
258
    { DE_THRIFT_T_BINARY, "T_BINARY" },
259
    { DE_THRIFT_T_STRUCT, "T_STRUCT" },
260
    { DE_THRIFT_T_MAP, "T_MAP" },
261
    { DE_THRIFT_T_SET, "T_SET" },
262
    { DE_THRIFT_T_LIST, "T_LIST" },
263
    { DE_THRIFT_T_UUID, "T_UUID" },
264
    { 0, NULL }
265
};
266
267
/* type values used within structs in the compact protocol */
268
static const value_string thrift_compact_type_vals[] = {
269
    { DE_THRIFT_C_BOOL_TRUE, "BOOLEAN_TRUE" },
270
    { DE_THRIFT_C_BOOL_FALSE, "BOOLEAN_FALSE" },
271
    { DE_THRIFT_C_I8, "T_I8" },
272
    { DE_THRIFT_C_I16, "T_I16" },
273
    { DE_THRIFT_C_I32, "T_I32" },
274
    { DE_THRIFT_C_I64, "T_I64" },
275
    { DE_THRIFT_C_DOUBLE, "T_DOUBLE" },
276
    { DE_THRIFT_C_BINARY, "T_BINARY" },
277
    { DE_THRIFT_C_LIST, "T_LIST" },
278
    { DE_THRIFT_C_SET, "T_SET" },
279
    { DE_THRIFT_C_MAP, "T_MAP" },
280
    { DE_THRIFT_C_STRUCT, "T_STRUCT" },
281
    { DE_THRIFT_C_UUID, "T_UUID" },
282
    { 0, NULL }
283
};
284
285
static const value_string thrift_exception_type_vals[] = {
286
    {  0, "Unknown (type of peer)" },
287
    {  1, "Unknown Method" },
288
    {  2, "Invalid Message Type" },
289
    {  3, "Wrong Method Name" },
290
    {  4, "Bad Sequence Id" },
291
    {  5, "Missing Result" },
292
    {  6, "Internal Error" },
293
    {  7, "Protocol Error (something went wrong during decoding)" },
294
    {  8, "Invalid Transform" },
295
    {  9, "Invalid Protocol" },
296
    { 10, "Unsupported Client Type" },
297
    { 0, NULL }
298
};
299
300
static const value_string thrift_proto_vals[] = {
301
    { 0x80, "Strict Binary Protocol" },
302
    { 0x82, "Compact Protocol" },
303
    { 0, NULL }
304
};
305
306
static const value_string thrift_mtype_vals[] = {
307
    { ME_THRIFT_T_CALL,      "CALL" },
308
    { ME_THRIFT_T_REPLY,     "REPLY" },
309
    { ME_THRIFT_T_EXCEPTION, "EXCEPTION" },
310
    { ME_THRIFT_T_ONEWAY,    "ONEWAY" },
311
    { 0, NULL }
312
};
313
314
/* Options */
315
1.20k
#define DECODE_BINARY_AS_AUTO_UTF8      0
316
33
#define DECODE_BINARY_AS_BINARY         1
317
0
#define DECODE_BINARY_AS_ASCII          2
318
0
#define DECODE_BINARY_AS_UTF8           3
319
0
#define DECODE_BINARY_AS_UTF16BE        4
320
0
#define DECODE_BINARY_AS_UTF16LE        5
321
0
#define DECODE_BINARY_AS_UTF32BE        6
322
0
#define DECODE_BINARY_AS_UTF32LE        7
323
324
static int32_t  binary_decode = DECODE_BINARY_AS_AUTO_UTF8;
325
326
static const enum_val_t binary_display_options[] = {
327
    { "auto", "UTF-8 if printable", DECODE_BINARY_AS_AUTO_UTF8 },
328
    { "hexadecimal", "Binary (hexadecimal string)", DECODE_BINARY_AS_BINARY },
329
    { "ascii", "ASCII String", DECODE_BINARY_AS_ASCII },
330
    { "utf8", "UTF-8 String", DECODE_BINARY_AS_UTF8 },
331
    { "utf16be", "UTF-16 Big Endian", DECODE_BINARY_AS_UTF16BE },
332
    { "utf16le", "UTF-16 Little Endian", DECODE_BINARY_AS_UTF16LE },
333
    { "utf32be", "UTF-32 Big Endian", DECODE_BINARY_AS_UTF32BE },
334
    { "utf32le", "UTF-32 Little Endian", DECODE_BINARY_AS_UTF32LE },
335
    { NULL, NULL, -1 }
336
};
337
338
static int dissect_thrift_binary_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt, proto_tree *header_tree, int type, proto_item *type_pi);
339
static int dissect_thrift_compact_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt, proto_tree *header_tree, int type, proto_item *type_pi);
340
static int dissect_thrift_t_struct_expert(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id, int ett_id, const thrift_member_t *seq, expert_field* ei);
341
342
/*=====BEGIN GENERIC HELPERS=====*/
343
/* Check that the 4-byte value match a Thrift Strict TBinaryProtocol version
344
 * - 0x8001 The version itself
345
 * - 0x??   An undetermined byte (not used)
346
 * - 0x0m   The method type between 1 and 4.
347
 *          Values above 4 will be accepted if ignore_msg_type is true.
348
 */
349
static bool
350
is_thrift_strict_version(uint32_t header, bool ignore_msg_type)
351
7.21k
{
352
7.21k
    int msg_type;
353
7.21k
    if ((header & THRIFT_BINARY_VERSION_MASK) == THRIFT_BINARY_VERSION_1) {
354
106
        if (ignore_msg_type) {
355
31
            return true;
356
31
        }
357
75
        msg_type = (header & THRIFT_BINARY_MESSAGE_MASK);
358
75
        if ((ME_THRIFT_T_CALL <= msg_type) && (msg_type <= ME_THRIFT_T_ONEWAY)) {
359
67
            return true;
360
67
        }
361
75
    }
362
7.12k
    return false;
363
7.21k
}
364
365
/* Check that the 2-byte value match a Thrift TCompactProtocol version
366
 * - 0x82 The protocol id.
367
 * - 0bmmmvvvvv The method on the 3 MSbits and version on the 5 LSbits.
368
 */
369
static bool
370
is_thrift_compact_version(uint16_t header, bool ignore_msg_type)
371
7.46k
{
372
7.46k
    int msg_type;
373
7.46k
    if ((header & THRIFT_COMPACT_VERSION_MASK) == THRIFT_COMPACT_VERSION_1) {
374
634
        if (ignore_msg_type) {
375
0
            return true;
376
0
        }
377
634
        msg_type = (header & THRIFT_COMPACT_MESSAGE_MASK) >> THRIFT_COMPACT_MESSAGE_SHIFT;
378
634
        if ((ME_THRIFT_T_CALL <= msg_type) && (msg_type <= ME_THRIFT_T_ONEWAY)) {
379
630
            return true;
380
630
        }
381
634
    }
382
6.83k
    return false;
383
7.46k
}
384
385
/*
386
 * Check that the string at the designed position is valid UTF-8.
387
 * This allows us to fail early if the length of the string seems very long.
388
 * This /can/ indicate that this packet does not contain a Thrift PDU.
389
 *
390
 * This method does /NOT/ check if the data is available, the caller must check that if needed.
391
 * - Heuristic for method name must check for captured length.
392
 * - Check UTF-8 vs. binary before adding to tree must check for reported length.
393
 */
394
static unsigned
395
thrift_binary_utf8_isprint(tvbuff_t *tvb, unsigned offset, unsigned max_len, bool accept_crlf)
396
1.22k
{
397
1.22k
    unsigned check_len = tvb_reported_length_remaining(tvb, offset);
398
1.22k
    unsigned pos, remaining = 0; /* position in tvb, remaining bytes for multi-byte characters. */
399
1.22k
    uint8_t min_next = 0x80, max_next = 0xBF;
400
1.22k
    bool ended = false;
401
1.22k
    unsigned printable_len = 0; /* In case the string ends with several NUL bytes. */
402
1.22k
    if (max_len < check_len) {
403
1.21k
        check_len = max_len;
404
1.21k
    }
405
4.60k
    for (pos = offset; pos < offset + check_len; pos++) {
406
3.88k
        uint8_t current = tvb_get_uint8(tvb, pos);
407
3.88k
        if (ended) {
408
705
            if (current != 0) {
409
115
                return -1;
410
115
            }
411
3.17k
        } else if (remaining == 0) {
412
            /* We are at the beginning of a character. */
413
3.09k
            if (current == 0) {
414
146
                ended = true;
415
146
                continue; /* Avoid counting this NUL byte as printable. */
416
2.95k
            } else if ((current & 0x80) == 0) {
417
2.78k
                if (!g_ascii_isprint(current)) {
418
547
                    if (!accept_crlf) {
419
                        /* New line and chariot return or not valid in the method name */
420
11
                        return -1;
421
11
                    }
422
536
                    if (current != '\r' && current != '\n') {
423
                        /* But would have been acceptable for data content */
424
229
                        return -1;
425
229
                    }
426
536
                }
427
2.78k
            } else if ((current & 0xE0) == 0xC0) {
428
                /* 2 bytes code 8 to 11 bits */
429
28
                if (current >= 0xC2) {
430
24
                    remaining = 1;
431
24
                    min_next = 0x80;
432
24
                } else {
433
                    /* Overlong encoding of ASCII for C0 and C1. */
434
4
                    return -1;
435
4
                }
436
138
            } else if ((current & 0xF0) == 0xE0) {
437
                /* 3 bytes code 12 to 16 bits */
438
26
                remaining = 2;
439
26
                if (current == 0xE0) {
440
8
                    min_next = 0xA0; /* 0b101x xxxx to code at least 12 bits. */
441
18
                } else {
442
18
                    if (current == 0xED) {
443
                        /* Reject reserved UTF-16 surrogates as specified for UTF-8. */
444
2
                        max_next = 0x9F;
445
2
                    }
446
18
                    min_next = 0x80;
447
18
                }
448
112
            } else if ((current & 0xF8) == 0xF0) {
449
                /* 4 bytes code 17 to 21 bits */
450
24
                remaining = 3;
451
24
                if (current == 0xF0) {
452
4
                    min_next = 0x90; /* 0b1001 xxxx to code at least 17 bits. */
453
20
                } else if (current > 0xF4) {
454
                    /* Invalid leading byte (above U+10FFFF). */
455
7
                    return -1;
456
13
                } else {
457
13
                    min_next = 0x80;
458
13
                }
459
88
            } else {
460
                /* Not the beginning of an UTF-8 character. */
461
88
                return -1;
462
88
            }
463
2.61k
            ++printable_len;
464
2.61k
        } else {
465
80
            if ((current < min_next) || (max_next < current)) {
466
                /* Not a canonical UTF-8 character continuation. */
467
42
                return -1;
468
42
            }
469
38
            min_next = 0x80;
470
38
            max_next = 0xBF;
471
38
            --remaining;
472
38
            ++printable_len;
473
38
        }
474
3.88k
    }
475
725
    return printable_len;
476
1.22k
}
477
478
/** Simple wrapper around tvb_get_varint to handle reassembly.
479
 *
480
 * @param[in] tvb        Pointer to the tvbuff_t holding the captured data.
481
 * @param[in] pinfo      Pointer to the packet_info holding information about the currently dissected packet.
482
 * @param[in] tree       Pointer to the proto_tree used to hold the display tree in Wireshark's interface.
483
 * @param[in] offset     Offset from the beginning of the tvbuff_t where the Thrift field is. Function will dissect type, id, & data.
484
 * @param[in] max_length The expected maximum encoding length of the integer.
485
 * @param[in] value      If parsing succeeds, parsed varint will be stored here.
486
 * @param[in] encoding   The ENC_* that defines the format (e.g., ENC_VARINT_PROTOBUF or ENC_VARINT_ZIGZAG).
487
 *
488
 * @return THRIFT_REQUEST_REASSEMBLY(-1) if reassembly is necessary,
489
 *                                    0  in case of error,
490
 *         a positive value indicating the length of the varint otherwise.
491
 */
492
static int
493
thrift_get_varint_enc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, int max_length, uint64_t *value, const unsigned encoding)
494
13.4k
{
495
13.4k
    unsigned length;
496
13.4k
    int readable = tvb_reported_length_remaining(tvb, offset);
497
13.4k
    if (readable <= 0) {
498
39
        return THRIFT_REQUEST_REASSEMBLY;
499
39
    }
500
13.3k
    if (readable > max_length) {
501
13.0k
        readable = max_length;
502
13.0k
    }
503
13.3k
    length = tvb_get_varint(tvb, offset, readable, value, encoding);
504
13.3k
    if (length == 0) {
505
115
        if (readable < max_length) {
506
            /* There was not enough data to ensure the varint is complete for the expected size of integer. */
507
29
            return THRIFT_REQUEST_REASSEMBLY;
508
86
        } else {
509
            /* Either an error on the wire or a sub-optimal encoding, we always consider it as an error. */
510
86
            proto_tree_add_expert(tree, pinfo, &ei_thrift_varint_too_large, tvb, offset, max_length);
511
86
        }
512
115
    }
513
13.3k
    return length;
514
13.3k
}
515
516
static bool
517
is_thrift_compact_bool_type(thrift_compact_type_enum_t type)
518
14.6k
{
519
14.6k
    return type == DE_THRIFT_C_BOOL_TRUE || type == DE_THRIFT_C_BOOL_FALSE;
520
14.6k
}
521
522
/* Function that reads the field header and return all associated data.
523
 *
524
 * @param[in] tvb:          Pointer to the tvbuff_t holding the captured data.
525
 * @param[in] pinfo:        Pointer to the packet_info holding information about the currently dissected packet.
526
 * @param[in] tree:         Pointer to the proto_tree used to hold the display tree in Wireshark's interface.
527
 *                          The caller may set it to NULL to prevent the creation of the field header sub-tree.
528
 *                          This possibility is used by sub-dissector when show_internal_thrift_fields is false,
529
 *                          and by dissect_thrift_common to differentiate between successful and exception T_REPLY.
530
 * @param[in] offset:       Offset from the beginning of the tvbuff_t where the Thrift field is. Function will dissect type, id, & data.
531
 * @param[in] thrift_opt:   Options from the Thrift dissector that will be necessary for sub-dissection (binary vs. compact, ...)
532
 * @param[in] gen_bool:     Generate item when one of the boolean types is encountered.
533
 *
534
 * @return                  See "GENERIC DISSECTION PARAMETERS DOCUMENTATION".
535
 */
536
static int
537
dissect_thrift_field_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt, thrift_field_header_t *header, bool gen_bool)
538
9.88k
{
539
    /*
540
     *  Binary protocol field header (3 bytes):
541
     *      +--------+--------+--------+
542
     *      |0000tttt| field id        |
543
     *      +--------+--------+--------+
544
     *
545
     *  Compact protocol field header (1 byte, short form):
546
     *      +--------+
547
     *      |ddddtttt|
548
     *      +--------+
549
     *
550
     *  Compact protocol field header (2 to 4 bytes, long form):
551
     *      +--------+--------+...+--------+
552
     *      |0000tttt| field id            |
553
     *      +--------+--------+...+--------+
554
     *
555
     *  Binary & Compact protocol stop field (1 byte):
556
     *      +--------+
557
     *      |00000000|
558
     *      +--------+
559
     *
560
     *  Where:
561
     *      'dddd'      is the field id delta, a strictly positive unsigned 4 bits integer.
562
     *      'tttt'      is the type of the field value, an unsigned 4 bits strictly positive integer.
563
     *      field id    is the numerical value of the field in the structure.
564
     */
565
566
9.88k
    DISSECTOR_ASSERT(header != NULL);
567
568
9.88k
    ABORT_SUBDISSECTION_ON_ISSUE(*offset); /* In case of sub-dissection functions. */
569
9.88k
    ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_TYPE_LEN); /* In all dissection cases. */
570
571
9.86k
    uint8_t dfid_type = tvb_get_uint8(tvb, *offset);
572
9.86k
    int32_t delta = TCP_THRIFT_DELTA_NOT_SET;
573
9.86k
    int64_t fid = 0;
574
575
9.86k
    memset(header, 0, sizeof(thrift_field_header_t));
576
577
    /* Read type (and delta for Compact) */
578
9.86k
    header->type_offset = *offset;
579
9.86k
    *offset += TBP_THRIFT_TYPE_LEN;
580
581
9.86k
    if (dfid_type == DE_THRIFT_T_STOP) {
582
1.84k
        header->type.binary = (thrift_type_enum_t)dfid_type;
583
        /* No need for sub-tree in this case. */
584
1.84k
        header->type_pi = proto_tree_add_item(tree, hf_thrift_type, tvb, header->type_offset, TBP_THRIFT_TYPE_LEN, ENC_BIG_ENDIAN);
585
1.84k
        return *offset;
586
1.84k
    }
587
588
    /* Read the field id */
589
8.01k
    if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) {
590
7.84k
        header->type.compact = (thrift_compact_type_enum_t)(dfid_type & TCP_THRIFT_NIBBLE_MASK);
591
7.84k
        delta = (dfid_type >> TCP_THRIFT_NIBBLE_SHIFT) & TCP_THRIFT_NIBBLE_MASK;
592
7.84k
        if (delta == TCP_THRIFT_DELTA_NOT_SET) {
593
1.90k
            header->fid_offset = *offset;
594
1.90k
            header->fid_length = thrift_get_varint_enc(tvb, pinfo, NULL, *offset, TCP_THRIFT_MAX_I16_LEN, (uint64_t*)&fid, ENC_VARINT_ZIGZAG);
595
1.90k
            switch (header->fid_length) {
596
9
            case THRIFT_REQUEST_REASSEMBLY:
597
                /* Will always return after setting the expert parts. */
598
9
                ABORT_ON_INCOMPLETE_PDU(TCP_THRIFT_MAX_I16_LEN);
599
0
                return THRIFT_REQUEST_REASSEMBLY; // Just to avoid a false positive warning.
600
21
            case 0: /* In case of error, the offset stay at the error position. */
601
1.89k
            default:
602
1.89k
                header->field_id = fid;
603
1.89k
                *offset += header->fid_length;
604
1.89k
                break;
605
1.90k
            }
606
5.94k
        } else {
607
            /* The field id data in the tvb is represented by the delta with the type. */
608
5.94k
            header->field_id = thrift_opt->previous_field_id + delta;
609
5.94k
            header->fid_offset = header->type_offset;
610
5.94k
            header->fid_length = TBP_THRIFT_TYPE_LEN;
611
5.94k
        }
612
7.84k
    } else {
613
        /* Fixed size field id for Binary protocol. */
614
162
        ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_FID_LEN);
615
159
        header->type.binary = (thrift_type_enum_t)dfid_type;
616
159
        header->field_id = tvb_get_ntohis(tvb, *offset);
617
159
        header->fid_offset = *offset;
618
159
        header->fid_length = TBP_THRIFT_FID_LEN;
619
159
        *offset += TBP_THRIFT_FID_LEN;
620
159
    }
621
622
    /* Create the field header sub-tree if requested only. */
623
7.99k
    if (tree != NULL) {
624
7.60k
        unsigned nested_count = p_get_proto_depth(pinfo, proto_thrift);
625
7.60k
        if (nested_count >= thrift_opt->nested_type_depth) {
626
2
            expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes);
627
2
            return THRIFT_REQUEST_REASSEMBLY;
628
2
        }
629
630
7.60k
        header->fh_tree = proto_tree_add_subtree_format(tree, tvb, header->type_offset, *offset - header->type_offset, ett_thrift_field, NULL,
631
7.60k
                "Field Header #%" PRId64, header->field_id);
632
7.60k
        if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) {
633
7.46k
            header->type_pi = proto_tree_add_bits_item(header->fh_tree, hf_thrift_compact_struct_type, tvb, (header->type_offset << OCTETS_TO_BITS_SHIFT) + TCP_THRIFT_NIBBLE_SHIFT, TCP_THRIFT_NIBBLE_SHIFT, ENC_BIG_ENDIAN);
634
7.46k
            header->fid_pi = proto_tree_add_bits_item(header->fh_tree, hf_thrift_fid_delta, tvb, header->type_offset << OCTETS_TO_BITS_SHIFT, TCP_THRIFT_NIBBLE_SHIFT, ENC_BIG_ENDIAN);
635
7.46k
            if (delta == TCP_THRIFT_DELTA_NOT_SET) {
636
1.82k
                proto_item_append_text(header->fid_pi, " (Not Set)");
637
1.82k
            }
638
7.46k
            if (gen_bool && is_thrift_compact_bool_type(header->type.compact)) {
639
3.51k
                proto_item *bool_item = proto_tree_add_boolean(tree, hf_thrift_bool, tvb, header->type_offset, TBP_THRIFT_TYPE_LEN, 2 - header->type.compact);
640
3.51k
                proto_item_set_generated(bool_item);
641
3.51k
            }
642
7.46k
        } else {
643
145
            header->type_pi = proto_tree_add_item(header->fh_tree, hf_thrift_type, tvb, header->type_offset, TBP_THRIFT_TYPE_LEN, ENC_BIG_ENDIAN);
644
145
        }
645
7.60k
        if (delta == TCP_THRIFT_DELTA_NOT_SET) {
646
1.96k
            if (header->fid_length > 0) {
647
1.94k
                header->fid_pi = proto_tree_add_item(header->fh_tree, hf_thrift_fid, tvb, header->fid_offset, header->fid_length, ENC_BIG_ENDIAN);
648
1.94k
            } else {
649
                /* Varint for field id was too long to decode, handle the error in the sub-tree. */
650
17
                proto_tree_add_expert(header->fh_tree, pinfo, &ei_thrift_varint_too_large, tvb, header->fid_offset, TCP_THRIFT_MAX_I16_LEN);
651
17
                return THRIFT_REQUEST_REASSEMBLY;
652
17
            }
653
5.64k
        } else {
654
5.64k
            if ((int64_t)INT16_MIN > header->field_id || header->field_id > (int64_t)INT16_MAX) {
655
239
                header->fid_pi = proto_tree_add_int64(header->fh_tree, hf_thrift_i64, tvb, header->fid_offset, header->fid_length, header->field_id);
656
239
                expert_add_info(pinfo, header->fid_pi, &ei_thrift_varint_too_large);
657
                /* We continue anyway as the field id was displayed successfully. */
658
5.40k
            } else {
659
5.40k
                header->fid_pi = proto_tree_add_int(header->fh_tree, hf_thrift_fid, tvb, header->fid_offset, header->fid_length, (int16_t)header->field_id);
660
5.40k
            }
661
5.64k
            proto_item_set_generated(header->fid_pi);
662
5.64k
        }
663
        /* When reading a successful T_REPLY, we always have
664
         * - previous_field_id == 0 because we are at the beginning of a structure, and
665
         * - header->field_id == 0 because it is the return value
666
         * so we need to ignore this case. */
667
7.58k
        if (header->field_id < thrift_opt->previous_field_id || (header->field_id == thrift_opt->previous_field_id && thrift_opt->previous_field_id != 0)) {
668
1.23k
            if (thrift_opt->previous_field_id == 0) {
669
                // Maybe an old application from when negative values were authorized.
670
134
                expert_add_info(pinfo, header->fid_pi, &ei_thrift_negative_field_id);
671
1.10k
            } else {
672
                // Although not mandated by Thrift protocol, applications should send fields in numerical order.
673
1.10k
                expert_add_info(pinfo, header->fid_pi, &ei_thrift_unordered_field_id);
674
1.10k
            }
675
1.23k
        }
676
7.58k
    } else {
677
        /* The fid_pi value (proto_item for field_id) is required for sub-dissectors.
678
         * Create it even in the absence of tree. */
679
389
        if (delta == TCP_THRIFT_DELTA_NOT_SET) {
680
86
            if (header->fid_length > 0) {
681
82
                header->fid_pi = proto_tree_add_item(header->fh_tree, hf_thrift_fid, tvb, header->fid_offset, header->fid_length, ENC_BIG_ENDIAN);
682
82
            } else {
683
                /* Varint for field id was too long to decode, handle the error without the sub-tree. */
684
4
                proto_tree_add_expert(tree, pinfo, &ei_thrift_varint_too_large, tvb, header->fid_offset, TCP_THRIFT_MAX_I16_LEN);
685
4
                return THRIFT_REQUEST_REASSEMBLY;
686
4
            }
687
303
        } else {
688
303
            if ((int64_t)INT16_MIN > header->field_id || header->field_id > (int64_t)INT16_MAX) {
689
0
                header->fid_pi = proto_tree_add_int64(header->fh_tree, hf_thrift_i64, tvb, header->fid_offset, header->fid_length, header->field_id);
690
0
                expert_add_info(pinfo, header->fid_pi, &ei_thrift_varint_too_large);
691
                /* We continue anyway as the field id was displayed successfully. */
692
303
            } else {
693
303
                header->fid_pi = proto_tree_add_int(header->fh_tree, hf_thrift_fid, tvb, header->fid_offset, header->fid_length, (int16_t)header->field_id);
694
303
            }
695
303
            proto_item_set_generated(header->fid_pi);
696
303
        }
697
389
    }
698
699
7.97k
    return *offset;
700
7.99k
}
701
702
/* Dissect a varint and add it to the display tree with the requested hf_id.
703
 * This function is used by both generic compact dissector and sub-dissector.
704
 *
705
 * @param[in] tvb:          Pointer to the tvbuff_t holding the captured data.
706
 * @param[in] pinfo:        Pointer to the packet_info holding information about the currently dissected packet.
707
 * @param[in] tree:         Pointer to the proto_tree used to hold the display tree in Wireshark's interface.
708
 * @param[in,out] offset:   Pointer to the offset from the beginning of the tvbuff_t where the Thrift field is. Function will dissect only the data.
709
 *                          The offset is modified according to table in "GENERIC DISSECTION PARAMETERS DOCUMENTATION" section.
710
 * @param[in] thrift_opt:   Options from the Thrift dissector that will be necessary for sub-dissection (binary vs. compact, ...)
711
 *
712
 * @param[in] max_length:   Expected maximum length of the data that encodes the integer.
713
 *                          This is only used to check if reassembly is necessary as with enough data and sub-optimal encoding,
714
 *                          this function will happily dissect the value anyway.
715
 * @param[in] hf_id:        The hf_id that needs to be used for the display.
716
 *                          If the found integer is larger that the expected integer size (driven by max_length parameter),
717
 *                          the integer will always be displayed as a generic T_I64 and an expert info will be added.
718
 * @param[in] raw_dissector Dissector for raw integer (we need to create a tvbuff_t with the flatten integer.
719
 *
720
 * @return                  See "GENERIC DISSECTION PARAMETERS DOCUMENTATION".
721
 */
722
static int
723
dissect_thrift_varint(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt, int max_length, int hf_id, dissector_t raw_dissector)
724
5.78k
{
725
5.78k
    int64_t varint;
726
5.78k
    proto_item *pi;
727
5.78k
    int length = thrift_get_varint_enc(tvb, pinfo, tree, *offset, max_length, (uint64_t*)&varint, ENC_VARINT_ZIGZAG);
728
5.78k
    switch (length) {
729
37
    case THRIFT_REQUEST_REASSEMBLY:
730
        /* Will always return after setting the expert parts. */
731
37
        ABORT_ON_INCOMPLETE_PDU(max_length);
732
0
        return THRIFT_REQUEST_REASSEMBLY; // Just to avoid a false positive warning.
733
33
    case 0:
734
        /* In case of error, the offset stay at the error position. */
735
33
        return THRIFT_REQUEST_REASSEMBLY;
736
5.71k
    default:
737
5.71k
        switch (max_length) {
738
1.38k
        case TCP_THRIFT_MAX_I16_LEN:
739
1.38k
            if ((int64_t)INT16_MIN > varint || varint > (int64_t)INT16_MAX) {
740
27
                pi = proto_tree_add_int64(tree, hf_thrift_i64, tvb, *offset, length, varint);
741
27
                expert_add_info(pinfo, pi, &ei_thrift_varint_too_large);
742
                /* We continue anyway as the varint was indeed decoded. */
743
1.36k
            } else {
744
1.36k
                if (raw_dissector != NULL) {
745
0
                    uint8_t *data = wmem_alloc(pinfo->pool, TBP_THRIFT_I16_LEN);
746
0
                    data[0] = (varint >> 8) & 0xFF;
747
0
                    data[1] =  varint       & 0xFF;
748
0
                    tvbuff_t* sub_tvb = tvb_new_child_real_data(tvb, data, TBP_THRIFT_I16_LEN, TBP_THRIFT_I16_LEN);
749
0
                    thrift_opt->use_std_dissector = false;
750
0
                    raw_dissector(sub_tvb, pinfo, tree, thrift_opt);
751
0
                }
752
1.36k
                if (thrift_opt->use_std_dissector) {
753
160
                    proto_tree_add_int(tree, hf_id, tvb, *offset, length, (int16_t)varint);
754
160
                }
755
1.36k
            }
756
1.38k
            break;
757
2.12k
        case TCP_THRIFT_MAX_I32_LEN:
758
2.12k
            if ((int64_t)INT32_MIN > varint || varint > (int64_t)INT32_MAX) {
759
34
                pi = proto_tree_add_int64(tree, hf_thrift_i64, tvb, *offset, length, varint);
760
34
                expert_add_info(pinfo, pi, &ei_thrift_varint_too_large);
761
                /* We continue anyway as the varint was indeed decoded. */
762
2.09k
            } else {
763
2.09k
                if (raw_dissector != NULL) {
764
0
                    uint8_t *data = wmem_alloc(pinfo->pool, TBP_THRIFT_I32_LEN);
765
0
                    data[0] = (varint >> 24) & 0xFF;
766
0
                    data[1] = (varint >> 16) & 0xFF;
767
0
                    data[2] = (varint >>  8) & 0xFF;
768
0
                    data[3] =  varint        & 0xFF;
769
0
                    tvbuff_t* sub_tvb = tvb_new_child_real_data(tvb, data, TBP_THRIFT_I32_LEN, TBP_THRIFT_I32_LEN);
770
0
                    thrift_opt->use_std_dissector = false;
771
0
                    raw_dissector(sub_tvb, pinfo, tree, thrift_opt);
772
0
                }
773
2.09k
                if (thrift_opt->use_std_dissector) {
774
227
                    proto_tree_add_int(tree, hf_id, tvb, *offset, length, (int32_t)varint);
775
227
                }
776
2.09k
            }
777
2.12k
            break;
778
2.19k
        case TCP_THRIFT_MAX_I64_LEN:
779
2.19k
        default:
780
2.19k
            if (raw_dissector != NULL) {
781
0
                uint8_t *data = wmem_alloc(pinfo->pool, TBP_THRIFT_I64_LEN);
782
0
                data[0] = (varint >> 56) & 0xFF;
783
0
                data[1] = (varint >> 48) & 0xFF;
784
0
                data[2] = (varint >> 40) & 0xFF;
785
0
                data[3] = (varint >> 32) & 0xFF;
786
0
                data[4] = (varint >> 24) & 0xFF;
787
0
                data[5] = (varint >> 16) & 0xFF;
788
0
                data[6] = (varint >>  8) & 0xFF;
789
0
                data[7] =  varint        & 0xFF;
790
0
                tvbuff_t* sub_tvb = tvb_new_child_real_data(tvb, data, TBP_THRIFT_I64_LEN, TBP_THRIFT_I64_LEN);
791
0
                thrift_opt->use_std_dissector = false;
792
0
                raw_dissector(sub_tvb, pinfo, tree, thrift_opt);
793
0
            }
794
2.19k
            if (thrift_opt->use_std_dissector) {
795
218
                proto_tree_add_int64(tree, hf_id, tvb, *offset, length, varint);
796
218
            }
797
2.19k
            break;
798
5.71k
        }
799
5.71k
        *offset += length;
800
5.71k
        break;
801
5.78k
    }
802
5.71k
    return *offset;
803
5.78k
}
804
805
/* Common function used by both Binary and Compact generic dissectors to dissect T_BINARY fields
806
 * as requested in the dissector preferences.
807
 * This function only dissects the data, not the field header nor the length.
808
 */
809
static int
810
dissect_thrift_string_as_preferred(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt, unsigned str_len)
811
1.26k
{
812
1.26k
    ABORT_ON_INCOMPLETE_PDU((int)str_len); /* Thrift assumes there will never be binary/string >= 2GiB */
813
814
1.20k
    if (tree) {
815
1.20k
        switch (binary_decode) {
816
0
            case DECODE_BINARY_AS_UTF32LE:
817
0
                proto_tree_add_item(tree, hf_thrift_string, tvb, *offset, str_len, ENC_UCS_4 | ENC_LITTLE_ENDIAN);
818
0
                break;
819
0
            case DECODE_BINARY_AS_UTF32BE:
820
0
                proto_tree_add_item(tree, hf_thrift_string, tvb, *offset, str_len, ENC_UCS_4 | ENC_BIG_ENDIAN);
821
0
                break;
822
0
            case DECODE_BINARY_AS_UTF16LE:
823
0
                proto_tree_add_item(tree, hf_thrift_string, tvb, *offset, str_len, ENC_UTF_16 | ENC_LITTLE_ENDIAN);
824
0
                break;
825
0
            case DECODE_BINARY_AS_UTF16BE:
826
0
                proto_tree_add_item(tree, hf_thrift_string, tvb, *offset, str_len, ENC_UTF_16 | ENC_BIG_ENDIAN);
827
0
                break;
828
0
            case DECODE_BINARY_AS_UTF8:
829
0
                proto_tree_add_item(tree, hf_thrift_string, tvb, *offset, str_len, ENC_UTF_8);
830
0
                break;
831
0
            case DECODE_BINARY_AS_ASCII:
832
0
                proto_tree_add_item(tree, hf_thrift_string, tvb, *offset, str_len, ENC_ASCII);
833
0
                break;
834
1.20k
            case DECODE_BINARY_AS_AUTO_UTF8:
835
                /* When there is no data at all, consider it a string
836
                 * but a buffer containing only NUL bytes is a binary.
837
                 * If not entirely captured, consider it as a binary. */
838
1.20k
                if (tvb_captured_length_remaining(tvb, *offset) >= str_len &&
839
1.19k
                    (str_len == 0 || thrift_binary_utf8_isprint(tvb, *offset, str_len, true) > 0)) {
840
1.17k
                    proto_tree_add_item(tree, hf_thrift_string, tvb, *offset, str_len, ENC_UTF_8);
841
1.17k
                    break;
842
1.17k
                }
843
                /* otherwise, continue with type BINARY */
844
                /* FALL THROUGH */
845
33
            case DECODE_BINARY_AS_BINARY:
846
33
            default:
847
33
                proto_tree_add_item(tree, hf_thrift_binary, tvb, *offset, str_len, ENC_NA);
848
33
                break;
849
1.20k
        }
850
1.20k
    }
851
1.19k
    *offset += str_len;
852
853
1.19k
    return *offset;
854
1.20k
}
855
856
// Converts the type value in TCompactProtocol to the equivalent standard
857
// value from TBinaryProtocol.
858
static thrift_type_enum_t
859
compact_struct_type_to_generic_type(thrift_compact_type_enum_t compact)
860
65
{
861
65
    switch (compact) {
862
0
    case DE_THRIFT_C_STOP:
863
0
        return DE_THRIFT_T_STOP;
864
2
    case DE_THRIFT_C_BOOL_TRUE:
865
4
    case DE_THRIFT_C_BOOL_FALSE:
866
4
        return DE_THRIFT_T_BOOL;
867
1
    case DE_THRIFT_C_I8:
868
1
        return DE_THRIFT_T_I8;
869
2
    case DE_THRIFT_C_I16:
870
2
        return DE_THRIFT_T_I16;
871
42
    case DE_THRIFT_C_I32:
872
42
        return DE_THRIFT_T_I32;
873
1
    case DE_THRIFT_C_I64:
874
1
        return DE_THRIFT_T_I64;
875
2
    case DE_THRIFT_C_DOUBLE:
876
2
        return DE_THRIFT_T_DOUBLE;
877
6
    case DE_THRIFT_C_BINARY:
878
6
        return DE_THRIFT_T_BINARY;
879
1
    case DE_THRIFT_C_LIST:
880
1
        return DE_THRIFT_T_LIST;
881
1
    case DE_THRIFT_C_SET:
882
1
        return DE_THRIFT_T_SET;
883
1
    case DE_THRIFT_C_MAP:
884
1
        return DE_THRIFT_T_MAP;
885
2
    case DE_THRIFT_C_STRUCT:
886
2
        return DE_THRIFT_T_STRUCT;
887
1
    case DE_THRIFT_C_UUID:
888
1
        return DE_THRIFT_T_UUID;
889
1
    default:
890
1
        return DE_THRIFT_T_VOID;
891
65
    }
892
65
}
893
/*=====END GENERIC HELPERS=====*/
894
895
/*=====BEGIN SUB-DISSECTION=====*/
896
/*
897
 * Helper functions to use within custom sub-dissectors.
898
 *
899
 * Behavior:
900
 * 1. If is_field is true, dissect the field header (field type + field id).
901
 * 1.a. Check the type in the PDU against the expected one according to the call.
902
 * 2. If requested, add the type and field id to the tree (internal thrift fields).
903
 * 3. Dissect the value of the field.
904
 *
905
 * Return the offset marking the end of the dissected value or a negative error code.
906
 * See packet-thrift.h for details.
907
 */
908
909
/*
910
 * See packet-thrift.h for parameters documentation of dissect_thrift_t_<type> functions.
911
 */
912
int
913
dissect_thrift_t_stop(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
914
42
{
915
42
    ABORT_SUBDISSECTION_ON_ISSUE(offset);
916
42
    if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_TYPE_LEN) {
917
0
        return THRIFT_REQUEST_REASSEMBLY;
918
0
    }
919
920
42
    if (tvb_get_uint8(tvb, offset) != DE_THRIFT_T_STOP) {
921
2
        proto_tree_add_expert(tree, pinfo, &ei_thrift_wrong_type, tvb, offset, TBP_THRIFT_TYPE_LEN);
922
2
        return THRIFT_SUBDISSECTOR_ERROR;
923
2
    }
924
40
    if (show_internal_thrift_fields) {
925
0
        proto_tree_add_item(tree, hf_thrift_type, tvb, offset, TBP_THRIFT_TYPE_LEN, ENC_BIG_ENDIAN);
926
0
    }
927
40
    offset += TBP_THRIFT_TYPE_LEN;
928
929
40
    return offset;
930
42
}
931
932
/* Common function used by all sub-dissection functions to handle the field header dissection as well as the display.
933
 *
934
 * @param[in] tvb:          Pointer to the tvbuff_t holding the captured data.
935
 * @param[in] pinfo:        Pointer to the packet_info holding information about the currently dissected packet.
936
 * @param[in] tree:         Pointer to the proto_tree used to hold the display tree in Wireshark's interface.
937
 * @param[in] offset:       Offset from the beginning of the tvbuff_t where the Thrift field is. Function will dissect type, id, & data.
938
 * @param[in] thrift_opt:   Options from the Thrift dissector that will be necessary for sub-dissection (binary vs. compact, ...)
939
 *
940
 * @param[in] expected:     The type expected by the caller sub-dissector.
941
 *                          Note: the generic type is used even in case of compact dissection.
942
 *
943
 * @param[in] field_id:     Thrift field identifier, to check that the right field is being dissected (in case of optional fields).
944
 *
945
 * @param[out] header_tree: Optional pointer to a proto_tree pointer.
946
 *                          If not NULL, the field header sub-tree will be set in this variable.
947
 *                          Used by binary/string sub-dissector to put the length in this field header as well.
948
 *
949
 * @return                  Offset of the first non-dissected byte in case of success,
950
 *                          THRIFT_REQUEST_REASSEMBLY (-1) in case reassembly is required, or
951
 *                          THRIFT_SUBDISSECTOR_ERROR (-2) in case of error.
952
 */
953
static int
954
dissect_thrift_t_field_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, thrift_type_enum_t expected, int field_id, proto_tree **header_tree)
955
66
{
956
66
    thrift_field_header_t field_header;
957
66
    proto_tree *internal_tree = NULL;
958
66
    thrift_type_enum_t generic_type;
959
960
    /* Get the current state of dissection. */
961
66
    DISSECTOR_ASSERT(thrift_opt);
962
66
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
963
964
66
    if (show_internal_thrift_fields) {
965
0
        internal_tree = tree;
966
0
    }
967
    /* Read the entire field header using the dedicated function. */
968
66
    if (dissect_thrift_field_header(tvb, pinfo, internal_tree, &offset, thrift_opt, &field_header, false) == THRIFT_REQUEST_REASSEMBLY) {
969
0
        if (offset == THRIFT_REQUEST_REASSEMBLY) {
970
0
            return THRIFT_REQUEST_REASSEMBLY;
971
0
        } else {
972
0
            return THRIFT_SUBDISSECTOR_ERROR;
973
0
        }
974
0
    }
975
976
    /* Check the type first. */
977
66
    if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) {
978
65
        generic_type = compact_struct_type_to_generic_type(field_header.type.compact);
979
65
    } else {
980
1
        generic_type = field_header.type.binary;
981
1
    }
982
66
    if (generic_type != expected) {
983
20
        proto_tree_add_expert_format(tree, pinfo, &ei_thrift_wrong_type, tvb, offset, TBP_THRIFT_TYPE_LEN,
984
20
                "Sub-dissector expects type = %s, found %s.",
985
20
                val_to_str(pinfo->pool, expected, thrift_type_vals, "%02x"),
986
20
                val_to_str(pinfo->pool, generic_type, thrift_type_vals, "%02x"));
987
20
        return THRIFT_SUBDISSECTOR_ERROR;
988
20
    }
989
990
    /* Once we know it's the expected type (which is /not/ T_STOP), we can read the field id. */
991
46
    if (field_header.field_id != (int64_t)field_id) {
992
0
        expert_add_info_format(pinfo, field_header.fid_pi, &ei_thrift_wrong_field_id,
993
0
                "Sub-dissector expects field id = %d, found %" PRId64 " instead.", field_id, field_header.field_id);
994
0
    }
995
996
    /* Expose the field header sub-tree if requested. */
997
46
    if (header_tree != NULL) {
998
4
        *header_tree = field_header.fh_tree;
999
4
    }
1000
1001
46
    return offset;
1002
66
}
1003
1004
static int
1005
dissect_thrift_raw_bool(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id, dissector_t raw_dissector)
1006
0
{
1007
0
    int dt_offset = offset;
1008
0
    bool bool_val = false;
1009
    /* Get the current state of dissection. */
1010
0
    DISSECTOR_ASSERT(thrift_opt);
1011
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1012
1013
0
    ABORT_SUBDISSECTION_ON_ISSUE(offset);
1014
0
    thrift_opt->use_std_dissector = true;
1015
0
    if (is_field) {
1016
        /* In case of Compact protocol struct field (or command parameter / return value),
1017
         * the boolean type also indicates the boolean value. */
1018
0
        if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) {
1019
            /* Read value in type nibble. */
1020
0
            if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_TYPE_LEN) {
1021
0
                return THRIFT_REQUEST_REASSEMBLY;
1022
0
            }
1023
0
            if (((tvb_get_uint8(tvb, offset) >> TCP_THRIFT_NIBBLE_SHIFT) & TCP_THRIFT_NIBBLE_MASK) == DE_THRIFT_C_BOOL_TRUE) {
1024
0
                bool_val = true;
1025
0
            }
1026
            /* If we have neither DE_THRIFT_C_BOOL_TRUE nor DE_THRIFT_C_BOOL_FALSE as the type,
1027
             * dissect_thrift_t_field_header will catch the issue anyway. */
1028
0
        }
1029
0
        offset = dissect_thrift_t_field_header(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_BOOL, field_id, NULL);
1030
0
        ABORT_SUBDISSECTION_ON_ISSUE(offset);
1031
0
        if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) {
1032
            /* It is the responsibility of the sub-dissector to only use the least significant bit. */
1033
0
            if (raw_dissector != NULL) {
1034
0
                tvbuff_t* sub_tvb = tvb_new_subset_length(tvb, offset, TBP_THRIFT_BOOL_LEN);
1035
0
                thrift_opt->use_std_dissector = false;
1036
0
                raw_dissector(sub_tvb, pinfo, tree, thrift_opt);
1037
0
            }
1038
0
            if (thrift_opt->use_std_dissector) {
1039
                /* The value must be in the top-level tree, /after/ the field header tree. */
1040
0
                proto_item *pi = proto_tree_add_boolean(tree, hf_id, tvb, dt_offset, TBP_THRIFT_TYPE_LEN, bool_val);
1041
0
                proto_item_set_generated(pi);
1042
0
            }
1043
0
            return offset;
1044
0
        }
1045
0
    }
1046
0
    if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_BOOL_LEN) {
1047
0
        return THRIFT_REQUEST_REASSEMBLY;
1048
0
    }
1049
1050
    /* Either in a list/set/map or in a Binary protocol encoding. */
1051
0
    if (raw_dissector != NULL) {
1052
0
        tvbuff_t* sub_tvb = tvb_new_subset_length(tvb, offset, TBP_THRIFT_BOOL_LEN);
1053
0
        thrift_opt->use_std_dissector = false;
1054
0
        raw_dissector(sub_tvb, pinfo, tree, thrift_opt);
1055
0
    }
1056
0
    if (thrift_opt->use_std_dissector) {
1057
0
        proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_BOOL_LEN, ENC_BIG_ENDIAN);
1058
0
    }
1059
0
    offset += TBP_THRIFT_BOOL_LEN;
1060
1061
0
    if (is_field) {
1062
0
        thrift_opt->previous_field_id = field_id;
1063
0
    }
1064
0
    return offset;
1065
0
}
1066
1067
int
1068
dissect_thrift_t_bool(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id)
1069
0
{
1070
0
    DISSECTOR_ASSERT(thrift_opt);
1071
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1072
0
    return dissect_thrift_raw_bool(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, NULL);
1073
0
}
1074
1075
static int
1076
dissect_thrift_raw_i8(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id, dissector_t raw_dissector)
1077
0
{
1078
    /* Get the current state of dissection. */
1079
0
    DISSECTOR_ASSERT(thrift_opt);
1080
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1081
1082
0
    if (is_field) {
1083
0
        offset = dissect_thrift_t_field_header(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_I8, field_id, NULL);
1084
0
    }
1085
0
    ABORT_SUBDISSECTION_ON_ISSUE(offset);
1086
0
    if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_I8_LEN) {
1087
0
        return THRIFT_REQUEST_REASSEMBLY;
1088
0
    }
1089
1090
0
    thrift_opt->use_std_dissector = true;
1091
    /* Compact protocol does not use varint for T_I8 as it would be counter-productive. */
1092
0
    if (raw_dissector != NULL) {
1093
0
        tvbuff_t* sub_tvb = tvb_new_subset_length(tvb, offset, TBP_THRIFT_I8_LEN);
1094
0
        thrift_opt->use_std_dissector = false;
1095
0
        raw_dissector(sub_tvb, pinfo, tree, thrift_opt);
1096
0
    }
1097
0
    if (thrift_opt->use_std_dissector) {
1098
0
        proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_I8_LEN, ENC_BIG_ENDIAN);
1099
0
    }
1100
0
    offset += TBP_THRIFT_I8_LEN;
1101
1102
0
    if (is_field) {
1103
0
        thrift_opt->previous_field_id = field_id;
1104
0
    }
1105
0
    return offset;
1106
0
}
1107
1108
int
1109
dissect_thrift_t_i8(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id)
1110
0
{
1111
0
    DISSECTOR_ASSERT(thrift_opt);
1112
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1113
0
    return dissect_thrift_raw_i8(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, NULL);
1114
0
}
1115
1116
static int
1117
dissect_thrift_raw_i16(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id, dissector_t raw_dissector)
1118
0
{
1119
    /* Get the current state of dissection. */
1120
0
    DISSECTOR_ASSERT(thrift_opt);
1121
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1122
1123
0
    if (is_field) {
1124
0
        offset = dissect_thrift_t_field_header(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_I16, field_id, NULL);
1125
0
    }
1126
0
    ABORT_SUBDISSECTION_ON_ISSUE(offset);
1127
0
    thrift_opt->use_std_dissector = true;
1128
0
    if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) {
1129
0
        int result = dissect_thrift_varint(tvb, pinfo, tree, &offset, thrift_opt, TCP_THRIFT_MAX_I16_LEN, hf_id, raw_dissector);
1130
0
        if (result == THRIFT_REQUEST_REASSEMBLY) {
1131
0
            if (offset == THRIFT_REQUEST_REASSEMBLY) {
1132
0
                return THRIFT_REQUEST_REASSEMBLY;
1133
0
            } else {
1134
0
                return THRIFT_SUBDISSECTOR_ERROR;
1135
0
            }
1136
0
        }
1137
0
    } else if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_I16_LEN) {
1138
0
        return THRIFT_REQUEST_REASSEMBLY;
1139
0
    } else {
1140
0
        if (raw_dissector != NULL) {
1141
0
            tvbuff_t* sub_tvb = tvb_new_subset_length(tvb, offset, TBP_THRIFT_I16_LEN);
1142
0
            thrift_opt->use_std_dissector = false;
1143
0
            raw_dissector(sub_tvb, pinfo, tree, thrift_opt);
1144
0
        }
1145
0
        if (thrift_opt->use_std_dissector) {
1146
0
            proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_I16_LEN, ENC_BIG_ENDIAN);
1147
0
        }
1148
0
        offset += TBP_THRIFT_I16_LEN;
1149
0
    }
1150
1151
0
    if (is_field) {
1152
0
        thrift_opt->previous_field_id = field_id;
1153
0
    }
1154
0
    return offset;
1155
0
}
1156
1157
int
1158
dissect_thrift_t_i16(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id)
1159
0
{
1160
0
    DISSECTOR_ASSERT(thrift_opt);
1161
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1162
0
    return dissect_thrift_raw_i16(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, NULL);
1163
0
}
1164
1165
static int
1166
dissect_thrift_raw_i32(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id, dissector_t raw_dissector)
1167
56
{
1168
    /* Get the current state of dissection. */
1169
56
    DISSECTOR_ASSERT(thrift_opt);
1170
56
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1171
1172
56
    if (is_field) {
1173
56
        offset = dissect_thrift_t_field_header(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_I32, field_id, NULL);
1174
56
    }
1175
56
    ABORT_SUBDISSECTION_ON_ISSUE(offset);
1176
42
    thrift_opt->use_std_dissector = true;
1177
42
    if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) {
1178
42
        int result = dissect_thrift_varint(tvb, pinfo, tree, &offset, thrift_opt, TCP_THRIFT_MAX_I32_LEN, hf_id, raw_dissector);
1179
42
        if (result == THRIFT_REQUEST_REASSEMBLY) {
1180
0
            if (offset == THRIFT_REQUEST_REASSEMBLY) {
1181
0
                return THRIFT_REQUEST_REASSEMBLY;
1182
0
            } else {
1183
0
                return THRIFT_SUBDISSECTOR_ERROR;
1184
0
            }
1185
0
        }
1186
42
    } else if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_I32_LEN) {
1187
0
        return THRIFT_REQUEST_REASSEMBLY;
1188
0
    } else {
1189
0
        if (raw_dissector != NULL) {
1190
0
            tvbuff_t* sub_tvb = tvb_new_subset_length(tvb, offset, TBP_THRIFT_I32_LEN);
1191
0
            thrift_opt->use_std_dissector = false;
1192
0
            raw_dissector(sub_tvb, pinfo, tree, thrift_opt);
1193
0
        }
1194
0
        if (thrift_opt->use_std_dissector) {
1195
0
            proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_I32_LEN, ENC_BIG_ENDIAN);
1196
0
        }
1197
0
        offset += TBP_THRIFT_I32_LEN;
1198
0
    }
1199
1200
42
    if (is_field) {
1201
42
        thrift_opt->previous_field_id = field_id;
1202
42
    }
1203
42
    return offset;
1204
42
}
1205
1206
int
1207
dissect_thrift_t_i32(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id)
1208
0
{
1209
0
    DISSECTOR_ASSERT(thrift_opt);
1210
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1211
0
    return dissect_thrift_raw_i32(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, NULL);
1212
0
}
1213
1214
static int
1215
dissect_thrift_raw_i64(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id, dissector_t raw_dissector)
1216
0
{
1217
    /* Get the current state of dissection. */
1218
0
    DISSECTOR_ASSERT(thrift_opt);
1219
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1220
1221
0
    if (is_field) {
1222
0
        offset = dissect_thrift_t_field_header(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_I64, field_id, NULL);
1223
0
    }
1224
0
    ABORT_SUBDISSECTION_ON_ISSUE(offset);
1225
0
    thrift_opt->use_std_dissector = true;
1226
0
    if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) {
1227
0
        int result = dissect_thrift_varint(tvb, pinfo, tree, &offset, thrift_opt, TCP_THRIFT_MAX_I64_LEN, hf_id, raw_dissector);
1228
0
        if (result == THRIFT_REQUEST_REASSEMBLY) {
1229
0
            if (offset == THRIFT_REQUEST_REASSEMBLY) {
1230
0
                return THRIFT_REQUEST_REASSEMBLY;
1231
0
            } else {
1232
0
                return THRIFT_SUBDISSECTOR_ERROR;
1233
0
            }
1234
0
        }
1235
0
    } else if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_I64_LEN) {
1236
0
        return THRIFT_REQUEST_REASSEMBLY;
1237
0
    } else {
1238
0
        if (raw_dissector != NULL) {
1239
0
            tvbuff_t* sub_tvb = tvb_new_subset_length(tvb, offset, TBP_THRIFT_I64_LEN);
1240
0
            thrift_opt->use_std_dissector = false;
1241
0
            raw_dissector(sub_tvb, pinfo, tree, thrift_opt);
1242
0
        }
1243
0
        if (thrift_opt->use_std_dissector) {
1244
0
            proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_I64_LEN, ENC_BIG_ENDIAN);
1245
0
        }
1246
0
        offset += TBP_THRIFT_I64_LEN;
1247
0
    }
1248
1249
0
    if (is_field) {
1250
0
        thrift_opt->previous_field_id = field_id;
1251
0
    }
1252
0
    return offset;
1253
0
}
1254
1255
int
1256
dissect_thrift_t_i64(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id)
1257
0
{
1258
0
    DISSECTOR_ASSERT(thrift_opt);
1259
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1260
0
    return dissect_thrift_raw_i64(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, NULL);
1261
0
}
1262
1263
static int
1264
dissect_thrift_raw_double(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id, dissector_t raw_dissector)
1265
0
{
1266
    /* Get the current state of dissection. */
1267
0
    DISSECTOR_ASSERT(thrift_opt);
1268
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1269
1270
0
    if (is_field) {
1271
0
        offset = dissect_thrift_t_field_header(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_DOUBLE, field_id, NULL);
1272
0
    }
1273
0
    ABORT_SUBDISSECTION_ON_ISSUE(offset);
1274
0
    if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_DOUBLE_LEN) {
1275
0
        return THRIFT_REQUEST_REASSEMBLY;
1276
0
    }
1277
1278
0
    thrift_opt->use_std_dissector = true;
1279
0
    if (raw_dissector != NULL) {
1280
0
        tvbuff_t* sub_tvb;
1281
0
        if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) {
1282
            /* Create a sub-tvbuff_t in big endian format as documented. */
1283
0
            uint8_t *data = wmem_alloc(pinfo->pool, TBP_THRIFT_DOUBLE_LEN);
1284
0
            data[0] = tvb_get_uint8(tvb, offset + 7);
1285
0
            data[1] = tvb_get_uint8(tvb, offset + 6);
1286
0
            data[2] = tvb_get_uint8(tvb, offset + 5);
1287
0
            data[3] = tvb_get_uint8(tvb, offset + 4);
1288
0
            data[4] = tvb_get_uint8(tvb, offset + 3);
1289
0
            data[5] = tvb_get_uint8(tvb, offset + 2);
1290
0
            data[6] = tvb_get_uint8(tvb, offset + 1);
1291
0
            data[7] = tvb_get_uint8(tvb, offset);
1292
0
            sub_tvb = tvb_new_child_real_data(tvb, data, TBP_THRIFT_DOUBLE_LEN, TBP_THRIFT_DOUBLE_LEN);
1293
0
        } else {
1294
0
            sub_tvb = tvb_new_subset_length(tvb, offset, TBP_THRIFT_DOUBLE_LEN);
1295
0
        }
1296
0
        thrift_opt->use_std_dissector = false;
1297
0
        raw_dissector(sub_tvb, pinfo, tree, thrift_opt);
1298
0
    }
1299
0
    if (thrift_opt->use_std_dissector) {
1300
0
        if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) {
1301
0
            proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_DOUBLE_LEN, ENC_LITTLE_ENDIAN);
1302
0
        } else {
1303
0
            proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_DOUBLE_LEN, ENC_BIG_ENDIAN);
1304
0
        }
1305
0
    }
1306
0
    offset += TBP_THRIFT_DOUBLE_LEN;
1307
1308
0
    if (is_field) {
1309
0
        thrift_opt->previous_field_id = field_id;
1310
0
    }
1311
0
    return offset;
1312
0
}
1313
1314
int
1315
dissect_thrift_t_double(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id)
1316
0
{
1317
0
    DISSECTOR_ASSERT(thrift_opt);
1318
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1319
0
    return dissect_thrift_raw_double(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, NULL);
1320
0
}
1321
1322
static int
1323
dissect_thrift_raw_uuid(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id, dissector_t raw_dissector)
1324
0
{
1325
    /* Get the current state of dissection. */
1326
0
    DISSECTOR_ASSERT(thrift_opt);
1327
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1328
1329
    /* Dissect field header if necessary. */
1330
0
    if (is_field) {
1331
0
        offset = dissect_thrift_t_field_header(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_UUID, field_id, NULL);
1332
0
    }
1333
0
    ABORT_SUBDISSECTION_ON_ISSUE(offset);
1334
1335
0
    if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_UUID_LEN) {
1336
0
        return THRIFT_REQUEST_REASSEMBLY;
1337
0
    }
1338
1339
0
    thrift_opt->use_std_dissector = true;
1340
0
    if (raw_dissector != NULL) {
1341
0
        tvbuff_t* sub_tvb = tvb_new_subset_length(tvb, offset, TBP_THRIFT_UUID_LEN);
1342
0
        thrift_opt->use_std_dissector = false;
1343
0
        raw_dissector(sub_tvb, pinfo, tree, thrift_opt);
1344
0
    }
1345
0
    if (thrift_opt->use_std_dissector) {
1346
0
        proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_UUID_LEN, ENC_BIG_ENDIAN);
1347
0
    }
1348
0
    offset += TBP_THRIFT_UUID_LEN;
1349
1350
0
    if (is_field) {
1351
0
        thrift_opt->previous_field_id = field_id;
1352
0
    }
1353
0
    return offset;
1354
0
}
1355
1356
int
1357
dissect_thrift_t_uuid(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id)
1358
0
{
1359
0
    DISSECTOR_ASSERT(thrift_opt);
1360
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1361
0
    return dissect_thrift_raw_uuid(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, NULL);
1362
0
}
1363
1364
static int
1365
dissect_thrift_raw_binary(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id, unsigned encoding, dissector_t raw_dissector)
1366
10
{
1367
    /* Get the current state of dissection. */
1368
10
    DISSECTOR_ASSERT(thrift_opt);
1369
10
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1370
1371
10
    proto_tree *header_tree = NULL;
1372
10
    proto_item *len_item = NULL;
1373
10
    int32_t str_len, len_len;
1374
10
    uint64_t varint;
1375
1376
    /* Dissect field header if necessary. */
1377
10
    if (is_field) {
1378
10
        offset = dissect_thrift_t_field_header(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_BINARY, field_id, &header_tree);
1379
10
    } else {
1380
0
        header_tree = tree;
1381
0
    }
1382
10
    ABORT_SUBDISSECTION_ON_ISSUE(offset);
1383
1384
    /* Dissect length. */
1385
5
    if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) {
1386
4
        len_len = thrift_get_varint_enc(tvb, pinfo, header_tree, offset, TCP_THRIFT_MAX_I32_LEN, &varint, ENC_VARINT_PROTOBUF);
1387
4
        switch (len_len) {
1388
0
            case THRIFT_REQUEST_REASSEMBLY:
1389
0
                return THRIFT_REQUEST_REASSEMBLY;
1390
0
            case 0:
1391
0
                return THRIFT_SUBDISSECTOR_ERROR;
1392
4
            default:
1393
4
                break;
1394
4
        }
1395
4
        if (varint > (uint64_t)INT32_MAX) {
1396
0
            len_item = proto_tree_add_uint64(header_tree, hf_thrift_u64, tvb, offset, len_len, varint);
1397
0
            expert_add_info(pinfo, len_item, &ei_thrift_varint_too_large);
1398
0
            return THRIFT_REQUEST_REASSEMBLY;
1399
0
        }
1400
4
        str_len = (int32_t)varint;
1401
4
        if (show_internal_thrift_fields) {
1402
0
            len_item = proto_tree_add_int(header_tree, hf_thrift_str_len, tvb, offset, len_len, str_len);
1403
0
        }
1404
4
    } else {
1405
1
        len_len = TBP_THRIFT_LENGTH_LEN;
1406
1
        if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_LENGTH_LEN) {
1407
0
            return THRIFT_REQUEST_REASSEMBLY;
1408
0
        }
1409
1
        if (show_internal_thrift_fields) {
1410
0
            len_item = proto_tree_add_item_ret_int(header_tree, hf_thrift_str_len, tvb, offset, TBP_THRIFT_LENGTH_LEN, ENC_BIG_ENDIAN, &str_len);
1411
1
        } else {
1412
1
            str_len = tvb_get_ntohil(tvb, offset);
1413
1
        }
1414
1
    }
1415
5
    if (str_len < 0) {
1416
0
        expert_add_info(pinfo, len_item, &ei_thrift_negative_length);
1417
0
        return THRIFT_SUBDISSECTOR_ERROR;
1418
0
    }
1419
5
    offset += len_len;
1420
    /* Since we put the length inside the field header, we need to extend it. */
1421
5
    if (header_tree != tree) {
1422
4
        proto_item_set_end(proto_tree_get_parent(header_tree), tvb, offset);
1423
4
    }
1424
1425
    /* Dissect data */
1426
5
    if (tvb_reported_length_remaining(tvb, offset) < str_len) {
1427
2
        return THRIFT_REQUEST_REASSEMBLY;
1428
2
    }
1429
1430
3
    thrift_opt->use_std_dissector = true;
1431
3
    if (raw_dissector != NULL) {
1432
0
        tvbuff_t* sub_tvb = tvb_new_subset_length(tvb, offset, str_len);
1433
0
        thrift_opt->use_std_dissector = false;
1434
0
        raw_dissector(sub_tvb, pinfo, tree, thrift_opt);
1435
0
    }
1436
3
    if (thrift_opt->use_std_dissector) {
1437
2
        proto_tree_add_item(tree, hf_id, tvb, offset, str_len, encoding);
1438
2
    }
1439
3
    offset = offset + str_len;
1440
1441
3
    if (is_field) {
1442
2
        thrift_opt->previous_field_id = field_id;
1443
2
    }
1444
3
    return offset;
1445
5
}
1446
1447
int
1448
dissect_thrift_t_binary(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id)
1449
0
{
1450
0
    DISSECTOR_ASSERT(thrift_opt);
1451
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1452
0
    return dissect_thrift_raw_binary(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ENC_NA, NULL);
1453
0
}
1454
1455
int
1456
dissect_thrift_t_string(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id)
1457
0
{
1458
0
    DISSECTOR_ASSERT(thrift_opt);
1459
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1460
0
    return dissect_thrift_raw_binary(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ENC_UTF_8, NULL);
1461
0
}
1462
1463
int
1464
dissect_thrift_t_string_enc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id, unsigned encoding)
1465
0
{
1466
0
    DISSECTOR_ASSERT(thrift_opt);
1467
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1468
0
    return dissect_thrift_raw_binary(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, encoding, NULL);
1469
0
}
1470
1471
int
1472
dissect_thrift_t_raw_data(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, int offset, thrift_option_data_t* thrift_opt, bool is_field, int field_id, int hf_id, thrift_type_enum_t type, dissector_t raw_dissector)
1473
0
{
1474
    /* Get the current state of dissection. */
1475
0
    DISSECTOR_ASSERT(thrift_opt);
1476
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1477
1478
0
    switch (type) {
1479
0
    case DE_THRIFT_T_BOOL:
1480
0
        offset = dissect_thrift_raw_bool(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, raw_dissector);
1481
0
        break;
1482
0
    case DE_THRIFT_T_I8:
1483
0
        offset = dissect_thrift_raw_i8(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, raw_dissector);
1484
0
        break;
1485
0
    case DE_THRIFT_T_I16:
1486
0
        offset = dissect_thrift_raw_i16(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, raw_dissector);
1487
0
        break;
1488
0
    case DE_THRIFT_T_I32:
1489
0
        offset = dissect_thrift_raw_i32(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, raw_dissector);
1490
0
        break;
1491
0
    case DE_THRIFT_T_I64:
1492
0
        offset = dissect_thrift_raw_i64(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, raw_dissector);
1493
0
        break;
1494
0
    case DE_THRIFT_T_DOUBLE:
1495
0
        offset = dissect_thrift_raw_double(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, raw_dissector);
1496
0
        break;
1497
0
    case DE_THRIFT_T_BINARY:
1498
0
        offset = dissect_thrift_raw_binary(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ENC_NA, raw_dissector);
1499
0
        break;
1500
0
    case DE_THRIFT_T_UUID:
1501
0
        offset = dissect_thrift_raw_uuid(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, raw_dissector);
1502
0
        break;
1503
0
    default:
1504
0
        REPORT_DISSECTOR_BUG("Only simple data types support raw dissection.");
1505
0
        break;
1506
0
    }
1507
0
    return offset;
1508
0
}
1509
1510
/* Simple dispatch function for lists, sets, maps, and structs internal elements to avoid code duplication. */
1511
static int
1512
// NOLINTNEXTLINE(misc-no-recursion)
1513
dissect_thrift_t_member(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, const thrift_member_t *elt)
1514
66
{
1515
66
    switch (elt->type) {
1516
0
    case DE_THRIFT_T_STOP:
1517
0
        offset = dissect_thrift_t_stop(tvb, pinfo, tree, offset);
1518
0
        break;
1519
0
    case DE_THRIFT_T_BOOL:
1520
0
        offset = dissect_thrift_raw_bool(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, elt->raw_dissector);
1521
0
        break;
1522
0
    case DE_THRIFT_T_I8:
1523
0
        offset = dissect_thrift_raw_i8(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, elt->raw_dissector);
1524
0
        break;
1525
0
    case DE_THRIFT_T_I16:
1526
0
        offset = dissect_thrift_raw_i16(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, elt->raw_dissector);
1527
0
        break;
1528
56
    case DE_THRIFT_T_I32:
1529
56
        offset = dissect_thrift_raw_i32(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, elt->raw_dissector);
1530
56
        break;
1531
0
    case DE_THRIFT_T_I64:
1532
0
        offset = dissect_thrift_raw_i64(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, elt->raw_dissector);
1533
0
        break;
1534
0
    case DE_THRIFT_T_DOUBLE:
1535
0
        offset = dissect_thrift_raw_double(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, elt->raw_dissector);
1536
0
        break;
1537
10
    case DE_THRIFT_T_BINARY:
1538
10
        offset = dissect_thrift_raw_binary(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, elt->u.encoding, elt->raw_dissector);
1539
10
        break;
1540
0
    case DE_THRIFT_T_LIST:
1541
0
        offset = dissect_thrift_t_list(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, *elt->p_ett_id, elt->u.element);
1542
0
        break;
1543
0
    case DE_THRIFT_T_SET:
1544
0
        offset = dissect_thrift_t_set(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, *elt->p_ett_id, elt->u.element);
1545
0
        break;
1546
0
    case DE_THRIFT_T_MAP:
1547
0
        offset = dissect_thrift_t_map(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, *elt->p_ett_id, elt->u.m.key, elt->u.m.value);
1548
0
        break;
1549
0
    case DE_THRIFT_T_STRUCT:
1550
0
        offset = dissect_thrift_t_struct_expert(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, *elt->p_ett_id, elt->u.s.members, elt->u.s.expert_info);
1551
0
        break;
1552
0
    case DE_THRIFT_T_UUID:
1553
0
        offset = dissect_thrift_raw_uuid(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, elt->raw_dissector);
1554
0
        break;
1555
0
    default:
1556
0
        REPORT_DISSECTOR_BUG("Unexpected Thrift type dissection requested.");
1557
0
        break;
1558
66
    }
1559
65
    return offset;
1560
66
}
1561
1562
/* Effective sub-dissection for lists, sets, and maps in Binary Protocol.
1563
 * Since the only difference is in the hf_id used when showing internal Thrift fields,
1564
 * this prevents code duplication.
1565
 * Map is only adding a type in the header and one element in each loop
1566
 * so it's easy to use the same code and handle the additional elements only when necessary.
1567
 */
1568
static int
1569
// NOLINTNEXTLINE(misc-no-recursion)
1570
dissect_thrift_b_linear(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id, int ett_id, const thrift_member_t *key, const thrift_member_t *val, thrift_type_enum_t expected)
1571
0
{
1572
0
    proto_item *container_pi = NULL;
1573
0
    proto_item *len_pi = NULL;
1574
0
    proto_tree *sub_tree;
1575
0
    int32_t key_type, val_type;
1576
0
    int32_t length;
1577
0
    unsigned nested_count = p_get_proto_depth(pinfo, proto_thrift);
1578
1579
    /* Get the current state of dissection. */
1580
0
    DISSECTOR_ASSERT(thrift_opt);
1581
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1582
0
    DISSECTOR_ASSERT((thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) == 0);
1583
1584
    /* Dissect field header if necessary. */
1585
0
    if (is_field) {
1586
0
        offset = dissect_thrift_t_field_header(tvb, pinfo, tree, offset, thrift_opt, expected, field_id, NULL);
1587
0
    }
1588
1589
    /* Create the sub-tree. */
1590
0
    if (nested_count >= thrift_opt->nested_type_depth) {
1591
0
        expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes);
1592
0
        return THRIFT_SUBDISSECTOR_ERROR;
1593
0
    }
1594
0
    p_set_proto_depth(pinfo, proto_thrift, nested_count + 1);
1595
0
    container_pi = proto_tree_add_item(tree, hf_id, tvb, offset, -1, ENC_BIG_ENDIAN);
1596
0
    sub_tree = proto_item_add_subtree(container_pi, ett_id);
1597
0
    ABORT_SUBDISSECTION_ON_ISSUE(offset);
1598
1599
    /* Read and check the type of the key in case of map. */
1600
0
    if (expected == DE_THRIFT_T_MAP) {
1601
0
        if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_TYPE_LEN) {
1602
0
            return THRIFT_REQUEST_REASSEMBLY;
1603
0
        }
1604
0
        key_type = tvb_get_uint8(tvb, offset);
1605
0
        if (show_internal_thrift_fields) {
1606
0
            proto_tree_add_item(sub_tree, hf_thrift_type, tvb, offset, TBP_THRIFT_TYPE_LEN, ENC_BIG_ENDIAN);
1607
0
        }
1608
0
        if (key_type != key->type) {
1609
0
            proto_tree_add_expert(sub_tree, pinfo, &ei_thrift_wrong_type, tvb, offset, TBP_THRIFT_TYPE_LEN);
1610
0
            return THRIFT_SUBDISSECTOR_ERROR;
1611
0
        }
1612
0
        offset += TBP_THRIFT_TYPE_LEN;
1613
0
    }
1614
1615
    /* Read and check the type of the elements (or type of the values in case of map). */
1616
0
    if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_TYPE_LEN) {
1617
0
        return THRIFT_REQUEST_REASSEMBLY;
1618
0
    }
1619
0
    val_type = tvb_get_uint8(tvb, offset);
1620
0
    if (show_internal_thrift_fields) {
1621
0
        proto_tree_add_item(sub_tree, hf_thrift_type, tvb, offset, TBP_THRIFT_TYPE_LEN, ENC_BIG_ENDIAN);
1622
0
    }
1623
0
    if (val_type != val->type) {
1624
0
        proto_tree_add_expert(sub_tree, pinfo, &ei_thrift_wrong_type, tvb, offset, TBP_THRIFT_TYPE_LEN);
1625
0
        return THRIFT_SUBDISSECTOR_ERROR;
1626
0
    }
1627
0
    offset += TBP_THRIFT_TYPE_LEN;
1628
1629
    /* Read and check the number of entries of the container. */
1630
0
    if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_LENGTH_LEN) {
1631
0
        return THRIFT_REQUEST_REASSEMBLY;
1632
0
    }
1633
0
    length = tvb_get_ntohil(tvb, offset);
1634
0
    if (show_internal_thrift_fields) {
1635
0
        int hf_num_item;
1636
0
        switch (expected) {
1637
0
            case DE_THRIFT_T_MAP:
1638
0
                hf_num_item = hf_thrift_num_map_item;
1639
0
                break;
1640
0
            case DE_THRIFT_T_SET:
1641
0
                hf_num_item = hf_thrift_num_set_item;
1642
0
                break;
1643
0
            case DE_THRIFT_T_LIST:
1644
0
                hf_num_item = hf_thrift_num_list_item;
1645
0
                break;
1646
0
            default:
1647
0
                return THRIFT_SUBDISSECTOR_ERROR;
1648
0
        }
1649
0
        len_pi = proto_tree_add_item_ret_int(sub_tree, hf_num_item, tvb, offset, TBP_THRIFT_LENGTH_LEN, ENC_BIG_ENDIAN, &length);
1650
0
    }
1651
0
    offset += TBP_THRIFT_LENGTH_LEN;
1652
0
    if (length < 0) {
1653
0
        expert_add_info(pinfo, len_pi, &ei_thrift_negative_length);
1654
0
        return THRIFT_SUBDISSECTOR_ERROR;
1655
0
    }
1656
1657
    /* Read the content of the container. */
1658
0
    for(int i = 0; i < length; ++i) {
1659
0
        if (expected == DE_THRIFT_T_MAP) {
1660
0
            offset = dissect_thrift_t_member(tvb, pinfo, sub_tree, offset, thrift_opt, false, key);
1661
0
        }
1662
0
        offset = dissect_thrift_t_member(tvb, pinfo, sub_tree, offset, thrift_opt, false, val);
1663
        /* Avoid continuing the loop if anything went sideways. */
1664
0
        ABORT_SUBDISSECTION_ON_ISSUE(offset);
1665
0
    }
1666
0
    if (container_pi && offset > 0) {
1667
0
        proto_item_set_end(container_pi, tvb, offset);
1668
0
    }
1669
0
    p_set_proto_depth(pinfo, proto_thrift, nested_count);
1670
0
    return offset;
1671
0
}
1672
1673
/* Effective sub-dissection for both lists and sets for Compact Protocol.
1674
 * Since the only difference is in the hf_id used when showing internal Thrift fields,
1675
 * this prevents code duplication.
1676
 */
1677
static int
1678
// NOLINTNEXTLINE(misc-no-recursion)
1679
dissect_thrift_c_list_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id, int ett_id, const thrift_member_t *elt, bool is_list)
1680
0
{
1681
0
    proto_item *container_pi;
1682
0
    proto_item *type_pi = NULL;
1683
0
    proto_item *len_pi = NULL;
1684
0
    proto_tree *sub_tree = NULL;
1685
0
    uint32_t len_type;
1686
0
    thrift_compact_type_enum_t elt_type;
1687
0
    int32_t container_len, len_len, i;
1688
0
    uint64_t varint;
1689
0
    int lt_offset;
1690
0
    int hf_num_item = hf_thrift_num_set_item;
1691
0
    int hf_pos_item = hf_thrift_num_set_pos;
1692
0
    thrift_type_enum_t expected = DE_THRIFT_T_SET;
1693
0
    unsigned nested_count = p_get_proto_depth(pinfo, proto_thrift);
1694
1695
0
    if (is_list) {
1696
0
        hf_num_item = hf_thrift_num_list_item;
1697
0
        hf_pos_item = hf_thrift_num_list_pos;
1698
0
        expected = DE_THRIFT_T_LIST;
1699
0
    }
1700
1701
    /* Get the current state of dissection. */
1702
0
    DISSECTOR_ASSERT(thrift_opt);
1703
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1704
0
    DISSECTOR_ASSERT(thrift_opt->tprotocol & PROTO_THRIFT_COMPACT);
1705
1706
    /* Dissect field header if necessary. */
1707
0
    if (is_field) {
1708
0
        offset = dissect_thrift_t_field_header(tvb, pinfo, tree, offset, thrift_opt, expected, field_id, NULL);
1709
0
    }
1710
0
    ABORT_SUBDISSECTION_ON_ISSUE(offset);
1711
0
    if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_TYPE_LEN) {
1712
0
        return THRIFT_REQUEST_REASSEMBLY;
1713
0
    }
1714
1715
    /* Create the sub-tree. */
1716
0
    if (nested_count >= thrift_opt->nested_type_depth) {
1717
0
        expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes);
1718
0
        return THRIFT_SUBDISSECTOR_ERROR;
1719
0
    }
1720
0
    p_set_proto_depth(pinfo, proto_thrift, nested_count + 1);
1721
0
    container_pi = proto_tree_add_item(tree, hf_id, tvb, offset, -1, ENC_BIG_ENDIAN);
1722
0
    sub_tree = proto_item_add_subtree(container_pi, ett_id);
1723
1724
    /* Read the type of the elements (and length if lower than 15). */
1725
0
    lt_offset = offset;
1726
0
    len_type = tvb_get_uint8(tvb, lt_offset);
1727
0
    offset += TBP_THRIFT_TYPE_LEN;
1728
0
    elt_type = (thrift_compact_type_enum_t)(len_type & TCP_THRIFT_NIBBLE_MASK);
1729
0
    if (show_internal_thrift_fields) {
1730
0
        type_pi = proto_tree_add_bits_item(sub_tree, hf_thrift_type, tvb, (lt_offset << OCTETS_TO_BITS_SHIFT) + TCP_THRIFT_NIBBLE_SHIFT, TCP_THRIFT_NIBBLE_SHIFT, ENC_BIG_ENDIAN);
1731
0
    }
1732
    /* Check the type of the elements. */
1733
0
    if (compact_struct_type_to_generic_type(elt_type) != elt->type) {
1734
0
        if (show_internal_thrift_fields) {
1735
0
            expert_add_info(pinfo, type_pi, &ei_thrift_wrong_type);
1736
0
        }
1737
0
        proto_tree_add_expert(sub_tree, pinfo, &ei_thrift_wrong_type, tvb, offset, TBP_THRIFT_TYPE_LEN);
1738
0
        return THRIFT_SUBDISSECTOR_ERROR;
1739
0
    }
1740
0
    container_len = (len_type >> TCP_THRIFT_NIBBLE_SHIFT) & TCP_THRIFT_NIBBLE_MASK;
1741
1742
    /* Read and check the number of entries of the container. */
1743
0
    if (container_len == TCP_THRIFT_LENGTH_LARGER) {
1744
0
        if (show_internal_thrift_fields) {
1745
0
            proto_tree_add_bits_item(sub_tree, hf_thrift_large_container, tvb, (lt_offset << OCTETS_TO_BITS_SHIFT), TCP_THRIFT_NIBBLE_SHIFT, ENC_BIG_ENDIAN);
1746
0
        }
1747
        /* Length is greater than 14, read effective length as a varint. */
1748
0
        len_len = thrift_get_varint_enc(tvb, pinfo, sub_tree, offset, TCP_THRIFT_MAX_I32_LEN, &varint, ENC_VARINT_PROTOBUF);
1749
0
        switch (len_len) {
1750
0
        case THRIFT_REQUEST_REASSEMBLY:
1751
0
            return THRIFT_REQUEST_REASSEMBLY;
1752
0
        case 0:
1753
            /* In case of error, the offset stay at the error position. */
1754
0
            return THRIFT_SUBDISSECTOR_ERROR;
1755
0
        default:
1756
0
            if (varint > (uint64_t)INT32_MAX) {
1757
0
                len_pi = proto_tree_add_int64(sub_tree, hf_thrift_i64, tvb, offset, len_len, varint);
1758
0
                expert_add_info(pinfo, len_pi, &ei_thrift_varint_too_large);
1759
0
                return THRIFT_SUBDISSECTOR_ERROR;
1760
0
            }
1761
0
            container_len = (uint32_t)varint;
1762
0
            if (show_internal_thrift_fields) {
1763
0
                proto_tree_add_int(sub_tree, hf_num_item, tvb, offset, len_len, container_len);
1764
0
            }
1765
0
            offset += len_len;
1766
0
            break;
1767
0
        }
1768
0
    } else if (show_internal_thrift_fields) {
1769
0
        proto_tree_add_bits_item(sub_tree, hf_pos_item, tvb, (lt_offset << OCTETS_TO_BITS_SHIFT), TCP_THRIFT_NIBBLE_SHIFT, ENC_BIG_ENDIAN);
1770
0
    }
1771
1772
    /* Read the content of the container. */
1773
0
    for (i = 0; i < container_len; ++i) {
1774
0
        offset = dissect_thrift_t_member(tvb, pinfo, sub_tree, offset, thrift_opt, false, elt);
1775
        /* Avoid continuing the loop if anything went sideways. */
1776
0
        ABORT_SUBDISSECTION_ON_ISSUE(offset);
1777
0
    }
1778
0
    if (container_pi && offset > 0) {
1779
0
        proto_item_set_end(container_pi, tvb, offset);
1780
0
    }
1781
0
    p_set_proto_depth(pinfo, proto_thrift, nested_count);
1782
0
    return offset;
1783
0
}
1784
1785
int
1786
// NOLINTNEXTLINE(misc-no-recursion)
1787
dissect_thrift_t_list(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id, int ett_id, const thrift_member_t *elt)
1788
0
{
1789
0
    int result;
1790
0
    if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) {
1791
0
        result = dissect_thrift_c_list_set(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ett_id, elt, true);
1792
0
    } else {
1793
0
        result = dissect_thrift_b_linear(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ett_id, NULL, elt, DE_THRIFT_T_LIST);
1794
0
    }
1795
1796
0
    if (is_field) {
1797
0
        thrift_opt->previous_field_id = field_id;
1798
0
    }
1799
0
    return result;
1800
0
}
1801
1802
int
1803
// NOLINTNEXTLINE(misc-no-recursion)
1804
dissect_thrift_t_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id, int ett_id, const thrift_member_t *elt)
1805
0
{
1806
0
    int result;
1807
0
    if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) {
1808
0
        result = dissect_thrift_c_list_set(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ett_id, elt, false);
1809
0
    } else {
1810
0
        result = dissect_thrift_b_linear(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ett_id, NULL, elt, DE_THRIFT_T_SET);
1811
0
    }
1812
1813
0
    if (is_field) {
1814
0
        thrift_opt->previous_field_id = field_id;
1815
0
    }
1816
0
    return result;
1817
0
}
1818
1819
int
1820
// NOLINTNEXTLINE(misc-no-recursion)
1821
dissect_thrift_t_map(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id, int ett_id, const thrift_member_t *key, const thrift_member_t *value)
1822
0
{
1823
0
    int result;
1824
    /* Get the current state of dissection. */
1825
0
    DISSECTOR_ASSERT(thrift_opt);
1826
0
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1827
1828
0
    if ((thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) == 0) {
1829
0
        result = dissect_thrift_b_linear(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ett_id, key, value, DE_THRIFT_T_MAP);
1830
0
    } else {
1831
0
        proto_tree *sub_tree = NULL;
1832
0
        proto_item *container_pi;
1833
0
        proto_item *ktype_pi = NULL;
1834
0
        proto_item *vtype_pi = NULL;
1835
0
        int32_t container_len, len_len, i, types;
1836
0
        int32_t len_offset = offset;
1837
0
        thrift_compact_type_enum_t ktype, vtype;
1838
0
        uint64_t varint;
1839
0
        unsigned nested_count = p_get_proto_depth(pinfo, proto_thrift);
1840
1841
        /* Dissect field header if necessary. */
1842
0
        if (is_field) {
1843
0
            if (show_internal_thrift_fields) {
1844
0
                sub_tree = tree;
1845
0
            }
1846
0
            offset = dissect_thrift_t_field_header(tvb, pinfo, sub_tree, offset, thrift_opt, DE_THRIFT_T_MAP, field_id, NULL);
1847
0
        }
1848
1849
        /* Read and check number of key-value pair in the map. */
1850
0
        if (tvb_reported_length_remaining(tvb, offset) < TCP_THRIFT_MIN_VARINT_LEN) {
1851
0
            return THRIFT_REQUEST_REASSEMBLY;
1852
0
        }
1853
0
        len_len = thrift_get_varint_enc(tvb, pinfo, sub_tree, offset, TCP_THRIFT_MAX_I32_LEN, &varint, ENC_VARINT_PROTOBUF);
1854
0
        switch (len_len) {
1855
0
        case THRIFT_REQUEST_REASSEMBLY:
1856
0
            return THRIFT_REQUEST_REASSEMBLY;
1857
0
        case 0:
1858
            /* In case of error, the offset stay at the error position. */
1859
0
            return THRIFT_SUBDISSECTOR_ERROR;
1860
0
        default:
1861
0
            if (varint > (uint64_t)INT32_MAX) {
1862
0
                proto_item *len_pi = proto_tree_add_int64(sub_tree, hf_thrift_i64, tvb, offset, len_len, varint);
1863
0
                expert_add_info(pinfo, len_pi, &ei_thrift_varint_too_large);
1864
0
                return THRIFT_SUBDISSECTOR_ERROR;
1865
0
            }
1866
0
            container_len = (uint32_t)varint;
1867
0
            offset += len_len;
1868
0
            break;
1869
0
        }
1870
1871
        /* Create the sub-tree. */
1872
0
        if (nested_count >= thrift_opt->nested_type_depth) {
1873
0
            expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes);
1874
0
            return THRIFT_SUBDISSECTOR_ERROR;
1875
0
        }
1876
0
        p_set_proto_depth(pinfo, proto_thrift, nested_count + 1);
1877
0
        container_pi = proto_tree_add_item(tree, hf_id, tvb, len_offset, -1, ENC_BIG_ENDIAN);
1878
0
        sub_tree = proto_item_add_subtree(container_pi, ett_id);
1879
1880
0
        if (container_len == 0) {
1881
0
            proto_item_set_end(container_pi, tvb, offset);
1882
0
            proto_item_append_text(container_pi, " (Empty)");
1883
0
            p_set_proto_depth(pinfo, proto_thrift, nested_count);
1884
0
            return offset;
1885
0
        }
1886
1887
        /* If the map is not empty, read the types of keys and values. */
1888
0
        if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_TYPE_LEN) {
1889
0
            return THRIFT_REQUEST_REASSEMBLY;
1890
0
        }
1891
0
        types = tvb_get_uint8(tvb, offset);
1892
0
        ktype = (thrift_compact_type_enum_t)((types >> TCP_THRIFT_NIBBLE_SHIFT) & TCP_THRIFT_NIBBLE_MASK);
1893
0
        vtype = (thrift_compact_type_enum_t)(types & TCP_THRIFT_NIBBLE_MASK);
1894
0
        if (show_internal_thrift_fields) {
1895
0
            proto_tree_add_int(sub_tree, hf_thrift_num_map_item, tvb, len_offset, len_len, container_len);
1896
0
            ktype_pi = proto_tree_add_bits_item(sub_tree, hf_thrift_key_type, tvb, offset << OCTETS_TO_BITS_SHIFT, TCP_THRIFT_NIBBLE_SHIFT, ENC_BIG_ENDIAN);
1897
0
            vtype_pi = proto_tree_add_bits_item(sub_tree, hf_thrift_value_type, tvb, (offset << OCTETS_TO_BITS_SHIFT) + TCP_THRIFT_NIBBLE_SHIFT, TCP_THRIFT_NIBBLE_SHIFT, ENC_BIG_ENDIAN);
1898
0
        }
1899
1900
        /* Check that types match what is expected. */
1901
0
        if (compact_struct_type_to_generic_type(ktype) != key->type) {
1902
0
            if (show_internal_thrift_fields) {
1903
0
                expert_add_info(pinfo, ktype_pi, &ei_thrift_wrong_type);
1904
0
            } else {
1905
0
                proto_tree_add_expert(sub_tree, pinfo, &ei_thrift_wrong_type, tvb, offset, TBP_THRIFT_TYPE_LEN);
1906
0
            }
1907
0
            return THRIFT_SUBDISSECTOR_ERROR;
1908
0
        }
1909
0
        if (compact_struct_type_to_generic_type(vtype) != value->type) {
1910
0
            if (show_internal_thrift_fields) {
1911
0
                expert_add_info(pinfo, vtype_pi, &ei_thrift_wrong_type);
1912
0
            } else {
1913
0
                proto_tree_add_expert(sub_tree, pinfo, &ei_thrift_wrong_type, tvb, offset, TBP_THRIFT_TYPE_LEN);
1914
0
            }
1915
0
            return THRIFT_SUBDISSECTOR_ERROR;
1916
0
        }
1917
1918
        /* Read the content of the container. */
1919
0
        for (i = 0; i < container_len; ++i) {
1920
0
            offset = dissect_thrift_t_member(tvb, pinfo, sub_tree, offset, thrift_opt, false, key);
1921
0
            offset = dissect_thrift_t_member(tvb, pinfo, sub_tree, offset, thrift_opt, false, value);
1922
            /* Avoid continuing the loop if anything went sideways. */
1923
0
            ABORT_SUBDISSECTION_ON_ISSUE(offset);
1924
0
        }
1925
1926
0
        if (container_pi && offset > 0) {
1927
0
            proto_item_set_end(container_pi, tvb, offset);
1928
0
        }
1929
0
        result = offset;
1930
0
        p_set_proto_depth(pinfo, proto_thrift, nested_count);
1931
0
    }
1932
1933
0
    if (is_field) {
1934
0
        thrift_opt->previous_field_id = field_id;
1935
0
    }
1936
0
    return result;
1937
0
}
1938
1939
int
1940
dissect_thrift_t_struct(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id, int ett_id, const thrift_member_t *seq)
1941
107
{
1942
107
    return dissect_thrift_t_struct_expert(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ett_id, seq, NULL);
1943
107
}
1944
1945
static int
1946
// NOLINTNEXTLINE(misc-no-recursion)
1947
dissect_thrift_t_struct_expert(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, bool is_field, int field_id, int hf_id, int ett_id, const thrift_member_t *seq, expert_field* ei)
1948
107
{
1949
107
    thrift_field_header_t field_header;
1950
107
    proto_tree *sub_tree = NULL;
1951
107
    proto_item *type_pi = NULL;
1952
1953
107
    bool enable_subtree = (ett_id != DISABLE_SUBTREE) || (hf_id != DISABLE_SUBTREE);
1954
107
    unsigned nested_count = p_get_proto_depth(pinfo, proto_thrift);
1955
1956
    /* Get the current state of dissection. */
1957
107
    DISSECTOR_ASSERT(thrift_opt);
1958
107
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
1959
1960
    /* Dissect field header if necessary. */
1961
107
    if (is_field) {
1962
0
        if (show_internal_thrift_fields) {
1963
0
            sub_tree = tree;
1964
0
        }
1965
0
        offset = dissect_thrift_t_field_header(tvb, pinfo, sub_tree, offset, thrift_opt, DE_THRIFT_T_STRUCT, field_id, NULL);
1966
0
    }
1967
107
    ABORT_SUBDISSECTION_ON_ISSUE(offset);
1968
107
    if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_TYPE_LEN) {
1969
0
        return THRIFT_REQUEST_REASSEMBLY;
1970
0
    }
1971
1972
    /* Create the sub-tree, if not explicitly refused. */
1973
107
    if (enable_subtree) {
1974
        /* Add the struct to the tree. */
1975
107
        if (nested_count >= thrift_opt->nested_type_depth) {
1976
0
            expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes);
1977
0
            return THRIFT_SUBDISSECTOR_ERROR;
1978
0
        }
1979
107
        p_set_proto_depth(pinfo, proto_thrift, nested_count + 1);
1980
107
        type_pi = proto_tree_add_item(tree, hf_id, tvb, offset, -1, ENC_BIG_ENDIAN);
1981
107
        sub_tree = proto_item_add_subtree(type_pi, ett_id);
1982
107
    } else {
1983
        /* Sub-dissector requested that we don't use a sub_tree.
1984
         * This is useful for ME_THRIFT_T_REPLY or unions where we always have only 1 sub-element. */
1985
0
        sub_tree = tree;
1986
0
    }
1987
1988
107
    thrift_opt->previous_field_id = 0;
1989
    /* Read and check available fields. */
1990
247
    while (seq->type != DE_THRIFT_T_STOP) {
1991
204
        int local_offset = offset;
1992
        /* Read the type and check for the end of the structure.
1993
         * Never create the field header sub-tree here as it will be handled by the field's own dissector.
1994
         * We only want to get the type & field id information to compare them against what we expect.
1995
         */
1996
204
        if (dissect_thrift_field_header(tvb, pinfo, NULL, &local_offset, thrift_opt, &field_header, false) == THRIFT_REQUEST_REASSEMBLY) {
1997
2
            if (local_offset == THRIFT_REQUEST_REASSEMBLY) {
1998
1
                return THRIFT_REQUEST_REASSEMBLY;
1999
1
            } else {
2000
1
                return THRIFT_SUBDISSECTOR_ERROR;
2001
1
            }
2002
2
        }
2003
        /* Right now, we only check for T_STOP type. The member sub-dissection will take care of checking the type. */
2004
202
        if (field_header.type.binary == DE_THRIFT_T_STOP) {
2005
59
            if (seq->optional) {
2006
29
                seq++;
2007
29
                continue;
2008
30
            } else {
2009
30
                proto_tree_add_expert(sub_tree, pinfo, &ei_thrift_struct_fid_not_in_seq, tvb, offset, TBP_THRIFT_TYPE_LEN);
2010
30
                return THRIFT_SUBDISSECTOR_ERROR;
2011
30
            }
2012
59
        }
2013
2014
        /* Read and check the field id which is the only way to make sure we are aligned. */
2015
143
        if (field_header.field_id != seq->fid) {
2016
            /* Wrong field in sequence or was it optional? */
2017
77
            if (seq->optional) {
2018
                /* Skip to next element*/
2019
66
                seq++;
2020
66
                continue;
2021
66
            } else {
2022
11
                proto_tree_add_expert(sub_tree, pinfo, &ei_thrift_struct_fid_not_in_seq, tvb, offset, TBP_THRIFT_TYPE_LEN);
2023
11
                return THRIFT_SUBDISSECTOR_ERROR;
2024
11
            }
2025
77
        }
2026
2027
66
        if (seq->type != DE_THRIFT_T_GENERIC) {
2028
            /* Type is not T_STOP and field id matches one we know how to dissect. */
2029
66
            offset = dissect_thrift_t_member(tvb, pinfo, sub_tree, offset, thrift_opt, true, seq);
2030
66
        } else {
2031
            /* The field is not defined in the struct, switch back to generic dissection.
2032
             * Re-read the header ensuring it is displayed (no need to check result,
2033
             * we already dissected it but without the header tree creation. */
2034
0
            dissect_thrift_field_header(tvb, pinfo, sub_tree, &offset, thrift_opt, &field_header, false);
2035
0
            expert_add_info(pinfo, field_header.fid_pi, &ei_thrift_undefined_field_id);
2036
            // Then dissect just this field.
2037
0
            if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) {
2038
0
                if (!is_thrift_compact_bool_type(field_header.type.compact) &&
2039
0
                    dissect_thrift_compact_type(tvb, pinfo, sub_tree, &offset, thrift_opt, field_header.fh_tree, field_header.type.compact, field_header.type_pi) == THRIFT_REQUEST_REASSEMBLY) {
2040
0
                    return THRIFT_REQUEST_REASSEMBLY;
2041
0
                }
2042
0
            } else {
2043
0
                if (dissect_thrift_binary_type(tvb, pinfo, sub_tree, &offset, thrift_opt, field_header.fh_tree, field_header.type.binary, field_header.type_pi) == THRIFT_REQUEST_REASSEMBLY) {
2044
0
                    return THRIFT_REQUEST_REASSEMBLY;
2045
0
                }
2046
0
            }
2047
0
        }
2048
66
        ABORT_SUBDISSECTION_ON_ISSUE(offset);
2049
45
        if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_TYPE_LEN) {
2050
0
            return THRIFT_REQUEST_REASSEMBLY;
2051
0
        }
2052
45
        seq++;
2053
2054
        /* Allow the proper handling of next delta field id in Compact protocol. */
2055
45
        thrift_opt->previous_field_id = field_header.field_id;
2056
45
    }
2057
2058
    /* The loop exits before dissecting the T_STOP. */
2059
43
    offset = dissect_thrift_t_stop(tvb, pinfo, sub_tree, offset);
2060
2061
    /* Set expert info if required. */
2062
43
    if (ei != NULL) {
2063
0
        expert_add_info(pinfo, type_pi, ei);
2064
0
    }
2065
2066
43
    if (enable_subtree && offset > 0) {
2067
40
        proto_item_set_end(type_pi, tvb, offset);
2068
40
    }
2069
2070
43
    if (is_field) {
2071
0
        thrift_opt->previous_field_id = field_id;
2072
0
    }
2073
43
    p_set_proto_depth(pinfo, proto_thrift, nested_count);
2074
43
    return offset;
2075
107
}
2076
/*=====END SUB-DISSECTION=====*/
2077
2078
/* GENERIC DISSECTION PARAMETERS DOCUMENTATION
2079
 *
2080
 * Generic functions for when there is no custom sub-dissector.
2081
 * Same conventions are used for binary and compact.
2082
 *
2083
 *  +--------------------+--------------------------+---------------------+
2084
 *  | offset   \  return | REQUEST_REASSEMBLY = -1  | Length              |
2085
 *  +--------------------+--------------------------+---------------------+
2086
 *  | REQUEST_REASSEMBLY | Reassembly required      | SHALL NEVER HAPPEN! |
2087
 *  +--------------------+--------------------------+---------------------+
2088
 *  | Length             | Error occurred at offset | Data fully parsed.  |
2089
 *  +--------------------+--------------------------+---------------------+
2090
 *
2091
 * @param[in] tvb:          Pointer to the tvbuff_t holding the captured data.
2092
 * @param[in] pinfo:        Pointer to the packet_info holding information about the currently dissected packet.
2093
 * @param[in] tree:         Pointer to the proto_tree used to hold the display tree in Wireshark's interface.
2094
 * @param[in,out] offset:   Pointer to the offset from the beginning of the tvbuff_t where the Thrift field is. Function will dissect only the data.
2095
 *                          The offset is modified according to table hereabove in sync with the return value.
2096
 * @param[in] thrift_opt:   Options from the Thrift dissector that will be useful for dissection of deep data (to expose the position of failure).
2097
 *
2098
 * //   Specific to dissect_thrift_binary_linear.
2099
 * @param[in] expected:     Expected container type (list, set, or map).
2100
 *
2101
 * //   Specific to dissect_thrift_compact_list_set.
2102
 * @param[in] is_list:      `true` if the expected container type is list.
2103
 *                          `false` if the expected container type is set.
2104
 *
2105
 * //   Specific to dissect_thrift_(binary|compact)_binary.
2106
 * //   Present in dissect_thrift_(binary|compact)_type for forwarding purpose.
2107
 * @param[in] header_tree:  The proto_tree in which the length must be inserted.
2108
 *                          If it is NULL, tree will be used instead.
2109
 *                          Used in structs to push the length in the field header.
2110
 *
2111
 * //   Specific to dissect_thrift_(binary|compact)_type.
2112
 * @param[in] type:         The type for which data needs to be dissected.
2113
 *                          Neither type nor field_id to dissect as there is no such "header" in list, set, and map.
2114
 * @param[in] type_pi:      The proto_item of the type field to indicate position of failure.
2115
 */
2116
2117
/*=====BEGIN BINARY GENERIC DISSECTION=====*/
2118
static int
2119
dissect_thrift_binary_binary(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt, proto_tree *header_tree)
2120
1
{
2121
    /*  Binary protocol binary/string data (4 bytes + data):
2122
     *      +--------+--------+--------+--------+--------+ ... +--------+
2123
     *      | Number of bytes                   | N bytes of data       |
2124
     *      +--------+--------+--------+--------+--------+ ... +--------+
2125
     *
2126
     *  Where:
2127
     *      Number of bytes is the number of encoded bytes of data.
2128
     *                      In particular, it might be larger than the number
2129
     *                      of characters in an UTF-8 string.
2130
     */
2131
1
    int32_t str_len;
2132
1
    proto_item *pi;
2133
1
    ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_BINARY_LEN);
2134
1
    if (header_tree == NULL) {
2135
0
        header_tree = tree;
2136
0
    }
2137
1
    pi = proto_tree_add_item_ret_int(header_tree, hf_thrift_str_len, tvb, *offset, TBP_THRIFT_LENGTH_LEN, ENC_BIG_ENDIAN, &str_len);
2138
1
    *offset += TBP_THRIFT_LENGTH_LEN;
2139
1
    if (header_tree != tree) {
2140
1
        proto_item_set_end(proto_tree_get_parent(header_tree), tvb, *offset);
2141
1
    }
2142
2143
1
    if (str_len < 0) {
2144
0
        expert_add_info(pinfo, pi, &ei_thrift_negative_length);
2145
0
        return THRIFT_REQUEST_REASSEMBLY;
2146
0
    }
2147
2148
1
    return dissect_thrift_string_as_preferred(tvb, pinfo, tree, offset, thrift_opt, str_len);
2149
1
}
2150
2151
static int
2152
// NOLINTNEXTLINE(misc-no-recursion)
2153
dissect_thrift_binary_linear(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt, thrift_type_enum_t expected)
2154
40
{
2155
    /*  Binary protocol list and set (5 bytes + elements):
2156
     *      +--------+--------+--------+--------+--------+---...---+ ... +---...---+
2157
     *      |0000tttt| number of elements                |element 1|     |element N|
2158
     *      +--------+--------+--------+--------+--------+---...---+ ... +---...---+
2159
     *
2160
     *  Binary protocol map (6 bytes + key-value pairs):
2161
     *      +--------+--------+--------+--------+--------+--------+--...--+---...---+ ... +--...--+---...---+
2162
     *      |0000kkkk|0000vvvv| number of key+value pairs         | key 1 | value 1 |     | key N | value N |
2163
     *      +--------+--------+--------+--------+--------+--------+--...--+---...---+ ... +--...--+---...---+
2164
     *
2165
     *  Where:
2166
     *      'tttt'      is the type of the list or set elements, an unsigned 4 bits strictly positive integer.
2167
     *      'kkkk'      is the type of the map keys, an unsigned 4 bits strictly positive integer.
2168
     *      'vvvv'      is the type of the map values, an unsigned 4 bits strictly positive integer.
2169
     */
2170
40
    proto_tree *sub_tree;
2171
40
    proto_item *container_pi, *len_pi, *vtype_pi;
2172
40
    proto_item *ktype_pi = NULL; // Avoid a false positive warning.
2173
40
    uint32_t ktype, vtype;
2174
40
    int32_t container_len, i;
2175
40
    int ett = 0;
2176
40
    int hf_container = 0;
2177
40
    int hf_num_item = 0;
2178
40
    int hf_vtype = hf_thrift_type;
2179
40
    int min_len = TBP_THRIFT_LINEAR_LEN;
2180
40
    unsigned nested_count = p_get_proto_depth(pinfo, proto_thrift);
2181
2182
    /* Set the different hf_id & ett depending on effective type. */
2183
40
    switch (expected) {
2184
30
        case DE_THRIFT_T_SET:
2185
30
            ett = ett_thrift_set;
2186
30
            hf_container = hf_thrift_set;
2187
30
            hf_num_item = hf_thrift_num_set_item;
2188
30
            break;
2189
9
        case DE_THRIFT_T_LIST:
2190
9
            ett = ett_thrift_list;
2191
9
            hf_container = hf_thrift_list;
2192
9
            hf_num_item = hf_thrift_num_list_item;
2193
9
            break;
2194
1
        case DE_THRIFT_T_MAP:
2195
1
            ett = ett_thrift_map;
2196
1
            hf_container = hf_thrift_map;
2197
1
            hf_num_item = hf_thrift_num_map_item;
2198
1
            hf_vtype = hf_thrift_value_type; /* Use specific hf_info as we have several types. */
2199
1
            min_len += TBP_THRIFT_TYPE_LEN; /* Additional type key + value instead of element only. */
2200
1
            break;
2201
0
        default:
2202
0
            REPORT_DISSECTOR_BUG("dissect_thrift_binary_linear called with something else than a container type.");
2203
0
            break;
2204
40
    }
2205
40
    ABORT_ON_INCOMPLETE_PDU(min_len);
2206
2207
    /* Create the sub-tree. */
2208
39
    if (nested_count >= thrift_opt->nested_type_depth) {
2209
0
        expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes);
2210
0
        return THRIFT_REQUEST_REASSEMBLY;
2211
0
    }
2212
39
    p_set_proto_depth(pinfo, proto_thrift, nested_count + 1);
2213
39
    container_pi = proto_tree_add_item(tree, hf_container, tvb, *offset, -1, ENC_NA);
2214
39
    sub_tree = proto_item_add_subtree(container_pi, ett);
2215
2216
    /* Read the type of the key in case of map. */
2217
39
    if (expected == DE_THRIFT_T_MAP) {
2218
1
        ktype_pi = proto_tree_add_item_ret_uint(sub_tree, hf_thrift_key_type, tvb, *offset, TBP_THRIFT_TYPE_LEN, ENC_BIG_ENDIAN, &ktype);
2219
1
        *offset += TBP_THRIFT_TYPE_LEN;
2220
1
    }
2221
    /* Read the type of the elements (or type of the values in case of map). */
2222
39
    vtype_pi = proto_tree_add_item_ret_uint(sub_tree, hf_vtype, tvb, *offset, TBP_THRIFT_TYPE_LEN, ENC_BIG_ENDIAN, &vtype);
2223
39
    *offset += TBP_THRIFT_TYPE_LEN;
2224
    /* Read and check the number of entries of the container. */
2225
39
    len_pi = proto_tree_add_item_ret_int(sub_tree, hf_num_item, tvb, *offset, TBP_THRIFT_LENGTH_LEN, ENC_BIG_ENDIAN, &container_len);
2226
39
    *offset += TBP_THRIFT_LENGTH_LEN;
2227
39
    if (container_len < 0) {
2228
1
        expert_add_info(pinfo, len_pi, &ei_thrift_negative_length);
2229
1
        return THRIFT_REQUEST_REASSEMBLY;
2230
1
    }
2231
2232
    /* Read the content of the container. */
2233
183
    for (i = 0; i < container_len; ++i) {
2234
164
        if (expected == DE_THRIFT_T_MAP) {
2235
1
            if (dissect_thrift_binary_type(tvb, pinfo, sub_tree, offset, thrift_opt, NULL, ktype, ktype_pi) == THRIFT_REQUEST_REASSEMBLY)
2236
1
                return THRIFT_REQUEST_REASSEMBLY;
2237
1
        }
2238
163
        if (dissect_thrift_binary_type(tvb, pinfo, sub_tree, offset, thrift_opt, NULL, vtype, vtype_pi) == THRIFT_REQUEST_REASSEMBLY)
2239
18
            return THRIFT_REQUEST_REASSEMBLY;
2240
163
    }
2241
19
    proto_item_set_end(container_pi, tvb, *offset);
2242
2243
19
    p_set_proto_depth(pinfo, proto_thrift, nested_count);
2244
19
    return *offset;
2245
38
}
2246
2247
static int
2248
// NOLINTNEXTLINE(misc-no-recursion)
2249
dissect_thrift_binary_list(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt)
2250
9
{
2251
9
    return dissect_thrift_binary_linear(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_LIST);
2252
9
}
2253
2254
static int
2255
// NOLINTNEXTLINE(misc-no-recursion)
2256
dissect_thrift_binary_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt)
2257
30
{
2258
30
    return dissect_thrift_binary_linear(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_SET);
2259
30
}
2260
2261
static int
2262
// NOLINTNEXTLINE(misc-no-recursion)
2263
dissect_thrift_binary_map(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt)
2264
1
{
2265
1
    return dissect_thrift_binary_linear(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_MAP);
2266
1
}
2267
2268
static int
2269
// NOLINTNEXTLINE(misc-no-recursion)
2270
dissect_thrift_binary_fields(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt)
2271
80
{
2272
    /*
2273
     *  Binary protocol field header (3 bytes) and field value:
2274
     *      +--------+--------+--------+--------+...+--------+
2275
     *      |0000tttt| field id        | field value         |
2276
     *      +--------+--------+--------+--------+...+--------+
2277
     *
2278
     *  Binary & Compact protocol stop field (1 byte):
2279
     *      +--------+
2280
     *      |00000000|
2281
     *      +--------+
2282
     *
2283
     *  Where:
2284
     *      'dddd'      is the field id delta, a strictly positive unsigned 4 bits integer.
2285
     *      'tttt'      is the type of the field value, an unsigned 4 bits strictly positive integer.
2286
     *      field id    is the numerical value of the field in the structure.
2287
     *      field value is the encoded value.
2288
     */
2289
2290
    /* This function does not create the subtree, it's the responsibility of the caller:
2291
     * - either dissect_thrift_common which creates the "Data" sub-tree,
2292
     * - or dissect_thrift_binary_struct which creates the "Struct" sub-tree.
2293
     */
2294
80
    thrift_field_header_t field_header = {
2295
80
        .type.binary = DE_THRIFT_T_STOP, // Overwritten by dissect_thrift_field_header() but undetected by Clang.
2296
80
    };
2297
2298
80
    thrift_opt->previous_field_id = 0;
2299
184
    while (true) {
2300
184
        if (dissect_thrift_field_header(tvb, pinfo, tree, offset, thrift_opt, &field_header, true) == THRIFT_REQUEST_REASSEMBLY) {
2301
3
            return THRIFT_REQUEST_REASSEMBLY;
2302
3
        }
2303
181
        if (field_header.type.binary == DE_THRIFT_T_STOP) {
2304
36
            break;
2305
36
        }
2306
145
        if (dissect_thrift_binary_type(tvb, pinfo, tree, offset, thrift_opt, field_header.fh_tree, field_header.type.binary, field_header.type_pi) == THRIFT_REQUEST_REASSEMBLY) {
2307
39
            return THRIFT_REQUEST_REASSEMBLY;
2308
39
        }
2309
145
    }
2310
2311
38
    return *offset;
2312
80
}
2313
2314
static int
2315
// NOLINTNEXTLINE(misc-no-recursion)
2316
dissect_thrift_binary_struct(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt)
2317
12
{
2318
    /* This function only creates the "Struct" sub-tree
2319
     * then it delegates the fields dissection to dissect_thrift_binary_fields.
2320
     */
2321
12
    proto_tree *sub_tree;
2322
12
    proto_item *pi;
2323
12
    unsigned nested_count = p_get_proto_depth(pinfo, proto_thrift);
2324
2325
12
    ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_STRUCT_LEN);
2326
12
    if (nested_count >= thrift_opt->nested_type_depth) {
2327
0
        expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes);
2328
0
        return THRIFT_REQUEST_REASSEMBLY;
2329
0
    }
2330
12
    p_set_proto_depth(pinfo, proto_thrift, nested_count + 1);
2331
12
    pi = proto_tree_add_item(tree, hf_thrift_struct, tvb, *offset, -1, ENC_NA);
2332
12
    sub_tree = proto_item_add_subtree(pi, ett_thrift_struct);
2333
2334
12
    if (dissect_thrift_binary_fields(tvb, pinfo, sub_tree, offset, thrift_opt) == THRIFT_REQUEST_REASSEMBLY) {
2335
7
        return THRIFT_REQUEST_REASSEMBLY;
2336
7
    } else {
2337
5
        proto_item_set_end(pi, tvb, *offset);
2338
5
    }
2339
5
    p_set_proto_depth(pinfo, proto_thrift, nested_count);
2340
5
    return *offset;
2341
12
}
2342
2343
static int
2344
// NOLINTNEXTLINE(misc-no-recursion)
2345
dissect_thrift_binary_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt, proto_tree *header_tree, int type, proto_item *type_pi)
2346
309
{
2347
309
    switch (type) {
2348
67
    case DE_THRIFT_T_BOOL:
2349
67
        ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_BOOL_LEN);
2350
64
        proto_tree_add_item(tree, hf_thrift_bool, tvb, *offset, TBP_THRIFT_BOOL_LEN, ENC_BIG_ENDIAN);
2351
64
        *offset += TBP_THRIFT_BOOL_LEN;
2352
64
        break;
2353
104
    case DE_THRIFT_T_I8:
2354
104
        ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_I8_LEN);
2355
103
        proto_tree_add_item(tree, hf_thrift_i8, tvb, *offset, TBP_THRIFT_I8_LEN, ENC_BIG_ENDIAN);
2356
103
        *offset += TBP_THRIFT_I8_LEN;
2357
103
        break;
2358
14
    case DE_THRIFT_T_I16:
2359
14
        ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_I16_LEN);
2360
14
        proto_tree_add_item(tree, hf_thrift_i16, tvb, *offset, TBP_THRIFT_I16_LEN, ENC_BIG_ENDIAN);
2361
14
        *offset += TBP_THRIFT_I16_LEN;
2362
14
        break;
2363
36
    case DE_THRIFT_T_I32:
2364
36
        ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_I32_LEN);
2365
34
        proto_tree_add_item(tree, hf_thrift_i32, tvb, *offset, TBP_THRIFT_I32_LEN, ENC_BIG_ENDIAN);
2366
34
        *offset += TBP_THRIFT_I32_LEN;
2367
34
        break;
2368
6
    case DE_THRIFT_T_I64:
2369
6
        ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_I64_LEN);
2370
6
        proto_tree_add_item(tree, hf_thrift_i64, tvb, *offset, TBP_THRIFT_I64_LEN, ENC_BIG_ENDIAN);
2371
6
        *offset += TBP_THRIFT_I64_LEN;
2372
6
        break;
2373
6
    case DE_THRIFT_T_DOUBLE:
2374
6
        ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_DOUBLE_LEN);
2375
5
        proto_tree_add_item(tree, hf_thrift_double, tvb, *offset, TBP_THRIFT_DOUBLE_LEN, ENC_BIG_ENDIAN);
2376
5
        *offset += TBP_THRIFT_DOUBLE_LEN;
2377
5
        break;
2378
1
    case DE_THRIFT_T_UUID:
2379
1
        ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_UUID_LEN);
2380
1
        proto_tree_add_item(tree, hf_thrift_uuid, tvb, *offset, TBP_THRIFT_UUID_LEN, ENC_BIG_ENDIAN);
2381
1
        *offset += TBP_THRIFT_UUID_LEN;
2382
1
        break;
2383
1
    case DE_THRIFT_T_BINARY:
2384
1
        if (dissect_thrift_binary_binary(tvb, pinfo, tree, offset, thrift_opt, header_tree) == THRIFT_REQUEST_REASSEMBLY) {
2385
1
            return THRIFT_REQUEST_REASSEMBLY;
2386
1
        }
2387
0
        break;
2388
9
    case DE_THRIFT_T_LIST:
2389
9
        if (dissect_thrift_binary_list(tvb, pinfo, tree, offset, thrift_opt) == THRIFT_REQUEST_REASSEMBLY) {
2390
5
            return THRIFT_REQUEST_REASSEMBLY;
2391
5
        }
2392
4
        break;
2393
30
    case DE_THRIFT_T_SET:
2394
30
        if (dissect_thrift_binary_set(tvb, pinfo, tree, offset, thrift_opt) == THRIFT_REQUEST_REASSEMBLY) {
2395
15
            return THRIFT_REQUEST_REASSEMBLY;
2396
15
        }
2397
15
        break;
2398
15
    case DE_THRIFT_T_MAP:
2399
1
        if (dissect_thrift_binary_map(tvb, pinfo, tree, offset, thrift_opt) == THRIFT_REQUEST_REASSEMBLY) {
2400
1
            return THRIFT_REQUEST_REASSEMBLY;
2401
1
        }
2402
0
        break;
2403
12
    case DE_THRIFT_T_STRUCT:
2404
12
        if (dissect_thrift_binary_struct(tvb, pinfo, tree, offset, thrift_opt) == THRIFT_REQUEST_REASSEMBLY) {
2405
7
            return THRIFT_REQUEST_REASSEMBLY;
2406
7
        }
2407
5
        break;
2408
22
    default:
2409
        /* Bail out */
2410
22
        expert_add_info(pinfo, type_pi, &ei_thrift_wrong_type);
2411
22
        return THRIFT_REQUEST_REASSEMBLY;
2412
309
    }
2413
2414
246
    return *offset;
2415
309
}
2416
/*=====END BINARY GENERIC DISSECTION=====*/
2417
2418
/*=====BEGIN COMPACT GENERIC DISSECTION=====*/
2419
/*
2420
 * Generic functions for when there is no custom sub-dissector.
2421
 *
2422
 * Use the same conventions (parameters & return values) as TBinaryProtocol.
2423
 *
2424
 * See "GENERIC DISSECTION PARAMETERS DOCUMENTATION" comment.
2425
 */
2426
2427
static int
2428
dissect_thrift_compact_binary(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt, proto_tree *header_tree)
2429
1.28k
{
2430
    /*  Compact protocol binary/string data (1 to 5 bytes + data):
2431
     *      +--------+...+--------+--------+...+--------+
2432
     *      | number of bytes     | N bytes of data     |
2433
     *      +--------+...+--------+--------+...+--------+
2434
     *
2435
     *  Where:
2436
     *      Number of bytes is the number of encoded bytes of data encoded as an unsigned varint.
2437
     *                      In particular, it might be larger than the number
2438
     *                      of characters in an UTF-8 string.
2439
     */
2440
1.28k
    int32_t str_len;
2441
1.28k
    proto_item *pi;
2442
1.28k
    uint64_t varint;
2443
2444
1.28k
    if (header_tree == NULL) {
2445
1.02k
        header_tree = tree;
2446
1.02k
    }
2447
1.28k
    int len_len = thrift_get_varint_enc(tvb, pinfo, header_tree, *offset, TCP_THRIFT_MAX_I32_LEN, &varint, ENC_VARINT_PROTOBUF);
2448
2449
1.28k
    switch (len_len) {
2450
7
    case THRIFT_REQUEST_REASSEMBLY:
2451
        /* Will always return after setting the expert parts. */
2452
7
        ABORT_ON_INCOMPLETE_PDU(TCP_THRIFT_MAX_I32_LEN);
2453
0
        return THRIFT_REQUEST_REASSEMBLY; // Just to avoid a false positive warning.
2454
9
    case 0:
2455
        /* In case of error, the offset stay at the error position. */
2456
9
        return THRIFT_REQUEST_REASSEMBLY;
2457
1.26k
    default:
2458
1.26k
        *offset += len_len;
2459
1.26k
        break;
2460
1.28k
    }
2461
1.26k
    if (header_tree != tree) {
2462
249
        proto_item_set_end(proto_tree_get_parent(header_tree), tvb, *offset);
2463
249
    }
2464
1.26k
    if (varint > (uint64_t)INT32_MAX) {
2465
2
        pi = proto_tree_add_uint64(header_tree, hf_thrift_u64, tvb, *offset, len_len, varint);
2466
2
        expert_add_info(pinfo, pi, &ei_thrift_varint_too_large);
2467
2
        return THRIFT_REQUEST_REASSEMBLY;
2468
2
    }
2469
1.25k
    str_len = (int32_t)varint;
2470
1.25k
    pi = proto_tree_add_int(header_tree, hf_thrift_str_len, tvb, *offset, len_len, str_len);
2471
1.25k
    if (str_len < 0) {
2472
0
        expert_add_info(pinfo, pi, &ei_thrift_negative_length);
2473
0
        return THRIFT_REQUEST_REASSEMBLY;
2474
0
    }
2475
2476
1.25k
    return dissect_thrift_string_as_preferred(tvb, pinfo, tree, offset, thrift_opt, str_len);
2477
1.25k
}
2478
2479
static int
2480
// NOLINTNEXTLINE(misc-no-recursion)
2481
dissect_thrift_compact_list_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt, bool is_list)
2482
3.50k
{
2483
    /*  Compact protocol list/set (short form, 1 byte):
2484
     *      +--------+--------+...+--------+
2485
     *      |nnnntttt| nnnn elements       |
2486
     *      +--------+--------+...+--------+
2487
     *
2488
     *  Compact protocol list/set (long form, 2+ bytes):
2489
     *      +--------+--------+...+--------+--------+...+--------+
2490
     *      |1111tttt| number of elements  | elements            |
2491
     *      +--------+--------+...+--------+--------+...+--------+
2492
     *
2493
     *  Where:
2494
     *      'nnnn'  is the number of elements if between 0 and 14 included encoded as a 4 bits unsigned integer.
2495
     *      'tttt'  is the type of the elements (using the same convention as TBinaryProtocol, unlike compact structures.
2496
     *      '1111'  indicates that the number of elements is encoded as an unsigned 32 bits varint (for number >= 15).
2497
     */
2498
3.50k
    proto_tree *sub_tree;
2499
3.50k
    proto_item *container_pi, *type_pi, *len_pi;
2500
3.50k
    uint32_t len_type, type;
2501
3.50k
    int32_t container_len, len_len, i;
2502
3.50k
    uint64_t varint;
2503
3.50k
    int lt_offset = *offset;
2504
3.50k
    int ett = ett_thrift_set;
2505
3.50k
    int hf_container = hf_thrift_set;
2506
3.50k
    int hf_num_item = hf_thrift_num_set_item;
2507
3.50k
    int hf_pos_item = hf_thrift_num_set_pos;
2508
3.50k
    unsigned nested_count = p_get_proto_depth(pinfo, proto_thrift);
2509
3.50k
    ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_TYPE_LEN);
2510
2511
    /* Set the different hf_id & ett depending on effective type. */
2512
3.48k
    if (is_list) {
2513
1.37k
        ett = ett_thrift_list;
2514
1.37k
        hf_container = hf_thrift_list;
2515
1.37k
        hf_num_item = hf_thrift_num_list_item;
2516
1.37k
        hf_pos_item = hf_thrift_num_list_pos;
2517
1.37k
    }
2518
2519
    /* Create the sub-tree. */
2520
3.48k
    if (nested_count >= thrift_opt->nested_type_depth) {
2521
4
        expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes);
2522
4
        return THRIFT_REQUEST_REASSEMBLY;
2523
4
    }
2524
3.48k
    p_set_proto_depth(pinfo, proto_thrift, nested_count + 1);
2525
3.48k
    container_pi = proto_tree_add_item(tree, hf_container, tvb, *offset, -1, ENC_NA);
2526
3.48k
    sub_tree = proto_item_add_subtree(container_pi, ett);
2527
2528
    /* Read the type of the elements (and length if lower than 15). */
2529
3.48k
    len_type = tvb_get_uint8(tvb, lt_offset);
2530
3.48k
    *offset += TBP_THRIFT_TYPE_LEN;
2531
3.48k
    type = len_type & TCP_THRIFT_NIBBLE_MASK;
2532
3.48k
    type_pi = proto_tree_add_bits_item(sub_tree, hf_thrift_type, tvb, (lt_offset << OCTETS_TO_BITS_SHIFT) + TCP_THRIFT_NIBBLE_SHIFT, TCP_THRIFT_NIBBLE_SHIFT, ENC_BIG_ENDIAN);
2533
3.48k
    container_len = (len_type >> TCP_THRIFT_NIBBLE_SHIFT) & TCP_THRIFT_NIBBLE_MASK;
2534
2535
    /* Read and check the number of entries of the container. */
2536
3.48k
    if (container_len == TCP_THRIFT_LENGTH_LARGER) {
2537
103
        proto_tree_add_bits_item(sub_tree, hf_thrift_large_container, tvb, (lt_offset << OCTETS_TO_BITS_SHIFT), TCP_THRIFT_NIBBLE_SHIFT, ENC_BIG_ENDIAN);
2538
        /* Length is greater than 14, read effective length as a varint. */
2539
103
        len_len = thrift_get_varint_enc(tvb, pinfo, sub_tree, *offset, TCP_THRIFT_MAX_I32_LEN, &varint, ENC_VARINT_PROTOBUF);
2540
103
        switch (len_len) {
2541
2
        case THRIFT_REQUEST_REASSEMBLY:
2542
            /* Will always return after setting the expert parts. */
2543
2
            ABORT_ON_INCOMPLETE_PDU(TCP_THRIFT_MAX_I32_LEN);
2544
0
            return THRIFT_REQUEST_REASSEMBLY; // Just to avoid a false positive warning.
2545
3
        case 0:
2546
            /* In case of error, the offset stay at the error position. */
2547
3
            return THRIFT_REQUEST_REASSEMBLY;
2548
98
        default:
2549
98
            if (varint > (uint64_t)INT32_MAX) {
2550
3
                len_pi = proto_tree_add_int64(sub_tree, hf_thrift_i64, tvb, *offset, len_len, varint);
2551
3
                expert_add_info(pinfo, len_pi, &ei_thrift_varint_too_large);
2552
3
                return THRIFT_REQUEST_REASSEMBLY;
2553
3
            }
2554
95
            container_len = (uint32_t)varint;
2555
95
            len_pi = proto_tree_add_int(sub_tree, hf_num_item, tvb, *offset, len_len, container_len);
2556
95
            *offset += len_len;
2557
95
            break;
2558
103
        }
2559
3.38k
    } else {
2560
3.38k
        len_pi = proto_tree_add_bits_item(sub_tree, hf_pos_item, tvb, (lt_offset << OCTETS_TO_BITS_SHIFT), TCP_THRIFT_NIBBLE_SHIFT, ENC_BIG_ENDIAN);
2561
3.38k
    }
2562
3.47k
    if (container_len < 0) {
2563
0
        expert_add_info(pinfo, len_pi, &ei_thrift_negative_length);
2564
0
        return THRIFT_REQUEST_REASSEMBLY;
2565
0
    }
2566
2567
    /* Read the content of the container. */
2568
10.3k
    for (i = 0; i < container_len; ++i) {
2569
7.41k
        if (dissect_thrift_compact_type(tvb, pinfo, sub_tree, offset, thrift_opt, NULL, type, type_pi) == THRIFT_REQUEST_REASSEMBLY) {
2570
588
            return THRIFT_REQUEST_REASSEMBLY;
2571
588
        }
2572
7.41k
    }
2573
2.88k
    proto_item_set_end(container_pi, tvb, *offset);
2574
2575
2.88k
    p_set_proto_depth(pinfo, proto_thrift, nested_count);
2576
2.88k
    return *offset;
2577
3.47k
}
2578
2579
static int
2580
// NOLINTNEXTLINE(misc-no-recursion)
2581
dissect_thrift_compact_list(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt)
2582
1.38k
{
2583
1.38k
    return dissect_thrift_compact_list_set(tvb, pinfo, tree, offset, thrift_opt, true);
2584
1.38k
}
2585
2586
static int
2587
// NOLINTNEXTLINE(misc-no-recursion)
2588
dissect_thrift_compact_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt)
2589
2.12k
{
2590
2.12k
    return dissect_thrift_compact_list_set(tvb, pinfo, tree, offset, thrift_opt, false);
2591
2.12k
}
2592
2593
static int
2594
// NOLINTNEXTLINE(misc-no-recursion)
2595
dissect_thrift_compact_map(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt)
2596
685
{
2597
    /* Compact protocol map header (1 byte, empty map):
2598
     *      +--------+
2599
     *      |00000000|
2600
     *      +--------+
2601
     *
2602
     *  Compact protocol map (4+ bytes, non empty map) and key-value pairs:
2603
     *      +--------+...+--------+--------+--...--+---...---+ ... +--...--+---...---+
2604
     *      | number of elements  |kkkkvvvv| key 1 | value 1 |     | key N | value N |
2605
     *      +--------+...+--------+--------+--...--+---...---+ ... +--...--+---...---+
2606
     *
2607
     *  Where:
2608
     *      nb of elts  is the number of key + value pairs, encoded as an unsigned 32 bits varint.
2609
     *                  If this varint is null (map is empty), the types are not encoded at all.
2610
     *      'kkkk'      is the type of the map keys, an unsigned 4 bits strictly positive integer.
2611
     *      'vvvv'      is the type of the map values, an unsigned 4 bits strictly positive integer.
2612
     */
2613
2614
685
    proto_tree *sub_tree;
2615
685
    proto_item *container_pi, *len_pi, *ktype_pi, *vtype_pi;
2616
685
    uint32_t types, ktype, vtype;
2617
685
    int32_t container_len, len_len, i;
2618
685
    uint64_t varint;
2619
685
    unsigned nested_count = p_get_proto_depth(pinfo, proto_thrift);
2620
2621
685
    ABORT_ON_INCOMPLETE_PDU(TCP_THRIFT_MIN_VARINT_LEN);
2622
    /* Create the sub-tree. */
2623
681
    if (nested_count >= thrift_opt->nested_type_depth) {
2624
2
        expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes);
2625
2
        return THRIFT_REQUEST_REASSEMBLY;
2626
2
    }
2627
679
    p_set_proto_depth(pinfo, proto_thrift, nested_count + 1);
2628
679
    container_pi = proto_tree_add_item(tree, hf_thrift_map, tvb, *offset, -1, ENC_NA);
2629
679
    sub_tree = proto_item_add_subtree(container_pi, ett_thrift_map);
2630
2631
    /* Read and check number of key-value pair in the map. */
2632
679
    len_len = thrift_get_varint_enc(tvb, pinfo, sub_tree, *offset, TCP_THRIFT_MAX_I32_LEN, &varint, ENC_VARINT_PROTOBUF);
2633
679
    switch (len_len) {
2634
4
    case THRIFT_REQUEST_REASSEMBLY:
2635
        /* Will always return after setting the expert parts. */
2636
4
        ABORT_ON_INCOMPLETE_PDU(TCP_THRIFT_MAX_I32_LEN);
2637
0
        return THRIFT_REQUEST_REASSEMBLY; // Just to avoid a false positive warning.
2638
5
    case 0:
2639
        /* In case of error, the offset stay at the error position. */
2640
5
        return THRIFT_REQUEST_REASSEMBLY;
2641
670
    default:
2642
670
        if (varint > (uint64_t)INT32_MAX) {
2643
2
            len_pi = proto_tree_add_int64(sub_tree, hf_thrift_i64, tvb, *offset, len_len, varint);
2644
2
            expert_add_info(pinfo, len_pi, &ei_thrift_varint_too_large);
2645
2
            return THRIFT_REQUEST_REASSEMBLY;
2646
2
        }
2647
668
        container_len = (uint32_t)varint;
2648
668
        len_pi = proto_tree_add_int(sub_tree, hf_thrift_num_map_item, tvb, *offset, len_len, container_len);
2649
668
        *offset += len_len;
2650
668
        break;
2651
679
    }
2652
668
    if (container_len < 0) {
2653
0
        expert_add_info(pinfo, len_pi, &ei_thrift_negative_length);
2654
0
        return THRIFT_REQUEST_REASSEMBLY;
2655
0
    }
2656
2657
    /* Do not try to read the key & value types of an empty map. */
2658
668
    if (container_len > 0) {
2659
        /* If the map is not empty, read the types of keys and values. */
2660
395
        types = tvb_get_uint8(tvb, *offset);
2661
395
        ktype = (types >> TCP_THRIFT_NIBBLE_SHIFT) & TCP_THRIFT_NIBBLE_MASK;
2662
395
        ktype_pi = proto_tree_add_bits_item(sub_tree, hf_thrift_key_type, tvb, *offset << OCTETS_TO_BITS_SHIFT, TCP_THRIFT_NIBBLE_SHIFT, ENC_BIG_ENDIAN);
2663
395
        vtype = types & TCP_THRIFT_NIBBLE_MASK;
2664
395
        vtype_pi = proto_tree_add_bits_item(sub_tree, hf_thrift_value_type, tvb, (*offset << OCTETS_TO_BITS_SHIFT) + TCP_THRIFT_NIBBLE_SHIFT, TCP_THRIFT_NIBBLE_SHIFT, ENC_BIG_ENDIAN);
2665
395
        *offset += TCP_THRIFT_MAP_TYPES_LEN;
2666
2667
        /* Read the content of the container. */
2668
4.50k
        for (i = 0; i < container_len; ++i) {
2669
4.46k
            if (dissect_thrift_compact_type(tvb, pinfo, sub_tree, offset, thrift_opt, NULL, ktype, ktype_pi) == THRIFT_REQUEST_REASSEMBLY) {
2670
94
                return THRIFT_REQUEST_REASSEMBLY;
2671
94
            }
2672
4.36k
            if (dissect_thrift_compact_type(tvb, pinfo, sub_tree, offset, thrift_opt, NULL, vtype, vtype_pi) == THRIFT_REQUEST_REASSEMBLY) {
2673
256
                return THRIFT_REQUEST_REASSEMBLY;
2674
256
            }
2675
4.36k
        }
2676
395
    }
2677
318
    proto_item_set_end(container_pi, tvb, *offset);
2678
2679
318
    p_set_proto_depth(pinfo, proto_thrift, nested_count);
2680
318
    return *offset;
2681
668
}
2682
2683
static int
2684
// NOLINTNEXTLINE(misc-no-recursion)
2685
dissect_thrift_compact_fields(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt)
2686
2.90k
{
2687
    /*
2688
     *  Compact protocol field header (1 byte, short form) and field value:
2689
     *      +--------+--------+...+--------+
2690
     *      |ddddtttt| field value         |
2691
     *      +--------+--------+...+--------+
2692
     *
2693
     *  Compact protocol field header (2 to 4 bytes, long form) and field value:
2694
     *      +--------+--------+...+--------+--------+...+--------+
2695
     *      |0000tttt| field id            | field value         |
2696
     *      +--------+--------+...+--------+--------+...+--------+
2697
     *
2698
     *  Compact protocol stop field (1 byte):
2699
     *      +--------+
2700
     *      |00000000|
2701
     *      +--------+
2702
     *
2703
     * Where:
2704
     *
2705
     *      'dddd'      is the field id delta, a strictly positive unsigned 4 bits integer.
2706
     *      'tttt'      is the type of the field value, a strictly positive unsigned 4 bits integer.
2707
     *      field id    is the numerical value of the field in the structure.
2708
     *      field value is the encoded value.
2709
     */
2710
2711
    /* This function does not create the subtree, it's the responsibility of the caller:
2712
     * - either dissect_thrift_common which creates the "Data" sub-tree,
2713
     * - or dissect_thrift_compact_struct which creates the "Struct" sub-tree.
2714
     */
2715
2.90k
    thrift_field_header_t field_header = {
2716
2.90k
        .type.compact = DE_THRIFT_C_STOP, // Overwritten by dissect_thrift_field_header() but undetected by Clang.
2717
2.90k
    };
2718
2719
2.90k
    thrift_opt->previous_field_id = 0;
2720
9.21k
    while (true) {
2721
9.21k
        if (dissect_thrift_field_header(tvb, pinfo, tree, offset, thrift_opt, &field_header, true) == THRIFT_REQUEST_REASSEMBLY) {
2722
54
            return THRIFT_REQUEST_REASSEMBLY;
2723
54
        }
2724
9.15k
        if (field_header.type.compact == DE_THRIFT_C_STOP) {
2725
1.99k
            break; /* Need to break out of the loop, cannot do that in the switch. */
2726
1.99k
        }
2727
7.16k
        if (!is_thrift_compact_bool_type(field_header.type.compact) &&
2728
3.65k
            dissect_thrift_compact_type(tvb, pinfo, tree, offset, thrift_opt, field_header.fh_tree, field_header.type.compact, field_header.type_pi) == THRIFT_REQUEST_REASSEMBLY) {
2729
752
            return THRIFT_REQUEST_REASSEMBLY;
2730
752
        }
2731
6.41k
        thrift_opt->previous_field_id = field_header.field_id;
2732
6.41k
    }
2733
2734
2.09k
    return *offset;
2735
2.90k
}
2736
2737
static int
2738
// NOLINTNEXTLINE(misc-no-recursion)
2739
dissect_thrift_compact_struct(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt)
2740
1.22k
{
2741
    /* This function only creates the "Struct" sub-tree
2742
     * then it delegates the fields dissection to dissect_thrift_compact_fields.
2743
     */
2744
1.22k
    proto_tree *sub_tree;
2745
1.22k
    proto_item *pi;
2746
1.22k
    unsigned nested_count = p_get_proto_depth(pinfo, proto_thrift);
2747
2748
1.22k
    ABORT_ON_INCOMPLETE_PDU(TCP_THRIFT_STRUCT_LEN);
2749
1.22k
    if (nested_count >= thrift_opt->nested_type_depth) {
2750
1
        expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes);
2751
1
        return THRIFT_REQUEST_REASSEMBLY;
2752
1
    }
2753
1.21k
    p_set_proto_depth(pinfo, proto_thrift, nested_count + 1);
2754
1.21k
    pi = proto_tree_add_item(tree, hf_thrift_struct, tvb, *offset, -1, ENC_NA);
2755
1.21k
    sub_tree = proto_item_add_subtree(pi, ett_thrift_struct);
2756
2757
1.21k
    if (dissect_thrift_compact_fields(tvb, pinfo, sub_tree, offset, thrift_opt) == THRIFT_REQUEST_REASSEMBLY) {
2758
368
        return THRIFT_REQUEST_REASSEMBLY;
2759
851
    } else {
2760
851
        proto_item_set_end(pi, tvb, *offset);
2761
851
    }
2762
851
    p_set_proto_depth(pinfo, proto_thrift, nested_count);
2763
851
    return *offset;
2764
1.21k
}
2765
2766
/* Dissect a compact thrift field of a given type.
2767
 *
2768
 * This function is used only for linear containers (list, set, map).
2769
 * It uses the same type identifiers as TCompactProtocol, except for
2770
 * the bool type which is encoded either as BOOL_TRUE (1) or BOOL_FALSE (2)
2771
 * depending on the implementation (due to a wide-spread bug that became a
2772
 * de-facto standard in large parts of the library).
2773
 */
2774
static int
2775
// NOLINTNEXTLINE(misc-no-recursion)
2776
dissect_thrift_compact_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt, proto_tree *header_tree, int type, proto_item *type_pi)
2777
19.8k
{
2778
19.8k
    switch (type) {
2779
1.81k
    case DE_THRIFT_C_BOOL_TRUE:
2780
4.59k
    case DE_THRIFT_C_BOOL_FALSE:
2781
4.59k
        ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_BOOL_LEN);
2782
4.57k
        proto_tree_add_item(tree, hf_thrift_bool, tvb, *offset, TBP_THRIFT_BOOL_LEN, ENC_BIG_ENDIAN);
2783
4.57k
        *offset += TBP_THRIFT_BOOL_LEN;
2784
4.57k
        break;
2785
2.09k
    case DE_THRIFT_C_I8:
2786
2.09k
        ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_I8_LEN);
2787
2.08k
        proto_tree_add_item(tree, hf_thrift_i8, tvb, *offset, TBP_THRIFT_I8_LEN, ENC_BIG_ENDIAN);
2788
2.08k
        *offset += TBP_THRIFT_I8_LEN;
2789
2.08k
        break;
2790
1.42k
    case DE_THRIFT_C_I16:
2791
1.42k
        if (dissect_thrift_varint(tvb, pinfo, tree, offset, thrift_opt, TCP_THRIFT_MAX_I16_LEN, hf_thrift_i16, NULL) == THRIFT_REQUEST_REASSEMBLY) {
2792
29
            return THRIFT_REQUEST_REASSEMBLY;
2793
29
        }
2794
1.39k
        break;
2795
2.11k
    case DE_THRIFT_C_I32:
2796
2.11k
        if (dissect_thrift_varint(tvb, pinfo, tree, offset, thrift_opt, TCP_THRIFT_MAX_I32_LEN, hf_thrift_i32, NULL) == THRIFT_REQUEST_REASSEMBLY) {
2797
26
            return THRIFT_REQUEST_REASSEMBLY;
2798
26
        }
2799
2.08k
        break;
2800
2.21k
    case DE_THRIFT_C_I64:
2801
2.21k
        if (dissect_thrift_varint(tvb, pinfo, tree, offset, thrift_opt, TCP_THRIFT_MAX_I64_LEN, hf_thrift_i64, NULL) == THRIFT_REQUEST_REASSEMBLY) {
2802
15
            return THRIFT_REQUEST_REASSEMBLY;
2803
15
        }
2804
2.19k
        break;
2805
2.19k
    case DE_THRIFT_C_DOUBLE:
2806
438
        ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_DOUBLE_LEN);
2807
        /* https://github.com/apache/thrift/blob/master/doc/specs/thrift-compact-protocol.md
2808
         * In section double encoding:
2809
         * "But while the binary protocol encodes the int64 in 8 bytes in big endian order,
2810
         * the compact protocol encodes it in little endian order - this is due to an early
2811
         * implementation bug that finally became the de-facto standard."
2812
         */
2813
420
        proto_tree_add_item(tree, hf_thrift_double, tvb, *offset, TBP_THRIFT_DOUBLE_LEN, ENC_LITTLE_ENDIAN);
2814
420
        *offset += TBP_THRIFT_DOUBLE_LEN;
2815
420
        break;
2816
212
    case DE_THRIFT_C_UUID:
2817
212
        ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_UUID_LEN);
2818
189
        proto_tree_add_item(tree, hf_thrift_uuid, tvb, *offset, TBP_THRIFT_UUID_LEN, ENC_BIG_ENDIAN);
2819
189
        *offset += TBP_THRIFT_UUID_LEN;
2820
189
        break;
2821
1.28k
    case DE_THRIFT_C_BINARY:
2822
1.28k
        if (dissect_thrift_compact_binary(tvb, pinfo, tree, offset, thrift_opt, header_tree) == THRIFT_REQUEST_REASSEMBLY) {
2823
72
            return THRIFT_REQUEST_REASSEMBLY;
2824
72
        }
2825
1.20k
        break;
2826
1.38k
    case DE_THRIFT_C_LIST:
2827
1.38k
        if (dissect_thrift_compact_list(tvb, pinfo, tree, offset, thrift_opt) == THRIFT_REQUEST_REASSEMBLY) {
2828
349
            return THRIFT_REQUEST_REASSEMBLY;
2829
349
        }
2830
1.03k
        break;
2831
2.12k
    case DE_THRIFT_C_SET:
2832
2.12k
        if (dissect_thrift_compact_set(tvb, pinfo, tree, offset, thrift_opt) == THRIFT_REQUEST_REASSEMBLY) {
2833
264
            return THRIFT_REQUEST_REASSEMBLY;
2834
264
        }
2835
1.85k
        break;
2836
1.85k
    case DE_THRIFT_C_MAP:
2837
685
        if (dissect_thrift_compact_map(tvb, pinfo, tree, offset, thrift_opt) == THRIFT_REQUEST_REASSEMBLY) {
2838
367
            return THRIFT_REQUEST_REASSEMBLY;
2839
367
        }
2840
318
        break;
2841
1.22k
    case DE_THRIFT_C_STRUCT:
2842
1.22k
        if (dissect_thrift_compact_struct(tvb, pinfo, tree, offset, thrift_opt) == THRIFT_REQUEST_REASSEMBLY) {
2843
374
            return THRIFT_REQUEST_REASSEMBLY;
2844
374
        }
2845
851
        break;
2846
851
    default:
2847
        /* Bail out */
2848
125
        expert_add_info(pinfo, type_pi, &ei_thrift_wrong_type);
2849
125
        return THRIFT_REQUEST_REASSEMBLY;
2850
19.8k
    }
2851
2852
18.0k
    return *offset;
2853
19.8k
}
2854
/*=====END COMPACT GENERIC DISSECTION=====*/
2855
2856
/*
2857
 * End of generic functions
2858
 */
2859
2860
/*
2861
Binary protocol Message, strict encoding, 13+ bytes:
2862
   +--------+--------+--------+--------++--------+--------+--------+--------++--------+...+--------++--------+--------+--------+--------++...++--------+
2863
   |1vvvvvvv|vvvvvvvv|unused  |00000mmm|| name length                       || name                || seq id                            ||   || T_STOP |
2864
   +--------+--------+--------+--------++--------+--------+--------+--------++--------+...+--------++--------+--------+--------+--------++...++--------+
2865
2866
   Where:
2867
2868
   * 'vvvvvvvvvvvvvvv' is the version, an unsigned 15 bit number fixed to '1' (in binary: '000 0000 0000 0001'). The leading bit is 1.
2869
   * Although for consistency with Compact protocol, we will use |pppppppp|000vvvvv| instead in the display:
2870
   *       'pppppppp' = 0x80 for the protocol id and
2871
   *       '000' 3 zeroed bits as mandated by the specs.
2872
   *       'vvvvv' 5 bits for the version (see below).
2873
   * 'unused' is an ignored byte.
2874
   * 'mmm' is the message type, an unsigned 3 bit integer.
2875
   *       The 5 leading bits must be '0' as some clients take the whole byte.
2876
   *       (checked for java in 0.9.1)
2877
   * 'name length' is the byte length of the name field, a signed 32 bit integer encoded in network (big endian) order (must be >= 0).
2878
   * 'name' is the method name, an UTF-8 encoded string.
2879
   * 'seq id' is the sequence id, a signed 32 bit integer encoded in network (big endian) order.
2880
2881
Binary protocol Message, old encoding, 9+ bytes:
2882
   +--------+--------+--------+--------++--------+...+--------++--------++--------+--------+--------+--------++...++--------+
2883
   | name length                       || name                ||00000mmm|| seq id                            ||   || T_STOP |
2884
   +--------+--------+--------+--------++--------+...+--------++--------++--------+--------+--------+--------++...++--------+
2885
2886
   Where name length, name, mmm, seq id are the same as above.
2887
2888
   Because name length must be positive (therefore the first bit is always 0),
2889
   the first bit allows the receiver to see whether the strict format or the old format is used.
2890
2891
Note: Double separators indicate how the Thrift parts are sent on the wire depending on the network settings.
2892
      There are clients and server in production that do not squeeze as much data as possible in a packet
2893
      but push each Thrift write<Type>() call directly to the wire, making it harder to detect
2894
      as we only have 4 bytes in the first packet.
2895
2896
Compact protocol Message (5+ bytes):
2897
   +--------+--------+--------+...+--------+--------+...+--------+--------+...+--------+...+--------+
2898
   |pppppppp|mmmvvvvv| seq id              | name length         | name                |   | T_STOP |
2899
   +--------+--------+--------+...+--------+--------+...+--------+--------+...+--------+...+--------+
2900
2901
   Where:
2902
2903
   * 'pppppppp' is the protocol id, fixed to '1000 0010', 0x82.
2904
   * 'mmm' is the message type, an unsigned 3 bit integer.
2905
   * 'vvvvv' is the version, an unsigned 5 bit integer, fixed to '00001'.
2906
   * 'seq id' is the sequence id, a signed 32 bit integer encoded as a varint.
2907
   * 'name length' is the byte length of the name field, a signed 32 bit integer encoded as a varint (must be >= 0).
2908
   * 'name' is the method name to invoke, an UTF-8 encoded string.
2909
2910
Note: The content of the message is everything after the header until and including the T_STOP last byte,
2911
      In both protocols, the content is arranged exactly as the content of a struct.
2912
2913
Framed Transport can encapsulate any protocol version:
2914
   +--------+--------+--------+--------+--------+...+--------+--------+
2915
   | message length                    | Any protocol message, T_STOP |
2916
   +--------+--------+--------+--------+--------+...+--------+--------+
2917
                                       |<------ message length ------>|
2918
2919
   Message types are encoded with the following values:
2920
2921
   * _Call_: 1
2922
   * _Reply_: 2
2923
   * _Exception_: 3
2924
   * _Oneway_: 4
2925
 */
2926
2927
/*=====BEGIN HEADER GENERIC DISSECTION=====*/
2928
/* Dissect a unique Thrift TBinaryProtocol PDU and return the effective length of this PDU.
2929
 *
2930
 * This method is called only if the preliminary verifications have been done so it will use as
2931
 * much data as possible and will return THRIFT_REQUEST_REASSEMBLY and ask for reassembly if there is
2932
 * not enough data.
2933
 *
2934
 * In case of TFramedTransport, tcp_dissect_pdus made sure that we had all necessary data so reassembly
2935
 * will fail if the effective data is bigger than the frame which is a real error.
2936
 *
2937
 * Returns:
2938
 * - THRIFT_REQUEST_REASSEMBLY = -1 if reassembly is required
2939
 * -                              0 if an error occurred
2940
 * -                     offset > 0 to indicate the end of the PDU in case of success
2941
 *
2942
 * This method MUST be called with non-null thrift_opt.
2943
 */
2944
static int
2945
dissect_thrift_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt)
2946
2.09k
{
2947
2.09k
    proto_tree *thrift_tree, *sub_tree;
2948
2.09k
    proto_item *thrift_pi, *data_pi;
2949
2.09k
    proto_item *mtype_pi = NULL;
2950
2.09k
    proto_item *fid_pi = NULL;
2951
2.09k
    int start_offset = offset;
2952
2.09k
    int header_offset = 0, data_offset = 0;
2953
2.09k
    int32_t seqid_len = TCP_THRIFT_MAX_I32_LEN;
2954
2.09k
    int32_t str_len_len = TCP_THRIFT_MAX_I32_LEN;
2955
2.09k
    uint8_t mtype;
2956
2.09k
    uint16_t version;
2957
2.09k
    int32_t str_len, seq_id;
2958
2.09k
    int64_t varint;
2959
2.09k
    const char *method_str;
2960
2.09k
    unsigned remaining;
2961
2.09k
    tvbuff_t *msg_tvb;
2962
2.09k
    int len, tframe_length = 0;
2963
2.09k
    bool is_framed, is_compact, request_reasm;
2964
2965
    /* Get the current state of dissection. */
2966
2.09k
    DISSECTOR_ASSERT(thrift_opt);
2967
2.09k
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
2968
2969
2.09k
    is_framed = (thrift_opt->tprotocol & PROTO_THRIFT_FRAMED) != 0;
2970
2.09k
    is_compact = (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) != 0;
2971
    /* Create the item now in case of malformed buffer to use with expert_add_info() */
2972
2.09k
    thrift_pi = proto_tree_add_item(tree, proto_thrift, tvb, offset, -1, ENC_NA);
2973
2.09k
    thrift_tree = proto_item_add_subtree(thrift_pi, ett_thrift);
2974
2.09k
    data_pi = thrift_pi; /* Used for expert_add_info in case of reassembly before the sub-tree is created. */
2975
2976
2.09k
    if (is_framed) {
2977
        /* Thrift documentation indicates a maximum of 16 MB frames by default.
2978
         * Configurable since Thrift 0.14.0 so better stay on the safe side.
2979
         * We are more tolerant with 2 GiB. */
2980
        /* TODO: Add a dissector parameter using the same default as Thrift?
2981
         *       If we do, check the length in test_thrift_strict as well. */
2982
347
        tframe_length = tvb_get_ntohil(tvb, offset);
2983
347
        if (tframe_length <= 0) {
2984
137
            thrift_tree = proto_item_add_subtree(thrift_pi, ett_thrift_error);
2985
137
            data_pi = proto_tree_add_item(thrift_tree, hf_thrift_frame_length, tvb, offset, TBP_THRIFT_LENGTH_LEN, ENC_BIG_ENDIAN);
2986
137
            expert_add_info(pinfo, data_pi, &ei_thrift_negative_length);
2987
137
            return 0;
2988
137
        }
2989
210
        proto_item_set_len(thrift_pi, TBP_THRIFT_LENGTH_LEN + tframe_length);
2990
        /* Keep the same start point to avoid awkward offset calculations */
2991
210
        offset += TBP_THRIFT_LENGTH_LEN;
2992
210
    }
2993
2994
1.96k
    header_offset = offset;
2995
1.96k
    remaining = tvb_reported_length_remaining(tvb, offset);
2996
    /* We should be called only when the entire frame is ready
2997
     * so we don't need to verify if we have enough data.
2998
     * If not framed, anything remaining is obviously greater than 0. */
2999
1.96k
    DISSECTOR_ASSERT(remaining >= (unsigned)tframe_length);
3000
3001
    /****************************************************************/
3002
    /* Decode the header depending on compact, strict (new) or old. */
3003
    /****************************************************************/
3004
1.96k
    if (is_compact) {
3005
1.85k
        if (remaining < TCP_THRIFT_MIN_MESSAGE_LEN) {
3006
9
            goto add_expert_and_reassemble;
3007
9
        }
3008
        /* Compact: proto_id|mtype+version|seqid|length|name */
3009
1.84k
        version = tvb_get_ntohs(tvb, offset) & THRIFT_COMPACT_VERSION_VALUE_MASK;
3010
1.84k
        mtype = (tvb_get_ntohs(tvb, offset) & THRIFT_COMPACT_MESSAGE_MASK) >> THRIFT_COMPACT_MESSAGE_SHIFT;
3011
1.84k
        offset += TCP_THRIFT_VERSION_LEN;
3012
        /* Pass sequence id */
3013
1.84k
        seqid_len = thrift_get_varint_enc(tvb, pinfo, tree, offset, TCP_THRIFT_MAX_I32_LEN, (uint64_t*)&varint, ENC_VARINT_ZIGZAG);
3014
        /* We use the same reassembly/error convention. */
3015
1.84k
        if (seqid_len <= 0) {
3016
12
            return seqid_len;
3017
12
        }
3018
1.83k
        offset += seqid_len;
3019
1.83k
        if (varint > (int64_t)INT32_MAX || varint < (int64_t)INT32_MIN) {
3020
25
            expert_add_info(pinfo, thrift_pi, &ei_thrift_varint_too_large);
3021
            /* Sequence id is only informative, we may be just fine. */
3022
25
        }
3023
1.83k
        seq_id = (int32_t)varint;
3024
        /* Read length of method name */
3025
1.83k
        str_len_len = thrift_get_varint_enc(tvb, pinfo, tree, offset, TCP_THRIFT_MAX_I32_LEN, (uint64_t*)&varint, ENC_VARINT_PROTOBUF);
3026
1.83k
        if (str_len_len <= 0) {
3027
12
            return str_len_len;
3028
12
        }
3029
1.81k
        if (varint > (int64_t)INT32_MAX) {
3030
2
            expert_add_info(pinfo, thrift_pi, &ei_thrift_varint_too_large);
3031
2
            return 0;
3032
2
        }
3033
1.81k
        str_len = (int32_t)varint;
3034
1.81k
        if (str_len < 0) {
3035
0
            expert_add_info(pinfo, thrift_pi, &ei_thrift_negative_length);
3036
0
            return 0;
3037
0
        }
3038
1.81k
        offset += str_len_len;
3039
        /* Set method name */
3040
1.81k
        if (tvb_reported_length_remaining(tvb, offset) < str_len) {
3041
38
            goto add_expert_and_reassemble;
3042
38
        }
3043
1.77k
        method_str = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset, str_len, ENC_UTF_8);
3044
1.77k
        offset += str_len;
3045
1.77k
    } else if (thrift_opt->tprotocol & PROTO_THRIFT_STRICT) {
3046
86
        if (remaining < TBP_THRIFT_STRICT_MIN_MESSAGE_LEN) {
3047
2
            goto add_expert_and_reassemble;
3048
2
        }
3049
84
        version = tvb_get_ntohs(tvb, offset) & THRIFT_BINARY_VERSION_VALUE_MASK;
3050
84
        mtype = tvb_get_uint8(tvb, offset + TBP_THRIFT_MTYPE_OFFSET) & THRIFT_BINARY_MESSAGE_MASK;
3051
84
        str_len = tvb_get_ntohil(tvb, offset + TBP_THRIFT_VERSION_LEN);
3052
84
        if (str_len < 0) {
3053
2
            expert_add_info(pinfo, thrift_pi, &ei_thrift_negative_length);
3054
2
            return 0;
3055
2
        }
3056
82
        if (remaining < TBP_THRIFT_STRICT_MIN_MESSAGE_LEN + str_len) {
3057
5
            goto add_expert_and_reassemble;
3058
5
        }
3059
77
        offset += TBP_THRIFT_VERSION_LEN + TBP_THRIFT_LENGTH_LEN;
3060
77
        method_str = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset, str_len, ENC_UTF_8);
3061
77
        offset += str_len;
3062
3063
77
        seq_id = tvb_get_ntohil(tvb, offset);
3064
77
        offset += TBP_THRIFT_SEQ_ID_LEN;
3065
77
    } else {
3066
25
        if (remaining < TBP_THRIFT_MIN_MESSAGE_LEN) {
3067
2
            goto add_expert_and_reassemble;
3068
2
        }
3069
23
        version = 0;
3070
23
        str_len = tvb_get_ntohil(tvb, offset);
3071
23
        if (str_len < 0) {
3072
0
            expert_add_info(pinfo, thrift_pi, &ei_thrift_negative_length);
3073
0
            return 0;
3074
0
        }
3075
23
        if (remaining < TBP_THRIFT_MIN_MESSAGE_LEN + str_len) {
3076
3
            goto add_expert_and_reassemble;
3077
3
        }
3078
20
        offset += TBP_THRIFT_LENGTH_LEN;
3079
20
        method_str = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset, str_len, ENC_UTF_8);
3080
20
        offset += str_len;
3081
20
        mtype = tvb_get_uint8(tvb, offset + TBP_THRIFT_LENGTH_LEN + str_len) & THRIFT_BINARY_MESSAGE_MASK;
3082
20
        offset += TBP_THRIFT_TYPE_LEN;
3083
3084
20
        seq_id = tvb_get_ntohil(tvb, offset);
3085
20
        offset += TBP_THRIFT_SEQ_ID_LEN;
3086
20
    }
3087
3088
1.87k
    data_offset = offset;
3089
3090
    /* Can be used in case of error, in particular when TFramedTransport is in use. */
3091
1.87k
    thrift_opt->reassembly_tree = thrift_tree;
3092
1.87k
    thrift_opt->reassembly_offset = start_offset;
3093
1.87k
    thrift_opt->reassembly_length = -1;
3094
1.87k
    thrift_opt->mtype = (thrift_method_type_enum_t)mtype;
3095
3096
    /*****************************************************/
3097
    /* Create the header tree with the extracted fields. */
3098
    /*****************************************************/
3099
1.87k
    col_append_sep_fstr(pinfo->cinfo, COL_INFO, ", ", "%s %s", val_to_str(pinfo->pool, mtype, thrift_mtype_vals, "%d"), method_str);
3100
3101
1.87k
    if (thrift_tree) {
3102
1.86k
        offset = start_offset; /* Reset parsing position. */
3103
1.86k
        if (is_framed) {
3104
191
            proto_tree_add_item(thrift_tree, hf_thrift_frame_length, tvb, offset, TBP_THRIFT_LENGTH_LEN, ENC_BIG_ENDIAN);
3105
191
            offset += TBP_THRIFT_LENGTH_LEN;
3106
191
        }
3107
1.86k
        sub_tree = proto_tree_add_subtree_format(thrift_tree, tvb, header_offset, data_offset - header_offset, ett_thrift_header, &data_pi,
3108
1.86k
                "%s [version: %d, seqid: %d, method: %s]",
3109
1.86k
                val_to_str(pinfo->pool, mtype, thrift_mtype_vals, "%d"),
3110
1.86k
                version, seq_id, method_str);
3111
        /* Decode the header depending on compact, strict (new) or old. */
3112
1.86k
        if (is_compact) {
3113
            /* Compact: proto_id|mtype+version|seqid|length|name */
3114
1.77k
            proto_tree_add_item(sub_tree, hf_thrift_protocol_id, tvb, offset, TBP_THRIFT_TYPE_LEN, ENC_BIG_ENDIAN);
3115
1.77k
            proto_tree_add_bits_item(sub_tree, hf_thrift_version, tvb, (offset << OCTETS_TO_BITS_SHIFT) + 11, 5, ENC_BIG_ENDIAN);
3116
1.77k
            mtype_pi = proto_tree_add_bits_item(sub_tree, hf_thrift_mtype, tvb, (offset << OCTETS_TO_BITS_SHIFT) + 8, 3, ENC_BIG_ENDIAN);
3117
1.77k
            offset += TCP_THRIFT_VERSION_LEN;
3118
1.77k
            proto_tree_add_int(sub_tree, hf_thrift_seq_id, tvb, offset, seqid_len, seq_id);
3119
1.77k
            offset += seqid_len;
3120
1.77k
            proto_tree_add_int(sub_tree, hf_thrift_str_len, tvb, offset, str_len_len, str_len);
3121
1.77k
            offset += str_len_len;
3122
1.77k
            proto_tree_add_item(sub_tree, hf_thrift_method, tvb, offset, str_len, ENC_UTF_8);
3123
1.77k
            offset = offset + str_len;
3124
1.77k
        } else if (thrift_opt->tprotocol & PROTO_THRIFT_STRICT) {
3125
            /* Strict: proto_id|version|mtype|length|name|seqid */
3126
77
            proto_tree_add_item(sub_tree, hf_thrift_protocol_id, tvb, offset, TBP_THRIFT_TYPE_LEN, ENC_BIG_ENDIAN);
3127
77
            proto_tree_add_bits_item(sub_tree, hf_thrift_version, tvb, (offset << OCTETS_TO_BITS_SHIFT) + 11, 5, ENC_BIG_ENDIAN);
3128
77
            offset += TBP_THRIFT_MTYPE_OFFSET;
3129
77
            mtype_pi = proto_tree_add_bits_item(sub_tree, hf_thrift_mtype, tvb, (offset << OCTETS_TO_BITS_SHIFT) + 5, 3, ENC_BIG_ENDIAN);
3130
77
            offset += TBP_THRIFT_MTYPE_LEN;
3131
77
            proto_tree_add_item(sub_tree, hf_thrift_str_len, tvb, offset, TBP_THRIFT_LENGTH_LEN, ENC_BIG_ENDIAN);
3132
77
            offset += TBP_THRIFT_LENGTH_LEN;
3133
77
            proto_tree_add_item(sub_tree, hf_thrift_method, tvb, offset, str_len, ENC_UTF_8);
3134
77
            offset = offset + str_len;
3135
77
            proto_tree_add_item(sub_tree, hf_thrift_seq_id, tvb, offset, TBP_THRIFT_SEQ_ID_LEN, ENC_BIG_ENDIAN);
3136
77
            offset += TBP_THRIFT_SEQ_ID_LEN;
3137
77
        } else {
3138
            /* Old: length|name|mtype|seqid */
3139
20
            proto_tree_add_item(sub_tree, hf_thrift_str_len, tvb, offset, TBP_THRIFT_LENGTH_LEN, ENC_BIG_ENDIAN);
3140
20
            offset += TBP_THRIFT_LENGTH_LEN;
3141
20
            proto_tree_add_item(sub_tree, hf_thrift_method, tvb, offset, str_len, ENC_UTF_8);
3142
20
            offset = offset + str_len;
3143
20
            mtype_pi = proto_tree_add_bits_item(sub_tree, hf_thrift_mtype, tvb, (offset << OCTETS_TO_BITS_SHIFT) + 5, 3, ENC_BIG_ENDIAN);
3144
20
            offset += TBP_THRIFT_MTYPE_LEN;
3145
20
            proto_tree_add_item(sub_tree, hf_thrift_seq_id, tvb, offset, TBP_THRIFT_SEQ_ID_LEN, ENC_BIG_ENDIAN);
3146
20
            offset += TBP_THRIFT_SEQ_ID_LEN;
3147
20
        }
3148
1.86k
        DISSECTOR_ASSERT(offset == data_offset);
3149
1.86k
    }
3150
3151
    /* TODO: Save CALL seq_id to link with matching REPLY|EXCEPTION for conversation_t. */
3152
    /* TODO: Track the command name as well? Act differently for null & non-null seq_id? */
3153
1.87k
    if (tvb_reported_length_remaining(tvb, data_offset) < TBP_THRIFT_TYPE_LEN) {
3154
2
        goto add_expert_and_reassemble;
3155
2
    }
3156
3157
    /***********************************************************/
3158
    /* Call method dissector here using dissector_try_string_with_data() */
3159
    /* except in case of EXCEPTION for detailed dissection.    */
3160
    /***********************************************************/
3161
1.87k
    thrift_opt->previous_field_id = 0;
3162
1.87k
    msg_tvb = tvb_new_subset_remaining(tvb, data_offset);
3163
1.87k
    if (thrift_opt->mtype == ME_THRIFT_T_REPLY) {
3164
220
        thrift_field_header_t header = {
3165
220
            .field_id = 0, // Overwritten by dissect_thrift_field_header() but undetected by Clang.
3166
220
        };
3167
        /* For REPLY, in order to separate successful answers from errors (exceptions),
3168
         * Thrift generates a struct with as much fields (all optional) as there are exceptions possible + 1.
3169
         * At most 1 field will be filled for any reply
3170
         * - Field id = 0: The effective type of the return value of the method (not set if void).
3171
         * - Field id > 0: The number of the exception that was raised by the method.
3172
         *   Note: This is different from the ME_THRIFT_T_EXCEPTION method type that is used in case the method is unknown
3173
         *         or the PDU invalid/impossible to decode for the other endpoint.
3174
         * We read this before checking for sub-dissectors as the information might be useful to them.
3175
         */
3176
220
        int result = data_offset;
3177
220
        result = dissect_thrift_field_header(tvb, pinfo, NULL, &result, thrift_opt, &header, false);
3178
220
        switch (result) {
3179
3
        case THRIFT_REQUEST_REASSEMBLY:
3180
3
            goto add_expert_and_reassemble;
3181
0
        case THRIFT_SUBDISSECTOR_ERROR:
3182
0
            return 0;
3183
217
        default:
3184
217
            break;
3185
220
        }
3186
217
        thrift_opt->reply_field_id = header.field_id;
3187
217
        fid_pi = header.fid_pi;
3188
217
    }
3189
1.87k
    if (thrift_opt->mtype != ME_THRIFT_T_EXCEPTION) {
3190
1.75k
        if (pinfo->can_desegment > 0) pinfo->can_desegment++;
3191
1.75k
        len = dissector_try_string_with_data(thrift_method_name_dissector_table, method_str, msg_tvb, pinfo, tree, true, thrift_opt);
3192
1.75k
        if (pinfo->can_desegment > 0) pinfo->can_desegment--;
3193
1.75k
    } else {
3194
        /* Attach the expert_info to the method type as it is a protocol-level exception. */
3195
114
        expert_add_info(pinfo, mtype_pi, &ei_thrift_protocol_exception);
3196
        /* Leverage the sub-dissector capabilities to dissect Thrift exceptions. */
3197
114
        len = dissect_thrift_t_struct(msg_tvb, pinfo, thrift_tree, 0, thrift_opt, false, 0, hf_thrift_exception, ett_thrift_exception, thrift_exception);
3198
114
    }
3199
1.87k
    if (len > 0) {
3200
        /* The sub dissector dissected the tvb*/
3201
40
        if (!is_framed) {
3202
40
            proto_item_set_end(thrift_pi, msg_tvb, len);
3203
40
        }
3204
40
        return data_offset + len;
3205
1.83k
    } else if (len == THRIFT_REQUEST_REASSEMBLY) {
3206
        /* The sub-dissector requested more bytes (len = -1) */
3207
3
        goto reassemble_pdu;
3208
1.82k
    } else if (len <= THRIFT_SUBDISSECTOR_ERROR) {
3209
        /* All other negative values are treated as error codes (only -2 is recommended). */
3210
63
        if (!try_generic_if_sub_dissector_fails) {
3211
63
            return 0;
3212
63
        }
3213
        /* else { Fallback to dissect using the generic dissector. } */
3214
        /* Reset proto depth as the sub-dissector might have failed within a sub-structure and changed its value. */
3215
0
        p_set_proto_depth(pinfo, proto_thrift, 0);
3216
0
    } /* else len = 0, no specific sub-dissector. */
3217
3218
    /***********************/
3219
    /* Generic dissection. */
3220
    /***********************/
3221
1.76k
    sub_tree = proto_tree_add_subtree(thrift_tree, tvb, data_offset, -1, ett_thrift_params, &data_pi, "Data");
3222
1.76k
    thrift_opt->reassembly_length = TBP_THRIFT_TYPE_LEN;
3223
1.76k
    if (thrift_opt->reply_field_id != 0) {
3224
483
        expert_add_info(pinfo, fid_pi, &ei_thrift_application_exception);
3225
483
        proto_item_set_text(data_pi, "Exception: %" PRId64, thrift_opt->reply_field_id);
3226
483
    }
3227
3228
1.76k
    if (is_compact) {
3229
1.68k
        request_reasm = dissect_thrift_compact_fields(tvb, pinfo, sub_tree, &offset, thrift_opt) == THRIFT_REQUEST_REASSEMBLY;
3230
1.68k
    } else { /* Binary (strict/old distinction only applies to the header) */
3231
77
        request_reasm = dissect_thrift_binary_fields(tvb, pinfo, sub_tree, &offset, thrift_opt) == THRIFT_REQUEST_REASSEMBLY;
3232
77
    }
3233
    /* Check the result of the Data part dissection. */
3234
1.76k
    if (request_reasm) {
3235
473
        if (offset > 0) {
3236
            /* An error occurred at the given offset, consume everything. */
3237
231
            return tvb_reported_length(tvb);
3238
231
        } /* else It's really a reassembly request. */
3239
242
        goto reassemble_pdu;
3240
1.29k
    } else {
3241
        /* We found the end of the data. */
3242
1.29k
        proto_item_set_end(data_pi, tvb, offset);
3243
1.29k
    }
3244
    /* Set the end of the global Thrift tree (except if framed because it's already set),
3245
     * as well as the end of the Data sub-tree. */
3246
1.29k
    if (!is_framed) {
3247
        /* In case the frame is larger than the data, we need to know the difference. */
3248
1.20k
        proto_item_set_end(thrift_pi, tvb, offset);
3249
1.20k
    }
3250
1.29k
    proto_item_set_end(data_pi, tvb, offset);
3251
1.29k
    return offset;
3252
64
add_expert_and_reassemble: /* When detected in this function. */
3253
64
    expert_add_info(pinfo, data_pi, &ei_thrift_not_enough_data);
3254
309
reassemble_pdu: /* When detected by any called function (that already added the expert info). */
3255
    /* We did not encounter final T_STOP. */
3256
309
    pinfo->desegment_offset = start_offset;
3257
309
    pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
3258
309
    return THRIFT_REQUEST_REASSEMBLY;
3259
64
}
3260
3261
/* For tcp_dissect_pdus. */
3262
static unsigned
3263
get_framed_thrift_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
3264
347
{
3265
347
    return (unsigned)TBP_THRIFT_LENGTH_LEN + tvb_get_ntohl(tvb, offset);
3266
347
}
3267
3268
/* Effective dissection once the exact encoding has been determined.
3269
 * - Calls dissect_thrift_common in a loop until end of a packet matches end of Thrift PDU.
3270
 *
3271
 * This method MUST be called with non-null thrift_opt.
3272
 */
3273
static int
3274
dissect_thrift_loop(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, thrift_option_data_t *thrift_opt)
3275
526
{
3276
526
    int32_t offset = 0;
3277
526
    int32_t hdr_offset = 0;
3278
526
    int32_t last_pdu_start_offset = 0;
3279
526
    int32_t remaining = tvb_reported_length_remaining(tvb, offset);
3280
3281
526
    DISSECTOR_ASSERT(thrift_opt);
3282
526
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
3283
3284
    /* Loop until the end of the packet coincides with the end of a PDU. */
3285
1.93k
    while (remaining > 0) {
3286
1.76k
        last_pdu_start_offset = offset;
3287
1.76k
        if (remaining < TBP_THRIFT_LENGTH_LEN) {
3288
14
            goto reassemble_pdu;
3289
14
        }
3290
1.75k
        if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) {
3291
1.69k
            offset = dissect_thrift_common(tvb, pinfo, tree, offset, thrift_opt);
3292
1.69k
        } else {
3293
            /* According to Thrift documentation, old and new (strict) binary protocols
3294
             * could coexist on a single server so we cannot assume it's still the same.
3295
             * In particular, client could send a first request in old format to get
3296
             * the server version and switch to strict if the server is up-to-date
3297
             * or if it rejected explicitly the old format (there's an example for that). */
3298
57
            if (tvb_get_int8(tvb, offset + hdr_offset) < 0) {
3299
                /* Strict header (If only the message type is incorrect, assume this is a new one. */
3300
32
                if (!is_thrift_strict_version(tvb_get_ntohl(tvb, offset + hdr_offset), true)) {
3301
1
                    expert_add_info(pinfo, NULL, &ei_thrift_wrong_proto_version);
3302
1
                    return tvb_reported_length_remaining(tvb, 0);
3303
1
                }
3304
31
                thrift_opt->tprotocol = (thrift_protocol_enum_t)(thrift_opt->tprotocol | PROTO_THRIFT_STRICT);
3305
31
            } else {
3306
                /* Old header. */
3307
25
                thrift_opt->tprotocol = (thrift_protocol_enum_t)(thrift_opt->tprotocol & ~PROTO_THRIFT_STRICT);
3308
25
            }
3309
56
            offset = dissect_thrift_common(tvb, pinfo, tree, offset, thrift_opt);
3310
56
        }
3311
3312
1.75k
        if (offset == THRIFT_REQUEST_REASSEMBLY) {
3313
296
            goto reassemble_pdu;
3314
1.45k
        } else if (offset == 0) {
3315
            /* An error occurred, we just stop, consuming everything. */
3316
48
            return tvb_reported_length_remaining(tvb, 0);
3317
48
        }
3318
1.40k
        remaining = tvb_reported_length_remaining(tvb, offset);
3319
1.40k
    }
3320
167
    return offset;
3321
310
reassemble_pdu:
3322
    /* We did not encounter a final T_STOP exactly at the last byte. */
3323
310
    pinfo->desegment_offset = last_pdu_start_offset;
3324
310
    pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
3325
310
    return tvb_reported_length(tvb);
3326
526
}
3327
3328
/* Dissect a unique Thrift PDU within a TFramedTransport and return the effective length of this PDU.
3329
 *
3330
 * This method is called only if the preliminary verifications have been done including length.
3331
 * This method will throw if there is not enough data or too much data.
3332
 *
3333
 * This method MUST be called with non-null thrift_opt/data using thrift_option_data_t effective type.
3334
 */
3335
static int
3336
dissect_thrift_framed(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
3337
347
{
3338
347
    int32_t offset = 0;
3339
347
    int32_t frame_len = 0;
3340
347
    int32_t reported = tvb_reported_length_remaining(tvb, offset);
3341
347
    thrift_option_data_t *thrift_opt = (thrift_option_data_t *)data;
3342
3343
347
    DISSECTOR_ASSERT(thrift_opt);
3344
347
    DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY);
3345
347
    DISSECTOR_ASSERT(thrift_opt->tprotocol & PROTO_THRIFT_FRAMED);
3346
347
    frame_len = tvb_get_ntohil(tvb, offset);
3347
    // Note: The framed dissector can be called even if the entire packet is bigger than the frame.
3348
    // This can happen when the frame was initially rejected because it was too short.
3349
347
    DISSECTOR_ASSERT((frame_len + TBP_THRIFT_LENGTH_LEN) <= reported);
3350
3351
347
    offset = dissect_thrift_common(tvb, pinfo, tree, offset, thrift_opt);
3352
347
    if (offset == THRIFT_REQUEST_REASSEMBLY) {
3353
        /* No reassembly possible in this case */
3354
22
        proto_tree_add_expert(thrift_opt->reassembly_tree, pinfo, &ei_thrift_frame_too_short,
3355
22
                tvb, thrift_opt->reassembly_offset, thrift_opt->reassembly_length);
3356
22
        pinfo->desegment_offset = reported;
3357
22
        pinfo->desegment_len = 0;
3358
325
    } else if (offset > 0 && tvb_reported_length_remaining(tvb, offset) > 0) {
3359
42
        proto_tree_add_expert(thrift_opt->reassembly_tree, pinfo, &ei_thrift_frame_too_long,
3360
42
                tvb, offset, tvb_reported_length_remaining(tvb, offset));
3361
42
    }
3362
347
    return reported;
3363
347
}
3364
3365
/* Thrift dissection when forced by Decode As… or port selection */
3366
static int
3367
dissect_thrift_transport(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
3368
0
{
3369
0
    unsigned str_len, length = tvb_reported_length(tvb);
3370
0
    thrift_option_data_t thrift_opt;
3371
0
    memset(&thrift_opt, 0, sizeof(thrift_option_data_t));
3372
0
    thrift_opt.nested_type_depth = nested_type_depth;
3373
3374
    /* Starting without even the version / frame length / name length probably means a Keep-Alive at the beginning of the capture. */
3375
0
    if (length < TBP_THRIFT_VERSION_LEN) {
3376
0
        if (tvb_get_uint8(tvb, 0) != (THRIFT_COMPACT_VERSION_1 >> 8)) {
3377
0
            proto_tree_add_expert(tree, pinfo, &ei_thrift_not_enough_data, tvb, 0, length);
3378
            /* Not a Thrift packet, maybe a keep-alive at the beginning of the capture. */
3379
0
            return NOT_A_VALID_PDU;
3380
0
        } /* else this might be a compact capture without Nagle activated. */
3381
0
    }
3382
    /* Need at least the old encoding header (Name Length + Method + Sequence Id) + ending T_STOP */
3383
0
    if (length < TBP_THRIFT_MIN_MESSAGE_LEN) {
3384
        /* Note: if Nagle algorithm is not active, some systems can spit out Thrift individual elements one by one.
3385
         * For instance on strict protocol:
3386
         * Frame x+0: 4 bytes = version + method type (sent using writeI32)
3387
         * Frame x+1: 4 bytes = method length
3388
         * Frame x+2: n bytes = method name
3389
         * Frame x+3: 4 bytes = sequence id
3390
         * Frame x+4: 1 byte  = field type… */
3391
0
        goto reassemble_pdu;
3392
0
    }
3393
3394
    /* MSb of first byte is 1 for compact and binary strict protocols
3395
     * and 0 for framed transport and old binary protocol. */
3396
0
    if (tvb_get_int8(tvb, 0) >= 0) {
3397
        /* Option 1 = old binary
3398
         * Option 2 = framed strict binary
3399
         * Option 3 = framed old binary
3400
         * Option 4 = framed compact or anything  not handled. */
3401
0
        unsigned remaining = tvb_reported_length_remaining(tvb, TBP_THRIFT_LENGTH_LEN); /* Remaining after initial 4 bytes of "length" */
3402
        /* Old header. */
3403
0
        str_len = tvb_get_ntohil(tvb, 0);
3404
3405
0
        if (remaining == 0) {
3406
            /* The endpoint probably does not have Nagle activated, wait for next packet. */
3407
0
            goto reassemble_pdu;
3408
0
        }
3409
        /* Checking for old binary option. */
3410
0
        if (remaining < str_len) {
3411
            /* Not enough data to check name validity.
3412
             * Even without Nagle activated, this is /not/ plain old binary Thrift data (or method name is awfully long).
3413
             * Non-framed old binary is not possible, consider framed data only. */
3414
            // TODO: Display available data & error in case we can't reassemble?
3415
0
            pinfo->desegment_len = str_len - remaining;
3416
            /* Maybe we should return NOT_A_VALID_PDU instead and drop this packet but port preferences tells us this /is/ Thrift data. */
3417
0
            return THRIFT_REQUEST_REASSEMBLY;
3418
0
        }
3419
3420
0
        if (thrift_binary_utf8_isprint(tvb, TBP_THRIFT_LENGTH_LEN, str_len, false) == str_len) {
3421
            /* UTF-8 valid data means first byte is greater than 0x20 and not between 0x80 and 0xbf (neither 0x80 nor 0x82 in particular).
3422
             * This would indicate a method name longer than 512 MiB in Framed old binary protocol which is insane.
3423
             * Therefore, most sane option is old binary without any framing. */
3424
0
            thrift_opt.canary = THRIFT_OPTION_DATA_CANARY;
3425
0
            thrift_opt.tprotocol = PROTO_THRIFT_BINARY;
3426
            /* Name length + name + method + seq_id + T_STOP */
3427
0
            if (length < TBP_THRIFT_MIN_MESSAGE_LEN + str_len) {
3428
0
                goto reassemble_pdu;
3429
0
            }
3430
0
        } else {
3431
            /* This cannot be non-framed old binary so it must be framed (and we have all of it). */
3432
0
            if (str_len < TBP_THRIFT_MIN_MESSAGE_LEN) {
3433
                /* This is /not/ valid Framed data. */
3434
0
                return NOT_A_VALID_PDU;
3435
0
            }
3436
0
            if (tvb_get_int8(tvb, TBP_THRIFT_LENGTH_LEN) >= 0) {
3437
                /* Framed old binary format is the only matching option remaining. */
3438
0
                thrift_opt.canary = THRIFT_OPTION_DATA_CANARY;
3439
0
                thrift_opt.tprotocol = PROTO_THRIFT_FRAMED;
3440
0
            } else {
3441
0
                if (is_thrift_strict_version(tvb_get_ntohl(tvb, TBP_THRIFT_LENGTH_LEN), true)) {
3442
                    /* Framed strict binary protocol. */
3443
0
                    thrift_opt.canary = THRIFT_OPTION_DATA_CANARY;
3444
0
                    thrift_opt.tprotocol = (thrift_protocol_enum_t)(PROTO_THRIFT_FRAMED | PROTO_THRIFT_STRICT);
3445
0
                } else {
3446
                    /* Framed compact protocol or something else entirely, bail out. */
3447
0
                    return NOT_A_VALID_PDU;
3448
0
                }
3449
0
            }
3450
0
        }
3451
0
    } else if (is_thrift_strict_version(tvb_get_ntohl(tvb, 0), true)) {
3452
        /* We don't need all the checks from the heuristic because the user prefs told us it /is/ Thrift data.
3453
         * If it fails, it will probably pass through otherwise hard-to-reach code-paths so that's good for tests. */
3454
0
        thrift_opt.canary = THRIFT_OPTION_DATA_CANARY;
3455
0
        thrift_opt.tprotocol = PROTO_THRIFT_STRICT;
3456
0
    } else if (tvb_get_uint8(tvb, 0) == 0x82) {
3457
        /* Same thing here so 0x82 gives us the TCompactProtocol answer. */
3458
0
        thrift_opt.canary = THRIFT_OPTION_DATA_CANARY;
3459
0
        thrift_opt.tprotocol = PROTO_THRIFT_COMPACT;
3460
0
    } else {
3461
        /* else { Not a Thrift packet. } */
3462
0
        return NOT_A_VALID_PDU;
3463
0
    }
3464
3465
0
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "THRIFT");
3466
0
    col_clear(pinfo->cinfo, COL_INFO);
3467
3468
0
    if (thrift_opt.tprotocol & PROTO_THRIFT_FRAMED) {
3469
0
        tcp_dissect_pdus(tvb, pinfo, tree, framed_desegment, TBP_THRIFT_LENGTH_LEN,
3470
0
                get_framed_thrift_pdu_len, dissect_thrift_framed, &thrift_opt);
3471
0
        return tvb_reported_length(tvb);
3472
0
    } else {
3473
0
        return dissect_thrift_loop(tvb, pinfo, tree, &thrift_opt);
3474
0
    }
3475
3476
0
reassemble_pdu:
3477
0
    pinfo->desegment_offset = 0;
3478
0
    pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
3479
0
    return THRIFT_REQUEST_REASSEMBLY;
3480
0
}
3481
3482
/* Test if the captured packet matches a Thrift strict binary packet header.
3483
 * We check for captured and not reported length because:
3484
 * - We need to check the content to verify validity;
3485
 * - We must not have exception in heuristic dissector.
3486
 * Due to that, we might have false negative if the capture is too much shorten
3487
 * but it would have been useless anyway.
3488
 */
3489
static bool
3490
test_thrift_strict(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree _U_, thrift_option_data_t *thrift_opt)
3491
8.38k
{
3492
8.38k
    unsigned tframe_length = 0;
3493
8.38k
    unsigned offset = 0;
3494
8.38k
    unsigned length = tvb_captured_length(tvb);
3495
8.38k
    unsigned str_len;
3496
3497
    /* This heuristic only detects strict binary protocol, possibly framed.
3498
     * Detection of old binary protocol is tricky due to the lack of fixed data.
3499
     * TODO: Maybe by assuming a maximum size for the method name like 1kB… or less.
3500
     *
3501
     * In order to avoid false positive, the first packet is expected to contain:
3502
     * 1. Possibly Frame size (4 bytes, if MSb of first byte is 0),
3503
     * 2. Thrift "version" (4 bytes = 0x8001..0m, containing protocol id, version, and method type),
3504
     * 3. Method length (4 bytes),
3505
     * 4. Method name (method length bytes, verified as acceptable UTF-8),
3506
     * 5. Sequence ID (4 bytes, content not verified),
3507
     * 6. First field type (1 byte, content not verified). */
3508
3509
    /* Enough data for elements 2 to 6? */
3510
8.38k
    if (length < (unsigned)TBP_THRIFT_STRICT_HEADER_LEN) {
3511
675
        return false;
3512
675
    }
3513
3514
    /* 1. Check if it is framed (and if the frame length is large enough for a complete message). */
3515
7.71k
    if (tvb_get_int8(tvb, offset) >= 0) {
3516
        /* It is framed. */
3517
5.55k
        tframe_length = tvb_get_ntohil(tvb, offset);
3518
3519
5.55k
        if (tframe_length < TBP_THRIFT_STRICT_MIN_MESSAGE_LEN) {
3520
178
            return false;
3521
178
        }
3522
5.37k
        offset = TBP_THRIFT_LENGTH_LEN; /* Strict header starts after frame length. */
3523
5.37k
        if (length < (unsigned)(offset + TBP_THRIFT_STRICT_HEADER_LEN)) {
3524
346
            return false;
3525
346
        }
3526
5.37k
    }
3527
7.18k
    if (thrift_opt) {
3528
7.18k
        thrift_opt->canary = THRIFT_OPTION_DATA_CANARY;
3529
        /* Set the protocol used since we now have enough information. */
3530
7.18k
        thrift_opt->tprotocol = PROTO_THRIFT_STRICT;
3531
7.18k
        if (tframe_length > 0) {
3532
5.03k
            thrift_opt->tprotocol = (thrift_protocol_enum_t)(thrift_opt->tprotocol | PROTO_THRIFT_FRAMED);
3533
5.03k
        }
3534
7.18k
    } else REPORT_DISSECTOR_BUG("%s called without data structure.", G_STRFUNC);
3535
3536
    /* 2. Thrift version & method type (heuristic does /not/ ignore the message type). */
3537
7.18k
    if (!is_thrift_strict_version(tvb_get_ntohl(tvb, offset), false)) {
3538
7.11k
        return false;
3539
7.11k
    }
3540
67
    offset += TBP_THRIFT_VERSION_LEN;
3541
3542
    /* 3. Get method name length and check against what we have. */
3543
67
    str_len = tvb_get_ntohil(tvb, offset);
3544
67
    if ((tframe_length > 0) && (tframe_length < TBP_THRIFT_STRICT_MIN_MESSAGE_LEN + str_len)) {
3545
        /* The frame cannot even contain an empty Thrift message (no data, only T_STOP after the sequence id). */
3546
4
        return false;
3547
4
    }
3548
63
    offset += TBP_THRIFT_LENGTH_LEN;
3549
3550
    /* 4. Check method name itself. */
3551
63
    if (tvb_captured_length_remaining(tvb, offset) < str_len) {
3552
        /* Method name is no entirely captured, we cannot check it. */
3553
4
        return false;
3554
4
    }
3555
59
    if (thrift_binary_utf8_isprint(tvb, offset, str_len, false) < str_len) {
3556
1
        return false;
3557
1
    }
3558
58
    offset += str_len;
3559
3560
    /* 5 & 6. Check that there is enough data remaining for a sequence ID and a field type (but no need for it to be captured). */
3561
58
    if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_LENGTH_LEN + TBP_THRIFT_TYPE_LEN) {
3562
0
        return false;
3563
0
    }
3564
3565
58
    thrift_opt->canary = THRIFT_OPTION_DATA_CANARY;
3566
58
    return true;
3567
58
}
3568
3569
/* Test if the captured packet matches a Thrift compact packet header.
3570
 * Same comments as for test_thrift_strict.
3571
 */
3572
static bool
3573
test_thrift_compact(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree _U_, thrift_option_data_t *thrift_opt)
3574
8.32k
{
3575
8.32k
    unsigned tframe_length = 0;
3576
8.32k
    unsigned offset = 0;
3577
8.32k
    unsigned length = tvb_captured_length(tvb);
3578
8.32k
    unsigned len_len;
3579
8.32k
    unsigned str_len = 0;
3580
8.32k
    uint64_t seq_id;
3581
3582
    /* This heuristic detects compact protocol, possibly framed.
3583
     * Note: It assumes a maximum size of 127 bytes for the method name.
3584
     *       Increasing the limit would be easy but this is the heuristic.
3585
     *
3586
     * In order to avoid false positive, the first packet is expected to contain:
3587
     * 1. Possibly Frame size (4 bytes, if MSb of first byte is 0),
3588
     * 2. Thrift "version" (2 bytes = 0x82mv, containing protocol id, method type, and version),
3589
     * 3. Sequence ID (1 to 5 bytes, content not verified),
3590
     * 4. Method length (theoretically 1 to 5 bytes, in practice only 1 byte is accepted),
3591
     * 5. Method name (method length bytes, verified as acceptable UTF-8),
3592
     * 6. First field type (1 byte, content not verified). */
3593
3594
    /* Enough data for elements 2 to 6? */
3595
8.32k
    if (length < TCP_THRIFT_MIN_MESSAGE_LEN) {
3596
439
        return false;
3597
439
    }
3598
3599
    /* 1. Check if it is framed (and if the frame length is large enough for a complete message). */
3600
7.88k
    if (tvb_get_int8(tvb, offset) >= 0) {
3601
        /* It is framed. */
3602
5.69k
        tframe_length = tvb_get_ntohil(tvb, offset);
3603
3604
5.69k
        if (tframe_length < TCP_THRIFT_MIN_MESSAGE_LEN) {
3605
174
            return false;
3606
174
        }
3607
5.52k
        offset = TBP_THRIFT_LENGTH_LEN; /* Compact header starts after frame length. */
3608
5.52k
        if (length < (offset + TCP_THRIFT_MIN_MESSAGE_LEN)) {
3609
253
            return false;
3610
253
        }
3611
5.52k
    }
3612
7.46k
    if (thrift_opt) {
3613
7.46k
        thrift_opt->canary = THRIFT_OPTION_DATA_CANARY;
3614
        /* Set the protocol used since we now have enough information. */
3615
7.46k
        if (tframe_length > 0) {
3616
5.26k
            thrift_opt->tprotocol = (thrift_protocol_enum_t)(PROTO_THRIFT_COMPACT | PROTO_THRIFT_FRAMED);
3617
5.26k
        } else {
3618
2.19k
            thrift_opt->tprotocol = PROTO_THRIFT_COMPACT;
3619
2.19k
        }
3620
7.46k
    } else REPORT_DISSECTOR_BUG("%s called without data structure.", G_STRFUNC);
3621
3622
    /* 2. Thrift version & method type (heuristic does /not/ ignore the message type). */
3623
7.46k
    if (!is_thrift_compact_version(tvb_get_ntohs(tvb, offset), false)) {
3624
6.83k
        return false;
3625
6.83k
    }
3626
630
    offset += TCP_THRIFT_VERSION_LEN;
3627
3628
    /* 3. Sequence id in varint encoding. We need to make sure we don't try to read not captured data. */
3629
630
    len_len = tvb_captured_length_remaining(tvb, offset);
3630
630
    if (len_len > TCP_THRIFT_MAX_I32_LEN) {
3631
619
        len_len = TCP_THRIFT_MAX_I32_LEN;
3632
619
    }
3633
630
    len_len = tvb_get_varint(tvb, offset, len_len, &seq_id, ENC_VARINT_ZIGZAG);
3634
630
    if (len_len == 0) return false;
3635
628
    offset += len_len;
3636
3637
    /* 4. Get method name length and check against what we have. */
3638
628
    if (offset >= length) return false;
3639
626
    str_len = tvb_get_uint8(tvb, offset);
3640
    // MSb = 1 means the method length is greater than 127 bytes long.
3641
626
    if ((str_len & 0x80) != 0) {
3642
4
        return false; // Reject it to avoid too many false positive.
3643
4
    }
3644
622
    ++offset;
3645
622
    if ((tframe_length > 0) && (TBP_THRIFT_LENGTH_LEN + tframe_length < offset + str_len)) {
3646
        /* The frame cannot even contain an empty Thrift message (no data, only T_STOP after the sequence id). */
3647
1
        return false;
3648
1
    }
3649
3650
    /* 5. Check method name itself. */
3651
621
    if (tvb_captured_length_remaining(tvb, offset) < str_len) {
3652
        /* Method name is no entirely captured, we cannot check it. */
3653
7
        return false;
3654
7
    }
3655
614
    if (thrift_binary_utf8_isprint(tvb, offset, str_len, false) < str_len) {
3656
2
        return false;
3657
2
    }
3658
612
    offset += str_len;
3659
3660
    /* 6. Check that there is enough data remaining for a field type (but no need for it to be captured). */
3661
612
    if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_TYPE_LEN) {
3662
2
        return false;
3663
2
    }
3664
3665
610
    thrift_opt->canary = THRIFT_OPTION_DATA_CANARY;
3666
610
    return true;
3667
612
}
3668
3669
/* Thrift heuristic dissection when the packet is not grabbed by another protocol dissector. */
3670
static bool
3671
dissect_thrift_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
3672
8.38k
{
3673
8.38k
    thrift_option_data_t thrift_opt;
3674
8.38k
    memset(&thrift_opt, 0, sizeof(thrift_option_data_t));
3675
8.38k
    thrift_opt.nested_type_depth = nested_type_depth;
3676
3677
8.38k
    if (!test_thrift_strict(tvb, pinfo, tree, &thrift_opt) && !test_thrift_compact(tvb, pinfo, tree, &thrift_opt)) {
3678
7.71k
        return false;
3679
7.71k
    }
3680
3681
668
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "THRIFT");
3682
668
    col_clear(pinfo->cinfo, COL_INFO);
3683
3684
668
    if (thrift_opt.tprotocol & PROTO_THRIFT_FRAMED) {
3685
142
        tcp_dissect_pdus(tvb, pinfo, tree, framed_desegment, TBP_THRIFT_LENGTH_LEN,
3686
142
                get_framed_thrift_pdu_len, dissect_thrift_framed, &thrift_opt);
3687
526
    } else {
3688
526
        dissect_thrift_loop(tvb, pinfo, tree, &thrift_opt);
3689
526
    }
3690
3691
668
    return true;
3692
8.38k
}
3693
3694
static int
3695
dissect_thrift_http(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
3696
0
{
3697
0
    return dissect_thrift_heur(tvb, pinfo, tree, data) ? tvb_captured_length(tvb) : 0;
3698
0
}
3699
/*=====END HEADER GENERIC DISSECTION=====*/
3700
3701
void
3702
proto_register_thrift(void)
3703
14
{
3704
14
    static hf_register_info hf[] = {
3705
14
        { &hf_thrift_frame_length,
3706
14
            { "Frame length", "thrift.frame_len",
3707
14
                FT_INT32, BASE_DEC, NULL, 0x0,
3708
14
                NULL, HFILL }
3709
14
        },
3710
14
        { &hf_thrift_exception,
3711
14
            { "Exception", "thrift.exception",
3712
14
                FT_NONE, BASE_NONE, NULL, 0x0,
3713
14
                NULL, HFILL }
3714
14
        },
3715
14
        { &hf_thrift_exception_message,
3716
14
            { "Exception Message", "thrift.exception.message",
3717
14
                FT_STRING, BASE_NONE, NULL, 0x0,
3718
14
                NULL, HFILL }
3719
14
        },
3720
14
        { &hf_thrift_exception_type,
3721
14
            { "Exception Type", "thrift.exception.type",
3722
14
                FT_INT32, BASE_DEC, VALS(thrift_exception_type_vals), 0x0,
3723
14
                NULL, HFILL }
3724
14
        },
3725
14
        { &hf_thrift_protocol_id,
3726
14
            { "Protocol id", "thrift.protocol_id",
3727
14
                FT_UINT8, BASE_HEX, VALS(thrift_proto_vals), 0x0,
3728
14
                NULL, HFILL }
3729
14
        },
3730
14
        { &hf_thrift_version,
3731
14
            { "Version", "thrift.version",
3732
14
                FT_UINT8, BASE_DEC, NULL, 0x0,
3733
14
                NULL, HFILL }
3734
14
        },
3735
14
        { &hf_thrift_mtype,
3736
14
            { "Message type", "thrift.mtype",
3737
14
                FT_UINT8, BASE_HEX, VALS(thrift_mtype_vals), 0x0,
3738
14
                NULL, HFILL }
3739
14
        },
3740
14
        { &hf_thrift_str_len,
3741
14
            { "Length", "thrift.str_len",
3742
14
                FT_INT32, BASE_DEC, NULL, 0x0,
3743
14
                NULL, HFILL }
3744
14
        },
3745
14
        { &hf_thrift_method,
3746
14
            { "Method", "thrift.method",
3747
14
                FT_STRING, BASE_NONE, NULL, 0x0,
3748
14
                NULL, HFILL }
3749
14
        },
3750
14
        { &hf_thrift_seq_id,
3751
14
            { "Sequence Id", "thrift.seq_id",
3752
14
                FT_INT32, BASE_DEC, NULL, 0x0,
3753
14
                NULL, HFILL }
3754
14
        },
3755
14
        { &hf_thrift_type,
3756
14
            { "Type", "thrift.type",
3757
14
                FT_UINT8, BASE_HEX, VALS(thrift_type_vals), 0x0,
3758
14
                NULL, HFILL }
3759
14
        },
3760
14
        { &hf_thrift_key_type,
3761
14
            { "Key Type", "thrift.type",
3762
14
                FT_UINT8, BASE_HEX, VALS(thrift_type_vals), 0x0,
3763
14
                NULL, HFILL }
3764
14
        },
3765
14
        { &hf_thrift_value_type,
3766
14
            { "Value Type", "thrift.type",
3767
14
                FT_UINT8, BASE_HEX, VALS(thrift_type_vals), 0x0,
3768
14
                NULL, HFILL }
3769
14
        },
3770
14
        { &hf_thrift_compact_struct_type,
3771
14
            { "Type", "thrift.type",
3772
14
                FT_UINT8, BASE_HEX, VALS(thrift_compact_type_vals), 0x0,
3773
14
                NULL, HFILL }
3774
14
        },
3775
14
        { &hf_thrift_fid,
3776
14
            { "Field Id", "thrift.fid",
3777
14
                FT_INT16, BASE_DEC, NULL, 0x0,
3778
14
                NULL, HFILL }
3779
14
        },
3780
14
        { &hf_thrift_fid_delta,
3781
14
            { "Field Id Delta", "thrift.fid_delta",
3782
14
                FT_UINT8, BASE_DEC, NULL, 0x0,
3783
14
                NULL, HFILL }
3784
14
        },
3785
14
        { &hf_thrift_bool,
3786
14
            { "Boolean", "thrift.bool",
3787
14
                FT_BOOLEAN, BASE_NONE, NULL, 0x0, /* libthrift (C++) also considers boolean value = (byte != 0x00) */
3788
14
                NULL, HFILL }
3789
14
        },
3790
14
        { &hf_thrift_i8,
3791
14
            { "Integer8", "thrift.i8",
3792
14
                FT_INT8, BASE_DEC, NULL, 0x0,
3793
14
                NULL, HFILL }
3794
14
        },
3795
14
        { &hf_thrift_i16,
3796
14
            { "Integer16", "thrift.i16",
3797
14
                FT_INT16, BASE_DEC, NULL, 0x0,
3798
14
                NULL, HFILL }
3799
14
        },
3800
14
        { &hf_thrift_i32,
3801
14
            { "Integer32", "thrift.i32",
3802
14
                FT_INT32, BASE_DEC, NULL, 0x0,
3803
14
                NULL, HFILL }
3804
14
        },
3805
14
        { &hf_thrift_i64,
3806
14
            { "Integer64", "thrift.i64",
3807
14
                FT_INT64, BASE_DEC, NULL, 0x0,
3808
14
                NULL, HFILL }
3809
14
        },
3810
14
        { &hf_thrift_u64,
3811
14
            { "Unsigned64", "thrift.u64",
3812
14
                FT_UINT64, BASE_DEC, NULL, 0x0,
3813
14
                "Usually an unsigned varint", HFILL }
3814
14
        },
3815
14
        { &hf_thrift_double,
3816
14
            { "Double", "thrift.double",
3817
14
                FT_DOUBLE, BASE_NONE, NULL, 0x0,
3818
14
                NULL, HFILL }
3819
14
        },
3820
14
        { &hf_thrift_binary,
3821
14
            { "Binary", "thrift.binary",
3822
14
                FT_BYTES, BASE_NONE, NULL, 0x0,
3823
14
                NULL, HFILL }
3824
14
        },
3825
14
        { &hf_thrift_string,
3826
14
            { "String", "thrift.string",
3827
14
                FT_STRING, BASE_NONE, NULL, 0x0,
3828
14
                "Binary field interpreted as a string.", HFILL }
3829
14
        },
3830
14
        { &hf_thrift_struct,
3831
14
            { "Struct", "thrift.struct",
3832
14
                FT_NONE, BASE_NONE, NULL, 0x0,
3833
14
                NULL, HFILL }
3834
14
        },
3835
14
        { &hf_thrift_list,
3836
14
            { "List", "thrift.list",
3837
14
                FT_NONE, BASE_NONE, NULL, 0x0,
3838
14
                NULL, HFILL }
3839
14
        },
3840
14
        { &hf_thrift_set,
3841
14
            { "Set", "thrift.set",
3842
14
                FT_NONE, BASE_NONE, NULL, 0x0,
3843
14
                NULL, HFILL }
3844
14
        },
3845
14
        { &hf_thrift_map,
3846
14
            { "Map", "thrift.map",
3847
14
                FT_NONE, BASE_NONE, NULL, 0x0,
3848
14
                NULL, HFILL }
3849
14
        },
3850
14
        { &hf_thrift_num_set_item,
3851
14
            { "Number of Set Items", "thrift.num_set_item",
3852
14
                FT_INT32, BASE_DEC, NULL, 0x0,
3853
14
                NULL, HFILL }
3854
14
        },
3855
14
        { &hf_thrift_num_set_pos,
3856
14
            { "Number of Set Items", "thrift.num_set_pos",
3857
14
                FT_UINT32, BASE_DEC, NULL, 0x0,
3858
14
                NULL, HFILL }
3859
14
        },
3860
14
        { &hf_thrift_num_list_item,
3861
14
            { "Number of List Items", "thrift.num_list_item",
3862
14
                FT_INT32, BASE_DEC, NULL, 0x0,
3863
14
                NULL, HFILL }
3864
14
        },
3865
14
        { &hf_thrift_num_list_pos,
3866
14
            { "Number of List Items", "thrift.num_list_pos",
3867
14
                FT_UINT32, BASE_DEC, NULL, 0x0,
3868
14
                NULL, HFILL }
3869
14
        },
3870
14
        { &hf_thrift_num_map_item,
3871
14
            { "Number of Map Items", "thrift.num_map_item",
3872
14
                FT_INT32, BASE_DEC, NULL, 0x0,
3873
14
                NULL, HFILL }
3874
14
        },
3875
14
        { &hf_thrift_large_container,
3876
14
            { "More than 14 items", "thrift.num_item",
3877
14
                FT_UINT8, BASE_HEX, NULL, 0x0,
3878
14
                NULL, HFILL }
3879
14
        },
3880
14
        { &hf_thrift_uuid,
3881
14
            { "UUID", "thrift.uuid",
3882
14
                FT_GUID, BASE_NONE, NULL, 0x0,
3883
14
                NULL, HFILL }
3884
14
        },
3885
14
    };
3886
3887
3888
    /* setup protocol subtree arrays */
3889
14
    static int *ett[] = {
3890
14
        &ett_thrift,
3891
14
        &ett_thrift_header,
3892
14
        &ett_thrift_params,
3893
14
        &ett_thrift_field,
3894
14
        &ett_thrift_struct,
3895
14
        &ett_thrift_list,
3896
14
        &ett_thrift_set,
3897
14
        &ett_thrift_map,
3898
14
        &ett_thrift_error,
3899
14
        &ett_thrift_exception,
3900
14
    };
3901
3902
14
    static ei_register_info ei[] = {
3903
14
        { &ei_thrift_wrong_type, { "thrift.wrong_type", PI_PROTOCOL, PI_ERROR, "Type value not expected.", EXPFILL } },
3904
14
        { &ei_thrift_wrong_field_id, { "thrift.wrong_field_id", PI_PROTOCOL, PI_WARN, "Field id different from value provided by sub-dissector.", EXPFILL } },
3905
14
        { &ei_thrift_negative_length, { "thrift.negative_length", PI_PROTOCOL, PI_ERROR, "Length greater than 2 GiB not supported.", EXPFILL } },
3906
14
        { &ei_thrift_wrong_proto_version, { "thrift.wrong_proto_version", PI_MALFORMED, PI_ERROR, "Protocol version invalid or unsupported.", EXPFILL } },
3907
14
        { &ei_thrift_struct_fid_not_in_seq, { "thrift.struct_fid_not_in_seq", PI_PROTOCOL, PI_ERROR, "Missing mandatory field id in struct.", EXPFILL } },
3908
14
        { &ei_thrift_not_enough_data, { "thrift.not_enough_data", PI_PROTOCOL, PI_WARN, "Not enough data to decode.", EXPFILL } },
3909
14
        { &ei_thrift_frame_too_short, { "thrift.frame_too_short", PI_MALFORMED, PI_ERROR, "Thrift frame shorter than data.", EXPFILL } },
3910
14
        { &ei_thrift_frame_too_long, { "thrift.frame_too_long", PI_PROTOCOL, PI_WARN, "Thrift frame longer than data.", EXPFILL } },
3911
14
        { &ei_thrift_varint_too_large, { "thrift.varint_too_large", PI_PROTOCOL, PI_ERROR, "Thrift varint value too large for target integer type.", EXPFILL } },
3912
14
        { &ei_thrift_undefined_field_id, { "thrift.undefined_field_id", PI_PROTOCOL, PI_NOTE, "Field id not defined by sub-dissector, using generic Thrift dissector.", EXPFILL } },
3913
14
        { &ei_thrift_negative_field_id, { "thrift.negative_field_id", PI_PROTOCOL, PI_NOTE, "Encountered unexpected negative field id, possibly an old application.", EXPFILL } },
3914
14
        { &ei_thrift_unordered_field_id, { "thrift.unordered_field_id", PI_PROTOCOL, PI_WARN, "Field id not in strictly increasing order.", EXPFILL } },
3915
14
        { &ei_thrift_application_exception, { "thrift.application_exception", PI_PROTOCOL, PI_NOTE, "The application recognized the method but rejected the content.", EXPFILL } },
3916
14
        { &ei_thrift_protocol_exception, { "thrift.protocol_exception", PI_PROTOCOL, PI_WARN, "The application was not able to handle the request.", EXPFILL } },
3917
14
        { &ei_thrift_too_many_subtypes, { "thrift.too_many_subtypes", PI_PROTOCOL, PI_ERROR, "Too many level of sub-types nesting.", EXPFILL } },
3918
14
    };
3919
3920
3921
14
    module_t *thrift_module;
3922
14
    expert_module_t *expert_thrift;
3923
3924
3925
    /* Register protocol name and description */
3926
14
    proto_thrift = proto_register_protocol("Thrift Protocol", "Thrift", "thrift");
3927
3928
14
    expert_thrift = expert_register_protocol(proto_thrift);
3929
3930
    /* register field array */
3931
14
    proto_register_field_array(proto_thrift, hf, array_length(hf));
3932
3933
    /* register subtree array */
3934
14
    proto_register_subtree_array(ett, array_length(ett));
3935
3936
14
    expert_register_field_array(expert_thrift, ei, array_length(ei));
3937
3938
    /* register dissector */
3939
14
    thrift_handle = register_dissector("thrift", dissect_thrift_transport, proto_thrift);
3940
14
    thrift_http_handle = register_dissector("thrift.http", dissect_thrift_http, proto_thrift);
3941
3942
14
    thrift_module = prefs_register_protocol(proto_thrift, proto_reg_handoff_thrift);
3943
3944
14
    thrift_method_name_dissector_table = register_dissector_table("thrift.method_names", "Thrift Method names",
3945
14
        proto_thrift, FT_STRING, STRING_CASE_SENSITIVE); /* Thrift is case-sensitive. */
3946
3947
14
    prefs_register_enum_preference(thrift_module, "decode_binary",
3948
14
                                   "Display binary as bytes or strings",
3949
14
                                   "How the binary should be decoded",
3950
14
                                   &binary_decode, binary_display_options, false);
3951
3952
14
    prefs_register_uint_preference(thrift_module, "tls.port",
3953
14
                                   "Thrift TLS port",
3954
14
                                   "Thrift TLS port",
3955
14
                                   10, &thrift_tls_port);
3956
3957
14
    prefs_register_bool_preference(thrift_module, "show_internal",
3958
14
                                   "Show internal Thrift fields in the dissection tree",
3959
14
                                   "Whether the Thrift dissector should display Thrift internal fields for sub-dissectors.",
3960
14
                                   &show_internal_thrift_fields);
3961
3962
14
    prefs_register_bool_preference(thrift_module, "fallback_on_generic",
3963
14
                                   "Fallback to generic Thrift dissector if sub-dissector fails.",
3964
14
                                   "Whether the Thrift dissector should try to dissect the data if the sub-dissector failed."
3965
14
                                   " This option can be useful if the data is well-formed but the sub-dissector is expecting different type/content.",
3966
14
                                   &try_generic_if_sub_dissector_fails);
3967
3968
14
    prefs_register_uint_preference(thrift_module, "nested_type_depth",
3969
14
                                   "Thrift nested types depth",
3970
14
                                   "Maximum expected depth of nested types in the Thrift structures and containers."
3971
14
                                   " A Thrift-based protocol using no parameter and void return types only uses a depth of 0."
3972
14
                                   " A Thrift-based protocol using only simple types as parameters or return values uses a depth of 1.",
3973
14
                                   10, &nested_type_depth);
3974
3975
14
    prefs_register_bool_preference(thrift_module, "desegment_framed",
3976
14
                                   "Reassemble Framed Thrift messages spanning multiple TCP segments",
3977
14
                                   "Whether the Thrift dissector should reassemble framed messages spanning multiple TCP segments."
3978
14
                                   " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
3979
14
                                   &framed_desegment);
3980
14
}
3981
3982
void
3983
proto_reg_handoff_thrift(void)
3984
14
{
3985
14
    static unsigned saved_thrift_tls_port;
3986
14
    static bool thrift_initialized = false;
3987
3988
14
    if (!thrift_initialized) {
3989
14
        thrift_initialized = true;
3990
14
        heur_dissector_add("tcp", dissect_thrift_heur, "Thrift over TCP", "thrift_tcp", proto_thrift, HEURISTIC_ENABLE);
3991
14
        heur_dissector_add("udp", dissect_thrift_heur, "Thrift over UDP", "thrift_udp", proto_thrift, HEURISTIC_ENABLE);
3992
14
        heur_dissector_add("usb.bulk", dissect_thrift_heur, "Thrift over USB", "thrift_usb_bulk", proto_thrift, HEURISTIC_ENABLE);
3993
14
        dissector_add_for_decode_as_with_preference("tcp.port", thrift_handle);
3994
14
        dissector_add_for_decode_as_with_preference("udp.port", thrift_handle);
3995
14
        dissector_add_string("media_type", "application/x-thrift", thrift_http_handle); /* Obsolete but still in use. */
3996
14
        dissector_add_string("media_type", "application/vnd.apache.thrift.binary", thrift_http_handle); /* Officially registered. */
3997
14
    } else {
3998
0
        ssl_dissector_delete(saved_thrift_tls_port, thrift_handle);
3999
0
    }
4000
14
    ssl_dissector_add(thrift_tls_port, thrift_handle);
4001
14
    saved_thrift_tls_port = thrift_tls_port;
4002
14
}
4003
4004
/*
4005
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
4006
 *
4007
 * Local variables:
4008
 * c-basic-offset: 4
4009
 * tab-width: 8
4010
 * indent-tabs-mode: nil
4011
 * End:
4012
 *
4013
 * vi: set shiftwidth=4 tabstop=8 expandtab:
4014
 * :indentSize=4:tabSize=8:noTabs=true:
4015
 */