diff options
-rw-r--r-- | epan/dissectors/packet-mysql.c | 69 |
1 files changed, 68 insertions, 1 deletions
diff --git a/epan/dissectors/packet-mysql.c b/epan/dissectors/packet-mysql.c index b23a9eff8e..244dcdc528 100644 --- a/epan/dissectors/packet-mysql.c +++ b/epan/dissectors/packet-mysql.c @@ -171,6 +171,11 @@ void proto_reg_handoff_mysql(void); #define MYSQL_PARAM_FLAG_STREAMED 0x01 +/* Compression states, internal to the dissector */ +#define MYSQL_COMPRESS_NONE 0 +#define MYSQL_COMPRESS_INIT 1 +#define MYSQL_COMPRESS_ACTIVE 2 + /* decoding table: command */ static const value_string mysql_command_vals[] = { {MYSQL_SLEEP, "SLEEP"}, @@ -618,6 +623,9 @@ static int hf_mysql_auth_switch_request_status = -1; static int hf_mysql_auth_switch_request_name = -1; static int hf_mysql_auth_switch_request_data = -1; static int hf_mysql_auth_switch_response_data = -1; +static int hf_mysql_compressed_packet_length = -1; +static int hf_mysql_compressed_packet_length_uncompressed = -1; +static int hf_mysql_compressed_packet_number = -1; static dissector_handle_t mysql_handle; static dissector_handle_t ssl_handle; @@ -712,6 +720,8 @@ typedef struct mysql_conn_data { #endif guint8 major_version; guint32 frame_start_ssl; + guint32 frame_start_compressed; + guint8 compressed_state; } mysql_conn_data_t; struct mysql_frame_data { @@ -1578,6 +1588,24 @@ hf_mysql_refresh, ett_refresh, mysql_rfsh_flags, ENC_BIG_ENDIAN, BMT_NO_APPEND); return offset; } +/* + * Decode the header of a compressed packet + * https://dev.mysql.com/doc/internals/en/compressed-packet-header.html + */ +static int +mysql_dissect_compressed_header(tvbuff_t *tvb, int offset, proto_tree *mysql_tree) +{ + proto_tree_add_item(mysql_tree, hf_mysql_compressed_packet_length, tvb, offset, 3, ENC_LITTLE_ENDIAN); + offset += 3; + + proto_tree_add_item(mysql_tree, hf_mysql_compressed_packet_number, tvb, offset, 1, ENC_NA); + offset += 1; + + proto_tree_add_item(mysql_tree, hf_mysql_compressed_packet_length_uncompressed, tvb, offset, 3, ENC_LITTLE_ENDIAN); + offset += 3; + + return offset; +} static int mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo, int offset, @@ -1635,6 +1663,10 @@ mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo, int offset, offset = mysql_dissect_response_prepare(tvb, offset, tree, conn_data); } else if (tvb_reported_length_remaining(tvb, offset+1) > tvb_get_fle(tvb, offset+1, NULL, NULL)) { offset = mysql_dissect_ok_packet(tvb, pinfo, offset+1, tree, conn_data); + if (conn_data->compressed_state == MYSQL_COMPRESS_INIT) { + /* This is the OK packet which follows the compressed protocol setup */ + conn_data->compressed_state = MYSQL_COMPRESS_ACTIVE; + } } else { offset = mysql_dissect_result_header(tvb, pinfo, offset, tree, conn_data); } @@ -2154,8 +2186,14 @@ tvb_get_fle(tvbuff_t *tvb, int offset, guint64 *res, guint8 *is_null) static guint get_mysql_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) { + int tvb_remain= tvb_reported_length_remaining(tvb, offset); guint plen= tvb_get_letoh24(tvb, offset); - return plen + 4; /* add length field + packet number */ + + if ((tvb_remain - plen) == 7) { + return plen + 7; /* compressed header 3+1+3 (len+id+cmp_len) */ + } else { + return plen + 4; /* regular header 3+1 (len+id) */ + } } /* dissector main function: handle one PDU */ @@ -2193,6 +2231,8 @@ dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat #endif conn_data->major_version= 0; conn_data->frame_start_ssl= 0; + conn_data->frame_start_compressed= 0; + conn_data->compressed_state= MYSQL_COMPRESS_NONE; conversation_add_proto_data(conversation, proto_mysql, conn_data); } @@ -2225,6 +2265,12 @@ dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat conn_data->state= mysql_frame_data_p->state; } + if ((conn_data->frame_start_compressed) && (pinfo->num > conn_data->frame_start_compressed)) { + if (conn_data->compressed_state == MYSQL_COMPRESS_ACTIVE) { + offset = mysql_dissect_compressed_header(tvb, offset, tree); + } + } + if (tree) { ti = proto_tree_add_item(tree, proto_mysql, tvb, offset, -1, ENC_NA); mysql_tree = proto_item_add_subtree(ti, ett_mysql); @@ -2278,6 +2324,12 @@ dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat if (packet_number == 1 || (packet_number == 2 && is_ssl)) { col_set_str(pinfo->cinfo, COL_INFO, "Login Request"); offset = mysql_dissect_login(tvb, pinfo, offset, mysql_tree, conn_data); + if (conn_data->srv_caps & MYSQL_CAPS_CP) { + if (conn_data->clnt_caps & MYSQL_CAPS_CP) { + conn_data->frame_start_compressed = pinfo->num; + conn_data->compressed_state = MYSQL_COMPRESS_INIT; + } + } } else { col_set_str(pinfo->cinfo, COL_INFO, "Request"); offset = mysql_dissect_request(tvb, pinfo, offset, mysql_tree, conn_data); @@ -3161,6 +3213,21 @@ void proto_register_mysql(void) { "Auth Method Data", "mysql.auth_switch_response.data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + + { &hf_mysql_compressed_packet_length, + { "Compressed Packet Length", "mysql.compressed_packet_length", + FT_UINT24, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_mysql_compressed_packet_number, + { "Compressed Packet Number", "mysql.compressed_packet_number", + FT_UINT24, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_mysql_compressed_packet_length_uncompressed, + { "Uncompressed Packet Length", "mysql.compressed_packet_length_uncompressed", + FT_UINT24, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, }; static gint *ett[]= |