From 2e98866171ca5a9622085447b2a400937ea91286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Eeden?= Date: Sun, 20 Mar 2016 19:32:05 +0100 Subject: [PATCH] MySQL: Decoding of the header of compressed packets If client and server have the flag set then compression starts after the greeting,login,ok. This comments makes it possible to decode packets which use the compressed protocol but don't have an compressed payload. Ping-Bug: 10342 Change-Id: I710f655c86feb9770556d1ffa69edd728e0374c3 Reviewed-on: https://code.wireshark.org/review/14603 Reviewed-by: Alexis La Goutte Petri-Dish: Alexis La Goutte Tested-by: Petri Dish Buildbot Reviewed-by: Michael Mann --- epan/dissectors/packet-mysql.c | 69 +++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) 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[]=