#include <epan/dissectors/packet-tcp.h>
#include <epan/prefs.h>
+#include <epan/expert.h>
/* port for protocol registration */
#define TCP_PORT_MySQL 3306
#define MYSQL_RFSH_SLAVE 64 /* Reset master info and restart slave thread */
#define MYSQL_RFSH_MASTER 128 /* Remove all bin logs in the index and truncate the index */
-/* MySQL operation codes */
+/* MySQL command codes */
#define MYSQL_SLEEP 0 /* not from client */
#define MYSQL_QUIT 1
#define MYSQL_INIT_DB 2
#define MYSQL_SET_OPTION 27
#define MYSQL_STMT_FETCH 28
+/* MySQL cursor types */
-/* decoding table: opcodes */
-static const value_string mysql_opcode_vals[] = {
+#define MYSQL_CURSOR_TYPE_NO_CURSOR 0
+#define MYSQL_CURSOR_TYPE_READ_ONLY 1
+#define MYSQL_CURSOR_TYPE_FOR_UPDATE 2
+#define MYSQL_CURSOR_TYPE_SCROLLABLE 4
+
+
+/* decoding table: command */
+static const value_string mysql_command_vals[] = {
{MYSQL_SLEEP, "SLEEP"},
{MYSQL_QUIT, "Quit"},
{MYSQL_INIT_DB, "Use Database"},
{0, NULL}
};
+/* decoding table: exec_flags */
+static const value_string mysql_exec_flags_vals[] = {
+ {MYSQL_CURSOR_TYPE_NO_CURSOR, "Defaults"},
+ {MYSQL_CURSOR_TYPE_READ_ONLY, "Read-only cursor"},
+ {MYSQL_CURSOR_TYPE_FOR_UPDATE, "Cursor for update"},
+ {MYSQL_CURSOR_TYPE_SCROLLABLE, "Scrollable cursor"},
+ {0, NULL}
+};
/* charset: pre-4.1 used the term 'charset', later changed to 'collation' */
static const value_string mysql_charset_vals[] = {
/* 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_field_flags = -1;
/* protocol fields */
-static int hf_mysql_caps= -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= -1;
-static int hf_mysql_cap_multi_statements= -1;
-static int hf_mysql_cap_multi_results= -1;
-static int hf_mysql_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_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_opcode= -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_protocol= -1;
-static int hf_mysql_version = -1;
-static int hf_mysql_max_packet= -1;
-static int hf_mysql_user= -1;
-static int hf_mysql_schema= -1;
-static int hf_mysql_thread_id = -1;
-static int hf_mysql_salt= -1;
-static int hf_mysql_salt2= -1;
-static int hf_mysql_charset= -1;
-static int hf_mysql_passwd= -1;
-static int hf_mysql_unused= -1;
-static int hf_mysql_parameter= -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_option= -1;
-static int hf_mysql_num_rows= -1;
-static int hf_mysql_param= -1;
-static int hf_mysql_exec_flags= -1;
-static int hf_mysql_exec_iter= -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_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_client = -1;
+static int hf_mysql_cap_multi_statements = -1;
+static int hf_mysql_cap_multi_results = -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_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_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_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_thread_id = -1;
+static int hf_mysql_salt = -1;
+static int hf_mysql_salt2 = -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_set = -1;
static int hf_mysql_fld_decimals = -1;
static int hf_mysql_fld_default = -1;
-static int hf_mysql_row_length = -1;
static int hf_mysql_row_text = -1;
/* type constants */
FIELD_PACKET,
ROW_PACKET,
RESPONSE_PREPARE,
- PARAM_PACKET
+ PREPARED_PARAMETERS,
+ PREPARED_FIELDS
} mysql_state_t;
#ifdef CTDEBUG
{FIELD_PACKET, "field packet"},
{ROW_PACKET, "row packet"},
{RESPONSE_PREPARE, "response to PREPARE"},
- {PARAM_PACKET, "parameter packet"},
+ {RESPONSE_PARAMETERS, "parameters in response to PREPARE"},
+ {RESPONSE_FIELDS, "fields in response to PREPARE"},
{0, NULL}
};
#endif
guint16 clnt_caps;
guint16 clnt_caps_ext;
mysql_state_t state;
+ guint16 stmt_num_params;
+ guint16 stmt_num_fields;
GHashTable* stmts;
#ifdef CTDEBUG
guint32 generation;
#endif
+ guint8 major_version;
} mysql_conn_data_t;
struct mysql_frame_data {
static int mysql_dissect_error_packet(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree);
static int mysql_dissect_ok_packet(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, mysql_conn_data_t *conn_data);
static int mysql_dissect_server_status(tvbuff_t *tvb, int offset, proto_tree *tree);
-static void mysql_dissect_collation(tvbuff_t *tvb, int offset, proto_tree *tree, guint16 caps, gint charset, int field);
-static int mysql_dissect_caps(tvbuff_t *tvb, int offset, proto_tree *tree, guint16 *caps, const char* whom);
-static int mysql_dissect_ext_caps(tvbuff_t *tvb, int offset, proto_tree *tree, guint16 *caps, const char* whom);
+static int mysql_dissect_caps_server(tvbuff_t *tvb, int offset, proto_tree *tree, guint16 *caps);
+static int mysql_dissect_caps_client(tvbuff_t *tvb, int offset, proto_tree *tree, guint16 *caps);
+static int mysql_dissect_ext_caps_client(tvbuff_t *tvb, int offset, proto_tree *tree, 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_response_prepare(tvbuff_t *tvb, int offset, proto_tree *tree);
-static int mysql_dissect_param_packet(tvbuff_t *tvb, int offset, proto_tree *tree);
+static int mysql_dissect_response_prepare(tvbuff_t *tvb, int offset, proto_tree *tree, mysql_conn_data_t *conn_data);
static gint my_tvb_strsize(tvbuff_t *tvb, int offset);
static int tvb_get_fle(tvbuff_t *tvb, int offset, guint64 *res, guint8 *is_null);
+/* dissector entrypoint, handles TCP-desegmentation */
+static void
+dissect_mysql(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ tcp_dissect_pdus(tvb, pinfo, tree, mysql_desegment, 3,
+ get_mysql_pdu_len, dissect_mysql_pdu);
+}
+
-/* dissector registration */
-void proto_reg_handoff_mysql(void)
+/* dissector helper: length of PDU */
+static guint
+get_mysql_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
{
- dissector_handle_t mysql_handle;
- mysql_handle = create_dissector_handle(dissect_mysql, proto_mysql);
- dissector_add("tcp.port", TCP_PORT_MySQL, mysql_handle);
+ guint plen= tvb_get_letoh24(tvb, offset);
+ return plen + 4; /* add length field + packet number */
}
-/* protocol registration */
-void proto_register_mysql(void)
+/* dissector main function: handle one PDU */
+static void
+dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
- static hf_register_info hf[]=
- {
- { &hf_mysql_packet_length,
- { "Packet Length", "mysql.packet_length",
- FT_UINT24, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ proto_tree *mysql_tree= NULL;
+ proto_item *ti;
+ conversation_t *conversation;
+ int offset = 0;
+ guint packet_number;
+ gboolean is_response;
+ mysql_conn_data_t *conn_data;
+#ifdef CTDEBUG
+ mysql_state_t conn_state_in, conn_state_out, frame_state;
+ guint64 generation;
+ proto_item *pi;
+#endif
+ struct mysql_frame_data *mysql_frame_data_p;
- { &hf_mysql_packet_number,
- { "Packet Number", "mysql.packet_number",
- FT_UINT8, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ /* get conversation, create if neccessary*/
+ conversation= find_or_create_conversation(pinfo);
- { &hf_mysql_opcode,
- { "Command", "mysql.opcode",
- FT_UINT8, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ /* get associated state information, create if neccessary */
+ conn_data= conversation_get_proto_data(conversation, proto_mysql);
+ if (!conn_data) {
+ conn_data= se_alloc(sizeof(mysql_conn_data_t));
+ conn_data->srv_caps= 0;
+ conn_data->clnt_caps= 0;
+ conn_data->clnt_caps_ext= 0;
+ conn_data->state= UNDEFINED;
+ conn_data->stmts= g_hash_table_new(g_int_hash, g_int_equal);
+#ifdef CTDEBUG
+ conn_data->generation= 0;
+#endif
+ conn_data->major_version= 0;
+ conversation_add_proto_data(conversation, proto_mysql, conn_data);
+ }
- { &hf_mysql_response_code,
- { "Response Code", "mysql.response_code",
- FT_UINT8, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ mysql_frame_data_p = p_get_proto_data(pinfo->fd, proto_mysql);
+ if (!mysql_frame_data_p) {
+ /* We haven't seen this frame before. Store the state of the
+ * conversation now so if/when we dissect the frame again
+ * we'll start with the same state.
+ */
+ mysql_frame_data_p = se_alloc(sizeof(struct mysql_frame_data));
+ mysql_frame_data_p->state = conn_data->state;
+ p_add_proto_data(pinfo->fd, proto_mysql, mysql_frame_data_p);
- { &hf_mysql_error_code,
- { "Error Code", "mysql.error_code",
- FT_UINT16, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ } else if (conn_data->state != FIELD_PACKET && conn_data->state != ROW_PACKET ) {
+ /* We have seen this frame before. Set the connection state
+ * to whatever state it had the first time we saw this frame
+ * (e.g., based on whatever frames came before it).
+ * The state may change as we dissect this packet.
+ * XXX: I think the logic of the above else if test is as follows:
+ * During the first (sequential) dissection pass thru the capture
+ * file the conversation connection state as of the beginning of each frame
+ * is saved in the connection_state for that frame.
+ * Any state changes *within* a mysql "message" (ie: query/response/etc)
+ * while processing mysql PDUS (aka "packets") in that message must be preserved.
+ * It appears that FIELD_PACKET & ROW_PACKET are the only two
+ * state changes which can occur within a mysql message which affect
+ * subsequent processing within the message.
+ * Question: Does this logic work OK for a reassembled message ?
+ */
+ conn_data->state= mysql_frame_data_p->state;
+ }
- { &hf_mysql_error_string,
- { "Error message", "mysql.error.message",
- FT_STRING, BASE_NONE, NULL, 0x0,
- "Error string in case of MySQL error message", HFILL }},
+ if (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);
+ }
+ offset+= 3;
- { &hf_mysql_sqlstate,
- { "SQL state", "mysql.sqlstate",
- FT_STRING, BASE_NONE, NULL, 0x0,
- NULL, HFILL }},
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "MySQL");
- { &hf_mysql_message,
- { "Message", "mysql.message",
- FT_STRINGZ, BASE_NONE, NULL, 0x0,
- NULL, HFILL }},
+ if (pinfo->destport == pinfo->match_uint) {
+ is_response= FALSE;
+ } else {
+ is_response= TRUE;
+ }
- { &hf_mysql_protocol,
- { "Protocol", "mysql.protocol",
- FT_UINT8, BASE_DEC, NULL, 0x0,
- "Protocol Version", HFILL }},
+ packet_number = tvb_get_guint8(tvb, offset);
+ proto_tree_add_item(mysql_tree, hf_mysql_packet_number, tvb, offset, 1, ENC_NA);
+ offset += 1;
- { &hf_mysql_version,
- { "Version", "mysql.version",
- FT_STRINGZ, BASE_NONE, NULL, 0x0,
- "MySQL Version", HFILL }},
+#ifdef CTDEBUG
+ conn_state_in= conn_data->state;
+ frame_state = mysql_frame_data_p->state;
+ generation= conn_data->generation;
+ if (tree) {
+ pi= proto_tree_add_text(mysql_tree, tvb, offset, 0, "conversation: %p", conversation);
+ PROTO_ITEM_SET_GENERATED(pi);
+ pi= proto_tree_add_text(mysql_tree, tvb, offset, 0, "generation: %" G_GINT64_MODIFIER "d", generation);
+ PROTO_ITEM_SET_GENERATED(pi);
+ pi= proto_tree_add_text(mysql_tree, tvb, offset, 0, "conn state: %s (%u)",
+ val_to_str(conn_state_in, state_vals, "Unknown (%u)"),
+ conn_state_in);
+ PROTO_ITEM_SET_GENERATED(pi);
+ pi= proto_tree_add_text(mysql_tree, tvb, offset, 0, "frame state: %s (%u)",
+ val_to_str(frame_state, state_vals, "Unknown (%u)"),
+ frame_state);
+ PROTO_ITEM_SET_GENERATED(pi);
+ }
+#endif
- { &hf_mysql_caps,
- { "Caps", "mysql.caps",
- FT_UINT16, BASE_DEC, NULL, 0x0,
- "MySQL Capabilities", HFILL }},
+ if (is_response) {
+ if (packet_number == 0) {
+ col_set_str(pinfo->cinfo, COL_INFO, "Server Greeting");
+ offset = mysql_dissect_greeting(tvb, pinfo, offset, mysql_tree, conn_data);
+ } else {
+ col_set_str(pinfo->cinfo, COL_INFO, "Response");
+ offset = mysql_dissect_response(tvb, pinfo, offset, mysql_tree, conn_data);
+ }
+ } else {
+ if (packet_number == 1) {
+ col_set_str(pinfo->cinfo, COL_INFO, "Login Request");
+ offset = mysql_dissect_login(tvb, pinfo, offset, mysql_tree, conn_data);
+ } else {
+ col_set_str(pinfo->cinfo, COL_INFO, "Request");
+ offset = mysql_dissect_request(tvb, pinfo, offset, mysql_tree, conn_data);
+ }
+ }
- { &hf_mysql_cap_long_password,
- { "Long Password","mysql.caps.lp",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_LP,
- NULL, HFILL }},
+#ifdef CTDEBUG
+ conn_state_out= conn_data->state;
+ ++(conn_data->generation);
+ pi= proto_tree_add_text(mysql_tree, tvb, offset, 0, "next proto state: %s (%u)",
+ val_to_str(conn_state_out, state_vals, "Unknown (%u)"),
+ conn_state_out);
+ PROTO_ITEM_SET_GENERATED(pi);
+#endif
- { &hf_mysql_cap_found_rows,
- { "Found Rows","mysql.caps.fr",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_FR,
- NULL, HFILL }},
+ /* remaining payload indicates an error */
+ if (tree && tvb_reported_length_remaining(tvb, offset) > 0) {
+ ti = proto_tree_add_item(mysql_tree, hf_mysql_payload, tvb, offset, -1, ENC_NA);
+ expert_add_info_format(pinfo, ti, PI_UNDECODED, PI_WARN, "FIXME - dissector is incomplete");
+ }
+}
- { &hf_mysql_cap_long_flag,
- { "Long Column Flags","mysql.caps.lf",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_LF,
- NULL, HFILL }},
- { &hf_mysql_cap_connect_with_db,
- { "Connect With Database","mysql.caps.cd",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_CD,
- NULL, HFILL }},
+static int
+mysql_dissect_greeting(tvbuff_t *tvb, packet_info *pinfo, int offset,
+ proto_tree *tree, mysql_conn_data_t *conn_data)
+{
+ gint protocol;
+ gint strlen;
+ int ver_offset;
- { &hf_mysql_cap_no_schema,
- { "Don't Allow database.table.column","mysql.caps.ns",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_NS,
- NULL, HFILL }},
+ proto_item *tf;
+ proto_item *greeting_tree= NULL;
- { &hf_mysql_cap_compress,
- { "Can use compression protocol","mysql.caps.cp",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_CP,
- NULL, HFILL }},
+ protocol= tvb_get_guint8(tvb, offset);
- { &hf_mysql_cap_odbc,
- { "ODBC Client","mysql.caps.ob",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_OB,
- NULL, HFILL }},
+ if (protocol == 0xff) {
+ return mysql_dissect_error_packet(tvb, pinfo, offset+1, tree);
+ }
- { &hf_mysql_cap_local_files,
- { "Can Use LOAD DATA LOCAL","mysql.caps.li",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_LI,
- NULL, HFILL }},
+ conn_data->state= LOGIN;
- { &hf_mysql_cap_ignore_space,
- { "Ignore Spaces before '('","mysql.caps.is",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_IS,
- NULL, HFILL }},
+ if (tree) {
+ tf = proto_tree_add_item(tree, hf_mysql_server_greeting, tvb, offset, -1, ENC_NA);
+ greeting_tree = proto_item_add_subtree(tf, ett_server_greeting);
+ }
- { &hf_mysql_cap_change_user,
- { "Speaks 4.1 protocol (new flag)","mysql.caps.cu",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_CU,
- NULL, HFILL }},
+ if (check_col(pinfo->cinfo, COL_INFO)) {
+ col_append_fstr(pinfo->cinfo, COL_INFO, " proto=%d", protocol) ;
+ }
+ proto_tree_add_item(greeting_tree, hf_mysql_protocol, tvb, offset, 1, ENC_NA);
- { &hf_mysql_cap_interactive,
- { "Interactive Client","mysql.caps.ia",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_IA,
- NULL, HFILL }},
+ offset += 1;
- { &hf_mysql_cap_ssl,
- { "Switch to SSL after handshake","mysql.caps.sl",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_SL,
- NULL, HFILL }},
+ /* version string */
+ strlen = tvb_strsize(tvb,offset);
+ if (check_col(pinfo->cinfo, COL_INFO)) {
+ col_append_fstr(pinfo->cinfo, COL_INFO, " version=%s", tvb_get_ephemeral_string(tvb, offset, strlen));
+ }
+ proto_tree_add_item(greeting_tree, hf_mysql_version, tvb, offset, strlen, ENC_NA);
+ conn_data->major_version = 0;
+ for (ver_offset = 0; ver_offset < strlen; ver_offset++) {
+ guint8 ver_char = tvb_get_guint8(tvb, offset + ver_offset);
+ if (ver_char == '.') break;
+ conn_data->major_version = conn_data->major_version * 10 + ver_char - '0';
+ }
+ offset += strlen;
- { &hf_mysql_cap_ignore_sigpipe,
- { "Ignore sigpipes","mysql.caps.ii",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_II,
- NULL, HFILL }},
+ /* 4 bytes little endian thread_id */
+ proto_tree_add_item(greeting_tree, hf_mysql_thread_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
- { &hf_mysql_cap_transactions,
- { "Knows about transactions","mysql.caps.ta",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_TA,
- NULL, HFILL }},
+ /* salt string */
+ strlen = tvb_strsize(tvb,offset);
+ proto_tree_add_item(greeting_tree, hf_mysql_salt, tvb, offset, strlen, ENC_NA);
+ offset += strlen;
- { &hf_mysql_cap_reserved,
- { "Speaks 4.1 protocol (old flag)","mysql.caps.rs",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_RS,
- NULL, HFILL }},
+ /* rest is optional */
+ if (!tvb_reported_length_remaining(tvb, offset)) return offset;
- { &hf_mysql_cap_secure_connect,
- { "Can do 4.1 authentication","mysql.caps.sc",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_SC,
- NULL, HFILL }},
+ /* 2 bytes CAPS */
+ offset = mysql_dissect_caps_server(tvb, offset, greeting_tree, &conn_data->srv_caps);
- { &hf_mysql_extcaps,
- { "Ext. Caps", "mysql.extcaps",
- FT_UINT16, BASE_DEC, NULL, 0x0,
- "MySQL Extended Capabilities", HFILL }},
+ /* rest is optional */
+ if (!tvb_reported_length_remaining(tvb, offset)) return offset;
- { &hf_mysql_cap_multi_statements,
- { "Supports multiple statements","mysql.caps.ms",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_MS,
- NULL, HFILL }},
+ proto_tree_add_item(greeting_tree, hf_mysql_server_language, tvb, offset, 1, ENC_NA);
+ offset += 1; /* for charset */
- { &hf_mysql_cap_multi_results,
- { "Supports multiple results","mysql.caps.mr",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_MR,
- NULL, HFILL }},
+ offset = mysql_dissect_server_status(tvb, offset, greeting_tree);
- { &hf_mysql_max_packet,
- { "MAX Packet", "mysql.max_packet",
- FT_UINT24, BASE_DEC, NULL, 0x0,
- "MySQL Max packet", HFILL }},
+ /* 13 bytes unused */
+ proto_tree_add_item(greeting_tree, hf_mysql_unused, tvb, offset, 13, ENC_NA);
+ offset += 13;
- { &hf_mysql_user,
- { "Username", "mysql.user",
- FT_STRINGZ, BASE_NONE, NULL, 0x0,
- "Login Username", HFILL }},
+ /* 4.1+ server: rest of salt */
+ if (tvb_reported_length_remaining(tvb, offset)) {
+ strlen = tvb_strsize(tvb,offset);
+ proto_tree_add_item(greeting_tree, hf_mysql_salt2, tvb, offset, strlen, ENC_NA);
+ offset += strlen;
+ }
- { &hf_mysql_schema,
- { "Schema", "mysql.schema",
- FT_STRING, BASE_NONE, NULL, 0x0,
- "Login Schema", HFILL }},
+ return offset;
+}
- { &hf_mysql_salt,
- { "Salt", "mysql.salt",
- FT_STRINGZ, BASE_NONE, NULL, 0x0,
- NULL, HFILL }},
- { &hf_mysql_salt2,
- { "Salt", "mysql.salt2",
- FT_STRINGZ, BASE_NONE, NULL, 0x0,
- NULL, HFILL }},
+static int
+mysql_dissect_login(tvbuff_t *tvb, packet_info *pinfo, int offset,
+ proto_tree *tree, mysql_conn_data_t *conn_data)
+{
+ gint strlen;
- { &hf_mysql_thread_id,
- { "Thread ID", "mysql.thread_id",
- FT_UINT32, BASE_DEC, NULL, 0x0,
- "MySQL Thread ID", HFILL }},
+ proto_item *tf;
+ proto_item *login_tree= NULL;
- { &hf_mysql_charset,
- { "Charset", "mysql.charset",
- FT_UINT8, BASE_DEC, NULL, 0x0,
- "MySQL Charset", HFILL }},
+ /* after login there can be OK or DENIED */
+ conn_data->state = RESPONSE_OK;
- { &hf_mysql_status,
- { "Status", "mysql.status",
- FT_UINT16, BASE_DEC, NULL, 0x0,
- "MySQL Status", HFILL }},
+ if (tree) {
+ 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);
+ }
- { &hf_mysql_stat_it,
- { "In transaction", "mysql.stat.it",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_IT,
- NULL, HFILL }},
+ offset = mysql_dissect_caps_client(tvb, offset, login_tree, &conn_data->clnt_caps);
- { &hf_mysql_stat_ac,
- { "AUTO_COMMIT", "mysql.stat.ac",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_AC,
- NULL, HFILL }},
+ if (conn_data->clnt_caps & MYSQL_CAPS_CU) /* 4.1 protocol */
+ {
+ offset = mysql_dissect_ext_caps_client(tvb, offset, login_tree, &conn_data->clnt_caps_ext);
- { &hf_mysql_stat_mr,
- { "More results", "mysql.stat.mr",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_MR,
- NULL, HFILL }},
+ proto_tree_add_item(login_tree, hf_mysql_max_packet, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
- { &hf_mysql_stat_mu,
- { "Multi query - more resultsets", "mysql.stat.mu",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_MU,
- NULL, HFILL }},
+ proto_tree_add_item(login_tree, hf_mysql_charset, tvb, offset, 1, ENC_NA);
+ offset += 1; /* for charset */
- { &hf_mysql_stat_bi,
- { "Bad index used", "mysql.stat.bi",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_BI,
- NULL, HFILL }},
+ offset += 23; /* filler bytes */
- { &hf_mysql_stat_ni,
- { "No index used", "mysql.stat.ni",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_NI,
- NULL, HFILL }},
+ } else { /* pre-4.1 */
+ proto_tree_add_item(login_tree, hf_mysql_max_packet, tvb, offset, 3, ENC_LITTLE_ENDIAN);
+ offset += 3;
+ }
- { &hf_mysql_stat_cr,
- { "Cursor exists", "mysql.stat.cr",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_CR,
- NULL, HFILL }},
+ /* User name */
+ strlen = my_tvb_strsize(tvb, offset);
+ if (check_col(pinfo->cinfo, COL_INFO)) {
+ col_append_fstr(pinfo->cinfo, COL_INFO, " user=%s", tvb_get_ephemeral_string(tvb, offset, strlen));
+ }
+ proto_tree_add_item(login_tree, hf_mysql_user, tvb, offset, strlen, ENC_NA);
+ offset += strlen;
- { &hf_mysql_stat_lr,
- { "Last row sebd", "mysql.stat.lr",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_LR,
- NULL, HFILL }},
+ /* rest is optional */
+ if (!tvb_reported_length_remaining(tvb, offset)) return offset;
- { &hf_mysql_stat_dr,
- { "database dropped", "mysql.stat.dr",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_DR,
- NULL, HFILL }},
+ /* password: asciiz or length+ascii */
+ if (conn_data->clnt_caps & MYSQL_CAPS_SC) {
+ strlen = tvb_get_guint8(tvb, offset);
+ offset += 1;
+ } else {
+ strlen = my_tvb_strsize(tvb, offset);
+ }
+ if (tree && strlen > 1) {
+ proto_tree_add_item(login_tree, hf_mysql_passwd, tvb, offset, strlen, ENC_NA);
+ }
+ offset += strlen;
- { &hf_mysql_stat_bs,
- { "No backslash escapes", "mysql.stat.bs",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_BS,
- NULL, HFILL }},
+ /* optional: initial schema */
+ if (conn_data->clnt_caps & MYSQL_CAPS_CD)
+ {
+ strlen= my_tvb_strsize(tvb,offset);
+ if(strlen<0){
+ return offset;
+ }
- { &hf_mysql_refresh,
- { "Refresh Option", "mysql.refresh",
- FT_UINT8, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ if (check_col(pinfo->cinfo, COL_INFO)) {
+ col_append_fstr(pinfo->cinfo, COL_INFO, " db=%s", tvb_get_ephemeral_string(tvb, offset, strlen));
+ }
+ proto_tree_add_item(login_tree, hf_mysql_schema, tvb, offset, strlen, ENC_NA);
+ offset += strlen;
+ }
- { &hf_mysql_rfsh_grants,
- { "reload permissions", "mysql.rfsh.grants",
- FT_BOOLEAN, 8, TFS(&tfs_set_notset), MYSQL_RFSH_GRANT,
- NULL, HFILL }},
+ return offset;
+}
- { &hf_mysql_rfsh_log,
- { "flush logfiles", "mysql.rfsh.log",
- FT_BOOLEAN, 8, TFS(&tfs_set_notset), MYSQL_RFSH_LOG,
- NULL, HFILL }},
- { &hf_mysql_rfsh_tables,
- { "flush tables", "mysql.rfsh.tables",
- FT_BOOLEAN, 8, TFS(&tfs_set_notset), MYSQL_RFSH_TABLES,
- NULL, HFILL }},
+static int
+mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset,
+ proto_tree *tree, mysql_conn_data_t *conn_data)
+{
+ gint opcode;
+ gint strlen;
+ proto_item *tf = NULL, *ti;
+ proto_item *req_tree = NULL;
- { &hf_mysql_rfsh_hosts,
- { "flush hosts", "mysql.rfsh.hosts",
- FT_BOOLEAN, 8, TFS(&tfs_set_notset), MYSQL_RFSH_HOSTS,
- NULL, HFILL }},
+ if (tree) {
+ tf = proto_tree_add_item(tree, hf_mysql_request, tvb, offset, 1, ENC_NA);
+ req_tree = proto_item_add_subtree(tf, ett_request);
+ }
- { &hf_mysql_rfsh_status,
- { "reset statistics", "mysql.rfsh.status",
- FT_BOOLEAN, 8, TFS(&tfs_set_notset), MYSQL_RFSH_STATUS,
- NULL, HFILL }},
+ opcode = tvb_get_guint8(tvb, offset);
+ if (check_col(pinfo->cinfo, COL_INFO)) {
+ col_append_fstr(pinfo->cinfo, COL_INFO, " %s", val_to_str(opcode, mysql_command_vals, "Unknown (%u)"));
+ }
+ proto_tree_add_item(req_tree, hf_mysql_command, tvb, offset, 1, ENC_NA);
+ proto_item_append_text(tf, " %s", val_to_str(opcode, mysql_command_vals, "Unknown (%u)"));
+ offset += 1;
- { &hf_mysql_rfsh_threads,
- { "empty thread cache", "mysql.rfsh.threads",
- FT_BOOLEAN, 8, TFS(&tfs_set_notset), MYSQL_RFSH_THREADS,
- NULL, HFILL }},
- { &hf_mysql_rfsh_slave,
- { "flush slave status", "mysql.rfsh.slave",
- FT_BOOLEAN, 8, TFS(&tfs_set_notset), MYSQL_RFSH_SLAVE,
- NULL, HFILL }},
+ switch (opcode) {
- { &hf_mysql_rfsh_master,
- { "flush master status", "mysql.rfsh.master",
- FT_BOOLEAN, 8, TFS(&tfs_set_notset), MYSQL_RFSH_MASTER,
- NULL, HFILL }},
+ case MYSQL_QUIT:
+ if (conn_data->stmts) {
+ g_hash_table_destroy(conn_data->stmts);
+ conn_data->stmts = NULL;
+ }
+ break;
- { &hf_mysql_unused,
- { "Unused", "mysql.unused",
- FT_STRING, BASE_NONE, NULL, 0x0,
- NULL, HFILL }},
+ case MYSQL_PROCESS_INFO:
+ conn_data->state = RESPONSE_TABULAR;
+ break;
- { &hf_mysql_passwd,
- { "Password", "mysql.passwd",
- FT_STRING, BASE_NONE, NULL, 0x0,
- NULL, HFILL }},
+ case MYSQL_DEBUG:
+ case MYSQL_PING:
+ conn_data->state = RESPONSE_OK;
+ break;
- { &hf_mysql_parameter,
- { "Parameter", "mysql.parameter",
- FT_STRING, BASE_NONE, NULL, 0x0,
- NULL, HFILL }},
+ case MYSQL_STATISTICS:
+ conn_data->state = RESPONSE_MESSAGE;
+ break;
- { &hf_mysql_payload,
- { "Payload", "mysql.payload",
- FT_STRING, BASE_NONE, NULL, 0x0,
- "Additional Payload", HFILL }},
+ case MYSQL_INIT_DB:
+ case MYSQL_CREATE_DB:
+ case MYSQL_DROP_DB:
+ strlen = my_tvb_strsize(tvb, offset);
+ proto_tree_add_item(req_tree, hf_mysql_schema, tvb, offset, strlen, ENC_NA);
+ offset += strlen;
+ conn_data->state = RESPONSE_OK;
+ break;
- { &hf_mysql_affected_rows,
- { "Affected Rows", "mysql.affected_rows",
- FT_UINT64, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ case MYSQL_QUERY:
+ strlen = my_tvb_strsize(tvb, offset);
+ proto_tree_add_item(req_tree, hf_mysql_query, tvb, offset, strlen, ENC_NA);
+ if (mysql_showquery) {
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_append_fstr(pinfo->cinfo, COL_INFO, " { %s } ", tvb_get_ephemeral_string(tvb, offset, strlen));
+ }
+ offset += strlen;
+ conn_data->state = RESPONSE_TABULAR;
+ break;
- { &hf_mysql_insert_id,
- { "Last INSERT ID", "mysql.insert_id",
- FT_UINT64, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ case MYSQL_STMT_PREPARE:
+ strlen = my_tvb_strsize(tvb, offset);
+ proto_tree_add_item(req_tree, hf_mysql_query, tvb, offset, strlen, ENC_NA);
+ offset += strlen;
+ conn_data->state = RESPONSE_PREPARE;
+ break;
- { &hf_mysql_num_warn,
- { "Warnings", "mysql.warnings",
- FT_UINT16, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ case MYSQL_STMT_CLOSE:
+ if (conn_data->stmts) {
+ gint stmt = tvb_get_letohl(tvb, offset);
+ g_hash_table_remove(conn_data->stmts, &stmt);
+ }
+ proto_tree_add_item(req_tree, hf_mysql_stmt_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+ conn_data->state = REQUEST;
+ break;
- { &hf_mysql_thd_id,
- { "Thread ID", "mysql.thd_id",
- FT_UINT32, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ case MYSQL_STMT_RESET:
+ proto_tree_add_item(req_tree, hf_mysql_stmt_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+ conn_data->state = RESPONSE_OK;
+ break;
- { &hf_mysql_stmt_id,
- { "Statement ID", "mysql.stmt_id",
- FT_UINT32, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ case MYSQL_FIELD_LIST:
+ strlen = my_tvb_strsize(tvb, offset);
+ proto_tree_add_item(req_tree, hf_mysql_table_name, tvb, offset, strlen, ENC_NA);
+ offset += strlen;
+ conn_data->state = RESPONSE_SHOW_FIELDS;
+ break;
- { &hf_mysql_query,
- { "Statement", "mysql.query",
- FT_STRING, BASE_NONE, NULL, 0x0,
- NULL, HFILL }},
+ case MYSQL_PROCESS_KILL:
+ proto_tree_add_item(req_tree, hf_mysql_thd_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+ conn_data->state = RESPONSE_OK;
+ break;
- { &hf_mysql_option,
- { "Option", "mysql.option",
- FT_UINT16, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ case MYSQL_CHANGE_USER:
+ strlen = tvb_strsize(tvb, offset);
+ proto_tree_add_item(req_tree, hf_mysql_user, tvb, offset, strlen, ENC_NA);
+ offset += strlen;
- { &hf_mysql_param,
- { "Parameter", "mysql.param",
- FT_UINT16, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ strlen = tvb_strsize(tvb, offset);
+ proto_tree_add_item(req_tree, hf_mysql_passwd, tvb, offset, strlen, ENC_NA);
+ offset += strlen;
- { &hf_mysql_num_rows,
- { "Rows to fetch", "mysql.num_rows",
- FT_UINT32, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ strlen = my_tvb_strsize(tvb, offset);
+ proto_tree_add_item(req_tree, hf_mysql_schema, tvb, offset, strlen, ENC_NA);
+ offset += strlen;
- { &hf_mysql_exec_flags,
- { "Flags (unused)", "mysql.exec_flags",
- FT_UINT8, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ conn_data->state= RESPONSE_OK;
+ break;
- { &hf_mysql_exec_iter,
- { "Iterations (unused)", "mysql.exec_iter",
- FT_UINT32, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ case MYSQL_REFRESH:
+ {
+ proto_item *tff;
+ proto_item *rfsh_tree;
- { &hf_mysql_eof,
- { "EOF", "mysql.eof",
- FT_UINT8, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ tff = proto_tree_add_item(req_tree, hf_mysql_refresh, tvb, offset, 1, ENC_NA);
+ rfsh_tree = proto_item_add_subtree(tff, ett_refresh);
+ proto_tree_add_item(rfsh_tree, hf_mysql_rfsh_grants, tvb, offset, 1, ENC_NA);
+ proto_tree_add_item(rfsh_tree, hf_mysql_rfsh_log, tvb, offset, 1, ENC_NA);
+ proto_tree_add_item(rfsh_tree, hf_mysql_rfsh_tables, tvb, offset, 1, ENC_NA);
+ proto_tree_add_item(rfsh_tree, hf_mysql_rfsh_hosts, tvb, offset, 1, ENC_NA);
+ proto_tree_add_item(rfsh_tree, hf_mysql_rfsh_status, tvb, offset, 1, ENC_NA);
+ proto_tree_add_item(rfsh_tree, hf_mysql_rfsh_threads, tvb, offset, 1, ENC_NA);
+ proto_tree_add_item(rfsh_tree, hf_mysql_rfsh_slave, tvb, offset, 1, ENC_NA);
+ proto_tree_add_item(rfsh_tree, hf_mysql_rfsh_master, tvb, offset, 1, ENC_NA);
- { &hf_mysql_num_fields,
- { "Number of fields", "mysql.num_fields",
- FT_UINT64, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ }
+ offset += 1;
+ conn_data->state= RESPONSE_OK;
+ break;
- { &hf_mysql_extra,
- { "Extra data", "mysql.extra",
- FT_UINT64, BASE_DEC, NULL, 0x0,
- NULL, HFILL }},
+ case MYSQL_SHUTDOWN:
+ proto_tree_add_item(req_tree, hf_mysql_shutdown, tvb, offset, 1, ENC_NA);
+ offset += 1;
+ conn_data->state = RESPONSE_OK;
+ break;
- { &hf_mysql_fld_catalog,
- { "Catalog", "mysql.field.catalog",
- FT_STRING, BASE_NONE, NULL, 0x0,
- "Field: catalog", HFILL }},
+ case MYSQL_SET_OPTION:
+ proto_tree_add_item(req_tree, hf_mysql_option, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ offset += 2;
+ conn_data->state = RESPONSE_OK;
+ break;
- { &hf_mysql_fld_db,
- { "Database", "mysql.field.db",
- FT_STRING, BASE_NONE, NULL, 0x0,
- "Field: database", HFILL }},
+ case MYSQL_STMT_FETCH:
+ proto_tree_add_item(req_tree, hf_mysql_stmt_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
- { &hf_mysql_fld_table,
- { "Table", "mysql.field.table",
- FT_STRING, BASE_NONE, NULL, 0x0,
- "Field: table", HFILL }},
+ proto_tree_add_item(req_tree, hf_mysql_num_rows, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
+ conn_data->state = RESPONSE_TABULAR;
+ break;
- { &hf_mysql_fld_org_table,
- { "Original table", "mysql.field.org_table",
- FT_STRING, BASE_NONE, NULL, 0x0,
- "Field: original table", HFILL }},
+ case MYSQL_STMT_SEND_LONG_DATA:
+ proto_tree_add_item(req_tree, hf_mysql_stmt_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
- { &hf_mysql_fld_name,
- { "Name", "mysql.field.name",
- FT_STRING, BASE_NONE, NULL, 0x0,
- "Field: name", HFILL }},
+ proto_tree_add_item(req_tree, hf_mysql_param, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ offset += 2;
- { &hf_mysql_fld_org_name,
- { "Original name", "mysql.field.org_name",
- FT_STRING, BASE_NONE, NULL, 0x0,
- "Field: original name", HFILL }},
+ /* rest is data */
+ strlen = tvb_reported_length_remaining(tvb, offset);
+ if (tree && strlen > 0) {
+ proto_tree_add_item(req_tree, hf_mysql_payload, tvb, offset, strlen, ENC_NA);
+ }
+ offset += strlen;
+ conn_data->state = REQUEST;
+ break;
- { &hf_mysql_fld_charsetnr,
- { "Charset number", "mysql.field.charsetnr",
- FT_UINT16, BASE_DEC, NULL, 0x0,
- "Field: charset number", HFILL }},
+ case MYSQL_STMT_EXECUTE:
+ proto_tree_add_item(req_tree, hf_mysql_stmt_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
- { &hf_mysql_fld_length,
- { "Length", "mysql.field.length",
- FT_UINT32, BASE_DEC, NULL, 0x0,
- "Field: length", HFILL }},
+ if (conn_data->major_version >= 5) {
+ proto_tree_add_item(req_tree, hf_mysql_exec_flags5, tvb, offset, 1, ENC_NA);
+ } else {
+ proto_tree_add_item(req_tree, hf_mysql_exec_flags4, tvb, offset, 1, ENC_NA);
+ }
+ offset += 1;
- { &hf_mysql_fld_type,
- { "Type", "mysql.field.type",
- FT_UINT8, BASE_DEC, VALS(type_constants), 0x0,
- "Field: type", HFILL }},
+ proto_tree_add_item(req_tree, hf_mysql_exec_iter, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
- { &hf_mysql_fld_flags,
- { "Flags", "mysql.field.flags",
- FT_UINT16, BASE_DEC, NULL, 0x0,
- "Field: flags", HFILL }},
+#if 0
+/* FIXME: rest needs metadata about statement */
+#else
+ strlen = tvb_reported_length_remaining(tvb, offset);
+ if (tree && strlen > 0) {
+ ti = proto_tree_add_item(req_tree, hf_mysql_payload, tvb, offset, strlen, ENC_NA);
+ expert_add_info_format(pinfo, ti, PI_UNDECODED, PI_WARN, "FIXME: execute dissector incomplete");
+ }
+ offset += strlen;
+#endif
+ conn_data->state= RESPONSE_TABULAR;
+ break;
- { &hf_mysql_fld_not_null,
- { "Not null", "mysql.field.flags.not_null",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_NOT_NULL_FLAG,
- "Field: flag not null", HFILL }},
+ case MYSQL_BINLOG_DUMP:
+ proto_tree_add_item(req_tree, hf_mysql_binlog_position, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
- { &hf_mysql_fld_primary_key,
- { "Primary key", "mysql.field.flags.primary_key",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_PRI_KEY_FLAG,
- "Field: flag primary key", HFILL }},
+ proto_tree_add_item(req_tree, hf_mysql_binlog_flags, tvb, offset, 2, ENC_NA);
+ offset += 2;
- { &hf_mysql_fld_unique_key,
- { "Unique key", "mysql.field.flags.unique_key",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_UNIQUE_KEY_FLAG,
- "Field: flag unique key", HFILL }},
+ proto_tree_add_item(req_tree, hf_mysql_binlog_server_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4;
- { &hf_mysql_fld_multiple_key,
- { "Multiple key", "mysql.field.flags.multiple_key",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_MULTIPLE_KEY_FLAG,
- "Field: flag multiple key", HFILL }},
+ /* binlog file name ? */
+ strlen = tvb_reported_length_remaining(tvb, offset);
+ if (tree && strlen > 0) {
+ proto_tree_add_item(req_tree, hf_mysql_binlog_file_name, tvb, offset, strlen, ENC_NA);
+ }
+ offset += strlen;
- { &hf_mysql_fld_blob,
- { "Blob", "mysql.field.flags.blob",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_BLOB_FLAG,
- "Field: flag blob", HFILL }},
+ conn_data->state = 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, PI_UNDECODED, PI_WARN, "FIXME: implement replication packets");
+ offset += tvb_reported_length_remaining(tvb, offset);
+ conn_data->state = REQUEST;
+ break;
- { &hf_mysql_fld_unsigned,
- { "Unsigned", "mysql.field.flags.unsigned",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_UNSIGNED_FLAG,
- "Field: flag unsigned", HFILL }},
+ default:
+ ti = proto_tree_add_item(req_tree, hf_mysql_payload, tvb, offset, -1, ENC_NA);
+ expert_add_info_format(pinfo, ti, PI_PROTOCOL, PI_WARN, "Unknown/invalid command code");
+ offset += tvb_reported_length_remaining(tvb, offset);
+ conn_data->state = UNDEFINED;
+ }
- { &hf_mysql_fld_zero_fill,
- { "Zero fill", "mysql.field.flags.zero_fill",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_ZEROFILL_FLAG,
- "Field: flag zero fill", HFILL }},
+ return offset;
+}
- { &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_fld_enum,
- { "Enum", "mysql.field.flags.enum",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_ENUM_FLAG,
- "Field: flag enum", HFILL }},
+static int
+mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo, int offset,
+ proto_tree *tree, mysql_conn_data_t *conn_data)
+{
+ gint response_code;
+ gint strlen;
+ gint server_status = 0;
- { &hf_mysql_fld_auto_increment,
- { "Auto increment", "mysql.field.flags.auto_increment",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_AUTO_INCREMENT_FLAG,
- "Field: flag auto increment", HFILL }},
+ response_code = tvb_get_guint8(tvb, offset);
- { &hf_mysql_fld_timestamp,
- { "Timestamp", "mysql.field.flags.timestamp",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_TIMESTAMP_FLAG,
- "Field: flag timestamp", HFILL }},
+ if (response_code == 0xff ) {
+ offset = mysql_dissect_error_packet(tvb, pinfo, offset+1, tree);
+ conn_data->state= REQUEST;
+ }
- { &hf_mysql_fld_set,
- { "Set", "mysql.field.flags.set",
- FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_SET_FLAG,
- "Field: flag set", HFILL }},
+ else if (response_code == 0xfe && tvb_reported_length_remaining(tvb, offset) < 9) {
- { &hf_mysql_fld_decimals,
- { "Decimals", "mysql.field.decimals",
- FT_UINT8, BASE_DEC, NULL, 0x0,
- "Field: decimals", HFILL }},
+ proto_tree_add_item(tree, hf_mysql_eof, tvb, offset, 1, ENC_NA);
- { &hf_mysql_fld_default,
- { "Default", "mysql.field.default",
- FT_STRING, BASE_NONE, NULL, 0x0,
- "Field: default", HFILL }},
+ offset += 1;
- { &hf_mysql_row_length,
- { "length", "mysql.row.length",
- FT_UINT8, BASE_DEC, NULL, 0x0,
- "Field: row packet text length", HFILL }},
+ /* pre-4.1 packet ends here */
+ if (tvb_reported_length_remaining(tvb, offset)) {
+ proto_tree_add_item(tree, hf_mysql_num_warn, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ server_status = tvb_get_letohs(tvb, offset+2);
+ offset = mysql_dissect_server_status(tvb, offset+2, tree);
+ }
- { &hf_mysql_row_text,
- { "text", "mysql.row.text",
- FT_STRING, BASE_NONE, NULL, 0x0,
- "Field: row packet text", HFILL }},
- };
+ if (conn_data->state == FIELD_PACKET) {
+ conn_data->state= ROW_PACKET;
+ } else if (conn_data->state == ROW_PACKET) {
+ if (server_status & MYSQL_STAT_MU) {
+ conn_data->state= RESPONSE_TABULAR;
+ } else {
+ conn_data->state= REQUEST;
+ }
+ } else if (conn_data->state == PREPARED_PARAMETERS) {
+ if (conn_data->stmt_num_fields > 0) {
+ conn_data->state= PREPARED_FIELDS;
+ } else {
+ conn_data->state= REQUEST;
+ }
+ } else if (conn_data->state == PREPARED_FIELDS) {
+ conn_data->state= REQUEST;
+ } else {
+ /* This should be an unreachable case */
+ conn_data->state= REQUEST;
+ }
+ }
- static gint *ett[]=
- {
- &ett_mysql,
- &ett_server_greeting,
- &ett_caps,
- &ett_extcaps,
- &ett_stat,
- &ett_request,
- &ett_refresh,
- &ett_field_flags
- };
+ else if (response_code == 0) {
+ if (conn_data->state == RESPONSE_PREPARE) {
+ offset = mysql_dissect_response_prepare(tvb, offset, tree, conn_data);
+ } else if (tvb_reported_length_remaining(tvb, offset+1) > tvb_get_fle(tvb, offset+1, NULL, NULL)) {
+ offset = mysql_dissect_ok_packet(tvb, pinfo, offset+1, tree, conn_data);
+ } else {
+ offset = mysql_dissect_result_header(tvb, pinfo, offset, tree, conn_data);
+ }
+ }
- module_t *mysql_module;
+ else {
+ switch (conn_data->state) {
+ case RESPONSE_MESSAGE:
+ if ((strlen = tvb_reported_length_remaining(tvb, offset))) {
+ proto_tree_add_item(tree, hf_mysql_message, tvb, offset, strlen, ENC_NA);
+ offset += strlen;
+ }
+ conn_data->state = REQUEST;
+ break;
- proto_mysql= proto_register_protocol("MySQL Protocol", "MySQL", "mysql");
- proto_register_field_array(proto_mysql, hf, array_length(hf));
- proto_register_subtree_array(ett, array_length(ett));
+ case RESPONSE_TABULAR:
+ offset = mysql_dissect_result_header(tvb, pinfo, offset, tree, conn_data);
+ break;
- mysql_module= prefs_register_protocol(proto_mysql, NULL);
- prefs_register_bool_preference(mysql_module, "desegment_buffers",
- "Reassemble MySQL buffers spanning multiple TCP segments",
- "Whether the MySQL dissector should reassemble MySQL buffers spanning multiple TCP segments."
- " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
- &mysql_desegment);
- prefs_register_bool_preference(mysql_module, "show_sql_query",
- "Show SQL Query string in INFO column",
- "Whether the MySQL dissector should display the SQL query string in the INFO column.",
- &mysql_showquery);
+ case FIELD_PACKET:
+ case RESPONSE_SHOW_FIELDS:
+ case RESPONSE_PREPARE:
+ case PREPARED_PARAMETERS:
+ offset = mysql_dissect_field_packet(tvb, offset, tree, conn_data);
+ break;
- register_dissector("mysql", dissect_mysql_pdu, proto_mysql);
-}
+ case ROW_PACKET:
+ offset = mysql_dissect_row_packet(tvb, offset, tree);
+ break;
+ case PREPARED_FIELDS:
+ offset = mysql_dissect_field_packet(tvb, offset, tree, conn_data);
+ break;
-/* dissector entrypoint, handles TCP-desegmentation */
-static void
-dissect_mysql(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
-{
- tcp_dissect_pdus(tvb, pinfo, tree, mysql_desegment, 3,
- get_mysql_pdu_len, dissect_mysql_pdu);
+ default:
+ proto_tree_add_string(tree, hf_mysql_payload, tvb, offset, -1,
+ "unknown/invalid response");
+ offset += tvb_reported_length_remaining(tvb, offset);
+ conn_data->state = UNDEFINED;
+ }
+ }
+
+ return offset;
}
-/* dissector helper: length of PDU */
-static guint
-get_mysql_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
-{
- guint plen= tvb_get_letoh24(tvb, offset);
- return plen + 4; /* add length field + packet number */
-}
-
-/* dissector main function: handle one PDU */
-static void
-dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+static int
+mysql_dissect_error_packet(tvbuff_t *tvb, packet_info *pinfo,
+ int offset, proto_tree *tree)
{
- proto_tree *mysql_tree= NULL;
- proto_item *ti;
- conversation_t *conversation;
- int offset = 0;
- guint packet_number;
- gboolean is_response;
- mysql_conn_data_t *conn_data;
-#ifdef CTDEBUG
- mysql_state_t conn_state_in, conn_state_out, frame_state;
- guint64 generation;
- proto_item *pi;
-#endif
- struct mysql_frame_data *mysql_frame_data_p;
-
- /* get conversation, create if neccessary*/
- conversation= find_or_create_conversation(pinfo);
+ if (check_col(pinfo->cinfo, COL_INFO)) {
+ col_append_fstr(pinfo->cinfo, COL_INFO, " Error %d", tvb_get_letohs(tvb, offset));
+ }
+ proto_tree_add_item(tree, hf_mysql_error_code, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ offset += 2;
- /* get associated state information, create if neccessary */
- conn_data= conversation_get_proto_data(conversation, proto_mysql);
- if (!conn_data) {
- conn_data= se_alloc(sizeof(mysql_conn_data_t));
- conn_data->srv_caps= 0;
- conn_data->clnt_caps= 0;
- conn_data->clnt_caps_ext= 0;
- conn_data->state= UNDEFINED;
- conn_data->stmts= g_hash_table_new(g_int_hash, g_int_equal);
-#ifdef CTDEBUG
- conn_data->generation= 0;
-#endif
- conversation_add_proto_data(conversation, proto_mysql, conn_data);
+ if (tvb_get_guint8(tvb, offset) == '#')
+ {
+ offset += 1;
+ proto_tree_add_item(tree, hf_mysql_sqlstate, tvb, offset, 5, ENC_NA);
+ offset += 5;
}
- mysql_frame_data_p = p_get_proto_data(pinfo->fd, proto_mysql);
- if (!mysql_frame_data_p) {
- /* We haven't seen this frame before. Store the state of the
- * conversation now so if/when we dissect the frame again
- * we'll start with the same state.
- */
- mysql_frame_data_p = se_alloc(sizeof(struct mysql_frame_data));
- mysql_frame_data_p->state = conn_data->state;
- p_add_proto_data(pinfo->fd, proto_mysql, mysql_frame_data_p);
+ proto_tree_add_item(tree, hf_mysql_error_string, tvb, offset, -1, ENC_NA);
+ offset += tvb_reported_length_remaining(tvb, offset);
- } else if (conn_data->state != FIELD_PACKET && conn_data->state != ROW_PACKET ) {
- /* We have seen this frame before. Set the connection state
- * to whatever state it had the first time we saw this frame
- * (e.g., based on whatever frames came before it).
- * The state may change as we dissect this packet.
- * XXX: I think the logic of the above else if test is as follows:
- * During the first (sequential) dissection pass thru the capture
- * file the conversation connection state as of the beginning of each frame
- * is saved in the connection_state for that frame.
- * Any state changes *within* a mysql "message" (ie: query/response/etc)
- * while processing mysql PDUS (aka "packets") in that message must be preserved.
- * It appears that FIELD_PACKET & ROW_PACKET are the only two
- * state changes which can occur within a mysql message which affect
- * subsequent processing within the message.
- * Question: Does this logic work OK for a reassembled message ?
- */
- conn_data->state= mysql_frame_data_p->state;
- }
+ return offset;
+}
- if (tree) {
- ti= proto_tree_add_item(tree, proto_mysql, tvb, offset, -1, FALSE);
- mysql_tree= proto_item_add_subtree(ti, ett_mysql);
- proto_tree_add_item(mysql_tree, hf_mysql_packet_length, tvb,
- offset, 3, TRUE);
- }
- offset+= 3;
- col_set_str(pinfo->cinfo, COL_PROTOCOL, "MySQL");
+static int
+mysql_dissect_ok_packet(tvbuff_t *tvb, packet_info *pinfo, int offset,
+ proto_tree *tree, mysql_conn_data_t *conn_data)
+{
+ gint strlen;
+ guint64 affected_rows;
+ guint64 insert_id;
+ int fle;
- if (pinfo->destport == pinfo->match_port) {
- is_response= FALSE;
- } else {
- is_response= TRUE;
- }
+ col_append_str(pinfo->cinfo, COL_INFO, " OK" );
- packet_number= tvb_get_guint8(tvb, offset);
- proto_tree_add_uint(mysql_tree, hf_mysql_packet_number, tvb,
- offset, 1, packet_number);
- offset+= 1;
+ fle = tvb_get_fle(tvb, offset, &affected_rows, NULL);
+ proto_tree_add_uint64(tree, hf_mysql_affected_rows, tvb, offset, fle, affected_rows);
+ offset += fle;
-#ifdef CTDEBUG
- conn_state_in= conn_data->state;
- frame_state = mysql_frame_data_p->state;
- generation= conn_data->generation;
- if (tree) {
- pi= proto_tree_add_text(mysql_tree, tvb, offset, 0, "conversation: %p", conversation);
- PROTO_ITEM_SET_GENERATED(pi);
- pi= proto_tree_add_text(mysql_tree, tvb, offset, 0, "generation: %" G_GINT64_MODIFIER "d", generation);
- PROTO_ITEM_SET_GENERATED(pi);
- pi= proto_tree_add_text(mysql_tree, tvb, offset, 0, "conn state: %s (%u)",
- val_to_str(conn_state_in, state_vals, "Unknown (%u)"),
- conn_state_in);
- PROTO_ITEM_SET_GENERATED(pi);
- pi= proto_tree_add_text(mysql_tree, tvb, offset, 0, "frame state: %s (%u)",
- val_to_str(frame_state, state_vals, "Unknown (%u)"),
- frame_state);
- PROTO_ITEM_SET_GENERATED(pi);
+ fle= tvb_get_fle(tvb, offset, &insert_id, NULL);
+ if (tree && insert_id) {
+ proto_tree_add_uint64(tree, hf_mysql_insert_id, tvb, offset, fle, insert_id);
}
-#endif
+ offset += fle;
- if (is_response) {
- if (packet_number == 0) {
- col_set_str(pinfo->cinfo, COL_INFO, "Server Greeting");
- offset= mysql_dissect_greeting(tvb, pinfo, offset,
- mysql_tree, conn_data);
- } else {
- col_set_str(pinfo->cinfo, COL_INFO, "Response");
- offset= mysql_dissect_response(tvb, pinfo, offset,
- mysql_tree, conn_data);
- }
- } else {
- if (packet_number == 1) {
- col_set_str(pinfo->cinfo, COL_INFO, "Login Request");
- offset= mysql_dissect_login(tvb, pinfo, offset,
- mysql_tree, conn_data);
- } else {
- col_set_str(pinfo->cinfo, COL_INFO, "Request");
- offset= mysql_dissect_request(tvb, pinfo, offset,
- mysql_tree, conn_data);
+ if (tvb_reported_length_remaining(tvb, offset) > 0) {
+ offset = mysql_dissect_server_status(tvb, offset, tree);
+
+ /* 4.1+ protocol only: 2 bytes number of warnings */
+ if (conn_data->clnt_caps & conn_data->srv_caps & MYSQL_CAPS_CU) {
+ proto_tree_add_item(tree, hf_mysql_num_warn, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ offset += 2;
}
}
-#ifdef CTDEBUG
- conn_state_out= conn_data->state;
- ++(conn_data->generation);
- pi= proto_tree_add_text(mysql_tree, tvb, offset, 0, "next proto state: %s (%u)",
- val_to_str(conn_state_out, state_vals, "Unknown (%u)"),
- conn_state_out);
- PROTO_ITEM_SET_GENERATED(pi);
-#endif
-
- /* remaining payload indicates an error */
- if (tree && tvb_reported_length_remaining(tvb, offset) > 0) {
- proto_tree_add_string(mysql_tree, hf_mysql_payload, tvb, offset, -1,
- "FIXME - dissector is incomplete");
+ /* optional: message string */
+ if (tvb_reported_length_remaining(tvb, offset) > 0) {
+ strlen = tvb_reported_length_remaining(tvb, offset);
+ proto_tree_add_item(tree, hf_mysql_message, tvb, offset, strlen, ENC_NA);
+ offset += strlen;
}
+
+ conn_data->state = REQUEST;
+ return offset;
}
static int
-mysql_dissect_greeting(tvbuff_t *tvb, packet_info *pinfo, int offset,
- proto_tree *tree, mysql_conn_data_t *conn_data)
+mysql_dissect_server_status(tvbuff_t *tvb, int offset, proto_tree *tree)
{
- gint protocol;
- gint strlen;
- gint32 thread_id;
-
proto_item *tf;
- proto_item *greeting_tree= NULL;
-
- protocol= tvb_get_guint8(tvb, offset);
-
- if (protocol == 0xff) {
- return mysql_dissect_error_packet(tvb, pinfo, offset+1, tree);
- }
-
- conn_data->state= LOGIN;
+ proto_item *stat_tree;
if (tree) {
- tf= proto_tree_add_text(tree, tvb, offset, -1, "Server Greeting");
- greeting_tree= proto_item_add_subtree(tf, ett_server_greeting);
- }
-
- if (check_col(pinfo->cinfo, COL_INFO)) {
- col_append_fstr(pinfo->cinfo, COL_INFO, " proto=%d", protocol) ;
+ tf= proto_tree_add_item(tree, hf_mysql_server_status, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ stat_tree= proto_item_add_subtree(tf, ett_stat);
+ proto_tree_add_item(stat_tree, hf_mysql_stat_it, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(stat_tree, hf_mysql_stat_ac, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(stat_tree, hf_mysql_stat_mr, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(stat_tree, hf_mysql_stat_mu, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(stat_tree, hf_mysql_stat_bi, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(stat_tree, hf_mysql_stat_ni, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(stat_tree, hf_mysql_stat_cr, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(stat_tree, hf_mysql_stat_lr, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(stat_tree, hf_mysql_stat_dr, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(stat_tree, hf_mysql_stat_bs, tvb, offset, 2, ENC_LITTLE_ENDIAN);
}
- proto_tree_add_uint(greeting_tree, hf_mysql_protocol, tvb,
- offset, 1, protocol);
- offset+= 1;
-
- /* version string */
- strlen= tvb_strsize(tvb,offset);
+ offset += 2;
- if (check_col(pinfo->cinfo, COL_INFO)) {
- col_append_fstr(pinfo->cinfo, COL_INFO, " version=%s",
- tvb_get_ptr(tvb, offset, strlen));
- }
- proto_tree_add_item(greeting_tree, hf_mysql_version, tvb,
- offset, strlen, FALSE );
- offset+= strlen;
+ return offset;
+}
- /* 4 bytes little endian thread_id */
- thread_id= tvb_get_letohl(tvb, offset);
- proto_tree_add_uint(greeting_tree, hf_mysql_thread_id, tvb,
- offset, 4, thread_id);
- offset+= 4;
- /* salt string */
- strlen= tvb_strsize(tvb,offset);
- proto_tree_add_item(greeting_tree, hf_mysql_salt, tvb,
- offset, strlen, FALSE );
- offset+=strlen;
+static int
+mysql_dissect_caps_server(tvbuff_t *tvb, int offset, proto_tree *tree, guint16 *caps)
+{
+ proto_item *tf;
+ proto_item *cap_tree;
- /* rest is optional */
- if (!tvb_reported_length_remaining(tvb, offset)) return offset;
+ *caps= tvb_get_letohs(tvb, offset);
- /* 2 bytes CAPS */
- offset= mysql_dissect_caps(tvb, offset, greeting_tree, &conn_data->srv_caps, "Server");
+ if (tree) {
+ tf = proto_tree_add_item(tree, hf_mysql_caps_server, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ cap_tree= proto_item_add_subtree(tf, ett_caps);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_long_password, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_found_rows, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_long_flag, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_connect_with_db, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_no_schema, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_compress, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_odbc, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_local_files, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_ignore_space, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_change_user, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_interactive, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_ssl, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_ignore_sigpipe, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_transactions, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_reserved, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_secure_connect, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ }
- /* rest is optional */
- if (!tvb_reported_length_remaining(tvb, offset)) return offset;
+ offset += 2;
+ return offset;
+}
- mysql_dissect_collation(tvb, offset, greeting_tree, conn_data->srv_caps,
- tvb_get_guint8(tvb, offset), hf_mysql_charset);
- offset++; /* for charset */
- offset= mysql_dissect_server_status(tvb, offset, greeting_tree);
+static int
+mysql_dissect_caps_client(tvbuff_t *tvb, int offset, proto_tree *tree, guint16 *caps)
+{
+ proto_item *tf;
+ proto_item *cap_tree;
- /* 13 bytes unused */
- proto_tree_add_item(greeting_tree, hf_mysql_unused, tvb,
- offset, 13, FALSE );
- offset+= 13;
+ *caps= tvb_get_letohs(tvb, offset);
- /* 4.1+ server: rest of salt */
- if (tvb_reported_length_remaining(tvb, offset)) {
- strlen= tvb_strsize(tvb,offset);
- proto_tree_add_item(greeting_tree, hf_mysql_salt2, tvb,
- offset, strlen, FALSE );
- offset+= strlen;
+ if (tree) {
+ tf = proto_tree_add_item(tree, hf_mysql_caps_client, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ cap_tree= proto_item_add_subtree(tf, ett_caps);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_long_password, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_found_rows, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_long_flag, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_connect_with_db, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_no_schema, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_compress, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_odbc, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_local_files, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_ignore_space, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_change_user, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_interactive, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_ssl, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_ignore_sigpipe, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_transactions, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_reserved, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(cap_tree, hf_mysql_cap_secure_connect, tvb, offset, 2, ENC_LITTLE_ENDIAN);
}
+ offset += 2;
return offset;
}
-
-
static int
-mysql_dissect_login(tvbuff_t *tvb, packet_info *pinfo, int offset,
- proto_tree *tree, mysql_conn_data_t *conn_data)
+mysql_dissect_ext_caps_client(tvbuff_t *tvb, int offset, proto_tree *tree, guint16 *ext_caps)
{
- guint16 ext_caps;
- guint32 max_packet;
- gint strlen;
-
proto_item *tf;
- proto_item *login_tree= NULL;
-
- /* after login there can be OK or DENIED */
- conn_data->state= RESPONSE_OK;
-
+ proto_item *extcap_tree;
+ *ext_caps= tvb_get_letohs(tvb, offset);
if (tree) {
- tf= proto_tree_add_text(tree, tvb, offset, -1, "Login Request");
- login_tree= proto_item_add_subtree(tf, ett_server_greeting);
+ tf = proto_tree_add_item(tree, hf_mysql_extcaps_client, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ extcap_tree = proto_item_add_subtree(tf, ett_extcaps);
+ proto_tree_add_item(extcap_tree, hf_mysql_cap_multi_statements, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(extcap_tree, hf_mysql_cap_multi_results, tvb, offset, 2, ENC_LITTLE_ENDIAN);
}
- offset= mysql_dissect_caps(tvb, offset, login_tree, &conn_data->clnt_caps, "Client");
+ offset += 2;
+ return offset;
+}
- if (conn_data->clnt_caps & MYSQL_CAPS_CU) /* 4.1 protocol */
- {
- offset= mysql_dissect_ext_caps(tvb, offset, login_tree, &ext_caps, "Client");
- conn_data->clnt_caps_ext= ext_caps;
- max_packet= tvb_get_letohl(tvb, offset);
- proto_tree_add_uint(login_tree, hf_mysql_max_packet, tvb,
- offset, 4, max_packet);
- offset+= 4;
+static int
+mysql_dissect_result_header(tvbuff_t *tvb, packet_info *pinfo, int offset,
+ proto_tree *tree, mysql_conn_data_t *conn_data)
+{
+ gint fle;
+ guint64 num_fields, extra;
- mysql_dissect_collation(tvb, offset, login_tree, conn_data->clnt_caps,
- tvb_get_guint8(tvb, offset), hf_mysql_charset);
- offset++; /* for charset */
+ col_append_str(pinfo->cinfo, COL_INFO, " TABULAR" );
- offset+= 23; /* filler bytes */
+ fle = tvb_get_fle(tvb, offset, &num_fields, NULL);
+ proto_tree_add_uint64(tree, hf_mysql_num_fields, tvb, offset, fle, num_fields);
+ offset += fle;
- } else { /* pre-4.1 */
- max_packet= 0xffffff - tvb_get_letoh24(tvb, offset);
- proto_tree_add_uint(login_tree, hf_mysql_max_packet, tvb,
- offset, 3, max_packet);
- offset+= 3;
+ if (tvb_reported_length_remaining(tvb, offset)) {
+ fle = tvb_get_fle(tvb, offset, &extra, NULL);
+ proto_tree_add_uint64(tree, hf_mysql_extra, tvb, offset, fle, extra);
+ offset += fle;
}
- /* User name */
- strlen= my_tvb_strsize(tvb, offset);
- if (check_col(pinfo->cinfo, COL_INFO)) {
- col_append_fstr(pinfo->cinfo, COL_INFO, " user=%s",
- tvb_get_ptr(tvb,offset,strlen));
+ if (num_fields) {
+ conn_data->state = FIELD_PACKET;
+ } else {
+ conn_data->state = ROW_PACKET;
}
- proto_tree_add_item(login_tree, hf_mysql_user, tvb,
- offset, strlen, FALSE );
- offset+= strlen;
- /* rest is optional */
- if (!tvb_reported_length_remaining(tvb, offset)) return offset;
+ return offset;
+}
- /* password: asciiz or length+ascii */
- if (conn_data->clnt_caps & MYSQL_CAPS_SC) {
- strlen= tvb_get_guint8(tvb, offset);
- offset+= 1;
- } else {
- strlen= my_tvb_strsize(tvb, offset);
- }
- if (tree && strlen > 1) {
- proto_tree_add_item(login_tree, hf_mysql_passwd,
- tvb, offset, strlen, FALSE);
- }
- offset+= strlen;
- /* optional: initial schema */
- if (conn_data->clnt_caps & MYSQL_CAPS_CD)
- {
- strlen= my_tvb_strsize(tvb,offset);
- if(strlen<0){
- return offset;
- }
+/*
+ * Add length encoded string to tree
+ */
+static int
+mysql_field_add_lestring(tvbuff_t *tvb, int offset, proto_tree *tree, int field)
+{
+ guint64 lelen;
+ guint8 is_null;
- if (check_col(pinfo->cinfo, COL_INFO)) {
- /* ugly hack: copy database to new buffer*/
- guint8 buf[65];
- if (strlen > 64)
- strlen= 64;
- tvb_memcpy(tvb, buf, offset, strlen);
- buf[strlen]= '\0';
- col_append_fstr(pinfo->cinfo, COL_INFO, " db=%s", buf);
- }
- proto_tree_add_item(login_tree, hf_mysql_schema, tvb,
- offset, strlen, FALSE );
- offset+= strlen;
+ offset += tvb_get_fle(tvb, offset, &lelen, &is_null);
+ if(is_null)
+ proto_tree_add_string(tree, field, tvb, offset, 4, "NULL");
+ else
+ {
+ proto_tree_add_item(tree, field, tvb, offset, (int)lelen, ENC_NA);
+ offset += (int)lelen;
}
-
return offset;
}
static int
-mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset,
- proto_tree *tree, mysql_conn_data_t *conn_data)
+mysql_dissect_field_packet(tvbuff_t *tvb, int offset, proto_tree *tree, mysql_conn_data_t *conn_data _U_)
{
- gint opcode;
- gint strlen;
proto_item *tf;
- proto_item *req_tree= NULL;
- guint16 option;
-
- if (tree) {
- tf= proto_tree_add_text(tree, tvb, offset, -1, "Command");
- req_tree= proto_item_add_subtree(tf, ett_request);
- }
+ proto_item *flags_tree;
- opcode= tvb_get_guint8(tvb, offset);
- if (check_col(pinfo->cinfo, COL_INFO)) {
- col_append_fstr(pinfo->cinfo, COL_INFO, " %s",
- val_to_str(opcode, mysql_opcode_vals, "Unknown (%u)"));
- }
- proto_tree_add_uint_format(req_tree, hf_mysql_opcode, tvb,
- offset, 1, opcode, "Command: %s (%u)",
- val_to_str(opcode, mysql_opcode_vals, "Unknown (%u)"),
- opcode);
- offset+= 1;
+ offset = mysql_field_add_lestring(tvb, offset, tree, hf_mysql_fld_catalog);
+ offset = mysql_field_add_lestring(tvb, offset, tree, hf_mysql_fld_db);
+ offset = mysql_field_add_lestring(tvb, offset, tree, hf_mysql_fld_table);
+ 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);
+ offset +=1; /* filler */
+ proto_tree_add_item(tree, hf_mysql_fld_charsetnr, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ offset += 2; /* charset */
- switch (opcode) {
+ proto_tree_add_item(tree, hf_mysql_fld_length, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ offset += 4; /* length */
- case MYSQL_QUIT:
- if (conn_data->stmts) {
- g_hash_table_destroy(conn_data->stmts);
- conn_data->stmts= NULL;
- }
- break;
+ proto_tree_add_item(tree, hf_mysql_fld_type, tvb, offset, 1, ENC_NA);
+ offset += 1; /* type */
- case MYSQL_PROCESS_INFO:
- conn_data->state= RESPONSE_TABULAR;
- break;
+ tf = proto_tree_add_item(tree, hf_mysql_fld_flags, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ flags_tree = proto_item_add_subtree(tf, ett_field_flags);
+ proto_tree_add_item(flags_tree, hf_mysql_fld_not_null, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(flags_tree, hf_mysql_fld_primary_key, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(flags_tree, hf_mysql_fld_unique_key, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(flags_tree, hf_mysql_fld_multiple_key, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(flags_tree, hf_mysql_fld_blob, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(flags_tree, hf_mysql_fld_unsigned, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(flags_tree, hf_mysql_fld_zero_fill, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(flags_tree, hf_mysql_fld_binary, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(flags_tree, hf_mysql_fld_enum, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(flags_tree, hf_mysql_fld_auto_increment, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(flags_tree, hf_mysql_fld_timestamp, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(flags_tree, hf_mysql_fld_set, tvb, offset, 2, ENC_LITTLE_ENDIAN);
+ offset += 2; /* flags */
- case MYSQL_DEBUG:
- case MYSQL_PING:
- conn_data->state= RESPONSE_OK;
- break;
+ proto_tree_add_item(tree, hf_mysql_fld_decimals, tvb, offset, 1, ENC_NA);
+ offset += 1; /* decimals */
- case MYSQL_STATISTICS:
- conn_data->state= RESPONSE_MESSAGE;
- break;
+ offset += 2; /* filler */
- case MYSQL_INIT_DB:
- case MYSQL_CREATE_DB:
- case MYSQL_DROP_DB:
- strlen= my_tvb_strsize(tvb, offset);
- proto_tree_add_item(req_tree, hf_mysql_schema, tvb,
- offset, strlen, FALSE);
- offset+= strlen;
- conn_data->state= RESPONSE_OK;
- break;
+ /* default (Only use for show fields) */
+ if (tree && tvb_reported_length_remaining(tvb, offset) > 0) {
+ offset = mysql_field_add_lestring(tvb, offset, tree, hf_mysql_fld_default);
+ }
+ return offset;
+}
- case MYSQL_QUERY:
- strlen= my_tvb_strsize(tvb, offset);
- proto_tree_add_item(req_tree, hf_mysql_query, tvb,
- offset, strlen, FALSE);
- if (mysql_showquery) {
- if (check_col(pinfo->cinfo, COL_INFO))
- col_append_fstr(pinfo->cinfo, COL_INFO, " { %s } ",
- tvb_get_ephemeral_string(tvb, offset, strlen));
- }
- offset+= strlen;
- conn_data->state= RESPONSE_TABULAR;
- break;
- case MYSQL_STMT_PREPARE:
- strlen= my_tvb_strsize(tvb, offset);
- proto_tree_add_item(req_tree, hf_mysql_query, tvb,
- offset, strlen, FALSE);
- offset+= strlen;
- conn_data->state= RESPONSE_PREPARE;
- break;
+static int
+mysql_dissect_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);
+ }
- case MYSQL_STMT_CLOSE:
- if (conn_data->stmts) {
- gint stmt= tvb_get_letohl(tvb, offset);
- g_hash_table_remove(conn_data->stmts, &stmt);
- }
- proto_tree_add_item(req_tree, hf_mysql_stmt_id,
- tvb, offset, 4, TRUE);
- offset+= 4;
- conn_data->state= REQUEST;
- break;
+ return offset;
+}
- case MYSQL_STMT_RESET:
- proto_tree_add_item(req_tree, hf_mysql_stmt_id,
- tvb, offset, 4, TRUE);
- offset+= 4;
- conn_data->state= RESPONSE_OK;
- break;
- case MYSQL_FIELD_LIST:
- strlen= my_tvb_strsize(tvb, offset);
- proto_tree_add_text(req_tree, tvb, offset, strlen, "Table name: %s",
- tvb_get_ephemeral_string(tvb, offset, strlen));
- offset+= strlen;
- conn_data->state= RESPONSE_SHOW_FIELDS;
- break;
+static int
+mysql_dissect_response_prepare(tvbuff_t *tvb, int offset, proto_tree *tree, mysql_conn_data_t *conn_data)
+{
+ /* 0, marker for OK packet */
+ offset += 1;
+ proto_tree_add_item(tree, hf_mysql_stmt_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
+ 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);
+ 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);
+ 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)
+ conn_data->state = PREPARED_PARAMETERS;
+ else if (conn_data->stmt_num_fields > 0)
+ conn_data->state = PREPARED_FIELDS;
+ else
+ conn_data->state = REQUEST;
- case MYSQL_PROCESS_KILL:
- proto_tree_add_item(req_tree, hf_mysql_thd_id,
- tvb, offset, 4, TRUE);
- offset+= 4;
- conn_data->state= RESPONSE_OK;
- break;
+ return offset + tvb_reported_length_remaining(tvb, offset);
+}
- case MYSQL_CHANGE_USER:
- strlen= tvb_strsize(tvb, offset);
- proto_tree_add_item(req_tree, hf_mysql_user, tvb,
- offset, strlen, FALSE);
- offset+= strlen;
-
- strlen= tvb_strsize(tvb, offset);
- proto_tree_add_item(req_tree, hf_mysql_passwd, tvb,
- offset, strlen, FALSE);
- offset+= strlen;
-
- strlen= my_tvb_strsize(tvb, offset);
- proto_tree_add_item(req_tree, hf_mysql_schema, tvb,
- offset, strlen, FALSE);
- offset+= strlen;
- conn_data->state= RESPONSE_OK;
- break;
- case MYSQL_REFRESH:
- {
- proto_item *tff;
- proto_item *rfsh_tree;
- gint refresh= tvb_get_guint8(tvb, offset);
-
- if (req_tree) {
- tff= proto_tree_add_uint_format(req_tree, hf_mysql_refresh, tvb, offset, 1, refresh, "Refresh Bitmap: 0x%02X ", refresh);
- rfsh_tree= proto_item_add_subtree(tff, ett_refresh);
- proto_tree_add_boolean(rfsh_tree, hf_mysql_rfsh_grants, tvb, offset, 1, refresh);
- proto_tree_add_boolean(rfsh_tree, hf_mysql_rfsh_log, tvb, offset, 1, refresh);
- proto_tree_add_boolean(rfsh_tree, hf_mysql_rfsh_tables, tvb, offset, 1, refresh);
- proto_tree_add_boolean(rfsh_tree, hf_mysql_rfsh_hosts, tvb, offset, 1, refresh);
- proto_tree_add_boolean(rfsh_tree, hf_mysql_rfsh_status, tvb, offset, 1, refresh);
- proto_tree_add_boolean(rfsh_tree, hf_mysql_rfsh_threads, tvb, offset, 1, refresh);
- proto_tree_add_boolean(rfsh_tree, hf_mysql_rfsh_slave, tvb, offset, 1, refresh);
- proto_tree_add_boolean(rfsh_tree, hf_mysql_rfsh_master, tvb, offset, 1, refresh);
- }
- }
- offset+= 1;
- conn_data->state= RESPONSE_OK;
- break;
- case MYSQL_SHUTDOWN:
- opcode= tvb_get_guint8(tvb, offset);
- proto_tree_add_uint_format(req_tree, hf_mysql_opcode, tvb, offset,
- 1, opcode, "Shutdown-Level: %s",
- val_to_str(opcode, mysql_shutdown_vals, "Unknown (%u)"));
- offset+= 1;
- conn_data->state= RESPONSE_OK;
- break;
+/*
+ get length of string in packet buffer
- case MYSQL_SET_OPTION:
- option= tvb_get_letohs(tvb, offset);
- proto_tree_add_uint_format(req_tree, hf_mysql_option, tvb, offset,
- 2, option, "Option: %s",
- val_to_str(option, mysql_option_vals, "Unknown (%u)"));
- offset+= 2;
- conn_data->state= RESPONSE_OK;
- break;
+ SYNOPSIS
+ my_tvb_strsize()
+ tvb packet buffer
+ offset current offset
- case MYSQL_STMT_FETCH:
- proto_tree_add_item(req_tree, hf_mysql_stmt_id,
- tvb, offset, 4, TRUE);
- offset+= 4;
+ DESCRIPTION
+ deliver length of string, delimited by either \0 or end of buffer
- proto_tree_add_item(req_tree, hf_mysql_num_rows,
- tvb, offset, 4, TRUE);
- offset+= 4;
- conn_data->state= RESPONSE_TABULAR;
- break;
+ RETURN VALUE
+ length of string found, including \0 (if present)
- case MYSQL_STMT_SEND_LONG_DATA:
- proto_tree_add_item(req_tree, hf_mysql_stmt_id,
- tvb, offset, 4, TRUE);
- offset+= 4;
+*/
+static gint
+my_tvb_strsize(tvbuff_t *tvb, int offset)
+{
+ gint len = tvb_strnlen(tvb, offset, -1);
+ if (len == -1) {
+ len = tvb_reported_length_remaining(tvb, offset);
+ } else {
+ len++; /* the trailing \0 */
+ }
+ return len;
+}
- proto_tree_add_item(req_tree, hf_mysql_param,
- tvb, offset, 2, TRUE);
- offset+= 2;
- /* rest is data */
- strlen= tvb_reported_length_remaining(tvb, offset);
- if (tree && strlen > 0) {
- proto_tree_add_item(req_tree, hf_mysql_payload,
- tvb, offset, strlen, FALSE);
- }
- offset+= strlen;
- conn_data->state= REQUEST;
- break;
+/*
+ read "field length encoded" value from packet buffer
- case MYSQL_STMT_EXECUTE:
- proto_tree_add_item(req_tree, hf_mysql_stmt_id,
- tvb, offset, 4, TRUE);
- offset+= 4;
+ SYNOPSIS
+ tvb_get_fle()
+ tvb in packet buffer
+ offset in offset in buffer
+ res out where to store FLE value, may be NULL
+ is_null out where to store ISNULL flag, may be NULL
- proto_tree_add_item(req_tree, hf_mysql_exec_flags,
- tvb, offset, 1, TRUE);
- offset+= 1;
+ DESCRIPTION
+ read FLE from packet buffer and store its value and ISNULL flag
+ in caller provided variables
- proto_tree_add_item(req_tree, hf_mysql_exec_iter,
- tvb, offset, 4, TRUE);
- offset+= 4;
+ RETURN VALUE
+ length of FLE
+*/
+static int
+tvb_get_fle(tvbuff_t *tvb, int offset, guint64 *res, guint8 *is_null)
+{
+ guint8 prefix;
-#if 0
-/* FIXME: rest needs metadata about statement */
-#else
- strlen= tvb_reported_length_remaining(tvb, offset);
- if (tree && strlen > 0) {
- proto_tree_add_string(req_tree, hf_mysql_payload, tvb, offset,
- strlen, "FIXME: execute dissector incomplete");
- }
- offset+= strlen;
-#endif
- conn_data->state= RESPONSE_TABULAR;
- break;
+ prefix = tvb_get_guint8(tvb, offset);
-/* FIXME: implement replication packets */
- case MYSQL_BINLOG_DUMP:
- case MYSQL_TABLE_DUMP:
- case MYSQL_CONNECT_OUT:
- case MYSQL_REGISTER_SLAVE:
- proto_tree_add_string(req_tree, hf_mysql_payload, tvb, offset, -1,
- "FIXME: implement replication packets");
- offset+= tvb_reported_length_remaining(tvb, offset);
- conn_data->state= REQUEST;
- break;
+ if (is_null)
+ *is_null = 0;
+ switch (prefix) {
+ case 251:
+ if (res)
+ *res = 0;
+ if (is_null)
+ *is_null = 1;
+ break;
+ case 252:
+ if (res)
+ *res = tvb_get_letohs(tvb, offset+1);
+ return 3;
+ case 253:
+ if (res)
+ *res = tvb_get_letohl(tvb, offset+1);
+ return 5;
+ case 254:
+ if (res)
+ *res = tvb_get_letoh64(tvb, offset+1);
+ return 9;
default:
- proto_tree_add_string(req_tree, hf_mysql_payload, tvb, offset, -1,
- "unknown/invalid command code");
- offset+= tvb_reported_length_remaining(tvb, offset);
- conn_data->state= UNDEFINED;
+ if (res)
+ *res = prefix;
}
- return offset;
+ return 1;
}
-
-static int
-mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo, int offset,
- proto_tree *tree, mysql_conn_data_t *conn_data)
+/* protocol registration */
+void proto_register_mysql(void)
{
- gint response_code;
- gint strlen;
+ static hf_register_info hf[]=
+ {
+ { &hf_mysql_packet_length,
+ { "Packet Length", "mysql.packet_length",
+ FT_UINT24, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
- response_code= tvb_get_guint8(tvb, offset);
+ { &hf_mysql_packet_number,
+ { "Packet Number", "mysql.packet_number",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
- if (response_code == 0xff ) {
- offset= mysql_dissect_error_packet(tvb, pinfo, offset+1, tree);
- conn_data->state= REQUEST;
- }
+ { &hf_mysql_request,
+ { "Request Command", "mysql.request",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
- else if (response_code == 0xfe && tvb_reported_length_remaining(tvb, offset) < 9) {
+ { &hf_mysql_command,
+ { "Command", "mysql.command",
+ FT_UINT8, BASE_DEC, VALS(mysql_command_vals), 0x0,
+ NULL, HFILL }},
- proto_tree_add_uint_format(tree, hf_mysql_eof, tvb, offset, 1,
- response_code, "EOF marker (%u)",
- response_code);
- offset+= 1;
+ { &hf_mysql_error_code,
+ { "Error Code", "mysql.error_code",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
- /* pre-4.1 packet ends here */
- if (tvb_reported_length_remaining(tvb, offset)) {
- proto_tree_add_item(tree, hf_mysql_num_warn,
- tvb, offset, 2, FALSE);
- offset= mysql_dissect_server_status(tvb, offset+2, tree);
- }
+ { &hf_mysql_error_string,
+ { "Error message", "mysql.error.message",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "Error string in case of MySQL error message", HFILL }},
- if (conn_data->state == FIELD_PACKET) {
- conn_data->state= ROW_PACKET;
- } else {
- conn_data->state= REQUEST;
- }
- }
+ { &hf_mysql_sqlstate,
+ { "SQL state", "mysql.sqlstate",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
- else if (response_code == 0) {
- if (tvb_reported_length_remaining(tvb, offset+1)
- > tvb_get_fle(tvb, offset+1, NULL, NULL)) {
- offset= mysql_dissect_ok_packet(tvb, pinfo, offset+1,
- tree, conn_data);
- } else {
- offset= mysql_dissect_result_header(tvb, pinfo, offset,
- tree, conn_data);
- }
- }
+ { &hf_mysql_message,
+ { "Message", "mysql.message",
+ FT_STRINGZ, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
- else {
- switch (conn_data->state) {
- case RESPONSE_MESSAGE:
- if ((strlen= tvb_reported_length_remaining(tvb, offset))) {
- proto_tree_add_item(tree, hf_mysql_message, tvb,
- offset, strlen, FALSE);
- offset+= strlen;
- }
- conn_data->state= REQUEST;
- break;
+ { &hf_mysql_server_greeting,
+ { "Server Greeting", "mysql.server_greeting",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
- case RESPONSE_TABULAR:
- offset= mysql_dissect_result_header(tvb, pinfo, offset,
- tree, conn_data);
- break;
+ { &hf_mysql_protocol,
+ { "Protocol", "mysql.protocol",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "Protocol Version", HFILL }},
- case FIELD_PACKET:
- case RESPONSE_SHOW_FIELDS:
- offset= mysql_dissect_field_packet(tvb, offset, tree, conn_data);
- break;
+ { &hf_mysql_version,
+ { "Version", "mysql.version",
+ FT_STRINGZ, BASE_NONE, NULL, 0x0,
+ "MySQL Version", HFILL }},
- case ROW_PACKET:
- offset= mysql_dissect_row_packet(tvb, offset, tree);
- break;
+ { &hf_mysql_caps_server,
+ { "Server Capabilities", "mysql.caps.server",
+ FT_UINT16, BASE_HEX, NULL, 0x0,
+ "MySQL Capabilities", HFILL }},
- case RESPONSE_PREPARE:
- offset= mysql_dissect_response_prepare(tvb, offset, tree);
- break;
+ { &hf_mysql_caps_client,
+ { "Client Capabilities", "mysql.caps.client",
+ FT_UINT16, BASE_HEX, NULL, 0x0,
+ "MySQL Capabilities", HFILL }},
- case PARAM_PACKET:
- offset= mysql_dissect_param_packet(tvb, offset, tree);
- break;
+ { &hf_mysql_cap_long_password,
+ { "Long Password","mysql.caps.lp",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_LP,
+ NULL, HFILL }},
- default:
- proto_tree_add_string(tree, hf_mysql_payload, tvb, offset, -1,
- "unknown/invalid response");
- offset+= tvb_reported_length_remaining(tvb, offset);
- conn_data->state= UNDEFINED;
- }
- }
+ { &hf_mysql_cap_found_rows,
+ { "Found Rows","mysql.caps.fr",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_FR,
+ NULL, HFILL }},
- return offset;
-}
+ { &hf_mysql_cap_long_flag,
+ { "Long Column Flags","mysql.caps.lf",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_LF,
+ NULL, HFILL }},
+ { &hf_mysql_cap_connect_with_db,
+ { "Connect With Database","mysql.caps.cd",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_CD,
+ NULL, HFILL }},
-static int
-mysql_dissect_error_packet(tvbuff_t *tvb, packet_info *pinfo,
- int offset, proto_tree *tree)
-{
- gint error_code;
- error_code= tvb_get_letohs(tvb, offset);
+ { &hf_mysql_cap_no_schema,
+ { "Don't Allow database.table.column","mysql.caps.ns",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_NS,
+ NULL, HFILL }},
- if (check_col(pinfo->cinfo, COL_INFO)) {
- col_append_fstr(pinfo->cinfo, COL_INFO, " Error %d", error_code);
- }
- proto_tree_add_uint(tree, hf_mysql_error_code, tvb,
- offset, 2, error_code);
- offset+= 2;
+ { &hf_mysql_cap_compress,
+ { "Can use compression protocol","mysql.caps.cp",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_CP,
+ NULL, HFILL }},
- if (tvb_get_guint8(tvb, offset) == '#')
- {
- offset+= 1;
- proto_tree_add_item(tree, hf_mysql_sqlstate, tvb, offset, 5, FALSE);
- offset+= 5;
- }
+ { &hf_mysql_cap_odbc,
+ { "ODBC Client","mysql.caps.ob",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_OB,
+ NULL, HFILL }},
- proto_tree_add_item(tree, hf_mysql_error_string, tvb, offset, -1, FALSE);
- offset+= tvb_reported_length_remaining(tvb, offset);
+ { &hf_mysql_cap_local_files,
+ { "Can Use LOAD DATA LOCAL","mysql.caps.li",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_LI,
+ NULL, HFILL }},
- return offset;
-}
+ { &hf_mysql_cap_ignore_space,
+ { "Ignore Spaces before '('","mysql.caps.is",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_IS,
+ NULL, HFILL }},
+ { &hf_mysql_cap_change_user,
+ { "Speaks 4.1 protocol (new flag)","mysql.caps.cu",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_CU,
+ NULL, HFILL }},
-static int
-mysql_dissect_ok_packet(tvbuff_t *tvb, packet_info *pinfo, int offset,
- proto_tree *tree, mysql_conn_data_t *conn_data)
-{
- gint strlen;
- guint64 affected_rows;
- guint64 insert_id;
- int fle;
+ { &hf_mysql_cap_interactive,
+ { "Interactive Client","mysql.caps.ia",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_IA,
+ NULL, HFILL }},
- col_append_str(pinfo->cinfo, COL_INFO, " OK" );
+ { &hf_mysql_cap_ssl,
+ { "Switch to SSL after handshake","mysql.caps.sl",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_SL,
+ NULL, HFILL }},
- fle= tvb_get_fle(tvb, offset, &affected_rows, NULL);
- proto_tree_add_uint64(tree, hf_mysql_affected_rows,
- tvb, offset, fle, affected_rows);
- offset+= fle;
+ { &hf_mysql_cap_ignore_sigpipe,
+ { "Ignore sigpipes","mysql.caps.ii",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_II,
+ NULL, HFILL }},
- fle= tvb_get_fle(tvb, offset, &insert_id, NULL);
- if (tree && insert_id) {
- proto_tree_add_uint64(tree, hf_mysql_insert_id,
- tvb, offset, fle, insert_id);
- }
- offset+= fle;
+ { &hf_mysql_cap_transactions,
+ { "Knows about transactions","mysql.caps.ta",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_TA,
+ NULL, HFILL }},
- if ((strlen= tvb_reported_length_remaining(tvb, offset))) {
- offset= mysql_dissect_server_status(tvb, offset, tree);
+ { &hf_mysql_cap_reserved,
+ { "Speaks 4.1 protocol (old flag)","mysql.caps.rs",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_RS,
+ NULL, HFILL }},
- /* 4.1+ protocol only: 2 bytes number of warnings */
- if (conn_data->clnt_caps & conn_data->srv_caps & MYSQL_CAPS_CU) {
- proto_tree_add_item(tree, hf_mysql_num_warn, tvb,
- offset, 2, FALSE);
- offset+= 2;
- }
- }
+ { &hf_mysql_cap_secure_connect,
+ { "Can do 4.1 authentication","mysql.caps.sc",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_SC,
+ NULL, HFILL }},
- /* optional: message string */
- if ((strlen= tvb_reported_length_remaining(tvb, offset))) {
- proto_tree_add_item(tree, hf_mysql_message, tvb,
- offset, strlen, FALSE);
- offset+= strlen;
- }
+ { &hf_mysql_extcaps_client,
+ { "Extended Client Capabilities", "mysql.extcaps.client",
+ FT_UINT16, BASE_HEX, NULL, 0x0,
+ "MySQL Extended Capabilities", HFILL }},
- conn_data->state= REQUEST;
- return offset;
-}
+ { &hf_mysql_cap_multi_statements,
+ { "Supports multiple statements","mysql.caps.ms",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_MS,
+ NULL, HFILL }},
+ { &hf_mysql_cap_multi_results,
+ { "Supports multiple results","mysql.caps.mr",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_CAPS_MR,
+ NULL, HFILL }},
-static int
-mysql_dissect_server_status(tvbuff_t *tvb, int offset, proto_tree *tree)
-{
- guint16 status;
- proto_item *tf;
- proto_item *stat_tree;
+ { &hf_mysql_login_request,
+ { "Login Request", "mysql.login_request",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
- status= tvb_get_letohs(tvb, offset);
- if (tree) {
- tf= proto_tree_add_uint_format(tree, hf_mysql_status, tvb, offset, 2, status, "Server Status: 0x%04X ", status);
- stat_tree= proto_item_add_subtree(tf, ett_stat);
- proto_tree_add_boolean(stat_tree, hf_mysql_stat_it, tvb, offset, 2, status);
- proto_tree_add_boolean(stat_tree, hf_mysql_stat_ac, tvb, offset, 2, status);
- proto_tree_add_boolean(stat_tree, hf_mysql_stat_mr, tvb, offset, 2, status);
- proto_tree_add_boolean(stat_tree, hf_mysql_stat_mu, tvb, offset, 2, status);
- proto_tree_add_boolean(stat_tree, hf_mysql_stat_bi, tvb, offset, 2, status);
- proto_tree_add_boolean(stat_tree, hf_mysql_stat_ni, tvb, offset, 2, status);
- proto_tree_add_boolean(stat_tree, hf_mysql_stat_cr, tvb, offset, 2, status);
- proto_tree_add_boolean(stat_tree, hf_mysql_stat_lr, tvb, offset, 2, status);
- proto_tree_add_boolean(stat_tree, hf_mysql_stat_dr, tvb, offset, 2, status);
- proto_tree_add_boolean(stat_tree, hf_mysql_stat_bs, tvb, offset, 2, status);
- }
- offset+= 2;
+ { &hf_mysql_max_packet,
+ { "MAX Packet", "mysql.max_packet",
+ FT_UINT24, BASE_DEC, NULL, 0x0,
+ "MySQL Max packet", HFILL }},
- return offset;
-}
+ { &hf_mysql_charset,
+ { "Charset", "mysql.charset",
+ FT_UINT8, BASE_DEC, VALS(mysql_collation_vals), 0x0,
+ "MySQL Charset", HFILL }},
+ { &hf_mysql_table_name,
+ { "Table Name", "mysql.table_name",
+ FT_STRINGZ, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
-static void
-mysql_dissect_collation(tvbuff_t *tvb, int offset, proto_tree *tree, guint16 caps, gint charset, int field)
-{
- proto_tree_add_uint_format(tree, field, tvb, offset, 1,
- charset, "Charset: %s (%u)",
- val_to_str(charset,
- caps & MYSQL_CAPS_CU
- ? mysql_collation_vals
- : mysql_charset_vals,
- "Unknown (%u)"), charset);
-}
+ { &hf_mysql_user,
+ { "Username", "mysql.user",
+ FT_STRINGZ, BASE_NONE, NULL, 0x0,
+ "Login Username", HFILL }},
+ { &hf_mysql_schema,
+ { "Schema", "mysql.schema",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "Login Schema", HFILL }},
-static int
-mysql_dissect_caps(tvbuff_t *tvb, int offset, proto_tree *tree,
- guint16 *caps, const char* whom)
-{
- *caps= tvb_get_letohs(tvb, offset);
- if (tree) {
- proto_item *tf= proto_tree_add_uint_format(tree, hf_mysql_caps, tvb, offset, 2, *caps,
- "%s Capabilities: 0x%04X ", whom, *caps);
- proto_item *cap_tree= proto_item_add_subtree(tf, ett_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_long_password, tvb, offset, 2, *caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_found_rows, tvb, offset, 2, *caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_long_flag, tvb, offset, 2, *caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_connect_with_db, tvb, offset, 2, *caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_no_schema, tvb, offset, 2, *caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_compress, tvb, offset, 2, *caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_odbc, tvb, offset, 2, *caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_local_files, tvb, offset, 2, *caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_ignore_space, tvb, offset, 2, *caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_change_user, tvb, offset, 2, *caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_interactive, tvb, offset, 2, *caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_ssl, tvb, offset, 2, *caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_ignore_sigpipe, tvb, offset, 2, *caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_transactions, tvb, offset, 2, *caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_reserved, tvb, offset, 2, *caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_secure_connect, tvb, offset, 2, *caps);
- }
+ { &hf_mysql_salt,
+ { "Salt", "mysql.salt",
+ FT_STRINGZ, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
- offset+= 2;
- return offset;
-}
+ { &hf_mysql_salt2,
+ { "Salt", "mysql.salt2",
+ FT_STRINGZ, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+ { &hf_mysql_thread_id,
+ { "Thread ID", "mysql.thread_id",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "MySQL Thread ID", HFILL }},
-static int
-mysql_dissect_ext_caps(tvbuff_t *tvb, int offset, proto_tree *tree,
- guint16 *caps, const char* whom)
-{
- proto_item *extcap_tree;
- *caps= tvb_get_letohs(tvb, offset);
- if (tree) {
- proto_item *tf= proto_tree_add_uint_format(tree, hf_mysql_extcaps, tvb, offset, 2, *caps,
- "Extended %s Capabilities: 0x%04X ", whom, *caps);
- extcap_tree= proto_item_add_subtree(tf, ett_extcaps);
- proto_tree_add_boolean(extcap_tree, hf_mysql_cap_multi_statements, tvb, offset, 2, *caps);
- proto_tree_add_boolean(extcap_tree, hf_mysql_cap_multi_results, tvb, offset, 2, *caps);
- }
+ { &hf_mysql_server_language,
+ { "Server Language", "mysql.server_language",
+ FT_UINT8, BASE_DEC, VALS(mysql_collation_vals), 0x0,
+ "MySQL Charset", HFILL }},
- offset+= 2;
- return offset;
-}
+ { &hf_mysql_server_status,
+ { "Server Status", "mysql.server_status",
+ FT_UINT16, BASE_HEX, NULL, 0x0,
+ "MySQL Status", HFILL }},
+ { &hf_mysql_stat_it,
+ { "In transaction", "mysql.stat.it",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_IT,
+ NULL, HFILL }},
-static int
-mysql_dissect_result_header(tvbuff_t *tvb, packet_info *pinfo, int offset,
- proto_tree *tree, mysql_conn_data_t *conn_data)
-{
- gint fle;
- guint64 num_fields, extra;
+ { &hf_mysql_stat_ac,
+ { "AUTO_COMMIT", "mysql.stat.ac",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_AC,
+ NULL, HFILL }},
- col_append_str(pinfo->cinfo, COL_INFO, " TABULAR" );
+ { &hf_mysql_stat_mr,
+ { "More results", "mysql.stat.mr",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_MR,
+ NULL, HFILL }},
- fle= tvb_get_fle(tvb, offset, &num_fields, NULL);
- proto_tree_add_uint64(tree, hf_mysql_num_fields,
- tvb, offset, fle, num_fields);
- offset+= fle;
+ { &hf_mysql_stat_mu,
+ { "Multi query - more resultsets", "mysql.stat.mu",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_MU,
+ NULL, HFILL }},
- if (tvb_reported_length_remaining(tvb, offset)) {
- fle= tvb_get_fle(tvb, offset, &extra, NULL);
- proto_tree_add_uint64(tree, hf_mysql_extra,
- tvb, offset, fle, extra);
- offset+= fle;
- }
+ { &hf_mysql_stat_bi,
+ { "Bad index used", "mysql.stat.bi",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_BI,
+ NULL, HFILL }},
- if (num_fields) {
- conn_data->state= FIELD_PACKET;
- } else {
- conn_data->state= ROW_PACKET;
- }
+ { &hf_mysql_stat_ni,
+ { "No index used", "mysql.stat.ni",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_NI,
+ NULL, HFILL }},
- return offset;
-}
+ { &hf_mysql_stat_cr,
+ { "Cursor exists", "mysql.stat.cr",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_CR,
+ NULL, HFILL }},
+ { &hf_mysql_stat_lr,
+ { "Last row sebd", "mysql.stat.lr",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_LR,
+ NULL, HFILL }},
-/*
- * Add length encoded string to tree
- */
-static int
-mysql_field_add_lestring(tvbuff_t *tvb, int offset, proto_tree *tree, int field)
-{
- guint64 lelen;
- guint8 is_null;
+ { &hf_mysql_stat_dr,
+ { "database dropped", "mysql.stat.dr",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_DR,
+ NULL, HFILL }},
- offset += tvb_get_fle(tvb, offset, &lelen, &is_null);
- if(is_null)
- proto_tree_add_string(tree, field, tvb, offset, 4, "NULL");
- else
- {
- proto_tree_add_item(tree, field, tvb, offset,
- (int)lelen, FALSE);
- offset += (int)lelen;
- }
- return offset;
-}
+ { &hf_mysql_stat_bs,
+ { "No backslash escapes", "mysql.stat.bs",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_STAT_BS,
+ NULL, HFILL }},
+ { &hf_mysql_refresh,
+ { "Refresh Option", "mysql.refresh",
+ FT_UINT8, BASE_HEX, NULL, 0x0,
+ NULL, HFILL }},
-static int
-mysql_dissect_field_packet(tvbuff_t *tvb, int offset, proto_tree *tree, mysql_conn_data_t *conn_data)
-{
- guint16 flags;
- proto_item *tf;
- proto_item *flags_tree;
+ { &hf_mysql_rfsh_grants,
+ { "reload permissions", "mysql.rfsh.grants",
+ FT_BOOLEAN, 8, TFS(&tfs_set_notset), MYSQL_RFSH_GRANT,
+ NULL, HFILL }},
- offset = mysql_field_add_lestring(tvb, offset, tree, hf_mysql_fld_catalog);
- offset = mysql_field_add_lestring(tvb, offset, tree, hf_mysql_fld_db);
- offset = mysql_field_add_lestring(tvb, offset, tree, hf_mysql_fld_table);
- 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);
- offset++; /* filler */
- mysql_dissect_collation(tvb, offset, tree, conn_data->srv_caps,
- tvb_get_letohs(tvb, offset), hf_mysql_fld_charsetnr);
- offset += 2; /* charset */
- proto_tree_add_item(tree, hf_mysql_fld_length, tvb, offset, 4, TRUE);
- offset += 4; /* length */
- proto_tree_add_item(tree, hf_mysql_fld_type, tvb, offset, 1, FALSE);
- offset++; /* type */
+ { &hf_mysql_rfsh_log,
+ { "flush logfiles", "mysql.rfsh.log",
+ FT_BOOLEAN, 8, TFS(&tfs_set_notset), MYSQL_RFSH_LOG,
+ NULL, HFILL }},
- flags = tvb_get_letohs(tvb, offset);
- tf = proto_tree_add_uint_format(tree, hf_mysql_fld_flags, tvb, offset,
- 2, flags, "Field flags: 0x%04X", flags);
- flags_tree = proto_item_add_subtree(tf, ett_field_flags);
- proto_tree_add_boolean(flags_tree, hf_mysql_fld_not_null, tvb, offset, 2, flags);
- proto_tree_add_boolean(flags_tree, hf_mysql_fld_primary_key, tvb, offset, 2, flags);
- proto_tree_add_boolean(flags_tree, hf_mysql_fld_unique_key, tvb, offset, 2, flags);
- proto_tree_add_boolean(flags_tree, hf_mysql_fld_multiple_key, tvb, offset, 2, flags);
- proto_tree_add_boolean(flags_tree, hf_mysql_fld_blob, tvb, offset, 2, flags);
- proto_tree_add_boolean(flags_tree, hf_mysql_fld_unsigned, tvb, offset, 2, flags);
- proto_tree_add_boolean(flags_tree, hf_mysql_fld_zero_fill, tvb, offset, 2, flags);
- proto_tree_add_boolean(flags_tree, hf_mysql_fld_binary, tvb, offset, 2, flags);
- proto_tree_add_boolean(flags_tree, hf_mysql_fld_enum, tvb, offset, 2, flags);
- proto_tree_add_boolean(flags_tree, hf_mysql_fld_auto_increment, tvb, offset, 2, flags);
- proto_tree_add_boolean(flags_tree, hf_mysql_fld_timestamp, tvb, offset, 2, flags);
- proto_tree_add_boolean(flags_tree, hf_mysql_fld_set, tvb, offset, 2, flags);
- offset += 2; /* flags */
+ { &hf_mysql_rfsh_tables,
+ { "flush tables", "mysql.rfsh.tables",
+ FT_BOOLEAN, 8, TFS(&tfs_set_notset), MYSQL_RFSH_TABLES,
+ NULL, HFILL }},
- proto_tree_add_item(tree, hf_mysql_fld_decimals, tvb, offset, 1, FALSE);
- offset++; /* decimals */
+ { &hf_mysql_rfsh_hosts,
+ { "flush hosts", "mysql.rfsh.hosts",
+ FT_BOOLEAN, 8, TFS(&tfs_set_notset), MYSQL_RFSH_HOSTS,
+ NULL, HFILL }},
- offset += 2; /* filler */
+ { &hf_mysql_rfsh_status,
+ { "reset statistics", "mysql.rfsh.status",
+ FT_BOOLEAN, 8, TFS(&tfs_set_notset), MYSQL_RFSH_STATUS,
+ NULL, HFILL }},
- /* default (Only use for show fields) */
- if (tree && tvb_reported_length_remaining(tvb, offset) > 0) {
- offset = mysql_field_add_lestring(tvb, offset, tree, hf_mysql_fld_default);
- }
- return offset;
-}
+ { &hf_mysql_rfsh_threads,
+ { "empty thread cache", "mysql.rfsh.threads",
+ FT_BOOLEAN, 8, TFS(&tfs_set_notset), MYSQL_RFSH_THREADS,
+ NULL, HFILL }},
+ { &hf_mysql_rfsh_slave,
+ { "flush slave status", "mysql.rfsh.slave",
+ FT_BOOLEAN, 8, TFS(&tfs_set_notset), MYSQL_RFSH_SLAVE,
+ NULL, HFILL }},
-static int
-mysql_dissect_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);
- }
+ { &hf_mysql_rfsh_master,
+ { "flush master status", "mysql.rfsh.master",
+ FT_BOOLEAN, 8, TFS(&tfs_set_notset), MYSQL_RFSH_MASTER,
+ NULL, HFILL }},
- return offset;
-}
+ { &hf_mysql_unused,
+ { "Unused", "mysql.unused",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
+ { &hf_mysql_passwd,
+ { "Password", "mysql.passwd",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
-static int
-mysql_dissect_response_prepare(tvbuff_t *tvb, int offset, proto_tree *tree)
-{
- proto_tree_add_text(tree, tvb, offset, -1, "FIXME: write mysql_dissect_response_prepare()");
- return offset + tvb_reported_length_remaining(tvb, offset);
-}
+ { &hf_mysql_payload,
+ { "Payload", "mysql.payload",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ "Additional Payload", HFILL }},
+ { &hf_mysql_affected_rows,
+ { "Affected Rows", "mysql.affected_rows",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
-static int
-mysql_dissect_param_packet(tvbuff_t *tvb, int offset, proto_tree *tree)
-{
- proto_tree_add_text(tree, tvb, offset, -1, "FIXME: write mysql_dissect_param_packet()");
- return offset + tvb_reported_length_remaining(tvb, offset);
-}
+ { &hf_mysql_insert_id,
+ { "Last INSERT ID", "mysql.insert_id",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+ { &hf_mysql_num_warn,
+ { "Warnings", "mysql.warnings",
+ 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 }},
-/*
- get length of string in packet buffer
+ { &hf_mysql_stmt_id,
+ { "Statement ID", "mysql.stmt_id",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
- SYNOPSIS
- my_tvb_strsize()
- tvb packet buffer
- offset current offset
+ { &hf_mysql_query,
+ { "Statement", "mysql.query",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ NULL, HFILL }},
- DESCRIPTION
- deliver length of string, delimited by either \0 or end of buffer
+ { &hf_mysql_shutdown,
+ { "Shutdown Level", "mysql.shutdown",
+ FT_UINT8, BASE_DEC, VALS(mysql_shutdown_vals), 0x0,
+ NULL, HFILL }},
- RETURN VALUE
- length of string found, including \0 (if present)
+ { &hf_mysql_option,
+ { "Option", "mysql.option",
+ FT_UINT16, BASE_DEC, VALS(mysql_option_vals), 0x0,
+ NULL, HFILL }},
-*/
-static gint
-my_tvb_strsize(tvbuff_t *tvb, int offset)
-{
- gint len = tvb_strnlen(tvb, offset, -1);
- if (len == -1) {
- len = tvb_reported_length_remaining(tvb, offset);
- } else {
- len++; /* the trailing \0 */
- }
- return len;
-}
+ { &hf_mysql_param,
+ { "Parameter", "mysql.param",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+ { &hf_mysql_num_params,
+ { "Number of parameter", "mysql.num_params",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
-/*
- read "field length encoded" value from packet buffer
+ { &hf_mysql_num_rows,
+ { "Rows to fetch", "mysql.num_rows",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
- SYNOPSIS
- tvb_get_fle()
- tvb in packet buffer
- offset in offset in buffer
- res out where to store FLE value, may be NULL
- is_null out where to store ISNULL flag, may be NULL
+ { &hf_mysql_exec_flags4,
+ { "Flags (unused)", "mysql.exec_flags",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
- DESCRIPTION
- read FLE from packet buffer and store its value and ISNULL flag
- in caller provided variables
+ { &hf_mysql_exec_flags5,
+ { "Flags", "mysql.exec_flags",
+ FT_UINT8, BASE_DEC, VALS(mysql_exec_flags_vals), 0x0,
+ NULL, HFILL }},
- RETURN VALUE
- length of FLE
-*/
-static int
-tvb_get_fle(tvbuff_t *tvb, int offset, guint64 *res, guint8 *is_null)
-{
- guint8 prefix= tvb_get_guint8(tvb, offset);
+ { &hf_mysql_exec_iter,
+ { "Iterations (unused)", "mysql.exec_iter",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
- if (is_null)
- *is_null= 0;
+ { &hf_mysql_binlog_position,
+ { "Binlog Position", "mysql.binlog.position",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Position to start at", HFILL }},
- switch (prefix) {
- case 251:
- if (res)
- *res= 0;
- if (is_null)
- *is_null= 1;
- break;
- case 252:
- if (res)
- *res= tvb_get_letohs(tvb, offset+1);
- return 3;
- case 253:
- if (res)
- *res= tvb_get_letohl(tvb, offset+1);
- return 5;
- case 254:
- if (res)
- *res= tvb_get_letoh64(tvb, offset+1);
- return 9;
- default:
- if (res)
- *res= prefix;
- }
+ { &hf_mysql_binlog_flags,
+ { "Binlog Flags", "mysql.binlog.flags",
+ FT_UINT16, BASE_HEX, NULL, 0x0,
+ "(currently not used; always 0)", HFILL }},
- return 1;
+ { &hf_mysql_binlog_server_id,
+ { "Binlog server id", "mysql.binlog.server_id",
+ FT_UINT16, BASE_HEX, NULL, 0x0,
+ "server_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_eof,
+ { "EOF marker", "mysql.eof",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_num_fields,
+ { "Number of fields", "mysql.num_fields",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_extra,
+ { "Extra data", "mysql.extra",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+
+ { &hf_mysql_fld_catalog,
+ { "Catalog", "mysql.field.catalog",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "Field: catalog", HFILL }},
+
+ { &hf_mysql_fld_db,
+ { "Database", "mysql.field.db",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "Field: database", HFILL }},
+
+ { &hf_mysql_fld_table,
+ { "Table", "mysql.field.table",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "Field: table", HFILL }},
+
+ { &hf_mysql_fld_org_table,
+ { "Original table", "mysql.field.org_table",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "Field: original table", HFILL }},
+
+ { &hf_mysql_fld_name,
+ { "Name", "mysql.field.name",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "Field: name", HFILL }},
+
+ { &hf_mysql_fld_org_name,
+ { "Original name", "mysql.field.org_name",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "Field: original name", HFILL }},
+
+ { &hf_mysql_fld_charsetnr,
+ { "Charset number", "mysql.field.charsetnr",
+ FT_UINT16, BASE_DEC, VALS(mysql_collation_vals), 0x0,
+ "Field: charset number", HFILL }},
+
+ { &hf_mysql_fld_length,
+ { "Length", "mysql.field.length",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Field: length", HFILL }},
+
+ { &hf_mysql_fld_type,
+ { "Type", "mysql.field.type",
+ FT_UINT8, BASE_DEC, VALS(type_constants), 0x0,
+ "Field: type", HFILL }},
+
+ { &hf_mysql_fld_flags,
+ { "Flags", "mysql.field.flags",
+ FT_UINT16, BASE_HEX, NULL, 0x0,
+ "Field: flags", HFILL }},
+
+ { &hf_mysql_fld_not_null,
+ { "Not null", "mysql.field.flags.not_null",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_NOT_NULL_FLAG,
+ "Field: flag not null", HFILL }},
+
+ { &hf_mysql_fld_primary_key,
+ { "Primary key", "mysql.field.flags.primary_key",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_PRI_KEY_FLAG,
+ "Field: flag primary key", HFILL }},
+
+ { &hf_mysql_fld_unique_key,
+ { "Unique key", "mysql.field.flags.unique_key",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_UNIQUE_KEY_FLAG,
+ "Field: flag unique key", HFILL }},
+
+ { &hf_mysql_fld_multiple_key,
+ { "Multiple key", "mysql.field.flags.multiple_key",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_MULTIPLE_KEY_FLAG,
+ "Field: flag multiple key", HFILL }},
+
+ { &hf_mysql_fld_blob,
+ { "Blob", "mysql.field.flags.blob",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_BLOB_FLAG,
+ "Field: flag blob", HFILL }},
+
+ { &hf_mysql_fld_unsigned,
+ { "Unsigned", "mysql.field.flags.unsigned",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_UNSIGNED_FLAG,
+ "Field: flag unsigned", HFILL }},
+
+ { &hf_mysql_fld_zero_fill,
+ { "Zero fill", "mysql.field.flags.zero_fill",
+ 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_fld_enum,
+ { "Enum", "mysql.field.flags.enum",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_ENUM_FLAG,
+ "Field: flag enum", HFILL }},
+
+ { &hf_mysql_fld_auto_increment,
+ { "Auto increment", "mysql.field.flags.auto_increment",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_AUTO_INCREMENT_FLAG,
+ "Field: flag auto increment", HFILL }},
+
+ { &hf_mysql_fld_timestamp,
+ { "Timestamp", "mysql.field.flags.timestamp",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_TIMESTAMP_FLAG,
+ "Field: flag timestamp", HFILL }},
+
+ { &hf_mysql_fld_set,
+ { "Set", "mysql.field.flags.set",
+ FT_BOOLEAN, 16, TFS(&tfs_set_notset), MYSQL_FLD_SET_FLAG,
+ "Field: flag set", HFILL }},
+
+ { &hf_mysql_fld_decimals,
+ { "Decimals", "mysql.field.decimals",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "Field: decimals", HFILL }},
+
+ { &hf_mysql_fld_default,
+ { "Default", "mysql.field.default",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "Field: default", HFILL }},
+
+ { &hf_mysql_row_text,
+ { "text", "mysql.row.text",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "Field: row packet text", HFILL }},
+ };
+
+ static gint *ett[]=
+ {
+ &ett_mysql,
+ &ett_server_greeting,
+ &ett_login_request,
+ &ett_caps,
+ &ett_extcaps,
+ &ett_stat,
+ &ett_request,
+ &ett_refresh,
+ &ett_field_flags
+ };
+
+ module_t *mysql_module;
+
+ proto_mysql = proto_register_protocol("MySQL Protocol", "MySQL", "mysql");
+ proto_register_field_array(proto_mysql, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+
+ mysql_module = prefs_register_protocol(proto_mysql, NULL);
+ prefs_register_bool_preference(mysql_module, "desegment_buffers",
+ "Reassemble MySQL buffers spanning multiple TCP segments",
+ "Whether the MySQL dissector should reassemble MySQL buffers spanning multiple TCP segments."
+ " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
+ &mysql_desegment);
+ prefs_register_bool_preference(mysql_module, "show_sql_query",
+ "Show SQL Query string in INFO column",
+ "Whether the MySQL dissector should display the SQL query string in the INFO column.",
+ &mysql_showquery);
+
+ register_dissector("mysql", dissect_mysql_pdu, proto_mysql);
+}
+
+/* dissector registration */
+void proto_reg_handoff_mysql(void)
+{
+ dissector_handle_t mysql_handle;
+ mysql_handle = create_dissector_handle(dissect_mysql, proto_mysql);
+ dissector_add_uint("tcp.port", TCP_PORT_MySQL, mysql_handle);
}
+