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