aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-mysql.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/dissectors/packet-mysql.c')
-rw-r--r--epan/dissectors/packet-mysql.c2722
1 files changed, 2204 insertions, 518 deletions
diff --git a/epan/dissectors/packet-mysql.c b/epan/dissectors/packet-mysql.c
index dd251ebfc8..7e5bd6515c 100644
--- a/epan/dissectors/packet-mysql.c
+++ b/epan/dissectors/packet-mysql.c
@@ -1,10 +1,11 @@
/* packet-mysql.c
- * Routines for mysql packet dissection
+ * Routines for MySQL/MariaDB packet dissection
*
* Huagang XIE <huagang@intruvert.com>
*
* MySQL 4.1+ protocol by Axel Schwenke <axel@mysql.com>
* MariaDB protocol by Georg Richter <georg@mariadb.com>
+ * & Diego Dupin <diego.dupin@mariadb.com>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
@@ -17,7 +18,7 @@
*
* the protocol specifications
* For MySQL at
- * https://dev.mysql.com/doc/internals/en/client-server-protocol.html
+ * https://dev.mysql.com/doc/dev/mysql-server/latest/PAGE_PROTOCOL.html
* For MariaDB at
* https://mariadb.com/kb/en/clientserver-protocol/
* and MySQL source code
@@ -33,6 +34,9 @@
#include <epan/expert.h>
#include <epan/strutil.h>
#include <epan/proto_data.h>
+#include <epan/reassemble.h>
+#include <epan/exceptions.h>
+#include <epan/show_exception.h>
#include "packet-tcp.h"
#include "packet-tls-utils.h"
@@ -42,16 +46,18 @@ void proto_reg_handoff_mysql(void);
/* port for protocol registration */
#define TCP_PORT_MySQL 3306
+#define MYSQL_HEADER_LENGTH 4
+
/* MariaDB Server >= 10.0 sends a 5.5.5- prefix for the version, since
replication doesn't support a two digit version number. Version 5.5.5
was never released in MySQL and MariaDB */
#define MARIADB_RPL_VERSION_HACK "5.5.5-"
/* client/server capabilities
- * Source: http://dev.mysql.com/doc/internals/en/capability-flags.html
- * Source: mysql_com.h
+ * Docs: https://dev.mysql.com/doc/dev/mysql-server/latest/group__group__cs__capabilities__flags.html
+ * Source: https://github.com/mysql/mysql-server/blob/8.0/include/mysql_com.h
*/
-#define MYSQL_CAPS_LP 0x0001 /* CLIENT_LONG_PASSWORD */
+#define MYSQL_CAPS_LP 0x0001 /* CLIENT_LONG_PASSWORD/CLIENT_IS_MYSQL */
#define MYSQL_CAPS_FR 0x0002 /* CLIENT_FOUND_ROWS */
#define MYSQL_CAPS_LF 0x0004 /* CLIENT_LONG_FLAG */
#define MYSQL_CAPS_CD 0x0008 /* CLIENT_CONNECT_WITH_DB */
@@ -87,7 +93,6 @@ void proto_reg_handoff_mysql(void);
*
* These are libmysqlclient flags and NOT present
* in the protocol:
- * CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30)
* CLIENT_REMEMBER_OPTIONS (1UL << 31)
*/
#define MYSQL_CAPS_MS 0x0001 /* CLIENT_MULTI_STATMENTS */
@@ -99,7 +104,14 @@ void proto_reg_handoff_mysql(void);
#define MYSQL_CAPS_EP 0x0040 /* CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS */
#define MYSQL_CAPS_ST 0x0080 /* CLIENT_SESSION_TRACK */
#define MYSQL_CAPS_DE 0x0100 /* CLIENT_DEPRECATE_EOF */
-#define MYSQL_CAPS_UNUSED 0xFE00
+#define MYSQL_CAPS_RM 0x0200 /* CLIENT_OPTIONAL_RESULTSET_METADATA */
+#define MYSQL_CAPS_ZS 0x0400 /* CLIENT_ZSTD_COMPRESSION_ALGORITHM */
+#define MYSQL_CAPS_QA 0x0800 /* CLIENT_QUERY_ATTRIBUTES */
+#define MYSQL_CAPS_MF 0x1000 /* MULTI_FACTOR_AUTHENTICATION */
+#define MYSQL_CAPS_CE 0x2000 /* CLIENT_CAPABILITY_EXTENSION */
+#define MYSQL_CAPS_VC 0x4000 /* CLIENT_SSL_VERIFY_SERVER_CERT */
+
+#define MYSQL_CAPS_UNUSED 0x8000 /* Currently only a single bit */
/* status bitfield */
#define MYSQL_STAT_IT 0x0001
@@ -161,6 +173,54 @@ void proto_reg_handoff_mysql(void);
#define MYSQL_DAEMON 29
#define MYSQL_BINLOG_DUMP_GTID 30 /* replication */
#define MYSQL_RESET_CONNECTION 31
+#define MYSQL_CLONE 32
+#define MYSQL_SUBSCRIBE_GROUP_REPLICATION_STREAM 33
+
+/* MySQL Native Cloning Commands */
+#define MYSQL_CLONE_COM_INIT 1
+#define MYSQL_CLONE_COM_ATTACH 2
+#define MYSQL_CLONE_COM_REINIT 3
+#define MYSQL_CLONE_COM_EXECUTE 4
+#define MYSQL_CLONE_COM_ACK 5
+#define MYSQL_CLONE_COM_EXIT 6
+
+/* decoding table: clone command */
+static const value_string mysql_clone_command_vals[] = {
+ {MYSQL_CLONE_COM_INIT, "Init"},
+ {MYSQL_CLONE_COM_ATTACH, "Attach"},
+ {MYSQL_CLONE_COM_REINIT, "Re-init"},
+ {MYSQL_CLONE_COM_EXECUTE, "Execute"},
+ {MYSQL_CLONE_COM_ACK, "Ack"},
+ {MYSQL_CLONE_COM_EXIT, "Exit"},
+ {0, NULL}
+};
+
+/* MySQL Native Cloning Responses */
+#define MYSQL_CLONE_COM_RES_LOCS 1
+#define MYSQL_CLONE_COM_RES_DATA_DESC 2
+#define MYSQL_CLONE_COM_RES_DATA 3
+#define MYSQL_CLONE_COM_RES_PLUGIN 4
+#define MYSQL_CLONE_COM_RES_CONFIG 5
+#define MYSQL_CLONE_COM_RES_COLLATION 6
+#define MYSQL_CLONE_COM_RES_PLUGIN_V2 7
+#define MYSQL_CLONE_COM_RES_CONFIG_V3 8
+#define MYSQL_CLONE_COM_RES_COMPLETE 99
+#define MYSQL_CLONE_COM_RES_ERROR 100
+
+/* decoding table: clone command */
+static const value_string mysql_clone_response_vals[] = {
+ {MYSQL_CLONE_COM_RES_LOCS, "Remote Resource Locator"},
+ {MYSQL_CLONE_COM_RES_DATA_DESC, "Remote Data Descriptor"},
+ {MYSQL_CLONE_COM_RES_DATA, "Remote Data"},
+ {MYSQL_CLONE_COM_RES_PLUGIN, "Plugin V1"},
+ {MYSQL_CLONE_COM_RES_CONFIG, "Config"},
+ {MYSQL_CLONE_COM_RES_COLLATION, "Collation"},
+ {MYSQL_CLONE_COM_RES_PLUGIN_V2, "Plugin V2"},
+ {MYSQL_CLONE_COM_RES_CONFIG_V3, "Plugin V3"},
+ {MYSQL_CLONE_COM_RES_COMPLETE, "Complete"},
+ {MYSQL_CLONE_COM_RES_ERROR, "Error"},
+ {0, NULL}
+};
/* MariaDB specific commands */
#define MARIADB_STMT_BULK_EXECUTE 250
@@ -170,10 +230,11 @@ void proto_reg_handoff_mysql(void);
#define MARIADB_BULK_SEND_TYPES 128
/* MariaDB extended capabilities */
-#define MARIADB_CAPS_PR 0x0001 /* MARIADB_CLIENT_PROGRESS */
-#define MARIADB_CAPS_CM 0x0002 /* MARIADB_CLIENT_COM_MULTI */
-#define MARIADB_CAPS_BO 0x0004 /* MARIADB_CLIENT_STMT_BULK_OPERATIONS */
-#define MARIADB_CAPS_EM 0x0008 /* MARIADB_CLIENT_EXTENDED_METADATA */
+#define MARIADB_CAPS_PR 0x00000001 /* MARIADB_CLIENT_PROGRESS */
+#define MARIADB_CAPS_CM 0x00000002 /* MARIADB_CLIENT_COM_MULTI */
+#define MARIADB_CAPS_BO 0x00000004 /* MARIADB_CLIENT_STMT_BULK_OPERATIONS */
+#define MARIADB_CAPS_EM 0x00000008 /* MARIADB_CLIENT_EXTENDED_METADATA */
+#define MARIADB_CAPS_ME 0x00000010 /* MARIADB_CLIENT_CACHE_METADATA */
/* MariaDB bulk indicators */
#define MARIADB_INDICATOR_NONE 0
@@ -199,10 +260,18 @@ void proto_reg_handoff_mysql(void);
#define MYSQL_COMPRESS_INIT 1
#define MYSQL_COMPRESS_ACTIVE 2
+#define MYSQL_COMPRESS_ALG_ZLIB 0
+#define MYSQL_COMPRESS_ALG_ZSTD 1
+
/* Generic Response Codes */
-#define MYSQL_RESPONSE_OK 0x00
-#define MYSQL_RESPONSE_ERR 0xFF
-#define MYSQL_RESPONSE_EOF 0xFE
+#define MYSQL_RESPONSE_OK 0x00
+#define MYSQL_RESPONSE_ERR 0xFF
+#define MYSQL_RESPONSE_EOF 0xFE
+#define MYSQL_RESPONSE_INFILE 0xFB
+
+/* mariadb extended keys */
+#define MARIADB_EXT_META_TYPE 0
+#define MARIADB_EXT_META_FORMAT 1
/* decoding table: command */
static const value_string mysql_command_vals[] = {
@@ -238,6 +307,8 @@ static const value_string mysql_command_vals[] = {
{MYSQL_DAEMON, "Daemon"},
{MYSQL_BINLOG_DUMP_GTID, "Send Binlog GTID"},
{MYSQL_RESET_CONNECTION, "Reset Connection"},
+ {MYSQL_CLONE, "Native cloning"},
+ {MYSQL_SUBSCRIBE_GROUP_REPLICATION_STREAM, "Subscribe Group Replication Stream"},
{MARIADB_STMT_BULK_EXECUTE, "Execute Bulk Statement"},
{0, NULL}
};
@@ -872,245 +943,350 @@ static const value_string mysql_session_track_type_vals[] = {
};
static const value_string mysql_response_code_vals[] = {
- { MYSQL_RESPONSE_OK, "OK Packet" },
- { MYSQL_RESPONSE_ERR, "ERR Packet" },
- { MYSQL_RESPONSE_EOF, "EOF Packet" },
+ { MYSQL_RESPONSE_OK, "OK Packet" },
+ { MYSQL_RESPONSE_ERR, "ERR Packet" },
+ { MYSQL_RESPONSE_EOF, "EOF Packet" },
+ { MYSQL_RESPONSE_INFILE, "LOCAL INFILE Packet" },
{ 0, NULL }
};
/* protocol id */
-static int proto_mysql = -1;
+static int proto_mysql;
/* dissector configuration */
static gboolean mysql_desegment = TRUE;
static gboolean mysql_showquery = FALSE;
/* expand-the-tree flags */
-static gint ett_mysql = -1;
-static gint ett_server_greeting = -1;
-static gint ett_login_request = -1;
-static gint ett_caps = -1;
-static gint ett_extcaps = -1;
-static gint ett_stat = -1;
-static gint ett_request = -1;
-static gint ett_refresh = -1;
-static gint ett_field_flags = -1;
-static gint ett_exec_param = -1;
-static gint ett_bulk_param = -1;
-static gint ett_session_track = -1;
-static gint ett_session_track_data = -1;
-static gint ett_connattrs = -1;
-static gint ett_connattrs_attr = -1;
-static gint ett_mysql_field = -1;
+static gint ett_mysql;
+static gint ett_server_greeting;
+static gint ett_login_request;
+static gint ett_caps;
+static gint ett_extcaps;
+static gint ett_stat;
+static gint ett_row_value;
+static gint ett_request;
+static gint ett_query_attributes;
+static gint ett_refresh;
+static gint ett_field_flags;
+static gint ett_exec_param;
+static gint ett_bulk_param;
+static gint ett_session_track;
+static gint ett_session_track_data;
+static gint ett_extmeta;
+static gint ett_extmeta_data;
+static gint ett_connattrs;
+static gint ett_connattrs_attr;
+static gint ett_mysql_field;
+static gint ett_binlog_event;
+static gint ett_binlog_event_hb_v2;
/* protocol fields */
-static int hf_mysql_caps_server = -1;
-static int hf_mysql_caps_client = -1;
-static int hf_mysql_cap_long_password = -1;
-static int hf_mysql_cap_found_rows = -1;
-static int hf_mysql_cap_long_flag = -1;
-static int hf_mysql_cap_connect_with_db = -1;
-static int hf_mysql_cap_no_schema = -1;
-static int hf_mysql_cap_compress = -1;
-static int hf_mysql_cap_odbc = -1;
-static int hf_mysql_cap_local_files = -1;
-static int hf_mysql_cap_ignore_space = -1;
-static int hf_mysql_cap_change_user = -1;
-static int hf_mysql_cap_interactive = -1;
-static int hf_mysql_cap_ssl = -1;
-static int hf_mysql_cap_ignore_sigpipe = -1;
-static int hf_mysql_cap_transactions = -1;
-static int hf_mysql_cap_reserved = -1;
-static int hf_mysql_cap_secure_connect = -1;
-static int hf_mysql_extcaps_server = -1;
-static int hf_mysql_extcaps_client = -1;
-static int hf_mysql_cap_multi_statements = -1;
-static int hf_mysql_cap_multi_results = -1;
-static int hf_mysql_cap_ps_multi_results = -1;
-static int hf_mysql_cap_plugin_auth = -1;
-static int hf_mysql_cap_connect_attrs = -1;
-static int hf_mysql_cap_plugin_auth_lenenc_client_data = -1;
-static int hf_mysql_cap_client_can_handle_expired_passwords = -1;
-static int hf_mysql_cap_session_track = -1;
-static int hf_mysql_cap_deprecate_eof = -1;
-static int hf_mysql_cap_unused = -1;
-static int hf_mysql_server_language = -1;
-static int hf_mysql_server_status = -1;
-static int hf_mysql_stat_it = -1;
-static int hf_mysql_stat_ac = -1;
-static int hf_mysql_stat_mr = -1;
-static int hf_mysql_stat_mu = -1;
-static int hf_mysql_stat_bi = -1;
-static int hf_mysql_stat_ni = -1;
-static int hf_mysql_stat_cr = -1;
-static int hf_mysql_stat_lr = -1;
-static int hf_mysql_stat_dr = -1;
-static int hf_mysql_stat_bs = -1;
-static int hf_mysql_stat_mc = -1;
-static int hf_mysql_stat_session_state_changed = -1;
-static int hf_mysql_stat_query_was_slow = -1;
-static int hf_mysql_stat_ps_out_params = -1;
-static int hf_mysql_stat_trans_readonly = -1;
-static int hf_mysql_refresh = -1;
-static int hf_mysql_rfsh_grants = -1;
-static int hf_mysql_rfsh_log = -1;
-static int hf_mysql_rfsh_tables = -1;
-static int hf_mysql_rfsh_hosts = -1;
-static int hf_mysql_rfsh_status = -1;
-static int hf_mysql_rfsh_threads = -1;
-static int hf_mysql_rfsh_slave = -1;
-static int hf_mysql_rfsh_master = -1;
-static int hf_mysql_packet_length = -1;
-static int hf_mysql_packet_number = -1;
-static int hf_mysql_request = -1;
-static int hf_mysql_command = -1;
-static int hf_mysql_response_code = -1;
-static int hf_mysql_error_code = -1;
-static int hf_mysql_error_string = -1;
-static int hf_mysql_sqlstate = -1;
-static int hf_mysql_message = -1;
-static int hf_mysql_payload = -1;
-static int hf_mysql_server_greeting = -1;
-static int hf_mysql_session_track = -1;
-static int hf_mysql_session_track_type = -1;
-static int hf_mysql_session_track_length = -1;
-static int hf_mysql_session_track_data = -1;
-static int hf_mysql_session_track_data_length = -1;
-static int hf_mysql_session_track_sysvar_length = -1;
-static int hf_mysql_session_track_sysvar_name = -1;
-static int hf_mysql_session_track_sysvar_value = -1;
-static int hf_mysql_session_track_schema = -1;
-static int hf_mysql_session_track_schema_length = -1;
-static int hf_mysql_session_state_change = -1;
-static int hf_mysql_session_track_gtids = -1;
-static int hf_mysql_session_track_gtids_encoding = -1;
-static int hf_mysql_session_track_gtids_length = -1;
-static int hf_mysql_session_track_transaction_characteristics = -1;
-static int hf_mysql_session_track_transaction_characteristics_length = -1;
-static int hf_mysql_session_track_transaction_state = -1;
-static int hf_mysql_session_track_transaction_state_length = -1;
-static int hf_mysql_protocol = -1;
-static int hf_mysql_version = -1;
-static int hf_mysql_login_request = -1;
-static int hf_mysql_max_packet = -1;
-static int hf_mysql_user = -1;
-static int hf_mysql_table_name = -1;
-static int hf_mysql_schema = -1;
-static int hf_mysql_client_auth_plugin = -1;
-static int hf_mysql_connattrs = -1;
-static int hf_mysql_connattrs_length = -1;
-static int hf_mysql_connattrs_attr = -1;
-static int hf_mysql_connattrs_name_length = -1;
-static int hf_mysql_connattrs_name = -1;
-static int hf_mysql_connattrs_value_length = -1;
-static int hf_mysql_connattrs_value = -1;
-static int hf_mysql_thread_id = -1;
-static int hf_mysql_salt = -1;
-static int hf_mysql_salt2 = -1;
-static int hf_mysql_auth_plugin_length = -1;
-static int hf_mysql_auth_plugin = -1;
-static int hf_mysql_charset = -1;
-static int hf_mysql_passwd = -1;
-static int hf_mysql_unused = -1;
-static int hf_mysql_affected_rows = -1;
-static int hf_mysql_insert_id = -1;
-static int hf_mysql_num_warn = -1;
-static int hf_mysql_thd_id = -1;
-static int hf_mysql_stmt_id = -1;
-static int hf_mysql_query = -1;
-static int hf_mysql_shutdown = -1;
-static int hf_mysql_option = -1;
-static int hf_mysql_num_rows = -1;
-static int hf_mysql_param = -1;
-static int hf_mysql_num_params = -1;
-static int hf_mysql_exec_flags4 = -1;
-static int hf_mysql_exec_flags5 = -1;
-static int hf_mysql_exec_iter = -1;
-static int hf_mysql_binlog_position = -1;
-static int hf_mysql_binlog_flags = -1;
-static int hf_mysql_binlog_server_id = -1;
-static int hf_mysql_binlog_file_name = -1;
-static int hf_mysql_eof = -1;
-static int hf_mysql_num_fields = -1;
-static int hf_mysql_extra = -1;
-static int hf_mysql_fld_catalog = -1;
-static int hf_mysql_fld_db = -1;
-static int hf_mysql_fld_table = -1;
-static int hf_mysql_fld_org_table = -1;
-static int hf_mysql_fld_name = -1;
-static int hf_mysql_fld_org_name = -1;
-static int hf_mysql_fld_charsetnr = -1;
-static int hf_mysql_fld_length = -1;
-static int hf_mysql_fld_type = -1;
-static int hf_mysql_fld_flags = -1;
-static int hf_mysql_fld_not_null = -1;
-static int hf_mysql_fld_primary_key = -1;
-static int hf_mysql_fld_unique_key = -1;
-static int hf_mysql_fld_multiple_key = -1;
-static int hf_mysql_fld_blob = -1;
-static int hf_mysql_fld_unsigned = -1;
-static int hf_mysql_fld_zero_fill = -1;
-static int hf_mysql_fld_binary = -1;
-static int hf_mysql_fld_enum = -1;
-static int hf_mysql_fld_auto_increment = -1;
-static int hf_mysql_fld_timestamp = -1;
-static int hf_mysql_fld_set = -1;
-static int hf_mysql_fld_decimals = -1;
-static int hf_mysql_fld_default = -1;
-static int hf_mysql_row_text = -1;
-static int hf_mysql_new_parameter_bound_flag = -1;
-static int hf_mysql_exec_param = -1;
-static int hf_mysql_exec_unsigned = -1;
-static int hf_mysql_exec_field_longlong = -1;
-static int hf_mysql_exec_field_string = -1;
-static int hf_mysql_exec_field_double = -1;
-static int hf_mysql_exec_field_datetime_length = -1;
-static int hf_mysql_exec_field_year = -1;
-static int hf_mysql_exec_field_month = -1;
-static int hf_mysql_exec_field_day = -1;
-static int hf_mysql_exec_field_hour = -1;
-static int hf_mysql_exec_field_minute = -1;
-static int hf_mysql_exec_field_second = -1;
-static int hf_mysql_exec_field_second_b = -1;
-static int hf_mysql_exec_field_long = -1;
-static int hf_mysql_exec_field_tiny = -1;
-static int hf_mysql_exec_field_short = -1;
-static int hf_mysql_exec_field_float = -1;
-static int hf_mysql_exec_field_time_length = -1;
-static int hf_mysql_exec_field_time_sign = -1;
-static int hf_mysql_exec_field_time_days = -1;
-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 int hf_mysql_prefix = -1;
-static int hf_mysql_length = -1;
-//static int hf_mariadb_fld_charsetnr = -1;
-static int hf_mariadb_server_language = -1;
-static int hf_mariadb_charset = -1;
-static int hf_mariadb_cap_progress = -1;
-static int hf_mariadb_cap_commulti = -1;
-static int hf_mariadb_cap_bulk = -1;
-static int hf_mariadb_cap_extmetadata = -1;
-static int hf_mariadb_extcaps_server = -1;
-static int hf_mariadb_extcaps_client = -1;
-static int hf_mariadb_bulk_flag_autoid = -1;
-static int hf_mariadb_bulk_flag_sendtypes = -1;
-static int hf_mariadb_bulk_caps_flags = -1;
-static int hf_mariadb_bulk_paramtypes = -1;
-static int hf_mariadb_bulk_indicator = -1;
+static int hf_mysql_caps_server;
+static int hf_mysql_caps_client;
+static int hf_mysql_cap_long_password;
+static int hf_mysql_cap_found_rows;
+static int hf_mysql_cap_long_flag;
+static int hf_mysql_cap_connect_with_db;
+static int hf_mysql_cap_no_schema;
+static int hf_mysql_cap_compress;
+static int hf_mysql_cap_odbc;
+static int hf_mysql_cap_local_files;
+static int hf_mysql_cap_ignore_space;
+static int hf_mysql_cap_change_user;
+static int hf_mysql_cap_interactive;
+static int hf_mysql_cap_ssl;
+static int hf_mysql_cap_ignore_sigpipe;
+static int hf_mysql_cap_transactions;
+static int hf_mysql_cap_reserved;
+static int hf_mysql_cap_secure_connect;
+static int hf_mysql_extcaps_server;
+static int hf_mysql_extcaps_client;
+static int hf_mysql_cap_multi_statements;
+static int hf_mysql_cap_multi_results;
+static int hf_mysql_cap_ps_multi_results;
+static int hf_mysql_cap_plugin_auth;
+static int hf_mysql_cap_connect_attrs;
+static int hf_mysql_cap_plugin_auth_lenenc_client_data;
+static int hf_mysql_cap_client_can_handle_expired_passwords;
+static int hf_mysql_cap_session_track;
+static int hf_mysql_cap_deprecate_eof;
+static int hf_mysql_cap_optional_metadata;
+static int hf_mysql_cap_compress_zstd;
+static int hf_mysql_cap_query_attrs;
+static int hf_mysql_cap_mf_auth;
+static int hf_mysql_cap_cap_ext;
+static int hf_mysql_cap_ssl_verify_server_cert;
+static int hf_mysql_cap_unused;
+static int hf_mysql_server_language;
+static int hf_mysql_server_status;
+static int hf_mysql_stat_it;
+static int hf_mysql_stat_ac;
+static int hf_mysql_stat_mr;
+static int hf_mysql_stat_mu;
+static int hf_mysql_stat_bi;
+static int hf_mysql_stat_ni;
+static int hf_mysql_stat_cr;
+static int hf_mysql_stat_lr;
+static int hf_mysql_stat_dr;
+static int hf_mysql_stat_bs;
+static int hf_mysql_stat_mc;
+static int hf_mysql_stat_session_state_changed;
+static int hf_mysql_stat_query_was_slow;
+static int hf_mysql_stat_ps_out_params;
+static int hf_mysql_stat_trans_readonly;
+static int hf_mysql_refresh;
+static int hf_mysql_rfsh_grants;
+static int hf_mysql_rfsh_log;
+static int hf_mysql_rfsh_tables;
+static int hf_mysql_rfsh_hosts;
+static int hf_mysql_rfsh_status;
+static int hf_mysql_rfsh_threads;
+static int hf_mysql_rfsh_slave;
+static int hf_mysql_rfsh_master;
+static int hf_mysql_packet_length;
+static int hf_mysql_packet_number;
+static int hf_mysql_request;
+static int hf_mysql_command;
+static int hf_mysql_response_code;
+static int hf_mysql_error_code;
+static int hf_mysql_error_string;
+static int hf_mysql_sqlstate;
+static int hf_mysql_message;
+static int hf_mysql_payload;
+static int hf_mysql_server_greeting;
+static int hf_mysql_session_track;
+static int hf_mysql_session_track_type;
+static int hf_mysql_session_track_length;
+static int hf_mysql_session_track_data;
+static int hf_mysql_session_track_data_length;
+static int hf_mysql_session_track_sysvar_length;
+static int hf_mysql_session_track_sysvar_name;
+static int hf_mysql_session_track_sysvar_value;
+static int hf_mysql_session_track_schema;
+static int hf_mysql_session_track_schema_length;
+static int hf_mysql_session_state_change;
+static int hf_mysql_session_track_gtids;
+static int hf_mysql_session_track_gtids_encoding;
+static int hf_mysql_session_track_gtids_length;
+static int hf_mysql_session_track_transaction_characteristics;
+static int hf_mysql_session_track_transaction_characteristics_length;
+static int hf_mysql_session_track_transaction_state;
+static int hf_mysql_session_track_transaction_state_length;
+static int hf_mysql_protocol;
+static int hf_mysql_version;
+static int hf_mysql_login_request;
+static int hf_mysql_max_packet;
+static int hf_mysql_user;
+static int hf_mysql_table_name;
+static int hf_mysql_schema;
+static int hf_mysql_client_auth_plugin;
+static int hf_mysql_connattrs;
+static int hf_mysql_connattrs_length;
+static int hf_mysql_connattrs_attr;
+static int hf_mysql_connattrs_name_length;
+static int hf_mysql_connattrs_name;
+static int hf_mysql_connattrs_value_length;
+static int hf_mysql_connattrs_value;
+static int hf_mysql_zstd_compression_level;
+static int hf_mysql_thread_id;
+static int hf_mysql_salt;
+static int hf_mysql_salt2;
+static int hf_mysql_auth_plugin_length;
+static int hf_mysql_auth_plugin;
+static int hf_mysql_charset;
+static int hf_mysql_passwd;
+static int hf_mysql_unused;
+static int hf_mysql_affected_rows;
+static int hf_mysql_insert_id;
+static int hf_mysql_num_warn;
+static int hf_mysql_stmt_id;
+static int hf_mysql_query_attributes;
+static int hf_mysql_query_attributes_count;
+static int hf_mysql_query_attributes_send_types_to_server;
+static int hf_mysql_query_attribute_name_type;
+static int hf_mysql_query_attribute_name;
+static int hf_mysql_query_attribute_value;
+static int hf_mysql_query;
+static int hf_mysql_shutdown;
+static int hf_mysql_option;
+static int hf_mysql_num_rows;
+static int hf_mysql_param;
+static int hf_mysql_num_params;
+static int hf_mysql_exec_flags4;
+static int hf_mysql_exec_flags5;
+static int hf_mysql_exec_iter;
+static int hf_mysql_binlog_position;
+static int hf_mysql_binlog_position8;
+static int hf_mysql_binlog_flags;
+static int hf_mysql_binlog_server_id;
+static int hf_mysql_binlog_file_name;
+static int hf_mysql_binlog_file_name_length;
+static int hf_mysql_binlog_slave_hostname_length;
+static int hf_mysql_binlog_slave_hostname;
+static int hf_mysql_binlog_slave_user_length;
+static int hf_mysql_binlog_slave_user;
+static int hf_mysql_binlog_slave_password_length;
+static int hf_mysql_binlog_slave_password;
+static int hf_mysql_binlog_slave_mysql_port;
+static int hf_mysql_binlog_replication_rank;
+static int hf_mysql_binlog_master_id;
+static int hf_mysql_binlog_event_header_timestamp;
+static int hf_mysql_binlog_event_header_event_type;
+static int hf_mysql_binlog_event_header_server_id;
+static int hf_mysql_binlog_event_header_event_size;
+static int hf_mysql_binlog_event_header_log_position;
+static int hf_mysql_binlog_event_header_flags;
+static int hf_mysql_binlog_event_checksum;
+static int hf_mysql_binlog_event_heartbeat_v2;
+static int hf_mysql_binlog_event_heartbeat_v2_otw;
+static int hf_mysql_binlog_event_heartbeat_v2_otw_type;
+static int hf_mysql_binlog_gtid_data;
+static int hf_mysql_binlog_gtid_data_length;
+static int hf_mysql_binlog_hb_event_filename;
+static int hf_mysql_binlog_hb_event_log_position;
+static int hf_mysql_clone_command_code;
+static int hf_mysql_clone_response_code;
+static int hf_mysql_eof;
+static int hf_mysql_num_fields;
+static int hf_mysql_extra;
+static int hf_mysql_fld_catalog;
+static int hf_mysql_fld_db;
+static int hf_mysql_fld_table;
+static int hf_mysql_fld_org_table;
+static int hf_mysql_fld_name;
+static int hf_mysql_fld_org_name;
+static int hf_mysql_fld_charsetnr;
+static int hf_mysql_fld_length;
+static int hf_mysql_fld_type;
+static int hf_mysql_fld_flags;
+static int hf_mysql_fld_not_null;
+static int hf_mysql_fld_primary_key;
+static int hf_mysql_fld_unique_key;
+static int hf_mysql_fld_multiple_key;
+static int hf_mysql_fld_blob;
+static int hf_mysql_fld_unsigned;
+static int hf_mysql_fld_zero_fill;
+static int hf_mysql_exec_field_null;
+static int hf_mysql_null_buffer;
+static int hf_mysql_fld_enum;
+static int hf_mysql_fld_auto_increment;
+static int hf_mysql_fld_timestamp;
+static int hf_mysql_fld_set;
+static int hf_mysql_fld_decimals;
+static int hf_mysql_fld_default;
+static int hf_mysql_row_text;
+static int hf_mysql_new_parameter_bound_flag;
+static int hf_mysql_exec_param;
+static int hf_mysql_exec_unsigned;
+static int hf_mysql_exec_field_longlong;
+static int hf_mysql_exec_field_unsigned_longlong;
+static int hf_mysql_exec_field_string;
+static int hf_mysql_exec_field_double;
+static int hf_mysql_exec_field_datetime_length;
+static int hf_mysql_exec_field_year;
+static int hf_mysql_exec_field_month;
+static int hf_mysql_exec_field_day;
+static int hf_mysql_exec_field_hour;
+static int hf_mysql_exec_field_minute;
+static int hf_mysql_exec_field_second;
+static int hf_mysql_exec_field_second_b;
+static int hf_mysql_exec_field_long;
+static int hf_mysql_exec_field_unsigned_long;
+static int hf_mysql_exec_field_tiny;
+static int hf_mysql_exec_field_unsigned_tiny;
+static int hf_mysql_exec_field_short;
+static int hf_mysql_exec_field_unsigned_short;
+static int hf_mysql_exec_field_float;
+static int hf_mysql_exec_field_time_length;
+static int hf_mysql_exec_field_time_sign;
+static int hf_mysql_exec_field_time_days;
+static int hf_mysql_auth_switch_request_status;
+static int hf_mysql_auth_switch_request_name;
+static int hf_mysql_auth_switch_request_data;
+static int hf_mysql_auth_switch_response_data;
+static int hf_mysql_sha2_auth;
+static int hf_mysql_sha2_response;
+static int hf_mysql_pubkey;
+static int hf_mysql_compressed_packet_length;
+static int hf_mysql_compressed_packet_length_uncompressed;
+static int hf_mysql_compressed_packet_number;
+static int hf_mysql_loaddata_filename;
+static int hf_mysql_loaddata_payload;
+
+//static int hf_mariadb_fld_charsetnr;
+static int hf_mariadb_server_language;
+static int hf_mariadb_charset;
+static int hf_mariadb_cap_progress;
+static int hf_mariadb_cap_commulti;
+static int hf_mariadb_cap_bulk;
+static int hf_mariadb_cap_extmetadata;
+static int hf_mariadb_cap_cache_metadata;
+static int hf_mariadb_extcaps_server;
+static int hf_mariadb_extcaps_client;
+static int hf_mariadb_bulk_flag_autoid;
+static int hf_mariadb_bulk_flag_sendtypes;
+static int hf_mariadb_bulk_caps_flags;
+static int hf_mariadb_bulk_paramtypes;
+static int hf_mariadb_bulk_indicator;
+static int hf_mariadb_bulk_row_nr;
+static int hf_mariadb_send_meta;
+static int hf_mariadb_extmeta;
+static int hf_mariadb_extmeta_data;
+static int hf_mariadb_extmeta_length;
+static int hf_mariadb_extmeta_key;
+static int hf_mariadb_extmeta_type;
+static int hf_mariadb_extmeta_format;
+
static dissector_handle_t mysql_handle;
+static dissector_handle_t decompressed_handle;
static dissector_handle_t tls_handle;
-static expert_field ei_mysql_eof = EI_INIT;
-static expert_field ei_mysql_dissector_incomplete = EI_INIT;
-static expert_field ei_mysql_streamed_param = EI_INIT;
-static expert_field ei_mysql_prepare_response_needed = EI_INIT;
-static expert_field ei_mysql_unknown_response = EI_INIT;
-static expert_field ei_mysql_command = EI_INIT;
+static expert_field ei_mysql_dissector_incomplete;
+static expert_field ei_mysql_streamed_param;
+static expert_field ei_mysql_prepare_response_needed;
+static expert_field ei_mysql_unknown_response;
+static expert_field ei_mysql_command;
+static expert_field ei_mysql_invalid_length;
+static expert_field ei_mysql_compression;
+
+/* Reassembly of decompressed packets in compressed packets {{{ */
+
+static int hf_mysql_fragments;
+static int hf_mysql_fragment;
+static int hf_mysql_fragment_overlap;
+static int hf_mysql_fragment_overlap_conflicts;
+static int hf_mysql_fragment_multiple_tails;
+static int hf_mysql_fragment_too_long_fragment;
+static int hf_mysql_fragment_error;
+static int hf_mysql_fragment_count;
+static int hf_mysql_reassembled_in;
+static int hf_mysql_reassembled_length;
+static int hf_mysql_fragment_data;
+
+static gint ett_mysql_fragment;
+static gint ett_mysql_fragments;
+
+static const fragment_items mysql_frag_items = {
+ &ett_mysql_fragment,
+ &ett_mysql_fragments,
+ &hf_mysql_fragments,
+ &hf_mysql_fragment,
+ &hf_mysql_fragment_overlap,
+ &hf_mysql_fragment_overlap_conflicts,
+ &hf_mysql_fragment_multiple_tails,
+ &hf_mysql_fragment_too_long_fragment,
+ &hf_mysql_fragment_error,
+ &hf_mysql_fragment_count,
+ &hf_mysql_reassembled_in,
+ &hf_mysql_reassembled_length,
+ NULL,
+ "MySQL fragments"
+};
+
+static reassembly_table mysql_reassembly_table;
+
+/* }}} Reassembly of decompressed packets */
/* type constants */
static const value_string type_constants[] = {
@@ -1149,46 +1325,85 @@ typedef enum mysql_state {
LOGIN,
REQUEST,
RESPONSE_OK,
+ RESPONSE_ERROR,
+ RESPONSE_EOF,
+ INTERMEDIATE_EOF,
RESPONSE_MESSAGE,
RESPONSE_TABULAR,
RESPONSE_SHOW_FIELDS,
FIELD_PACKET,
ROW_PACKET,
+ COLUMN_COUNT,
RESPONSE_PREPARE,
PREPARED_PARAMETERS,
PREPARED_FIELDS,
AUTH_SWITCH_REQUEST,
- AUTH_SWITCH_RESPONSE
+ AUTH_SWITCH_RESPONSE,
+ AUTH_SHA2,
+ AUTH_PUBKEY,
+ AUTH_SHA2_RESPONSE,
+ BINLOG_DUMP,
+ CLONE_INIT,
+ CLONE_ACTIVE,
+ CLONE_EXIT,
+ RESPONSE_LOCALINFILE,
+ INFILE_DATA
} mysql_state_t;
-#ifdef CTDEBUG
static const value_string state_vals[] = {
{UNDEFINED, "undefined"},
{LOGIN, "login"},
{REQUEST, "request"},
{RESPONSE_OK, "response OK"},
+ {RESPONSE_ERROR, "response ERROR"},
+ {RESPONSE_EOF, "response EOF"},
+ {INTERMEDIATE_EOF, "intermediate EOF"},
{RESPONSE_MESSAGE, "response message"},
{RESPONSE_TABULAR, "tabular response"},
{RESPONSE_SHOW_FIELDS, "response to SHOW FIELDS"},
{FIELD_PACKET, "field packet"},
{ROW_PACKET, "row packet"},
+ {COLUMN_COUNT, "column count"},
{RESPONSE_PREPARE, "response to PREPARE"},
{PREPARED_PARAMETERS, "parameters in response to PREPARE"},
{PREPARED_FIELDS, "fields in response to PREPARE"},
{AUTH_SWITCH_REQUEST, "authentication switch request"},
{AUTH_SWITCH_RESPONSE, "authentication switch response"},
+ {AUTH_SHA2, "caching_sha2_password"},
+ {AUTH_PUBKEY, "public key request"},
+ {AUTH_SHA2_RESPONSE, "caching_sha2_password response"},
+ {BINLOG_DUMP, "binlog event"},
+ {CLONE_INIT, "cloning initializing"},
+ {CLONE_ACTIVE, "cloning active"},
+ {CLONE_EXIT, "cloning shutting down"},
+ {RESPONSE_LOCALINFILE, "local infile"},
+ {INFILE_DATA, "local infile data"},
{0, NULL}
};
-#endif
+typedef enum mysql_resultset_fmt {
+ TEXT,
+ BINARY
+} mysql_resultset_fmt_t;
+
+#define MAX_MY_METADATA_COUNT G_MAXINT16 // Arbitrary; is 32k enough?
+typedef struct {
+ guint16 count;
+ guint16* flags;
+ guint8* types;
+} my_metadata_list_t;
+
+/* Data for the entire conversation. Most data is fixed once known.
+ * For data which changes from packet to packet such as the state,
+ * this holds the value of the last value seen during the first
+ * sequential pass. On subsequent passes, for random packet access,
+ * the per-packet frame data below should be used to access the state.
+ */
typedef struct mysql_conn_data {
guint16 srv_caps;
guint16 srv_caps_ext;
guint16 clnt_caps;
guint16 clnt_caps_ext;
- mysql_state_t state;
- guint16 stmt_num_params;
- guint16 stmt_num_fields;
wmem_tree_t* stmts;
#ifdef CTDEBUG
guint32 generation;
@@ -1197,21 +1412,38 @@ typedef struct mysql_conn_data {
guint32 frame_start_ssl;
guint32 frame_start_compressed;
guint8 compressed_state;
+ guint8 compressed_alg;
gboolean is_mariadb_server; /* set to 1, if connected to a MariaDB server */
gboolean is_mariadb_client; /* set to 1, if connected from a MariaDB client */
guint32 mariadb_server_ext_caps;
guint32 mariadb_client_ext_caps;
+ guint8 *auth_method;
+ streaming_reassembly_info_t *reassembly_info;
+
+ /* The members below refer to the latest state or prepared statement,
+ * and is only valid during the first pass. For random access on
+ * later passes, use the data stored in the mysql_frame_data. */
+ mysql_state_t state;
+ mysql_resultset_fmt_t resultset_fmt;
+ guint32 stmt_id;
guint64 remaining_field_packet_count;
+ my_metadata_list_t field_metas;
} mysql_conn_data_t;
-struct mysql_frame_data {
+/* Data stored for a particular PDU. Use this on random access after
+ * the first pass to obtain the state at the start of a PDU.
+ */
+typedef struct mysql_frame_data {
mysql_state_t state;
-};
+ mysql_resultset_fmt_t resultset_fmt;
+ guint32 stmt_id; /* The last prepared stmt ID before this PDU */
+ guint64 remaining_field_packet_count;
+ my_metadata_list_t field_metas;
+} mysql_frame_data_t;
typedef struct my_stmt_data {
- guint16 nparam;
- guint8* param_flags;
- guint8* param_types;
+ my_metadata_list_t param_metas;
+ my_metadata_list_t field_metas;
guint16 bulk_flags;
} my_stmt_data_t;
@@ -1227,23 +1459,35 @@ static int mysql_dissect_ok_packet(tvbuff_t *tvb, packet_info *pinfo, int offset
static int mysql_dissect_server_status(tvbuff_t *tvb, int offset, proto_tree *tree, guint16 *server_status);
static int mysql_dissect_caps(tvbuff_t *tvb, int offset, proto_tree *tree, int mysql_caps, guint16 *caps);
static int mysql_dissect_extcaps(tvbuff_t *tvb, int offset, proto_tree *tree, int mysql_extcaps, guint16 *caps);
-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_result_header(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, mysql_conn_data_t *conn_data, const mysql_frame_data_t *my_frame_data);
+static int mysql_dissect_field_packet(tvbuff_t *tvb, proto_item *pi, int offset, proto_tree *tree, packet_info *pinfo, mysql_conn_data_t *conn_data, const mysql_frame_data_t *my_frame_data);
+static int mysql_dissect_text_row_packet(tvbuff_t *tvb, int offset, proto_tree *tree);
+static int mysql_dissect_binary_row_packet(tvbuff_t *tvb, packet_info *pinfo, proto_item *pi, int offset, proto_tree *tree, mysql_conn_data_t *conn_data, const mysql_frame_data_t *my_frame_data);
+static int mysql_dissect_binlog_event_packet(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, proto_item *pi);
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_eof(tvbuff_t *tvb, packet_info *pinfo, proto_item *pi, 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 int mysql_dissect_auth_sha2(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, mysql_conn_data_t *conn_data);
+static int mysql_dissect_loaddata(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);
static void mysql_dissect_exec_datetime(tvbuff_t *tvb, int *param_offset, proto_item *field_tree);
static void mysql_dissect_exec_tiny(tvbuff_t *tvb, int *param_offset, proto_item *field_tree);
+static void mysql_dissect_exec_unsigned_tiny(tvbuff_t *tvb, int *param_offset, proto_item *field_tree);
static void mysql_dissect_exec_short(tvbuff_t *tvb, int *param_offset, proto_item *field_tree);
+static void mysql_dissect_exec_unsigned_short(tvbuff_t *tvb, int *param_offset, proto_item *field_tree);
static void mysql_dissect_exec_long(tvbuff_t *tvb, int *param_offset, proto_item *field_tree);
+static void mysql_dissect_exec_unsigned_long(tvbuff_t *tvb, int *param_offset, proto_item *field_tree);
static void mysql_dissect_exec_float(tvbuff_t *tvb, int *param_offset, proto_item *field_tree);
static void mysql_dissect_exec_double(tvbuff_t *tvb, int *param_offset, proto_item *field_tree);
static void mysql_dissect_exec_longlong(tvbuff_t *tvb, int *param_offset, proto_item *field_tree);
+static void mysql_dissect_exec_unsigned_longlong(tvbuff_t *tvb, int *param_offset, proto_item *field_tree);
static void mysql_dissect_exec_null(tvbuff_t *tvb, int *param_offset, proto_item *field_tree);
static char mysql_dissect_exec_param(proto_item *req_tree, tvbuff_t *tvb, int *offset,
int *param_offset, guint8 param_flags, packet_info *pinfo);
+static char mysql_dissect_binary_row_value(tvbuff_t *tvb, packet_info *pinfo, proto_item *pi, int *offset,
+ proto_item *tree, guint8 field_type, guint16 field_flag);
+
static void mysql_dissect_exec_primitive(tvbuff_t *tvb, int *param_offset,
proto_item *field_tree, const int hfindex, const int offset);
static void mysql_dissect_exec_time(tvbuff_t *tvb, int *param_offset, proto_item *field_tree);
@@ -1253,15 +1497,23 @@ static int mariadb_dissect_caps_or_flags(tvbuff_t *tvb, int offset, enum ftenum
static gint my_tvb_strsize(tvbuff_t *tvb, int offset);
static int tvb_get_fle(tvbuff_t *tvb, proto_tree* tree, int offset, guint64 *res, guint8 *is_null);
+static int mysql_field_add_lestring(tvbuff_t *tvb, int offset, proto_tree *tree, int field);
+static int dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_);
+static guint get_mysql_pdu_len(packet_info *pinfo, tvbuff_t *tvb, int offset, void *data _U_);
+
static const mysql_exec_dissector_t mysql_exec_dissectors[] = {
{ 0x01, 0, mysql_dissect_exec_tiny },
+ { 0x01, 1, mysql_dissect_exec_unsigned_tiny },
{ 0x02, 0, mysql_dissect_exec_short },
+ { 0x02, 1, mysql_dissect_exec_unsigned_short },
{ 0x03, 0, mysql_dissect_exec_long },
+ { 0x03, 1, mysql_dissect_exec_unsigned_long },
{ 0x04, 0, mysql_dissect_exec_float },
{ 0x05, 0, mysql_dissect_exec_double },
{ 0x06, 0, mysql_dissect_exec_null },
{ 0x07, 0, mysql_dissect_exec_datetime },
{ 0x08, 0, mysql_dissect_exec_longlong },
+ { 0x08, 1, mysql_dissect_exec_unsigned_longlong },
{ 0x0a, 0, mysql_dissect_exec_datetime },
{ 0x0b, 0, mysql_dissect_exec_time },
{ 0x0c, 0, mysql_dissect_exec_datetime },
@@ -1333,6 +1585,12 @@ static int * const mysql_extcaps_flags[] = {
&hf_mysql_cap_client_can_handle_expired_passwords,
&hf_mysql_cap_session_track,
&hf_mysql_cap_deprecate_eof,
+ &hf_mysql_cap_optional_metadata,
+ &hf_mysql_cap_compress_zstd,
+ &hf_mysql_cap_query_attrs,
+ &hf_mysql_cap_mf_auth,
+ &hf_mysql_cap_cap_ext,
+ &hf_mysql_cap_ssl_verify_server_cert,
&hf_mysql_cap_unused,
NULL
};
@@ -1342,6 +1600,7 @@ static int * const mariadb_extcaps_flags[] = {
&hf_mariadb_cap_commulti,
&hf_mariadb_cap_bulk,
&hf_mariadb_cap_extmetadata,
+ &hf_mariadb_cap_cache_metadata,
NULL
};
@@ -1359,7 +1618,6 @@ static int * const mysql_fld_flags[] = {
&hf_mysql_fld_blob,
&hf_mysql_fld_unsigned,
&hf_mysql_fld_zero_fill,
- &hf_mysql_fld_binary,
&hf_mysql_fld_enum,
&hf_mysql_fld_auto_increment,
&hf_mysql_fld_timestamp,
@@ -1376,19 +1634,49 @@ static void mysql_set_conn_state(packet_info *pinfo, mysql_conn_data_t *conn_dat
}
}
-static guint64 mysql_get_remaining_field_packet_count(mysql_conn_data_t *conn_data)
+static void mysql_set_resultset_fmt(packet_info *pinfo, mysql_conn_data_t *conn_data, mysql_resultset_fmt_t fmt)
{
- return conn_data->remaining_field_packet_count;
+ if (!pinfo->fd->visited)
+ {
+ conn_data->resultset_fmt = fmt;
+ }
}
-static void mysql_dec_remaining_field_packet_count(mysql_conn_data_t *conn_data)
+static void mysql_set_prepared_stmt_id(packet_info *pinfo, mysql_conn_data_t *conn_data, guint32 stmt_id)
{
- conn_data->remaining_field_packet_count--;
+ if (!pinfo->fd->visited)
+ {
+ conn_data->stmt_id = stmt_id;
+ }
}
-static void mysql_set_remaining_field_packet_count(mysql_conn_data_t *conn_data, guint64 num_fields)
+/* Decrements the number of remaining field packets. Returns TRUE if this
+ * was the last field packet (and thus the state should change.)
+ */
+static gboolean mysql_dec_remaining_field_packet_count(packet_info *pinfo, mysql_conn_data_t *conn_data)
{
- conn_data->remaining_field_packet_count = num_fields;
+ if (!pinfo->fd->visited)
+ {
+ conn_data->remaining_field_packet_count--;
+ return (conn_data->remaining_field_packet_count == 0);
+ }
+ return FALSE;
+}
+
+static void mysql_set_remaining_field_packet_count(packet_info *pinfo, mysql_conn_data_t *conn_data, guint64 num_fields)
+{
+ if (!pinfo->fd->visited)
+ {
+ conn_data->remaining_field_packet_count = num_fields;
+ }
+}
+
+static void mysql_set_field_metas(packet_info *pinfo, mysql_conn_data_t *conn_data, my_metadata_list_t *field_metas)
+{
+ if (!pinfo->fd->visited)
+ {
+ conn_data->field_metas = *field_metas;
+ }
}
static int
@@ -1428,13 +1716,16 @@ mysql_dissect_greeting(tvbuff_t *tvb, packet_info *pinfo, int offset,
if (lenstr > 6 && strncmp(buffer, MARIADB_RPL_VERSION_HACK, sizeof(MARIADB_RPL_VERSION_HACK) - 1) == 0)
{
conn_data->is_mariadb_server= 1;
+ col_append_fstr(pinfo->cinfo, COL_INFO, " version=%s ",
+ tvb_format_text(pinfo->pool, tvb, offset + 6, lenstr - 7));
+ } else {
+ col_append_fstr(pinfo->cinfo, COL_INFO, " version=%s ",
+ tvb_format_text(pinfo->pool, tvb, offset, lenstr-1));
}
- col_append_fstr(pinfo->cinfo, COL_INFO, " version=%s ",
- tvb_format_text(pinfo->pool, tvb, conn_data->is_mariadb_server ? offset + 6 : offset, conn_data->is_mariadb_server ? lenstr - 7 : lenstr-1));
col_set_fence(pinfo->cinfo, COL_INFO);
- proto_tree_add_item(greeting_tree, hf_mysql_version, tvb, offset, lenstr, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(greeting_tree, hf_mysql_version, tvb, offset, lenstr, ENC_ASCII);
conn_data->major_version = 0;
for (ver_offset = 0; ver_offset < lenstr; ver_offset++) {
guint8 ver_char = tvb_get_guint8(tvb, offset + ver_offset);
@@ -1449,7 +1740,7 @@ mysql_dissect_greeting(tvbuff_t *tvb, packet_info *pinfo, int offset,
/* salt string */
lenstr = tvb_strsize(tvb,offset);
- proto_tree_add_item(greeting_tree, hf_mysql_salt, tvb, offset, lenstr, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(greeting_tree, hf_mysql_salt, tvb, offset, lenstr, ENC_ASCII);
offset += lenstr;
/* rest is optional */
@@ -1458,6 +1749,12 @@ mysql_dissect_greeting(tvbuff_t *tvb, packet_info *pinfo, int offset,
/* 2 bytes CAPS */
offset = mysql_dissect_caps(tvb, offset, greeting_tree, hf_mysql_caps_server, &conn_data->srv_caps);
+ /* MariaDB server don't have the CLIENT_MYSQL/CLIENT_LONG_PASSWORD capability */
+ if (!(conn_data->srv_caps & MYSQL_CAPS_LP))
+ {
+ conn_data->is_mariadb_server= 1;
+ }
+
/* rest is optional */
if (!tvb_reported_length_remaining(tvb, offset)) return offset;
@@ -1477,7 +1774,7 @@ mysql_dissect_greeting(tvbuff_t *tvb, packet_info *pinfo, int offset,
{
/* 6 bytes unused */
proto_tree_add_item(greeting_tree, hf_mysql_unused, tvb, offset, 6, ENC_NA);
- offset += 6;
+ offset += 6;
/* MariaDB specific extended capabilities */
offset= mariadb_dissect_caps_or_flags(tvb, offset, FT_UINT32, greeting_tree,
hf_mariadb_extcaps_server, mariadb_extcaps_flags, &conn_data->mariadb_server_ext_caps);
@@ -1490,14 +1787,15 @@ mysql_dissect_greeting(tvbuff_t *tvb, packet_info *pinfo, int offset,
/* 4.1+ server: rest of salt */
if (tvb_reported_length_remaining(tvb, offset)) {
lenstr = tvb_strsize(tvb,offset);
- proto_tree_add_item(greeting_tree, hf_mysql_salt2, tvb, offset, lenstr, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(greeting_tree, hf_mysql_salt2, tvb, offset, lenstr, ENC_ASCII);
offset += lenstr;
}
/* 5.x server: auth plugin */
if (tvb_reported_length_remaining(tvb, offset)) {
lenstr = tvb_strsize(tvb,offset);
- proto_tree_add_item(greeting_tree, hf_mysql_auth_plugin, tvb, offset, lenstr, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(greeting_tree, hf_mysql_auth_plugin, tvb, offset, lenstr, ENC_ASCII);
+ conn_data->auth_method = tvb_get_string_enc(wmem_file_scope(), tvb, offset, lenstr, ENC_ASCII);
offset += lenstr;
}
@@ -1506,7 +1804,7 @@ mysql_dissect_greeting(tvbuff_t *tvb, packet_info *pinfo, int offset,
/*
- Add a connect attributs entry to the connattrs subtree
+ Add a connect attributes entry to the connattrs subtree
return bytes read
*/
@@ -1552,7 +1850,11 @@ mysql_dissect_login(tvbuff_t *tvb, packet_info *pinfo, int offset,
proto_item *login_tree;
/* after login there can be OK or DENIED */
- mysql_set_conn_state(pinfo, conn_data, RESPONSE_OK);
+ if (conn_data->clnt_caps & MYSQL_CAPS_SL) {
+ mysql_set_conn_state(pinfo, conn_data, LOGIN);
+ } else if (!(conn_data->clnt_caps == 0)) {
+ 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);
@@ -1600,7 +1902,7 @@ mysql_dissect_login(tvbuff_t *tvb, packet_info *pinfo, int offset,
lenstr = my_tvb_strsize(tvb, offset);
col_append_fstr(pinfo->cinfo, COL_INFO, " user=%s ",
tvb_format_text(pinfo->pool, tvb, offset, lenstr-1));
- proto_tree_add_item(login_tree, hf_mysql_user, tvb, offset, lenstr, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(login_tree, hf_mysql_user, tvb, offset, lenstr, ENC_ASCII);
offset += lenstr;
/* rest is optional */
@@ -1633,7 +1935,7 @@ mysql_dissect_login(tvbuff_t *tvb, packet_info *pinfo, int offset,
tvb_format_text(pinfo->pool, tvb, offset, lenstr-1));
col_set_fence(pinfo->cinfo, COL_INFO);
- proto_tree_add_item(login_tree, hf_mysql_schema, tvb, offset, lenstr, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(login_tree, hf_mysql_schema, tvb, offset, lenstr, ENC_ASCII);
offset += lenstr;
}
@@ -1642,7 +1944,8 @@ mysql_dissect_login(tvbuff_t *tvb, packet_info *pinfo, int offset,
{
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);
+ proto_tree_add_item(login_tree, hf_mysql_client_auth_plugin, tvb, offset, lenstr, ENC_ASCII);
+ conn_data->auth_method = tvb_get_string_enc(wmem_file_scope(), tvb, offset, lenstr, ENC_ASCII);
offset += lenstr;
}
@@ -1667,6 +1970,11 @@ mysql_dissect_login(tvbuff_t *tvb, packet_info *pinfo, int offset,
}
}
+ if (conn_data->clnt_caps_ext & MYSQL_CAPS_ZS)
+ {
+ proto_tree_add_item(login_tree, hf_mysql_zstd_compression_level, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ offset += 1;
+ }
return offset;
}
@@ -1767,18 +2075,36 @@ mysql_dissect_exec_tiny(tvbuff_t *tvb, int *param_offset, proto_item *field_tree
}
static void
+mysql_dissect_exec_unsigned_tiny(tvbuff_t *tvb, int *param_offset, proto_item *field_tree)
+{
+ mysql_dissect_exec_primitive(tvb, param_offset, field_tree, hf_mysql_exec_field_unsigned_tiny, 1);
+}
+
+static void
mysql_dissect_exec_short(tvbuff_t *tvb, int *param_offset, proto_item *field_tree)
{
mysql_dissect_exec_primitive(tvb, param_offset, field_tree, hf_mysql_exec_field_short, 2);
}
static void
+mysql_dissect_exec_unsigned_short(tvbuff_t *tvb, int *param_offset, proto_item *field_tree)
+{
+ mysql_dissect_exec_primitive(tvb, param_offset, field_tree, hf_mysql_exec_field_unsigned_short, 2);
+}
+
+static void
mysql_dissect_exec_long(tvbuff_t *tvb, int *param_offset, proto_item *field_tree)
{
mysql_dissect_exec_primitive(tvb, param_offset, field_tree, hf_mysql_exec_field_long, 4);
}
static void
+mysql_dissect_exec_unsigned_long(tvbuff_t *tvb, int *param_offset, proto_item *field_tree)
+{
+ mysql_dissect_exec_primitive(tvb, param_offset, field_tree, hf_mysql_exec_field_unsigned_long, 4);
+}
+
+static void
mysql_dissect_exec_float(tvbuff_t *tvb, int *param_offset, proto_item *field_tree)
{
mysql_dissect_exec_primitive(tvb, param_offset, field_tree, hf_mysql_exec_field_float, 4);
@@ -1797,6 +2123,12 @@ mysql_dissect_exec_longlong(tvbuff_t *tvb, int *param_offset, proto_item *field_
}
static void
+mysql_dissect_exec_unsigned_longlong(tvbuff_t *tvb, int *param_offset, proto_item *field_tree)
+{
+ mysql_dissect_exec_primitive(tvb, param_offset, field_tree, hf_mysql_exec_field_unsigned_longlong, 8);
+}
+
+static void
mysql_dissect_exec_null(tvbuff_t *tvb _U_, int *param_offset _U_, proto_item *field_tree _U_)
{}
@@ -1816,7 +2148,11 @@ mysql_dissect_exec_param(proto_item *req_tree, tvbuff_t *tvb, int *offset,
param_type = tvb_get_guint8(tvb, *offset);
*offset += 1; /* type */
proto_tree_add_item(field_tree, hf_mysql_exec_unsigned, tvb, *offset, 1, ENC_NA);
- param_unsigned = tvb_get_guint8(tvb, *offset);
+ if ((tvb_get_guint8(tvb, *offset) & 128) == 128) {
+ param_unsigned = 1;
+ } else {
+ param_unsigned = 0;
+ }
*offset += 1; /* signedness */
if ((param_flags & MYSQL_PARAM_FLAG_STREAMED) == MYSQL_PARAM_FLAG_STREAMED) {
expert_add_info(pinfo, field_tree, &ei_mysql_streamed_param);
@@ -1834,7 +2170,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, mysql_state_t current_state)
+mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *tree, mysql_conn_data_t *conn_data, const mysql_frame_data_t *my_frame_data)
{
gint opcode;
gint lenstr;
@@ -1843,9 +2179,21 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *
guint32 stmt_id;
my_stmt_data_t *stmt_data;
int stmt_pos, param_offset;
+ mysql_state_t current_state = my_frame_data->state;
- if(current_state == AUTH_SWITCH_RESPONSE){
+ /* LOCAL INFILE Request sends an empty packet after sending the file content
+ * https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_query_response_local_infile_request.html */
+ if (tvb_reported_length_remaining(tvb, offset) == 0)
+ return offset;
+
+ switch(current_state) {
+ case AUTH_SWITCH_RESPONSE:
return mysql_dissect_auth_switch_response(tvb, pinfo, offset, tree, conn_data);
+ case AUTH_SHA2:
+ return mysql_dissect_auth_sha2(tvb, pinfo, offset, tree, conn_data);
+ case INFILE_DATA:
+ return mysql_dissect_loaddata(tvb, pinfo, offset, tree, conn_data);
+ default:;
}
request_item = proto_tree_add_item(tree, hf_mysql_request, tvb, offset, -1, ENC_NA);
@@ -1866,6 +2214,7 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *
case MYSQL_PROCESS_INFO:
mysql_set_conn_state(pinfo, conn_data, RESPONSE_TABULAR);
+ mysql_set_resultset_fmt(pinfo, conn_data, TEXT);
break;
case MYSQL_DEBUG:
@@ -1881,14 +2230,48 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *
case MYSQL_CREATE_DB:
case MYSQL_DROP_DB:
lenstr = my_tvb_strsize(tvb, offset);
- proto_tree_add_item(req_tree, hf_mysql_schema, tvb, offset, lenstr, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(req_tree, hf_mysql_schema, tvb, offset, lenstr, ENC_ASCII);
offset += lenstr;
mysql_set_conn_state(pinfo, conn_data, RESPONSE_OK);
break;
case MYSQL_QUERY:
+ /* Check both the extended capabilities of the client and server. The flag is set by the client
+ * even if the server didn't set it. This is only actively used if both set the flag. */
+ if ((conn_data->clnt_caps_ext & MYSQL_CAPS_QA) && (conn_data->srv_caps_ext & MYSQL_CAPS_QA)){
+ proto_item *query_attrs_item = proto_tree_add_item(req_tree, hf_mysql_query_attributes, tvb, offset, -1, ENC_NA);
+ proto_item *query_attrs_tree = proto_item_add_subtree(query_attrs_item, ett_query_attributes);
+
+ gint n_params = tvb_get_guint8(tvb, offset);
+ proto_tree_add_item(query_attrs_tree, hf_mysql_query_attributes_count, tvb, offset, 1, ENC_ASCII);
+ offset += 2;
+
+ if (n_params > 0) {
+ gint null_count = (n_params + 7) / 8;
+ proto_tree_add_item(query_attrs_tree, hf_mysql_unused, tvb, offset, null_count, ENC_ASCII);
+ offset += null_count;
+
+ proto_tree_add_item(query_attrs_tree, hf_mysql_query_attributes_send_types_to_server, tvb, offset, 1, ENC_ASCII);
+ offset += 1;
+
+ for (int i = 0; i < n_params; ++i) {
+ proto_tree_add_item(query_attrs_tree, hf_mysql_query_attribute_name_type, tvb, offset, 2, ENC_ASCII);
+ offset += 2;
+ offset = mysql_field_add_lestring(tvb, offset, query_attrs_tree, hf_mysql_query_attribute_name);
+ }
+ for (int i = 0; i < n_params; ++i) {
+ offset = mysql_field_add_lestring(tvb, offset, query_attrs_tree, hf_mysql_query_attribute_value);
+ }
+ }
+ } else if ((conn_data->clnt_caps_ext == 0) && (conn_data->srv_caps_ext == 0)){
+ // No server/client capabilities, probably in the middle of a conversation
+ // As the query isn't likely to start with 0x00 this probably means these are
+ // query attributes we need to skip
+ if (tvb_get_guint8(tvb, offset) == 0)
+ offset += 2;
+ }
lenstr = my_tvb_strsize(tvb, offset);
- proto_tree_add_item(req_tree, hf_mysql_query, tvb, offset, lenstr, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(req_tree, hf_mysql_query, tvb, offset, lenstr, ENC_ASCII);
if (mysql_showquery) {
col_append_fstr(pinfo->cinfo, COL_INFO, " { %s } ",
tvb_format_text(pinfo->pool, tvb, offset, lenstr));
@@ -1896,11 +2279,12 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *
}
offset += lenstr;
mysql_set_conn_state(pinfo, conn_data, RESPONSE_TABULAR);
+ mysql_set_resultset_fmt(pinfo, conn_data, TEXT);
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);
+ proto_tree_add_item(req_tree, hf_mysql_query, tvb, offset, lenstr, ENC_ASCII);
offset += lenstr;
mysql_set_conn_state(pinfo, conn_data, RESPONSE_PREPARE);
break;
@@ -1919,20 +2303,20 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *
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);
+ proto_tree_add_item(req_tree, hf_mysql_table_name, tvb, offset, lenstr, ENC_ASCII);
offset += lenstr;
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);
+ proto_tree_add_item(req_tree, hf_mysql_thread_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
mysql_set_conn_state(pinfo, conn_data, RESPONSE_OK);
break;
case MYSQL_CHANGE_USER:
lenstr = tvb_strsize(tvb, offset);
- proto_tree_add_item(req_tree, hf_mysql_user, tvb, offset, lenstr, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(req_tree, hf_mysql_user, tvb, offset, lenstr, ENC_ASCII);
offset += lenstr;
if (conn_data->clnt_caps & MYSQL_CAPS_SC) {
@@ -1945,7 +2329,7 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *
offset += lenstr;
lenstr = my_tvb_strsize(tvb, offset);
- proto_tree_add_item(req_tree, hf_mysql_schema, tvb, offset, lenstr, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(req_tree, hf_mysql_schema, tvb, offset, lenstr, ENC_ASCII);
offset += lenstr;
if (tvb_reported_length_remaining(tvb, offset) > 0) {
@@ -1959,7 +2343,7 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *
{
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);
+ proto_tree_add_item(req_tree, hf_mysql_client_auth_plugin, tvb, offset, lenstr, ENC_ASCII);
offset += lenstr;
}
@@ -2012,6 +2396,7 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *
proto_tree_add_item(req_tree, hf_mysql_num_rows, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
mysql_set_conn_state(pinfo, conn_data, RESPONSE_TABULAR);
+ mysql_set_resultset_fmt(pinfo, conn_data, BINARY);
break;
case MYSQL_STMT_SEND_LONG_DATA:
@@ -2022,8 +2407,8 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *
stmt_data = (my_stmt_data_t *)wmem_tree_lookup32(conn_data->stmts, stmt_id);
if (stmt_data != NULL) {
guint16 data_param = tvb_get_letohs(tvb, offset);
- if (stmt_data->nparam > data_param) {
- stmt_data->param_flags[data_param] |= MYSQL_PARAM_FLAG_STREAMED;
+ if (stmt_data->param_metas.count > data_param) {
+ stmt_data->param_metas.flags[data_param] |= MYSQL_PARAM_FLAG_STREAMED;
}
}
@@ -2036,13 +2421,22 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *
proto_tree_add_item(req_tree, hf_mysql_payload, tvb, offset, lenstr, ENC_NA);
}
offset += lenstr;
- mysql_set_conn_state(pinfo, conn_data, REQUEST);
+ if (current_state != RESPONSE_PREPARE) {
+ // if pipelining, keeping PREPARE state
+ mysql_set_conn_state(pinfo, conn_data, REQUEST);
+ }
break;
case MARIADB_STMT_BULK_EXECUTE:
proto_tree_add_item(req_tree, hf_mysql_stmt_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
stmt_id = tvb_get_letohl(tvb, offset);
offset += 4;
+
+ // use last prepared statement
+ if (stmt_id == 0xffffffff) {
+ stmt_id = my_frame_data->stmt_id;
+ }
+
stmt_data = (my_stmt_data_t *)wmem_tree_lookup32(conn_data->stmts, stmt_id);
if (stmt_data != NULL) {
@@ -2052,24 +2446,25 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *
mariadb_dissect_caps_or_flags(tvb, offset, FT_UINT16, req_tree, hf_mariadb_bulk_caps_flags, mariadb_bulk_caps_flags, &stmt_data->bulk_flags);
offset += 2;
- if ((stmt_data->bulk_flags & MARIADB_BULK_SEND_TYPES) && stmt_data->nparam)
+ if ((stmt_data->bulk_flags & MARIADB_BULK_SEND_TYPES) && stmt_data->param_metas.count)
{
tf = proto_tree_add_item(req_tree, hf_mariadb_bulk_paramtypes, tvb, offset, -1, ENC_NA);
param_tree = proto_item_add_subtree(tf, ett_exec_param);
- for (stmt_pos = 0; stmt_pos < stmt_data->nparam; stmt_pos++) {
- stmt_data->param_types[stmt_pos] = tvb_get_guint8(tvb, offset);
+ for (stmt_pos = 0; stmt_pos < stmt_data->param_metas.count; stmt_pos++) {
+ stmt_data->param_metas.types[stmt_pos] = tvb_get_guint8(tvb, offset);
proto_tree_add_item(param_tree, hf_mysql_fld_type, tvb, offset, 1, ENC_NA);
offset+= 1;
- stmt_data->param_flags[stmt_pos] = tvb_get_guint8(tvb, offset);
+ stmt_data->param_metas.flags[stmt_pos] = tvb_get_guint8(tvb, offset);
proto_tree_add_item(param_tree, hf_mysql_exec_unsigned, tvb, offset, 1, ENC_NA);
offset+= 1;
}
}
while (tvb_reported_length_remaining(tvb, offset) > 0){
- tf = proto_tree_add_text_internal(req_tree, tvb, offset, 0, "%d. Dataset", row_nr++);
+ tf = proto_tree_add_uint_format(req_tree, hf_mariadb_bulk_row_nr, tvb, offset, 0, row_nr, "%d. Dataset", row_nr);
+ proto_item_set_generated(tf);
param_tree = proto_item_add_subtree(tf, ett_bulk_param);
- for (stmt_pos = 0; stmt_pos < stmt_data->nparam; stmt_pos++)
+ for (stmt_pos = 0; stmt_pos < stmt_data->param_metas.count; stmt_pos++)
{
guint8 indicator= tvb_get_guint8(tvb, offset);
proto_tree_add_item(param_tree, hf_mariadb_bulk_indicator, tvb, offset, 1, ENC_NA);
@@ -2078,7 +2473,7 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *
if (!indicator) {
int dissector_index= 0;
while (mysql_exec_dissectors[dissector_index].dissector != NULL) {
- if (mysql_exec_dissectors[dissector_index].type == stmt_data->param_types[stmt_pos])
+ if (mysql_exec_dissectors[dissector_index].type == stmt_data->param_metas.types[stmt_pos])
/* &&
mysql_exec_dissectors[dissector_index].unsigned_flag == stmt_data->param_flags[stmt_pos]) */
{
@@ -2089,8 +2484,14 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *
}
}
}
+ row_nr++;
}
}
+ if (current_state != RESPONSE_PREPARE) {
+ // if pipelining, keeping PREPARE state
+ mysql_set_conn_state(pinfo, conn_data, REQUEST);
+ }
+
break;
case MYSQL_STMT_EXECUTE:
@@ -2108,19 +2509,25 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *
proto_tree_add_item(req_tree, hf_mysql_exec_iter, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
+ // use last prepared statement
+ if (stmt_id == 0xffffffff) {
+ stmt_id = my_frame_data->stmt_id;
+ }
stmt_data = (my_stmt_data_t *)wmem_tree_lookup32(conn_data->stmts, stmt_id);
if (stmt_data != NULL) {
- if (stmt_data->nparam != 0) {
+ if (stmt_data->param_metas.count != 0) {
guint8 stmt_bound;
- offset += (stmt_data->nparam + 7) / 8; /* NULL bitmap */
+ offset += (stmt_data->param_metas.count + 7) / 8; /* NULL bitmap */
proto_tree_add_item(req_tree, hf_mysql_new_parameter_bound_flag, tvb, offset, 1, ENC_NA);
stmt_bound = tvb_get_guint8(tvb, offset);
offset += 1;
if (stmt_bound == 1) {
- param_offset = offset + stmt_data->nparam * 2;
- for (stmt_pos = 0; stmt_pos < stmt_data->nparam; stmt_pos++) {
+ param_offset = offset + stmt_data->param_metas.count * 2;
+ guint8 flags;
+ for (stmt_pos = 0; stmt_pos < stmt_data->param_metas.count; stmt_pos++) {
+ flags = (guint8)stmt_data->param_metas.flags[stmt_pos];
if (!mysql_dissect_exec_param(req_tree, tvb, &offset, &param_offset,
- stmt_data->param_flags[stmt_pos], pinfo))
+ flags, pinfo))
break;
}
offset = param_offset;
@@ -2134,20 +2541,45 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *
}
offset += lenstr;
}
-#if 0
-/* FIXME: rest needs metadata about statement */
-#else
- lenstr = tvb_reported_length_remaining(tvb, offset);
- if (tree && lenstr > 0) {
- ti = proto_tree_add_item(req_tree, hf_mysql_payload, tvb, offset, lenstr, ENC_NA);
- expert_add_info_format(pinfo, ti, &ei_mysql_dissector_incomplete, "FIXME: execute dissector incomplete");
+
+ if (current_state != RESPONSE_PREPARE) {
+ // if pipelining, keeping PREPARE state
+ mysql_set_conn_state(pinfo, conn_data, RESPONSE_TABULAR);
}
- offset += lenstr;
-#endif
- mysql_set_conn_state(pinfo, conn_data, RESPONSE_TABULAR);
+ mysql_set_resultset_fmt(pinfo, conn_data, BINARY);
+
break;
case MYSQL_BINLOG_DUMP_GTID:
+ // See mysql_binlog_open() in "sql-common/client.cc"
+
+ proto_tree_add_item(req_tree, hf_mysql_binlog_flags, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset += 2;
+
+ proto_tree_add_item(req_tree, hf_mysql_binlog_server_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ lenstr = tvb_get_guint32(tvb, offset, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(req_tree, hf_mysql_binlog_file_name_length, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ if (tree && lenstr > 0) {
+ proto_tree_add_item(req_tree, hf_mysql_binlog_file_name, tvb, offset, lenstr, ENC_ASCII);
+ }
+ offset += lenstr;
+
+ proto_tree_add_item(req_tree, hf_mysql_binlog_position8, tvb, offset, 8, ENC_LITTLE_ENDIAN);
+ offset += 8;
+
+ lenstr = tvb_get_guint32(tvb, offset, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(req_tree, hf_mysql_binlog_gtid_data_length, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ proto_tree_add_item(req_tree, hf_mysql_binlog_gtid_data, tvb, offset, lenstr, ENC_ASCII);
+ offset += lenstr;
+
+ mysql_set_conn_state(pinfo, conn_data, BINLOG_DUMP);
+ break;
case MYSQL_BINLOG_DUMP:
proto_tree_add_item(req_tree, hf_mysql_binlog_position, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
@@ -2161,22 +2593,66 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *
/* binlog file name ? */
lenstr = tvb_reported_length_remaining(tvb, offset);
if (tree && lenstr > 0) {
- proto_tree_add_item(req_tree, hf_mysql_binlog_file_name, tvb, offset, lenstr, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(req_tree, hf_mysql_binlog_file_name, tvb, offset, lenstr, ENC_ASCII);
}
offset += lenstr;
+ mysql_set_conn_state(pinfo, conn_data, BINLOG_DUMP);
+ break;
+
+ case MYSQL_REGISTER_SLAVE:
+ proto_tree_add_item(req_tree, hf_mysql_binlog_server_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ lenstr = tvb_get_guint8(tvb, offset);
+ proto_tree_add_item(req_tree, hf_mysql_binlog_slave_hostname_length, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ offset += 1;
+
+ proto_tree_add_item(req_tree, hf_mysql_binlog_slave_hostname, tvb, offset, lenstr, ENC_ASCII);
+ offset += lenstr;
+
+ lenstr = tvb_get_guint8(tvb, offset);
+ proto_tree_add_item(req_tree, hf_mysql_binlog_slave_user_length, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ offset += 1;
+
+ proto_tree_add_item(req_tree, hf_mysql_binlog_slave_user, tvb, offset, lenstr, ENC_ASCII);
+ offset += lenstr;
+
+ lenstr = tvb_get_guint8(tvb, offset);
+ proto_tree_add_item(req_tree, hf_mysql_binlog_slave_password_length, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ offset += 1;
+
+ proto_tree_add_item(req_tree, hf_mysql_binlog_slave_password, tvb, offset, lenstr, ENC_ASCII);
+ offset += lenstr;
+
+ proto_tree_add_item(req_tree, hf_mysql_binlog_slave_mysql_port, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ offset += 2;
+
+ proto_tree_add_item(req_tree, hf_mysql_binlog_replication_rank, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ proto_tree_add_item(req_tree, hf_mysql_binlog_master_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
mysql_set_conn_state(pinfo, conn_data, REQUEST);
break;
+
/* FIXME: implement replication packets */
case MYSQL_TABLE_DUMP:
case MYSQL_CONNECT_OUT:
- case MYSQL_REGISTER_SLAVE:
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);
mysql_set_conn_state(pinfo, conn_data, REQUEST);
break;
+ case MYSQL_CLONE:
+ mysql_set_conn_state(pinfo, conn_data, CLONE_INIT);
+ break;
+
+ case MYSQL_RESET_CONNECTION:
+ 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);
@@ -2188,140 +2664,189 @@ mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset, proto_tree *
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,
- proto_tree *tree, mysql_conn_data_t *conn_data, mysql_state_t current_state)
+ proto_tree *tree, mysql_conn_data_t *conn_data, proto_item *pi, const mysql_frame_data_t *my_frame_data)
{
gint response_code;
gint lenstr;
proto_item *ti;
- guint16 server_status = 0;
- response_code = tvb_get_guint8(tvb, offset);
+ mysql_state_t current_state = my_frame_data->state;
+ my_stmt_data_t *stmt_data = NULL;
+ if (my_frame_data->stmt_id) {
+ stmt_data = (my_stmt_data_t *)wmem_tree_lookup32(conn_data->stmts, my_frame_data->stmt_id);
+ }
- if (response_code == 0xff ) {
+ response_code = tvb_get_guint8(tvb, offset);
+ switch (response_code) {
+ case 0xff:
proto_tree_add_item(tree, hf_mysql_response_code, tvb, offset, 1, ENC_NA);
+ proto_item_append_text(pi, " - %s", val_to_str(RESPONSE_ERROR, state_vals, "Unknown (%u)"));
offset = mysql_dissect_error_packet(tvb, pinfo, offset+1, tree);
mysql_set_conn_state(pinfo, conn_data, REQUEST);
- }
-
- else if (response_code == 0xfe && tvb_reported_length_remaining(tvb, offset) < 9) {
-
+ break;
+ case 0xfe:
proto_tree_add_item(tree, hf_mysql_response_code, tvb, offset, 1, ENC_NA);
- ti = proto_tree_add_item(tree, hf_mysql_eof, tvb, offset, 1, ENC_NA);
-
+ proto_tree_add_item(tree, hf_mysql_eof, tvb, offset, 1, ENC_NA);
offset += 1;
- /* pre-4.1 packet ends here */
- if (tvb_reported_length_remaining(tvb, offset)) {
- if (conn_data->clnt_caps_ext & MYSQL_CAPS_DE) {
- offset = mysql_dissect_ok_packet(tvb, pinfo, offset, tree, conn_data);
+ if (tvb_reported_length_remaining(tvb, offset) <= 5) {
+ // real EOF packet
+ offset = mysql_dissect_eof(tvb, pinfo, pi, offset, tree, conn_data);
+
+ if (current_state == PREPARED_PARAMETERS) {
+ if (stmt_data != NULL && stmt_data->field_metas.count > 0) {
+ proto_item_append_text(pi, " - %s", val_to_str(INTERMEDIATE_EOF, state_vals, "Unknown (%u)"));
+ mysql_set_remaining_field_packet_count(pinfo, conn_data, stmt_data->field_metas.count);
+ mysql_set_conn_state(pinfo, conn_data, PREPARED_FIELDS);
+ } else {
+ proto_item_append_text(pi, " - %s", val_to_str(RESPONSE_EOF, state_vals, "Unknown (%u)"));
+ mysql_set_conn_state(pinfo, conn_data, REQUEST);
+ }
+ } else if (current_state == FIELD_PACKET) {
+ // intermediate EOF packet
+ proto_item_append_text(pi, " - %s", val_to_str(INTERMEDIATE_EOF, state_vals, "Unknown (%u)"));
+ mysql_set_conn_state(pinfo, conn_data, ROW_PACKET);
+ } else {
+ // ending EOF packet
+ proto_item_append_text(pi, " - %s", val_to_str(RESPONSE_EOF, state_vals, "Unknown (%u)"));
+ mysql_set_conn_state(pinfo, conn_data, REQUEST);
+ }
+ } else if (tvb_reported_length_remaining(tvb, offset) < 0xffffff) {
+ // not an EOF
+ if (current_state == AUTH_SWITCH_REQUEST) {
+ proto_item_append_text(pi, " - %s", val_to_str(AUTH_SWITCH_REQUEST, state_vals, "Unknown (%u)"));
+ offset = mysql_dissect_auth_switch_request(tvb, pinfo, offset, tree, conn_data);
} else {
- proto_tree_add_item(tree, hf_mysql_num_warn, tvb, offset, 2, ENC_LITTLE_ENDIAN);
- offset = mysql_dissect_server_status(tvb, offset, tree, &server_status);
+ proto_item_append_text(pi, " - %s", val_to_str(RESPONSE_OK, state_vals, "Unknown (%u)"));
+ offset = mysql_dissect_ok_packet(tvb, pinfo, offset, tree, conn_data);
+ mysql_set_conn_state(pinfo, conn_data, REQUEST);
}
+ } else {
+ // text row packet
+ proto_item_append_text(pi, " - %s", val_to_str(ROW_PACKET, state_vals, "Unknown (%u)"));
+ mysql_set_conn_state(pinfo, conn_data, ROW_PACKET);
+ offset = mysql_dissect_text_row_packet(tvb, offset, tree);
}
+ break;
+ case 0x00:
switch (current_state) {
- case FIELD_PACKET:
- mysql_set_conn_state(pinfo, conn_data, ROW_PACKET);
+ case RESPONSE_PREPARE:
+ proto_tree_add_item(tree, hf_mysql_response_code, tvb, offset, 1, ENC_NA);
+ offset+=1;
+ proto_item_append_text(pi, " - %s", val_to_str(RESPONSE_PREPARE, state_vals, "Unknown (%u)"));
+ offset = mysql_dissect_response_prepare(tvb, pinfo, offset, tree, conn_data);
break;
case ROW_PACKET:
- if (server_status & MYSQL_STAT_MU) {
- mysql_set_conn_state(pinfo, conn_data, RESPONSE_TABULAR);
- } else {
- mysql_set_conn_state(pinfo, conn_data, REQUEST);
- }
- break;
- case PREPARED_PARAMETERS:
- if (conn_data->stmt_num_fields > 0) {
- mysql_set_conn_state(pinfo, conn_data, PREPARED_FIELDS);
+ proto_item_append_text(pi, " - %s", val_to_str(ROW_PACKET, state_vals, "Unknown (%u)"));
+ if (my_frame_data->resultset_fmt == BINARY) {
+ proto_tree_add_item(tree, hf_mysql_response_code, tvb, offset, 1, ENC_NA);
+ offset+=1;
+ offset = mysql_dissect_binary_row_packet(tvb, pinfo, pi, offset, tree, conn_data, my_frame_data);
} else {
- mysql_set_conn_state(pinfo, conn_data, REQUEST);
+ offset = mysql_dissect_text_row_packet(tvb, offset, tree);
}
break;
- case PREPARED_FIELDS:
- mysql_set_conn_state(pinfo, conn_data, REQUEST);
+ case BINLOG_DUMP:
+ proto_tree_add_item(tree, hf_mysql_response_code, tvb, offset, 1, ENC_NA);
+ offset+=1;
+ proto_item_append_text(pi, " - %s", val_to_str(BINLOG_DUMP, state_vals, "Unknown (%u)"));
+ offset = mysql_dissect_binlog_event_packet(tvb, pinfo, offset, tree, pi);
break;
default:
- /* This should be an unreachable case */
- mysql_set_conn_state(pinfo, conn_data, REQUEST);
- expert_add_info(pinfo, ti, &ei_mysql_eof);
- }
- }
-
- else if (response_code == 0) {
- proto_tree_add_item(tree, hf_mysql_response_code, tvb, offset, 1, ENC_NA);
- 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, tree, offset+1, NULL, NULL)) {
- offset = mysql_dissect_ok_packet(tvb, pinfo, offset+1, tree, conn_data);
+ proto_tree_add_item(tree, hf_mysql_response_code, tvb, offset, 1, ENC_NA);
+ offset+=1;
+ proto_item_append_text(pi, " - %s", val_to_str(RESPONSE_OK, state_vals, "Unknown (%u)"));
+ offset = mysql_dissect_ok_packet(tvb, pinfo, offset, 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);
+ if (current_state == CLONE_INIT)
+ mysql_set_conn_state(pinfo, conn_data, CLONE_ACTIVE);
+ break;
}
- }
-
- else {
+ break;
+ default:
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);
+ proto_tree_add_item(tree, hf_mysql_message, tvb, offset, lenstr, ENC_ASCII);
offset += lenstr;
}
mysql_set_conn_state(pinfo, conn_data, REQUEST);
break;
case RESPONSE_TABULAR:
- case REQUEST: /* That shouldn't be the case; maybe two requests in a row (s. bug 15074) */
- offset = mysql_dissect_result_header(tvb, pinfo, offset, tree, conn_data);
+ case REQUEST: /* That shouldn't be the case; maybe two requests in a row (s. bug 15074), or after pipelining */
+ if (response_code == 0xfb) {
+ /* https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_query_response_local_infile_request.html */
+ col_append_str(pinfo->cinfo, COL_INFO, " LOCAL INFILE");
+ proto_tree_add_item(tree, hf_mysql_response_code, tvb, offset, 1, ENC_NA);
+ proto_item_append_text(pi, " - %s", val_to_str(RESPONSE_LOCALINFILE, state_vals, "Unknown (%u)"));
+
+ lenstr = tvb_reported_length_remaining(tvb, ++offset);
+ proto_tree_add_item(tree, hf_mysql_loaddata_filename, tvb, offset, lenstr, ENC_ASCII);
+ offset += lenstr;
+ mysql_set_conn_state(pinfo, conn_data, INFILE_DATA);
+ break;
+ }
+ proto_item_append_text(pi, " - %s", val_to_str(COLUMN_COUNT, state_vals, "Unknown (%u)"));
+ offset = mysql_dissect_result_header(tvb, pinfo, offset, tree, conn_data, my_frame_data);
+ break;
+ case PREPARED_PARAMETERS:
+ proto_item_append_text(pi, " - %s", val_to_str(current_state, state_vals, "Unknown (%u)"));
+ offset = mysql_dissect_field_packet(tvb, pi, offset, tree, pinfo, conn_data, my_frame_data);
+ if (mysql_dec_remaining_field_packet_count(pinfo, conn_data)) {
+ if (conn_data->clnt_caps_ext & MYSQL_CAPS_DE) {
+ if (stmt_data != NULL && stmt_data->field_metas.count > 0) {
+ mysql_set_remaining_field_packet_count(pinfo, conn_data, stmt_data->field_metas.count);
+ mysql_set_conn_state(pinfo, conn_data, PREPARED_FIELDS);
+ } else {
+ mysql_set_conn_state(pinfo, conn_data, REQUEST);
+ }
+ }
+ }
break;
case FIELD_PACKET:
case RESPONSE_SHOW_FIELDS:
- case RESPONSE_PREPARE:
- case PREPARED_PARAMETERS:
- offset = mysql_dissect_field_packet(tvb, offset, tree, conn_data);
- mysql_dec_remaining_field_packet_count(conn_data);
- if ((conn_data->clnt_caps_ext & MYSQL_CAPS_DE) && (mysql_get_remaining_field_packet_count(conn_data) == 0)) {
+ proto_item_append_text(pi, " - %s", val_to_str(current_state, state_vals, "Unknown (%u)"));
+ offset = mysql_dissect_field_packet(tvb, pi, offset, tree, pinfo, conn_data, my_frame_data);
+ if (mysql_dec_remaining_field_packet_count(pinfo, conn_data) && (conn_data->clnt_caps_ext & MYSQL_CAPS_DE)) {
mysql_set_conn_state(pinfo, conn_data, ROW_PACKET);
}
break;
case ROW_PACKET:
- offset = mysql_dissect_row_packet(tvb, offset, tree);
+ proto_item_append_text(pi, " - %s", val_to_str(current_state, state_vals, "Unknown (%u)"));
+ offset = mysql_dissect_text_row_packet(tvb, offset, tree);
break;
case PREPARED_FIELDS:
- offset = mysql_dissect_field_packet(tvb, offset, tree, conn_data);
+ proto_item_append_text(pi, " - %s", val_to_str(current_state, state_vals, "Unknown (%u)"));
+ offset = mysql_dissect_field_packet(tvb, pi, offset, tree, pinfo, conn_data, my_frame_data);
+ if (mysql_dec_remaining_field_packet_count(pinfo, conn_data) && (conn_data->clnt_caps_ext & MYSQL_CAPS_DE)) {
+ mysql_set_conn_state(pinfo, conn_data, REQUEST);
+ }
break;
case AUTH_SWITCH_REQUEST:
- offset = mysql_dissect_auth_switch_request(tvb, pinfo, offset, tree, conn_data);
+ if (tvb_reported_length_remaining(tvb,offset) == 2) {
+ proto_item_append_text(pi, " - %s", val_to_str(AUTH_SHA2, state_vals, "Unknown (%u)"));
+ offset = mysql_dissect_auth_sha2(tvb, pinfo, offset, tree, conn_data);
+ } else {
+ proto_item_append_text(pi, " - %s", val_to_str(AUTH_SWITCH_REQUEST, state_vals, "Unknown (%u)"));
+ offset = mysql_dissect_auth_switch_request(tvb, pinfo, offset, tree, conn_data);
+ }
break;
+ case AUTH_SHA2:
+ proto_item_append_text(pi, " - %s", val_to_str(AUTH_SHA2, state_vals, "Unknown (%u)"));
+ offset = mysql_dissect_auth_sha2(tvb, pinfo, offset, tree, conn_data);
+ break;
default:
ti = proto_tree_add_item(tree, hf_mysql_payload, tvb, offset, -1, ENC_NA);
@@ -2348,11 +2873,11 @@ mysql_dissect_error_packet(tvbuff_t *tvb, packet_info *pinfo,
if (tvb_get_guint8(tvb, offset) == '#')
{
offset += 1;
- proto_tree_add_item(tree, hf_mysql_sqlstate, tvb, offset, 5, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(tree, hf_mysql_sqlstate, tvb, offset, 5, ENC_ASCII);
offset += 5;
}
- proto_tree_add_item(tree, hf_mysql_error_string, tvb, offset, -1, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(tree, hf_mysql_error_string, tvb, offset, -1, ENC_ASCII);
offset += tvb_reported_length_remaining(tvb, offset);
return offset;
@@ -2389,14 +2914,14 @@ add_session_tracker_entry_to_tree(tvbuff_t *tvb, packet_info *pinfo, proto_item
proto_tree_add_uint64(session_track_tree, hf_mysql_session_track_sysvar_length, tvb, offset, lenfle, lenstr);
offset += lenfle;
- proto_tree_add_item(session_track_tree, hf_mysql_session_track_sysvar_name, tvb, offset, (gint)lenstr, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(session_track_tree, hf_mysql_session_track_sysvar_name, tvb, offset, (gint)lenstr, ENC_ASCII);
offset += (int)lenstr;
lenfle = tvb_get_fle(tvb, session_track_tree, offset, &lenstr, NULL);
proto_tree_add_uint64(session_track_tree, hf_mysql_session_track_sysvar_length, tvb, offset, lenfle, lenstr);
offset += lenfle;
- proto_tree_add_item(session_track_tree, hf_mysql_session_track_sysvar_value, tvb, offset, (gint)lenstr, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(session_track_tree, hf_mysql_session_track_sysvar_value, tvb, offset, (gint)lenstr, ENC_ASCII);
offset += (int)lenstr;
break;
case 1: /* CURRENT_SCHEMA_TRACKER */
@@ -2404,11 +2929,11 @@ add_session_tracker_entry_to_tree(tvbuff_t *tvb, packet_info *pinfo, proto_item
proto_tree_add_uint64(session_track_tree, hf_mysql_session_track_schema_length, tvb, offset, lenfle, lenstr);
offset += lenfle;
- proto_tree_add_item(session_track_tree, hf_mysql_session_track_schema, tvb, offset, (gint)lenstr, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(session_track_tree, hf_mysql_session_track_schema, tvb, offset, (gint)lenstr, ENC_ASCII);
offset += (int)lenstr;
break;
case 2: /* SESSION_STATE_CHANGE_TRACKER */
- proto_tree_add_item(session_track_tree, hf_mysql_session_state_change, tvb, offset, 1, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(session_track_tree, hf_mysql_session_state_change, tvb, offset, 1, ENC_ASCII);
offset++;
break;
case 3: /* SESSION_TRACK_GTIDS */
@@ -2418,7 +2943,7 @@ add_session_tracker_entry_to_tree(tvbuff_t *tvb, packet_info *pinfo, proto_item
proto_tree_add_uint64(session_track_tree, hf_mysql_session_track_gtids_length, tvb, offset, lenfle, lenstr);
offset += lenfle;
- proto_tree_add_item(session_track_tree, hf_mysql_session_track_gtids, tvb, offset, (gint)lenstr, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(session_track_tree, hf_mysql_session_track_gtids, tvb, offset, (gint)lenstr, ENC_ASCII);
offset += (int)lenstr;
break;
case 4: /* SESSION_TRACK_TRANSACTION_CHARACTERISTICS */
@@ -2426,7 +2951,7 @@ add_session_tracker_entry_to_tree(tvbuff_t *tvb, packet_info *pinfo, proto_item
proto_tree_add_uint64(session_track_tree, hf_mysql_session_track_transaction_characteristics_length, tvb, offset, lenfle, lenstr);
offset += lenfle;
- proto_tree_add_item(session_track_tree, hf_mysql_session_track_transaction_characteristics, tvb, offset, (gint)lenstr, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(session_track_tree, hf_mysql_session_track_transaction_characteristics, tvb, offset, (gint)lenstr, ENC_ASCII);
offset += (int)lenstr;
break;
case 5: /* SESSION_TRACK_TRANSACTION_STATE */
@@ -2434,7 +2959,7 @@ add_session_tracker_entry_to_tree(tvbuff_t *tvb, packet_info *pinfo, proto_item
proto_tree_add_uint64(session_track_tree, hf_mysql_session_track_transaction_state_length, tvb, offset, lenfle, lenstr);
offset += lenfle;
- proto_tree_add_item(session_track_tree, hf_mysql_session_track_transaction_state, tvb, offset, (gint)lenstr, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(session_track_tree, hf_mysql_session_track_transaction_state, tvb, offset, (gint)lenstr, ENC_ASCII);
offset += (int)lenstr;
break;
default: /* unsupported types skipped */
@@ -2447,11 +2972,55 @@ add_session_tracker_entry_to_tree(tvbuff_t *tvb, packet_info *pinfo, proto_item
return (offset - orig_offset);
}
+
+/*
+ Add a extended metadata entry to the extended meta subtree
+
+ return bytes read
+*/
+static int
+add_extended_meta_entry_to_tree(tvbuff_t *tvb, packet_info *pinfo, proto_item *tree, int offset) {
+ guint8 data_type;
+ guint64 lenstr;
+ int orig_offset = offset, lenfle;
+ proto_item *item, *ti;
+ proto_tree *extmeta_tree;
+
+ ti = proto_tree_add_item(tree, hf_mariadb_extmeta, tvb, offset, 1, ENC_NA);
+ extmeta_tree = proto_item_add_subtree(ti, ett_extmeta);
+
+ proto_tree_add_item(extmeta_tree, hf_mariadb_extmeta_key, tvb, offset, 1, ENC_BIG_ENDIAN);
+ data_type = tvb_get_guint8(tvb, offset);
+ offset += 1;
+
+ lenfle = tvb_get_fle(tvb, extmeta_tree, offset, &lenstr, NULL);
+ proto_tree_add_uint64(extmeta_tree, hf_mariadb_extmeta_length, tvb, offset, lenfle, lenstr);
+ offset += lenfle;
+
+ switch (data_type) {
+ case 0: /* TYPE */
+ proto_tree_add_item(extmeta_tree, hf_mariadb_extmeta_type, tvb, offset, (gint)lenstr, ENC_ASCII);
+ offset += (int)lenstr;
+ break;
+ case 1: /* FORMAT */
+ proto_tree_add_item(extmeta_tree, hf_mariadb_extmeta_format, tvb, offset, (gint)lenstr, ENC_ASCII);
+ offset += (int)lenstr;
+ break;
+ default: /* unsupported types skipped */
+ item = proto_tree_add_item(extmeta_tree, hf_mysql_payload, tvb, offset, (gint)lenstr, ENC_NA);
+ expert_add_info_format(pinfo, item, &ei_mysql_dissector_incomplete, "FIXME: unrecognized extended metadata data");
+ offset += (int)lenstr;
+ }
+ proto_item_set_len(ti, offset - orig_offset);
+
+ return (offset - orig_offset);
+}
+
static int
mysql_dissect_ok_packet(tvbuff_t *tvb, packet_info *pinfo, int offset,
proto_tree *tree, mysql_conn_data_t *conn_data)
{
- guint64 lenstr;
+ guint64 lenstr = 0;
guint64 affected_rows;
guint64 insert_id;
int fle;
@@ -2474,9 +3043,11 @@ mysql_dissect_ok_packet(tvbuff_t *tvb, packet_info *pinfo, int offset,
offset = mysql_dissect_server_status(tvb, offset, tree, &server_status);
/* 4.1+ protocol only: 2 bytes number of warnings */
- if (conn_data->clnt_caps & conn_data->srv_caps & MYSQL_CAPS_CU) {
+ if ((conn_data->clnt_caps & conn_data->srv_caps & MYSQL_CAPS_CU)
+ || ((conn_data->clnt_caps == 0) && (conn_data->srv_caps == 0))) {
proto_tree_add_item(tree, hf_mysql_num_warn, tvb, offset, 2, ENC_LITTLE_ENDIAN);
- offset += 2;
+ lenstr = tvb_get_ntohs(tvb, offset);
+ offset += 2;
}
}
@@ -2490,7 +3061,7 @@ mysql_dissect_ok_packet(tvbuff_t *tvb, packet_info *pinfo, int offset,
offset += tvb_get_fle(tvb, tree, offset, &lenstr, NULL);
/* first read the optional message */
if (lenstr) {
- proto_tree_add_item(tree, hf_mysql_message, tvb, offset, (gint)lenstr, ENC_ASCII|ENC_NA);
+ proto_tree_add_item(tree, hf_mysql_message, tvb, offset, (gint)lenstr, ENC_ASCII);
offset += (int)lenstr;
}
@@ -2510,10 +3081,17 @@ mysql_dissect_ok_packet(tvbuff_t *tvb, packet_info *pinfo, int offset,
}
}
} else {
+ if ((tvb_reported_length_remaining(tvb, offset) > 0)
+ && ((conn_data->clnt_caps == 0) && (conn_data->srv_caps == 0))) {
+ // No client or server capabilities to work with, Let's try to skip over session state
+ offset += tvb_get_fle(tvb, tree, offset, &lenstr, NULL);
+ }
+
/* optional: message string */
if (tvb_reported_length_remaining(tvb, offset) > 0) {
- lenstr = tvb_reported_length_remaining(tvb, offset);
- proto_tree_add_item(tree, hf_mysql_message, tvb, offset, (gint)lenstr, ENC_ASCII|ENC_NA);
+ if(lenstr > (guint64)tvb_reported_length_remaining(tvb, offset))
+ lenstr = tvb_reported_length_remaining(tvb, offset);
+ proto_tree_add_item(tree, hf_mysql_message, tvb, offset, (gint)lenstr, ENC_ASCII);
offset += (int)lenstr;
}
}
@@ -2589,13 +3167,16 @@ static int mariadb_dissect_caps_or_flags(tvbuff_t *tvb, int offset, enum ftenum
return offset;
}
-
static int
mysql_dissect_result_header(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,
+ const mysql_frame_data_t *my_frame_data)
{
gint fle;
guint64 num_fields, extra;
+ guint8 send_meta= 0;
+ my_metadata_list_t *field_metas;
+ my_stmt_data_t *stmt_data;
col_append_str(pinfo->cinfo, COL_INFO, "TABULAR " );
col_set_fence(pinfo->cinfo, COL_INFO);
@@ -2604,6 +3185,37 @@ mysql_dissect_result_header(tvbuff_t *tvb, packet_info *pinfo, int offset,
proto_tree_add_uint64(tree, hf_mysql_num_fields, tvb, offset, fle, num_fields);
offset += fle;
+ /** skip info flag **/
+ send_meta = 1;
+ if (conn_data->mariadb_client_ext_caps & MARIADB_CAPS_ME
+ && conn_data->mariadb_server_ext_caps & MARIADB_CAPS_ME
+ && tvb_reported_length_remaining(tvb, offset)) {
+ send_meta = tvb_get_guint8(tvb, offset);
+ proto_tree_add_item(tree, hf_mariadb_send_meta, tvb, offset, 1, ENC_NA);
+ offset += 1;
+ }
+
+
+ if (num_fields > MAX_MY_METADATA_COUNT) {
+ expert_add_info_format(pinfo, tree, &ei_mysql_invalid_length, "Invalid length: %" G_GUINT64_FORMAT, num_fields);
+ return tvb_reported_length_remaining(tvb, 0);
+ } else if (send_meta) {
+ field_metas = wmem_new(wmem_file_scope(), my_metadata_list_t);
+ field_metas->count = (guint16)num_fields;
+ field_metas->flags = (guint16 *)wmem_alloc0_array(wmem_file_scope(), guint16, (size_t)num_fields);
+ field_metas->types = (guint8 *)wmem_alloc0_array(wmem_file_scope(), guint8, (size_t)num_fields);
+ mysql_set_field_metas(pinfo, conn_data, field_metas);
+ } else {
+ if (my_frame_data->stmt_id) {
+ stmt_data = (my_stmt_data_t *)wmem_tree_lookup32(conn_data->stmts, my_frame_data->stmt_id);
+ if (stmt_data != NULL) {
+ field_metas = &stmt_data->field_metas;
+ mysql_set_field_metas(pinfo, conn_data, field_metas);
+ }
+ }
+
+ }
+
if (tvb_reported_length_remaining(tvb, offset)) {
fle = tvb_get_fle(tvb, tree, offset, &extra, NULL);
proto_tree_add_uint64(tree, hf_mysql_extra, tvb, offset, fle, extra);
@@ -2611,8 +3223,18 @@ mysql_dissect_result_header(tvbuff_t *tvb, packet_info *pinfo, int offset,
}
if (num_fields) {
- mysql_set_conn_state(pinfo, conn_data, FIELD_PACKET);
- mysql_set_remaining_field_packet_count(conn_data, num_fields);
+ if (send_meta) {
+ mysql_set_conn_state(pinfo, conn_data, FIELD_PACKET);
+ mysql_set_remaining_field_packet_count(pinfo, conn_data, num_fields);
+ } else {
+ mysql_set_remaining_field_packet_count(pinfo, conn_data, 0);
+ if (conn_data->clnt_caps_ext & MYSQL_CAPS_DE) {
+ mysql_set_conn_state(pinfo, conn_data, ROW_PACKET);
+ } else {
+ /** Intermediate EOF follow **/
+ mysql_set_conn_state(pinfo, conn_data, FIELD_PACKET);
+ }
+ }
} else {
mysql_set_conn_state(pinfo, conn_data, ROW_PACKET);
}
@@ -2659,9 +3281,12 @@ mysql_field_add_lestring(tvbuff_t *tvb, int offset, proto_tree *tree, int field)
static int
-mysql_dissect_field_packet(tvbuff_t *tvb, int offset, proto_tree *tree, mysql_conn_data_t *conn_data _U_)
+mysql_dissect_field_packet(tvbuff_t *tvb, proto_item *pi _U_, int offset, proto_tree *tree, packet_info *pinfo _U_, mysql_conn_data_t *conn_data, const mysql_frame_data_t *my_frame_data)
{
+ guint8 fld_type;
+ guint16 fld_flag;
int length = tvb_reported_length(tvb);
+ mysql_state_t current_state = my_frame_data->state;
/* Are these fields optional? a trace suggests they are...*/
offset = mysql_field_add_lestring(tvb, offset, tree, hf_mysql_fld_catalog);
@@ -2673,6 +3298,28 @@ mysql_dissect_field_packet(tvbuff_t *tvb, int offset, proto_tree *tree, mysql_co
offset = mysql_field_add_lestring(tvb, offset, tree, hf_mysql_fld_org_table);
offset = mysql_field_add_lestring(tvb, offset, tree, hf_mysql_fld_name);
offset = mysql_field_add_lestring(tvb, offset, tree, hf_mysql_fld_org_name);
+
+ // mariadb extended metadata infos
+ if (conn_data->mariadb_client_ext_caps & MARIADB_CAPS_EM
+ && conn_data->mariadb_server_ext_caps & MARIADB_CAPS_EM) {
+ guint64 extended_length;
+ proto_item *extended_tree = NULL;
+ proto_item *tf;
+ int fle;
+
+ fle = tvb_get_fle(tvb, tree, offset, &extended_length, NULL);
+ tf = proto_tree_add_item(tree, hf_mariadb_extmeta_data, tvb, offset, fle + (guint32) extended_length, ENC_NA);
+ extended_tree = proto_item_add_subtree(tf, ett_extmeta_data);
+ proto_tree_add_uint64(tf, hf_mariadb_extmeta_length, tvb, offset, fle, extended_length);
+ offset += fle;
+
+ while (extended_length > 0) {
+ length = add_extended_meta_entry_to_tree(tvb, pinfo, extended_tree, offset);
+ offset += length;
+ extended_length -= length;
+ }
+ }
+
offset +=1; /* filler */
proto_tree_add_item(tree, hf_mysql_fld_charsetnr, tvb, offset, 2, ENC_LITTLE_ENDIAN);
@@ -2682,9 +3329,11 @@ mysql_dissect_field_packet(tvbuff_t *tvb, int offset, proto_tree *tree, mysql_co
offset += 4; /* length */
proto_tree_add_item(tree, hf_mysql_fld_type, tvb, offset, 1, ENC_NA);
+ fld_type = tvb_get_guint8(tvb, offset);
offset += 1; /* type */
proto_tree_add_bitmask_with_flags(tree, tvb, offset, hf_mysql_fld_flags, ett_field_flags, mysql_fld_flags, ENC_LITTLE_ENDIAN, BMT_NO_APPEND);
+ fld_flag = tvb_get_letohs(tvb, offset);
offset += 2; /* flags */
proto_tree_add_item(tree, hf_mysql_fld_decimals, tvb, offset, 1, ENC_NA);
@@ -2692,8 +3341,20 @@ mysql_dissect_field_packet(tvbuff_t *tvb, int offset, proto_tree *tree, mysql_co
offset += 2; /* filler */
+ if (current_state == FIELD_PACKET || current_state == PREPARED_FIELDS) {
+ if (my_frame_data->field_metas.count) {
+ guint64 fieldpos = my_frame_data->field_metas.count - my_frame_data->remaining_field_packet_count;
+ if (fieldpos >= my_frame_data->field_metas.count) {
+ expert_add_info_format(pinfo, tree, &ei_mysql_invalid_length, "Invalid length: %" G_GUINT64_FORMAT, fieldpos);
+ return tvb_reported_length_remaining(tvb, 0);
+ }
+ my_frame_data->field_metas.types[fieldpos] = fld_type;
+ my_frame_data->field_metas.flags[fieldpos] = fld_flag;
+ }
+ }
+
/* default (Only use for show fields) */
- if (tree && tvb_reported_length_remaining(tvb, offset) > 0) {
+ if (tvb_reported_length_remaining(tvb, offset) > 0) {
offset = mysql_field_add_lestring(tvb, offset, tree, hf_mysql_fld_default);
}
return offset;
@@ -2701,7 +3362,7 @@ mysql_dissect_field_packet(tvbuff_t *tvb, int offset, proto_tree *tree, mysql_co
static int
-mysql_dissect_row_packet(tvbuff_t *tvb, int offset, proto_tree *tree)
+mysql_dissect_text_row_packet(tvbuff_t *tvb, int offset, proto_tree *tree)
{
while (tvb_reported_length_remaining(tvb, offset) > 0) {
offset = mysql_field_add_lestring(tvb, offset, tree, hf_mysql_row_text);
@@ -2710,48 +3371,395 @@ mysql_dissect_row_packet(tvbuff_t *tvb, int offset, proto_tree *tree)
return offset;
}
+static int
+mysql_dissect_binary_row_packet(tvbuff_t *tvb, packet_info *pinfo, proto_item *pi, int offset, proto_tree *tree, mysql_conn_data_t *conn_data _U_, const mysql_frame_data_t *my_frame_data)
+{
+ int fieldpos;
+ if (my_frame_data->field_metas.count) {
+
+ /* null bitmap */
+ int nfields = my_frame_data->field_metas.count;
+ int null_len = (nfields + 9) / 8;
+
+ char *null_buffer;
+ null_buffer = (guint8 *)wmem_alloc(wmem_packet_scope(), (size_t)null_len + 1);
+ tvb_get_raw_bytes_as_string(tvb, offset, null_buffer, (size_t)null_len + 1);
+ proto_tree_add_bytes_with_length(tree, hf_mysql_null_buffer, tvb, offset, null_len, null_buffer, null_len);
+ offset += null_len;
+
+ for (fieldpos = 0; fieldpos < nfields; fieldpos++) {
+ if ((null_buffer[(fieldpos + 2) / 8] & (1 << ((fieldpos + 2) % 8))) == 0) {
+ // data is not null
+ if (tvb_reported_length_remaining(tvb, offset) > 0) {
+ if (!mysql_dissect_binary_row_value(tvb, pinfo, pi, &offset, tree, my_frame_data->field_metas.types[fieldpos], my_frame_data->field_metas.flags[fieldpos]))
+ break;
+ }
+ } else {
+ proto_tree_add_item(tree, hf_mysql_exec_field_null, tvb, offset, 0, ENC_NA);
+ }
+ }
+ }
+
+
+ return offset;
+}
+
+static char
+mysql_dissect_binary_row_value(tvbuff_t *tvb, packet_info *pinfo _U_, proto_item *pi _U_, int *offset, proto_item *tree, guint8 field_type, guint16 field_flag)
+{
+ int dissector_index = 0;
+ guint8 param_unsigned = 0;
+ if (field_flag & MYSQL_FLD_UNSIGNED_FLAG) {
+ param_unsigned = 1;
+ }
+
+ while (mysql_exec_dissectors[dissector_index].dissector != NULL) {
+ if (mysql_exec_dissectors[dissector_index].type == field_type &&
+ mysql_exec_dissectors[dissector_index].unsigned_flag == param_unsigned) {
+ mysql_exec_dissectors[dissector_index].dissector(tvb, offset, tree);
+ return 1;
+ }
+ dissector_index++;
+ }
+ return 0;
+}
static int
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;
+ my_metadata_list_t *field_metas;
+ my_metadata_list_t *param_metas;
+
guint32 stmt_id;
- int flagsize;
+ guint16 stmt_num_fields;
+ guint16 stmt_num_params;
- /* 0, marker for OK packet */
- offset += 1;
- proto_tree_add_item(tree, hf_mysql_stmt_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
- stmt_id = tvb_get_letohl(tvb, offset);
+ proto_tree_add_item_ret_uint(tree, hf_mysql_stmt_id, tvb, offset, 4, ENC_LITTLE_ENDIAN, &stmt_id);
+ mysql_set_prepared_stmt_id(pinfo, conn_data, stmt_id);
offset += 4;
proto_tree_add_item(tree, hf_mysql_num_fields, tvb, offset, 2, ENC_LITTLE_ENDIAN);
- conn_data->stmt_num_fields = tvb_get_letohs(tvb, offset);
+ stmt_num_fields = tvb_get_letohs(tvb, offset);
offset += 2;
proto_tree_add_item(tree, hf_mysql_num_params, tvb, offset, 2, ENC_LITTLE_ENDIAN);
- conn_data->stmt_num_params = tvb_get_letohs(tvb, offset);
- stmt_data = wmem_new(wmem_file_scope(), struct my_stmt_data);
- stmt_data->nparam = conn_data->stmt_num_params;
- flagsize = (int)(sizeof(guint8) * stmt_data->nparam);
- stmt_data->param_flags = (guint8 *)wmem_alloc(wmem_file_scope(), flagsize);
- memset(stmt_data->param_flags, 0, flagsize);
- stmt_data->param_types = (guint8 *)wmem_alloc(wmem_file_scope(), flagsize);
- memset(stmt_data->param_types, 0, flagsize);
-
- wmem_tree_insert32(conn_data->stmts, stmt_id, stmt_data);
+ stmt_num_params = tvb_get_letohs(tvb, offset);
+
+ if (!pinfo->fd->visited) {
+#if 0
+ /* XXX: Can statement ids be reused on the same connection?
+ * If so, the tree should be a multimap or similar. If not,
+ * there should be an expert info if we see a reused one.
+ */
+ if (wmem_tree_lookup32(conn_data->stmts, stmt_id) != NULL) {
+ /* Expert Info? */
+ }
+#endif
+
+ stmt_data = wmem_new(wmem_file_scope(), struct my_stmt_data);
+ param_metas = wmem_new(wmem_file_scope(), my_metadata_list_t);
+ param_metas->count = stmt_num_params;
+ param_metas->flags = (guint16 *)wmem_alloc0_array(wmem_file_scope(), guint16, param_metas->count);
+ param_metas->types = (guint8 *)wmem_alloc0_array(wmem_file_scope(), guint8, param_metas->count);
+ stmt_data->param_metas = *param_metas;
+
+ field_metas = wmem_new(wmem_file_scope(), my_metadata_list_t);
+ field_metas->count = stmt_num_fields;
+ field_metas->flags = (guint16 *)wmem_alloc0_array(wmem_file_scope(), guint16, field_metas->count);
+ field_metas->types = (guint8 *)wmem_alloc0_array(wmem_file_scope(), guint8, field_metas->count);
+ stmt_data->field_metas = *field_metas;
+
+ wmem_tree_insert32(conn_data->stmts, stmt_id, stmt_data);
+
+ mysql_set_field_metas(pinfo, conn_data, field_metas);
+ }
+
offset += 2;
/* Filler */
offset += 1;
proto_tree_add_item(tree, hf_mysql_num_warn, tvb, offset, 2, ENC_LITTLE_ENDIAN);
- if (conn_data->stmt_num_params > 0)
+ if (stmt_num_params > 0) {
+ mysql_set_remaining_field_packet_count(pinfo, conn_data, stmt_num_params);
mysql_set_conn_state(pinfo, conn_data, PREPARED_PARAMETERS);
- else if (conn_data->stmt_num_fields > 0)
+ } else if (stmt_num_fields > 0) {
+ mysql_set_remaining_field_packet_count(pinfo, conn_data, stmt_num_fields);
mysql_set_conn_state(pinfo, conn_data, PREPARED_FIELDS);
- else
+ } else {
+ mysql_set_remaining_field_packet_count(pinfo, conn_data, 0);
mysql_set_conn_state(pinfo, conn_data, REQUEST);
+ }
return offset + tvb_reported_length_remaining(tvb, offset);
}
+/**
+ Enumeration type for the different types of log events.
+*/
+enum Log_event_type {
+ /**
+ Every time you add a type, you have to
+ - Assign it a number explicitly. Otherwise it will cause trouble
+ if a event type before is deprecated and removed directly from
+ the enum.
+ - Fix Format_description_event::Format_description_event().
+ */
+ UNKNOWN_EVENT = 0,
+ /*
+ Deprecated since mysql 8.0.2. It is just a placeholder,
+ should not be used anywhere else.
+ */
+ START_EVENT_V3 = 1,
+ QUERY_EVENT = 2,
+ STOP_EVENT = 3,
+ ROTATE_EVENT = 4,
+ INTVAR_EVENT = 5,
+
+ SLAVE_EVENT = 7,
+
+ APPEND_BLOCK_EVENT = 9,
+ DELETE_FILE_EVENT = 11,
+
+ RAND_EVENT = 13,
+ USER_VAR_EVENT = 14,
+ FORMAT_DESCRIPTION_EVENT = 15,
+ XID_EVENT = 16,
+ BEGIN_LOAD_QUERY_EVENT = 17,
+ EXECUTE_LOAD_QUERY_EVENT = 18,
+
+ TABLE_MAP_EVENT = 19,
+
+ /**
+ The V1 event numbers are used from 5.1.16 until mysql-5.6.
+ */
+ WRITE_ROWS_EVENT_V1 = 23,
+ UPDATE_ROWS_EVENT_V1 = 24,
+ DELETE_ROWS_EVENT_V1 = 25,
+
+ /**
+ Something out of the ordinary happened on the master
+ */
+ INCIDENT_EVENT = 26,
+
+ /**
+ Heartbeat event to be send by master at its idle time
+ to ensure master's online status to slave
+ */
+ HEARTBEAT_LOG_EVENT = 27,
+
+ /**
+ In some situations, it is necessary to send over ignorable
+ data to the slave: data that a slave can handle in case there
+ is code for handling it, but which can be ignored if it is not
+ recognized.
+ */
+ IGNORABLE_LOG_EVENT = 28,
+ ROWS_QUERY_LOG_EVENT = 29,
+
+ /** Version 2 of the Row events */
+ WRITE_ROWS_EVENT = 30,
+ UPDATE_ROWS_EVENT = 31,
+ DELETE_ROWS_EVENT = 32,
+
+ GTID_LOG_EVENT = 33,
+ ANONYMOUS_GTID_LOG_EVENT = 34,
+
+ PREVIOUS_GTIDS_LOG_EVENT = 35,
+
+ TRANSACTION_CONTEXT_EVENT = 36,
+
+ VIEW_CHANGE_EVENT = 37,
+
+ /* Prepared XA transaction terminal event similar to Xid */
+ XA_PREPARE_LOG_EVENT = 38,
+
+ /**
+ Extension of UPDATE_ROWS_EVENT, allowing partial values according
+ to binlog_row_value_options.
+ */
+ PARTIAL_UPDATE_ROWS_EVENT = 39,
+
+ TRANSACTION_PAYLOAD_EVENT = 40,
+
+ HEARTBEAT_LOG_EVENT_V2 = 41,
+ /**
+ Add new events here - right above this comment!
+ Existing events (except ENUM_END_EVENT) should never change their numbers
+ */
+ ENUM_END_EVENT /* end marker */
+};
+
+static const value_string mysql_binlog_event_type_vals[] = {
+ {0, "Unknown"},
+
+ {1, "START_EVENT_V3"},
+ {2, "Query"},
+ {3, "Stop"},
+ {4, "Rotate"},
+ {5, "Intvar"},
+
+ {7, "SLAVE_EVENT"},
+
+ {9, "Append_block"},
+ {11, "Delete_file"},
+
+ {13, "RAND"},
+ {14, "User_var"},
+ {15, "Format_desc"},
+ {16, "Xid"},
+ {17, "Begin_load_query"},
+ {18, "Execute_load_query"},
+
+ {19, "Table_map"},
+
+ {23, "Write_rows_v1"},
+ {24, "Update_rows_v1"},
+ {25, "Delete_rows_v1"},
+
+ {26, "Incident"},
+
+ {27, "Heartbeat"},
+
+ {28, "Ignorable"},
+ {29, "Rows_query"},
+
+ {30, "Write_rows"},
+ {31, "Update_rows"},
+ {32, "Delete_rows"},
+
+ {33, "Gtid"},
+ {34, "Anonymous_Gtid"},
+
+ {35, "Previous_gtids"},
+
+ {36, "Transaction_context"},
+
+ {37, "View_change"},
+
+ {38, "XA_prepare"},
+
+ {39, "Update_rows_partial"},
+
+ {40, "Transaction_payload"},
+
+ {41, "Heartbeat_v2"},
+ {0, NULL},
+};
+
+static int
+mysql_dissect_binlog_event_heartbeat_v2(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree)
+{
+ int fle;
+ guint64 num;
+ proto_item *item, *parent_item;
+ proto_item *hb_v2_tree, *hb_v2_subtree;
+
+ col_append_str(pinfo->cinfo, COL_INFO, "Heartbeat_v2 ");
+ col_set_fence(pinfo->cinfo, COL_INFO);
+ item = proto_tree_add_item(tree, hf_mysql_binlog_event_heartbeat_v2, tvb, offset, -1, ENC_NA);
+ hb_v2_tree = proto_item_add_subtree(item, ett_binlog_event);
+
+ // OTW_HB_LOG_FILENAME_FIELD
+ parent_item = proto_tree_add_item(hb_v2_tree, hf_mysql_binlog_event_heartbeat_v2_otw, tvb, offset, -1, ENC_NA);
+ hb_v2_subtree = proto_item_add_subtree(parent_item, ett_binlog_event_hb_v2);
+
+ item = proto_tree_add_item(hb_v2_subtree, hf_mysql_binlog_event_heartbeat_v2_otw_type, tvb, offset, 1, ENC_NA);
+ proto_item_append_text(item, " (OTW_HB_LOG_FILENAME_FIELD)");
+ proto_item_append_text(parent_item, " OTW_HB_LOG_FILENAME_FIELD");
+ offset += 1;
+
+ fle = tvb_get_fle(tvb, hb_v2_subtree, offset, &num, ENC_NA);
+ offset += fle;
+
+ proto_tree_add_item(hb_v2_subtree, hf_mysql_binlog_hb_event_filename, tvb, offset, (gint) num, ENC_ASCII);
+ offset += (gint)num;
+
+ // OTW_HB_LOG_POSITION_FIELD
+ parent_item = proto_tree_add_item(hb_v2_tree, hf_mysql_binlog_event_heartbeat_v2_otw, tvb, offset, -1, ENC_NA);
+ hb_v2_subtree = proto_item_add_subtree(parent_item, ett_binlog_event_hb_v2);
+ item = proto_tree_add_item(hb_v2_subtree, hf_mysql_binlog_event_heartbeat_v2_otw_type, tvb, offset, 1, ENC_NA);
+ proto_item_append_text(item, " (OTW_HB_LOG_POSITION_FIELD)");
+ proto_item_append_text(parent_item, " OTW_HB_LOG_POSITION_FIELD");
+ offset += 1;
+
+ fle = tvb_get_fle(tvb, hb_v2_subtree, offset, &num, NULL);
+ offset += fle;
+
+ fle = tvb_get_fle(tvb, hb_v2_subtree, offset, &num, NULL);
+ proto_tree_add_uint64(hb_v2_subtree, hf_mysql_binlog_hb_event_log_position, tvb, offset, fle, num);
+ offset += fle;
+
+ // OTW_HB_LOG_FILENAME_FIELD
+ parent_item = proto_tree_add_item(hb_v2_tree, hf_mysql_binlog_event_heartbeat_v2_otw, tvb, offset, -1, ENC_NA);
+ hb_v2_subtree = proto_item_add_subtree(parent_item, ett_binlog_event_hb_v2);
+ item = proto_tree_add_item(hb_v2_subtree, hf_mysql_binlog_event_heartbeat_v2_otw_type, tvb, offset, 1, ENC_NA);
+ proto_item_append_text(item, " (OTW_HB_HEADER_END_MARK)");
+ proto_item_append_text(parent_item, " OTW_HB_HEADER_END_MARK");
+ offset += 1;
+ return offset;
+}
+
+static int
+mysql_dissect_binlog_event_header(tvbuff_t *tvb, int offset, proto_tree *tree, proto_item *pi)
+{
+ proto_tree_add_item(tree, hf_mysql_binlog_event_header_timestamp, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ proto_tree_add_item(tree, hf_mysql_binlog_event_header_event_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ proto_item_append_text(pi, ": %s", val_to_str(tvb_get_guint8(tvb, offset), mysql_binlog_event_type_vals, "Unknown event type: %d"));
+ offset += 1;
+
+ proto_tree_add_item(tree, hf_mysql_binlog_event_header_server_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ proto_tree_add_item(tree, hf_mysql_binlog_event_header_event_size, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ proto_tree_add_item(tree, hf_mysql_binlog_event_header_log_position, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ proto_tree_add_item(tree, hf_mysql_binlog_event_header_flags, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ offset += 2;
+
+ return offset;
+}
+
+static int
+mysql_dissect_binlog_event_packet(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, proto_item *pi)
+{
+ guint8 event_type;
+ int fle;
+
+ col_append_str(pinfo->cinfo, COL_INFO, "Binlog Event " );
+ col_set_fence(pinfo->cinfo, COL_INFO);
+
+ event_type = tvb_get_guint8(tvb, offset + 4);
+ offset = mysql_dissect_binlog_event_header(tvb, offset, tree, pi);
+
+ switch (event_type) {
+ case HEARTBEAT_LOG_EVENT_V2:
+ offset = mysql_dissect_binlog_event_heartbeat_v2(tvb, pinfo, offset, tree);
+ break;
+ default:
+ fle = tvb_reported_length_remaining(tvb, offset);
+ offset += fle - 4;
+ break;
+ }
+ // checksum
+ proto_tree_add_item(tree, hf_mysql_binlog_event_checksum, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ return offset;
+}
+
+static int
+mysql_dissect_eof(tvbuff_t *tvb, packet_info *pinfo _U_, proto_item *pi _U_, int offset, proto_tree *tree, mysql_conn_data_t *conn_data _U_)
+{
+ guint16 server_status = 0;
+ proto_tree_add_item(tree, hf_mysql_num_warn, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ offset += 2;
+ offset = mysql_dissect_server_status(tvb, offset, tree, &server_status);
+ return offset + tvb_reported_length_remaining(tvb, offset);
+}
static int
mysql_dissect_auth_switch_request(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, mysql_conn_data_t *conn_data _U_)
@@ -2762,19 +3770,26 @@ mysql_dissect_auth_switch_request(tvbuff_t *tvb, packet_info *pinfo, int offset,
col_set_fence(pinfo->cinfo, COL_INFO);
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);
- offset += 1;
+ if (conn_data->clnt_caps_ext & MYSQL_CAPS_PA) {
+ /* https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase_packets_protocol_auth_switch_request.html */
- /* name */
- lenstr = my_tvb_strsize(tvb, offset);
- proto_tree_add_item(tree, hf_mysql_auth_switch_request_name, tvb, offset, lenstr, ENC_ASCII|ENC_NA);
- offset += lenstr;
+ /* name */
+ lenstr = my_tvb_strsize(tvb, offset);
+ proto_tree_add_item(tree, hf_mysql_auth_switch_request_name, tvb, offset, lenstr, ENC_ASCII);
+ conn_data->auth_method = tvb_get_string_enc(wmem_file_scope(), tvb, offset, lenstr, ENC_ASCII);
+ offset += lenstr;
- /* Data */
- lenstr = my_tvb_strsize(tvb, offset);
- proto_tree_add_item(tree, hf_mysql_auth_switch_request_data, tvb, offset, lenstr, ENC_NA);
- offset += lenstr;
+ /* Data */
+ lenstr = my_tvb_strsize(tvb, offset);
+ proto_tree_add_item(tree, hf_mysql_auth_switch_request_data, tvb, offset, lenstr, ENC_NA);
+ offset += lenstr;
+ } else {
+ /* https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase_packets_protocol_old_auth_switch_request.html */
+
+ /* Status (Always 0xfe) */
+ proto_tree_add_item(tree, hf_mysql_auth_switch_request_status, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ offset += 1;
+ }
return offset + tvb_reported_length_remaining(tvb, offset);
@@ -2792,9 +3807,165 @@ mysql_dissect_auth_switch_response(tvbuff_t *tvb, packet_info *pinfo, int offset
proto_tree_add_item(tree, hf_mysql_auth_switch_response_data, tvb, offset, lenstr, ENC_NA);
offset += lenstr;
+ if (g_strcmp0(conn_data->auth_method,"caching_sha2_password") == 0) {
+ mysql_set_conn_state(pinfo, conn_data, AUTH_SHA2);
+ }
+
return offset + tvb_reported_length_remaining(tvb, offset);
}
+
+/*
+ caching_sha2_password authentication state
+
+ Doc: https://dev.mysql.com/doc/dev/mysql-server/latest/page_caching_sha2_authentication_exchanges.html
+*/
+static int
+mysql_dissect_auth_sha2(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, mysql_conn_data_t *conn_data _U_)
+{
+ col_set_str(pinfo->cinfo, COL_INFO, "Caching_sha2_password " );
+ col_set_fence(pinfo->cinfo, COL_INFO);
+
+ if (tvb_reported_length_remaining(tvb,offset) == 2)
+ offset++;
+ char *auth2_state;
+ guint8 c = tvb_get_guint8(tvb, offset);
+ switch (c) {
+ case 2:
+ auth2_state = "request_public_key";
+ mysql_set_conn_state(pinfo, conn_data, AUTH_PUBKEY);
+ break;
+ case 3:
+ auth2_state = "fast_auth_success";
+ break;
+ case 4:
+ auth2_state = "perform_full_authentication";
+ mysql_set_conn_state(pinfo, conn_data, AUTH_SHA2);
+ break;
+ default:
+ auth2_state = "unknown";
+ }
+ col_append_str(pinfo->cinfo, COL_INFO, auth2_state);
+ proto_tree_add_string(tree, hf_mysql_sha2_auth, tvb, offset, 1, auth2_state);
+ offset++;
+
+ return offset + tvb_reported_length_remaining(tvb, offset);
+}
+
+/*
+ Public key as requested during caching_sha2_password authentication
+*/
+static int
+mysql_dissect_pubkey(tvbuff_t *tvb, packet_info *pinfo, int offset,
+ proto_tree *tree, mysql_conn_data_t *conn_data _U_, proto_item *pi _U_, mysql_state_t current_state _U_)
+{
+ tvbuff_t *next_tvb;
+ col_set_str(pinfo->cinfo, COL_INFO, "Public key " );
+ col_set_fence(pinfo->cinfo, COL_INFO);
+ mysql_set_conn_state(pinfo, conn_data, AUTH_SHA2_RESPONSE);
+
+ offset++;
+ gint len = tvb_reported_length_remaining(tvb, offset) - 1;
+ next_tvb = tvb_new_subset_length(tvb, offset, len);
+ add_new_data_source(pinfo, next_tvb, "public key");
+ proto_tree_add_item(tree, hf_mysql_pubkey, tvb, offset, len, ENC_ASCII);
+ offset += len;
+
+ return offset + tvb_reported_length_remaining(tvb,offset);
+}
+
+/*
+ If caching_sha2_password authentication is used over a non-secure channel
+ then the authentication response (password) is encrypted with a RSA public key.
+
+ This means that we can't dissect this response without access to the RSA private key.
+*/
+static int
+mysql_dissect_sha2_response(tvbuff_t *tvb, packet_info *pinfo _U_, int offset,
+ proto_tree *tree _U_, mysql_conn_data_t *conn_data _U_, proto_item *pi _U_, mysql_state_t current_state _U_)
+{
+ gint len = tvb_reported_length_remaining(tvb, offset);
+ proto_tree_add_item(tree, hf_mysql_sha2_response, tvb, offset, len, ENC_NA);
+ return offset + tvb_reported_length_remaining(tvb, offset);
+}
+
+static int
+mysql_dissect_clone_request(tvbuff_t *tvb _U_, packet_info *pinfo _U_, int offset _U_,
+ proto_tree *tree _U_, mysql_conn_data_t *conn_data _U_, proto_item *pi _U_, mysql_state_t current_state _U_)
+{
+ guint8 req_code = tvb_get_guint8(tvb, offset);
+ switch (req_code) {
+ case MYSQL_CLONE_COM_INIT:
+ case MYSQL_CLONE_COM_ATTACH:
+ case MYSQL_CLONE_COM_REINIT:
+ case MYSQL_CLONE_COM_EXECUTE:
+ case MYSQL_CLONE_COM_ACK:
+ col_append_fstr(pinfo->cinfo, COL_INFO, " %s", val_to_str(req_code, mysql_clone_command_vals, "Unknown clone request: %d"));
+ proto_tree_add_item(tree, hf_mysql_clone_command_code, tvb, offset, 1, ENC_NA);
+ break;
+ case MYSQL_CLONE_COM_EXIT:
+ col_append_fstr(pinfo->cinfo, COL_INFO, " %s", val_to_str(req_code, mysql_clone_command_vals, "Unknown clone request: %d"));
+ proto_tree_add_item(tree, hf_mysql_clone_command_code, tvb, offset, 1, ENC_NA);
+ mysql_set_conn_state(pinfo, conn_data, CLONE_EXIT);
+ break;
+ default:
+ col_append_str(pinfo->cinfo, COL_INFO, " Unknown Clone Command Code") ;
+ /* TODO, Set error etc */
+ }
+ offset++;
+
+ return offset;
+}
+
+static int
+mysql_dissect_clone_response(tvbuff_t *tvb, packet_info *pinfo, int offset,
+ proto_tree *tree, mysql_conn_data_t *conn_data, proto_item *pi _U_, mysql_state_t current_state)
+{
+ guint8 resp_code = tvb_get_guint8(tvb, offset);
+ switch (resp_code) {
+ case MYSQL_CLONE_COM_RES_LOCS:
+ case MYSQL_CLONE_COM_RES_DATA_DESC:
+ case MYSQL_CLONE_COM_RES_DATA:
+ case MYSQL_CLONE_COM_RES_PLUGIN:
+ case MYSQL_CLONE_COM_RES_CONFIG:
+ case MYSQL_CLONE_COM_RES_COLLATION:
+ case MYSQL_CLONE_COM_RES_PLUGIN_V2:
+ case MYSQL_CLONE_COM_RES_CONFIG_V3:
+ case MYSQL_CLONE_COM_RES_COMPLETE:
+ if (current_state == CLONE_EXIT)
+ mysql_set_conn_state(pinfo, conn_data, REQUEST);
+ /* fall through */
+ case MYSQL_CLONE_COM_RES_ERROR:
+ col_append_fstr(pinfo->cinfo, COL_INFO, " %s", val_to_str(resp_code, mysql_clone_response_vals, "unknown clone request: %d"));
+ proto_tree_add_item(tree, hf_mysql_clone_response_code, tvb, offset, 1, ENC_NA);
+ break;
+ default:
+ col_append_str(pinfo->cinfo, COL_INFO, " Unknown Clone Response Code") ;
+ /* TODO, Set error etc */
+ }
+ offset++;
+
+ return offset;
+}
+
+/*
+ This is the payload (file content) of the LOAD DATA LOCAL INFILE
+ https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_query_response_local_infile_data.html
+*/
+static int
+mysql_dissect_loaddata(tvbuff_t *tvb, packet_info *pinfo _U_, int offset, proto_tree *tree, mysql_conn_data_t *conn_data _U_)
+{
+ col_append_str(pinfo->cinfo, COL_INFO, " LOCAL INFILE Payload");
+ col_set_fence(pinfo->cinfo, COL_INFO);
+ gint lenstr = tvb_reported_length_remaining(tvb, offset);
+ tvbuff_t *next_tvb = tvb_new_subset_length(tvb, offset, lenstr);
+ add_new_data_source(pinfo, next_tvb, "local infile");
+ proto_tree_add_item(tree, hf_mysql_loaddata_payload, tvb, offset, lenstr, ENC_NA);
+ offset += lenstr;
+ mysql_set_conn_state(pinfo, conn_data, REQUEST);
+ return offset;
+}
+
/*
get length of string in packet buffer
@@ -2837,18 +4008,19 @@ my_tvb_strsize(tvbuff_t *tvb, int offset)
read FLE from packet buffer and store its value and ISNULL flag
in caller provided variables
+ Docs: https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_integers.html
+
RETURN VALUE
length of FLE
*/
static int
-tvb_get_fle(tvbuff_t *tvb, proto_tree *tree, int offset, guint64 *res, guint8 *is_null)
+tvb_get_fle(tvbuff_t *tvb, proto_tree *tree _U_, int offset, guint64 *res, guint8 *is_null)
{
- guint32 prefix;
- proto_item* ti;
- int num_bytes, len_len;
+ guint8 prefix;
+ int num_bytes;
guint64 length;
- ti = proto_tree_add_item_ret_uint(tree, hf_mysql_prefix, tvb, offset, 1, ENC_BIG_ENDIAN, &prefix);
+ prefix = tvb_get_guint8(tvb, offset);
if (is_null) {
*is_null = 0;
@@ -2860,57 +4032,48 @@ tvb_get_fle(tvbuff_t *tvb, proto_tree *tree, int offset, guint64 *res, guint8 *i
*res = 0;
if (is_null)
*is_null = 1;
- proto_item_append_text(ti, "(NULL)");
return 1;
- case 252:
- proto_item_append_text(ti, " Length in following 2 bytes");
+ case 252: // 0xFC
num_bytes = 3;
- len_len = 2;
offset++;
+ length = (guint64)tvb_get_guint16(tvb, offset, ENC_LITTLE_ENDIAN);
break;
- case 253:
- proto_item_append_text(ti, " Length in following 4 bytes");
- num_bytes = 5;
- len_len = 4;
+ case 253: // 0xFD
+ num_bytes = 4;
offset++;
+ length = (guint64)tvb_get_guint24(tvb, offset, ENC_LITTLE_ENDIAN);
break;
- case 254:
- proto_item_append_text(ti, " Length in following 8 bytes");
+ case 254: // 0xFE
num_bytes = 9;
- len_len = 8;
offset++;
+ length = tvb_get_guint64(tvb, offset, ENC_LITTLE_ENDIAN);
break;
default:
- len_len = 1;
num_bytes = 1;
+ length = tvb_get_guint8(tvb, offset);
}
- proto_tree_add_item_ret_uint64(tree, hf_mysql_length, tvb, offset, len_len, ENC_LITTLE_ENDIAN, &length);
if (res) {
*res = length;
}
return num_bytes;
}
+static guint
+get_mysql_compressed_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
+{
+ /* Compressed packet header: compressed length (3) + sequence number (1)
+ * + uncompressed packet length (3) */
+ guint len = 7 + tvb_get_letoh24(tvb, offset);
+ return len;
+}
+
/* dissector helper: length of PDU */
static guint
-get_mysql_pdu_len(packet_info *pinfo, tvbuff_t *tvb, int offset, void *data _U_)
+get_mysql_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
{
/* Regular packet header: length (3) + sequence number (1) */
- conversation_t *conversation;
- mysql_conn_data_t *conn_data;
- guint len = 4 + tvb_get_letoh24(tvb, offset);
-
- conversation = find_conversation_pinfo(pinfo, 0);
- if (conversation) {
- conn_data = (mysql_conn_data_t *)conversation_get_proto_data(conversation, proto_mysql);
- if (conn_data && conn_data->compressed_state == MYSQL_COMPRESS_ACTIVE &&
- pinfo->num > conn_data->frame_start_compressed) {
- /* Compressed packet header includes uncompressed packet length (3) */
- len += 3;
- }
- }
-
+ guint len = 4 + tvb_get_letoh24(tvb, offset);
return len;
}
@@ -2953,15 +4116,13 @@ 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;
+ mysql_frame_data_p->resultset_fmt = conn_data->resultset_fmt;
+ mysql_frame_data_p->stmt_id = conn_data->stmt_id;
+ mysql_frame_data_p->remaining_field_packet_count = conn_data->remaining_field_packet_count;
+ mysql_frame_data_p->field_metas = conn_data->field_metas;
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)) {
- if (conn_data->compressed_state == MYSQL_COMPRESS_ACTIVE) {
- offset = mysql_dissect_compressed_header(tvb, offset, tree);
- }
- }
-
ti = proto_tree_add_item(tree, proto_mysql, tvb, offset, -1, ENC_NA);
mysql_tree = proto_item_add_subtree(ti, ett_mysql);
proto_tree_add_item(mysql_tree, hf_mysql_packet_length, tvb, offset, 3, ENC_LITTLE_ENDIAN);
@@ -2986,7 +4147,7 @@ dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat
if (tree) {
pi = proto_tree_add_debug_text(mysql_tree, "conversation: %p", conversation);
proto_item_set_generated(pi);
- pi = proto_tree_add_debug_text(mysql_tree, "generation: %" G_GINT64_MODIFIER "d", generation);
+ pi = proto_tree_add_debug_text(mysql_tree, "generation: %" PRId64, generation);
proto_item_set_generated(pi);
pi = proto_tree_add_debug_text(mysql_tree, "conn state: %s (%u)",
val_to_str(conn_state_in, state_vals, "Unknown (%u)"),
@@ -3005,23 +4166,40 @@ dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat
if (packet_number == 0 && mysql_frame_data_p->state == UNDEFINED) {
col_set_str(pinfo->cinfo, COL_INFO, "Server Greeting ");
offset = mysql_dissect_greeting(tvb, pinfo, offset, mysql_tree, conn_data);
+ } else if ((mysql_frame_data_p->state == CLONE_ACTIVE) || (mysql_frame_data_p->state == CLONE_EXIT)) {
+ col_set_str(pinfo->cinfo, COL_INFO, "Clone Response");
+ offset = mysql_dissect_clone_response(tvb, pinfo, offset, mysql_tree, conn_data, ti, mysql_frame_data_p->state);
+ } else if (mysql_frame_data_p->state == AUTH_PUBKEY) {
+ col_set_str(pinfo->cinfo, COL_INFO, "Public key ");
+ offset = mysql_dissect_pubkey(tvb, pinfo, offset, mysql_tree, conn_data, ti, mysql_frame_data_p->state);
} else {
col_set_str(pinfo->cinfo, COL_INFO, "Response ");
- offset = mysql_dissect_response(tvb, pinfo, offset, mysql_tree, conn_data, mysql_frame_data_p->state);
+ offset = mysql_dissect_response(tvb, pinfo, offset, mysql_tree, conn_data, ti, mysql_frame_data_p);
}
} else {
if (mysql_frame_data_p->state == LOGIN && (packet_number == 1 || (packet_number == 2 && is_tls))) {
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;
- }
+
+ // If both zlib and ZSTD flags are set then zlib is used.
+ if ((conn_data->srv_caps & MYSQL_CAPS_CP) && (conn_data->clnt_caps & MYSQL_CAPS_CP)) {
+ conn_data->frame_start_compressed = pinfo->num;
+ conn_data->compressed_state = MYSQL_COMPRESS_INIT;
+ conn_data->compressed_alg = MYSQL_COMPRESS_ALG_ZLIB;
+ } else if ((conn_data->srv_caps_ext & MYSQL_CAPS_ZS) && (conn_data->clnt_caps_ext & MYSQL_CAPS_ZS)) {
+ conn_data->frame_start_compressed = pinfo->num;
+ conn_data->compressed_state = MYSQL_COMPRESS_INIT;
+ conn_data->compressed_alg = MYSQL_COMPRESS_ALG_ZSTD;
}
+ } else if ((mysql_frame_data_p->state == CLONE_ACTIVE) || (mysql_frame_data_p->state == CLONE_EXIT)) {
+ col_set_str(pinfo->cinfo, COL_INFO, "Clone Request");
+ offset = mysql_dissect_clone_request(tvb, pinfo, offset, mysql_tree, conn_data, ti, mysql_frame_data_p->state);
+ } else if (mysql_frame_data_p->state == AUTH_SHA2_RESPONSE) {
+ col_set_str(pinfo->cinfo, COL_INFO, "Caching_sha2_password response");
+ offset = mysql_dissect_sha2_response(tvb, pinfo, offset, mysql_tree, conn_data, ti, mysql_frame_data_p->state);
} else {
col_set_str(pinfo->cinfo, COL_INFO, "Request");
- offset = mysql_dissect_request(tvb, pinfo, offset, mysql_tree, conn_data, mysql_frame_data_p->state);
+ offset = mysql_dissect_request(tvb, pinfo, offset, mysql_tree, conn_data, mysql_frame_data_p);
}
}
@@ -3043,12 +4221,171 @@ dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat
return tvb_reported_length(tvb);
}
+/* A helper function to reassemble MySQL decompressed PDUs on top of compressed
+ * packets. Decompressed PDUs may span multiple compressed packets:
+ * https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_compression_packet.html
+ * "The payload can be anything from a piece of a MySQL Packet to several MySQL
+ * Packets."
+ *
+ * Some of this error checking is likely unnecessary when dealing with PDUs
+ * that have been decompressed (would decompression really have succeeded),
+ * but this could be used later instead of tcp_dissect_pdus() for the
+ * uncompressed base case as well.
+ */
+static int
+dissect_mysql_decompressed_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
+{
+ tvbuff_t *volatile next_tvb;
+ volatile int offset = 0;
+ int offset_before;
+ unsigned int remaining, pdu_len;
+ while (tvb_reported_length_remaining(tvb, offset)) {
+ remaining = tvb_ensure_reported_length_remaining(tvb, offset);
+ if (remaining < 3) {
+ pinfo->desegment_len = 3 - remaining;
+ /* reassemble_streaming_data_and_call_subdissector()
+ * expects the remaining bytes of the fixed header
+ * instead of ONE_MORE_SEGMENT. */
+ return tvb_reported_length(tvb);
+ }
+
+ pdu_len = get_mysql_pdu_len(pinfo, tvb, offset, data);
+ if (pdu_len < MYSQL_HEADER_LENGTH) {
+ /* The length value overflowed when adding the
+ * fixed portion. */
+ show_reported_bounds_error(tvb, pinfo, tree);
+ }
+ if (remaining < pdu_len && pinfo->can_desegment) {
+ pinfo->desegment_offset = offset;
+ pinfo->desegment_len = pdu_len - remaining;
+ return tvb_reported_length(tvb);
+ }
+
+ next_tvb = tvb_new_subset_length(tvb, offset, pdu_len);
+ if (remaining < pdu_len && !pinfo->can_desegment) {
+ tvb_set_fragment(next_tvb);
+ }
+ TRY {
+ dissect_mysql_pdu(next_tvb, pinfo, tree, data);
+ }
+ CATCH_NONFATAL_ERRORS {
+ show_exception(tvb, pinfo, tree, EXCEPT_CODE, GET_MESSAGE);
+ /* We don't need to restore pinfo->current_proto,
+ * because MySQL doesn't call anything else.
+ */
+ }
+ ENDTRY;
+ offset_before = offset;
+ offset += pdu_len;
+ if (offset <= offset_before)
+ break;
+ }
+ return tvb_reported_length(tvb);
+}
+
+/*
+ * Decode a compressed packet
+ * https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_compression.html
+ */
+static int
+dissect_mysql_compressed_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
+{
+ proto_tree *mysql_tree;
+ proto_item *ti;
+ tvbuff_t *next_tvb;
+
+ conversation_t *conversation;
+ mysql_conn_data_t *conn_data;
+ int offset = 0;
+ guint clen, ulen;
+
+ /* get conversation, create if necessary*/
+ conversation = find_or_create_conversation(pinfo);
+
+ /* get associated state information, create if necessary */
+ conn_data = (mysql_conn_data_t *)conversation_get_proto_data(conversation, proto_mysql);
+ if (!conn_data) {
+ conn_data = wmem_new0(wmem_file_scope(), mysql_conn_data_t);
+ conn_data->stmts = wmem_tree_new(wmem_file_scope());
+ conn_data->compressed_state = MYSQL_COMPRESS_ACTIVE;
+ conversation_add_proto_data(conversation, proto_mysql, conn_data);
+ }
+ if (!conn_data->reassembly_info) {
+ conn_data->reassembly_info = streaming_reassembly_info_new();
+ }
+
+ ti = proto_tree_add_item(tree, proto_mysql, tvb, offset, 7, ENC_NA);
+ proto_item_append_text(ti, " - compressed packet header");
+ mysql_tree = proto_item_add_subtree(ti, ett_mysql);
+
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "MySQL");
+
+ clen = tvb_get_letoh24(tvb, offset);
+ 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;
+
+ ulen = tvb_get_letoh24(tvb, offset);
+ proto_tree_add_item(mysql_tree, hf_mysql_compressed_packet_length_uncompressed, tvb, offset, 3, ENC_LITTLE_ENDIAN);
+ offset += 3;
+
+ if (ulen>0) {
+ switch (conn_data->compressed_alg) {
+#ifdef HAVE_ZSTD
+ case MYSQL_COMPRESS_ALG_ZSTD:
+ next_tvb = tvb_child_uncompress_zstd(tvb, tvb, offset, clen);
+ break;
+#endif
+ case MYSQL_COMPRESS_ALG_ZLIB:
+ default:
+ next_tvb = tvb_child_uncompress(tvb, tvb, offset, clen);
+ break;
+ }
+ if (next_tvb) {
+ add_new_data_source(pinfo, next_tvb, "compressed data");
+ reassemble_streaming_data_and_call_subdissector(next_tvb, pinfo, 0, ulen, mysql_tree, tree, mysql_reassembly_table, conn_data->reassembly_info, get_virtual_frame_num64(next_tvb, pinfo, 0), decompressed_handle, tree, data, "MySQL", &mysql_frag_items, hf_mysql_fragment_data);
+
+ offset += clen;
+ } else {
+ expert_add_info_format(pinfo, mysql_tree, &ei_mysql_compression, "Can't uncompress packet");
+ }
+ } else {
+ /* No compression was chosen. It's unlikely that there are
+ * multiple PDUs, and extremely unlikely that they span
+ * frame boundaries (otherwise compression would have been
+ * used), but it doesn't hurt to do this.
+ */
+ reassemble_streaming_data_and_call_subdissector(tvb, pinfo, offset, tvb_reported_length_remaining(tvb, offset), mysql_tree, tree, mysql_reassembly_table, conn_data->reassembly_info, get_virtual_frame_num64(tvb, pinfo, offset), decompressed_handle, tree, data, "MySQL", &mysql_frag_items, hf_mysql_fragment_data);
+ offset = tvb_reported_length(tvb);
+ }
+
+ return offset;
+}
+
+
/* dissector entrypoint, handles TCP-desegmentation */
static int
dissect_mysql(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
{
- tcp_dissect_pdus(tvb, pinfo, tree, mysql_desegment, 3,
- get_mysql_pdu_len, dissect_mysql_pdu, data);
+ conversation_t *conversation;
+ mysql_conn_data_t *conn_data = NULL;
+
+ conversation = find_conversation_pinfo(pinfo, 0);
+ if (conversation) {
+ conn_data = (mysql_conn_data_t *)conversation_get_proto_data(conversation, proto_mysql);
+ }
+ if (conn_data && conn_data->compressed_state == MYSQL_COMPRESS_ACTIVE && pinfo->num > conn_data->frame_start_compressed) {
+ tcp_dissect_pdus(tvb, pinfo, tree, mysql_desegment,
+ MYSQL_HEADER_LENGTH + 3,
+ get_mysql_compressed_pdu_len,
+ dissect_mysql_compressed_pdu, data);
+ } else {
+ tcp_dissect_pdus(tvb, pinfo, tree, mysql_desegment,
+ MYSQL_HEADER_LENGTH, get_mysql_pdu_len,
+ dissect_mysql_pdu, data);
+ }
return tvb_reported_length(tvb);
}
@@ -3334,7 +4671,7 @@ void proto_register_mysql(void)
NULL, HFILL }},
{ &hf_mysql_cap_plugin_auth_lenenc_client_data,
- { "Plugin Auth LENENC Client Data","mysql.caps.pm",
+ { "Plugin Auth LENENC Client Data","mysql.caps.cd",
FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_AL,
NULL, HFILL }},
@@ -3353,6 +4690,36 @@ void proto_register_mysql(void)
FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_DE,
NULL, HFILL }},
+ { &hf_mysql_cap_optional_metadata,
+ { "Client can handle optional resultset metadata","mysql.caps.optional_metadata",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_RM,
+ NULL, HFILL }},
+
+ { &hf_mysql_cap_compress_zstd,
+ { "ZSTD Compression Algorithm","mysql.caps.compress_zsd",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_ZS,
+ NULL, HFILL }},
+
+ { &hf_mysql_cap_query_attrs,
+ { "Query Attributes","mysql.caps.query_attrs",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_QA,
+ NULL, HFILL }},
+
+ { &hf_mysql_cap_mf_auth,
+ { "Multifactor Authentication","mysql.caps.mf_auth",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_MF,
+ NULL, HFILL }},
+
+ { &hf_mysql_cap_cap_ext,
+ { "Capability Extension","mysql.caps.cap_ext",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_CE,
+ NULL, HFILL }},
+
+ { &hf_mysql_cap_ssl_verify_server_cert,
+ { "Client verifies server's TLS/SSL certificate","mysql.caps.vc",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_VC,
+ NULL, HFILL }},
+
{ &hf_mysql_cap_unused,
{ "Unused","mysql.caps.unused",
FT_UINT16, BASE_HEX, NULL, MYSQL_CAPS_UNUSED,
@@ -3424,7 +4791,7 @@ void proto_register_mysql(void)
NULL, HFILL }},
{ &hf_mysql_connattrs_value_length,
- { "Connection Attribute Name Length", "mysql.connattrs.name.length",
+ { "Connection Attribute Value Length", "mysql.connattrs.value.length",
FT_UINT64, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
@@ -3433,6 +4800,42 @@ void proto_register_mysql(void)
FT_STRINGZ, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
+ { &hf_mysql_zstd_compression_level,
+ { "ZSTD Compression Level", "mysql.compression.zstd_level",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
+
+ { &hf_mariadb_extmeta_data,
+ { "Extended metadata data", "mysql.extmeta_data",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mariadb_extmeta,
+ { "Extended metadata", "mysql.extmeta",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mariadb_extmeta_length,
+ { "Extended metadata length", "mysql.extmeta.length",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mariadb_extmeta_key,
+ { "Extended metadata key", "mysql.extmeta.key",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mariadb_extmeta_type,
+ { "Extended metadata type", "mysql.extmeta.type",
+ FT_STRINGZ, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mariadb_extmeta_format,
+ { "Extended metadata format", "mysql.extmeta.format",
+ FT_STRINGZ, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
{ &hf_mysql_salt,
{ "Salt", "mysql.salt",
FT_STRINGZ, BASE_NONE, NULL, 0x0,
@@ -3623,16 +5026,41 @@ void proto_register_mysql(void)
FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
- { &hf_mysql_thd_id,
- { "Thread ID", "mysql.thd_id",
- FT_UINT32, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
-
{ &hf_mysql_stmt_id,
{ "Statement ID", "mysql.stmt_id",
FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
+ { &hf_mysql_query_attributes,
+ { "Query Attributes", "mysql.query_attrs",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_query_attributes_count,
+ { "Count", "mysql.query_attrs_count",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_query_attributes_send_types_to_server,
+ { "Send types to server", "mysql.query_attrs_send_types_to_server",
+ FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_query_attribute_name_type,
+ { "Attribute Name Type", "mysql.query_attr_name_type",
+ FT_UINT16, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_query_attribute_name,
+ { "Attribute Name", "mysql.query_attr_name",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_query_attribute_value,
+ { "Attribute Value", "mysql.query_attr_value",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
{ &hf_mysql_query,
{ "Statement", "mysql.query",
FT_STRING, BASE_NONE, NULL, 0x0,
@@ -3688,6 +5116,11 @@ void proto_register_mysql(void)
FT_UINT32, BASE_DEC, NULL, 0x0,
"Position to start at", HFILL }},
+ { &hf_mysql_binlog_position8,
+ { "Binlog Position", "mysql.binlog.position8",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ "Position to start at", HFILL }},
+
{ &hf_mysql_binlog_flags,
{ "Binlog Flags", "mysql.binlog.flags",
FT_UINT16, BASE_HEX, NULL, 0x0,
@@ -3695,14 +5128,144 @@ void proto_register_mysql(void)
{ &hf_mysql_binlog_server_id,
{ "Binlog server id", "mysql.binlog.server_id",
- FT_UINT32, BASE_HEX, NULL, 0x0,
+ FT_UINT32, BASE_DEC, NULL, 0x0,
"server_id of the slave", HFILL }},
+ { &hf_mysql_binlog_slave_hostname_length,
+ { "Slave hostname length", "mysql.binlog.slave_hostname_length",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "slave_hostname field length", HFILL }},
+
+ { &hf_mysql_binlog_slave_hostname,
+ { "Slave hostname", "mysql.binlog.slave_hostname",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "slave_hostname", HFILL }},
+
+ { &hf_mysql_binlog_slave_user_length,
+ { "Slave user length", "mysql.binlog.slave_user_length",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "slave_hostname field length", HFILL }},
+
+ { &hf_mysql_binlog_slave_user,
+ { "Slave user", "mysql.binlog.slave_user",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "slave_user", HFILL }},
+
+ { &hf_mysql_binlog_slave_password_length,
+ { "Slave password length", "mysql.binlog.slave_password_length",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "slave_password field length", HFILL }},
+
+ { &hf_mysql_binlog_slave_password,
+ { "Slave password", "mysql.binlog.slave_password",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "slave_password", HFILL }},
+
+ { &hf_mysql_binlog_slave_mysql_port,
+ { "Slave MySQL port", "mysql.binlog.slave_mysql_port",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "slave's mysql port", HFILL }},
+
+ { &hf_mysql_binlog_replication_rank,
+ { "Replication rank", "mysql.binlog.replication_rank",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "ignored", HFILL }},
+
+ { &hf_mysql_binlog_master_id,
+ { "Master id", "mysql.binlog.master_id",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ "master_id of the slave", HFILL }},
+
{ &hf_mysql_binlog_file_name,
{ "Binlog file name", "mysql.binlog.file_name",
FT_STRINGZ, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
+ { &hf_mysql_binlog_file_name_length,
+ { "Binlog file name length", "mysql.binlog.file_name_length",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_binlog_gtid_data,
+ { "Binlog GTID Data", "mysql.binlog.gtid_data",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_binlog_gtid_data_length,
+ { "Binlog file GTID data length", "mysql.binlog.gtid_data_length",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_binlog_event_header_timestamp,
+ { "Timestamp", "mysql.binlog.event_header.timestamp",
+ FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_binlog_event_header_event_type,
+ { "Binlog Event Type", "mysql.binlog.event_header.event_type",
+ FT_UINT8, BASE_DEC, VALS(mysql_binlog_event_type_vals), 0x0,
+ "event type", HFILL }},
+
+ { &hf_mysql_binlog_event_header_server_id,
+ { "Server ID", "mysql.binlog.event_header.server_id",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "server-id of the originating mysql-server", HFILL }},
+
+ { &hf_mysql_binlog_event_header_event_size,
+ { "Event Size", "mysql.binlog.event_header.event_size",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "size of the event (header, post-header, body)", HFILL }},
+
+ { &hf_mysql_binlog_event_header_log_position,
+ { "Binlog Position", "mysql.binlog.event_header.log_position",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "position of the next event", HFILL }},
+
+ { &hf_mysql_binlog_event_header_flags,
+ { "Binlog Event Flags", "mysql.binlog.event_header.flags",
+ FT_UINT16, BASE_HEX, NULL, 0x0,
+ "flag", HFILL }},
+
+ { &hf_mysql_binlog_event_checksum,
+ { "Checksum", "mysql.binlog.event_checksum",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ "binlog event checksum", HFILL }},
+
+ { &hf_mysql_binlog_event_heartbeat_v2,
+ { "Binlog Event: HEARTBEAT_LOG_EVENT_V2", "mysql.binlog.event_heartbeat_v2",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_binlog_event_heartbeat_v2_otw,
+ { "Entry", "mysql.binlog.event_heartbeat_v2_otw",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_binlog_event_heartbeat_v2_otw_type,
+ { "Type", "mysql.binlog.event_heartbeat_v2_otw_type",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_binlog_hb_event_filename,
+ { "Binlog Filename", "mysql.binlog.hb_event.filename",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "filename", HFILL }},
+
+ { &hf_mysql_binlog_hb_event_log_position,
+ { "Binlog Position", "mysql.binlog.hb_event.log_position",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ "position of the next event", HFILL }},
+
+ { &hf_mysql_clone_command_code,
+ { "Clone Command Code", "mysql.clone.command_code",
+ FT_UINT8, BASE_HEX, VALS(mysql_clone_command_vals), 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_clone_response_code,
+ { "Clone Response Code", "mysql.clone.response_code",
+ FT_UINT8, BASE_HEX, VALS(mysql_clone_response_vals), 0x0,
+ NULL, HFILL }},
+
{ &hf_mysql_eof,
{ "EOF marker", "mysql.eof",
FT_UINT8, BASE_DEC, NULL, 0x0,
@@ -3713,6 +5276,11 @@ void proto_register_mysql(void)
FT_UINT64, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
+ { &hf_mariadb_send_meta,
+ { "send metadata", "mysql.metadata_follows",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
{ &hf_mysql_extra,
{ "Extra data", "mysql.extra",
FT_UINT64, BASE_DEC, NULL, 0x0,
@@ -3808,10 +5376,10 @@ void proto_register_mysql(void)
FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_ZEROFILL_FLAG,
"Field: flag zero fill", HFILL }},
- { &hf_mysql_fld_binary,
- { "Binary", "mysql.field.flags.binary",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_BINARY_FLAG,
- "Field: flag binary", HFILL }},
+ { &hf_mysql_null_buffer,
+ { "Row null buffer", "mysql.row.nullbuffer",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
{ &hf_mysql_fld_enum,
{ "Enum", "mysql.field.flags.enum",
@@ -3859,17 +5427,22 @@ void proto_register_mysql(void)
NULL, HFILL }},
{ &hf_mysql_exec_field_longlong,
- { "Value", "mysql.exec.field.longlong",
+ { "Value (INT64)", "mysql.exec.field.longlong",
FT_INT64, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
+ { &hf_mysql_exec_field_unsigned_longlong,
+ { "Value (UINT64)", "mysql.exec.field.unsigned_longlong",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
{ &hf_mysql_exec_field_string,
- { "Value", "mysql.exec.field.string",
+ { "Value (String)", "mysql.exec.field.string",
FT_UINT_STRING, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
{ &hf_mysql_exec_field_double,
- { "Value", "mysql.exec.field.double",
+ { "Value (Double)", "mysql.exec.field.double",
FT_DOUBLE, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
@@ -3914,25 +5487,45 @@ void proto_register_mysql(void)
NULL, HFILL }},
{ &hf_mysql_exec_field_long,
- { "Value", "mysql.exec.field.long",
+ { "Value (INT32)", "mysql.exec.field.long",
FT_INT32, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
+ { &hf_mysql_exec_field_unsigned_long,
+ { "Value (UINT32)", "mysql.exec.field.unsigned_long",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
{ &hf_mysql_exec_field_tiny,
- { "Value", "mysql.exec.field.tiny",
+ { "Value (INT8)", "mysql.exec.field.tiny",
FT_INT8, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
+ { &hf_mysql_exec_field_unsigned_tiny,
+ { "Value (UINT8)", "mysql.exec.field.unsigned_tiny",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
{ &hf_mysql_exec_field_short,
- { "Value", "mysql.exec.field.short",
+ { "Value (INT16)", "mysql.exec.field.short",
FT_INT16, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
+ { &hf_mysql_exec_field_unsigned_short,
+ { "Value (UINT16)", "mysql.exec.field.unsigned_short",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
{ &hf_mysql_exec_field_float,
- { "Value", "mysql.exec.field.float",
+ { "Value (Float)", "mysql.exec.field.float",
FT_FLOAT, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
+ { &hf_mysql_exec_field_null,
+ { "Value: -NULL-", "mysql.exec.field.null",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
{ &hf_mysql_exec_field_time_length,
{ "Length", "mysql.exec.field.time.length",
FT_INT8, BASE_DEC, NULL, 0x0,
@@ -3968,6 +5561,21 @@ void proto_register_mysql(void)
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }},
+ { &hf_mysql_sha2_auth,
+ { "SHA2 Auth State", "mysql.hf_mysql_sha2_auth.name",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_pubkey,
+ { "Public Key", "mysql.hf_mysql_pubkey",
+ FT_STRINGZ, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_sha2_response,
+ { "SHA2 Auth Response", "mysql.hf_mysql_sha2_response",
+ 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,
@@ -3983,6 +5591,16 @@ void proto_register_mysql(void)
FT_UINT24, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
+ { &hf_mysql_loaddata_filename,
+ { "LOCAL INFILE Filename", "mysql.load_data.filename",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_loaddata_payload,
+ { "LOCAL INFILE Payload", "mysql.load_data.payload",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
{ &hf_mariadb_cap_progress,
{ "Progress indication", "mariadb.caps.pr",
FT_BOOLEAN, 32, TFS(&tfs_set_notset), MARIADB_CAPS_PR,
@@ -4003,6 +5621,11 @@ void proto_register_mysql(void)
FT_BOOLEAN, 32, TFS(&tfs_set_notset), MARIADB_CAPS_EM,
NULL, HFILL }},
+ { &hf_mariadb_cap_cache_metadata,
+ { "Cache metadata", "mariadb.caps.me",
+ FT_BOOLEAN, 32, TFS(&tfs_set_notset), MARIADB_CAPS_ME,
+ NULL, HFILL }},
+
{ &hf_mariadb_extcaps_server,
{ "MariaDB Extended Server Capabilities", "mariadb.extcaps.server",
FT_UINT32, BASE_HEX, NULL, 0x0,
@@ -4035,16 +5658,67 @@ void proto_register_mysql(void)
{ &hf_mariadb_bulk_indicator,
{ "Indicator", "mariadb.bulk.indicators",
- FT_UINT8, BASE_HEX, VALS(mariadb_bulk_indicator_vals), 0x00,
- NULL, HFILL }},
- { &hf_mysql_prefix,
- { "Prefix", "mariadb.prefix",
- FT_UINT8, BASE_DEC, NULL, 0x00,
- NULL, HFILL } },
- { &hf_mysql_length,
- { "Length", "mariadb.length",
- FT_UINT64, BASE_DEC, NULL, 0x00,
- NULL, HFILL } }
+ FT_UINT8, BASE_HEX, VALS(mariadb_bulk_indicator_vals), 0x00,
+ NULL, HFILL }},
+
+ { &hf_mariadb_bulk_row_nr,
+ { "Row nr", "mariadb.bulk.row_nr",
+ FT_UINT32, BASE_DEC, NULL, 0x00,
+ NULL, HFILL }},
+
+ { &hf_mysql_fragments,
+ { "Reassembled MySQL fragments", "mysql.fragments",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_fragment,
+ { "MySQL fragment", "mysql.fragment",
+ FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_fragment_overlap,
+ { "Fragment overlap", "mysql.fragment.overlap",
+ FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_fragment_overlap_conflicts,
+ { "Conflicting data in fragment overlap", "mysql.fragment.overlap.conflicts",
+ FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_fragment_multiple_tails,
+ { "Multiple tail fragments found", "mysql.fragment.multiple_tails",
+ FT_BOOLEAN, BASE_NONE, NULL, 0x00,
+ NULL, HFILL }},
+
+ { &hf_mysql_fragment_too_long_fragment,
+ { "Fragment too long", "mysql.fragment.too_long_fragment",
+ FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_fragment_error,
+ { "Defragmentation error", "mysql.fragment.error",
+ FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_fragment_count,
+ { "Fragment count", "mysql.fragment.count",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_reassembled_in,
+ { "Reassembled in", "mysql.reassembled.in",
+ FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_reassembled_length,
+ { "Reassembled length", "mysql.reassembled.length",
+ FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
+
+ { &hf_mysql_fragment_data,
+ { "MySQL fragment data", "mysql.fragment.data",
+ FT_BYTES, BASE_NONE, NULL, 0x00, NULL, HFILL } },
+
};
static gint *ett[]=
@@ -4055,6 +5729,7 @@ void proto_register_mysql(void)
&ett_caps,
&ett_extcaps,
&ett_stat,
+ &ett_row_value,
&ett_request,
&ett_refresh,
&ett_field_flags,
@@ -4062,9 +5737,16 @@ void proto_register_mysql(void)
&ett_bulk_param,
&ett_session_track,
&ett_session_track_data,
+ &ett_extmeta,
+ &ett_extmeta_data,
&ett_connattrs,
&ett_connattrs_attr,
- &ett_mysql_field
+ &ett_mysql_field,
+ &ett_query_attributes,
+ &ett_binlog_event,
+ &ett_binlog_event_hb_v2,
+ &ett_mysql_fragment,
+ &ett_mysql_fragments,
};
static ei_register_info ei[] = {
@@ -4072,8 +5754,9 @@ void proto_register_mysql(void)
{ &ei_mysql_streamed_param, { "mysql.streamed_param", PI_SEQUENCE, PI_CHAT, "This parameter was streamed, its value can be found in Send BLOB packets", EXPFILL }},
{ &ei_mysql_prepare_response_needed, { "mysql.prepare_response_needed", PI_UNDECODED, PI_WARN, "PREPARE Response packet is needed to dissect the payload", EXPFILL }},
{ &ei_mysql_command, { "mysql.command.invalid", PI_PROTOCOL, PI_WARN, "Unknown/invalid command code", EXPFILL }},
- { &ei_mysql_eof, { "mysql.eof.wrong_state", PI_PROTOCOL, PI_WARN, "EOF Marker found while connection in wrong state.", EXPFILL }},
{ &ei_mysql_unknown_response, { "mysql.unknown_response", PI_UNDECODED, PI_WARN, "unknown/invalid response", EXPFILL }},
+ { &ei_mysql_invalid_length, { "mysql.invalid_length", PI_MALFORMED, PI_ERROR, "Invalid length", EXPFILL }},
+ { &ei_mysql_compression, { "mysql.uncompress_failure", PI_MALFORMED, PI_WARN, "Uncompression failed", EXPFILL }},
};
module_t *mysql_module;
@@ -4096,6 +5779,8 @@ void proto_register_mysql(void)
"Whether the MySQL dissector should display the SQL query string in the INFO column.",
&mysql_showquery);
+ reassembly_table_register(&mysql_reassembly_table,
+ &addresses_ports_reassembly_table_functions);
mysql_handle = register_dissector("mysql", dissect_mysql, proto_mysql);
}
@@ -4103,6 +5788,7 @@ void proto_register_mysql(void)
void proto_reg_handoff_mysql(void)
{
tls_handle = find_dissector("tls");
+ decompressed_handle = create_dissector_handle(dissect_mysql_decompressed_pdus, proto_mysql);
dissector_add_uint_with_preference("tcp.port", TCP_PORT_MySQL, mysql_handle);
}