2 * Routines for thrift protocol dissection.
3 * Based on work by John Song <jsong@facebook.com> and
4 * Bill Fumerola <bill@facebook.com>
6 * https://github.com/andrewcox/wireshark-with-thrift-plugin/blob/wireshark-1.8.6-with-thrift-plugin/plugins/thrift/packet-thrift.cpp
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>
12 * Wireshark - Network traffic analyzer
13 * By Gerald Combs <gerald@wireshark.org>
14 * Copyright 1998 Gerald Combs
16 * SPDX-License-Identifier: GPL-2.0-or-later
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
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"
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. */
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.
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.
57 void proto_register_thrift(void);
58 void proto_reg_handoff_thrift(void);
60 #define THRIFT_BINARY_VERSION_VALUE_MASK 0x7fff
61 #define THRIFT_BINARY_VERSION_MASK 0xffff00f8
62 #define THRIFT_BINARY_MESSAGE_MASK 0x00000007
63 #define THRIFT_BINARY_VERSION_1 0x80010000
65 #define THRIFT_COMPACT_VERSION_VALUE_MASK 0x001f
66 #define THRIFT_COMPACT_VERSION_MASK 0xff1f
67 #define THRIFT_COMPACT_MESSAGE_MASK 0x00e0
68 #define THRIFT_COMPACT_VERSION_1 0x8201
69 #define THRIFT_COMPACT_MESSAGE_SHIFT 5
71 #define NOT_A_VALID_PDU (0)
73 #define ABORT_SUBDISSECTION_ON_ISSUE(offset) do { if (offset < 0) return offset; } while (0)
75 #define ABORT_ON_INCOMPLETE_PDU(len) do {\
76 if (tvb_reported_length_remaining(tvb, *offset) < (len)) {\
77 /* Do not indicate the incomplete data if we know the above dissector is able to reassemble. */\
78 if (pinfo->can_desegment <= 0) \
79 expert_add_info(pinfo, NULL, &ei_thrift_not_enough_data);\
80 /* Do not consume more than available for the reassembly to work. */\
81 thrift_opt->reassembly_offset = *offset;\
82 thrift_opt->reassembly_length = len;\
83 *offset = THRIFT_REQUEST_REASSEMBLY;\
84 return THRIFT_REQUEST_REASSEMBLY;\
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
;
92 static bool show_internal_thrift_fields
;
93 static bool try_generic_if_sub_dissector_fails
;
94 static unsigned nested_type_depth
= 25;
96 static dissector_table_t thrift_method_name_dissector_table
;
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 int 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 int 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 int 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. */
121 /* TCompactProtocol elements length when different from TBinaryProtocol.
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)
126 * - T_DOUBLE (endianness is inverted, though)
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 #define TCP_THRIFT_MAX_I16_LEN (3)
137 #define TCP_THRIFT_MAX_I32_LEN (5)
138 #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 int TCP_THRIFT_MIN_MESSAGE_LEN
= 5; /* Protocol id + (Method type + Version) + Name length [+ Name] + Sequence Identifier + T_STOP */
142 static const uint32_t TCP_THRIFT_NIBBLE_MASK
= 0xf;
144 static const int OCTETS_TO_BITS_SHIFT
= 3; /* 8 bits per octets = 3 shifts left. */
145 static const int DISABLE_SUBTREE
= -1;
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_uuid
;
167 static int hf_thrift_binary
;
168 static int hf_thrift_string
;
169 static int hf_thrift_struct
;
170 static int hf_thrift_list
;
171 static int hf_thrift_set
;
172 static int hf_thrift_map
;
173 static int hf_thrift_num_list_item
;
174 static int hf_thrift_num_list_pos
;
175 static int hf_thrift_num_set_item
;
176 static int hf_thrift_num_set_pos
;
177 static int hf_thrift_num_map_item
;
178 static int hf_thrift_large_container
;
179 static int hf_thrift_double
;
180 static int hf_thrift_exception
;
181 static int hf_thrift_exception_message
;
182 static int hf_thrift_exception_type
;
184 static int ett_thrift
;
185 static int ett_thrift_header
;
186 static int ett_thrift_params
;
187 static int ett_thrift_field
;
188 static int ett_thrift_struct
;
189 static int ett_thrift_list
;
190 static int ett_thrift_set
;
191 static int ett_thrift_map
;
192 static int ett_thrift_error
; /* Error while reading the header. */
193 static int ett_thrift_exception
; /* ME_THRIFT_T_EXCEPTION */
195 static expert_field ei_thrift_wrong_type
;
196 static expert_field ei_thrift_wrong_field_id
;
197 static expert_field ei_thrift_negative_length
;
198 static expert_field ei_thrift_wrong_proto_version
;
199 static expert_field ei_thrift_struct_fid_not_in_seq
;
200 static expert_field ei_thrift_frame_too_short
;
201 static expert_field ei_thrift_not_enough_data
;
202 static expert_field ei_thrift_frame_too_long
;
203 static expert_field ei_thrift_varint_too_large
;
204 static expert_field ei_thrift_undefined_field_id
;
205 static expert_field ei_thrift_negative_field_id
;
206 static expert_field ei_thrift_unordered_field_id
;
207 static expert_field ei_thrift_application_exception
;
208 static expert_field ei_thrift_protocol_exception
;
209 static expert_field ei_thrift_too_many_subtypes
;
211 static const thrift_member_t thrift_exception
[] = {
212 { &hf_thrift_exception_message
, 1, true, DE_THRIFT_T_BINARY
, NULL
, { .encoding
= ENC_UTF_8
}, NULL
},
213 { &hf_thrift_exception_type
, 2, false, DE_THRIFT_T_I32
, TMFILL
},
214 { NULL
, 0, false, DE_THRIFT_T_STOP
, TMFILL
}
218 DE_THRIFT_C_STOP
= DE_THRIFT_T_STOP
,
219 DE_THRIFT_C_BOOL_TRUE
,
220 DE_THRIFT_C_BOOL_FALSE
,
232 } thrift_compact_type_enum_t
;
234 typedef struct _thrift_field_header_t
{
236 thrift_type_enum_t binary
;
237 thrift_compact_type_enum_t compact
;
246 } thrift_field_header_t
;
248 static const value_string thrift_type_vals
[] = {
249 { DE_THRIFT_T_STOP
, "T_STOP" },
250 { DE_THRIFT_T_VOID
, "T_VOID" },
251 { DE_THRIFT_T_BOOL
, "T_BOOL" },
252 { DE_THRIFT_T_I8
, "T_I8" },
253 { DE_THRIFT_T_DOUBLE
, "T_DOUBLE" },
254 { DE_THRIFT_T_I16
, "T_I16" },
255 { DE_THRIFT_T_I32
, "T_I32" },
256 { DE_THRIFT_T_I64
, "T_I64" },
257 { DE_THRIFT_T_BINARY
, "T_BINARY" },
258 { DE_THRIFT_T_STRUCT
, "T_STRUCT" },
259 { DE_THRIFT_T_MAP
, "T_MAP" },
260 { DE_THRIFT_T_SET
, "T_SET" },
261 { DE_THRIFT_T_LIST
, "T_LIST" },
262 { DE_THRIFT_T_UUID
, "T_UUID" },
266 /* type values used within structs in the compact protocol */
267 static const value_string thrift_compact_type_vals
[] = {
268 { DE_THRIFT_C_BOOL_TRUE
, "BOOLEAN_TRUE" },
269 { DE_THRIFT_C_BOOL_FALSE
, "BOOLEAN_FALSE" },
270 { DE_THRIFT_C_I8
, "T_I8" },
271 { DE_THRIFT_C_I16
, "T_I16" },
272 { DE_THRIFT_C_I32
, "T_I32" },
273 { DE_THRIFT_C_I64
, "T_I64" },
274 { DE_THRIFT_C_DOUBLE
, "T_DOUBLE" },
275 { DE_THRIFT_C_BINARY
, "T_BINARY" },
276 { DE_THRIFT_C_LIST
, "T_LIST" },
277 { DE_THRIFT_C_SET
, "T_SET" },
278 { DE_THRIFT_C_MAP
, "T_MAP" },
279 { DE_THRIFT_C_STRUCT
, "T_STRUCT" },
280 { DE_THRIFT_C_UUID
, "T_UUID" },
284 static const value_string thrift_exception_type_vals
[] = {
285 { 0, "Unknown (type of peer)" },
286 { 1, "Unknown Method" },
287 { 2, "Invalid Message Type" },
288 { 3, "Wrong Method Name" },
289 { 4, "Bad Sequence Id" },
290 { 5, "Missing Result" },
291 { 6, "Internal Error" },
292 { 7, "Protocol Error (something went wrong during decoding)" },
293 { 8, "Invalid Transform" },
294 { 9, "Invalid Protocol" },
295 { 10, "Unsupported Client Type" },
299 static const value_string thrift_proto_vals
[] = {
300 { 0x80, "Strict Binary Protocol" },
301 { 0x82, "Compact Protocol" },
305 static const value_string thrift_mtype_vals
[] = {
306 { ME_THRIFT_T_CALL
, "CALL" },
307 { ME_THRIFT_T_REPLY
, "REPLY" },
308 { ME_THRIFT_T_EXCEPTION
, "EXCEPTION" },
309 { ME_THRIFT_T_ONEWAY
, "ONEWAY" },
314 #define DECODE_BINARY_AS_AUTO_UTF8 0
315 #define DECODE_BINARY_AS_BINARY 1
316 #define DECODE_BINARY_AS_ASCII 2
317 #define DECODE_BINARY_AS_UTF8 3
318 #define DECODE_BINARY_AS_UTF16BE 4
319 #define DECODE_BINARY_AS_UTF16LE 5
320 #define DECODE_BINARY_AS_UTF32BE 6
321 #define DECODE_BINARY_AS_UTF32LE 7
323 static int32_t binary_decode
= DECODE_BINARY_AS_AUTO_UTF8
;
325 static const enum_val_t binary_display_options
[] = {
326 { "auto", "UTF-8 if printable", DECODE_BINARY_AS_AUTO_UTF8
},
327 { "hexadecimal", "Binary (hexadecimal string)", DECODE_BINARY_AS_BINARY
},
328 { "ascii", "ASCII String", DECODE_BINARY_AS_ASCII
},
329 { "utf8", "UTF-8 String", DECODE_BINARY_AS_UTF8
},
330 { "utf16be", "UTF-16 Big Endian", DECODE_BINARY_AS_UTF16BE
},
331 { "utf16le", "UTF-16 Little Endian", DECODE_BINARY_AS_UTF16LE
},
332 { "utf32be", "UTF-32 Big Endian", DECODE_BINARY_AS_UTF32BE
},
333 { "utf32le", "UTF-32 Little Endian", DECODE_BINARY_AS_UTF32LE
},
337 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
);
338 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
);
339 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 /*=====BEGIN GENERIC HELPERS=====*/
342 /* Check that the 4-byte value match a Thrift Strict TBinaryProtocol version
343 * - 0x8001 The version itself
344 * - 0x?? An undetermined byte (not used)
345 * - 0x0m The method type between 1 and 4.
346 * Values above 4 will be accepted if ignore_msg_type is true.
349 is_thrift_strict_version(uint32_t header
, bool ignore_msg_type
)
352 if ((header
& THRIFT_BINARY_VERSION_MASK
) == THRIFT_BINARY_VERSION_1
) {
353 if (ignore_msg_type
) {
356 msg_type
= (header
& THRIFT_BINARY_MESSAGE_MASK
);
357 if ((ME_THRIFT_T_CALL
<= msg_type
) && (msg_type
<= ME_THRIFT_T_ONEWAY
)) {
364 /* Check that the 2-byte value match a Thrift TCompactProtocol version
365 * - 0x82 The protocol id.
366 * - 0bmmmvvvvv The method on the 3 MSbits and version on the 5 LSbits.
369 is_thrift_compact_version(uint16_t header
, bool ignore_msg_type
)
372 if ((header
& THRIFT_COMPACT_VERSION_MASK
) == THRIFT_COMPACT_VERSION_1
) {
373 if (ignore_msg_type
) {
376 msg_type
= (header
& THRIFT_COMPACT_MESSAGE_MASK
) >> THRIFT_COMPACT_MESSAGE_SHIFT
;
377 if ((ME_THRIFT_T_CALL
<= msg_type
) && (msg_type
<= ME_THRIFT_T_ONEWAY
)) {
385 * Check that the string at the designed position is valid UTF-8.
386 * This allows us to fail early if the length of the string seems very long.
387 * This /can/ indicate that this packet does not contain a Thrift PDU.
389 * This method does /NOT/ check if the data is available, the caller must check that if needed.
390 * - Heuristic for method name must check for captured length.
391 * - Check UTF-8 vs. binary before adding to tree must check for reported length.
394 thrift_binary_utf8_isprint(tvbuff_t
*tvb
, int offset
, int max_len
, bool accept_crlf
)
396 int check_len
= tvb_reported_length_remaining(tvb
, offset
);
397 int pos
, remaining
= 0; /* position in tvb, remaining bytes for multi-byte characters. */
398 uint8_t min_next
= 0x80, max_next
= 0xBF;
400 int printable_len
= 0; /* In case the string ends with several NUL bytes. */
401 if (max_len
< check_len
) {
404 for (pos
= offset
; pos
< offset
+ check_len
; pos
++) {
405 uint8_t current
= tvb_get_uint8(tvb
, pos
);
410 } else if (remaining
== 0) {
411 /* We are at the beginning of a character. */
414 continue; /* Avoid counting this NUL byte as printable. */
415 } else if ((current
& 0x80) == 0) {
416 if (!g_ascii_isprint(current
)) {
418 /* New line and chariot return or not valid in the method name */
421 if (current
!= '\r' && current
!= '\n') {
422 /* But would have been acceptable for data content */
426 } else if ((current
& 0xE0) == 0xC0) {
427 /* 2 bytes code 8 to 11 bits */
428 if (current
>= 0xC2) {
432 /* Overlong encoding of ASCII for C0 and C1. */
435 } else if ((current
& 0xF0) == 0xE0) {
436 /* 3 bytes code 12 to 16 bits */
438 if (current
== 0xE0) {
439 min_next
= 0xA0; /* 0b101x xxxx to code at least 12 bits. */
441 if (current
== 0xED) {
442 /* Reject reserved UTF-16 surrogates as specified for UTF-8. */
447 } else if ((current
& 0xF8) == 0xF0) {
448 /* 4 bytes code 17 to 21 bits */
450 if (current
== 0xF0) {
451 min_next
= 0x90; /* 0b1001 xxxx to code at least 17 bits. */
452 } else if (current
> 0xF4) {
453 /* Invalid leading byte (above U+10FFFF). */
459 /* Not the beginning of an UTF-8 character. */
464 if ((current
< min_next
) || (max_next
< current
)) {
465 /* Not a canonical UTF-8 character continuation. */
474 return printable_len
;
477 /** Simple wrapper around tvb_get_varint to handle reassembly.
479 * @param[in] tvb Pointer to the tvbuff_t holding the captured data.
480 * @param[in] pinfo Pointer to the packet_info holding information about the currently dissected packet.
481 * @param[in] tree Pointer to the proto_tree used to hold the display tree in Wireshark's interface.
482 * @param[in] offset Offset from the beginning of the tvbuff_t where the Thrift field is. Function will dissect type, id, & data.
483 * @param[in] max_length The expected maximum encoding length of the integer.
484 * @param[in] value If parsing succeeds, parsed varint will be stored here.
485 * @param[in] encoding The ENC_* that defines the format (e.g., ENC_VARINT_PROTOBUF or ENC_VARINT_ZIGZAG).
487 * @return THRIFT_REQUEST_REASSEMBLY(-1) if reassembly is necessary,
488 * 0 in case of error,
489 * a positive value indicating the length of the varint otherwise.
492 thrift_get_varint_enc(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int offset
, int max_length
, uint64_t *value
, const unsigned encoding
)
495 int readable
= tvb_reported_length_remaining(tvb
, offset
);
497 return THRIFT_REQUEST_REASSEMBLY
;
499 if (readable
> TCP_THRIFT_MAX_I64_LEN
) {
500 readable
= TCP_THRIFT_MAX_I64_LEN
;
502 length
= tvb_get_varint(tvb
, offset
, readable
, value
, encoding
);
504 if (readable
< max_length
) {
505 /* There was not enough data to ensure the varint is complete for the expected size of integer. */
506 return THRIFT_REQUEST_REASSEMBLY
;
508 /* Either an error on the wire or a sub-optimal encoding, we always consider it as an error. */
509 proto_tree_add_expert(tree
, pinfo
, &ei_thrift_varint_too_large
, tvb
, offset
, max_length
);
516 is_thrift_compact_bool_type(thrift_compact_type_enum_t type
)
518 return type
== DE_THRIFT_C_BOOL_TRUE
|| type
== DE_THRIFT_C_BOOL_FALSE
;
521 /* Function that reads the field header and return all associated data.
523 * @param[in] tvb: Pointer to the tvbuff_t holding the captured data.
524 * @param[in] pinfo: Pointer to the packet_info holding information about the currently dissected packet.
525 * @param[in] tree: Pointer to the proto_tree used to hold the display tree in Wireshark's interface.
526 * The caller may set it to NULL to prevent the creation of the field header sub-tree.
527 * This possibility is used by sub-dissector when show_internal_thrift_fields is false,
528 * and by dissect_thrift_common to differentiate between successful and exception T_REPLY.
529 * @param[in] offset: Offset from the beginning of the tvbuff_t where the Thrift field is. Function will dissect type, id, & data.
530 * @param[in] thrift_opt: Options from the Thrift dissector that will be necessary for sub-dissection (binary vs. compact, ...)
531 * @param[in] gen_bool: Generate item when one of the boolean types is encountered.
533 * @return See "GENERIC DISSECTION PARAMETERS DOCUMENTATION".
536 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
)
539 * Binary protocol field header (3 bytes):
540 * +--------+--------+--------+
541 * |0000tttt| field id |
542 * +--------+--------+--------+
544 * Compact protocol field header (1 byte, short form):
549 * Compact protocol field header (2 to 4 bytes, long form):
550 * +--------+--------+...+--------+
551 * |0000tttt| field id |
552 * +--------+--------+...+--------+
554 * Binary & Compact protocol stop field (1 byte):
560 * 'dddd' is the field id delta, a strictly positive unsigned 4 bits integer.
561 * 'tttt' is the type of the field value, an unsigned 4 bits strictly positive integer.
562 * field id is the numerical value of the field in the structure.
565 DISSECTOR_ASSERT(header
!= NULL
);
567 ABORT_SUBDISSECTION_ON_ISSUE(*offset
); /* In case of sub-dissection functions. */
568 ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_TYPE_LEN
); /* In all dissection cases. */
570 uint8_t dfid_type
= tvb_get_uint8(tvb
, *offset
);
571 int32_t delta
= TCP_THRIFT_DELTA_NOT_SET
;
574 memset(header
, 0, sizeof(thrift_field_header_t
));
576 /* Read type (and delta for Compact) */
577 header
->type_offset
= *offset
;
578 *offset
+= TBP_THRIFT_TYPE_LEN
;
580 if (dfid_type
== DE_THRIFT_T_STOP
) {
581 header
->type
.binary
= (thrift_type_enum_t
)dfid_type
;
582 /* No need for sub-tree in this case. */
583 header
->type_pi
= proto_tree_add_item(tree
, hf_thrift_type
, tvb
, header
->type_offset
, TBP_THRIFT_TYPE_LEN
, ENC_BIG_ENDIAN
);
587 /* Read the field id */
588 if (thrift_opt
->tprotocol
& PROTO_THRIFT_COMPACT
) {
589 header
->type
.compact
= (thrift_compact_type_enum_t
)(dfid_type
& TCP_THRIFT_NIBBLE_MASK
);
590 delta
= (dfid_type
>> TCP_THRIFT_NIBBLE_SHIFT
) & TCP_THRIFT_NIBBLE_MASK
;
591 if (delta
== TCP_THRIFT_DELTA_NOT_SET
) {
592 header
->fid_offset
= *offset
;
593 header
->fid_length
= thrift_get_varint_enc(tvb
, pinfo
, NULL
, *offset
, TCP_THRIFT_MAX_I16_LEN
, &fid
, ENC_VARINT_ZIGZAG
);
594 switch (header
->fid_length
) {
595 case THRIFT_REQUEST_REASSEMBLY
:
596 /* Will always return after setting the expert parts. */
597 ABORT_ON_INCOMPLETE_PDU(TCP_THRIFT_MAX_I16_LEN
);
598 return THRIFT_REQUEST_REASSEMBLY
; // Just to avoid a false positive warning.
599 case 0: /* In case of error, the offset stay at the error position. */
601 header
->field_id
= fid
;
602 *offset
+= header
->fid_length
;
606 /* The field id data in the tvb is represented by the delta with the type. */
607 header
->field_id
= thrift_opt
->previous_field_id
+ delta
;
608 header
->fid_offset
= header
->type_offset
;
609 header
->fid_length
= TBP_THRIFT_TYPE_LEN
;
612 /* Fixed size field id for Binary protocol. */
613 ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_FID_LEN
);
614 header
->type
.binary
= (thrift_type_enum_t
)dfid_type
;
615 header
->field_id
= tvb_get_ntohis(tvb
, *offset
);
616 header
->fid_offset
= *offset
;
617 header
->fid_length
= TBP_THRIFT_FID_LEN
;
618 *offset
+= TBP_THRIFT_FID_LEN
;
621 /* Create the field header sub-tree if requested only. */
623 unsigned nested_count
= p_get_proto_depth(pinfo
, proto_thrift
);
624 if (nested_count
>= thrift_opt
->nested_type_depth
) {
625 expert_add_info(pinfo
, proto_tree_get_parent(tree
), &ei_thrift_too_many_subtypes
);
626 return THRIFT_REQUEST_REASSEMBLY
;
629 header
->fh_tree
= proto_tree_add_subtree_format(tree
, tvb
, header
->type_offset
, *offset
- header
->type_offset
, ett_thrift_field
, NULL
,
630 "Field Header #%" PRId64
, header
->field_id
);
631 if (thrift_opt
->tprotocol
& PROTO_THRIFT_COMPACT
) {
632 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
);
633 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
);
634 if (delta
== TCP_THRIFT_DELTA_NOT_SET
) {
635 proto_item_append_text(header
->fid_pi
, " (Not Set)");
637 if (gen_bool
&& is_thrift_compact_bool_type(header
->type
.compact
)) {
638 proto_item
*bool_item
= proto_tree_add_boolean(tree
, hf_thrift_bool
, tvb
, header
->type_offset
, TBP_THRIFT_TYPE_LEN
, 2 - header
->type
.compact
);
639 proto_item_set_generated(bool_item
);
641 if (gen_bool
&& is_thrift_compact_bool_type(header
->type
.compact
)) {
642 proto_item
*bool_item
= proto_tree_add_boolean(tree
, hf_thrift_bool
, tvb
, header
->type_offset
, TBP_THRIFT_TYPE_LEN
, 2 - header
->type
.compact
);
643 proto_item_set_generated(bool_item
);
646 header
->type_pi
= proto_tree_add_item(header
->fh_tree
, hf_thrift_type
, tvb
, header
->type_offset
, TBP_THRIFT_TYPE_LEN
, ENC_BIG_ENDIAN
);
648 if (delta
== TCP_THRIFT_DELTA_NOT_SET
) {
649 if (header
->fid_length
> 0) {
650 header
->fid_pi
= proto_tree_add_item(header
->fh_tree
, hf_thrift_fid
, tvb
, header
->fid_offset
, header
->fid_length
, ENC_BIG_ENDIAN
);
652 /* Varint for field id was too long to decode, handle the error in the sub-tree. */
653 proto_tree_add_expert(header
->fh_tree
, pinfo
, &ei_thrift_varint_too_large
, tvb
, header
->fid_offset
, TCP_THRIFT_MAX_I16_LEN
);
654 return THRIFT_REQUEST_REASSEMBLY
;
657 if ((int64_t)INT16_MIN
> header
->field_id
|| header
->field_id
> (int64_t)INT16_MAX
) {
658 header
->fid_pi
= proto_tree_add_int64(header
->fh_tree
, hf_thrift_i64
, tvb
, header
->fid_offset
, header
->fid_length
, header
->field_id
);
659 expert_add_info(pinfo
, header
->fid_pi
, &ei_thrift_varint_too_large
);
660 /* We continue anyway as the field id was displayed successfully. */
662 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
);
664 proto_item_set_generated(header
->fid_pi
);
666 /* When reading a successful T_REPLY, we always have
667 * - previous_field_id == 0 because we are at the beginning of a structure, and
668 * - header->field_id == 0 because it is the return value
669 * so we need to ignore this case. */
670 if (header
->field_id
< thrift_opt
->previous_field_id
|| (header
->field_id
== thrift_opt
->previous_field_id
&& thrift_opt
->previous_field_id
!= 0)) {
671 if (thrift_opt
->previous_field_id
== 0) {
672 // Maybe an old application from when negative values were authorized.
673 expert_add_info(pinfo
, header
->fid_pi
, &ei_thrift_negative_field_id
);
675 // Although not mandated by Thrift protocol, applications should send fields in numerical order.
676 expert_add_info(pinfo
, header
->fid_pi
, &ei_thrift_unordered_field_id
);
680 /* The fid_pi value (proto_item for field_id) is required for sub-dissectors.
681 * Create it even in the absence of tree. */
682 if (delta
== TCP_THRIFT_DELTA_NOT_SET
) {
683 if (header
->fid_length
> 0) {
684 header
->fid_pi
= proto_tree_add_item(header
->fh_tree
, hf_thrift_fid
, tvb
, header
->fid_offset
, header
->fid_length
, ENC_BIG_ENDIAN
);
686 /* Varint for field id was too long to decode, handle the error without the sub-tree. */
687 proto_tree_add_expert(tree
, pinfo
, &ei_thrift_varint_too_large
, tvb
, header
->fid_offset
, TCP_THRIFT_MAX_I16_LEN
);
688 return THRIFT_REQUEST_REASSEMBLY
;
691 if ((int64_t)INT16_MIN
> header
->field_id
|| header
->field_id
> (int64_t)INT16_MAX
) {
692 header
->fid_pi
= proto_tree_add_int64(header
->fh_tree
, hf_thrift_i64
, tvb
, header
->fid_offset
, header
->fid_length
, header
->field_id
);
693 expert_add_info(pinfo
, header
->fid_pi
, &ei_thrift_varint_too_large
);
694 /* We continue anyway as the field id was displayed successfully. */
696 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
);
698 proto_item_set_generated(header
->fid_pi
);
705 /* Dissect a varint and add it to the display tree with the requested hf_id.
706 * This function is used by both generic compact dissector and sub-dissector.
708 * @param[in] tvb: Pointer to the tvbuff_t holding the captured data.
709 * @param[in] pinfo: Pointer to the packet_info holding information about the currently dissected packet.
710 * @param[in] tree: Pointer to the proto_tree used to hold the display tree in Wireshark's interface.
711 * @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.
712 * The offset is modified according to table in "GENERIC DISSECTION PARAMETERS DOCUMENTATION" section.
713 * @param[in] thrift_opt: Options from the Thrift dissector that will be necessary for sub-dissection (binary vs. compact, ...)
715 * @param[in] max_length: Expected maximum length of the data that encodes the integer.
716 * This is only used to check if reassembly is necessary as with enough data and sub-optimal encoding,
717 * this function will happily dissect the value anyway.
718 * @param[in] hf_id: The hf_id that needs to be used for the display.
719 * If the found integer is larger that the expected integer size (driven by max_length parameter),
720 * the integer will always be displayed as a generic T_I64 and an expert info will be added.
721 * @param[in] raw_dissector Dissector for raw integer (we need to create a tvbuff_t with the flatten integer.
723 * @return See "GENERIC DISSECTION PARAMETERS DOCUMENTATION".
726 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
)
730 int length
= thrift_get_varint_enc(tvb
, pinfo
, tree
, *offset
, max_length
, &varint
, ENC_VARINT_ZIGZAG
);
732 case THRIFT_REQUEST_REASSEMBLY
:
733 /* Will always return after setting the expert parts. */
734 ABORT_ON_INCOMPLETE_PDU(max_length
);
735 return THRIFT_REQUEST_REASSEMBLY
; // Just to avoid a false positive warning.
737 /* In case of error, the offset stay at the error position. */
738 return THRIFT_REQUEST_REASSEMBLY
;
740 switch (max_length
) {
741 case TCP_THRIFT_MAX_I16_LEN
:
742 if ((int64_t)INT16_MIN
> varint
|| varint
> (int64_t)INT16_MAX
) {
743 pi
= proto_tree_add_int64(tree
, hf_thrift_i64
, tvb
, *offset
, length
, varint
);
744 expert_add_info(pinfo
, pi
, &ei_thrift_varint_too_large
);
745 /* We continue anyway as the varint was indeed decoded. */
747 if (raw_dissector
!= NULL
) {
748 uint8_t *data
= wmem_alloc(wmem_packet_scope(), TBP_THRIFT_I16_LEN
);
749 data
[0] = (varint
>> 8) & 0xFF;
750 data
[1] = varint
& 0xFF;
751 tvbuff_t
* sub_tvb
= tvb_new_child_real_data(tvb
, data
, TBP_THRIFT_I16_LEN
, TBP_THRIFT_I16_LEN
);
752 thrift_opt
->use_std_dissector
= false;
753 raw_dissector(sub_tvb
, pinfo
, tree
, thrift_opt
);
755 if (thrift_opt
->use_std_dissector
) {
756 proto_tree_add_int(tree
, hf_id
, tvb
, *offset
, length
, (int16_t)varint
);
760 case TCP_THRIFT_MAX_I32_LEN
:
761 if ((int64_t)INT32_MIN
> varint
|| varint
> (int64_t)INT32_MAX
) {
762 pi
= proto_tree_add_int64(tree
, hf_thrift_i64
, tvb
, *offset
, length
, varint
);
763 expert_add_info(pinfo
, pi
, &ei_thrift_varint_too_large
);
764 /* We continue anyway as the varint was indeed decoded. */
766 if (raw_dissector
!= NULL
) {
767 uint8_t *data
= wmem_alloc(wmem_packet_scope(), TBP_THRIFT_I32_LEN
);
768 data
[0] = (varint
>> 24) & 0xFF;
769 data
[1] = (varint
>> 16) & 0xFF;
770 data
[2] = (varint
>> 8) & 0xFF;
771 data
[3] = varint
& 0xFF;
772 tvbuff_t
* sub_tvb
= tvb_new_child_real_data(tvb
, data
, TBP_THRIFT_I32_LEN
, TBP_THRIFT_I32_LEN
);
773 thrift_opt
->use_std_dissector
= false;
774 raw_dissector(sub_tvb
, pinfo
, tree
, thrift_opt
);
776 if (thrift_opt
->use_std_dissector
) {
777 proto_tree_add_int(tree
, hf_id
, tvb
, *offset
, length
, (int32_t)varint
);
781 case TCP_THRIFT_MAX_I64_LEN
:
783 if (raw_dissector
!= NULL
) {
784 uint8_t *data
= wmem_alloc(wmem_packet_scope(), TBP_THRIFT_I64_LEN
);
785 data
[0] = (varint
>> 56) & 0xFF;
786 data
[1] = (varint
>> 48) & 0xFF;
787 data
[2] = (varint
>> 40) & 0xFF;
788 data
[3] = (varint
>> 32) & 0xFF;
789 data
[4] = (varint
>> 24) & 0xFF;
790 data
[5] = (varint
>> 16) & 0xFF;
791 data
[6] = (varint
>> 8) & 0xFF;
792 data
[7] = varint
& 0xFF;
793 tvbuff_t
* sub_tvb
= tvb_new_child_real_data(tvb
, data
, TBP_THRIFT_I64_LEN
, TBP_THRIFT_I64_LEN
);
794 thrift_opt
->use_std_dissector
= false;
795 raw_dissector(sub_tvb
, pinfo
, tree
, thrift_opt
);
797 if (thrift_opt
->use_std_dissector
) {
798 proto_tree_add_int64(tree
, hf_id
, tvb
, *offset
, length
, varint
);
808 /* Common function used by both Binary and Compact generic dissectors to dissect T_BINARY fields
809 * as requested in the dissector preferences.
810 * This function only dissects the data, not the field header nor the length.
813 dissect_thrift_string_as_preferred(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
, thrift_option_data_t
*thrift_opt
, int str_len
)
815 ABORT_ON_INCOMPLETE_PDU(str_len
); /* Thrift assumes there will never be binary/string >= 2GiB */
818 switch (binary_decode
) {
819 case DECODE_BINARY_AS_UTF32LE
:
820 proto_tree_add_item(tree
, hf_thrift_string
, tvb
, *offset
, str_len
, ENC_UCS_4
| ENC_LITTLE_ENDIAN
);
822 case DECODE_BINARY_AS_UTF32BE
:
823 proto_tree_add_item(tree
, hf_thrift_string
, tvb
, *offset
, str_len
, ENC_UCS_4
| ENC_BIG_ENDIAN
);
825 case DECODE_BINARY_AS_UTF16LE
:
826 proto_tree_add_item(tree
, hf_thrift_string
, tvb
, *offset
, str_len
, ENC_UTF_16
| ENC_LITTLE_ENDIAN
);
828 case DECODE_BINARY_AS_UTF16BE
:
829 proto_tree_add_item(tree
, hf_thrift_string
, tvb
, *offset
, str_len
, ENC_UTF_16
| ENC_BIG_ENDIAN
);
831 case DECODE_BINARY_AS_UTF8
:
832 proto_tree_add_item(tree
, hf_thrift_string
, tvb
, *offset
, str_len
, ENC_UTF_8
);
834 case DECODE_BINARY_AS_ASCII
:
835 proto_tree_add_item(tree
, hf_thrift_string
, tvb
, *offset
, str_len
, ENC_ASCII
);
837 case DECODE_BINARY_AS_AUTO_UTF8
:
838 /* When there is no data at all, consider it a string
839 * but a buffer containing only NUL bytes is a binary.
840 * If not entirely captured, consider it as a binary. */
841 if (tvb_captured_length_remaining(tvb
, *offset
) >= str_len
&&
842 (str_len
== 0 || thrift_binary_utf8_isprint(tvb
, *offset
, str_len
, true) > 0)) {
843 proto_tree_add_item(tree
, hf_thrift_string
, tvb
, *offset
, str_len
, ENC_UTF_8
);
846 /* otherwise, continue with type BINARY */
848 case DECODE_BINARY_AS_BINARY
:
850 proto_tree_add_item(tree
, hf_thrift_binary
, tvb
, *offset
, str_len
, ENC_NA
);
859 // Converts the type value in TCompactProtocol to the equivalent standard
860 // value from TBinaryProtocol.
861 static thrift_type_enum_t
862 compact_struct_type_to_generic_type(thrift_compact_type_enum_t compact
)
865 case DE_THRIFT_C_STOP
:
866 return DE_THRIFT_T_STOP
;
867 case DE_THRIFT_C_BOOL_TRUE
:
868 case DE_THRIFT_C_BOOL_FALSE
:
869 return DE_THRIFT_T_BOOL
;
871 return DE_THRIFT_T_I8
;
872 case DE_THRIFT_C_I16
:
873 return DE_THRIFT_T_I16
;
874 case DE_THRIFT_C_I32
:
875 return DE_THRIFT_T_I32
;
876 case DE_THRIFT_C_I64
:
877 return DE_THRIFT_T_I64
;
878 case DE_THRIFT_C_DOUBLE
:
879 return DE_THRIFT_T_DOUBLE
;
880 case DE_THRIFT_C_BINARY
:
881 return DE_THRIFT_T_BINARY
;
882 case DE_THRIFT_C_LIST
:
883 return DE_THRIFT_T_LIST
;
884 case DE_THRIFT_C_SET
:
885 return DE_THRIFT_T_SET
;
886 case DE_THRIFT_C_MAP
:
887 return DE_THRIFT_T_MAP
;
888 case DE_THRIFT_C_STRUCT
:
889 return DE_THRIFT_T_STRUCT
;
890 case DE_THRIFT_C_UUID
:
891 return DE_THRIFT_T_UUID
;
893 return DE_THRIFT_T_VOID
;
896 /*=====END GENERIC HELPERS=====*/
898 /*=====BEGIN SUB-DISSECTION=====*/
900 * Helper functions to use within custom sub-dissectors.
903 * 1. If is_field is true, dissect the field header (field type + field id).
904 * 1.a. Check the type in the PDU against the expected one according to the call.
905 * 2. If requested, add the type and field id to the tree (internal thrift fields).
906 * 3. Dissect the value of the field.
908 * Return the offset marking the end of the dissected value or a negative error code.
909 * See packet-thrift.h for details.
913 * See packet-thrift.h for parameters documentation of dissect_thrift_t_<type> functions.
916 dissect_thrift_t_stop(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int offset
)
918 ABORT_SUBDISSECTION_ON_ISSUE(offset
);
919 if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_TYPE_LEN
) {
920 return THRIFT_REQUEST_REASSEMBLY
;
923 if (tvb_get_uint8(tvb
, offset
) != DE_THRIFT_T_STOP
) {
924 proto_tree_add_expert(tree
, pinfo
, &ei_thrift_wrong_type
, tvb
, offset
, TBP_THRIFT_TYPE_LEN
);
925 return THRIFT_SUBDISSECTOR_ERROR
;
927 if (show_internal_thrift_fields
) {
928 proto_tree_add_item(tree
, hf_thrift_type
, tvb
, offset
, TBP_THRIFT_TYPE_LEN
, ENC_BIG_ENDIAN
);
930 offset
+= TBP_THRIFT_TYPE_LEN
;
935 /* Common function used by all sub-dissection functions to handle the field header dissection as well as the display.
937 * @param[in] tvb: Pointer to the tvbuff_t holding the captured data.
938 * @param[in] pinfo: Pointer to the packet_info holding information about the currently dissected packet.
939 * @param[in] tree: Pointer to the proto_tree used to hold the display tree in Wireshark's interface.
940 * @param[in] offset: Offset from the beginning of the tvbuff_t where the Thrift field is. Function will dissect type, id, & data.
941 * @param[in] thrift_opt: Options from the Thrift dissector that will be necessary for sub-dissection (binary vs. compact, ...)
943 * @param[in] expected: The type expected by the caller sub-dissector.
944 * Note: the generic type is used even in case of compact dissection.
946 * @param[in] field_id: Thrift field identifier, to check that the right field is being dissected (in case of optional fields).
948 * @param[out] header_tree: Optional pointer to a proto_tree pointer.
949 * If not NULL, the field header sub-tree will be set in this variable.
950 * Used by binary/string sub-dissector to put the length in this field header as well.
952 * @return Offset of the first non-dissected byte in case of success,
953 * THRIFT_REQUEST_REASSEMBLY (-1) in case reassembly is required, or
954 * THRIFT_SUBDISSECTOR_ERROR (-2) in case of error.
957 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
)
959 thrift_field_header_t field_header
;
960 proto_tree
*internal_tree
= NULL
;
961 thrift_type_enum_t generic_type
;
963 /* Get the current state of dissection. */
964 DISSECTOR_ASSERT(thrift_opt
);
965 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
967 if (show_internal_thrift_fields
) {
968 internal_tree
= tree
;
970 /* Read the entire field header using the dedicated function. */
971 if (dissect_thrift_field_header(tvb
, pinfo
, internal_tree
, &offset
, thrift_opt
, &field_header
, false) == THRIFT_REQUEST_REASSEMBLY
) {
972 if (offset
== THRIFT_REQUEST_REASSEMBLY
) {
973 return THRIFT_REQUEST_REASSEMBLY
;
975 return THRIFT_SUBDISSECTOR_ERROR
;
979 /* Check the type first. */
980 if (thrift_opt
->tprotocol
& PROTO_THRIFT_COMPACT
) {
981 generic_type
= compact_struct_type_to_generic_type(field_header
.type
.compact
);
983 generic_type
= field_header
.type
.binary
;
985 if (generic_type
!= expected
) {
986 proto_tree_add_expert_format(tree
, pinfo
, &ei_thrift_wrong_type
, tvb
, offset
, TBP_THRIFT_TYPE_LEN
,
987 "Sub-dissector expects type = %s, found %s.",
988 val_to_str(expected
, thrift_type_vals
, "%02x"),
989 val_to_str(generic_type
, thrift_type_vals
, "%02x"));
990 return THRIFT_SUBDISSECTOR_ERROR
;
993 /* Once we know it's the expected type (which is /not/ T_STOP), we can read the field id. */
994 if (field_header
.field_id
!= (int64_t)field_id
) {
995 expert_add_info_format(pinfo
, field_header
.fid_pi
, &ei_thrift_wrong_field_id
,
996 "Sub-dissector expects field id = %d, found %" PRId64
" instead.", field_id
, field_header
.field_id
);
999 /* Expose the field header sub-tree if requested. */
1000 if (header_tree
!= NULL
) {
1001 *header_tree
= field_header
.fh_tree
;
1008 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
)
1010 int dt_offset
= offset
;
1011 bool bool_val
= false;
1012 /* Get the current state of dissection. */
1013 DISSECTOR_ASSERT(thrift_opt
);
1014 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1016 ABORT_SUBDISSECTION_ON_ISSUE(offset
);
1017 thrift_opt
->use_std_dissector
= true;
1019 /* In case of Compact protocol struct field (or command parameter / return value),
1020 * the boolean type also indicates the boolean value. */
1021 if (thrift_opt
->tprotocol
& PROTO_THRIFT_COMPACT
) {
1022 /* Read value in type nibble. */
1023 if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_TYPE_LEN
) {
1024 return THRIFT_REQUEST_REASSEMBLY
;
1026 if (((tvb_get_uint8(tvb
, offset
) >> TCP_THRIFT_NIBBLE_SHIFT
) & TCP_THRIFT_NIBBLE_MASK
) == DE_THRIFT_C_BOOL_TRUE
) {
1029 /* If we have neither DE_THRIFT_C_BOOL_TRUE nor DE_THRIFT_C_BOOL_FALSE as the type,
1030 * dissect_thrift_t_field_header will catch the issue anyway. */
1032 offset
= dissect_thrift_t_field_header(tvb
, pinfo
, tree
, offset
, thrift_opt
, DE_THRIFT_T_BOOL
, field_id
, NULL
);
1033 ABORT_SUBDISSECTION_ON_ISSUE(offset
);
1034 if (thrift_opt
->tprotocol
& PROTO_THRIFT_COMPACT
) {
1035 /* It is the responsibility of the sub-dissector to only use the least significant bit. */
1036 if (raw_dissector
!= NULL
) {
1037 tvbuff_t
* sub_tvb
= tvb_new_subset_length(tvb
, offset
, TBP_THRIFT_BOOL_LEN
);
1038 thrift_opt
->use_std_dissector
= false;
1039 raw_dissector(sub_tvb
, pinfo
, tree
, thrift_opt
);
1041 if (thrift_opt
->use_std_dissector
) {
1042 /* The value must be in the top-level tree, /after/ the field header tree. */
1043 proto_item
*pi
= proto_tree_add_boolean(tree
, hf_id
, tvb
, dt_offset
, TBP_THRIFT_TYPE_LEN
, bool_val
);
1044 proto_item_set_generated(pi
);
1049 if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_BOOL_LEN
) {
1050 return THRIFT_REQUEST_REASSEMBLY
;
1053 /* Either in a list/set/map or in a Binary protocol encoding. */
1054 if (raw_dissector
!= NULL
) {
1055 tvbuff_t
* sub_tvb
= tvb_new_subset_length(tvb
, offset
, TBP_THRIFT_BOOL_LEN
);
1056 thrift_opt
->use_std_dissector
= false;
1057 raw_dissector(sub_tvb
, pinfo
, tree
, thrift_opt
);
1059 if (thrift_opt
->use_std_dissector
) {
1060 proto_tree_add_item(tree
, hf_id
, tvb
, offset
, TBP_THRIFT_BOOL_LEN
, ENC_BIG_ENDIAN
);
1062 offset
+= TBP_THRIFT_BOOL_LEN
;
1065 thrift_opt
->previous_field_id
= field_id
;
1071 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
)
1073 DISSECTOR_ASSERT(thrift_opt
);
1074 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1075 return dissect_thrift_raw_bool(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, NULL
);
1079 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
)
1081 /* Get the current state of dissection. */
1082 DISSECTOR_ASSERT(thrift_opt
);
1083 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1086 offset
= dissect_thrift_t_field_header(tvb
, pinfo
, tree
, offset
, thrift_opt
, DE_THRIFT_T_I8
, field_id
, NULL
);
1088 ABORT_SUBDISSECTION_ON_ISSUE(offset
);
1089 if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_I8_LEN
) {
1090 return THRIFT_REQUEST_REASSEMBLY
;
1093 thrift_opt
->use_std_dissector
= true;
1094 /* Compact protocol does not use varint for T_I8 as it would be counter-productive. */
1095 if (raw_dissector
!= NULL
) {
1096 tvbuff_t
* sub_tvb
= tvb_new_subset_length(tvb
, offset
, TBP_THRIFT_I8_LEN
);
1097 thrift_opt
->use_std_dissector
= false;
1098 raw_dissector(sub_tvb
, pinfo
, tree
, thrift_opt
);
1100 if (thrift_opt
->use_std_dissector
) {
1101 proto_tree_add_item(tree
, hf_id
, tvb
, offset
, TBP_THRIFT_I8_LEN
, ENC_BIG_ENDIAN
);
1103 offset
+= TBP_THRIFT_I8_LEN
;
1106 thrift_opt
->previous_field_id
= field_id
;
1112 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
)
1114 DISSECTOR_ASSERT(thrift_opt
);
1115 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1116 return dissect_thrift_raw_i8(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, NULL
);
1120 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
)
1122 /* Get the current state of dissection. */
1123 DISSECTOR_ASSERT(thrift_opt
);
1124 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1127 offset
= dissect_thrift_t_field_header(tvb
, pinfo
, tree
, offset
, thrift_opt
, DE_THRIFT_T_I16
, field_id
, NULL
);
1129 ABORT_SUBDISSECTION_ON_ISSUE(offset
);
1130 thrift_opt
->use_std_dissector
= true;
1131 if (thrift_opt
->tprotocol
& PROTO_THRIFT_COMPACT
) {
1132 int result
= dissect_thrift_varint(tvb
, pinfo
, tree
, &offset
, thrift_opt
, TCP_THRIFT_MAX_I16_LEN
, hf_id
, raw_dissector
);
1133 if (result
== THRIFT_REQUEST_REASSEMBLY
) {
1134 if (offset
== THRIFT_REQUEST_REASSEMBLY
) {
1135 return THRIFT_REQUEST_REASSEMBLY
;
1137 return THRIFT_SUBDISSECTOR_ERROR
;
1140 } else if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_I16_LEN
) {
1141 return THRIFT_REQUEST_REASSEMBLY
;
1143 if (raw_dissector
!= NULL
) {
1144 tvbuff_t
* sub_tvb
= tvb_new_subset_length(tvb
, offset
, TBP_THRIFT_I16_LEN
);
1145 thrift_opt
->use_std_dissector
= false;
1146 raw_dissector(sub_tvb
, pinfo
, tree
, thrift_opt
);
1148 if (thrift_opt
->use_std_dissector
) {
1149 proto_tree_add_item(tree
, hf_id
, tvb
, offset
, TBP_THRIFT_I16_LEN
, ENC_BIG_ENDIAN
);
1151 offset
+= TBP_THRIFT_I16_LEN
;
1155 thrift_opt
->previous_field_id
= field_id
;
1161 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
)
1163 DISSECTOR_ASSERT(thrift_opt
);
1164 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1165 return dissect_thrift_raw_i16(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, NULL
);
1169 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
)
1171 /* Get the current state of dissection. */
1172 DISSECTOR_ASSERT(thrift_opt
);
1173 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1176 offset
= dissect_thrift_t_field_header(tvb
, pinfo
, tree
, offset
, thrift_opt
, DE_THRIFT_T_I32
, field_id
, NULL
);
1178 ABORT_SUBDISSECTION_ON_ISSUE(offset
);
1179 thrift_opt
->use_std_dissector
= true;
1180 if (thrift_opt
->tprotocol
& PROTO_THRIFT_COMPACT
) {
1181 int result
= dissect_thrift_varint(tvb
, pinfo
, tree
, &offset
, thrift_opt
, TCP_THRIFT_MAX_I32_LEN
, hf_id
, raw_dissector
);
1182 if (result
== THRIFT_REQUEST_REASSEMBLY
) {
1183 if (offset
== THRIFT_REQUEST_REASSEMBLY
) {
1184 return THRIFT_REQUEST_REASSEMBLY
;
1186 return THRIFT_SUBDISSECTOR_ERROR
;
1189 } else if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_I32_LEN
) {
1190 return THRIFT_REQUEST_REASSEMBLY
;
1192 if (raw_dissector
!= NULL
) {
1193 tvbuff_t
* sub_tvb
= tvb_new_subset_length(tvb
, offset
, TBP_THRIFT_I32_LEN
);
1194 thrift_opt
->use_std_dissector
= false;
1195 raw_dissector(sub_tvb
, pinfo
, tree
, thrift_opt
);
1197 if (thrift_opt
->use_std_dissector
) {
1198 proto_tree_add_item(tree
, hf_id
, tvb
, offset
, TBP_THRIFT_I32_LEN
, ENC_BIG_ENDIAN
);
1200 offset
+= TBP_THRIFT_I32_LEN
;
1204 thrift_opt
->previous_field_id
= field_id
;
1210 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
)
1212 DISSECTOR_ASSERT(thrift_opt
);
1213 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1214 return dissect_thrift_raw_i32(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, NULL
);
1218 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
)
1220 /* Get the current state of dissection. */
1221 DISSECTOR_ASSERT(thrift_opt
);
1222 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1225 offset
= dissect_thrift_t_field_header(tvb
, pinfo
, tree
, offset
, thrift_opt
, DE_THRIFT_T_I64
, field_id
, NULL
);
1227 ABORT_SUBDISSECTION_ON_ISSUE(offset
);
1228 thrift_opt
->use_std_dissector
= true;
1229 if (thrift_opt
->tprotocol
& PROTO_THRIFT_COMPACT
) {
1230 int result
= dissect_thrift_varint(tvb
, pinfo
, tree
, &offset
, thrift_opt
, TCP_THRIFT_MAX_I64_LEN
, hf_id
, raw_dissector
);
1231 if (result
== THRIFT_REQUEST_REASSEMBLY
) {
1232 if (offset
== THRIFT_REQUEST_REASSEMBLY
) {
1233 return THRIFT_REQUEST_REASSEMBLY
;
1235 return THRIFT_SUBDISSECTOR_ERROR
;
1238 } else if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_I64_LEN
) {
1239 return THRIFT_REQUEST_REASSEMBLY
;
1241 if (raw_dissector
!= NULL
) {
1242 tvbuff_t
* sub_tvb
= tvb_new_subset_length(tvb
, offset
, TBP_THRIFT_I64_LEN
);
1243 thrift_opt
->use_std_dissector
= false;
1244 raw_dissector(sub_tvb
, pinfo
, tree
, thrift_opt
);
1246 if (thrift_opt
->use_std_dissector
) {
1247 proto_tree_add_item(tree
, hf_id
, tvb
, offset
, TBP_THRIFT_I64_LEN
, ENC_BIG_ENDIAN
);
1249 offset
+= TBP_THRIFT_I64_LEN
;
1253 thrift_opt
->previous_field_id
= field_id
;
1259 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
)
1261 DISSECTOR_ASSERT(thrift_opt
);
1262 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1263 return dissect_thrift_raw_i64(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, NULL
);
1267 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
)
1269 /* Get the current state of dissection. */
1270 DISSECTOR_ASSERT(thrift_opt
);
1271 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1274 offset
= dissect_thrift_t_field_header(tvb
, pinfo
, tree
, offset
, thrift_opt
, DE_THRIFT_T_DOUBLE
, field_id
, NULL
);
1276 ABORT_SUBDISSECTION_ON_ISSUE(offset
);
1277 if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_DOUBLE_LEN
) {
1278 return THRIFT_REQUEST_REASSEMBLY
;
1281 thrift_opt
->use_std_dissector
= true;
1282 if (raw_dissector
!= NULL
) {
1284 if (thrift_opt
->tprotocol
& PROTO_THRIFT_COMPACT
) {
1285 /* Create a sub-tvbuff_t in big endian format as documented. */
1286 uint8_t *data
= wmem_alloc(wmem_packet_scope(), TBP_THRIFT_DOUBLE_LEN
);
1287 data
[0] = tvb_get_uint8(tvb
, offset
+ 7);
1288 data
[1] = tvb_get_uint8(tvb
, offset
+ 6);
1289 data
[2] = tvb_get_uint8(tvb
, offset
+ 5);
1290 data
[3] = tvb_get_uint8(tvb
, offset
+ 4);
1291 data
[4] = tvb_get_uint8(tvb
, offset
+ 3);
1292 data
[5] = tvb_get_uint8(tvb
, offset
+ 2);
1293 data
[6] = tvb_get_uint8(tvb
, offset
+ 1);
1294 data
[7] = tvb_get_uint8(tvb
, offset
);
1295 sub_tvb
= tvb_new_child_real_data(tvb
, data
, TBP_THRIFT_DOUBLE_LEN
, TBP_THRIFT_DOUBLE_LEN
);
1297 sub_tvb
= tvb_new_subset_length(tvb
, offset
, TBP_THRIFT_DOUBLE_LEN
);
1299 thrift_opt
->use_std_dissector
= false;
1300 raw_dissector(sub_tvb
, pinfo
, tree
, thrift_opt
);
1302 if (thrift_opt
->use_std_dissector
) {
1303 if (thrift_opt
->tprotocol
& PROTO_THRIFT_COMPACT
) {
1304 proto_tree_add_item(tree
, hf_id
, tvb
, offset
, TBP_THRIFT_DOUBLE_LEN
, ENC_LITTLE_ENDIAN
);
1306 proto_tree_add_item(tree
, hf_id
, tvb
, offset
, TBP_THRIFT_DOUBLE_LEN
, ENC_BIG_ENDIAN
);
1309 offset
+= TBP_THRIFT_DOUBLE_LEN
;
1312 thrift_opt
->previous_field_id
= field_id
;
1318 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
)
1320 DISSECTOR_ASSERT(thrift_opt
);
1321 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1322 return dissect_thrift_raw_double(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, NULL
);
1326 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
)
1328 /* Get the current state of dissection. */
1329 DISSECTOR_ASSERT(thrift_opt
);
1330 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1332 /* Dissect field header if necessary. */
1334 offset
= dissect_thrift_t_field_header(tvb
, pinfo
, tree
, offset
, thrift_opt
, DE_THRIFT_T_UUID
, field_id
, NULL
);
1336 ABORT_SUBDISSECTION_ON_ISSUE(offset
);
1338 if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_UUID_LEN
) {
1339 return THRIFT_REQUEST_REASSEMBLY
;
1342 thrift_opt
->use_std_dissector
= true;
1343 if (raw_dissector
!= NULL
) {
1344 tvbuff_t
* sub_tvb
= tvb_new_subset_length(tvb
, offset
, TBP_THRIFT_UUID_LEN
);
1345 thrift_opt
->use_std_dissector
= false;
1346 raw_dissector(sub_tvb
, pinfo
, tree
, thrift_opt
);
1348 if (thrift_opt
->use_std_dissector
) {
1349 proto_tree_add_item(tree
, hf_id
, tvb
, offset
, TBP_THRIFT_UUID_LEN
, ENC_BIG_ENDIAN
);
1351 offset
+= TBP_THRIFT_UUID_LEN
;
1354 thrift_opt
->previous_field_id
= field_id
;
1360 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
)
1362 DISSECTOR_ASSERT(thrift_opt
);
1363 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1364 return dissect_thrift_raw_uuid(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, NULL
);
1368 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
)
1370 /* Get the current state of dissection. */
1371 DISSECTOR_ASSERT(thrift_opt
);
1372 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1374 proto_tree
*header_tree
= NULL
;
1375 proto_item
*len_item
= NULL
;
1376 int32_t str_len
, len_len
;
1379 /* Dissect field header if necessary. */
1381 offset
= dissect_thrift_t_field_header(tvb
, pinfo
, tree
, offset
, thrift_opt
, DE_THRIFT_T_BINARY
, field_id
, &header_tree
);
1385 ABORT_SUBDISSECTION_ON_ISSUE(offset
);
1387 /* Dissect length. */
1388 if (thrift_opt
->tprotocol
& PROTO_THRIFT_COMPACT
) {
1389 len_len
= thrift_get_varint_enc(tvb
, pinfo
, header_tree
, offset
, TCP_THRIFT_MAX_I32_LEN
, &varint
, ENC_VARINT_PROTOBUF
);
1391 case THRIFT_REQUEST_REASSEMBLY
:
1392 return THRIFT_REQUEST_REASSEMBLY
;
1394 return THRIFT_SUBDISSECTOR_ERROR
;
1398 if ((int64_t)INT32_MIN
> varint
|| varint
> (int64_t)INT32_MAX
) {
1399 len_item
= proto_tree_add_int64(header_tree
, hf_thrift_i64
, tvb
, offset
, len_len
, varint
);
1400 expert_add_info(pinfo
, len_item
, &ei_thrift_varint_too_large
);
1401 return THRIFT_REQUEST_REASSEMBLY
;
1403 str_len
= (int32_t)varint
;
1404 if (show_internal_thrift_fields
) {
1405 len_item
= proto_tree_add_int(header_tree
, hf_thrift_str_len
, tvb
, offset
, len_len
, str_len
);
1408 len_len
= TBP_THRIFT_LENGTH_LEN
;
1409 if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_LENGTH_LEN
) {
1410 return THRIFT_REQUEST_REASSEMBLY
;
1412 if (show_internal_thrift_fields
) {
1413 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
);
1415 str_len
= tvb_get_ntohil(tvb
, offset
);
1419 expert_add_info(pinfo
, len_item
, &ei_thrift_negative_length
);
1420 return THRIFT_SUBDISSECTOR_ERROR
;
1423 /* Since we put the length inside the field header, we need to extend it. */
1424 if (header_tree
!= tree
) {
1425 proto_item_set_end(proto_tree_get_parent(header_tree
), tvb
, offset
);
1429 if (tvb_reported_length_remaining(tvb
, offset
) < str_len
) {
1430 return THRIFT_REQUEST_REASSEMBLY
;
1433 thrift_opt
->use_std_dissector
= true;
1434 if (raw_dissector
!= NULL
) {
1435 tvbuff_t
* sub_tvb
= tvb_new_subset_length(tvb
, offset
, str_len
);
1436 thrift_opt
->use_std_dissector
= false;
1437 raw_dissector(sub_tvb
, pinfo
, tree
, thrift_opt
);
1439 if (thrift_opt
->use_std_dissector
) {
1440 proto_tree_add_item(tree
, hf_id
, tvb
, offset
, str_len
, encoding
);
1442 offset
= offset
+ str_len
;
1445 thrift_opt
->previous_field_id
= field_id
;
1451 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
)
1453 DISSECTOR_ASSERT(thrift_opt
);
1454 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1455 return dissect_thrift_raw_binary(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, ENC_NA
, NULL
);
1459 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
)
1461 DISSECTOR_ASSERT(thrift_opt
);
1462 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1463 return dissect_thrift_raw_binary(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, ENC_UTF_8
, NULL
);
1467 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
)
1469 DISSECTOR_ASSERT(thrift_opt
);
1470 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1471 return dissect_thrift_raw_binary(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, encoding
, NULL
);
1475 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
)
1477 /* Get the current state of dissection. */
1478 DISSECTOR_ASSERT(thrift_opt
);
1479 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1482 case DE_THRIFT_T_BOOL
:
1483 offset
= dissect_thrift_raw_bool(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, raw_dissector
);
1485 case DE_THRIFT_T_I8
:
1486 offset
= dissect_thrift_raw_i8(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, raw_dissector
);
1488 case DE_THRIFT_T_I16
:
1489 offset
= dissect_thrift_raw_i16(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, raw_dissector
);
1491 case DE_THRIFT_T_I32
:
1492 offset
= dissect_thrift_raw_i32(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, raw_dissector
);
1494 case DE_THRIFT_T_I64
:
1495 offset
= dissect_thrift_raw_i64(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, raw_dissector
);
1497 case DE_THRIFT_T_DOUBLE
:
1498 offset
= dissect_thrift_raw_double(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, raw_dissector
);
1500 case DE_THRIFT_T_BINARY
:
1501 offset
= dissect_thrift_raw_binary(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, ENC_NA
, raw_dissector
);
1503 case DE_THRIFT_T_UUID
:
1504 offset
= dissect_thrift_raw_uuid(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, raw_dissector
);
1507 REPORT_DISSECTOR_BUG("Only simple data types support raw dissection.");
1513 /* Simple dispatch function for lists, sets, maps, and structs internal elements to avoid code duplication. */
1515 // NOLINTNEXTLINE(misc-no-recursion)
1516 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
)
1518 switch (elt
->type
) {
1519 case DE_THRIFT_T_STOP
:
1520 offset
= dissect_thrift_t_stop(tvb
, pinfo
, tree
, offset
);
1522 case DE_THRIFT_T_BOOL
:
1523 offset
= dissect_thrift_raw_bool(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, elt
->fid
, *elt
->p_hf_id
, elt
->raw_dissector
);
1525 case DE_THRIFT_T_I8
:
1526 offset
= dissect_thrift_raw_i8(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, elt
->fid
, *elt
->p_hf_id
, elt
->raw_dissector
);
1528 case DE_THRIFT_T_I16
:
1529 offset
= dissect_thrift_raw_i16(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, elt
->fid
, *elt
->p_hf_id
, elt
->raw_dissector
);
1531 case DE_THRIFT_T_I32
:
1532 offset
= dissect_thrift_raw_i32(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, elt
->fid
, *elt
->p_hf_id
, elt
->raw_dissector
);
1534 case DE_THRIFT_T_I64
:
1535 offset
= dissect_thrift_raw_i64(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, elt
->fid
, *elt
->p_hf_id
, elt
->raw_dissector
);
1537 case DE_THRIFT_T_DOUBLE
:
1538 offset
= dissect_thrift_raw_double(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, elt
->fid
, *elt
->p_hf_id
, elt
->raw_dissector
);
1540 case DE_THRIFT_T_BINARY
:
1541 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
);
1543 case DE_THRIFT_T_LIST
:
1544 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
);
1546 case DE_THRIFT_T_SET
:
1547 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
);
1549 case DE_THRIFT_T_MAP
:
1550 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
);
1552 case DE_THRIFT_T_STRUCT
:
1553 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
);
1555 case DE_THRIFT_T_UUID
:
1556 offset
= dissect_thrift_raw_uuid(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, elt
->fid
, *elt
->p_hf_id
, elt
->raw_dissector
);
1559 REPORT_DISSECTOR_BUG("Unexpected Thrift type dissection requested.");
1565 /* Effective sub-dissection for lists, sets, and maps in Binary Protocol.
1566 * Since the only difference is in the hf_id used when showing internal Thrift fields,
1567 * this prevents code duplication.
1568 * Map is only adding a type in the header and one element in each loop
1569 * so it's easy to use the same code and handle the additional elements only when necessary.
1572 // NOLINTNEXTLINE(misc-no-recursion)
1573 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
)
1575 proto_item
*container_pi
= NULL
;
1576 proto_item
*len_pi
= NULL
;
1577 proto_tree
*sub_tree
;
1578 int32_t key_type
, val_type
;
1580 unsigned nested_count
= p_get_proto_depth(pinfo
, proto_thrift
);
1582 /* Get the current state of dissection. */
1583 DISSECTOR_ASSERT(thrift_opt
);
1584 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1585 DISSECTOR_ASSERT((thrift_opt
->tprotocol
& PROTO_THRIFT_COMPACT
) == 0);
1587 /* Dissect field header if necessary. */
1589 offset
= dissect_thrift_t_field_header(tvb
, pinfo
, tree
, offset
, thrift_opt
, expected
, field_id
, NULL
);
1592 /* Create the sub-tree. */
1593 if (nested_count
>= thrift_opt
->nested_type_depth
) {
1594 expert_add_info(pinfo
, proto_tree_get_parent(tree
), &ei_thrift_too_many_subtypes
);
1595 return THRIFT_SUBDISSECTOR_ERROR
;
1597 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
+ 1);
1598 container_pi
= proto_tree_add_item(tree
, hf_id
, tvb
, offset
, -1, ENC_BIG_ENDIAN
);
1599 sub_tree
= proto_item_add_subtree(container_pi
, ett_id
);
1600 ABORT_SUBDISSECTION_ON_ISSUE(offset
);
1602 /* Read and check the type of the key in case of map. */
1603 if (expected
== DE_THRIFT_T_MAP
) {
1604 if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_TYPE_LEN
) {
1605 return THRIFT_REQUEST_REASSEMBLY
;
1607 key_type
= tvb_get_uint8(tvb
, offset
);
1608 if (show_internal_thrift_fields
) {
1609 proto_tree_add_item(sub_tree
, hf_thrift_type
, tvb
, offset
, TBP_THRIFT_TYPE_LEN
, ENC_BIG_ENDIAN
);
1611 if (key_type
!= key
->type
) {
1612 proto_tree_add_expert(sub_tree
, pinfo
, &ei_thrift_wrong_type
, tvb
, offset
, TBP_THRIFT_TYPE_LEN
);
1613 return THRIFT_SUBDISSECTOR_ERROR
;
1615 offset
+= TBP_THRIFT_TYPE_LEN
;
1618 /* Read and check the type of the elements (or type of the values in case of map). */
1619 if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_TYPE_LEN
) {
1620 return THRIFT_REQUEST_REASSEMBLY
;
1622 val_type
= tvb_get_uint8(tvb
, offset
);
1623 if (show_internal_thrift_fields
) {
1624 proto_tree_add_item(sub_tree
, hf_thrift_type
, tvb
, offset
, TBP_THRIFT_TYPE_LEN
, ENC_BIG_ENDIAN
);
1626 if (val_type
!= val
->type
) {
1627 proto_tree_add_expert(sub_tree
, pinfo
, &ei_thrift_wrong_type
, tvb
, offset
, TBP_THRIFT_TYPE_LEN
);
1628 return THRIFT_SUBDISSECTOR_ERROR
;
1630 offset
+= TBP_THRIFT_TYPE_LEN
;
1632 /* Read and check the number of entries of the container. */
1633 if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_LENGTH_LEN
) {
1634 return THRIFT_REQUEST_REASSEMBLY
;
1636 length
= tvb_get_ntohil(tvb
, offset
);
1637 if (show_internal_thrift_fields
) {
1640 case DE_THRIFT_T_MAP
:
1641 hf_num_item
= hf_thrift_num_map_item
;
1643 case DE_THRIFT_T_SET
:
1644 hf_num_item
= hf_thrift_num_set_item
;
1646 case DE_THRIFT_T_LIST
:
1647 hf_num_item
= hf_thrift_num_list_item
;
1650 return THRIFT_SUBDISSECTOR_ERROR
;
1652 len_pi
= proto_tree_add_item_ret_int(sub_tree
, hf_num_item
, tvb
, offset
, TBP_THRIFT_LENGTH_LEN
, ENC_BIG_ENDIAN
, &length
);
1654 offset
+= TBP_THRIFT_LENGTH_LEN
;
1656 expert_add_info(pinfo
, len_pi
, &ei_thrift_negative_length
);
1657 return THRIFT_SUBDISSECTOR_ERROR
;
1660 /* Read the content of the container. */
1661 for(int i
= 0; i
< length
; ++i
) {
1662 if (expected
== DE_THRIFT_T_MAP
) {
1663 offset
= dissect_thrift_t_member(tvb
, pinfo
, sub_tree
, offset
, thrift_opt
, false, key
);
1665 offset
= dissect_thrift_t_member(tvb
, pinfo
, sub_tree
, offset
, thrift_opt
, false, val
);
1666 /* Avoid continuing the loop if anything went sideways. */
1667 ABORT_SUBDISSECTION_ON_ISSUE(offset
);
1669 if (container_pi
&& offset
> 0) {
1670 proto_item_set_end(container_pi
, tvb
, offset
);
1672 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
);
1676 /* Effective sub-dissection for both lists and sets for Compact Protocol.
1677 * Since the only difference is in the hf_id used when showing internal Thrift fields,
1678 * this prevents code duplication.
1681 // NOLINTNEXTLINE(misc-no-recursion)
1682 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
)
1684 proto_item
*container_pi
;
1685 proto_item
*type_pi
= NULL
;
1686 proto_item
*len_pi
= NULL
;
1687 proto_tree
*sub_tree
= NULL
;
1689 thrift_compact_type_enum_t elt_type
;
1690 int32_t container_len
, len_len
, i
;
1693 int hf_num_item
= hf_thrift_num_set_item
;
1694 int hf_pos_item
= hf_thrift_num_set_pos
;
1695 thrift_type_enum_t expected
= DE_THRIFT_T_SET
;
1696 unsigned nested_count
= p_get_proto_depth(pinfo
, proto_thrift
);
1699 hf_num_item
= hf_thrift_num_list_item
;
1700 hf_pos_item
= hf_thrift_num_list_pos
;
1701 expected
= DE_THRIFT_T_LIST
;
1704 /* Get the current state of dissection. */
1705 DISSECTOR_ASSERT(thrift_opt
);
1706 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1707 DISSECTOR_ASSERT(thrift_opt
->tprotocol
& PROTO_THRIFT_COMPACT
);
1709 /* Dissect field header if necessary. */
1711 offset
= dissect_thrift_t_field_header(tvb
, pinfo
, tree
, offset
, thrift_opt
, expected
, field_id
, NULL
);
1713 ABORT_SUBDISSECTION_ON_ISSUE(offset
);
1714 if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_TYPE_LEN
) {
1715 return THRIFT_REQUEST_REASSEMBLY
;
1718 /* Create the sub-tree. */
1719 if (nested_count
>= thrift_opt
->nested_type_depth
) {
1720 expert_add_info(pinfo
, proto_tree_get_parent(tree
), &ei_thrift_too_many_subtypes
);
1721 return THRIFT_SUBDISSECTOR_ERROR
;
1723 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
+ 1);
1724 container_pi
= proto_tree_add_item(tree
, hf_id
, tvb
, offset
, -1, ENC_BIG_ENDIAN
);
1725 sub_tree
= proto_item_add_subtree(container_pi
, ett_id
);
1727 /* Read the type of the elements (and length if lower than 15). */
1729 len_type
= tvb_get_uint8(tvb
, lt_offset
);
1730 offset
+= TBP_THRIFT_TYPE_LEN
;
1731 elt_type
= (thrift_compact_type_enum_t
)(len_type
& TCP_THRIFT_NIBBLE_MASK
);
1732 if (show_internal_thrift_fields
) {
1733 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
);
1735 /* Check the type of the elements. */
1736 if (compact_struct_type_to_generic_type(elt_type
) != elt
->type
) {
1737 if (show_internal_thrift_fields
) {
1738 expert_add_info(pinfo
, type_pi
, &ei_thrift_wrong_type
);
1740 proto_tree_add_expert(sub_tree
, pinfo
, &ei_thrift_wrong_type
, tvb
, offset
, TBP_THRIFT_TYPE_LEN
);
1741 return THRIFT_SUBDISSECTOR_ERROR
;
1743 container_len
= (len_type
>> TCP_THRIFT_NIBBLE_SHIFT
) & TCP_THRIFT_NIBBLE_MASK
;
1745 /* Read and check the number of entries of the container. */
1746 if (container_len
== TCP_THRIFT_LENGTH_LARGER
) {
1747 if (show_internal_thrift_fields
) {
1748 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
);
1750 /* Length is greater than 14, read effective length as a varint. */
1751 len_len
= thrift_get_varint_enc(tvb
, pinfo
, sub_tree
, offset
, TCP_THRIFT_MAX_I32_LEN
, &varint
, ENC_VARINT_PROTOBUF
);
1753 case THRIFT_REQUEST_REASSEMBLY
:
1754 return THRIFT_REQUEST_REASSEMBLY
;
1756 /* In case of error, the offset stay at the error position. */
1757 return THRIFT_SUBDISSECTOR_ERROR
;
1759 if (varint
> (uint64_t)INT32_MAX
) {
1760 len_pi
= proto_tree_add_int64(sub_tree
, hf_thrift_i64
, tvb
, offset
, len_len
, varint
);
1761 expert_add_info(pinfo
, len_pi
, &ei_thrift_varint_too_large
);
1762 return THRIFT_SUBDISSECTOR_ERROR
;
1764 container_len
= (uint32_t)varint
;
1765 if (show_internal_thrift_fields
) {
1766 proto_tree_add_int(sub_tree
, hf_num_item
, tvb
, offset
, len_len
, container_len
);
1771 } else if (show_internal_thrift_fields
) {
1772 proto_tree_add_bits_item(sub_tree
, hf_pos_item
, tvb
, (lt_offset
<< OCTETS_TO_BITS_SHIFT
), TCP_THRIFT_NIBBLE_SHIFT
, ENC_BIG_ENDIAN
);
1775 /* Read the content of the container. */
1776 for (i
= 0; i
< container_len
; ++i
) {
1777 offset
= dissect_thrift_t_member(tvb
, pinfo
, sub_tree
, offset
, thrift_opt
, false, elt
);
1778 /* Avoid continuing the loop if anything went sideways. */
1779 ABORT_SUBDISSECTION_ON_ISSUE(offset
);
1781 if (container_pi
&& offset
> 0) {
1782 proto_item_set_end(container_pi
, tvb
, offset
);
1784 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
);
1789 // NOLINTNEXTLINE(misc-no-recursion)
1790 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
)
1793 if (thrift_opt
->tprotocol
& PROTO_THRIFT_COMPACT
) {
1794 result
= dissect_thrift_c_list_set(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, ett_id
, elt
, true);
1796 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
);
1800 thrift_opt
->previous_field_id
= field_id
;
1806 // NOLINTNEXTLINE(misc-no-recursion)
1807 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
)
1810 if (thrift_opt
->tprotocol
& PROTO_THRIFT_COMPACT
) {
1811 result
= dissect_thrift_c_list_set(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, ett_id
, elt
, false);
1813 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
);
1817 thrift_opt
->previous_field_id
= field_id
;
1823 // NOLINTNEXTLINE(misc-no-recursion)
1824 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
)
1827 /* Get the current state of dissection. */
1828 DISSECTOR_ASSERT(thrift_opt
);
1829 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1831 if ((thrift_opt
->tprotocol
& PROTO_THRIFT_COMPACT
) == 0) {
1832 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
);
1834 proto_tree
*sub_tree
= NULL
;
1835 proto_item
*container_pi
;
1836 proto_item
*ktype_pi
= NULL
;
1837 proto_item
*vtype_pi
= NULL
;
1838 int32_t container_len
, len_len
, i
, types
;
1839 int32_t len_offset
= offset
;
1840 thrift_compact_type_enum_t ktype
, vtype
;
1842 unsigned nested_count
= p_get_proto_depth(pinfo
, proto_thrift
);
1844 /* Dissect field header if necessary. */
1846 if (show_internal_thrift_fields
) {
1849 offset
= dissect_thrift_t_field_header(tvb
, pinfo
, sub_tree
, offset
, thrift_opt
, DE_THRIFT_T_MAP
, field_id
, NULL
);
1852 /* Read and check number of key-value pair in the map. */
1853 if (tvb_reported_length_remaining(tvb
, offset
) < TCP_THRIFT_MIN_VARINT_LEN
) {
1854 return THRIFT_REQUEST_REASSEMBLY
;
1856 len_len
= thrift_get_varint_enc(tvb
, pinfo
, sub_tree
, offset
, TCP_THRIFT_MAX_I32_LEN
, &varint
, ENC_VARINT_PROTOBUF
);
1858 case THRIFT_REQUEST_REASSEMBLY
:
1859 return THRIFT_REQUEST_REASSEMBLY
;
1861 /* In case of error, the offset stay at the error position. */
1862 return THRIFT_SUBDISSECTOR_ERROR
;
1864 if (varint
> (uint64_t)INT32_MAX
) {
1865 proto_item
*len_pi
= proto_tree_add_int64(sub_tree
, hf_thrift_i64
, tvb
, offset
, len_len
, varint
);
1866 expert_add_info(pinfo
, len_pi
, &ei_thrift_varint_too_large
);
1867 return THRIFT_SUBDISSECTOR_ERROR
;
1869 container_len
= (uint32_t)varint
;
1874 /* Create the sub-tree. */
1875 if (nested_count
>= thrift_opt
->nested_type_depth
) {
1876 expert_add_info(pinfo
, proto_tree_get_parent(tree
), &ei_thrift_too_many_subtypes
);
1877 return THRIFT_SUBDISSECTOR_ERROR
;
1879 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
+ 1);
1880 container_pi
= proto_tree_add_item(tree
, hf_id
, tvb
, len_offset
, -1, ENC_BIG_ENDIAN
);
1881 sub_tree
= proto_item_add_subtree(container_pi
, ett_id
);
1883 if (container_len
== 0) {
1884 proto_item_set_end(container_pi
, tvb
, offset
);
1885 proto_item_append_text(container_pi
, " (Empty)");
1886 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
);
1890 /* If the map is not empty, read the types of keys and values. */
1891 if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_TYPE_LEN
) {
1892 return THRIFT_REQUEST_REASSEMBLY
;
1894 types
= tvb_get_uint8(tvb
, offset
);
1895 ktype
= (thrift_compact_type_enum_t
)((types
>> TCP_THRIFT_NIBBLE_SHIFT
) & TCP_THRIFT_NIBBLE_MASK
);
1896 vtype
= (thrift_compact_type_enum_t
)(types
& TCP_THRIFT_NIBBLE_MASK
);
1897 if (show_internal_thrift_fields
) {
1898 proto_tree_add_int(sub_tree
, hf_thrift_num_map_item
, tvb
, len_offset
, len_len
, container_len
);
1899 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
);
1900 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
);
1903 /* Check that types match what is expected. */
1904 if (compact_struct_type_to_generic_type(ktype
) != key
->type
) {
1905 if (show_internal_thrift_fields
) {
1906 expert_add_info(pinfo
, ktype_pi
, &ei_thrift_wrong_type
);
1908 proto_tree_add_expert(sub_tree
, pinfo
, &ei_thrift_wrong_type
, tvb
, offset
, TBP_THRIFT_TYPE_LEN
);
1910 return THRIFT_SUBDISSECTOR_ERROR
;
1912 if (compact_struct_type_to_generic_type(vtype
) != value
->type
) {
1913 if (show_internal_thrift_fields
) {
1914 expert_add_info(pinfo
, vtype_pi
, &ei_thrift_wrong_type
);
1916 proto_tree_add_expert(sub_tree
, pinfo
, &ei_thrift_wrong_type
, tvb
, offset
, TBP_THRIFT_TYPE_LEN
);
1918 return THRIFT_SUBDISSECTOR_ERROR
;
1921 /* Read the content of the container. */
1922 for (i
= 0; i
< container_len
; ++i
) {
1923 offset
= dissect_thrift_t_member(tvb
, pinfo
, sub_tree
, offset
, thrift_opt
, false, key
);
1924 offset
= dissect_thrift_t_member(tvb
, pinfo
, sub_tree
, offset
, thrift_opt
, false, value
);
1925 /* Avoid continuing the loop if anything went sideways. */
1926 ABORT_SUBDISSECTION_ON_ISSUE(offset
);
1929 if (container_pi
&& offset
> 0) {
1930 proto_item_set_end(container_pi
, tvb
, offset
);
1933 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
);
1937 thrift_opt
->previous_field_id
= field_id
;
1943 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
)
1945 return dissect_thrift_t_struct_expert(tvb
, pinfo
, tree
, offset
, thrift_opt
, is_field
, field_id
, hf_id
, ett_id
, seq
, NULL
);
1949 // NOLINTNEXTLINE(misc-no-recursion)
1950 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
)
1952 thrift_field_header_t field_header
;
1953 proto_tree
*sub_tree
= NULL
;
1954 proto_item
*type_pi
= NULL
;
1956 bool enable_subtree
= (ett_id
!= DISABLE_SUBTREE
) || (hf_id
!= DISABLE_SUBTREE
);
1957 unsigned nested_count
= p_get_proto_depth(pinfo
, proto_thrift
);
1959 /* Get the current state of dissection. */
1960 DISSECTOR_ASSERT(thrift_opt
);
1961 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
1963 /* Dissect field header if necessary. */
1965 if (show_internal_thrift_fields
) {
1968 offset
= dissect_thrift_t_field_header(tvb
, pinfo
, sub_tree
, offset
, thrift_opt
, DE_THRIFT_T_STRUCT
, field_id
, NULL
);
1970 ABORT_SUBDISSECTION_ON_ISSUE(offset
);
1971 if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_TYPE_LEN
) {
1972 return THRIFT_REQUEST_REASSEMBLY
;
1975 /* Create the sub-tree, if not explicitly refused. */
1976 if (enable_subtree
) {
1977 /* Add the struct to the tree. */
1978 if (nested_count
>= thrift_opt
->nested_type_depth
) {
1979 expert_add_info(pinfo
, proto_tree_get_parent(tree
), &ei_thrift_too_many_subtypes
);
1980 return THRIFT_SUBDISSECTOR_ERROR
;
1982 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
+ 1);
1983 type_pi
= proto_tree_add_item(tree
, hf_id
, tvb
, offset
, -1, ENC_BIG_ENDIAN
);
1984 sub_tree
= proto_item_add_subtree(type_pi
, ett_id
);
1986 /* Sub-dissector requested that we don't use a sub_tree.
1987 * This is useful for ME_THRIFT_T_REPLY or unions where we always have only 1 sub-element. */
1991 thrift_opt
->previous_field_id
= 0;
1992 /* Read and check available fields. */
1993 while (seq
->type
!= DE_THRIFT_T_STOP
) {
1994 int local_offset
= offset
;
1995 /* Read the type and check for the end of the structure.
1996 * Never create the field header sub-tree here as it will be handled by the field's own dissector.
1997 * We only want to get the type & field id information to compare them against what we expect.
1999 if (dissect_thrift_field_header(tvb
, pinfo
, NULL
, &local_offset
, thrift_opt
, &field_header
, false) == THRIFT_REQUEST_REASSEMBLY
) {
2000 if (local_offset
== THRIFT_REQUEST_REASSEMBLY
) {
2001 return THRIFT_REQUEST_REASSEMBLY
;
2003 return THRIFT_SUBDISSECTOR_ERROR
;
2006 /* Right now, we only check for T_STOP type. The member sub-dissection will take care of checking the type. */
2007 if (field_header
.type
.binary
== DE_THRIFT_T_STOP
) {
2008 if (seq
->optional
) {
2012 proto_tree_add_expert(sub_tree
, pinfo
, &ei_thrift_struct_fid_not_in_seq
, tvb
, offset
, TBP_THRIFT_TYPE_LEN
);
2013 return THRIFT_SUBDISSECTOR_ERROR
;
2017 /* Read and check the field id which is the only way to make sure we are aligned. */
2018 if (field_header
.field_id
!= seq
->fid
) {
2019 /* Wrong field in sequence or was it optional? */
2020 if (seq
->optional
) {
2021 /* Skip to next element*/
2025 proto_tree_add_expert(sub_tree
, pinfo
, &ei_thrift_struct_fid_not_in_seq
, tvb
, offset
, TBP_THRIFT_TYPE_LEN
);
2026 return THRIFT_SUBDISSECTOR_ERROR
;
2030 if (seq
->type
!= DE_THRIFT_T_GENERIC
) {
2031 /* Type is not T_STOP and field id matches one we know how to dissect. */
2032 offset
= dissect_thrift_t_member(tvb
, pinfo
, sub_tree
, offset
, thrift_opt
, true, seq
);
2034 /* The field is not defined in the struct, switch back to generic dissection.
2035 * Re-read the header ensuring it is displayed (no need to check result,
2036 * we already dissected it but without the header tree creation. */
2037 dissect_thrift_field_header(tvb
, pinfo
, sub_tree
, &offset
, thrift_opt
, &field_header
, false);
2038 expert_add_info(pinfo
, field_header
.fid_pi
, &ei_thrift_undefined_field_id
);
2039 // Then dissect just this field.
2040 if (thrift_opt
->tprotocol
& PROTO_THRIFT_COMPACT
) {
2041 if (!is_thrift_compact_bool_type(field_header
.type
.compact
) &&
2042 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
) {
2043 return THRIFT_REQUEST_REASSEMBLY
;
2046 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
) {
2047 return THRIFT_REQUEST_REASSEMBLY
;
2051 ABORT_SUBDISSECTION_ON_ISSUE(offset
);
2052 if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_TYPE_LEN
) {
2053 return THRIFT_REQUEST_REASSEMBLY
;
2057 /* Allow the proper handling of next delta field id in Compact protocol. */
2058 thrift_opt
->previous_field_id
= field_header
.field_id
;
2061 /* The loop exits before dissecting the T_STOP. */
2062 offset
= dissect_thrift_t_stop(tvb
, pinfo
, sub_tree
, offset
);
2064 /* Set expert info if required. */
2066 expert_add_info(pinfo
, type_pi
, ei
);
2069 if (enable_subtree
&& offset
> 0) {
2070 proto_item_set_end(type_pi
, tvb
, offset
);
2074 thrift_opt
->previous_field_id
= field_id
;
2076 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
);
2079 /*=====END SUB-DISSECTION=====*/
2081 /* GENERIC DISSECTION PARAMETERS DOCUMENTATION
2083 * Generic functions for when there is no custom sub-dissector.
2084 * Same conventions are used for binary and compact.
2086 * +--------------------+--------------------------+---------------------+
2087 * | offset \ return | REQUEST_REASSEMBLY = -1 | Length |
2088 * +--------------------+--------------------------+---------------------+
2089 * | REQUEST_REASSEMBLY | Reassembly required | SHALL NEVER HAPPEN! |
2090 * +--------------------+--------------------------+---------------------+
2091 * | Length | Error occurred at offset | Data fully parsed. |
2092 * +--------------------+--------------------------+---------------------+
2094 * @param[in] tvb: Pointer to the tvbuff_t holding the captured data.
2095 * @param[in] pinfo: Pointer to the packet_info holding information about the currently dissected packet.
2096 * @param[in] tree: Pointer to the proto_tree used to hold the display tree in Wireshark's interface.
2097 * @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.
2098 * The offset is modified according to table hereabove in sync with the return value.
2099 * @param[in] thrift_opt: Options from the Thrift dissector that will be useful for dissection of deep data (to expose the position of failure).
2101 * // Specific to dissect_thrift_binary_linear.
2102 * @param[in] expected: Expected container type (list, set, or map).
2104 * // Specific to dissect_thrift_compact_list_set.
2105 * @param[in] is_list: `true` if the expected container type is list.
2106 * `false` if the expected container type is set.
2108 * // Specific to dissect_thrift_(binary|compact)_binary.
2109 * // Present in dissect_thrift_(binary|compact)_type for forwarding purpose.
2110 * @param[in] header_tree: The proto_tree in which the length must be inserted.
2111 * If it is NULL, tree will be used instead.
2112 * Used in structs to push the length in the field header.
2114 * // Specific to dissect_thrift_(binary|compact)_type.
2115 * @param[in] type: The type for which data needs to be dissected.
2116 * Neither type nor field_id to dissect as there is no such "header" in list, set, and map.
2117 * @param[in] type_pi: The proto_item of the type field to indicate position of failure.
2120 /*=====BEGIN BINARY GENERIC DISSECTION=====*/
2122 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
)
2124 /* Binary protocol binary/string data (4 bytes + data):
2125 * +--------+--------+--------+--------+--------+ ... +--------+
2126 * | Number of bytes | N bytes of data |
2127 * +--------+--------+--------+--------+--------+ ... +--------+
2130 * Number of bytes is the number of encoded bytes of data.
2131 * In particular, it might be larger than the number
2132 * of characters in an UTF-8 string.
2136 ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_BINARY_LEN
);
2137 if (header_tree
== NULL
) {
2140 pi
= proto_tree_add_item_ret_int(header_tree
, hf_thrift_str_len
, tvb
, *offset
, TBP_THRIFT_LENGTH_LEN
, ENC_BIG_ENDIAN
, &str_len
);
2141 *offset
+= TBP_THRIFT_LENGTH_LEN
;
2142 if (header_tree
!= tree
) {
2143 proto_item_set_end(proto_tree_get_parent(header_tree
), tvb
, *offset
);
2147 expert_add_info(pinfo
, pi
, &ei_thrift_negative_length
);
2148 return THRIFT_REQUEST_REASSEMBLY
;
2151 return dissect_thrift_string_as_preferred(tvb
, pinfo
, tree
, offset
, thrift_opt
, str_len
);
2155 // NOLINTNEXTLINE(misc-no-recursion)
2156 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
)
2158 /* Binary protocol list and set (5 bytes + elements):
2159 * +--------+--------+--------+--------+--------+---...---+ ... +---...---+
2160 * |0000tttt| number of elements |element 1| |element N|
2161 * +--------+--------+--------+--------+--------+---...---+ ... +---...---+
2163 * Binary protocol map (6 bytes + key-value pairs):
2164 * +--------+--------+--------+--------+--------+--------+--...--+---...---+ ... +--...--+---...---+
2165 * |0000kkkk|0000vvvv| number of key+value pairs | key 1 | value 1 | | key N | value N |
2166 * +--------+--------+--------+--------+--------+--------+--...--+---...---+ ... +--...--+---...---+
2169 * 'tttt' is the type of the list or set elements, an unsigned 4 bits strictly positive integer.
2170 * 'kkkk' is the type of the map keys, an unsigned 4 bits strictly positive integer.
2171 * 'vvvv' is the type of the map values, an unsigned 4 bits strictly positive integer.
2173 proto_tree
*sub_tree
;
2174 proto_item
*container_pi
, *len_pi
, *vtype_pi
;
2175 proto_item
*ktype_pi
= NULL
; // Avoid a false positive warning.
2176 int32_t ktype
, vtype
;
2177 int32_t container_len
, i
;
2179 int hf_container
= 0;
2180 int hf_num_item
= 0;
2181 int hf_vtype
= hf_thrift_type
;
2182 int min_len
= TBP_THRIFT_LINEAR_LEN
;
2183 unsigned nested_count
= p_get_proto_depth(pinfo
, proto_thrift
);
2185 /* Set the different hf_id & ett depending on effective type. */
2187 case DE_THRIFT_T_SET
:
2188 ett
= ett_thrift_set
;
2189 hf_container
= hf_thrift_set
;
2190 hf_num_item
= hf_thrift_num_set_item
;
2192 case DE_THRIFT_T_LIST
:
2193 ett
= ett_thrift_list
;
2194 hf_container
= hf_thrift_list
;
2195 hf_num_item
= hf_thrift_num_list_item
;
2197 case DE_THRIFT_T_MAP
:
2198 ett
= ett_thrift_map
;
2199 hf_container
= hf_thrift_map
;
2200 hf_num_item
= hf_thrift_num_map_item
;
2201 hf_vtype
= hf_thrift_value_type
; /* Use specific hf_info as we have several types. */
2202 min_len
+= TBP_THRIFT_TYPE_LEN
; /* Additional type key + value instead of element only. */
2205 REPORT_DISSECTOR_BUG("dissect_thrift_binary_linear called with something else than a container type.");
2208 ABORT_ON_INCOMPLETE_PDU(min_len
);
2210 /* Create the sub-tree. */
2211 if (nested_count
>= thrift_opt
->nested_type_depth
) {
2212 expert_add_info(pinfo
, proto_tree_get_parent(tree
), &ei_thrift_too_many_subtypes
);
2213 return THRIFT_REQUEST_REASSEMBLY
;
2215 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
+ 1);
2216 container_pi
= proto_tree_add_item(tree
, hf_container
, tvb
, *offset
, -1, ENC_NA
);
2217 sub_tree
= proto_item_add_subtree(container_pi
, ett
);
2219 /* Read the type of the key in case of map. */
2220 if (expected
== DE_THRIFT_T_MAP
) {
2221 ktype_pi
= proto_tree_add_item_ret_uint(sub_tree
, hf_thrift_key_type
, tvb
, *offset
, TBP_THRIFT_TYPE_LEN
, ENC_BIG_ENDIAN
, &ktype
);
2222 *offset
+= TBP_THRIFT_TYPE_LEN
;
2224 /* Read the type of the elements (or type of the values in case of map). */
2225 vtype_pi
= proto_tree_add_item_ret_uint(sub_tree
, hf_vtype
, tvb
, *offset
, TBP_THRIFT_TYPE_LEN
, ENC_BIG_ENDIAN
, &vtype
);
2226 *offset
+= TBP_THRIFT_TYPE_LEN
;
2227 /* Read and check the number of entries of the container. */
2228 len_pi
= proto_tree_add_item_ret_int(sub_tree
, hf_num_item
, tvb
, *offset
, TBP_THRIFT_LENGTH_LEN
, ENC_BIG_ENDIAN
, &container_len
);
2229 *offset
+= TBP_THRIFT_LENGTH_LEN
;
2230 if (container_len
< 0) {
2231 expert_add_info(pinfo
, len_pi
, &ei_thrift_negative_length
);
2232 return THRIFT_REQUEST_REASSEMBLY
;
2235 /* Read the content of the container. */
2236 for (i
= 0; i
< container_len
; ++i
) {
2237 if (expected
== DE_THRIFT_T_MAP
) {
2238 if (dissect_thrift_binary_type(tvb
, pinfo
, sub_tree
, offset
, thrift_opt
, NULL
, ktype
, ktype_pi
) == THRIFT_REQUEST_REASSEMBLY
)
2239 return THRIFT_REQUEST_REASSEMBLY
;
2241 if (dissect_thrift_binary_type(tvb
, pinfo
, sub_tree
, offset
, thrift_opt
, NULL
, vtype
, vtype_pi
) == THRIFT_REQUEST_REASSEMBLY
)
2242 return THRIFT_REQUEST_REASSEMBLY
;
2244 proto_item_set_end(container_pi
, tvb
, *offset
);
2246 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
);
2251 // NOLINTNEXTLINE(misc-no-recursion)
2252 dissect_thrift_binary_list(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
, thrift_option_data_t
*thrift_opt
)
2254 return dissect_thrift_binary_linear(tvb
, pinfo
, tree
, offset
, thrift_opt
, DE_THRIFT_T_LIST
);
2258 // NOLINTNEXTLINE(misc-no-recursion)
2259 dissect_thrift_binary_set(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
, thrift_option_data_t
*thrift_opt
)
2261 return dissect_thrift_binary_linear(tvb
, pinfo
, tree
, offset
, thrift_opt
, DE_THRIFT_T_SET
);
2265 // NOLINTNEXTLINE(misc-no-recursion)
2266 dissect_thrift_binary_map(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
, thrift_option_data_t
*thrift_opt
)
2268 return dissect_thrift_binary_linear(tvb
, pinfo
, tree
, offset
, thrift_opt
, DE_THRIFT_T_MAP
);
2272 // NOLINTNEXTLINE(misc-no-recursion)
2273 dissect_thrift_binary_fields(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
, thrift_option_data_t
*thrift_opt
)
2276 * Binary protocol field header (3 bytes) and field value:
2277 * +--------+--------+--------+--------+...+--------+
2278 * |0000tttt| field id | field value |
2279 * +--------+--------+--------+--------+...+--------+
2281 * Binary & Compact protocol stop field (1 byte):
2287 * 'dddd' is the field id delta, a strictly positive unsigned 4 bits integer.
2288 * 'tttt' is the type of the field value, an unsigned 4 bits strictly positive integer.
2289 * field id is the numerical value of the field in the structure.
2290 * field value is the encoded value.
2293 /* This function does not create the subtree, it's the responsibility of the caller:
2294 * - either dissect_thrift_common which creates the "Data" sub-tree,
2295 * - or dissect_thrift_binary_struct which creates the "Struct" sub-tree.
2297 thrift_field_header_t field_header
= {
2298 .type
.binary
= DE_THRIFT_T_STOP
, // Overwritten by dissect_thrift_field_header() but undetected by Clang.
2301 thrift_opt
->previous_field_id
= 0;
2303 if (dissect_thrift_field_header(tvb
, pinfo
, tree
, offset
, thrift_opt
, &field_header
, true) == THRIFT_REQUEST_REASSEMBLY
) {
2304 return THRIFT_REQUEST_REASSEMBLY
;
2306 if (field_header
.type
.binary
== DE_THRIFT_T_STOP
) {
2309 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
) {
2310 return THRIFT_REQUEST_REASSEMBLY
;
2318 // NOLINTNEXTLINE(misc-no-recursion)
2319 dissect_thrift_binary_struct(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
, thrift_option_data_t
*thrift_opt
)
2321 /* This function only creates the "Struct" sub-tree
2322 * then it delegates the fields dissection to dissect_thrift_binary_fields.
2324 proto_tree
*sub_tree
;
2326 unsigned nested_count
= p_get_proto_depth(pinfo
, proto_thrift
);
2328 ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_STRUCT_LEN
);
2329 if (nested_count
>= thrift_opt
->nested_type_depth
) {
2330 expert_add_info(pinfo
, proto_tree_get_parent(tree
), &ei_thrift_too_many_subtypes
);
2331 return THRIFT_REQUEST_REASSEMBLY
;
2333 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
+ 1);
2334 pi
= proto_tree_add_item(tree
, hf_thrift_struct
, tvb
, *offset
, -1, ENC_NA
);
2335 sub_tree
= proto_item_add_subtree(pi
, ett_thrift_struct
);
2337 if (dissect_thrift_binary_fields(tvb
, pinfo
, sub_tree
, offset
, thrift_opt
) == THRIFT_REQUEST_REASSEMBLY
) {
2338 return THRIFT_REQUEST_REASSEMBLY
;
2340 proto_item_set_end(pi
, tvb
, *offset
);
2342 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
);
2347 // NOLINTNEXTLINE(misc-no-recursion)
2348 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
)
2351 case DE_THRIFT_T_BOOL
:
2352 ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_BOOL_LEN
);
2353 proto_tree_add_item(tree
, hf_thrift_bool
, tvb
, *offset
, TBP_THRIFT_BOOL_LEN
, ENC_BIG_ENDIAN
);
2354 *offset
+= TBP_THRIFT_BOOL_LEN
;
2356 case DE_THRIFT_T_I8
:
2357 ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_I8_LEN
);
2358 proto_tree_add_item(tree
, hf_thrift_i8
, tvb
, *offset
, TBP_THRIFT_I8_LEN
, ENC_BIG_ENDIAN
);
2359 *offset
+= TBP_THRIFT_I8_LEN
;
2361 case DE_THRIFT_T_I16
:
2362 ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_I16_LEN
);
2363 proto_tree_add_item(tree
, hf_thrift_i16
, tvb
, *offset
, TBP_THRIFT_I16_LEN
, ENC_BIG_ENDIAN
);
2364 *offset
+= TBP_THRIFT_I16_LEN
;
2366 case DE_THRIFT_T_I32
:
2367 ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_I32_LEN
);
2368 proto_tree_add_item(tree
, hf_thrift_i32
, tvb
, *offset
, TBP_THRIFT_I32_LEN
, ENC_BIG_ENDIAN
);
2369 *offset
+= TBP_THRIFT_I32_LEN
;
2371 case DE_THRIFT_T_I64
:
2372 ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_I64_LEN
);
2373 proto_tree_add_item(tree
, hf_thrift_i64
, tvb
, *offset
, TBP_THRIFT_I64_LEN
, ENC_BIG_ENDIAN
);
2374 *offset
+= TBP_THRIFT_I64_LEN
;
2376 case DE_THRIFT_T_DOUBLE
:
2377 ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_DOUBLE_LEN
);
2378 proto_tree_add_item(tree
, hf_thrift_double
, tvb
, *offset
, TBP_THRIFT_DOUBLE_LEN
, ENC_BIG_ENDIAN
);
2379 *offset
+= TBP_THRIFT_DOUBLE_LEN
;
2381 case DE_THRIFT_T_UUID
:
2382 ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_UUID_LEN
);
2383 proto_tree_add_item(tree
, hf_thrift_uuid
, tvb
, *offset
, TBP_THRIFT_UUID_LEN
, ENC_BIG_ENDIAN
);
2384 *offset
+= TBP_THRIFT_UUID_LEN
;
2386 case DE_THRIFT_T_BINARY
:
2387 if (dissect_thrift_binary_binary(tvb
, pinfo
, tree
, offset
, thrift_opt
, header_tree
) == THRIFT_REQUEST_REASSEMBLY
) {
2388 return THRIFT_REQUEST_REASSEMBLY
;
2391 case DE_THRIFT_T_LIST
:
2392 if (dissect_thrift_binary_list(tvb
, pinfo
, tree
, offset
, thrift_opt
) == THRIFT_REQUEST_REASSEMBLY
) {
2393 return THRIFT_REQUEST_REASSEMBLY
;
2396 case DE_THRIFT_T_SET
:
2397 if (dissect_thrift_binary_set(tvb
, pinfo
, tree
, offset
, thrift_opt
) == THRIFT_REQUEST_REASSEMBLY
) {
2398 return THRIFT_REQUEST_REASSEMBLY
;
2401 case DE_THRIFT_T_MAP
:
2402 if (dissect_thrift_binary_map(tvb
, pinfo
, tree
, offset
, thrift_opt
) == THRIFT_REQUEST_REASSEMBLY
) {
2403 return THRIFT_REQUEST_REASSEMBLY
;
2406 case DE_THRIFT_T_STRUCT
:
2407 if (dissect_thrift_binary_struct(tvb
, pinfo
, tree
, offset
, thrift_opt
) == THRIFT_REQUEST_REASSEMBLY
) {
2408 return THRIFT_REQUEST_REASSEMBLY
;
2413 expert_add_info(pinfo
, type_pi
, &ei_thrift_wrong_type
);
2414 return THRIFT_REQUEST_REASSEMBLY
;
2419 /*=====END BINARY GENERIC DISSECTION=====*/
2421 /*=====BEGIN COMPACT GENERIC DISSECTION=====*/
2423 * Generic functions for when there is no custom sub-dissector.
2425 * Use the same conventions (parameters & return values) as TBinaryProtocol.
2427 * See "GENERIC DISSECTION PARAMETERS DOCUMENTATION" comment.
2431 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
)
2433 /* Compact protocol binary/string data (1 to 5 bytes + data):
2434 * +--------+...+--------+--------+...+--------+
2435 * | number of bytes | N bytes of data |
2436 * +--------+...+--------+--------+...+--------+
2439 * Number of bytes is the number of encoded bytes of data encoded as an unsigned varint.
2440 * In particular, it might be larger than the number
2441 * of characters in an UTF-8 string.
2447 if (header_tree
== NULL
) {
2450 int len_len
= thrift_get_varint_enc(tvb
, pinfo
, header_tree
, *offset
, TCP_THRIFT_MAX_I32_LEN
, &varint
, ENC_VARINT_PROTOBUF
);
2453 case THRIFT_REQUEST_REASSEMBLY
:
2454 /* Will always return after setting the expert parts. */
2455 ABORT_ON_INCOMPLETE_PDU(TCP_THRIFT_MAX_I32_LEN
);
2456 return THRIFT_REQUEST_REASSEMBLY
; // Just to avoid a false positive warning.
2458 /* In case of error, the offset stay at the error position. */
2459 return THRIFT_REQUEST_REASSEMBLY
;
2464 if (header_tree
!= tree
) {
2465 proto_item_set_end(proto_tree_get_parent(header_tree
), tvb
, *offset
);
2467 if ((int64_t)INT32_MIN
> varint
|| varint
> (int64_t)INT32_MAX
) {
2468 pi
= proto_tree_add_int64(header_tree
, hf_thrift_i64
, tvb
, *offset
, len_len
, varint
);
2469 expert_add_info(pinfo
, pi
, &ei_thrift_varint_too_large
);
2470 return THRIFT_REQUEST_REASSEMBLY
;
2472 str_len
= (int32_t)varint
;
2473 pi
= proto_tree_add_int(header_tree
, hf_thrift_str_len
, tvb
, *offset
, len_len
, str_len
);
2475 expert_add_info(pinfo
, pi
, &ei_thrift_negative_length
);
2476 return THRIFT_REQUEST_REASSEMBLY
;
2479 return dissect_thrift_string_as_preferred(tvb
, pinfo
, tree
, offset
, thrift_opt
, str_len
);
2483 // NOLINTNEXTLINE(misc-no-recursion)
2484 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
)
2486 /* Compact protocol list/set (short form, 1 byte):
2487 * +--------+--------+...+--------+
2488 * |nnnntttt| nnnn elements |
2489 * +--------+--------+...+--------+
2491 * Compact protocol list/set (long form, 2+ bytes):
2492 * +--------+--------+...+--------+--------+...+--------+
2493 * |1111tttt| number of elements | elements |
2494 * +--------+--------+...+--------+--------+...+--------+
2497 * 'nnnn' is the number of elements if between 0 and 14 included encoded as a 4 bits unsigned integer.
2498 * 'tttt' is the type of the elements (using the same convention as TBinaryProtocol, unlike compact structures.
2499 * '1111' indicates that the number of elements is encoded as an unsigned 32 bits varint (for number >= 15).
2501 proto_tree
*sub_tree
;
2502 proto_item
*container_pi
, *type_pi
, *len_pi
;
2503 uint32_t len_type
, type
;
2504 int32_t container_len
, len_len
, i
;
2506 int lt_offset
= *offset
;
2507 int ett
= ett_thrift_set
;
2508 int hf_container
= hf_thrift_set
;
2509 int hf_num_item
= hf_thrift_num_set_item
;
2510 int hf_pos_item
= hf_thrift_num_set_pos
;
2511 unsigned nested_count
= p_get_proto_depth(pinfo
, proto_thrift
);
2512 ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_TYPE_LEN
);
2514 /* Set the different hf_id & ett depending on effective type. */
2516 ett
= ett_thrift_list
;
2517 hf_container
= hf_thrift_list
;
2518 hf_num_item
= hf_thrift_num_list_item
;
2519 hf_pos_item
= hf_thrift_num_list_pos
;
2522 /* Create the sub-tree. */
2523 if (nested_count
>= thrift_opt
->nested_type_depth
) {
2524 expert_add_info(pinfo
, proto_tree_get_parent(tree
), &ei_thrift_too_many_subtypes
);
2525 return THRIFT_REQUEST_REASSEMBLY
;
2527 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
+ 1);
2528 container_pi
= proto_tree_add_item(tree
, hf_container
, tvb
, *offset
, -1, ENC_NA
);
2529 sub_tree
= proto_item_add_subtree(container_pi
, ett
);
2531 /* Read the type of the elements (and length if lower than 15). */
2532 len_type
= tvb_get_uint8(tvb
, lt_offset
);
2533 *offset
+= TBP_THRIFT_TYPE_LEN
;
2534 type
= len_type
& TCP_THRIFT_NIBBLE_MASK
;
2535 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
);
2536 container_len
= (len_type
>> TCP_THRIFT_NIBBLE_SHIFT
) & TCP_THRIFT_NIBBLE_MASK
;
2538 /* Read and check the number of entries of the container. */
2539 if (container_len
== TCP_THRIFT_LENGTH_LARGER
) {
2540 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
);
2541 /* Length is greater than 14, read effective length as a varint. */
2542 len_len
= thrift_get_varint_enc(tvb
, pinfo
, sub_tree
, *offset
, TCP_THRIFT_MAX_I32_LEN
, &varint
, ENC_VARINT_PROTOBUF
);
2544 case THRIFT_REQUEST_REASSEMBLY
:
2545 /* Will always return after setting the expert parts. */
2546 ABORT_ON_INCOMPLETE_PDU(TCP_THRIFT_MAX_I32_LEN
);
2547 return THRIFT_REQUEST_REASSEMBLY
; // Just to avoid a false positive warning.
2549 /* In case of error, the offset stay at the error position. */
2550 return THRIFT_REQUEST_REASSEMBLY
;
2552 if (varint
> (uint64_t)INT32_MAX
) {
2553 len_pi
= proto_tree_add_int64(sub_tree
, hf_thrift_i64
, tvb
, *offset
, len_len
, varint
);
2554 expert_add_info(pinfo
, len_pi
, &ei_thrift_varint_too_large
);
2555 return THRIFT_REQUEST_REASSEMBLY
;
2557 container_len
= (uint32_t)varint
;
2558 len_pi
= proto_tree_add_int(sub_tree
, hf_num_item
, tvb
, *offset
, len_len
, container_len
);
2563 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
);
2565 if (container_len
< 0) {
2566 expert_add_info(pinfo
, len_pi
, &ei_thrift_negative_length
);
2567 return THRIFT_REQUEST_REASSEMBLY
;
2570 /* Read the content of the container. */
2571 for (i
= 0; i
< container_len
; ++i
) {
2572 if (dissect_thrift_compact_type(tvb
, pinfo
, sub_tree
, offset
, thrift_opt
, NULL
, type
, type_pi
) == THRIFT_REQUEST_REASSEMBLY
) {
2573 return THRIFT_REQUEST_REASSEMBLY
;
2576 proto_item_set_end(container_pi
, tvb
, *offset
);
2578 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
);
2583 // NOLINTNEXTLINE(misc-no-recursion)
2584 dissect_thrift_compact_list(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
, thrift_option_data_t
*thrift_opt
)
2586 return dissect_thrift_compact_list_set(tvb
, pinfo
, tree
, offset
, thrift_opt
, true);
2590 // NOLINTNEXTLINE(misc-no-recursion)
2591 dissect_thrift_compact_set(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
, thrift_option_data_t
*thrift_opt
)
2593 return dissect_thrift_compact_list_set(tvb
, pinfo
, tree
, offset
, thrift_opt
, false);
2597 // NOLINTNEXTLINE(misc-no-recursion)
2598 dissect_thrift_compact_map(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
, thrift_option_data_t
*thrift_opt
)
2600 /* Compact protocol map header (1 byte, empty map):
2605 * Compact protocol map (4+ bytes, non empty map) and key-value pairs:
2606 * +--------+...+--------+--------+--...--+---...---+ ... +--...--+---...---+
2607 * | number of elements |kkkkvvvv| key 1 | value 1 | | key N | value N |
2608 * +--------+...+--------+--------+--...--+---...---+ ... +--...--+---...---+
2611 * nb of elts is the number of key + value pairs, encoded as an unsigned 32 bits varint.
2612 * If this varint is null (map is empty), the types are not encoded at all.
2613 * 'kkkk' is the type of the map keys, an unsigned 4 bits strictly positive integer.
2614 * 'vvvv' is the type of the map values, an unsigned 4 bits strictly positive integer.
2617 proto_tree
*sub_tree
;
2618 proto_item
*container_pi
, *len_pi
, *ktype_pi
, *vtype_pi
;
2619 uint32_t types
, ktype
, vtype
;
2620 int32_t container_len
, len_len
, i
;
2622 unsigned nested_count
= p_get_proto_depth(pinfo
, proto_thrift
);
2624 ABORT_ON_INCOMPLETE_PDU(TCP_THRIFT_MIN_VARINT_LEN
);
2625 /* Create the sub-tree. */
2626 if (nested_count
>= thrift_opt
->nested_type_depth
) {
2627 expert_add_info(pinfo
, proto_tree_get_parent(tree
), &ei_thrift_too_many_subtypes
);
2628 return THRIFT_REQUEST_REASSEMBLY
;
2630 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
+ 1);
2631 container_pi
= proto_tree_add_item(tree
, hf_thrift_map
, tvb
, *offset
, -1, ENC_NA
);
2632 sub_tree
= proto_item_add_subtree(container_pi
, ett_thrift_map
);
2634 /* Read and check number of key-value pair in the map. */
2635 len_len
= thrift_get_varint_enc(tvb
, pinfo
, sub_tree
, *offset
, TCP_THRIFT_MAX_I32_LEN
, &varint
, ENC_VARINT_PROTOBUF
);
2637 case THRIFT_REQUEST_REASSEMBLY
:
2638 /* Will always return after setting the expert parts. */
2639 ABORT_ON_INCOMPLETE_PDU(TCP_THRIFT_MAX_I32_LEN
);
2640 return THRIFT_REQUEST_REASSEMBLY
; // Just to avoid a false positive warning.
2642 /* In case of error, the offset stay at the error position. */
2643 return THRIFT_REQUEST_REASSEMBLY
;
2645 if (varint
> (uint64_t)INT32_MAX
) {
2646 len_pi
= proto_tree_add_int64(sub_tree
, hf_thrift_i64
, tvb
, *offset
, len_len
, varint
);
2647 expert_add_info(pinfo
, len_pi
, &ei_thrift_varint_too_large
);
2648 return THRIFT_REQUEST_REASSEMBLY
;
2650 container_len
= (uint32_t)varint
;
2651 len_pi
= proto_tree_add_int(sub_tree
, hf_thrift_num_map_item
, tvb
, *offset
, len_len
, container_len
);
2655 if (container_len
< 0) {
2656 expert_add_info(pinfo
, len_pi
, &ei_thrift_negative_length
);
2657 return THRIFT_REQUEST_REASSEMBLY
;
2660 /* Do not try to read the key & value types of an empty map. */
2661 if (container_len
> 0) {
2662 /* If the map is not empty, read the types of keys and values. */
2663 types
= tvb_get_uint8(tvb
, *offset
);
2664 ktype
= (types
>> TCP_THRIFT_NIBBLE_SHIFT
) & TCP_THRIFT_NIBBLE_MASK
;
2665 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
);
2666 vtype
= types
& TCP_THRIFT_NIBBLE_MASK
;
2667 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
);
2668 *offset
+= TCP_THRIFT_MAP_TYPES_LEN
;
2670 /* Read the content of the container. */
2671 for (i
= 0; i
< container_len
; ++i
) {
2672 if (dissect_thrift_compact_type(tvb
, pinfo
, sub_tree
, offset
, thrift_opt
, NULL
, ktype
, ktype_pi
) == THRIFT_REQUEST_REASSEMBLY
) {
2673 return THRIFT_REQUEST_REASSEMBLY
;
2675 if (dissect_thrift_compact_type(tvb
, pinfo
, sub_tree
, offset
, thrift_opt
, NULL
, vtype
, vtype_pi
) == THRIFT_REQUEST_REASSEMBLY
) {
2676 return THRIFT_REQUEST_REASSEMBLY
;
2680 proto_item_set_end(container_pi
, tvb
, *offset
);
2682 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
);
2687 // NOLINTNEXTLINE(misc-no-recursion)
2688 dissect_thrift_compact_fields(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
, thrift_option_data_t
*thrift_opt
)
2691 * Compact protocol field header (1 byte, short form) and field value:
2692 * +--------+--------+...+--------+
2693 * |ddddtttt| field value |
2694 * +--------+--------+...+--------+
2696 * Compact protocol field header (2 to 4 bytes, long form) and field value:
2697 * +--------+--------+...+--------+--------+...+--------+
2698 * |0000tttt| field id | field value |
2699 * +--------+--------+...+--------+--------+...+--------+
2701 * Compact protocol stop field (1 byte):
2708 * 'dddd' is the field id delta, a strictly positive unsigned 4 bits integer.
2709 * 'tttt' is the type of the field value, a strictly positive unsigned 4 bits integer.
2710 * field id is the numerical value of the field in the structure.
2711 * field value is the encoded value.
2714 /* This function does not create the subtree, it's the responsibility of the caller:
2715 * - either dissect_thrift_common which creates the "Data" sub-tree,
2716 * - or dissect_thrift_compact_struct which creates the "Struct" sub-tree.
2718 thrift_field_header_t field_header
= {
2719 .type
.compact
= DE_THRIFT_C_STOP
, // Overwritten by dissect_thrift_field_header() but undetected by Clang.
2722 thrift_opt
->previous_field_id
= 0;
2724 if (dissect_thrift_field_header(tvb
, pinfo
, tree
, offset
, thrift_opt
, &field_header
, true) == THRIFT_REQUEST_REASSEMBLY
) {
2725 return THRIFT_REQUEST_REASSEMBLY
;
2727 if (field_header
.type
.compact
== DE_THRIFT_C_STOP
) {
2728 break; /* Need to break out of the loop, cannot do that in the switch. */
2730 if (!is_thrift_compact_bool_type(field_header
.type
.compact
) &&
2731 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
) {
2732 return THRIFT_REQUEST_REASSEMBLY
;
2734 thrift_opt
->previous_field_id
= field_header
.field_id
;
2741 // NOLINTNEXTLINE(misc-no-recursion)
2742 dissect_thrift_compact_struct(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int *offset
, thrift_option_data_t
*thrift_opt
)
2744 /* This function only creates the "Struct" sub-tree
2745 * then it delegates the fields dissection to dissect_thrift_compact_fields.
2747 proto_tree
*sub_tree
;
2749 unsigned nested_count
= p_get_proto_depth(pinfo
, proto_thrift
);
2751 ABORT_ON_INCOMPLETE_PDU(TCP_THRIFT_STRUCT_LEN
);
2752 if (nested_count
>= thrift_opt
->nested_type_depth
) {
2753 expert_add_info(pinfo
, proto_tree_get_parent(tree
), &ei_thrift_too_many_subtypes
);
2754 return THRIFT_REQUEST_REASSEMBLY
;
2756 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
+ 1);
2757 pi
= proto_tree_add_item(tree
, hf_thrift_struct
, tvb
, *offset
, -1, ENC_NA
);
2758 sub_tree
= proto_item_add_subtree(pi
, ett_thrift_struct
);
2760 if (dissect_thrift_compact_fields(tvb
, pinfo
, sub_tree
, offset
, thrift_opt
) == THRIFT_REQUEST_REASSEMBLY
) {
2761 return THRIFT_REQUEST_REASSEMBLY
;
2763 proto_item_set_end(pi
, tvb
, *offset
);
2765 p_set_proto_depth(pinfo
, proto_thrift
, nested_count
);
2769 /* Dissect a compact thrift field of a given type.
2771 * This function is used only for linear containers (list, set, map).
2772 * It uses the same type identifiers as TCompactProtocol, except for
2773 * the bool type which is encoded in the same way as BOOL_FALSE (2).
2776 // NOLINTNEXTLINE(misc-no-recursion)
2777 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
)
2780 case DE_THRIFT_C_BOOL_FALSE
:
2781 ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_BOOL_LEN
);
2782 proto_tree_add_item(tree
, hf_thrift_bool
, tvb
, *offset
, TBP_THRIFT_BOOL_LEN
, ENC_BIG_ENDIAN
);
2783 *offset
+= TBP_THRIFT_BOOL_LEN
;
2785 case DE_THRIFT_C_I8
:
2786 ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_I8_LEN
);
2787 proto_tree_add_item(tree
, hf_thrift_i8
, tvb
, *offset
, TBP_THRIFT_I8_LEN
, ENC_BIG_ENDIAN
);
2788 *offset
+= TBP_THRIFT_I8_LEN
;
2790 case DE_THRIFT_C_I16
:
2791 if (dissect_thrift_varint(tvb
, pinfo
, tree
, offset
, thrift_opt
, TCP_THRIFT_MAX_I16_LEN
, hf_thrift_i16
, NULL
) == THRIFT_REQUEST_REASSEMBLY
) {
2792 return THRIFT_REQUEST_REASSEMBLY
;
2795 case DE_THRIFT_C_I32
:
2796 if (dissect_thrift_varint(tvb
, pinfo
, tree
, offset
, thrift_opt
, TCP_THRIFT_MAX_I32_LEN
, hf_thrift_i32
, NULL
) == THRIFT_REQUEST_REASSEMBLY
) {
2797 return THRIFT_REQUEST_REASSEMBLY
;
2800 case DE_THRIFT_C_I64
:
2801 if (dissect_thrift_varint(tvb
, pinfo
, tree
, offset
, thrift_opt
, TCP_THRIFT_MAX_I64_LEN
, hf_thrift_i64
, NULL
) == THRIFT_REQUEST_REASSEMBLY
) {
2802 return THRIFT_REQUEST_REASSEMBLY
;
2805 case DE_THRIFT_C_DOUBLE
:
2806 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."
2813 proto_tree_add_item(tree
, hf_thrift_double
, tvb
, *offset
, TBP_THRIFT_DOUBLE_LEN
, ENC_LITTLE_ENDIAN
);
2814 *offset
+= TBP_THRIFT_DOUBLE_LEN
;
2816 case DE_THRIFT_C_UUID
:
2817 ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_UUID_LEN
);
2818 proto_tree_add_item(tree
, hf_thrift_uuid
, tvb
, *offset
, TBP_THRIFT_UUID_LEN
, ENC_BIG_ENDIAN
);
2819 *offset
+= TBP_THRIFT_UUID_LEN
;
2821 case DE_THRIFT_C_BINARY
:
2822 if (dissect_thrift_compact_binary(tvb
, pinfo
, tree
, offset
, thrift_opt
, header_tree
) == THRIFT_REQUEST_REASSEMBLY
) {
2823 return THRIFT_REQUEST_REASSEMBLY
;
2826 case DE_THRIFT_C_LIST
:
2827 if (dissect_thrift_compact_list(tvb
, pinfo
, tree
, offset
, thrift_opt
) == THRIFT_REQUEST_REASSEMBLY
) {
2828 return THRIFT_REQUEST_REASSEMBLY
;
2831 case DE_THRIFT_C_SET
:
2832 if (dissect_thrift_compact_set(tvb
, pinfo
, tree
, offset
, thrift_opt
) == THRIFT_REQUEST_REASSEMBLY
) {
2833 return THRIFT_REQUEST_REASSEMBLY
;
2836 case DE_THRIFT_C_MAP
:
2837 if (dissect_thrift_compact_map(tvb
, pinfo
, tree
, offset
, thrift_opt
) == THRIFT_REQUEST_REASSEMBLY
) {
2838 return THRIFT_REQUEST_REASSEMBLY
;
2841 case DE_THRIFT_C_STRUCT
:
2842 if (dissect_thrift_compact_struct(tvb
, pinfo
, tree
, offset
, thrift_opt
) == THRIFT_REQUEST_REASSEMBLY
) {
2843 return THRIFT_REQUEST_REASSEMBLY
;
2848 expert_add_info(pinfo
, type_pi
, &ei_thrift_wrong_type
);
2849 return THRIFT_REQUEST_REASSEMBLY
;
2854 /*=====END COMPACT GENERIC DISSECTION=====*/
2857 * End of generic functions
2861 Binary protocol Message, strict encoding, 13+ bytes:
2862 +--------+--------+--------+--------++--------+--------+--------+--------++--------+...+--------++--------+--------+--------+--------++...++--------+
2863 |1vvvvvvv|vvvvvvvv|unused |00000mmm|| name length || name || seq id || || T_STOP |
2864 +--------+--------+--------+--------++--------+--------+--------+--------++--------+...+--------++--------+--------+--------+--------++...++--------+
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.
2881 Binary protocol Message, old encoding, 9+ bytes:
2882 +--------+--------+--------+--------++--------+...+--------++--------++--------+--------+--------+--------++...++--------+
2883 | name length || name ||00000mmm|| seq id || || T_STOP |
2884 +--------+--------+--------+--------++--------+...+--------++--------++--------+--------+--------+--------++...++--------+
2886 Where name length, name, mmm, seq id are the same as above.
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.
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.
2896 Compact protocol Message (5+ bytes):
2897 +--------+--------+--------+...+--------+--------+...+--------+--------+...+--------+...+--------+
2898 |pppppppp|mmmvvvvv| seq id | name length | name | | T_STOP |
2899 +--------+--------+--------+...+--------+--------+...+--------+--------+...+--------+...+--------+
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.
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.
2913 Framed Transport can encapsulate any protocol version:
2914 +--------+--------+--------+--------+--------+...+--------+--------+
2915 | message length | Any protocol message, T_STOP |
2916 +--------+--------+--------+--------+--------+...+--------+--------+
2917 |<------ message length ------>|
2919 Message types are encoded with the following values:
2927 /*=====BEGIN HEADER GENERIC DISSECTION=====*/
2928 /* Dissect a unique Thrift TBinaryProtocol PDU and return the effective length of this PDU.
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
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.
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
2942 * This method MUST be called with non-null thrift_opt.
2945 dissect_thrift_common(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int offset
, thrift_option_data_t
*thrift_opt
)
2947 proto_tree
*thrift_tree
, *sub_tree
;
2948 proto_item
*thrift_pi
, *data_pi
;
2949 proto_item
*mtype_pi
= NULL
;
2950 proto_item
*fid_pi
= NULL
;
2951 int start_offset
= offset
;
2952 int header_offset
= 0, data_offset
= 0;
2953 int32_t seqid_len
= TCP_THRIFT_MAX_I32_LEN
;
2954 int32_t str_len_len
= TCP_THRIFT_MAX_I32_LEN
;
2957 int32_t str_len
, seq_id
;
2959 uint8_t *method_str
;
2962 int len
, tframe_length
= 0;
2963 bool is_framed
, is_compact
, request_reasm
;
2965 /* Get the current state of dissection. */
2966 DISSECTOR_ASSERT(thrift_opt
);
2967 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
2969 is_framed
= (thrift_opt
->tprotocol
& PROTO_THRIFT_FRAMED
) != 0;
2970 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 thrift_pi
= proto_tree_add_item(tree
, proto_thrift
, tvb
, offset
, -1, ENC_NA
);
2973 thrift_tree
= proto_item_add_subtree(thrift_pi
, ett_thrift
);
2974 data_pi
= thrift_pi
; /* Used for expert_add_info in case of reassembly before the sub-tree is created. */
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 tframe_length
= tvb_get_ntohil(tvb
, offset
);
2983 if (tframe_length
<= 0) {
2984 thrift_tree
= proto_item_add_subtree(thrift_pi
, ett_thrift_error
);
2985 data_pi
= proto_tree_add_item(thrift_tree
, hf_thrift_frame_length
, tvb
, offset
, TBP_THRIFT_LENGTH_LEN
, ENC_BIG_ENDIAN
);
2986 expert_add_info(pinfo
, data_pi
, &ei_thrift_negative_length
);
2989 proto_item_set_len(thrift_pi
, TBP_THRIFT_LENGTH_LEN
+ tframe_length
);
2990 /* Keep the same start point to avoid awkward offset calculations */
2991 offset
+= TBP_THRIFT_LENGTH_LEN
;
2994 header_offset
= offset
;
2995 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 DISSECTOR_ASSERT(remaining
>= tframe_length
);
3001 /****************************************************************/
3002 /* Decode the header depending on compact, strict (new) or old. */
3003 /****************************************************************/
3005 if (remaining
< TCP_THRIFT_MIN_MESSAGE_LEN
) {
3006 goto add_expert_and_reassemble
;
3008 /* Compact: proto_id|mtype+version|seqid|length|name */
3009 version
= tvb_get_ntohs(tvb
, offset
) & THRIFT_COMPACT_VERSION_VALUE_MASK
;
3010 mtype
= (tvb_get_ntohs(tvb
, offset
) & THRIFT_COMPACT_MESSAGE_MASK
) >> THRIFT_COMPACT_MESSAGE_SHIFT
;
3011 offset
+= TCP_THRIFT_VERSION_LEN
;
3012 /* Pass sequence id */
3013 seqid_len
= thrift_get_varint_enc(tvb
, pinfo
, tree
, offset
, TCP_THRIFT_MAX_I32_LEN
, &varint
, ENC_VARINT_ZIGZAG
);
3014 /* We use the same reassembly/error convention. */
3015 if (seqid_len
<= 0) {
3018 offset
+= seqid_len
;
3019 if (varint
> (int64_t)INT32_MAX
|| varint
< (int64_t)INT32_MIN
) {
3020 expert_add_info(pinfo
, thrift_pi
, &ei_thrift_varint_too_large
);
3021 /* Sequence id is only informative, we may be just fine. */
3023 seq_id
= (int32_t)varint
;
3024 /* Read length of method name */
3025 str_len_len
= thrift_get_varint_enc(tvb
, pinfo
, tree
, offset
, TCP_THRIFT_MAX_I32_LEN
, &varint
, ENC_VARINT_PROTOBUF
);
3026 if (str_len_len
<= 0) {
3029 if (varint
> (int64_t)INT32_MAX
) {
3030 expert_add_info(pinfo
, thrift_pi
, &ei_thrift_varint_too_large
);
3033 str_len
= (int32_t)varint
;
3035 expert_add_info(pinfo
, thrift_pi
, &ei_thrift_negative_length
);
3038 offset
+= str_len_len
;
3039 /* Set method name */
3040 if (tvb_reported_length_remaining(tvb
, offset
) < str_len
) {
3041 goto add_expert_and_reassemble
;
3043 method_str
= tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, str_len
, ENC_UTF_8
);
3045 } else if (thrift_opt
->tprotocol
& PROTO_THRIFT_STRICT
) {
3046 if (remaining
< TBP_THRIFT_STRICT_MIN_MESSAGE_LEN
) {
3047 goto add_expert_and_reassemble
;
3049 version
= tvb_get_ntohs(tvb
, offset
) & THRIFT_BINARY_VERSION_VALUE_MASK
;
3050 mtype
= tvb_get_uint8(tvb
, offset
+ TBP_THRIFT_MTYPE_OFFSET
) & THRIFT_BINARY_MESSAGE_MASK
;
3051 str_len
= tvb_get_ntohil(tvb
, offset
+ TBP_THRIFT_VERSION_LEN
);
3053 expert_add_info(pinfo
, thrift_pi
, &ei_thrift_negative_length
);
3056 if (remaining
< TBP_THRIFT_STRICT_MIN_MESSAGE_LEN
+ str_len
) {
3057 goto add_expert_and_reassemble
;
3059 offset
+= TBP_THRIFT_VERSION_LEN
+ TBP_THRIFT_LENGTH_LEN
;
3060 method_str
= tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, str_len
, ENC_UTF_8
);
3063 seq_id
= tvb_get_ntohil(tvb
, offset
);
3064 offset
+= TBP_THRIFT_SEQ_ID_LEN
;
3066 if (remaining
< TBP_THRIFT_MIN_MESSAGE_LEN
) {
3067 goto add_expert_and_reassemble
;
3070 str_len
= tvb_get_ntohil(tvb
, offset
);
3072 expert_add_info(pinfo
, thrift_pi
, &ei_thrift_negative_length
);
3075 if (remaining
< TBP_THRIFT_MIN_MESSAGE_LEN
+ str_len
) {
3076 goto add_expert_and_reassemble
;
3078 offset
+= TBP_THRIFT_LENGTH_LEN
;
3079 method_str
= tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, str_len
, ENC_UTF_8
);
3081 mtype
= tvb_get_uint8(tvb
, offset
+ TBP_THRIFT_LENGTH_LEN
+ str_len
) & THRIFT_BINARY_MESSAGE_MASK
;
3082 offset
+= TBP_THRIFT_TYPE_LEN
;
3084 seq_id
= tvb_get_ntohil(tvb
, offset
);
3085 offset
+= TBP_THRIFT_SEQ_ID_LEN
;
3088 data_offset
= offset
;
3090 /* Can be used in case of error, in particular when TFramedTransport is in use. */
3091 thrift_opt
->reassembly_tree
= thrift_tree
;
3092 thrift_opt
->reassembly_offset
= start_offset
;
3093 thrift_opt
->reassembly_length
= -1;
3094 thrift_opt
->mtype
= (thrift_method_type_enum_t
)mtype
;
3096 /*****************************************************/
3097 /* Create the header tree with the extracted fields. */
3098 /*****************************************************/
3099 col_append_sep_fstr(pinfo
->cinfo
, COL_INFO
, ", ", "%s %s", val_to_str(mtype
, thrift_mtype_vals
, "%d"), method_str
);
3102 offset
= start_offset
; /* Reset parsing position. */
3104 proto_tree_add_item(thrift_tree
, hf_thrift_frame_length
, tvb
, offset
, TBP_THRIFT_LENGTH_LEN
, ENC_BIG_ENDIAN
);
3105 offset
+= TBP_THRIFT_LENGTH_LEN
;
3107 sub_tree
= proto_tree_add_subtree_format(thrift_tree
, tvb
, header_offset
, data_offset
- header_offset
, ett_thrift_header
, &data_pi
,
3108 "%s [version: %d, seqid: %d, method: %s]",
3109 val_to_str(mtype
, thrift_mtype_vals
, "%d"),
3110 version
, seq_id
, method_str
);
3111 /* Decode the header depending on compact, strict (new) or old. */
3113 /* Compact: proto_id|mtype+version|seqid|length|name */
3114 proto_tree_add_item(sub_tree
, hf_thrift_protocol_id
, tvb
, offset
, TBP_THRIFT_TYPE_LEN
, ENC_BIG_ENDIAN
);
3115 proto_tree_add_bits_item(sub_tree
, hf_thrift_version
, tvb
, (offset
<< OCTETS_TO_BITS_SHIFT
) + 11, 5, ENC_BIG_ENDIAN
);
3116 mtype_pi
= proto_tree_add_bits_item(sub_tree
, hf_thrift_mtype
, tvb
, (offset
<< OCTETS_TO_BITS_SHIFT
) + 8, 3, ENC_BIG_ENDIAN
);
3117 offset
+= TCP_THRIFT_VERSION_LEN
;
3118 proto_tree_add_int(sub_tree
, hf_thrift_seq_id
, tvb
, offset
, seqid_len
, seq_id
);
3119 offset
+= seqid_len
;
3120 proto_tree_add_int(sub_tree
, hf_thrift_str_len
, tvb
, offset
, str_len_len
, str_len
);
3121 offset
+= str_len_len
;
3122 proto_tree_add_item(sub_tree
, hf_thrift_method
, tvb
, offset
, str_len
, ENC_UTF_8
);
3123 offset
= offset
+ str_len
;
3124 } else if (thrift_opt
->tprotocol
& PROTO_THRIFT_STRICT
) {
3125 /* Strict: proto_id|version|mtype|length|name|seqid */
3126 proto_tree_add_item(sub_tree
, hf_thrift_protocol_id
, tvb
, offset
, TBP_THRIFT_TYPE_LEN
, ENC_BIG_ENDIAN
);
3127 proto_tree_add_bits_item(sub_tree
, hf_thrift_version
, tvb
, (offset
<< OCTETS_TO_BITS_SHIFT
) + 11, 5, ENC_BIG_ENDIAN
);
3128 offset
+= TBP_THRIFT_MTYPE_OFFSET
;
3129 mtype_pi
= proto_tree_add_bits_item(sub_tree
, hf_thrift_mtype
, tvb
, (offset
<< OCTETS_TO_BITS_SHIFT
) + 5, 3, ENC_BIG_ENDIAN
);
3130 offset
+= TBP_THRIFT_MTYPE_LEN
;
3131 proto_tree_add_item(sub_tree
, hf_thrift_str_len
, tvb
, offset
, TBP_THRIFT_LENGTH_LEN
, ENC_BIG_ENDIAN
);
3132 offset
+= TBP_THRIFT_LENGTH_LEN
;
3133 proto_tree_add_item(sub_tree
, hf_thrift_method
, tvb
, offset
, str_len
, ENC_UTF_8
);
3134 offset
= offset
+ str_len
;
3135 proto_tree_add_item(sub_tree
, hf_thrift_seq_id
, tvb
, offset
, TBP_THRIFT_SEQ_ID_LEN
, ENC_BIG_ENDIAN
);
3136 offset
+= TBP_THRIFT_SEQ_ID_LEN
;
3138 /* Old: length|name|mtype|seqid */
3139 proto_tree_add_item(sub_tree
, hf_thrift_str_len
, tvb
, offset
, TBP_THRIFT_LENGTH_LEN
, ENC_BIG_ENDIAN
);
3140 offset
+= TBP_THRIFT_LENGTH_LEN
;
3141 proto_tree_add_item(sub_tree
, hf_thrift_method
, tvb
, offset
, str_len
, ENC_UTF_8
);
3142 offset
= offset
+ str_len
;
3143 mtype_pi
= proto_tree_add_bits_item(sub_tree
, hf_thrift_mtype
, tvb
, (offset
<< OCTETS_TO_BITS_SHIFT
) + 5, 3, ENC_BIG_ENDIAN
);
3144 offset
+= TBP_THRIFT_MTYPE_LEN
;
3145 proto_tree_add_item(sub_tree
, hf_thrift_seq_id
, tvb
, offset
, TBP_THRIFT_SEQ_ID_LEN
, ENC_BIG_ENDIAN
);
3146 offset
+= TBP_THRIFT_SEQ_ID_LEN
;
3148 DISSECTOR_ASSERT(offset
== data_offset
);
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 if (tvb_reported_length_remaining(tvb
, data_offset
) < TBP_THRIFT_TYPE_LEN
) {
3154 goto add_expert_and_reassemble
;
3157 /***********************************************************/
3158 /* Call method dissector here using dissector_try_string_with_data() */
3159 /* except in case of EXCEPTION for detailed dissection. */
3160 /***********************************************************/
3161 thrift_opt
->previous_field_id
= 0;
3162 msg_tvb
= tvb_new_subset_remaining(tvb
, data_offset
);
3163 if (thrift_opt
->mtype
== ME_THRIFT_T_REPLY
) {
3164 thrift_field_header_t header
= {
3165 .field_id
= 0, // Overwritten by dissect_thrift_field_header() but undetected by Clang.
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.
3176 int result
= data_offset
;
3177 result
= dissect_thrift_field_header(tvb
, pinfo
, NULL
, &result
, thrift_opt
, &header
, false);
3179 case THRIFT_REQUEST_REASSEMBLY
:
3180 goto add_expert_and_reassemble
;
3181 case THRIFT_SUBDISSECTOR_ERROR
:
3186 thrift_opt
->reply_field_id
= header
.field_id
;
3187 fid_pi
= header
.fid_pi
;
3189 if (thrift_opt
->mtype
!= ME_THRIFT_T_EXCEPTION
) {
3190 if (pinfo
->can_desegment
> 0) pinfo
->can_desegment
++;
3191 len
= dissector_try_string_with_data(thrift_method_name_dissector_table
, method_str
, msg_tvb
, pinfo
, tree
, true, thrift_opt
);
3192 if (pinfo
->can_desegment
> 0) pinfo
->can_desegment
--;
3194 /* Attach the expert_info to the method type as it is a protocol-level exception. */
3195 expert_add_info(pinfo
, mtype_pi
, &ei_thrift_protocol_exception
);
3196 /* Leverage the sub-dissector capabilities to dissect Thrift exceptions. */
3197 len
= dissect_thrift_t_struct(msg_tvb
, pinfo
, thrift_tree
, 0, thrift_opt
, false, 0, hf_thrift_exception
, ett_thrift_exception
, thrift_exception
);
3200 /* The sub dissector dissected the tvb*/
3202 proto_item_set_end(thrift_pi
, msg_tvb
, len
);
3204 return data_offset
+ len
;
3205 } else if (len
== THRIFT_REQUEST_REASSEMBLY
) {
3206 /* The sub-dissector requested more bytes (len = -1) */
3207 goto reassemble_pdu
;
3208 } else if (len
<= THRIFT_SUBDISSECTOR_ERROR
) {
3209 /* All other negative values are treated as error codes (only -2 is recommended). */
3210 if (!try_generic_if_sub_dissector_fails
) {
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 p_set_proto_depth(pinfo
, proto_thrift
, 0);
3216 } /* else len = 0, no specific sub-dissector. */
3218 /***********************/
3219 /* Generic dissection. */
3220 /***********************/
3221 sub_tree
= proto_tree_add_subtree(thrift_tree
, tvb
, data_offset
, -1, ett_thrift_params
, &data_pi
, "Data");
3222 thrift_opt
->reassembly_length
= TBP_THRIFT_TYPE_LEN
;
3223 if (thrift_opt
->reply_field_id
!= 0) {
3224 expert_add_info(pinfo
, fid_pi
, &ei_thrift_application_exception
);
3225 proto_item_set_text(data_pi
, "Exception: %" PRId64
, thrift_opt
->reply_field_id
);
3229 request_reasm
= dissect_thrift_compact_fields(tvb
, pinfo
, sub_tree
, &offset
, thrift_opt
) == THRIFT_REQUEST_REASSEMBLY
;
3230 } else { /* Binary (strict/old distinction only applies to the header) */
3231 request_reasm
= dissect_thrift_binary_fields(tvb
, pinfo
, sub_tree
, &offset
, thrift_opt
) == THRIFT_REQUEST_REASSEMBLY
;
3233 /* Check the result of the Data part dissection. */
3234 if (request_reasm
) {
3236 /* An error occurred at the given offset, consume everything. */
3237 return tvb_reported_length(tvb
);
3238 } /* else It's really a reassembly request. */
3239 goto reassemble_pdu
;
3241 /* We found the end of the data. */
3242 proto_item_set_end(data_pi
, tvb
, offset
);
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. */
3247 /* In case the frame is larger than the data, we need to know the difference. */
3248 proto_item_set_end(thrift_pi
, tvb
, offset
);
3250 proto_item_set_end(data_pi
, tvb
, offset
);
3252 add_expert_and_reassemble
: /* When detected in this function. */
3253 expert_add_info(pinfo
, data_pi
, &ei_thrift_not_enough_data
);
3254 reassemble_pdu
: /* When detected by any called function (that already added the expert info). */
3255 /* We did not encounter final T_STOP. */
3256 pinfo
->desegment_offset
= start_offset
;
3257 pinfo
->desegment_len
= DESEGMENT_ONE_MORE_SEGMENT
;
3258 return THRIFT_REQUEST_REASSEMBLY
;
3261 /* For tcp_dissect_pdus. */
3263 get_framed_thrift_pdu_len(packet_info
*pinfo _U_
, tvbuff_t
*tvb
, int offset
, void *data _U_
)
3265 return (unsigned)TBP_THRIFT_LENGTH_LEN
+ tvb_get_ntohl(tvb
, offset
);
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.
3271 * This method MUST be called with non-null thrift_opt.
3274 dissect_thrift_loop(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, thrift_option_data_t
*thrift_opt
)
3277 int32_t hdr_offset
= 0;
3278 int32_t last_pdu_start_offset
= 0;
3279 int32_t remaining
= tvb_reported_length_remaining(tvb
, offset
);
3281 DISSECTOR_ASSERT(thrift_opt
);
3282 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
3284 /* Loop until the end of the packet coincides with the end of a PDU. */
3285 while (remaining
> 0) {
3286 last_pdu_start_offset
= offset
;
3287 if (remaining
< TBP_THRIFT_LENGTH_LEN
) {
3288 goto reassemble_pdu
;
3290 if (thrift_opt
->tprotocol
& PROTO_THRIFT_COMPACT
) {
3291 offset
= dissect_thrift_common(tvb
, pinfo
, tree
, offset
, thrift_opt
);
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 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 if (!is_thrift_strict_version(tvb_get_ntohl(tvb
, offset
+ hdr_offset
), true)) {
3301 expert_add_info(pinfo
, NULL
, &ei_thrift_wrong_proto_version
);
3302 return tvb_reported_length_remaining(tvb
, 0);
3304 thrift_opt
->tprotocol
= (thrift_protocol_enum_t
)(thrift_opt
->tprotocol
| PROTO_THRIFT_STRICT
);
3307 thrift_opt
->tprotocol
= (thrift_protocol_enum_t
)(thrift_opt
->tprotocol
& ~PROTO_THRIFT_STRICT
);
3309 offset
= dissect_thrift_common(tvb
, pinfo
, tree
, offset
, thrift_opt
);
3312 if (offset
== THRIFT_REQUEST_REASSEMBLY
) {
3313 goto reassemble_pdu
;
3314 } else if (offset
== 0) {
3315 /* An error occurred, we just stop, consuming everything. */
3316 return tvb_reported_length_remaining(tvb
, 0);
3318 remaining
= tvb_reported_length_remaining(tvb
, offset
);
3322 /* We did not encounter a final T_STOP exactly at the last byte. */
3323 pinfo
->desegment_offset
= last_pdu_start_offset
;
3324 pinfo
->desegment_len
= DESEGMENT_ONE_MORE_SEGMENT
;
3325 return tvb_reported_length(tvb
);
3328 /* Dissect a unique Thrift PDU within a TFramedTransport and return the effective length of this PDU.
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.
3333 * This method MUST be called with non-null thrift_opt/data using thrift_option_data_t effective type.
3336 dissect_thrift_framed(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
3339 int32_t frame_len
= 0;
3340 int32_t reported
= tvb_reported_length_remaining(tvb
, offset
);
3341 thrift_option_data_t
*thrift_opt
= (thrift_option_data_t
*)data
;
3343 DISSECTOR_ASSERT(thrift_opt
);
3344 DISSECTOR_ASSERT(thrift_opt
->canary
== THRIFT_OPTION_DATA_CANARY
);
3345 DISSECTOR_ASSERT(thrift_opt
->tprotocol
& PROTO_THRIFT_FRAMED
);
3346 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 DISSECTOR_ASSERT((frame_len
+ TBP_THRIFT_LENGTH_LEN
) <= reported
);
3351 offset
= dissect_thrift_common(tvb
, pinfo
, tree
, offset
, thrift_opt
);
3352 if (offset
== THRIFT_REQUEST_REASSEMBLY
) {
3353 /* No reassembly possible in this case */
3354 proto_tree_add_expert(thrift_opt
->reassembly_tree
, pinfo
, &ei_thrift_frame_too_short
,
3355 tvb
, thrift_opt
->reassembly_offset
, thrift_opt
->reassembly_length
);
3356 pinfo
->desegment_offset
= reported
;
3357 pinfo
->desegment_len
= 0;
3358 } else if (offset
> 0 && tvb_reported_length_remaining(tvb
, offset
) > 0) {
3359 proto_tree_add_expert(thrift_opt
->reassembly_tree
, pinfo
, &ei_thrift_frame_too_long
,
3360 tvb
, offset
, tvb_reported_length_remaining(tvb
, offset
));
3365 /* Thrift dissection when forced by Decode As… or port selection */
3367 dissect_thrift_transport(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
3369 int32_t str_len
, length
= tvb_reported_length(tvb
);
3370 thrift_option_data_t thrift_opt
;
3371 memset(&thrift_opt
, 0, sizeof(thrift_option_data_t
));
3372 thrift_opt
.nested_type_depth
= nested_type_depth
;
3374 /* Starting without even the version / frame length / name length probably means a Keep-Alive at the beginning of the capture. */
3375 if (length
< TBP_THRIFT_VERSION_LEN
) {
3376 if (tvb_get_uint8(tvb
, 0) != (THRIFT_COMPACT_VERSION_1
>> 8)) {
3377 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 return NOT_A_VALID_PDU
;
3380 } /* else this might be a compact capture without Nagle activated. */
3382 /* Need at least the old encoding header (Name Length + Method + Sequence Id) + ending T_STOP */
3383 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 goto reassemble_pdu
;
3394 /* MSb of first byte is 1 for compact and binary strict protocols
3395 * and 0 for framed transport and old binary protocol. */
3396 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 int remaining
= tvb_reported_length_remaining(tvb
, TBP_THRIFT_LENGTH_LEN
); /* Remaining after initial 4 bytes of "length" */
3403 str_len
= tvb_get_ntohil(tvb
, 0);
3405 if (remaining
== 0) {
3406 /* The endpoint probably does not have Nagle activated, wait for next packet. */
3407 goto reassemble_pdu
;
3409 /* Checking for old binary option. */
3410 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 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 return THRIFT_REQUEST_REASSEMBLY
;
3420 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 thrift_opt
.canary
= THRIFT_OPTION_DATA_CANARY
;
3425 thrift_opt
.tprotocol
= PROTO_THRIFT_BINARY
;
3426 /* Name length + name + method + seq_id + T_STOP */
3427 if (length
< TBP_THRIFT_MIN_MESSAGE_LEN
+ str_len
) {
3428 goto reassemble_pdu
;
3431 /* This cannot be non-framed old binary so it must be framed (and we have all of it). */
3432 if (str_len
< TBP_THRIFT_MIN_MESSAGE_LEN
) {
3433 /* This is /not/ valid Framed data. */
3434 return NOT_A_VALID_PDU
;
3436 if (tvb_get_int8(tvb
, TBP_THRIFT_LENGTH_LEN
) >= 0) {
3437 /* Framed old binary format is the only matching option remaining. */
3438 thrift_opt
.canary
= THRIFT_OPTION_DATA_CANARY
;
3439 thrift_opt
.tprotocol
= PROTO_THRIFT_FRAMED
;
3441 if (is_thrift_strict_version(tvb_get_ntohl(tvb
, TBP_THRIFT_LENGTH_LEN
), true)) {
3442 /* Framed strict binary protocol. */
3443 thrift_opt
.canary
= THRIFT_OPTION_DATA_CANARY
;
3444 thrift_opt
.tprotocol
= (thrift_protocol_enum_t
)(PROTO_THRIFT_FRAMED
| PROTO_THRIFT_STRICT
);
3446 /* Framed compact protocol or something else entirely, bail out. */
3447 return NOT_A_VALID_PDU
;
3451 } 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 thrift_opt
.canary
= THRIFT_OPTION_DATA_CANARY
;
3455 thrift_opt
.tprotocol
= PROTO_THRIFT_STRICT
;
3456 } else if (tvb_get_uint8(tvb
, 0) == 0x82) {
3457 /* Same thing here so 0x82 gives us the TCompactProtocol answer. */
3458 thrift_opt
.canary
= THRIFT_OPTION_DATA_CANARY
;
3459 thrift_opt
.tprotocol
= PROTO_THRIFT_COMPACT
;
3461 /* else { Not a Thrift packet. } */
3462 return NOT_A_VALID_PDU
;
3465 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "THRIFT");
3466 col_clear(pinfo
->cinfo
, COL_INFO
);
3468 if (thrift_opt
.tprotocol
& PROTO_THRIFT_FRAMED
) {
3469 tcp_dissect_pdus(tvb
, pinfo
, tree
, framed_desegment
, TBP_THRIFT_LENGTH_LEN
,
3470 get_framed_thrift_pdu_len
, dissect_thrift_framed
, &thrift_opt
);
3471 return tvb_reported_length(tvb
);
3473 return dissect_thrift_loop(tvb
, pinfo
, tree
, &thrift_opt
);
3477 pinfo
->desegment_offset
= 0;
3478 pinfo
->desegment_len
= DESEGMENT_ONE_MORE_SEGMENT
;
3479 return THRIFT_REQUEST_REASSEMBLY
;
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.
3490 test_thrift_strict(tvbuff_t
*tvb
, packet_info
*pinfo _U_
, proto_tree
*tree _U_
, thrift_option_data_t
*thrift_opt
)
3492 int tframe_length
= 0;
3494 unsigned length
= tvb_captured_length(tvb
);
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.
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). */
3509 /* Enough data for elements 2 to 6? */
3510 if (length
< (unsigned)TBP_THRIFT_STRICT_HEADER_LEN
) {
3514 /* 1. Check if it is framed (and if the frame length is large enough for a complete message). */
3515 if (tvb_get_int8(tvb
, offset
) >= 0) {
3517 tframe_length
= tvb_get_ntohil(tvb
, offset
);
3519 if (tframe_length
< TBP_THRIFT_STRICT_MIN_MESSAGE_LEN
) {
3522 offset
= TBP_THRIFT_LENGTH_LEN
; /* Strict header starts after frame length. */
3523 if (length
< (unsigned)(offset
+ TBP_THRIFT_STRICT_HEADER_LEN
)) {
3528 thrift_opt
->canary
= THRIFT_OPTION_DATA_CANARY
;
3529 /* Set the protocol used since we now have enough information. */
3530 thrift_opt
->tprotocol
= PROTO_THRIFT_STRICT
;
3531 if (tframe_length
> 0) {
3532 thrift_opt
->tprotocol
= (thrift_protocol_enum_t
)(thrift_opt
->tprotocol
| PROTO_THRIFT_FRAMED
);
3534 } else REPORT_DISSECTOR_BUG("%s called without data structure.", G_STRFUNC
);
3536 /* 2. Thrift version & method type (heuristic does /not/ ignore the message type). */
3537 if (!is_thrift_strict_version(tvb_get_ntohl(tvb
, offset
), false)) {
3540 offset
+= TBP_THRIFT_VERSION_LEN
;
3542 /* 3. Get method name length and check against what we have. */
3543 str_len
= tvb_get_ntohil(tvb
, offset
);
3544 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). */
3548 offset
+= TBP_THRIFT_LENGTH_LEN
;
3550 /* 4. Check method name itself. */
3551 if (tvb_captured_length_remaining(tvb
, offset
) < str_len
) {
3552 /* Method name is no entirely captured, we cannot check it. */
3555 if (thrift_binary_utf8_isprint(tvb
, offset
, str_len
, false) < str_len
) {
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 if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_LENGTH_LEN
+ TBP_THRIFT_TYPE_LEN
) {
3565 thrift_opt
->canary
= THRIFT_OPTION_DATA_CANARY
;
3569 /* Test if the captured packet matches a Thrift compact packet header.
3570 * Same comments as for test_thrift_strict.
3573 test_thrift_compact(tvbuff_t
*tvb
, packet_info
*pinfo _U_
, proto_tree
*tree _U_
, thrift_option_data_t
*thrift_opt
)
3575 int tframe_length
= 0;
3577 unsigned length
= tvb_captured_length(tvb
);
3579 int32_t str_len
= 0;
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.
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). */
3594 /* Enough data for elements 2 to 6? */
3595 if (length
< (unsigned)TCP_THRIFT_MIN_MESSAGE_LEN
) {
3599 /* 1. Check if it is framed (and if the frame length is large enough for a complete message). */
3600 if (tvb_get_int8(tvb
, offset
) >= 0) {
3602 tframe_length
= tvb_get_ntohil(tvb
, offset
);
3604 if (tframe_length
< TCP_THRIFT_MIN_MESSAGE_LEN
) {
3607 offset
= TBP_THRIFT_LENGTH_LEN
; /* Compact header starts after frame length. */
3608 if (length
< (unsigned)(offset
+ TCP_THRIFT_MIN_MESSAGE_LEN
)) {
3613 thrift_opt
->canary
= THRIFT_OPTION_DATA_CANARY
;
3614 /* Set the protocol used since we now have enough information. */
3615 if (tframe_length
> 0) {
3616 thrift_opt
->tprotocol
= (thrift_protocol_enum_t
)(PROTO_THRIFT_COMPACT
| PROTO_THRIFT_FRAMED
);
3618 thrift_opt
->tprotocol
= PROTO_THRIFT_COMPACT
;
3620 } else REPORT_DISSECTOR_BUG("%s called without data structure.", G_STRFUNC
);
3622 /* 2. Thrift version & method type (heuristic does /not/ ignore the message type). */
3623 if (!is_thrift_compact_version(tvb_get_ntohs(tvb
, offset
), false)) {
3626 offset
+= TCP_THRIFT_VERSION_LEN
;
3628 /* 3. Sequence id in varint encoding. We need to make sure we don't try to read not captured data. */
3629 len_len
= tvb_captured_length_remaining(tvb
, offset
);
3630 if (len_len
> TCP_THRIFT_MAX_I32_LEN
) {
3631 len_len
= TCP_THRIFT_MAX_I32_LEN
;
3633 len_len
= tvb_get_varint(tvb
, offset
, len_len
, &seq_id
, ENC_VARINT_ZIGZAG
);
3634 if (len_len
== 0) return false;
3637 /* 4. Get method name length and check against what we have. */
3638 if ((unsigned)offset
>= length
) return false;
3639 str_len
= tvb_get_uint8(tvb
, offset
);
3640 // MSb = 1 means the method length is greater than 127 bytes long.
3641 if ((str_len
& 0x80) != 0) {
3642 return false; // Reject it to avoid too many false positive.
3645 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). */
3650 /* 5. Check method name itself. */
3651 if (tvb_captured_length_remaining(tvb
, offset
) < str_len
) {
3652 /* Method name is no entirely captured, we cannot check it. */
3655 if (thrift_binary_utf8_isprint(tvb
, offset
, str_len
, false) < str_len
) {
3660 /* 6. Check that there is enough data remaining for a field type (but no need for it to be captured). */
3661 if (tvb_reported_length_remaining(tvb
, offset
) < TBP_THRIFT_TYPE_LEN
) {
3665 thrift_opt
->canary
= THRIFT_OPTION_DATA_CANARY
;
3669 /* Thrift heuristic dissection when the packet is not grabbed by another protocol dissector. */
3671 dissect_thrift_heur(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
3673 thrift_option_data_t thrift_opt
;
3674 memset(&thrift_opt
, 0, sizeof(thrift_option_data_t
));
3675 thrift_opt
.nested_type_depth
= nested_type_depth
;
3677 if (!test_thrift_strict(tvb
, pinfo
, tree
, &thrift_opt
) && !test_thrift_compact(tvb
, pinfo
, tree
, &thrift_opt
)) {
3681 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "THRIFT");
3682 col_clear(pinfo
->cinfo
, COL_INFO
);
3684 if (thrift_opt
.tprotocol
& PROTO_THRIFT_FRAMED
) {
3685 tcp_dissect_pdus(tvb
, pinfo
, tree
, framed_desegment
, TBP_THRIFT_LENGTH_LEN
,
3686 get_framed_thrift_pdu_len
, dissect_thrift_framed
, &thrift_opt
);
3688 dissect_thrift_loop(tvb
, pinfo
, tree
, &thrift_opt
);
3695 dissect_thrift_http(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
3697 return dissect_thrift_heur(tvb
, pinfo
, tree
, data
) ? tvb_captured_length(tvb
) : 0;
3699 /*=====END HEADER GENERIC DISSECTION=====*/
3702 proto_register_thrift(void)
3704 static hf_register_info hf
[] = {
3705 { &hf_thrift_frame_length
,
3706 { "Frame length", "thrift.frame_len",
3707 FT_INT32
, BASE_DEC
, NULL
, 0x0,
3710 { &hf_thrift_exception
,
3711 { "Exception", "thrift.exception",
3712 FT_NONE
, BASE_NONE
, NULL
, 0x0,
3715 { &hf_thrift_exception_message
,
3716 { "Exception Message", "thrift.exception.message",
3717 FT_STRING
, BASE_NONE
, NULL
, 0x0,
3720 { &hf_thrift_exception_type
,
3721 { "Exception Type", "thrift.exception.type",
3722 FT_INT32
, BASE_DEC
, VALS(thrift_exception_type_vals
), 0x0,
3725 { &hf_thrift_protocol_id
,
3726 { "Protocol id", "thrift.protocol_id",
3727 FT_UINT8
, BASE_HEX
, VALS(thrift_proto_vals
), 0x0,
3730 { &hf_thrift_version
,
3731 { "Version", "thrift.version",
3732 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
3736 { "Message type", "thrift.mtype",
3737 FT_UINT8
, BASE_HEX
, VALS(thrift_mtype_vals
), 0x0,
3740 { &hf_thrift_str_len
,
3741 { "Length", "thrift.str_len",
3742 FT_INT32
, BASE_DEC
, NULL
, 0x0,
3745 { &hf_thrift_method
,
3746 { "Method", "thrift.method",
3747 FT_STRING
, BASE_NONE
, NULL
, 0x0,
3750 { &hf_thrift_seq_id
,
3751 { "Sequence Id", "thrift.seq_id",
3752 FT_INT32
, BASE_DEC
, NULL
, 0x0,
3756 { "Type", "thrift.type",
3757 FT_UINT8
, BASE_HEX
, VALS(thrift_type_vals
), 0x0,
3760 { &hf_thrift_key_type
,
3761 { "Key Type", "thrift.type",
3762 FT_UINT8
, BASE_HEX
, VALS(thrift_type_vals
), 0x0,
3765 { &hf_thrift_value_type
,
3766 { "Value Type", "thrift.type",
3767 FT_UINT8
, BASE_HEX
, VALS(thrift_type_vals
), 0x0,
3770 { &hf_thrift_compact_struct_type
,
3771 { "Type", "thrift.type",
3772 FT_UINT8
, BASE_HEX
, VALS(thrift_compact_type_vals
), 0x0,
3776 { "Field Id", "thrift.fid",
3777 FT_INT16
, BASE_DEC
, NULL
, 0x0,
3780 { &hf_thrift_fid_delta
,
3781 { "Field Id Delta", "thrift.fid_delta",
3782 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
3786 { "Boolean", "thrift.bool",
3787 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0, /* libthrift (C++) also considers boolean value = (byte != 0x00) */
3791 { "Integer8", "thrift.i8",
3792 FT_INT8
, BASE_DEC
, NULL
, 0x0,
3796 { "Integer16", "thrift.i16",
3797 FT_INT16
, BASE_DEC
, NULL
, 0x0,
3801 { "Integer32", "thrift.i32",
3802 FT_INT32
, BASE_DEC
, NULL
, 0x0,
3806 { "Integer64", "thrift.i64",
3807 FT_INT64
, BASE_DEC
, NULL
, 0x0,
3810 { &hf_thrift_double
,
3811 { "Double", "thrift.double",
3812 FT_DOUBLE
, BASE_NONE
, NULL
, 0x0,
3815 { &hf_thrift_binary
,
3816 { "Binary", "thrift.binary",
3817 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
3820 { &hf_thrift_string
,
3821 { "String", "thrift.string",
3822 FT_STRING
, BASE_NONE
, NULL
, 0x0,
3823 "Binary field interpreted as a string.", HFILL
}
3825 { &hf_thrift_struct
,
3826 { "Struct", "thrift.struct",
3827 FT_NONE
, BASE_NONE
, NULL
, 0x0,
3831 { "List", "thrift.list",
3832 FT_NONE
, BASE_NONE
, NULL
, 0x0,
3836 { "Set", "thrift.set",
3837 FT_NONE
, BASE_NONE
, NULL
, 0x0,
3841 { "Map", "thrift.map",
3842 FT_NONE
, BASE_NONE
, NULL
, 0x0,
3845 { &hf_thrift_num_set_item
,
3846 { "Number of Set Items", "thrift.num_set_item",
3847 FT_INT32
, BASE_DEC
, NULL
, 0x0,
3850 { &hf_thrift_num_set_pos
,
3851 { "Number of Set Items", "thrift.num_set_pos",
3852 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
3855 { &hf_thrift_num_list_item
,
3856 { "Number of List Items", "thrift.num_list_item",
3857 FT_INT32
, BASE_DEC
, NULL
, 0x0,
3860 { &hf_thrift_num_list_pos
,
3861 { "Number of List Items", "thrift.num_list_pos",
3862 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
3865 { &hf_thrift_num_map_item
,
3866 { "Number of Map Items", "thrift.num_map_item",
3867 FT_INT32
, BASE_DEC
, NULL
, 0x0,
3870 { &hf_thrift_large_container
,
3871 { "More than 14 items", "thrift.num_item",
3872 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
3876 { "UUID", "thrift.uuid",
3877 FT_GUID
, BASE_NONE
, NULL
, 0x0,
3883 /* setup protocol subtree arrays */
3884 static int *ett
[] = {
3894 &ett_thrift_exception
,
3897 static ei_register_info ei
[] = {
3898 { &ei_thrift_wrong_type
, { "thrift.wrong_type", PI_PROTOCOL
, PI_ERROR
, "Type value not expected.", EXPFILL
} },
3899 { &ei_thrift_wrong_field_id
, { "thrift.wrong_field_id", PI_PROTOCOL
, PI_WARN
, "Field id different from value provided by sub-dissector.", EXPFILL
} },
3900 { &ei_thrift_negative_length
, { "thrift.negative_length", PI_PROTOCOL
, PI_ERROR
, "Length greater than 2 GiB not supported.", EXPFILL
} },
3901 { &ei_thrift_wrong_proto_version
, { "thrift.wrong_proto_version", PI_MALFORMED
, PI_ERROR
, "Protocol version invalid or unsupported.", EXPFILL
} },
3902 { &ei_thrift_struct_fid_not_in_seq
, { "thrift.struct_fid_not_in_seq", PI_PROTOCOL
, PI_ERROR
, "Missing mandatory field id in struct.", EXPFILL
} },
3903 { &ei_thrift_not_enough_data
, { "thrift.not_enough_data", PI_PROTOCOL
, PI_WARN
, "Not enough data to decode.", EXPFILL
} },
3904 { &ei_thrift_frame_too_short
, { "thrift.frame_too_short", PI_MALFORMED
, PI_ERROR
, "Thrift frame shorter than data.", EXPFILL
} },
3905 { &ei_thrift_frame_too_long
, { "thrift.frame_too_long", PI_PROTOCOL
, PI_WARN
, "Thrift frame longer than data.", EXPFILL
} },
3906 { &ei_thrift_varint_too_large
, { "thrift.varint_too_large", PI_PROTOCOL
, PI_ERROR
, "Thrift varint value too large for target integer type.", EXPFILL
} },
3907 { &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
} },
3908 { &ei_thrift_negative_field_id
, { "thrift.negative_field_id", PI_PROTOCOL
, PI_NOTE
, "Encountered unexpected negative field id, possibly an old application.", EXPFILL
} },
3909 { &ei_thrift_unordered_field_id
, { "thrift.unordered_field_id", PI_PROTOCOL
, PI_WARN
, "Field id not in strictly increasing order.", EXPFILL
} },
3910 { &ei_thrift_application_exception
, { "thrift.application_exception", PI_PROTOCOL
, PI_NOTE
, "The application recognized the method but rejected the content.", EXPFILL
} },
3911 { &ei_thrift_protocol_exception
, { "thrift.protocol_exception", PI_PROTOCOL
, PI_WARN
, "The application was not able to handle the request.", EXPFILL
} },
3912 { &ei_thrift_too_many_subtypes
, { "thrift.too_many_subtypes", PI_PROTOCOL
, PI_ERROR
, "Too many level of sub-types nesting.", EXPFILL
} },
3916 module_t
*thrift_module
;
3917 expert_module_t
*expert_thrift
;
3920 /* Register protocol name and description */
3921 proto_thrift
= proto_register_protocol("Thrift Protocol", "Thrift", "thrift");
3923 expert_thrift
= expert_register_protocol(proto_thrift
);
3925 /* register field array */
3926 proto_register_field_array(proto_thrift
, hf
, array_length(hf
));
3928 /* register subtree array */
3929 proto_register_subtree_array(ett
, array_length(ett
));
3931 expert_register_field_array(expert_thrift
, ei
, array_length(ei
));
3933 /* register dissector */
3934 thrift_handle
= register_dissector("thrift", dissect_thrift_transport
, proto_thrift
);
3935 thrift_http_handle
= register_dissector("thrift.http", dissect_thrift_http
, proto_thrift
);
3937 thrift_module
= prefs_register_protocol(proto_thrift
, proto_reg_handoff_thrift
);
3939 thrift_method_name_dissector_table
= register_dissector_table("thrift.method_names", "Thrift Method names",
3940 proto_thrift
, FT_STRING
, STRING_CASE_SENSITIVE
); /* Thrift is case-sensitive. */
3942 prefs_register_enum_preference(thrift_module
, "decode_binary",
3943 "Display binary as bytes or strings",
3944 "How the binary should be decoded",
3945 &binary_decode
, binary_display_options
, false);
3947 prefs_register_uint_preference(thrift_module
, "tls.port",
3950 10, &thrift_tls_port
);
3952 prefs_register_bool_preference(thrift_module
, "show_internal",
3953 "Show internal Thrift fields in the dissection tree",
3954 "Whether the Thrift dissector should display Thrift internal fields for sub-dissectors.",
3955 &show_internal_thrift_fields
);
3957 prefs_register_bool_preference(thrift_module
, "fallback_on_generic",
3958 "Fallback to generic Thrift dissector if sub-dissector fails.",
3959 "Whether the Thrift dissector should try to dissect the data if the sub-dissector failed."
3960 " This option can be useful if the data is well-formed but the sub-dissector is expecting different type/content.",
3961 &try_generic_if_sub_dissector_fails
);
3963 prefs_register_uint_preference(thrift_module
, "nested_type_depth",
3964 "Thrift nested types depth",
3965 "Maximum expected depth of nested types in the Thrift structures and containers."
3966 " A Thrift-based protocol using no parameter and void return types only uses a depth of 0."
3967 " A Thrift-based protocol using only simple types as parameters or return values uses a depth of 1.",
3968 10, &nested_type_depth
);
3970 prefs_register_bool_preference(thrift_module
, "desegment_framed",
3971 "Reassemble Framed Thrift messages spanning multiple TCP segments",
3972 "Whether the Thrift dissector should reassemble framed messages spanning multiple TCP segments."
3973 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
3978 proto_reg_handoff_thrift(void)
3980 static unsigned saved_thrift_tls_port
;
3981 static bool thrift_initialized
= false;
3983 if (!thrift_initialized
) {
3984 thrift_initialized
= true;
3985 heur_dissector_add("tcp", dissect_thrift_heur
, "Thrift over TCP", "thrift_tcp", proto_thrift
, HEURISTIC_ENABLE
);
3986 heur_dissector_add("udp", dissect_thrift_heur
, "Thrift over UDP", "thrift_udp", proto_thrift
, HEURISTIC_ENABLE
);
3987 heur_dissector_add("usb.bulk", dissect_thrift_heur
, "Thrift over USB", "thrift_usb_bulk", proto_thrift
, HEURISTIC_ENABLE
);
3988 dissector_add_for_decode_as_with_preference("tcp.port", thrift_handle
);
3989 dissector_add_for_decode_as_with_preference("udp.port", thrift_handle
);
3990 dissector_add_string("media_type", "application/x-thrift", thrift_http_handle
); /* Obsolete but still in use. */
3991 dissector_add_string("media_type", "application/vnd.apache.thrift.binary", thrift_http_handle
); /* Officially registered. */
3993 ssl_dissector_delete(saved_thrift_tls_port
, thrift_handle
);
3995 ssl_dissector_add(thrift_tls_port
, thrift_handle
);
3996 saved_thrift_tls_port
= thrift_tls_port
;
4000 * Editor modelines - https://www.wireshark.org/tools/modelines.html
4005 * indent-tabs-mode: nil
4008 * vi: set shiftwidth=4 tabstop=8 expandtab:
4009 * :indentSize=4:tabSize=8:noTabs=true: