diff options
Diffstat (limited to 'epan/dissectors/packet-thrift.c')
-rw-r--r-- | epan/dissectors/packet-thrift.c | 818 |
1 files changed, 660 insertions, 158 deletions
diff --git a/epan/dissectors/packet-thrift.c b/epan/dissectors/packet-thrift.c index f3667dc3f8..5205f18e7c 100644 --- a/epan/dissectors/packet-thrift.c +++ b/epan/dissectors/packet-thrift.c @@ -7,7 +7,7 @@ * * Copyright 2015, Anders Broman <anders.broman[at]ericsson.com> * Copyright 2021, Richard van der Hoff <richard[at]matrix.org> - * Copyright 2019-2021, Triton Circonflexe <triton[at]kumal.info> + * Copyright 2019-2024, Triton Circonflexe <triton[at]kumal.info> * * Wireshark - Network traffic analyzer * By Gerald Combs <gerald@wireshark.org> @@ -26,8 +26,8 @@ #include <stdint.h> #include <epan/packet.h> -#include <epan/expert.h> #include <epan/prefs.h> +#include <epan/proto_data.h> #include "packet-tcp.h" #include "packet-tls.h" @@ -70,11 +70,13 @@ void proto_reg_handoff_thrift(void); } } while (0) static dissector_handle_t thrift_handle; +static dissector_handle_t thrift_http_handle; static gboolean framed_desegment = TRUE; static guint thrift_tls_port = 0; static gboolean show_internal_thrift_fields = FALSE; static gboolean try_generic_if_sub_dissector_fails = FALSE; +static guint nested_type_depth = 25; static dissector_table_t thrift_method_name_dissector_table; @@ -87,6 +89,7 @@ static const int TBP_THRIFT_DOUBLE_LEN = 8; static const int TBP_THRIFT_I16_LEN = 2; static const int TBP_THRIFT_I32_LEN = 4; static const int TBP_THRIFT_I64_LEN = 8; +static const int TBP_THRIFT_UUID_LEN = 16; static const int TBP_THRIFT_MTYPE_OFFSET = 3; static const int TBP_THRIFT_MTYPE_LEN = 1; static const int TBP_THRIFT_VERSION_LEN = 4; /* (Version + method type) is explicitly passed as an int32 in libthrift */ @@ -126,68 +129,72 @@ static const guint32 TCP_THRIFT_NIBBLE_MASK = 0xf; static const int OCTETS_TO_BITS_SHIFT = 3; /* 8 bits per octets = 3 shifts left. */ static const int DISABLE_SUBTREE = -1; -static int proto_thrift = -1; -static int hf_thrift_frame_length = -1; -static int hf_thrift_protocol_id = -1; -static int hf_thrift_version = -1; -static int hf_thrift_mtype = -1; -static int hf_thrift_str_len = -1; -static int hf_thrift_method = -1; -static int hf_thrift_seq_id = -1; -static int hf_thrift_type = -1; -static int hf_thrift_key_type = -1; -static int hf_thrift_value_type = -1; -static int hf_thrift_compact_struct_type = -1; -static int hf_thrift_fid = -1; -static int hf_thrift_fid_delta = -1; -static int hf_thrift_bool = -1; -static int hf_thrift_i8 = -1; -static int hf_thrift_i16 = -1; -static int hf_thrift_i32 = -1; -static int hf_thrift_i64 = -1; -static int hf_thrift_binary = -1; -static int hf_thrift_string = -1; -static int hf_thrift_struct = -1; -static int hf_thrift_list = -1; -static int hf_thrift_set = -1; -static int hf_thrift_map = -1; -static int hf_thrift_num_list_item = -1; -static int hf_thrift_num_set_item = -1; -static int hf_thrift_num_map_item = -1; -static int hf_thrift_large_container = -1; -static int hf_thrift_double = -1; -static int hf_thrift_exception = -1; -static int hf_thrift_exception_message = -1; -static int hf_thrift_exception_type = -1; - -static int ett_thrift = -1; -static int ett_thrift_header = -1; -static int ett_thrift_params = -1; -static int ett_thrift_field = -1; -static int ett_thrift_struct = -1; -static int ett_thrift_list = -1; -static int ett_thrift_set = -1; -static int ett_thrift_map = -1; -static int ett_thrift_error = -1; /* ME_THRIFT_T_REPLY with field id != 0 */ -static int ett_thrift_exception = -1; /* ME_THRIFT_T_EXCEPTION */ - -static expert_field ei_thrift_wrong_type = EI_INIT; -static expert_field ei_thrift_wrong_field_id = EI_INIT; -static expert_field ei_thrift_negative_length = EI_INIT; -static expert_field ei_thrift_wrong_proto_version = EI_INIT; -static expert_field ei_thrift_struct_fid_not_in_seq = EI_INIT; -static expert_field ei_thrift_frame_too_short = EI_INIT; -static expert_field ei_thrift_not_enough_data = EI_INIT; -static expert_field ei_thrift_frame_too_long = EI_INIT; -static expert_field ei_thrift_varint_too_large = EI_INIT; -static expert_field ei_thrift_undefined_field_id = EI_INIT; -static expert_field ei_thrift_negative_field_id = EI_INIT; -static expert_field ei_thrift_unordered_field_id = EI_INIT; -static expert_field ei_thrift_application_exception = EI_INIT; -static expert_field ei_thrift_protocol_exception = EI_INIT; +static int proto_thrift; +static int hf_thrift_frame_length; +static int hf_thrift_protocol_id; +static int hf_thrift_version; +static int hf_thrift_mtype; +static int hf_thrift_str_len; +static int hf_thrift_method; +static int hf_thrift_seq_id; +static int hf_thrift_type; +static int hf_thrift_key_type; +static int hf_thrift_value_type; +static int hf_thrift_compact_struct_type; +static int hf_thrift_fid; +static int hf_thrift_fid_delta; +static int hf_thrift_bool; +static int hf_thrift_i8; +static int hf_thrift_i16; +static int hf_thrift_i32; +static int hf_thrift_i64; +static int hf_thrift_uuid; +static int hf_thrift_binary; +static int hf_thrift_string; +static int hf_thrift_struct; +static int hf_thrift_list; +static int hf_thrift_set; +static int hf_thrift_map; +static int hf_thrift_num_list_item; +static int hf_thrift_num_list_pos; +static int hf_thrift_num_set_item; +static int hf_thrift_num_set_pos; +static int hf_thrift_num_map_item; +static int hf_thrift_large_container; +static int hf_thrift_double; +static int hf_thrift_exception; +static int hf_thrift_exception_message; +static int hf_thrift_exception_type; + +static int ett_thrift; +static int ett_thrift_header; +static int ett_thrift_params; +static int ett_thrift_field; +static int ett_thrift_struct; +static int ett_thrift_list; +static int ett_thrift_set; +static int ett_thrift_map; +static int ett_thrift_error; /* Error while reading the header. */ +static int ett_thrift_exception; /* ME_THRIFT_T_EXCEPTION */ + +static expert_field ei_thrift_wrong_type; +static expert_field ei_thrift_wrong_field_id; +static expert_field ei_thrift_negative_length; +static expert_field ei_thrift_wrong_proto_version; +static expert_field ei_thrift_struct_fid_not_in_seq; +static expert_field ei_thrift_frame_too_short; +static expert_field ei_thrift_not_enough_data; +static expert_field ei_thrift_frame_too_long; +static expert_field ei_thrift_varint_too_large; +static expert_field ei_thrift_undefined_field_id; +static expert_field ei_thrift_negative_field_id; +static expert_field ei_thrift_unordered_field_id; +static expert_field ei_thrift_application_exception; +static expert_field ei_thrift_protocol_exception; +static expert_field ei_thrift_too_many_subtypes; static const thrift_member_t thrift_exception[] = { - { &hf_thrift_exception_message, 1, TRUE, DE_THRIFT_T_BINARY, NULL, { .encoding = ENC_UTF_8|ENC_NA } }, + { &hf_thrift_exception_message, 1, TRUE, DE_THRIFT_T_BINARY, NULL, { .encoding = ENC_UTF_8 }, NULL }, { &hf_thrift_exception_type, 2, FALSE, DE_THRIFT_T_I32, TMFILL }, { NULL, 0, FALSE, DE_THRIFT_T_STOP, TMFILL } }; @@ -206,6 +213,7 @@ typedef enum { DE_THRIFT_C_SET, DE_THRIFT_C_MAP, DE_THRIFT_C_STRUCT, + DE_THRIFT_C_UUID, } thrift_compact_type_enum_t; typedef struct _thrift_field_header_t { @@ -236,6 +244,7 @@ static const value_string thrift_type_vals[] = { { DE_THRIFT_T_MAP, "T_MAP" }, { DE_THRIFT_T_SET, "T_SET" }, { DE_THRIFT_T_LIST, "T_LIST" }, + { DE_THRIFT_T_UUID, "T_UUID" }, { 0, NULL } }; @@ -253,6 +262,7 @@ static const value_string thrift_compact_type_vals[] = { { DE_THRIFT_C_SET, "T_SET" }, { DE_THRIFT_C_MAP, "T_MAP" }, { DE_THRIFT_C_STRUCT, "T_STRUCT" }, + { DE_THRIFT_C_UUID, "T_UUID" }, { 0, NULL } }; @@ -311,7 +321,7 @@ static const enum_val_t binary_display_options[] = { 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); 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); - +static int dissect_thrift_t_struct_expert(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id, gint ett_id, const thrift_member_t *seq, expert_field* ei); /*=====BEGIN GENERIC HELPERS=====*/ /* Check that the 4-byte value match a Thrift Strict TBinaryProtocol version @@ -487,6 +497,12 @@ thrift_get_varint_enc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int o return length; } +static gboolean +is_thrift_compact_bool_type(thrift_compact_type_enum_t type) +{ + return type == DE_THRIFT_C_BOOL_TRUE || type == DE_THRIFT_C_BOOL_FALSE; +} + /* Function that reads the field header and return all associated data. * * @param[in] tvb: Pointer to the tvbuff_t holding the captured data. @@ -497,10 +513,12 @@ thrift_get_varint_enc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int o * and by dissect_thrift_common to differentiate between successful and exception T_REPLY. * @param[in] offset: Offset from the beginning of the tvbuff_t where the Thrift field is. Function will dissect type, id, & data. * @param[in] thrift_opt: Options from the Thrift dissector that will be necessary for sub-dissection (binary vs. compact, ...) + * @param[in] gen_bool: Generate item when one of the boolean types is encountered. * + * @return See "GENERIC DISSECTION PARAMETERS DOCUMENTATION". */ static int -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) +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, gboolean gen_bool) { /* * Binary protocol field header (3 bytes): @@ -587,14 +605,28 @@ dissect_thrift_field_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, /* Create the field header sub-tree if requested only. */ if (tree != NULL) { + guint nested_count = p_get_proto_depth(pinfo, proto_thrift); + if (nested_count >= thrift_opt->nested_type_depth) { + expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes); + return THRIFT_REQUEST_REASSEMBLY; + } + header->fh_tree = proto_tree_add_subtree_format(tree, tvb, header->type_offset, *offset - header->type_offset, ett_thrift_field, NULL, - "Field Header #%" G_GINT64_MODIFIER "d", header->field_id); + "Field Header #%" PRId64, header->field_id); if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) { 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); 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); if (delta == TCP_THRIFT_DELTA_NOT_SET) { proto_item_append_text(header->fid_pi, " (Not Set)"); } + if (gen_bool && is_thrift_compact_bool_type(header->type.compact)) { + proto_item *bool_item = proto_tree_add_boolean(tree, hf_thrift_bool, tvb, header->type_offset, TBP_THRIFT_TYPE_LEN, 2 - header->type.compact); + proto_item_set_generated(bool_item); + } + if (gen_bool && is_thrift_compact_bool_type(header->type.compact)) { + proto_item *bool_item = proto_tree_add_boolean(tree, hf_thrift_bool, tvb, header->type_offset, TBP_THRIFT_TYPE_LEN, 2 - header->type.compact); + proto_item_set_generated(bool_item); + } } else { header->type_pi = proto_tree_add_item(header->fh_tree, hf_thrift_type, tvb, header->type_offset, TBP_THRIFT_TYPE_LEN, ENC_BIG_ENDIAN); } @@ -629,10 +661,27 @@ dissect_thrift_field_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, expert_add_info(pinfo, header->fid_pi, &ei_thrift_unordered_field_id); } } - } else if (header->fid_length <= 0) { - /* Varint for field id was too long to decode, handle the error without the sub-tree. */ - proto_tree_add_expert(tree, pinfo, &ei_thrift_varint_too_large, tvb, header->fid_offset, TCP_THRIFT_MAX_I16_LEN); - return THRIFT_REQUEST_REASSEMBLY; + } else { + /* The fid_pi value (proto_item for field_id) is required for sub-dissectors. + * Create it even in the absence of tree. */ + if (delta == TCP_THRIFT_DELTA_NOT_SET) { + if (header->fid_length > 0) { + header->fid_pi = proto_tree_add_item(header->fh_tree, hf_thrift_fid, tvb, header->fid_offset, header->fid_length, ENC_BIG_ENDIAN); + } else { + /* Varint for field id was too long to decode, handle the error without the sub-tree. */ + proto_tree_add_expert(tree, pinfo, &ei_thrift_varint_too_large, tvb, header->fid_offset, TCP_THRIFT_MAX_I16_LEN); + return THRIFT_REQUEST_REASSEMBLY; + } + } else { + if ((gint64)INT16_MIN > header->field_id || header->field_id > (gint64)INT16_MAX) { + header->fid_pi = proto_tree_add_int64(header->fh_tree, hf_thrift_i64, tvb, header->fid_offset, header->fid_length, header->field_id); + expert_add_info(pinfo, header->fid_pi, &ei_thrift_varint_too_large); + /* We continue anyway as the field id was displayed successfully. */ + } else { + header->fid_pi = proto_tree_add_int(header->fh_tree, hf_thrift_fid, tvb, header->fid_offset, header->fid_length, (gint16)header->field_id); + } + proto_item_set_generated(header->fid_pi); + } } return *offset; @@ -654,11 +703,12 @@ dissect_thrift_field_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, * @param[in] hf_id: The hf_id that needs to be used for the display. * If the found integer is larger that the expected integer size (driven by max_length parameter), * the integer will always be displayed as a generic T_I64 and an expert info will be added. + * @param[in] raw_dissector Dissector for raw integer (we need to create a tvbuff_t with the flatten integer. * * @return See "GENERIC DISSECTION PARAMETERS DOCUMENTATION". */ static int -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) +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) { gint64 varint; proto_item *pi; @@ -679,7 +729,17 @@ dissect_thrift_varint(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int * expert_add_info(pinfo, pi, &ei_thrift_varint_too_large); /* We continue anyway as the varint was indeed decoded. */ } else { - proto_tree_add_int(tree, hf_id, tvb, *offset, length, (gint16)varint); + if (raw_dissector != NULL) { + guint8 *data = wmem_alloc(wmem_packet_scope(), TBP_THRIFT_I16_LEN); + data[0] = (varint >> 8) & 0xFF; + data[1] = varint & 0xFF; + tvbuff_t* sub_tvb = tvb_new_child_real_data(tvb, data, TBP_THRIFT_I16_LEN, TBP_THRIFT_I16_LEN); + thrift_opt->use_std_dissector = FALSE; + raw_dissector(sub_tvb, pinfo, tree, thrift_opt); + } + if (thrift_opt->use_std_dissector) { + proto_tree_add_int(tree, hf_id, tvb, *offset, length, (gint16)varint); + } } break; case TCP_THRIFT_MAX_I32_LEN: @@ -688,12 +748,40 @@ dissect_thrift_varint(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int * expert_add_info(pinfo, pi, &ei_thrift_varint_too_large); /* We continue anyway as the varint was indeed decoded. */ } else { - proto_tree_add_int(tree, hf_id, tvb, *offset, length, (gint32)varint); + if (raw_dissector != NULL) { + guint8 *data = wmem_alloc(wmem_packet_scope(), TBP_THRIFT_I32_LEN); + data[0] = (varint >> 24) & 0xFF; + data[1] = (varint >> 16) & 0xFF; + data[2] = (varint >> 8) & 0xFF; + data[3] = varint & 0xFF; + tvbuff_t* sub_tvb = tvb_new_child_real_data(tvb, data, TBP_THRIFT_I32_LEN, TBP_THRIFT_I32_LEN); + thrift_opt->use_std_dissector = FALSE; + raw_dissector(sub_tvb, pinfo, tree, thrift_opt); + } + if (thrift_opt->use_std_dissector) { + proto_tree_add_int(tree, hf_id, tvb, *offset, length, (gint32)varint); + } } break; case TCP_THRIFT_MAX_I64_LEN: default: - proto_tree_add_int64(tree, hf_id, tvb, *offset, length, varint); + if (raw_dissector != NULL) { + guint8 *data = wmem_alloc(wmem_packet_scope(), TBP_THRIFT_I64_LEN); + data[0] = (varint >> 56) & 0xFF; + data[1] = (varint >> 48) & 0xFF; + data[2] = (varint >> 40) & 0xFF; + data[3] = (varint >> 32) & 0xFF; + data[4] = (varint >> 24) & 0xFF; + data[5] = (varint >> 16) & 0xFF; + data[6] = (varint >> 8) & 0xFF; + data[7] = varint & 0xFF; + tvbuff_t* sub_tvb = tvb_new_child_real_data(tvb, data, TBP_THRIFT_I64_LEN, TBP_THRIFT_I64_LEN); + thrift_opt->use_std_dissector = FALSE; + raw_dissector(sub_tvb, pinfo, tree, thrift_opt); + } + if (thrift_opt->use_std_dissector) { + proto_tree_add_int64(tree, hf_id, tvb, *offset, length, varint); + } break; } *offset += length; @@ -726,10 +814,10 @@ dissect_thrift_string_as_preferred(tvbuff_t *tvb, packet_info *pinfo, proto_tree proto_tree_add_item(tree, hf_thrift_string, tvb, *offset, str_len, ENC_UTF_16 | ENC_BIG_ENDIAN); break; case DECODE_BINARY_AS_UTF8: - proto_tree_add_item(tree, hf_thrift_string, tvb, *offset, str_len, ENC_UTF_8|ENC_NA); + proto_tree_add_item(tree, hf_thrift_string, tvb, *offset, str_len, ENC_UTF_8); break; case DECODE_BINARY_AS_ASCII: - proto_tree_add_item(tree, hf_thrift_string, tvb, *offset, str_len, ENC_ASCII|ENC_NA); + proto_tree_add_item(tree, hf_thrift_string, tvb, *offset, str_len, ENC_ASCII); break; case DECODE_BINARY_AS_AUTO_UTF8: /* When there is no data at all, consider it a string @@ -737,7 +825,7 @@ dissect_thrift_string_as_preferred(tvbuff_t *tvb, packet_info *pinfo, proto_tree * If not entirely captured, consider it as a binary. */ if (tvb_captured_length_remaining(tvb, *offset) >= str_len && (str_len == 0 || thrift_binary_utf8_isprint(tvb, *offset, str_len, TRUE) > 0)) { - proto_tree_add_item(tree, hf_thrift_string, tvb, *offset, str_len, ENC_UTF_8|ENC_NA); + proto_tree_add_item(tree, hf_thrift_string, tvb, *offset, str_len, ENC_UTF_8); break; } /* otherwise, continue with type BINARY */ @@ -784,6 +872,8 @@ compact_struct_type_to_generic_type(thrift_compact_type_enum_t compact) return DE_THRIFT_T_MAP; case DE_THRIFT_C_STRUCT: return DE_THRIFT_T_STRUCT; + case DE_THRIFT_C_UUID: + return DE_THRIFT_T_UUID; default: return DE_THRIFT_T_VOID; } @@ -863,7 +953,7 @@ dissect_thrift_t_field_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre internal_tree = tree; } /* Read the entire field header using the dedicated function. */ - if (dissect_thrift_field_header(tvb, pinfo, internal_tree, &offset, thrift_opt, &field_header) == THRIFT_REQUEST_REASSEMBLY) { + if (dissect_thrift_field_header(tvb, pinfo, internal_tree, &offset, thrift_opt, &field_header, FALSE) == THRIFT_REQUEST_REASSEMBLY) { if (offset == THRIFT_REQUEST_REASSEMBLY) { return THRIFT_REQUEST_REASSEMBLY; } else { @@ -888,7 +978,7 @@ dissect_thrift_t_field_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre /* Once we know it's the expected type (which is /not/ T_STOP), we can read the field id. */ if (field_header.field_id != (gint64)field_id) { expert_add_info_format(pinfo, field_header.fid_pi, &ei_thrift_wrong_field_id, - "Sub-dissector expects field id = %d, found %" G_GINT64_MODIFIER "d instead.", field_id, field_header.field_id); + "Sub-dissector expects field id = %d, found %" PRId64 " instead.", field_id, field_header.field_id); } /* Expose the field header sub-tree if requested. */ @@ -899,17 +989,17 @@ dissect_thrift_t_field_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre return offset; } -int -dissect_thrift_t_bool(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id) +static int +dissect_thrift_raw_bool(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id, dissector_t raw_dissector) { int dt_offset = offset; gboolean bool_val = FALSE; - proto_item *pi; /* Get the current state of dissection. */ DISSECTOR_ASSERT(thrift_opt); DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY); ABORT_SUBDISSECTION_ON_ISSUE(offset); + thrift_opt->use_std_dissector = TRUE; if (is_field) { /* In case of Compact protocol struct field (or command parameter / return value), * the boolean type also indicates the boolean value. */ @@ -927,9 +1017,17 @@ dissect_thrift_t_bool(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int o offset = dissect_thrift_t_field_header(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_BOOL, field_id, NULL); ABORT_SUBDISSECTION_ON_ISSUE(offset); if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) { - /* The value must be in the top-level tree, /after/ the field header tree. */ - pi = proto_tree_add_boolean(tree, hf_id, tvb, dt_offset, TBP_THRIFT_TYPE_LEN, bool_val); - proto_item_set_generated(pi); + /* It is the responsibility of the sub-dissector to only use the least significant bit. */ + if (raw_dissector != NULL) { + tvbuff_t* sub_tvb = tvb_new_subset_length(tvb, offset, TBP_THRIFT_BOOL_LEN); + thrift_opt->use_std_dissector = FALSE; + raw_dissector(sub_tvb, pinfo, tree, thrift_opt); + } + if (thrift_opt->use_std_dissector) { + /* The value must be in the top-level tree, /after/ the field header tree. */ + proto_item *pi = proto_tree_add_boolean(tree, hf_id, tvb, dt_offset, TBP_THRIFT_TYPE_LEN, bool_val); + proto_item_set_generated(pi); + } return offset; } } @@ -938,14 +1036,32 @@ dissect_thrift_t_bool(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int o } /* Either in a list/set/map or in a Binary protocol encoding. */ - proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_BOOL_LEN, ENC_BIG_ENDIAN); + if (raw_dissector != NULL) { + tvbuff_t* sub_tvb = tvb_new_subset_length(tvb, offset, TBP_THRIFT_BOOL_LEN); + thrift_opt->use_std_dissector = FALSE; + raw_dissector(sub_tvb, pinfo, tree, thrift_opt); + } + if (thrift_opt->use_std_dissector) { + proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_BOOL_LEN, ENC_BIG_ENDIAN); + } offset += TBP_THRIFT_BOOL_LEN; + if (is_field) { + thrift_opt->previous_field_id = field_id; + } return offset; } int -dissect_thrift_t_i8(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id) +dissect_thrift_t_bool(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id) +{ + DISSECTOR_ASSERT(thrift_opt); + DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY); + return dissect_thrift_raw_bool(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, NULL); +} + +static int +dissect_thrift_raw_i8(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id, dissector_t raw_dissector) { /* Get the current state of dissection. */ DISSECTOR_ASSERT(thrift_opt); @@ -959,15 +1075,34 @@ dissect_thrift_t_i8(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int off return THRIFT_REQUEST_REASSEMBLY; } + thrift_opt->use_std_dissector = TRUE; /* Compact protocol does not use varint for T_I8 as it would be counter-productive. */ - proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_I8_LEN, ENC_BIG_ENDIAN); + if (raw_dissector != NULL) { + tvbuff_t* sub_tvb = tvb_new_subset_length(tvb, offset, TBP_THRIFT_I8_LEN); + thrift_opt->use_std_dissector = FALSE; + raw_dissector(sub_tvb, pinfo, tree, thrift_opt); + } + if (thrift_opt->use_std_dissector) { + proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_I8_LEN, ENC_BIG_ENDIAN); + } offset += TBP_THRIFT_I8_LEN; + if (is_field) { + thrift_opt->previous_field_id = field_id; + } return offset; } int -dissect_thrift_t_i16(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id) +dissect_thrift_t_i8(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id) +{ + DISSECTOR_ASSERT(thrift_opt); + DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY); + return dissect_thrift_raw_i8(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, NULL); +} + +static int +dissect_thrift_raw_i16(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id, dissector_t raw_dissector) { /* Get the current state of dissection. */ DISSECTOR_ASSERT(thrift_opt); @@ -977,8 +1112,9 @@ dissect_thrift_t_i16(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int of offset = dissect_thrift_t_field_header(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_I16, field_id, NULL); } ABORT_SUBDISSECTION_ON_ISSUE(offset); + thrift_opt->use_std_dissector = TRUE; if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) { - int result = dissect_thrift_varint(tvb, pinfo, tree, &offset, thrift_opt, TCP_THRIFT_MAX_I16_LEN, hf_id); + int result = dissect_thrift_varint(tvb, pinfo, tree, &offset, thrift_opt, TCP_THRIFT_MAX_I16_LEN, hf_id, raw_dissector); if (result == THRIFT_REQUEST_REASSEMBLY) { if (offset == THRIFT_REQUEST_REASSEMBLY) { return THRIFT_REQUEST_REASSEMBLY; @@ -989,15 +1125,33 @@ dissect_thrift_t_i16(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int of } else if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_I16_LEN) { return THRIFT_REQUEST_REASSEMBLY; } else { - proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_I16_LEN, ENC_BIG_ENDIAN); - offset += TBP_THRIFT_FID_LEN; + if (raw_dissector != NULL) { + tvbuff_t* sub_tvb = tvb_new_subset_length(tvb, offset, TBP_THRIFT_I16_LEN); + thrift_opt->use_std_dissector = FALSE; + raw_dissector(sub_tvb, pinfo, tree, thrift_opt); + } + if (thrift_opt->use_std_dissector) { + proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_I16_LEN, ENC_BIG_ENDIAN); + } + offset += TBP_THRIFT_I16_LEN; } + if (is_field) { + thrift_opt->previous_field_id = field_id; + } return offset; } int -dissect_thrift_t_i32(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id) +dissect_thrift_t_i16(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id) +{ + DISSECTOR_ASSERT(thrift_opt); + DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY); + return dissect_thrift_raw_i16(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, NULL); +} + +static int +dissect_thrift_raw_i32(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id, dissector_t raw_dissector) { /* Get the current state of dissection. */ DISSECTOR_ASSERT(thrift_opt); @@ -1007,8 +1161,9 @@ dissect_thrift_t_i32(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int of offset = dissect_thrift_t_field_header(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_I32, field_id, NULL); } ABORT_SUBDISSECTION_ON_ISSUE(offset); + thrift_opt->use_std_dissector = TRUE; if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) { - int result = dissect_thrift_varint(tvb, pinfo, tree, &offset, thrift_opt, TCP_THRIFT_MAX_I32_LEN, hf_id); + int result = dissect_thrift_varint(tvb, pinfo, tree, &offset, thrift_opt, TCP_THRIFT_MAX_I32_LEN, hf_id, raw_dissector); if (result == THRIFT_REQUEST_REASSEMBLY) { if (offset == THRIFT_REQUEST_REASSEMBLY) { return THRIFT_REQUEST_REASSEMBLY; @@ -1019,15 +1174,33 @@ dissect_thrift_t_i32(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int of } else if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_I32_LEN) { return THRIFT_REQUEST_REASSEMBLY; } else { - proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_I32_LEN, ENC_BIG_ENDIAN); + if (raw_dissector != NULL) { + tvbuff_t* sub_tvb = tvb_new_subset_length(tvb, offset, TBP_THRIFT_I32_LEN); + thrift_opt->use_std_dissector = FALSE; + raw_dissector(sub_tvb, pinfo, tree, thrift_opt); + } + if (thrift_opt->use_std_dissector) { + proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_I32_LEN, ENC_BIG_ENDIAN); + } offset += TBP_THRIFT_I32_LEN; } + if (is_field) { + thrift_opt->previous_field_id = field_id; + } return offset; } int -dissect_thrift_t_i64(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id) +dissect_thrift_t_i32(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id) +{ + DISSECTOR_ASSERT(thrift_opt); + DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY); + return dissect_thrift_raw_i32(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, NULL); +} + +static int +dissect_thrift_raw_i64(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id, dissector_t raw_dissector) { /* Get the current state of dissection. */ DISSECTOR_ASSERT(thrift_opt); @@ -1037,8 +1210,9 @@ dissect_thrift_t_i64(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int of offset = dissect_thrift_t_field_header(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_I64, field_id, NULL); } ABORT_SUBDISSECTION_ON_ISSUE(offset); + thrift_opt->use_std_dissector = TRUE; if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) { - int result = dissect_thrift_varint(tvb, pinfo, tree, &offset, thrift_opt, TCP_THRIFT_MAX_I64_LEN, hf_id); + int result = dissect_thrift_varint(tvb, pinfo, tree, &offset, thrift_opt, TCP_THRIFT_MAX_I64_LEN, hf_id, raw_dissector); if (result == THRIFT_REQUEST_REASSEMBLY) { if (offset == THRIFT_REQUEST_REASSEMBLY) { return THRIFT_REQUEST_REASSEMBLY; @@ -1049,15 +1223,33 @@ dissect_thrift_t_i64(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int of } else if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_I64_LEN) { return THRIFT_REQUEST_REASSEMBLY; } else { - proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_I64_LEN, ENC_BIG_ENDIAN); + if (raw_dissector != NULL) { + tvbuff_t* sub_tvb = tvb_new_subset_length(tvb, offset, TBP_THRIFT_I64_LEN); + thrift_opt->use_std_dissector = FALSE; + raw_dissector(sub_tvb, pinfo, tree, thrift_opt); + } + if (thrift_opt->use_std_dissector) { + proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_I64_LEN, ENC_BIG_ENDIAN); + } offset += TBP_THRIFT_I64_LEN; } + if (is_field) { + thrift_opt->previous_field_id = field_id; + } return offset; } int -dissect_thrift_t_double(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id) +dissect_thrift_t_i64(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id) +{ + DISSECTOR_ASSERT(thrift_opt); + DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY); + return dissect_thrift_raw_i64(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, NULL); +} + +static int +dissect_thrift_raw_double(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id, dissector_t raw_dissector) { /* Get the current state of dissection. */ DISSECTOR_ASSERT(thrift_opt); @@ -1071,30 +1263,94 @@ dissect_thrift_t_double(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int return THRIFT_REQUEST_REASSEMBLY; } - if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) { - proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_DOUBLE_LEN, ENC_LITTLE_ENDIAN); - } else { - proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_DOUBLE_LEN, ENC_BIG_ENDIAN); + thrift_opt->use_std_dissector = TRUE; + if (raw_dissector != NULL) { + tvbuff_t* sub_tvb; + if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) { + /* Create a sub-tvbuff_t in big endian format as documented. */ + guint8 *data = wmem_alloc(wmem_packet_scope(), TBP_THRIFT_DOUBLE_LEN); + data[0] = tvb_get_guint8(tvb, offset + 7); + data[1] = tvb_get_guint8(tvb, offset + 6); + data[2] = tvb_get_guint8(tvb, offset + 5); + data[3] = tvb_get_guint8(tvb, offset + 4); + data[4] = tvb_get_guint8(tvb, offset + 3); + data[5] = tvb_get_guint8(tvb, offset + 2); + data[6] = tvb_get_guint8(tvb, offset + 1); + data[7] = tvb_get_guint8(tvb, offset); + sub_tvb = tvb_new_child_real_data(tvb, data, TBP_THRIFT_DOUBLE_LEN, TBP_THRIFT_DOUBLE_LEN); + } else { + sub_tvb = tvb_new_subset_length(tvb, offset, TBP_THRIFT_DOUBLE_LEN); + } + thrift_opt->use_std_dissector = FALSE; + raw_dissector(sub_tvb, pinfo, tree, thrift_opt); + } + if (thrift_opt->use_std_dissector) { + if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) { + proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_DOUBLE_LEN, ENC_LITTLE_ENDIAN); + } else { + proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_DOUBLE_LEN, ENC_BIG_ENDIAN); + } } offset += TBP_THRIFT_DOUBLE_LEN; + if (is_field) { + thrift_opt->previous_field_id = field_id; + } return offset; } int -dissect_thrift_t_binary(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id) +dissect_thrift_t_double(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id) { - return dissect_thrift_t_string_enc(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ENC_NA); + DISSECTOR_ASSERT(thrift_opt); + DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY); + return dissect_thrift_raw_double(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, NULL); } -int -dissect_thrift_t_string(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id) +static int +dissect_thrift_raw_uuid(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id, dissector_t raw_dissector) { - return dissect_thrift_t_string_enc(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ENC_UTF_8|ENC_NA); + /* Get the current state of dissection. */ + DISSECTOR_ASSERT(thrift_opt); + DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY); + + /* Dissect field header if necessary. */ + if (is_field) { + offset = dissect_thrift_t_field_header(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_UUID, field_id, NULL); + } + ABORT_SUBDISSECTION_ON_ISSUE(offset); + + if (tvb_reported_length_remaining(tvb, offset) < TBP_THRIFT_UUID_LEN) { + return THRIFT_REQUEST_REASSEMBLY; + } + + thrift_opt->use_std_dissector = TRUE; + if (raw_dissector != NULL) { + tvbuff_t* sub_tvb = tvb_new_subset_length(tvb, offset, TBP_THRIFT_UUID_LEN); + thrift_opt->use_std_dissector = FALSE; + raw_dissector(sub_tvb, pinfo, tree, thrift_opt); + } + if (thrift_opt->use_std_dissector) { + proto_tree_add_item(tree, hf_id, tvb, offset, TBP_THRIFT_UUID_LEN, ENC_BIG_ENDIAN); + } + offset += TBP_THRIFT_UUID_LEN; + + if (is_field) { + thrift_opt->previous_field_id = field_id; + } + return offset; } int -dissect_thrift_t_string_enc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id, guint encoding) +dissect_thrift_t_uuid(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id) +{ + DISSECTOR_ASSERT(thrift_opt); + DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY); + return dissect_thrift_raw_uuid(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, NULL); +} + +static int +dissect_thrift_raw_binary(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id, guint encoding, dissector_t raw_dissector) { /* Get the current state of dissection. */ DISSECTOR_ASSERT(thrift_opt); @@ -1159,14 +1415,89 @@ dissect_thrift_t_string_enc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, return THRIFT_REQUEST_REASSEMBLY; } - proto_tree_add_item(tree, hf_id, tvb, offset, str_len, encoding); + thrift_opt->use_std_dissector = TRUE; + if (raw_dissector != NULL) { + tvbuff_t* sub_tvb = tvb_new_subset_length(tvb, offset, str_len); + thrift_opt->use_std_dissector = FALSE; + raw_dissector(sub_tvb, pinfo, tree, thrift_opt); + } + if (thrift_opt->use_std_dissector) { + proto_tree_add_item(tree, hf_id, tvb, offset, str_len, encoding); + } offset = offset + str_len; + if (is_field) { + thrift_opt->previous_field_id = field_id; + } + return offset; +} + +int +dissect_thrift_t_binary(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id) +{ + DISSECTOR_ASSERT(thrift_opt); + DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY); + return dissect_thrift_raw_binary(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ENC_NA, NULL); +} + +int +dissect_thrift_t_string(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id) +{ + DISSECTOR_ASSERT(thrift_opt); + DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY); + return dissect_thrift_raw_binary(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ENC_UTF_8, NULL); +} + +int +dissect_thrift_t_string_enc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id, guint encoding) +{ + DISSECTOR_ASSERT(thrift_opt); + DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY); + return dissect_thrift_raw_binary(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, encoding, NULL); +} + +int +dissect_thrift_t_raw_data(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, int offset, thrift_option_data_t* thrift_opt, gboolean is_field, int field_id, gint hf_id, thrift_type_enum_t type, dissector_t raw_dissector) +{ + /* Get the current state of dissection. */ + DISSECTOR_ASSERT(thrift_opt); + DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY); + + switch (type) { + case DE_THRIFT_T_BOOL: + offset = dissect_thrift_raw_bool(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, raw_dissector); + break; + case DE_THRIFT_T_I8: + offset = dissect_thrift_raw_i8(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, raw_dissector); + break; + case DE_THRIFT_T_I16: + offset = dissect_thrift_raw_i16(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, raw_dissector); + break; + case DE_THRIFT_T_I32: + offset = dissect_thrift_raw_i32(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, raw_dissector); + break; + case DE_THRIFT_T_I64: + offset = dissect_thrift_raw_i64(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, raw_dissector); + break; + case DE_THRIFT_T_DOUBLE: + offset = dissect_thrift_raw_double(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, raw_dissector); + break; + case DE_THRIFT_T_BINARY: + offset = dissect_thrift_raw_binary(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ENC_NA, raw_dissector); + break; + case DE_THRIFT_T_UUID: + offset = dissect_thrift_raw_uuid(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, raw_dissector); + break; + default: + REPORT_DISSECTOR_BUG("Only simple data types support raw dissection."); + break; + } return offset; } /* Simple dispatch function for lists, sets, maps, and structs internal elements to avoid code duplication. */ static int +// NOLINTNEXTLINE(misc-no-recursion) dissect_thrift_t_member(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, const thrift_member_t *elt) { switch (elt->type) { @@ -1174,25 +1505,25 @@ dissect_thrift_t_member(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset = dissect_thrift_t_stop(tvb, pinfo, tree, offset); break; case DE_THRIFT_T_BOOL: - offset = dissect_thrift_t_bool(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id); + offset = dissect_thrift_raw_bool(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, elt->raw_dissector); break; case DE_THRIFT_T_I8: - offset = dissect_thrift_t_i8(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id); + offset = dissect_thrift_raw_i8(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, elt->raw_dissector); break; case DE_THRIFT_T_I16: - offset = dissect_thrift_t_i16(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id); + offset = dissect_thrift_raw_i16(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, elt->raw_dissector); break; case DE_THRIFT_T_I32: - offset = dissect_thrift_t_i32(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id); + offset = dissect_thrift_raw_i32(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, elt->raw_dissector); break; case DE_THRIFT_T_I64: - offset = dissect_thrift_t_i64(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id); + offset = dissect_thrift_raw_i64(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, elt->raw_dissector); break; case DE_THRIFT_T_DOUBLE: - offset = dissect_thrift_t_double(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id); + offset = dissect_thrift_raw_double(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, elt->raw_dissector); break; case DE_THRIFT_T_BINARY: - offset = dissect_thrift_t_string(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id); + 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); break; case DE_THRIFT_T_LIST: 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); @@ -1204,7 +1535,10 @@ dissect_thrift_t_member(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int 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); break; case DE_THRIFT_T_STRUCT: - offset = dissect_thrift_t_struct(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, *elt->p_ett_id, elt->u.members); + 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); + break; + case DE_THRIFT_T_UUID: + offset = dissect_thrift_raw_uuid(tvb, pinfo, tree, offset, thrift_opt, is_field, elt->fid, *elt->p_hf_id, elt->raw_dissector); break; default: REPORT_DISSECTOR_BUG("Unexpected Thrift type dissection requested."); @@ -1220,6 +1554,7 @@ dissect_thrift_t_member(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int * so it's easy to use the same code and handle the additional elements only when necessary. */ static int +// NOLINTNEXTLINE(misc-no-recursion) dissect_thrift_b_linear(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id, gint ett_id, const thrift_member_t *key, const thrift_member_t *val, thrift_type_enum_t expected) { proto_item *container_pi = NULL; @@ -1227,6 +1562,7 @@ dissect_thrift_b_linear(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int proto_tree *sub_tree; gint32 key_type, val_type; gint32 length; + guint nested_count = p_get_proto_depth(pinfo, proto_thrift); /* Get the current state of dissection. */ DISSECTOR_ASSERT(thrift_opt); @@ -1239,6 +1575,11 @@ dissect_thrift_b_linear(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int } /* Create the sub-tree. */ + if (nested_count >= thrift_opt->nested_type_depth) { + expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes); + return THRIFT_REQUEST_REASSEMBLY; + } + p_set_proto_depth(pinfo, proto_thrift, nested_count + 1); container_pi = proto_tree_add_item(tree, hf_id, tvb, offset, -1, ENC_BIG_ENDIAN); sub_tree = proto_item_add_subtree(container_pi, ett_id); ABORT_SUBDISSECTION_ON_ISSUE(offset); @@ -1313,6 +1654,7 @@ dissect_thrift_b_linear(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int if (container_pi && offset > 0) { proto_item_set_end(container_pi, tvb, offset); } + p_set_proto_depth(pinfo, proto_thrift, nested_count); return offset; } @@ -1321,6 +1663,7 @@ dissect_thrift_b_linear(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int * this prevents code duplication. */ static int +// NOLINTNEXTLINE(misc-no-recursion) dissect_thrift_c_list_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id, gint ett_id, const thrift_member_t *elt, gboolean is_list) { proto_item *container_pi; @@ -1333,10 +1676,13 @@ dissect_thrift_c_list_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, i guint64 varint; int lt_offset; int hf_num_item = hf_thrift_num_set_item; + int hf_pos_item = hf_thrift_num_set_pos; thrift_type_enum_t expected = DE_THRIFT_T_SET; + guint nested_count = p_get_proto_depth(pinfo, proto_thrift); if (is_list) { hf_num_item = hf_thrift_num_list_item; + hf_pos_item = hf_thrift_num_list_pos; expected = DE_THRIFT_T_LIST; } @@ -1355,6 +1701,11 @@ dissect_thrift_c_list_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, i } /* Create the sub-tree. */ + if (nested_count >= thrift_opt->nested_type_depth) { + expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes); + return THRIFT_REQUEST_REASSEMBLY; + } + p_set_proto_depth(pinfo, proto_thrift, nested_count + 1); container_pi = proto_tree_add_item(tree, hf_id, tvb, offset, -1, ENC_BIG_ENDIAN); sub_tree = proto_item_add_subtree(container_pi, ett_id); @@ -1403,7 +1754,7 @@ dissect_thrift_c_list_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, i break; } } else if (show_internal_thrift_fields) { - proto_tree_add_bits_item(sub_tree, hf_num_item, tvb, (lt_offset << OCTETS_TO_BITS_SHIFT), TCP_THRIFT_NIBBLE_SHIFT, ENC_BIG_ENDIAN); + proto_tree_add_bits_item(sub_tree, hf_pos_item, tvb, (lt_offset << OCTETS_TO_BITS_SHIFT), TCP_THRIFT_NIBBLE_SHIFT, ENC_BIG_ENDIAN); } /* Read the content of the container. */ @@ -1415,47 +1766,65 @@ dissect_thrift_c_list_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, i if (container_pi && offset > 0) { proto_item_set_end(container_pi, tvb, offset); } + p_set_proto_depth(pinfo, proto_thrift, nested_count); return offset; } int +// NOLINTNEXTLINE(misc-no-recursion) dissect_thrift_t_list(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id, gint ett_id, const thrift_member_t *elt) { + int result; if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) { - return dissect_thrift_c_list_set(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ett_id, elt, TRUE); + result = dissect_thrift_c_list_set(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ett_id, elt, TRUE); } else { - return dissect_thrift_b_linear(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ett_id, NULL, elt, DE_THRIFT_T_LIST); + 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); } + + if (is_field) { + thrift_opt->previous_field_id = field_id; + } + return result; } int +// NOLINTNEXTLINE(misc-no-recursion) dissect_thrift_t_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id, gint ett_id, const thrift_member_t *elt) { + int result; if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) { - return dissect_thrift_c_list_set(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ett_id, elt, FALSE); + result = dissect_thrift_c_list_set(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ett_id, elt, FALSE); } else { - return dissect_thrift_b_linear(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ett_id, NULL, elt, DE_THRIFT_T_SET); + 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); + } + + if (is_field) { + thrift_opt->previous_field_id = field_id; } + return result; } int +// NOLINTNEXTLINE(misc-no-recursion) dissect_thrift_t_map(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id, gint ett_id, const thrift_member_t *key, const thrift_member_t *value) { + int result; /* Get the current state of dissection. */ DISSECTOR_ASSERT(thrift_opt); DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY); if ((thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) == 0) { - return dissect_thrift_b_linear(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ett_id, key, value, DE_THRIFT_T_MAP); + 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); } else { proto_tree *sub_tree = NULL; - proto_item *container_pi, *len_pi; + proto_item *container_pi; proto_item *ktype_pi = NULL; proto_item *vtype_pi = NULL; gint32 container_len, len_len, i, types; gint32 len_offset = offset; thrift_compact_type_enum_t ktype, vtype; guint64 varint; + guint nested_count = p_get_proto_depth(pinfo, proto_thrift); /* Dissect field header if necessary. */ if (is_field) { @@ -1478,7 +1847,7 @@ dissect_thrift_t_map(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int of return THRIFT_SUBDISSECTOR_ERROR; default: if (varint > (guint64)INT32_MAX) { - len_pi = proto_tree_add_int64(sub_tree, hf_thrift_i64, tvb, offset, len_len, varint); + proto_item *len_pi = proto_tree_add_int64(sub_tree, hf_thrift_i64, tvb, offset, len_len, varint); expert_add_info(pinfo, len_pi, &ei_thrift_varint_too_large); return THRIFT_SUBDISSECTOR_ERROR; } @@ -1488,12 +1857,18 @@ dissect_thrift_t_map(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int of } /* Create the sub-tree. */ + if (nested_count >= thrift_opt->nested_type_depth) { + expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes); + return THRIFT_REQUEST_REASSEMBLY; + } + p_set_proto_depth(pinfo, proto_thrift, nested_count + 1); container_pi = proto_tree_add_item(tree, hf_id, tvb, len_offset, -1, ENC_BIG_ENDIAN); sub_tree = proto_item_add_subtree(container_pi, ett_id); if (container_len == 0) { proto_item_set_end(container_pi, tvb, offset); proto_item_append_text(container_pi, " (Empty)"); + p_set_proto_depth(pinfo, proto_thrift, nested_count); return offset; } @@ -1539,18 +1914,32 @@ dissect_thrift_t_map(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int of if (container_pi && offset > 0) { proto_item_set_end(container_pi, tvb, offset); } - return offset; + result = offset; + p_set_proto_depth(pinfo, proto_thrift, nested_count); } + + if (is_field) { + thrift_opt->previous_field_id = field_id; + } + return result; } int dissect_thrift_t_struct(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id, gint ett_id, const thrift_member_t *seq) { + return dissect_thrift_t_struct_expert(tvb, pinfo, tree, offset, thrift_opt, is_field, field_id, hf_id, ett_id, seq, NULL); +} + +static int +// NOLINTNEXTLINE(misc-no-recursion) +dissect_thrift_t_struct_expert(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, thrift_option_data_t *thrift_opt, gboolean is_field, int field_id, gint hf_id, gint ett_id, const thrift_member_t *seq, expert_field* ei) +{ thrift_field_header_t field_header; proto_tree *sub_tree = NULL; proto_item *type_pi = NULL; gboolean enable_subtree = (ett_id != DISABLE_SUBTREE) || (hf_id != DISABLE_SUBTREE); + guint nested_count = p_get_proto_depth(pinfo, proto_thrift); /* Get the current state of dissection. */ DISSECTOR_ASSERT(thrift_opt); @@ -1571,6 +1960,11 @@ dissect_thrift_t_struct(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int /* Create the sub-tree, if not explicitly refused. */ if (enable_subtree) { /* Add the struct to the tree. */ + if (nested_count >= thrift_opt->nested_type_depth) { + expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes); + return THRIFT_REQUEST_REASSEMBLY; + } + p_set_proto_depth(pinfo, proto_thrift, nested_count--); type_pi = proto_tree_add_item(tree, hf_id, tvb, offset, -1, ENC_BIG_ENDIAN); sub_tree = proto_item_add_subtree(type_pi, ett_id); } else { @@ -1587,7 +1981,7 @@ dissect_thrift_t_struct(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int * Never create the field header sub-tree here as it will be handled by the field's own dissector. * We only want to get the type & field id information to compare them against what we expect. */ - if (dissect_thrift_field_header(tvb, pinfo, NULL, &local_offset, thrift_opt, &field_header) == THRIFT_REQUEST_REASSEMBLY) { + if (dissect_thrift_field_header(tvb, pinfo, NULL, &local_offset, thrift_opt, &field_header, FALSE) == THRIFT_REQUEST_REASSEMBLY) { if (local_offset == THRIFT_REQUEST_REASSEMBLY) { return THRIFT_REQUEST_REASSEMBLY; } else { @@ -1625,15 +2019,16 @@ dissect_thrift_t_struct(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int /* The field is not defined in the struct, switch back to generic dissection. * Re-read the header ensuring it is displayed (no need to check result, * we already dissected it but without the header tree creation. */ - dissect_thrift_field_header(tvb, pinfo, sub_tree, &offset, thrift_opt, &field_header); + dissect_thrift_field_header(tvb, pinfo, sub_tree, &offset, thrift_opt, &field_header, FALSE); expert_add_info(pinfo, field_header.fid_pi, &ei_thrift_undefined_field_id); // Then dissect just this field. if (thrift_opt->tprotocol & PROTO_THRIFT_COMPACT) { - if (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) { + if (!is_thrift_compact_bool_type(field_header.type.compact) && + 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) { return THRIFT_REQUEST_REASSEMBLY; } } else { - if (dissect_thrift_binary_type(tvb, pinfo, sub_tree, &offset, thrift_opt, field_header.fh_tree, field_header.type.compact, field_header.type_pi) == THRIFT_REQUEST_REASSEMBLY) { + 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) { return THRIFT_REQUEST_REASSEMBLY; } } @@ -1651,10 +2046,19 @@ dissect_thrift_t_struct(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int /* The loop exits before dissecting the T_STOP. */ offset = dissect_thrift_t_stop(tvb, pinfo, sub_tree, offset); + /* Set expert info if required. */ + if (ei != NULL) { + expert_add_info(pinfo, type_pi, ei); + } + if (enable_subtree && offset > 0) { proto_item_set_end(type_pi, tvb, offset); } + if (is_field) { + thrift_opt->previous_field_id = field_id; + } + p_set_proto_depth(pinfo, proto_thrift, nested_count); return offset; } /*=====END SUB-DISSECTION=====*/ @@ -1733,6 +2137,7 @@ dissect_thrift_binary_binary(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree } static int +// NOLINTNEXTLINE(misc-no-recursion) 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) { /* Binary protocol list and set (5 bytes + elements): @@ -1760,6 +2165,7 @@ dissect_thrift_binary_linear(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree int hf_num_item = -1; int hf_vtype = hf_thrift_type; int min_len = TBP_THRIFT_LINEAR_LEN; + guint nested_count = p_get_proto_depth(pinfo, proto_thrift); /* Set the different hf_id & ett depending on effective type. */ switch (expected) { @@ -1787,6 +2193,11 @@ dissect_thrift_binary_linear(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree ABORT_ON_INCOMPLETE_PDU(min_len); /* Create the sub-tree. */ + if (nested_count >= thrift_opt->nested_type_depth) { + expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes); + return THRIFT_REQUEST_REASSEMBLY; + } + p_set_proto_depth(pinfo, proto_thrift, nested_count + 1); container_pi = proto_tree_add_item(tree, hf_container, tvb, *offset, -1, ENC_NA); sub_tree = proto_item_add_subtree(container_pi, ett); @@ -1817,28 +2228,33 @@ dissect_thrift_binary_linear(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree } proto_item_set_end(container_pi, tvb, *offset); + p_set_proto_depth(pinfo, proto_thrift, nested_count); return *offset; } static int +// NOLINTNEXTLINE(misc-no-recursion) dissect_thrift_binary_list(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt) { return dissect_thrift_binary_linear(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_LIST); } static int +// NOLINTNEXTLINE(misc-no-recursion) dissect_thrift_binary_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt) { return dissect_thrift_binary_linear(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_SET); } static int +// NOLINTNEXTLINE(misc-no-recursion) dissect_thrift_binary_map(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt) { return dissect_thrift_binary_linear(tvb, pinfo, tree, offset, thrift_opt, DE_THRIFT_T_MAP); } static int +// NOLINTNEXTLINE(misc-no-recursion) dissect_thrift_binary_fields(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt) { /* @@ -1863,11 +2279,13 @@ dissect_thrift_binary_fields(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree * - either dissect_thrift_common which creates the "Data" sub-tree, * - or dissect_thrift_binary_struct which creates the "Struct" sub-tree. */ - thrift_field_header_t field_header; + thrift_field_header_t field_header = { + .type.binary = DE_THRIFT_T_STOP, // Overwritten by dissect_thrift_field_header() but undetected by Clang. + }; thrift_opt->previous_field_id = 0; while (TRUE) { - if (dissect_thrift_field_header(tvb, pinfo, tree, offset, thrift_opt, &field_header) == THRIFT_REQUEST_REASSEMBLY) { + if (dissect_thrift_field_header(tvb, pinfo, tree, offset, thrift_opt, &field_header, TRUE) == THRIFT_REQUEST_REASSEMBLY) { return THRIFT_REQUEST_REASSEMBLY; } if (field_header.type.binary == DE_THRIFT_T_STOP) { @@ -1882,6 +2300,7 @@ dissect_thrift_binary_fields(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree } static int +// NOLINTNEXTLINE(misc-no-recursion) dissect_thrift_binary_struct(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt) { /* This function only creates the "Struct" sub-tree @@ -1889,8 +2308,14 @@ dissect_thrift_binary_struct(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree */ proto_tree *sub_tree; proto_item *pi; + guint nested_count = p_get_proto_depth(pinfo, proto_thrift); ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_STRUCT_LEN); + if (nested_count >= thrift_opt->nested_type_depth) { + expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes); + return THRIFT_REQUEST_REASSEMBLY; + } + p_set_proto_depth(pinfo, proto_thrift, nested_count + 1); pi = proto_tree_add_item(tree, hf_thrift_struct, tvb, *offset, -1, ENC_NA); sub_tree = proto_item_add_subtree(pi, ett_thrift_struct); @@ -1899,10 +2324,12 @@ dissect_thrift_binary_struct(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree } else { proto_item_set_end(pi, tvb, *offset); } + p_set_proto_depth(pinfo, proto_thrift, nested_count); return *offset; } static int +// NOLINTNEXTLINE(misc-no-recursion) 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) { switch (type) { @@ -1936,6 +2363,11 @@ dissect_thrift_binary_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_tree_add_item(tree, hf_thrift_double, tvb, *offset, TBP_THRIFT_DOUBLE_LEN, ENC_BIG_ENDIAN); *offset += TBP_THRIFT_DOUBLE_LEN; break; + case DE_THRIFT_T_UUID: + ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_UUID_LEN); + proto_tree_add_item(tree, hf_thrift_uuid, tvb, *offset, TBP_THRIFT_UUID_LEN, ENC_BIG_ENDIAN); + *offset += TBP_THRIFT_UUID_LEN; + break; case DE_THRIFT_T_BINARY: if (dissect_thrift_binary_binary(tvb, pinfo, tree, offset, thrift_opt, header_tree) == THRIFT_REQUEST_REASSEMBLY) { return THRIFT_REQUEST_REASSEMBLY; @@ -2033,6 +2465,7 @@ dissect_thrift_compact_binary(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre } static int +// NOLINTNEXTLINE(misc-no-recursion) dissect_thrift_compact_list_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt, gboolean is_list) { /* Compact protocol list/set (short form, 1 byte): @@ -2059,6 +2492,8 @@ dissect_thrift_compact_list_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *t int ett = ett_thrift_set; int hf_container = hf_thrift_set; int hf_num_item = hf_thrift_num_set_item; + int hf_pos_item = hf_thrift_num_set_pos; + guint nested_count = p_get_proto_depth(pinfo, proto_thrift); ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_TYPE_LEN); /* Set the different hf_id & ett depending on effective type. */ @@ -2066,9 +2501,15 @@ dissect_thrift_compact_list_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *t ett = ett_thrift_list; hf_container = hf_thrift_list; hf_num_item = hf_thrift_num_list_item; + hf_pos_item = hf_thrift_num_list_pos; } /* Create the sub-tree. */ + if (nested_count >= thrift_opt->nested_type_depth) { + expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes); + return THRIFT_REQUEST_REASSEMBLY; + } + p_set_proto_depth(pinfo, proto_thrift, nested_count + 1); container_pi = proto_tree_add_item(tree, hf_container, tvb, *offset, -1, ENC_NA); sub_tree = proto_item_add_subtree(container_pi, ett); @@ -2104,7 +2545,7 @@ dissect_thrift_compact_list_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *t break; } } else { - len_pi = proto_tree_add_bits_item(sub_tree, hf_num_item, tvb, (lt_offset << OCTETS_TO_BITS_SHIFT), TCP_THRIFT_NIBBLE_SHIFT, ENC_BIG_ENDIAN); + 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); } if (container_len < 0) { expert_add_info(pinfo, len_pi, &ei_thrift_negative_length); @@ -2119,22 +2560,26 @@ dissect_thrift_compact_list_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *t } proto_item_set_end(container_pi, tvb, *offset); + p_set_proto_depth(pinfo, proto_thrift, nested_count); return *offset; } static int +// NOLINTNEXTLINE(misc-no-recursion) dissect_thrift_compact_list(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt) { return dissect_thrift_compact_list_set(tvb, pinfo, tree, offset, thrift_opt, TRUE); } static int +// NOLINTNEXTLINE(misc-no-recursion) dissect_thrift_compact_set(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt) { return dissect_thrift_compact_list_set(tvb, pinfo, tree, offset, thrift_opt, FALSE); } static int +// NOLINTNEXTLINE(misc-no-recursion) dissect_thrift_compact_map(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt) { /* Compact protocol map header (1 byte, empty map): @@ -2159,9 +2604,15 @@ dissect_thrift_compact_map(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 types, ktype, vtype; gint32 container_len, len_len, i; guint64 varint; + guint nested_count = p_get_proto_depth(pinfo, proto_thrift); ABORT_ON_INCOMPLETE_PDU(TCP_THRIFT_MIN_VARINT_LEN); /* Create the sub-tree. */ + if (nested_count >= thrift_opt->nested_type_depth) { + expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes); + return THRIFT_REQUEST_REASSEMBLY; + } + p_set_proto_depth(pinfo, proto_thrift, nested_count + 1); container_pi = proto_tree_add_item(tree, hf_thrift_map, tvb, *offset, -1, ENC_NA); sub_tree = proto_item_add_subtree(container_pi, ett_thrift_map); @@ -2213,10 +2664,12 @@ dissect_thrift_compact_map(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, } proto_item_set_end(container_pi, tvb, *offset); + p_set_proto_depth(pinfo, proto_thrift, nested_count); return *offset; } static int +// NOLINTNEXTLINE(misc-no-recursion) dissect_thrift_compact_fields(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt) { /* @@ -2247,17 +2700,20 @@ dissect_thrift_compact_fields(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre * - either dissect_thrift_common which creates the "Data" sub-tree, * - or dissect_thrift_compact_struct which creates the "Struct" sub-tree. */ - thrift_field_header_t field_header; + thrift_field_header_t field_header = { + .type.compact = DE_THRIFT_C_STOP, // Overwritten by dissect_thrift_field_header() but undetected by Clang. + }; thrift_opt->previous_field_id = 0; while (TRUE) { - if (dissect_thrift_field_header(tvb, pinfo, tree, offset, thrift_opt, &field_header) == THRIFT_REQUEST_REASSEMBLY) { + if (dissect_thrift_field_header(tvb, pinfo, tree, offset, thrift_opt, &field_header, TRUE) == THRIFT_REQUEST_REASSEMBLY) { return THRIFT_REQUEST_REASSEMBLY; } if (field_header.type.compact == DE_THRIFT_C_STOP) { break; /* Need to break out of the loop, cannot do that in the switch. */ } - if (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) { + if (!is_thrift_compact_bool_type(field_header.type.compact) && + 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) { return THRIFT_REQUEST_REASSEMBLY; } thrift_opt->previous_field_id = field_header.field_id; @@ -2267,6 +2723,7 @@ dissect_thrift_compact_fields(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre } static int +// NOLINTNEXTLINE(misc-no-recursion) dissect_thrift_compact_struct(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int *offset, thrift_option_data_t *thrift_opt) { /* This function only creates the "Struct" sub-tree @@ -2274,8 +2731,14 @@ dissect_thrift_compact_struct(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre */ proto_tree *sub_tree; proto_item *pi; + guint nested_count = p_get_proto_depth(pinfo, proto_thrift); ABORT_ON_INCOMPLETE_PDU(TCP_THRIFT_STRUCT_LEN); + if (nested_count >= thrift_opt->nested_type_depth) { + expert_add_info(pinfo, proto_tree_get_parent(tree), &ei_thrift_too_many_subtypes); + return THRIFT_REQUEST_REASSEMBLY; + } + p_set_proto_depth(pinfo, proto_thrift, nested_count + 1); pi = proto_tree_add_item(tree, hf_thrift_struct, tvb, *offset, -1, ENC_NA); sub_tree = proto_item_add_subtree(pi, ett_thrift_struct); @@ -2284,19 +2747,21 @@ dissect_thrift_compact_struct(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre } else { proto_item_set_end(pi, tvb, *offset); } + p_set_proto_depth(pinfo, proto_thrift, nested_count); return *offset; } /* Dissect a compact thrift field of a given type. * * This function is used only for linear containers (list, set, map). - * It uses the same type identifiers as TBinaryProtocol. + * It uses the same type identifiers as TCompactProtocol, except for + * the bool type which is encoded in the same way as BOOL_FALSE (2). */ static int +// NOLINTNEXTLINE(misc-no-recursion) 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) { switch (type) { - case DE_THRIFT_C_BOOL_TRUE: case DE_THRIFT_C_BOOL_FALSE: ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_BOOL_LEN); proto_tree_add_item(tree, hf_thrift_bool, tvb, *offset, TBP_THRIFT_BOOL_LEN, ENC_BIG_ENDIAN); @@ -2308,17 +2773,17 @@ dissect_thrift_compact_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, *offset += TBP_THRIFT_I8_LEN; break; case DE_THRIFT_C_I16: - if (dissect_thrift_varint(tvb, pinfo, tree, offset, thrift_opt, TCP_THRIFT_MAX_I16_LEN, hf_thrift_i16) == THRIFT_REQUEST_REASSEMBLY) { + if (dissect_thrift_varint(tvb, pinfo, tree, offset, thrift_opt, TCP_THRIFT_MAX_I16_LEN, hf_thrift_i16, NULL) == THRIFT_REQUEST_REASSEMBLY) { return THRIFT_REQUEST_REASSEMBLY; } break; case DE_THRIFT_C_I32: - if (dissect_thrift_varint(tvb, pinfo, tree, offset, thrift_opt, TCP_THRIFT_MAX_I32_LEN, hf_thrift_i32) == THRIFT_REQUEST_REASSEMBLY) { + if (dissect_thrift_varint(tvb, pinfo, tree, offset, thrift_opt, TCP_THRIFT_MAX_I32_LEN, hf_thrift_i32, NULL) == THRIFT_REQUEST_REASSEMBLY) { return THRIFT_REQUEST_REASSEMBLY; } break; case DE_THRIFT_C_I64: - if (dissect_thrift_varint(tvb, pinfo, tree, offset, thrift_opt, TCP_THRIFT_MAX_I64_LEN, hf_thrift_i64) == THRIFT_REQUEST_REASSEMBLY) { + if (dissect_thrift_varint(tvb, pinfo, tree, offset, thrift_opt, TCP_THRIFT_MAX_I64_LEN, hf_thrift_i64, NULL) == THRIFT_REQUEST_REASSEMBLY) { return THRIFT_REQUEST_REASSEMBLY; } break; @@ -2333,6 +2798,11 @@ dissect_thrift_compact_type(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_tree_add_item(tree, hf_thrift_double, tvb, *offset, TBP_THRIFT_DOUBLE_LEN, ENC_LITTLE_ENDIAN); *offset += TBP_THRIFT_DOUBLE_LEN; break; + case DE_THRIFT_C_UUID: + ABORT_ON_INCOMPLETE_PDU(TBP_THRIFT_UUID_LEN); + proto_tree_add_item(tree, hf_thrift_uuid, tvb, *offset, TBP_THRIFT_UUID_LEN, ENC_BIG_ENDIAN); + *offset += TBP_THRIFT_UUID_LEN; + break; case DE_THRIFT_C_BINARY: if (dissect_thrift_compact_binary(tvb, pinfo, tree, offset, thrift_opt, header_tree) == THRIFT_REQUEST_REASSEMBLY) { return THRIFT_REQUEST_REASSEMBLY; @@ -2555,7 +3025,7 @@ dissect_thrift_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int o if (tvb_reported_length_remaining(tvb, offset) < str_len) { goto add_expert_and_reassemble; } - method_str = tvb_get_string_enc(pinfo->pool, tvb, offset, str_len, ENC_UTF_8|ENC_NA); + method_str = tvb_get_string_enc(pinfo->pool, tvb, offset, str_len, ENC_UTF_8); offset += str_len; } else if (thrift_opt->tprotocol & PROTO_THRIFT_STRICT) { if (remaining < TBP_THRIFT_STRICT_MIN_MESSAGE_LEN) { @@ -2572,7 +3042,7 @@ dissect_thrift_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int o goto add_expert_and_reassemble; } offset += TBP_THRIFT_VERSION_LEN + TBP_THRIFT_LENGTH_LEN; - method_str = tvb_get_string_enc(pinfo->pool, tvb, offset, str_len, ENC_UTF_8|ENC_NA); + method_str = tvb_get_string_enc(pinfo->pool, tvb, offset, str_len, ENC_UTF_8); offset += str_len; seq_id = tvb_get_ntohil(tvb, offset); @@ -2591,7 +3061,7 @@ dissect_thrift_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int o goto add_expert_and_reassemble; } offset += TBP_THRIFT_LENGTH_LEN; - method_str = tvb_get_string_enc(pinfo->pool, tvb, offset, str_len, ENC_UTF_8|ENC_NA); + method_str = tvb_get_string_enc(pinfo->pool, tvb, offset, str_len, ENC_UTF_8); offset += str_len; mtype = tvb_get_guint8(tvb, offset + TBP_THRIFT_LENGTH_LEN + str_len) & THRIFT_BINARY_MESSAGE_MASK; offset += TBP_THRIFT_TYPE_LEN; @@ -2634,7 +3104,7 @@ dissect_thrift_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int o offset += seqid_len; proto_tree_add_int(sub_tree, hf_thrift_str_len, tvb, offset, str_len_len, str_len); offset += str_len_len; - proto_tree_add_item(sub_tree, hf_thrift_method, tvb, offset, str_len, ENC_UTF_8|ENC_NA); + proto_tree_add_item(sub_tree, hf_thrift_method, tvb, offset, str_len, ENC_UTF_8); offset = offset + str_len; } else if (thrift_opt->tprotocol & PROTO_THRIFT_STRICT) { /* Strict: proto_id|version|mtype|length|name|seqid */ @@ -2645,7 +3115,7 @@ dissect_thrift_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int o offset += TBP_THRIFT_MTYPE_LEN; proto_tree_add_item(sub_tree, hf_thrift_str_len, tvb, offset, TBP_THRIFT_LENGTH_LEN, ENC_BIG_ENDIAN); offset += TBP_THRIFT_LENGTH_LEN; - proto_tree_add_item(sub_tree, hf_thrift_method, tvb, offset, str_len, ENC_UTF_8|ENC_NA); + proto_tree_add_item(sub_tree, hf_thrift_method, tvb, offset, str_len, ENC_UTF_8); offset = offset + str_len; proto_tree_add_item(sub_tree, hf_thrift_seq_id, tvb, offset, TBP_THRIFT_SEQ_ID_LEN, ENC_BIG_ENDIAN); offset += TBP_THRIFT_SEQ_ID_LEN; @@ -2653,7 +3123,7 @@ dissect_thrift_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int o /* Old: length|name|mtype|seqid */ proto_tree_add_item(sub_tree, hf_thrift_str_len, tvb, offset, TBP_THRIFT_LENGTH_LEN, ENC_BIG_ENDIAN); offset += TBP_THRIFT_LENGTH_LEN; - proto_tree_add_item(sub_tree, hf_thrift_method, tvb, offset, str_len, ENC_UTF_8|ENC_NA); + proto_tree_add_item(sub_tree, hf_thrift_method, tvb, offset, str_len, ENC_UTF_8); offset = offset + str_len; mtype_pi = proto_tree_add_bits_item(sub_tree, hf_thrift_mtype, tvb, (offset << OCTETS_TO_BITS_SHIFT) + 5, 3, ENC_BIG_ENDIAN); offset += TBP_THRIFT_MTYPE_LEN; @@ -2676,7 +3146,9 @@ dissect_thrift_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int o thrift_opt->previous_field_id = 0; msg_tvb = tvb_new_subset_remaining(tvb, data_offset); if (thrift_opt->mtype == ME_THRIFT_T_REPLY) { - thrift_field_header_t header; + thrift_field_header_t header = { + .field_id = 0, // Overwritten by dissect_thrift_field_header() but undetected by Clang. + }; /* For REPLY, in order to separate successful answers from errors (exceptions), * Thrift generates a struct with as much fields (all optional) as there are exceptions possible + 1. * At most 1 field will be filled for any reply @@ -2687,7 +3159,7 @@ dissect_thrift_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int o * We read this before checking for sub-dissectors as the information might be useful to them. */ int result = data_offset; - result = dissect_thrift_field_header(tvb, pinfo, NULL, &result, thrift_opt, &header); + result = dissect_thrift_field_header(tvb, pinfo, NULL, &result, thrift_opt, &header, FALSE); switch (result) { case THRIFT_REQUEST_REASSEMBLY: goto add_expert_and_reassemble; @@ -2704,6 +3176,7 @@ dissect_thrift_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int o len = dissector_try_string(thrift_method_name_dissector_table, method_str, msg_tvb, pinfo, tree, thrift_opt); if (pinfo->can_desegment > 0) pinfo->can_desegment--; } else { + /* Attach the expert_info to the method type as it is a protocol-level exception. */ expert_add_info(pinfo, mtype_pi, &ei_thrift_protocol_exception); /* Leverage the sub-dissector capabilities to dissect Thrift exceptions. */ len = dissect_thrift_t_struct(msg_tvb, pinfo, thrift_tree, 0, thrift_opt, FALSE, 0, hf_thrift_exception, ett_thrift_exception, thrift_exception); @@ -2732,7 +3205,7 @@ dissect_thrift_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int o thrift_opt->reassembly_length = TBP_THRIFT_TYPE_LEN; if (thrift_opt->reply_field_id != 0) { expert_add_info(pinfo, fid_pi, &ei_thrift_application_exception); - proto_item_set_text(data_pi, "Exception: %" G_GINT64_MODIFIER "d", thrift_opt->reply_field_id); + proto_item_set_text(data_pi, "Exception: %" PRId64, thrift_opt->reply_field_id); } if (is_compact) { @@ -2854,7 +3327,9 @@ dissect_thrift_framed(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void DISSECTOR_ASSERT(thrift_opt->canary == THRIFT_OPTION_DATA_CANARY); DISSECTOR_ASSERT(thrift_opt->tprotocol & PROTO_THRIFT_FRAMED); frame_len = tvb_get_ntohil(tvb, offset); - DISSECTOR_ASSERT((frame_len + TBP_THRIFT_LENGTH_LEN) == reported); + // Note: The framed dissector can be called even if the entire packet is bigger than the frame. + // This can happen when the frame was initially rejected because it was too short. + DISSECTOR_ASSERT((frame_len + TBP_THRIFT_LENGTH_LEN) <= reported); offset = dissect_thrift_common(tvb, pinfo, tree, offset, thrift_opt); if (offset == THRIFT_REQUEST_REASSEMBLY) { @@ -2877,6 +3352,7 @@ dissect_thrift_transport(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, vo gint32 str_len, length = tvb_reported_length(tvb); thrift_option_data_t thrift_opt; memset(&thrift_opt, 0, sizeof(thrift_option_data_t)); + thrift_opt.nested_type_depth = nested_type_depth; /* Starting without even the version / frame length / name length probably means a Keep-Alive at the beginning of the capture. */ if (length < TBP_THRIFT_VERSION_LEN) { @@ -3143,7 +3619,11 @@ test_thrift_compact(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree _U_, /* 4. Get method name length and check against what we have. */ if ((guint)offset >= length) return FALSE; - str_len = tvb_get_gint8(tvb, offset); + str_len = tvb_get_guint8(tvb, offset); + // MSb = 1 means the method length is greater than 127 bytes long. + if ((str_len & 0x80) != 0) { + return FALSE; // Reject it to avoid too many false positive. + } ++offset; if ((tframe_length > 0) && (TBP_THRIFT_LENGTH_LEN + tframe_length < offset + str_len)) { /* The frame cannot even contain an empty Thrift message (no data, only T_STOP after the sequence id). */ @@ -3175,6 +3655,7 @@ dissect_thrift_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *d { thrift_option_data_t thrift_opt; memset(&thrift_opt, 0, sizeof(thrift_option_data_t)); + thrift_opt.nested_type_depth = nested_type_depth; if (!test_thrift_strict(tvb, pinfo, tree, &thrift_opt) && !test_thrift_compact(tvb, pinfo, tree, &thrift_opt)) { return FALSE; @@ -3314,7 +3795,7 @@ proto_register_thrift(void) NULL, HFILL } }, { &hf_thrift_string, - { "String", "thrift.binary", + { "String", "thrift.string", FT_STRING, BASE_NONE, NULL, 0x0, "Binary field interpreted as a string.", HFILL } }, @@ -3343,11 +3824,21 @@ proto_register_thrift(void) FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL } }, + { &hf_thrift_num_set_pos, + { "Number of Set Items", "thrift.num_set_pos", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, { &hf_thrift_num_list_item, { "Number of List Items", "thrift.num_list_item", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL } }, + { &hf_thrift_num_list_pos, + { "Number of List Items", "thrift.num_list_pos", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, { &hf_thrift_num_map_item, { "Number of Map Items", "thrift.num_map_item", FT_INT32, BASE_DEC, NULL, 0x0, @@ -3358,6 +3849,11 @@ proto_register_thrift(void) FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } }, + { &hf_thrift_uuid, + { "UUID", "thrift.uuid", + FT_GUID, BASE_NONE, NULL, 0x0, + NULL, HFILL } + }, }; @@ -3387,9 +3883,10 @@ proto_register_thrift(void) { &ei_thrift_varint_too_large, { "thrift.varint_too_large", PI_PROTOCOL, PI_ERROR, "Thrift varint value too large for target integer type.", EXPFILL } }, { &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 } }, { &ei_thrift_negative_field_id, { "thrift.negative_field_id", PI_PROTOCOL, PI_NOTE, "Encountered unexpected negative field id, possibly an old application.", EXPFILL } }, - { &ei_thrift_unordered_field_id, { "thrift.unordered_field_id", PI_PROTOCOL, PI_WARN, "Field id not defined by sub-dissector, using generic Thrift dissector.", EXPFILL } }, + { &ei_thrift_unordered_field_id, { "thrift.unordered_field_id", PI_PROTOCOL, PI_WARN, "Field id not in strictly increasing order.", EXPFILL } }, { &ei_thrift_application_exception, { "thrift.application_exception", PI_PROTOCOL, PI_NOTE, "The application recognized the method but rejected the content.", EXPFILL } }, { &ei_thrift_protocol_exception, { "thrift.protocol_exception", PI_PROTOCOL, PI_WARN, "The application was not able to handle the request.", EXPFILL } }, + { &ei_thrift_too_many_subtypes, { "thrift.too_many_subtypes", PI_PROTOCOL, PI_ERROR, "Too many level of sub-types nesting.", EXPFILL } }, }; @@ -3412,11 +3909,12 @@ proto_register_thrift(void) /* register dissector */ thrift_handle = register_dissector("thrift", dissect_thrift_transport, proto_thrift); + thrift_http_handle = register_dissector("thrift.http", dissect_thrift_heur, proto_thrift); thrift_module = prefs_register_protocol(proto_thrift, proto_reg_handoff_thrift); thrift_method_name_dissector_table = register_dissector_table("thrift.method_names", "Thrift Method names", - proto_thrift, FT_STRING, FALSE); /* FALSE because Thrift is case-sensitive */ + proto_thrift, FT_STRING, STRING_CASE_SENSITIVE); /* Thrift is case-sensitive. */ prefs_register_enum_preference(thrift_module, "decode_binary", "Display binary as bytes or strings", @@ -3439,6 +3937,13 @@ proto_register_thrift(void) " This option can be useful if the data is well-formed but the sub-dissector is expecting different type/content.", &try_generic_if_sub_dissector_fails); + prefs_register_uint_preference(thrift_module, "nested_type_depth", + "Thrift nested types depth", + "Maximum expected depth of nested types in the Thrift structures and containers." + " A Thrift-based protocol using no parameter and void return types only uses a depth of 0." + " A Thrift-based protocol using only simple types as parameters or return values uses a depth of 1.", + 10, &nested_type_depth); + prefs_register_bool_preference(thrift_module, "desegment_framed", "Reassemble Framed Thrift messages spanning multiple TCP segments", "Whether the Thrift dissector should reassemble framed messages spanning multiple TCP segments." @@ -3450,11 +3955,8 @@ void proto_reg_handoff_thrift(void) { static guint saved_thrift_tls_port; - static dissector_handle_t thrift_http_handle; static gboolean thrift_initialized = FALSE; - thrift_http_handle = create_dissector_handle(dissect_thrift_heur, proto_thrift); - if (!thrift_initialized) { thrift_initialized = TRUE; heur_dissector_add("tcp", dissect_thrift_heur, "Thrift over TCP", "thrift_tcp", proto_thrift, HEURISTIC_ENABLE); |