diff options
-rw-r--r-- | epan/dissectors/packet-mysql.c | 150 |
1 files changed, 74 insertions, 76 deletions
diff --git a/epan/dissectors/packet-mysql.c b/epan/dissectors/packet-mysql.c index cc2bf9e4f3..dc6881bf87 100644 --- a/epan/dissectors/packet-mysql.c +++ b/epan/dissectors/packet-mysql.c @@ -750,7 +750,7 @@ static int mysql_dissect_extcaps(tvbuff_t *tvb, int offset, proto_tree *tree, in static int mysql_dissect_result_header(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, mysql_conn_data_t *conn_data); static int mysql_dissect_field_packet(tvbuff_t *tvb, int offset, proto_tree *tree, mysql_conn_data_t *conn_data); static int mysql_dissect_row_packet(tvbuff_t *tvb, int offset, proto_tree *tree); -static int mysql_dissect_response_prepare(tvbuff_t *tvb, int offset, proto_tree *tree, mysql_conn_data_t *conn_data); +static int mysql_dissect_response_prepare(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, mysql_conn_data_t *conn_data); static int mysql_dissect_auth_switch_request(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, mysql_conn_data_t *conn_data); static int mysql_dissect_auth_switch_response(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, mysql_conn_data_t *conn_data); static void mysql_dissect_exec_string(tvbuff_t *tvb, int *param_offset, proto_item *field_tree); @@ -869,6 +869,15 @@ static const int * mysql_fld_flags[] = { NULL }; +/* Helper function to only set state on first pass */ +static void mysql_set_conn_state(packet_info *pinfo, mysql_conn_data_t *conn_data, mysql_state_t state) +{ + if (!pinfo->fd->flags.visited) + { + conn_data->state = state; + } +} + static int mysql_dissect_greeting(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, mysql_conn_data_t *conn_data) @@ -886,7 +895,7 @@ mysql_dissect_greeting(tvbuff_t *tvb, packet_info *pinfo, int offset, return mysql_dissect_error_packet(tvb, pinfo, offset+1, tree); } - conn_data->state= LOGIN; + mysql_set_conn_state(pinfo, conn_data, LOGIN); tf = proto_tree_add_item(tree, hf_mysql_server_greeting, tvb, offset, -1, ENC_NA); greeting_tree = proto_item_add_subtree(tf, ett_server_greeting); @@ -1010,7 +1019,7 @@ mysql_dissect_login(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_item *login_tree; /* after login there can be OK or DENIED */ - conn_data->state = RESPONSE_OK; + mysql_set_conn_state(pinfo, conn_data, RESPONSE_OK); tf = proto_tree_add_item(tree, hf_mysql_login_request, tvb, offset, -1, ENC_NA); login_tree = proto_item_add_subtree(tf, ett_login_request); @@ -1080,7 +1089,7 @@ mysql_dissect_login(tvbuff_t *tvb, packet_info *pinfo, int offset, /* optional: authentication plugin */ if (conn_data->clnt_caps_ext & MYSQL_CAPS_PA) { - conn_data->state = AUTH_SWITCH_REQUEST; + mysql_set_conn_state(pinfo, conn_data, AUTH_SWITCH_REQUEST); lenstr= my_tvb_strsize(tvb,offset); proto_tree_add_item(login_tree, hf_mysql_client_auth_plugin, tvb, offset, lenstr, ENC_ASCII|ENC_NA); offset += lenstr; @@ -1293,7 +1302,7 @@ mysql_dissect_exec_param(proto_item *req_tree, tvbuff_t *tvb, int *offset, static int mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, - proto_tree *tree, mysql_conn_data_t *conn_data) + proto_tree *tree, mysql_conn_data_t *conn_data, mysql_state_t current_state) { gint opcode; gint lenstr; @@ -1303,7 +1312,7 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, my_stmt_data_t *stmt_data; int stmt_pos, param_offset; - if(conn_data->state == AUTH_SWITCH_RESPONSE){ + if(current_state == AUTH_SWITCH_RESPONSE){ return mysql_dissect_auth_switch_response(tvb, pinfo, offset, tree, conn_data); } @@ -1324,16 +1333,16 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, break; case MYSQL_PROCESS_INFO: - conn_data->state = RESPONSE_TABULAR; + mysql_set_conn_state(pinfo, conn_data, RESPONSE_TABULAR); break; case MYSQL_DEBUG: case MYSQL_PING: - conn_data->state = RESPONSE_OK; + mysql_set_conn_state(pinfo, conn_data, RESPONSE_OK); break; case MYSQL_STATISTICS: - conn_data->state = RESPONSE_MESSAGE; + mysql_set_conn_state(pinfo, conn_data, RESPONSE_MESSAGE); break; case MYSQL_INIT_DB: @@ -1342,7 +1351,7 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, lenstr = my_tvb_strsize(tvb, offset); proto_tree_add_item(req_tree, hf_mysql_schema, tvb, offset, lenstr, ENC_ASCII|ENC_NA); offset += lenstr; - conn_data->state = RESPONSE_OK; + mysql_set_conn_state(pinfo, conn_data, RESPONSE_OK); break; case MYSQL_QUERY: @@ -1353,39 +1362,39 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, tvb_format_text(tvb, offset, lenstr)); } offset += lenstr; - conn_data->state = RESPONSE_TABULAR; + mysql_set_conn_state(pinfo, conn_data, RESPONSE_TABULAR); break; case MYSQL_STMT_PREPARE: lenstr = my_tvb_strsize(tvb, offset); proto_tree_add_item(req_tree, hf_mysql_query, tvb, offset, lenstr, ENC_ASCII|ENC_NA); offset += lenstr; - conn_data->state = RESPONSE_PREPARE; + mysql_set_conn_state(pinfo, conn_data, RESPONSE_PREPARE); break; case MYSQL_STMT_CLOSE: proto_tree_add_item(req_tree, hf_mysql_stmt_id, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; - conn_data->state = REQUEST; + mysql_set_conn_state(pinfo, conn_data, REQUEST); break; case MYSQL_STMT_RESET: proto_tree_add_item(req_tree, hf_mysql_stmt_id, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; - conn_data->state = RESPONSE_OK; + mysql_set_conn_state(pinfo, conn_data, RESPONSE_OK); break; case MYSQL_FIELD_LIST: lenstr = my_tvb_strsize(tvb, offset); proto_tree_add_item(req_tree, hf_mysql_table_name, tvb, offset, lenstr, ENC_ASCII|ENC_NA); offset += lenstr; - conn_data->state = RESPONSE_SHOW_FIELDS; + mysql_set_conn_state(pinfo, conn_data, RESPONSE_SHOW_FIELDS); break; case MYSQL_PROCESS_KILL: proto_tree_add_item(req_tree, hf_mysql_thd_id, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; - conn_data->state = RESPONSE_OK; + mysql_set_conn_state(pinfo, conn_data, RESPONSE_OK); break; case MYSQL_CHANGE_USER: @@ -1410,12 +1419,12 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree_add_item(req_tree, hf_mysql_charset, tvb, offset, 1, ENC_NA); offset += 2; /* for charset */ } - conn_data->state = RESPONSE_OK; + mysql_set_conn_state(pinfo, conn_data, RESPONSE_OK); /* optional: authentication plugin */ if (conn_data->clnt_caps_ext & MYSQL_CAPS_PA) { - conn_data->state = AUTH_SWITCH_REQUEST; + mysql_set_conn_state(pinfo, conn_data, AUTH_SWITCH_REQUEST); lenstr= my_tvb_strsize(tvb,offset); proto_tree_add_item(req_tree, hf_mysql_client_auth_plugin, tvb, offset, lenstr, ENC_ASCII|ENC_NA); offset += lenstr; @@ -1447,19 +1456,19 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree_add_bitmask_with_flags(req_tree, tvb, offset, hf_mysql_refresh, ett_refresh, mysql_rfsh_flags, ENC_BIG_ENDIAN, BMT_NO_APPEND); offset += 1; - conn_data->state= RESPONSE_OK; + mysql_set_conn_state(pinfo, conn_data, RESPONSE_OK); break; case MYSQL_SHUTDOWN: proto_tree_add_item(req_tree, hf_mysql_shutdown, tvb, offset, 1, ENC_NA); offset += 1; - conn_data->state = RESPONSE_OK; + mysql_set_conn_state(pinfo, conn_data, RESPONSE_OK); break; case MYSQL_SET_OPTION: proto_tree_add_item(req_tree, hf_mysql_option, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; - conn_data->state = RESPONSE_OK; + mysql_set_conn_state(pinfo, conn_data, RESPONSE_OK); break; case MYSQL_STMT_FETCH: @@ -1468,7 +1477,7 @@ hf_mysql_refresh, ett_refresh, mysql_rfsh_flags, ENC_BIG_ENDIAN, BMT_NO_APPEND); proto_tree_add_item(req_tree, hf_mysql_num_rows, tvb, offset, 4, ENC_LITTLE_ENDIAN); offset += 4; - conn_data->state = RESPONSE_TABULAR; + mysql_set_conn_state(pinfo, conn_data, RESPONSE_TABULAR); break; case MYSQL_STMT_SEND_LONG_DATA: @@ -1493,7 +1502,7 @@ hf_mysql_refresh, ett_refresh, mysql_rfsh_flags, ENC_BIG_ENDIAN, BMT_NO_APPEND); proto_tree_add_item(req_tree, hf_mysql_payload, tvb, offset, lenstr, ENC_NA); } offset += lenstr; - conn_data->state = REQUEST; + mysql_set_conn_state(pinfo, conn_data, REQUEST); break; case MYSQL_STMT_EXECUTE: @@ -1547,7 +1556,7 @@ hf_mysql_refresh, ett_refresh, mysql_rfsh_flags, ENC_BIG_ENDIAN, BMT_NO_APPEND); } offset += lenstr; #endif - conn_data->state= RESPONSE_TABULAR; + mysql_set_conn_state(pinfo, conn_data, RESPONSE_TABULAR); break; case MYSQL_BINLOG_DUMP: @@ -1567,7 +1576,7 @@ hf_mysql_refresh, ett_refresh, mysql_rfsh_flags, ENC_BIG_ENDIAN, BMT_NO_APPEND); } offset += lenstr; - conn_data->state = REQUEST; + mysql_set_conn_state(pinfo, conn_data, REQUEST); break; /* FIXME: implement replication packets */ case MYSQL_TABLE_DUMP: @@ -1576,14 +1585,14 @@ hf_mysql_refresh, ett_refresh, mysql_rfsh_flags, ENC_BIG_ENDIAN, BMT_NO_APPEND); ti = proto_tree_add_item(req_tree, hf_mysql_payload, tvb, offset, -1, ENC_NA); expert_add_info_format(pinfo, ti, &ei_mysql_dissector_incomplete, "FIXME: implement replication packets"); offset += tvb_reported_length_remaining(tvb, offset); - conn_data->state = REQUEST; + mysql_set_conn_state(pinfo, conn_data, REQUEST); break; default: ti = proto_tree_add_item(req_tree, hf_mysql_payload, tvb, offset, -1, ENC_NA); expert_add_info(pinfo, ti, &ei_mysql_command); offset += tvb_reported_length_remaining(tvb, offset); - conn_data->state = UNDEFINED; + mysql_set_conn_state(pinfo, conn_data, UNDEFINED); } return offset; @@ -1610,7 +1619,7 @@ mysql_dissect_compressed_header(tvbuff_t *tvb, int offset, proto_tree *mysql_tre static int mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo, int offset, - proto_tree *tree, mysql_conn_data_t *conn_data) + proto_tree *tree, mysql_conn_data_t *conn_data, mysql_state_t current_state) { gint response_code; gint lenstr; @@ -1621,7 +1630,7 @@ mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo, int offset, if (response_code == 0xff ) { offset = mysql_dissect_error_packet(tvb, pinfo, offset+1, tree); - conn_data->state= REQUEST; + mysql_set_conn_state(pinfo, conn_data, REQUEST); } else if (response_code == 0xfe && tvb_reported_length_remaining(tvb, offset) < 9) { @@ -1636,32 +1645,37 @@ mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo, int offset, offset = mysql_dissect_server_status(tvb, offset+2, tree, &server_status); } - if (conn_data->state == FIELD_PACKET) { - conn_data->state= ROW_PACKET; - } else if (conn_data->state == ROW_PACKET) { + switch (current_state) { + case FIELD_PACKET: + mysql_set_conn_state(pinfo, conn_data, ROW_PACKET); + break; + case ROW_PACKET: if (server_status & MYSQL_STAT_MU) { - conn_data->state= RESPONSE_TABULAR; + mysql_set_conn_state(pinfo, conn_data, RESPONSE_TABULAR); } else { - conn_data->state= REQUEST; + mysql_set_conn_state(pinfo, conn_data, REQUEST); } - } else if (conn_data->state == PREPARED_PARAMETERS) { + break; + case PREPARED_PARAMETERS: if (conn_data->stmt_num_fields > 0) { - conn_data->state= PREPARED_FIELDS; + mysql_set_conn_state(pinfo, conn_data, PREPARED_FIELDS); } else { - conn_data->state= REQUEST; + mysql_set_conn_state(pinfo, conn_data, REQUEST); } - } else if (conn_data->state == PREPARED_FIELDS) { - conn_data->state= REQUEST; - } else { + break; + case PREPARED_FIELDS: + mysql_set_conn_state(pinfo, conn_data, REQUEST); + break; + default: /* This should be an unreachable case */ - conn_data->state= REQUEST; + mysql_set_conn_state(pinfo, conn_data, REQUEST); expert_add_info(pinfo, ti, &ei_mysql_eof); } } else if (response_code == 0) { - if (conn_data->state == RESPONSE_PREPARE) { - offset = mysql_dissect_response_prepare(tvb, offset, tree, conn_data); + if (current_state == RESPONSE_PREPARE) { + offset = mysql_dissect_response_prepare(tvb, pinfo, 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) { @@ -1674,13 +1688,13 @@ mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo, int offset, } else { - switch (conn_data->state) { + switch (current_state) { case RESPONSE_MESSAGE: if ((lenstr = tvb_reported_length_remaining(tvb, offset))) { proto_tree_add_item(tree, hf_mysql_message, tvb, offset, lenstr, ENC_ASCII|ENC_NA); offset += lenstr; } - conn_data->state = REQUEST; + mysql_set_conn_state(pinfo, conn_data, REQUEST); break; case RESPONSE_TABULAR: @@ -1711,7 +1725,7 @@ mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo, int offset, ti = proto_tree_add_item(tree, hf_mysql_payload, tvb, offset, -1, ENC_NA); expert_add_info(pinfo, ti, &ei_mysql_unknown_response); offset += tvb_reported_length_remaining(tvb, offset); - conn_data->state = UNDEFINED; + mysql_set_conn_state(pinfo, conn_data, UNDEFINED); } } @@ -1874,7 +1888,7 @@ mysql_dissect_ok_packet(tvbuff_t *tvb, packet_info *pinfo, int offset, } } - conn_data->state = REQUEST; + mysql_set_conn_state(pinfo, conn_data, REQUEST); return offset; } @@ -1939,9 +1953,9 @@ mysql_dissect_result_header(tvbuff_t *tvb, packet_info *pinfo, int offset, } if (num_fields) { - conn_data->state = FIELD_PACKET; + mysql_set_conn_state(pinfo, conn_data, FIELD_PACKET); } else { - conn_data->state = ROW_PACKET; + mysql_set_conn_state(pinfo, conn_data, ROW_PACKET); } return offset; @@ -2024,7 +2038,7 @@ mysql_dissect_row_packet(tvbuff_t *tvb, int offset, proto_tree *tree) static int -mysql_dissect_response_prepare(tvbuff_t *tvb, int offset, proto_tree *tree, mysql_conn_data_t *conn_data) +mysql_dissect_response_prepare(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, mysql_conn_data_t *conn_data) { my_stmt_data_t *stmt_data; guint32 stmt_id; @@ -2052,11 +2066,11 @@ mysql_dissect_response_prepare(tvbuff_t *tvb, int offset, proto_tree *tree, mysq proto_tree_add_item(tree, hf_mysql_num_warn, tvb, offset, 2, ENC_LITTLE_ENDIAN); if (conn_data->stmt_num_params > 0) - conn_data->state = PREPARED_PARAMETERS; + mysql_set_conn_state(pinfo, conn_data, PREPARED_PARAMETERS); else if (conn_data->stmt_num_fields > 0) - conn_data->state = PREPARED_FIELDS; + mysql_set_conn_state(pinfo, conn_data, PREPARED_FIELDS); else - conn_data->state = REQUEST; + mysql_set_conn_state(pinfo, conn_data, REQUEST); return offset + tvb_reported_length_remaining(tvb, offset); } @@ -2068,7 +2082,7 @@ mysql_dissect_auth_switch_request(tvbuff_t *tvb, packet_info *pinfo, int offset, gint lenstr; col_set_str(pinfo->cinfo, COL_INFO, "Auth Switch Request" ); - conn_data->state = AUTH_SWITCH_RESPONSE; + mysql_set_conn_state(pinfo, conn_data, AUTH_SWITCH_RESPONSE); /* Status (Always 0xfe) */ proto_tree_add_item(tree, hf_mysql_auth_switch_request_status, tvb, offset, 1, ENC_LITTLE_ENDIAN); @@ -2237,7 +2251,9 @@ dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat conversation_add_proto_data(conversation, proto_mysql, conn_data); } - mysql_frame_data_p = (struct mysql_frame_data *)p_get_proto_data(wmem_file_scope(), pinfo, proto_mysql, 0); + /* Using tvb_raw_offset(tvb) allows storage of multiple "proto data" in a single frame + * (when there are multiple MySQL pdus in a single frame) */ + mysql_frame_data_p = (struct mysql_frame_data *)p_get_proto_data(wmem_file_scope(), pinfo, proto_mysql, tvb_raw_offset(tvb)); if (!mysql_frame_data_p) { /* We haven't seen this frame before. Store the state of the * conversation now so if/when we dissect the frame again @@ -2245,25 +2261,7 @@ dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat */ mysql_frame_data_p = wmem_new(wmem_file_scope(), struct mysql_frame_data); mysql_frame_data_p->state = conn_data->state; - p_add_proto_data(wmem_file_scope(), pinfo, proto_mysql, 0, mysql_frame_data_p); - - } else if (conn_data->state != FIELD_PACKET && conn_data->state != ROW_PACKET ) { - /* We have seen this frame before. Set the connection state - * to whatever state it had the first time we saw this frame - * (e.g., based on whatever frames came before it). - * The state may change as we dissect this packet. - * XXX: I think the logic of the above else if test is as follows: - * During the first (sequential) dissection pass thru the capture - * file the conversation connection state as of the beginning of each frame - * is saved in the connection_state for that frame. - * Any state changes *within* a mysql "message" (ie: query/response/etc) - * while processing mysql PDUS (aka "packets") in that message must be preserved. - * It appears that FIELD_PACKET & ROW_PACKET are the only two - * state changes which can occur within a mysql message which affect - * subsequent processing within the message. - * Question: Does this logic work OK for a reassembled message ? - */ - conn_data->state= mysql_frame_data_p->state; + p_add_proto_data(wmem_file_scope(), pinfo, proto_mysql, tvb_raw_offset(tvb), mysql_frame_data_p); } if ((conn_data->frame_start_compressed) && (pinfo->num > conn_data->frame_start_compressed)) { @@ -2317,7 +2315,7 @@ dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat offset = mysql_dissect_greeting(tvb, pinfo, offset, mysql_tree, conn_data); } else { col_set_str(pinfo->cinfo, COL_INFO, "Response"); - offset = mysql_dissect_response(tvb, pinfo, offset, mysql_tree, conn_data); + offset = mysql_dissect_response(tvb, pinfo, offset, mysql_tree, conn_data, mysql_frame_data_p->state); } } else { if (mysql_frame_data_p->state == LOGIN && (packet_number == 1 || (packet_number == 2 && is_ssl))) { @@ -2331,7 +2329,7 @@ dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat } } else { col_set_str(pinfo->cinfo, COL_INFO, "Request"); - offset = mysql_dissect_request(tvb, pinfo, offset, mysql_tree, conn_data); + offset = mysql_dissect_request(tvb, pinfo, offset, mysql_tree, conn_data, mysql_frame_data_p->state); } } |