aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--epan/dissectors/packet-mysql.c2359
1 files changed, 1742 insertions, 617 deletions
diff --git a/epan/dissectors/packet-mysql.c b/epan/dissectors/packet-mysql.c
index 386f4e2cbe..f54a7a71ef 100644
--- a/epan/dissectors/packet-mysql.c
+++ b/epan/dissectors/packet-mysql.c
@@ -3,6 +3,8 @@
*
* Huagang XIE <huagang@intruvert.com>
*
+ * MySQL 4.1+ protocol by Axel Schwenke <axel@mysql.com>
+ *
* $Id$
*
* Ethereal - Network traffic analyzer
@@ -26,11 +28,14 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*
- * the protocol spec at
- * http://public.logicacmg.com/~redferni/mysql/MySQL-Protocol.html
- * and MySQL source code
+ * the protocol spec at
+ * http://public.logicacmg.com/~redferni/mysql/MySQL-Protocol.html
+ * and MySQL source code
*/
+/* create extra output for conversation tracking */
+/* #define CTDEBUG 1 */
+
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
@@ -40,11 +45,21 @@
#include <epan/conversation.h>
#include <epan/emem.h>
-#include "packet-tcp.h"
+#include <epan/dissectors/packet-tcp.h>
#include <epan/reassemble.h>
#include <epan/prefs.h>
-/* Capabilities */
+/* Define version if we are not building ethereal statically */
+#ifndef ENABLE_STATIC
+#include <gmodule.h>
+G_MODULE_EXPORT const gchar version[] = "0.1";
+#endif
+
+
+/* port for protocol registration */
+#define TCP_PORT_MySQL 3306
+
+/* client/server capabilities */
#define MYSQL_CAPS_LP 0x0001
#define MYSQL_CAPS_FR 0x0002
#define MYSQL_CAPS_LF 0x0004
@@ -59,14 +74,277 @@
#define MYSQL_CAPS_SL 0x0800
#define MYSQL_CAPS_II 0x1000
#define MYSQL_CAPS_TA 0x2000
+#define MYSQL_CAPS_RS 0x4000
+#define MYSQL_CAPS_SC 0x8000
+
+/* extended capabilities: 4.1+ client only */
+#define MYSQL_CAPS_MS 0x0001
+#define MYSQL_CAPS_MR 0x0002
+
+/* status bitfield */
+#define MYSQL_STAT_IT 0x0001
+#define MYSQL_STAT_AC 0x0002
+#define MYSQL_STAT_MR 0x0004
+#define MYSQL_STAT_MU 0x0008
+#define MYSQL_STAT_BI 0x0010
+#define MYSQL_STAT_NI 0x0020
+#define MYSQL_STAT_CR 0x0040
+#define MYSQL_STAT_LR 0x0080
+#define MYSQL_STAT_DR 0x0100
+#define MYSQL_STAT_BS 0x0200
+
+/* bitfield for MYSQL_REFRESH */
+#define MYSQL_RFSH_GRANT 1 /* Refresh grant tables */
+#define MYSQL_RFSH_LOG 2 /* Start on new log file */
+#define MYSQL_RFSH_TABLES 4 /* close all tables */
+#define MYSQL_RFSH_HOSTS 8 /* Flush host cache */
+#define MYSQL_RFSH_STATUS 16 /* Flush status variables */
+#define MYSQL_RFSH_THREADS 32 /* Flush thread cache */
+#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 */
+#define MYSQL_SLEEP 0 /* not from client */
+#define MYSQL_QUIT 1
+#define MYSQL_INIT_DB 2
+#define MYSQL_QUERY 3
+#define MYSQL_FIELD_LIST 4
+#define MYSQL_CREATE_DB 5
+#define MYSQL_DROP_DB 6
+#define MYSQL_REFRESH 7
+#define MYSQL_SHUTDOWN 8
+#define MYSQL_STATISTICS 9
+#define MYSQL_PROCESS_INFO 10
+#define MYSQL_CONNECT 11 /* not from client */
+#define MYSQL_PROCESS_KILL 12
+#define MYSQL_DEBUG 13
+#define MYSQL_PING 14
+#define MYSQL_TIME 15 /* not from client */
+#define MYSQL_DELAY_INSERT 16 /* not from client */
+#define MYSQL_CHANGE_USER 17
+#define MYSQL_BINLOG_DUMP 18 /* replication */
+#define MYSQL_TABLE_DUMP 19 /* replication */
+#define MYSQL_CONNECT_OUT 20 /* replication */
+#define MYSQL_REGISTER_SLAVE 21 /* replication */
+#define MYSQL_STMT_PREPARE 22
+#define MYSQL_STMT_EXECUTE 23
+#define MYSQL_STMT_SEND_LONG_DATA 24
+#define MYSQL_STMT_CLOSE 25
+#define MYSQL_STMT_RESET 26
+#define MYSQL_SET_OPTION 27
+#define MYSQL_STMT_FETCH 28
+
+
+/* decoding table: opcodes */
+static const value_string mysql_opcode_vals[] = {
+ {MYSQL_SLEEP, "SLEEP"},
+ {MYSQL_QUIT, "Quit"},
+ {MYSQL_INIT_DB, "Use Database"},
+ {MYSQL_QUERY, "Query"},
+ {MYSQL_FIELD_LIST, "Show Fields"},
+ {MYSQL_CREATE_DB, "Create Database"},
+ {MYSQL_DROP_DB , "Drop Database"},
+ {MYSQL_REFRESH , "Refresh"},
+ {MYSQL_SHUTDOWN , "Shutdown"},
+ {MYSQL_STATISTICS , "Statistics"},
+ {MYSQL_PROCESS_INFO , "Process List"},
+ {MYSQL_CONNECT , "Connect"},
+ {MYSQL_PROCESS_KILL , "Kill Server Thread"},
+ {MYSQL_DEBUG , "Dump Debuginfo"},
+ {MYSQL_PING , "Ping"},
+ {MYSQL_TIME , "Time"},
+ {MYSQL_DELAY_INSERT , "Insert Delayed"},
+ {MYSQL_CHANGE_USER , "Change User"},
+ {MYSQL_BINLOG_DUMP , "Send Binlog"},
+ {MYSQL_TABLE_DUMP, "Send Table"},
+ {MYSQL_CONNECT_OUT, "Slave Connect"},
+ {MYSQL_REGISTER_SLAVE, "Register Slave"},
+ {MYSQL_STMT_PREPARE, "Prepare Statement"},
+ {MYSQL_STMT_EXECUTE, "Execute Statement"},
+ {MYSQL_STMT_SEND_LONG_DATA, "Send BLOB"},
+ {MYSQL_STMT_CLOSE, "Close Statement"},
+ {MYSQL_STMT_RESET, "Reset Statement"},
+ {MYSQL_SET_OPTION, "Set Option"},
+ {MYSQL_STMT_FETCH, "Fetch Data"},
+ {0, NULL}
+};
+
+/* charset: pre-4.1 used ther term 'charset', later changed to 'collation' */
+static const value_string mysql_charset_vals[] = {
+ {1, "big5"},
+ {2, "czech"},
+ {3, "dec8"},
+ {4, "dos" },
+ {5, "german1"},
+ {6, "hp8"},
+ {7, "koi8_ru"},
+ {8, "latin1"},
+ {9, "latin2"},
+ {9, "swe7 "},
+ {10, "usa7"},
+ {11, "ujis"},
+ {12, "sjis"},
+ {13, "cp1251"},
+ {14, "danish"},
+ {15, "hebrew"},
+ {16, "win1251"},
+ {17, "tis620"},
+ {18, "euc_kr"},
+ {19, "estonia"},
+ {20, "hungarian"},
+ {21, "koi8_ukr"},
+ {22, "win1251ukr"},
+ {23, "gb2312"},
+ {24, "greek"},
+ {25, "win1250"},
+ {26, "croat"},
+ {27, "gbk"},
+ {28, "cp1257"},
+ {29, "latin5"},
+ {0, NULL}
+};
+
+
+/* collation codes may change over time, recreate with the following SQL
+
+SELECT CONCAT(' {', ID, ',"', CHARACTER_SET_NAME,
+ ' COLLATE ', COLLATION_NAME, '"},')
+FROM INFORMATION_SCHEMA.COLLATIONS
+ORDER BY ID
+INTO OUTFILE '/tmp/mysql-collations';
+
+*/
+static const value_string mysql_collation_vals[] = {
+ {3, "dec8 COLLATE dec8_swedish_ci"},
+ {4, "cp850 COLLATE cp850_general_ci"},
+ {5, "latin1 COLLATE latin1_german1_ci"},
+ {6, "hp8 COLLATE hp8_english_ci"},
+ {7, "koi8r COLLATE koi8r_general_ci"},
+ {8, "latin1 COLLATE latin1_swedish_ci"},
+ {9, "latin2 COLLATE latin2_general_ci"},
+ {10, "swe7 COLLATE swe7_swedish_ci"},
+ {11, "ascii COLLATE ascii_general_ci"},
+ {14, "cp1251 COLLATE cp1251_bulgarian_ci"},
+ {15, "latin1 COLLATE latin1_danish_ci"},
+ {16, "hebrew COLLATE hebrew_general_ci"},
+ {20, "latin7 COLLATE latin7_estonian_cs"},
+ {21, "latin2 COLLATE latin2_hungarian_ci"},
+ {22, "koi8u COLLATE koi8u_general_ci"},
+ {23, "cp1251 COLLATE cp1251_ukrainian_ci"},
+ {25, "greek COLLATE greek_general_ci"},
+ {26, "cp1250 COLLATE cp1250_general_ci"},
+ {27, "latin2 COLLATE latin2_croatian_ci"},
+ {29, "cp1257 COLLATE cp1257_lithuanian_ci"},
+ {30, "latin5 COLLATE latin5_turkish_ci"},
+ {31, "latin1 COLLATE latin1_german2_ci"},
+ {32, "armscii8 COLLATE armscii8_general_ci"},
+ {33, "utf8 COLLATE utf8_general_ci"},
+ {36, "cp866 COLLATE cp866_general_ci"},
+ {37, "keybcs2 COLLATE keybcs2_general_ci"},
+ {38, "macce COLLATE macce_general_ci"},
+ {39, "macroman COLLATE macroman_general_ci"},
+ {40, "cp852 COLLATE cp852_general_ci"},
+ {41, "latin7 COLLATE latin7_general_ci"},
+ {42, "latin7 COLLATE latin7_general_cs"},
+ {43, "macce COLLATE macce_bin"},
+ {44, "cp1250 COLLATE cp1250_croatian_ci"},
+ {47, "latin1 COLLATE latin1_bin"},
+ {48, "latin1 COLLATE latin1_general_ci"},
+ {49, "latin1 COLLATE latin1_general_cs"},
+ {50, "cp1251 COLLATE cp1251_bin"},
+ {51, "cp1251 COLLATE cp1251_general_ci"},
+ {52, "cp1251 COLLATE cp1251_general_cs"},
+ {53, "macroman COLLATE macroman_bin"},
+ {57, "cp1256 COLLATE cp1256_general_ci"},
+ {58, "cp1257 COLLATE cp1257_bin"},
+ {59, "cp1257 COLLATE cp1257_general_ci"},
+ {63, "binary COLLATE binary"},
+ {64, "armscii8 COLLATE armscii8_bin"},
+ {65, "ascii COLLATE ascii_bin"},
+ {66, "cp1250 COLLATE cp1250_bin"},
+ {67, "cp1256 COLLATE cp1256_bin"},
+ {68, "cp866 COLLATE cp866_bin"},
+ {69, "dec8 COLLATE dec8_bin"},
+ {70, "greek COLLATE greek_bin"},
+ {71, "hebrew COLLATE hebrew_bin"},
+ {72, "hp8 COLLATE hp8_bin"},
+ {73, "keybcs2 COLLATE keybcs2_bin"},
+ {74, "koi8r COLLATE koi8r_bin"},
+ {75, "koi8u COLLATE koi8u_bin"},
+ {77, "latin2 COLLATE latin2_bin"},
+ {78, "latin5 COLLATE latin5_bin"},
+ {79, "latin7 COLLATE latin7_bin"},
+ {80, "cp850 COLLATE cp850_bin"},
+ {81, "cp852 COLLATE cp852_bin"},
+ {82, "swe7 COLLATE swe7_bin"},
+ {83, "utf8 COLLATE utf8_bin"},
+ {92, "geostd8 COLLATE geostd8_general_ci"},
+ {93, "geostd8 COLLATE geostd8_bin"},
+ {94, "latin1 COLLATE latin1_spanish_ci"},
+ {99, "cp1250 COLLATE cp1250_polish_ci"},
+ {192, "utf8 COLLATE utf8_unicode_ci"},
+ {193, "utf8 COLLATE utf8_icelandic_ci"},
+ {194, "utf8 COLLATE utf8_latvian_ci"},
+ {195, "utf8 COLLATE utf8_romanian_ci"},
+ {196, "utf8 COLLATE utf8_slovenian_ci"},
+ {197, "utf8 COLLATE utf8_polish_ci"},
+ {198, "utf8 COLLATE utf8_estonian_ci"},
+ {199, "utf8 COLLATE utf8_spanish_ci"},
+ {200, "utf8 COLLATE utf8_swedish_ci"},
+ {201, "utf8 COLLATE utf8_turkish_ci"},
+ {202, "utf8 COLLATE utf8_czech_ci"},
+ {203, "utf8 COLLATE utf8_danish_ci"},
+ {204, "utf8 COLLATE utf8_lithuanian_ci"},
+ {205, "utf8 COLLATE utf8_slovak_ci"},
+ {206, "utf8 COLLATE utf8_spanish2_ci"},
+ {207, "utf8 COLLATE utf8_roman_ci"},
+ {208, "utf8 COLLATE utf8_persian_ci"},
+ {209, "utf8 COLLATE utf8_esperanto_ci"},
+ {210, "utf8 COLLATE utf8_hungarian_ci"},
+ {0, NULL}
+};
+
+
+/* allowed MYSQL_SHUTDOWN levels */
+static const value_string mysql_shutdown_vals[] = {
+ {0, "default"},
+ {1, "wait for connections to finish"},
+ {2, "wait for transactions to finish"},
+ {8, "wait for updates to finish"},
+ {16, "wait flush all buffers"},
+ {17, "wait flush critical buffers"},
+ {254, "kill running queries"},
+ {255, "kill connections"},
+ {0, NULL}
+};
+
+
+/* allowed MYSQL_SET_OPTION values */
+static const value_string mysql_option_vals[] = {
+ {0, "multi statements on"},
+ {1, "multi statements off"},
+ {0, NULL}
+};
+
+
+
+/* protocol id */
static int proto_mysql = -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_payload= -1;
-static int hf_mysql_protocol= -1;
+
+/* dissector configuration */
+static gboolean mysql_desegment = TRUE;
+
+/* expand-the-tree flags */
+static gint ett_mysql = -1;
+static gint ett_server_greeting = -1;
+static gint ett_caps = -1;
+static gint ett_extcaps = -1;
+static gint ett_stat = -1;
+static gint ett_request = -1;
+static gint ett_refresh = -1;
+
+/* protocol fields */
static int hf_mysql_caps= -1;
static int hf_mysql_cap_long_password= -1;
static int hf_mysql_cap_found_rows= -1;
@@ -82,771 +360,1618 @@ 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_password= -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_status= -1;
+static int hf_mysql_passwd= -1;
static int hf_mysql_unused= -1;
static int hf_mysql_parameter= -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_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;
+
+typedef enum my_proto_state
+{
+ UNDEFINED,
+ LOGIN,
+ REQUEST,
+ RESPONSE_OK,
+ RESPONSE_MESSAGE,
+ RESPONSE_TABULAR,
+ FIELD_PACKET,
+ ROW_PACKET,
+ RESPONSE_PREPARE,
+ PARAM_PACKET
+} my_proto_state_t;
+
+#ifdef CTDEBUG
+static const value_string state_vals[] = {
+ {UNDEFINED, "undefined"},
+ {LOGIN, "login"},
+ {REQUEST, "request"},
+ {RESPONSE_OK, "response OK"},
+ {RESPONSE_MESSAGE, "response message"},
+ {RESPONSE_TABULAR, "tabular response"},
+ {FIELD_PACKET, "field packet"},
+ {ROW_PACKET, "row packet"},
+ {RESPONSE_PREPARE, "response to PREPARE"},
+ {PARAM_PACKET, "parameter packet"},
+ {0, NULL}
+};
+#endif
-static gint ett_mysql = -1;
-static gint ett_server_greeting = -1;
-static gint ett_caps = -1;
-static gint ett_request = -1;
+typedef struct my_conn_data
+{
+ guint16 srv_caps;
+ guint16 clnt_caps;
+ guint16 clnt_caps_ext;
+ my_proto_state_t state;
+ GHashTable* stmts;
+#ifdef CTDEBUG
+ guint32 generation;
+#endif
+} my_conn_data_t;
-static gboolean mysql_desegment = TRUE;
-#define TCP_PORT_MySQL 3306
-
-#define MySQL_SLEEP 0
-#define MySQL_QUIT 1
-#define MySQL_INIT_DB 2
-#define MySQL_QUERY 3
-#define MySQL_FIELD_LIST 4
-#define MySQL_CREATE_DB 5
-#define MySQL_DROP_DB 6
-#define MySQL_REFRESH 7
-#define MySQL_SHUTDOWN 8
-#define MySQL_STATISTICS 9
-#define MySQL_PROCESS_INFO 10
-#define MySQL_CONNECT 11
-#define MySQL_PROCESS_KILL 12
-#define MySQL_DEBUG 13
-#define MySQL_PING 14
-#define MySQL_TIME 15
-#define MySQL_DELAY_INSERT 16
-#define MySQL_CHANGE_USER 17
-#define MySQL_BINLOG_DUMP 18
-#define MySQL_TABLE_DUMP 19
-#define MySQL_CONNECT_OUT 20
+typedef struct my_stmt_data
+{
+ guint16 nparam;
+} my_stmt_data_t;
-static const value_string mysql_opcode_vals[] = {
- { MySQL_SLEEP, "SLEEP" },
- { MySQL_QUIT, "Quit" },
- { MySQL_INIT_DB, "Init Database" },
- { MySQL_QUERY, "Query" },
- { MySQL_FIELD_LIST, "Field List" },
- { MySQL_CREATE_DB, "Create Database" },
- { MySQL_DROP_DB , "Drop Database" },
- { MySQL_REFRESH , "Refresh" },
- { MySQL_SHUTDOWN , "Shutdown" },
- { MySQL_STATISTICS , "Statistics" },
- { MySQL_PROCESS_INFO , "Process Info" },
- { MySQL_CONNECT , "Connect" },
- { MySQL_PROCESS_KILL , "Process Kill" },
- { MySQL_DEBUG , "Debug" },
- { MySQL_PING , "Ping" },
- { MySQL_TIME , "Time" },
- { MySQL_DELAY_INSERT , "Delay Insert" },
- { MySQL_CHANGE_USER , "Change User" },
- { MySQL_BINLOG_DUMP , "Binlog Dump" },
- { MySQL_TABLE_DUMP, "Table Dump" },
- { MySQL_CONNECT_OUT, "Table Connect Out" },
- { 0, NULL }
-};
+/* function prototypes */
+void proto_reg_handoff_mysql(void);
+void proto_register_mysql(void);
+static void dissect_mysql(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
+static guint get_mysql_pdu_len(tvbuff_t *tvb, int offset);
+static void dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
+static int mysql_dissect_login(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, my_conn_data_t *conn_data);
+static int mysql_dissect_greeting(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, my_conn_data_t *conn_data);
+static int mysql_dissect_request(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, my_conn_data_t *conn_data);
+static int mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, my_conn_data_t *conn_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, my_conn_data_t *conn_data);
+static int mysql_dissect_server_status(tvbuff_t *tvb, int offset, proto_tree *tree);
+static int mysql_dissect_collation(tvbuff_t *tvb, int offset, proto_tree *tree, guint16 caps);
+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_result_header(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, my_conn_data_t *conn_data);
+static int mysql_dissect_field_packet(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, my_conn_data_t *conn_data);
+static int mysql_dissect_row_packet(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, my_conn_data_t *conn_data);
+static int mysql_dissect_response_prepare(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, my_conn_data_t *conn_data);
+static int mysql_dissect_param_packet(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, my_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);
+
+
+
+/* plugin registration (if compiled as plugin) */
+#ifndef ENABLE_STATIC
+G_MODULE_EXPORT void plugin_register(void)
+{
+ if (proto_mysql == -1) { /* only once */
+ proto_register_mysql();
+ }
+}
-static const value_string mysql_status_vals[] = {
- {1, "IN_TRANS" },
- {2, "AUTOCOMMIT"},
- { 0, NULL }
-};
-static const value_string mysql_charset_vals[] = {
- {1, "big5"},
- {2, "czech"},
- {3,"dec8"},
- {4, "dos" },
- {5,"german1"},
- {6,"hp8"},
- {7,"koi8_ru"},
- {8,"latin1"},
- {9,"latin2"},
- {9,"swe7 "},
- {10,"usa7"},
- {11,"ujis"},
- {12,"sjis"},
- {13,"cp1251"},
- {14,"danish"},
- {15,"hebrew"},
- {16,"win1251"},
- {17,"tis620"},
- {18,"euc_kr"},
- {19,"estonia"},
- {20,"hungarian"},
- {21,"koi8_ukr"},
- {22,"win1251ukr"},
- {23,"gb2312"},
- {24,"greek"},
- {25,"win1250"},
- {26,"croat"},
- {27,"gbk"},
- {28,"cp1257"},
- {29,"latin5"},
- {0,NULL}
-};
-#if 0
-static const value_string mysql_error_code_vals[] = {
- { 0, "Not defined" },
- { 1, "File not found" },
- { 2, "Access violation" },
- { 3, "Disk full or allocation exceeded" },
- { 4, "Illegal MySQL Operation" },
- { 5, "Unknown transfer ID" },
- { 6, "File already exists" },
- { 7, "No such user" },
- { 8, "Option negotiation failed" },
- { 0, NULL }
-};
+G_MODULE_EXPORT void plugin_reg_handoff(void){
+ proto_reg_handoff_mysql();
+}
#endif
-static guint get_mysql_pdu_len(tvbuff_t *tvb, int offset);
-static void dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo,
- proto_tree *tree);
-static int mysql_dissect_server_greeting(tvbuff_t *tvb, packet_info *pinfo,
- int offset, proto_tree *tree);
-static int mysql_dissect_authentication(tvbuff_t *tvb, packet_info *pinfo,
- int offset, proto_tree *tree);
-static int mysql_dissect_request(tvbuff_t *tvb, packet_info *pinfo,
- int offset, proto_tree *tree);
-static int mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo,
- int offset, proto_tree *tree);
-static int mysql_dissect_error_packet(tvbuff_t *tvb, packet_info *pinfo,
- int offset, proto_tree *tree);
-static void
-dissect_mysql(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+/* dissector registration */
+void proto_reg_handoff_mysql(void)
{
- tcp_dissect_pdus(tvb, pinfo, tree, mysql_desegment, 3,
- get_mysql_pdu_len, dissect_mysql_pdu);
+ dissector_handle_t mysql_handle;
+ mysql_handle = create_dissector_handle(dissect_mysql, proto_mysql);
+ dissector_add("tcp.port", TCP_PORT_MySQL, mysql_handle);
}
-static guint
-get_mysql_pdu_len(tvbuff_t *tvb, int offset)
+
+/* protocol registration */
+void proto_register_mysql(void)
{
- guint plen;
-
- /*
- * Get the length of the MySQL packet.
- */
- plen = tvb_get_letoh24(tvb, offset);
-
- /*
- * That length doesn't include the length field or the packet
- * number itself; add them in.
- */
- return plen + 4;
+ static hf_register_info hf[]=
+ {
+ { &hf_mysql_packet_length,
+ { "Packet Length", "mysql.packet_length",
+ FT_UINT24, BASE_DEC, NULL, 0x0,
+ "Packet Length", HFILL }},
+
+ { &hf_mysql_packet_number,
+ { "Packet Number", "mysql.packet_number",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "Packet Number", HFILL }},
+
+ { &hf_mysql_opcode,
+ { "Command", "mysql.opcode",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "Command", HFILL }},
+
+ { &hf_mysql_response_code,
+ { "Response Code", "mysql.response_code",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "Response Code", HFILL }},
+
+ { &hf_mysql_error_code,
+ { "Error Code", "mysql.error_code",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "Error Code", HFILL }},
+
+ { &hf_mysql_error_string,
+ { "Error message", "mysql.error.message",
+ FT_STRING, BASE_DEC, NULL, 0x0,
+ "Error string in case of MySQL error message", HFILL }},
+
+ { &hf_mysql_sqlstate,
+ { "SQL state", "mysql.sqlstate",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "", HFILL }},
+
+ { &hf_mysql_message,
+ { "Message", "mysql.message",
+ FT_STRINGZ, BASE_DEC, NULL, 0x0,
+ "Message", HFILL }},
+
+ { &hf_mysql_protocol,
+ { "Protocol", "mysql.protocol",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "Protocol Version", HFILL }},
+
+ { &hf_mysql_version,
+ { "Version", "mysql.version",
+ FT_STRINGZ, BASE_DEC, NULL, 0x0,
+ "MySQL Version", HFILL }},
+
+ { &hf_mysql_caps,
+ { "Caps", "mysql.caps",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "MySQL Capabilities", HFILL }},
+
+ { &hf_mysql_cap_long_password,
+ { "Long Password","mysql.caps.lp",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_LP,
+ "", HFILL }},
+
+ { &hf_mysql_cap_found_rows,
+ { "Found Rows","mysql.caps.fr",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_FR,
+ "", HFILL }},
+
+ { &hf_mysql_cap_long_flag,
+ { "Long Column Flags","mysql.caps.lf",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_LF,
+ "", HFILL }},
+
+ { &hf_mysql_cap_connect_with_db,
+ { "Connect With Database","mysql.caps.cd",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_CD,
+ "", HFILL }},
+
+ { &hf_mysql_cap_no_schema,
+ { "Dont Allow database.table.column","mysql.caps.ns",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_NS,
+ "", HFILL }},
+
+ { &hf_mysql_cap_compress,
+ { "Can use compression protocol","mysql.caps.cp",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_CP,
+ "", HFILL }},
+
+ { &hf_mysql_cap_odbc,
+ { "ODBC Client","mysql.caps.ob",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_OB,
+ "", HFILL }},
+
+ { &hf_mysql_cap_local_files,
+ { "Can Use LOAD DATA LOCAL","mysql.caps.li",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_LI,
+ "", HFILL }},
+
+ { &hf_mysql_cap_ignore_space,
+ { "Ignore Spaces before '('","mysql.caps.is",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_IS,
+ "", HFILL }},
+
+ { &hf_mysql_cap_change_user,
+ { "Speaks 4.1 protocol (new flag)","mysql.caps.cu",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_CU,
+ "", HFILL }},
+
+ { &hf_mysql_cap_interactive,
+ { "Interactive Client","mysql.caps.ia",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_IA,
+ "", HFILL }},
+
+ { &hf_mysql_cap_ssl,
+ { "Switch to SSL after handshake","mysql.caps.sl",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_SL,
+ "", HFILL }},
+
+ { &hf_mysql_cap_ignore_sigpipe,
+ { "Ignore sigpipes","mysql.caps.ii",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_II,
+ "", HFILL }},
+
+ { &hf_mysql_cap_transactions,
+ { "Knows about transactions","mysql.caps.ta",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_TA,
+ "", HFILL }},
+
+ { &hf_mysql_cap_reserved,
+ { "Speaks 4.1 protocol (old flag)","mysql.caps.rs",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_RS,
+ "", HFILL }},
+
+ { &hf_mysql_cap_secure_connect,
+ { "Can do 4.1 authentication","mysql.caps.sc",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_SC,
+ "", HFILL }},
+
+ { &hf_mysql_extcaps,
+ { "Ext. Caps", "mysql.extcaps",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "MySQL Extended Capabilities", HFILL }},
+
+ { &hf_mysql_cap_multi_statements,
+ { "Supports multiple statements","mysql.caps.ms",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_MS,
+ "", HFILL }},
+
+ { &hf_mysql_cap_multi_results,
+ { "Supports multiple results","mysql.caps.mr",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_MR,
+ "", HFILL }},
+
+ { &hf_mysql_max_packet,
+ { "MAX Packet", "mysql.max_packet",
+ FT_UINT24, BASE_DEC, NULL, 0x0,
+ "MySQL Max packet", HFILL }},
+
+ { &hf_mysql_user,
+ { "Username", "mysql.user",
+ FT_STRINGZ, BASE_DEC, NULL, 0x0,
+ "Login Username", HFILL }},
+
+ { &hf_mysql_schema,
+ { "Schema", "mysql.schema",
+ FT_STRING, BASE_DEC, NULL, 0x0,
+ "Login Schema", HFILL }},
+
+ { &hf_mysql_salt,
+ { "Salt", "mysql.salt",
+ FT_STRINGZ, BASE_DEC, NULL, 0x0,
+ "Salt", HFILL }},
+
+ { &hf_mysql_salt2,
+ { "Salt", "mysql.salt2",
+ FT_STRINGZ, BASE_DEC, NULL, 0x0,
+ "Salt", HFILL }},
+
+ { &hf_mysql_thread_id,
+ { "Thread ID", "mysql.thread_id",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "MySQL Thread ID", HFILL }},
+
+ { &hf_mysql_charset,
+ { "Charset", "mysql.charset",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "MySQL Charset", HFILL }},
+
+ { &hf_mysql_status,
+ { "Status", "mysql.status",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "MySQL Status", HFILL }},
+
+ { &hf_mysql_stat_it,
+ { "In transaction", "mysql.stat.it",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_IT,
+ "", HFILL }},
+
+ { &hf_mysql_stat_ac,
+ { "AUTO_COMMIT", "mysql.stat.ac",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_AC,
+ "", HFILL }},
+
+ { &hf_mysql_stat_mr,
+ { "More results", "mysql.stat.mr",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_MR,
+ "", HFILL }},
+
+ { &hf_mysql_stat_mu,
+ { "Multi query - more resultsets", "mysql.stat.mu",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_MU,
+ "", HFILL }},
+
+ { &hf_mysql_stat_bi,
+ { "Bad index used", "mysql.stat.bi",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_BI,
+ "", HFILL }},
+
+ { &hf_mysql_stat_ni,
+ { "No index used", "mysql.stat.ni",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_NI,
+ "", HFILL }},
+
+ { &hf_mysql_stat_cr,
+ { "Cursor exists", "mysql.stat.cr",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_CR,
+ "", HFILL }},
+
+ { &hf_mysql_stat_lr,
+ { "Last row sebd", "mysql.stat.lr",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_LR,
+ "", HFILL }},
+
+ { &hf_mysql_stat_dr,
+ { "database dropped", "mysql.stat.dr",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_DR,
+ "", HFILL }},
+
+ { &hf_mysql_stat_bs,
+ { "No backslash escapes", "mysql.stat.bs",
+ FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_BS,
+ "", HFILL }},
+
+ { &hf_mysql_refresh,
+ { "Refresh Option", "mysql.refresh",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "Refresh Option", HFILL }},
+
+ { &hf_mysql_rfsh_grants,
+ { "reload permissions", "mysql.rfsh.grants",
+ FT_BOOLEAN, 8, TFS(&flags_set_truth), MYSQL_RFSH_GRANT,
+ "", HFILL }},
+
+ { &hf_mysql_rfsh_log,
+ { "flush logfiles", "mysql.rfsh.log",
+ FT_BOOLEAN, 8, TFS(&flags_set_truth), MYSQL_RFSH_LOG,
+ "", HFILL }},
+
+ { &hf_mysql_rfsh_tables,
+ { "flush tables", "mysql.rfsh.tables",
+ FT_BOOLEAN, 8, TFS(&flags_set_truth), MYSQL_RFSH_TABLES,
+ "", HFILL }},
+
+ { &hf_mysql_rfsh_hosts,
+ { "flush hosts", "mysql.rfsh.hosts",
+ FT_BOOLEAN, 8, TFS(&flags_set_truth), MYSQL_RFSH_HOSTS,
+ "", HFILL }},
+
+ { &hf_mysql_rfsh_status,
+ { "reset statistics", "mysql.rfsh.status",
+ FT_BOOLEAN, 8, TFS(&flags_set_truth), MYSQL_RFSH_STATUS,
+ "", HFILL }},
+
+ { &hf_mysql_rfsh_threads,
+ { "empty thread cache", "mysql.rfsh.threads",
+ FT_BOOLEAN, 8, TFS(&flags_set_truth), MYSQL_RFSH_THREADS,
+ "", HFILL }},
+
+ { &hf_mysql_rfsh_slave,
+ { "flush slave status", "mysql.rfsh.slave",
+ FT_BOOLEAN, 8, TFS(&flags_set_truth), MYSQL_RFSH_SLAVE,
+ "", HFILL }},
+
+ { &hf_mysql_rfsh_master,
+ { "flush master status", "mysql.rfsh.master",
+ FT_BOOLEAN, 8, TFS(&flags_set_truth), MYSQL_RFSH_MASTER,
+ "", HFILL }},
+
+ { &hf_mysql_unused,
+ { "Unused", "mysql.unused",
+ FT_STRING, BASE_DEC, NULL, 0x0,
+ "Unused", HFILL }},
+
+ { &hf_mysql_passwd,
+ { "Password", "mysql.passwd",
+ FT_STRING, BASE_DEC, NULL, 0x0,
+ "Password", HFILL }},
+
+ { &hf_mysql_parameter,
+ { "Parameter", "mysql.parameter",
+ FT_STRING, BASE_DEC, NULL, 0x0,
+ "Parameter", HFILL }},
+
+ { &hf_mysql_payload,
+ { "Payload", "mysql.payload",
+ FT_STRING, BASE_DEC, NULL, 0x0,
+ "Additional Payload", HFILL }},
+
+ { &hf_mysql_affected_rows,
+ { "Affected Rows", "mysql.affected_rows",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ "Affected Rows", HFILL }},
+
+ { &hf_mysql_insert_id,
+ { "Last INSERT ID", "mysql.insert_id",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ "Last INSERT ID", HFILL }},
+
+ { &hf_mysql_num_warn,
+ { "Warnings", "mysql.warnings",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "Warnings", HFILL }},
+
+ { &hf_mysql_thd_id,
+ { "Thread ID", "mysql.thd_id",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Thread ID", HFILL }},
+
+ { &hf_mysql_stmt_id,
+ { "Statement ID", "mysql.stmt_id",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Statement ID", HFILL }},
+
+ { &hf_mysql_query,
+ { "Statement", "mysql.query",
+ FT_STRING, BASE_DEC, NULL, 0x0,
+ "Statement", HFILL }},
+
+ { &hf_mysql_option,
+ { "Option", "mysql.option",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "Option", HFILL }},
+
+ { &hf_mysql_param,
+ { "Parameter", "mysql.param",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "Parameter", HFILL }},
+
+ { &hf_mysql_num_rows,
+ { "Rows to fetch", "mysql.num_rows",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Rows to fetch", HFILL }},
+
+ { &hf_mysql_exec_flags,
+ { "Flags (unused)", "mysql.exec_flags",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "Flags (unused)", HFILL }},
+
+ { &hf_mysql_exec_iter,
+ { "Iterations (unused)", "mysql.exec_iter",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Iterations (unused)", HFILL }},
+
+ { &hf_mysql_eof,
+ { "EOF", "mysql.eof",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "EOF", HFILL }},
+
+ { &hf_mysql_num_fields,
+ { "Number of fields", "mysql.num_fields",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ "Number of fields", HFILL }},
+
+ { &hf_mysql_extra,
+ { "Extra data", "mysql.extra",
+ FT_UINT64, BASE_DEC, NULL, 0x0,
+ "Extra data", HFILL }}
+ };
+
+ static gint *ett[]=
+ {
+ &ett_mysql,
+ &ett_server_greeting,
+ &ett_caps,
+ &ett_extcaps,
+ &ett_stat,
+ &ett_request,
+ &ett_refresh,
+ };
+
+ 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);
}
-static void
-dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+
+/* dissector entrypoint, handles TCP-desegmentation */
+static void dissect_mysql(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
- proto_tree *mysql_tree = NULL;
- proto_item *ti;
- conversation_t *conversation;
+ tcp_dissect_pdus(tvb, pinfo, tree, mysql_desegment, 3,
+ get_mysql_pdu_len, dissect_mysql_pdu);
+}
- int offset = 0;
- guint packet_number;
- gboolean is_response;
+/* dissector helper: length of PDU */
+static guint get_mysql_pdu_len(tvbuff_t *tvb, int offset)
+{
+ guint plen= tvb_get_letoh24(tvb, offset);
+ return plen + 4; /* add length field + packet number */
+}
+
- conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
- pinfo->srcport, pinfo->destport, 0);
+/* dissector main function: handle one PDU */
+static void dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ proto_tree *mysql_tree= NULL;
+ proto_item *ti;
+ conversation_t *conversation;
+ int offset = 0;
+ guint packet_number;
+ gboolean is_response;
+ my_conn_data_t *conn_data;
+#ifdef CTDEBUG
+ my_proto_state_t state_in, state_out;
+ guint64 generation;
+#endif
+
+ /* get conversation, create if neccessary*/
+ conversation= find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
+ pinfo->ptype, pinfo->srcport,
+ pinfo->destport, 0);
if (!conversation) {
- /* create a new conversation */
- conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
- pinfo->srcport, pinfo->destport, 0);
+ conversation= conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst,
+ pinfo->ptype, pinfo->srcport,
+ pinfo->destport, 0);
}
+ /* get associated state information, create if neccessary */
+ conn_data= conversation_get_proto_data(conversation, proto_mysql);
+ if (!conn_data) {
+ conn_data= se_alloc(sizeof(my_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 (check_col(pinfo->cinfo, COL_PROTOCOL))
+ if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
col_set_str(pinfo->cinfo, COL_PROTOCOL, "MySQL");
+ }
if (pinfo->destport == pinfo->match_port) {
- is_response=FALSE;
- }else {
- is_response=TRUE;
+ is_response= FALSE;
+ } else {
+ is_response= TRUE;
}
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);
+ 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;
-/* packet number */
+ offset+= 3;
+
packet_number= tvb_get_guint8(tvb, offset);
if (tree) {
- proto_tree_add_uint(mysql_tree, hf_mysql_packet_number, tvb,
- offset, 1, packet_number);
+ proto_tree_add_uint(mysql_tree, hf_mysql_packet_number, tvb,
+ offset, 1, packet_number);
+ }
+ offset+= 1;
+
+#ifdef CTDEBUG
+ state_in= conn_data->state;
+ generation= conn_data->generation;
+ if (tree) {
+ proto_tree_add_text(mysql_tree, tvb, offset, 0, "conversation: %p", conversation);
+ proto_tree_add_text(mysql_tree, tvb, offset, 0, "generation: %lld", generation);
+ proto_tree_add_text(mysql_tree, tvb, offset, 0, "proto state: %s (%u)",
+ val_to_str(state_in, state_vals, "Unknown (%u)"),
+ state_in);
}
- offset += 1;
+#endif
- /*
- * packet == 0 && response --> server greeting
- * packet == 1 && request --> login request
- */
- if(is_response ) {
- if( packet_number == 0 ) {
+ if (is_response ) {
+ if (packet_number == 0) {
if (check_col(pinfo->cinfo, COL_INFO)) {
- col_add_str(pinfo->cinfo, COL_INFO, "Server Greeting" ) ;
+ col_add_str(pinfo->cinfo, COL_INFO, "Server Greeting" );
}
- offset = mysql_dissect_server_greeting(tvb,pinfo,offset,mysql_tree);
- }else {
+ offset= mysql_dissect_greeting(tvb, pinfo, offset,
+ mysql_tree, conn_data);
+ } else {
if (check_col(pinfo->cinfo, COL_INFO)) {
- col_add_str(pinfo->cinfo, COL_INFO, "Response" ) ;
+ col_add_str(pinfo->cinfo, COL_INFO, "Response" );
}
- offset = mysql_dissect_response(tvb,pinfo,offset,mysql_tree);
+ offset= mysql_dissect_response(tvb, pinfo, offset,
+ mysql_tree, conn_data);
}
} else {
- if( packet_number == 1 ) {
+ if (packet_number == 1) {
if (check_col(pinfo->cinfo, COL_INFO)) {
- col_add_str(pinfo->cinfo, COL_INFO, "Login Request") ;
+ col_add_str(pinfo->cinfo, COL_INFO, "Login Request");
}
- offset = mysql_dissect_authentication(tvb,pinfo,offset,mysql_tree);
- }else {
+ offset= mysql_dissect_login(tvb, pinfo, offset,
+ mysql_tree, conn_data);
+ } else {
if (check_col(pinfo->cinfo, COL_INFO)) {
- col_add_str(pinfo->cinfo, COL_INFO, "Request") ;
+ col_add_str(pinfo->cinfo, COL_INFO, "Request");
}
- offset = mysql_dissect_request(tvb,pinfo,offset,mysql_tree);
+ offset= mysql_dissect_request(tvb, pinfo, offset,
+ mysql_tree, conn_data);
}
}
-/* payload */
- if (tree && tvb_reported_length_remaining(tvb, offset) > 0) {
- proto_tree_add_item(mysql_tree, hf_mysql_payload,
- tvb, offset, -1, FALSE);
- }
-}
-static int
-mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo,
- int offset, proto_tree *tree)
-{
- gint response_code;
-
- /* response code */
- response_code= tvb_get_guint8(tvb, offset);
+#ifdef CTDEBUG
+ state_out= conn_data->state;
+ ++(conn_data->generation);
if (tree) {
- proto_tree_add_uint(tree, hf_mysql_response_code, tvb,
- offset, 1, response_code);
+ proto_tree_add_text(mysql_tree, tvb, offset, 0, "next proto state: %s (%u)",
+ val_to_str(state_out, state_vals, "Unknown (%u)"),
+ state_out);
}
- offset +=1;
-
- if(response_code== 0xff ) {
- offset = mysql_dissect_error_packet(tvb, pinfo, offset, tree);
- } else {
- if (check_col(pinfo->cinfo, COL_INFO)) {
- col_append_str(pinfo->cinfo, COL_INFO, " OK" );
- }
+#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");
}
- return offset;
}
-static int
-mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo,
- int offset, proto_tree *tree)
+
+static int mysql_dissect_greeting(tvbuff_t *tvb, packet_info *pinfo, int offset,
+ proto_tree *tree, my_conn_data_t *conn_data)
{
- gint opcode;
+ gint protocol;
gint strlen;
+ gint32 thread_id;
+ guint16 server_caps;
+
proto_item *tf;
- proto_item *req_tree=NULL;
+ 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;
- if(tree) {
- tf=proto_tree_add_text(tree,tvb,offset,-1,"Command");
- req_tree = proto_item_add_subtree(tf ,ett_request);
+ if (tree) {
+ tf= proto_tree_add_text(tree, tvb, offset, -1, "Server Greeting");
+ greeting_tree= proto_item_add_subtree(tf, ett_server_greeting);
}
- opcode = tvb_get_guint8(tvb, offset);
-
if (check_col(pinfo->cinfo, COL_INFO)) {
- col_append_fstr(pinfo->cinfo, COL_INFO, " Command: %s",
- val_to_str(opcode, mysql_opcode_vals, "Unknown (%u)"));
+ col_append_fstr(pinfo->cinfo, COL_INFO, " proto=%d", protocol) ;
+ }
+ if (tree) {
+ proto_tree_add_uint(greeting_tree, hf_mysql_protocol, tvb,
+ offset, 1, protocol);
}
+ offset+= 1;
- if (req_tree) {
- 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);
+ /* 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_ptr(tvb, offset, strlen));
}
- /* command parameter */
+ if (tree) {
+ proto_tree_add_item(greeting_tree, hf_mysql_version, tvb,
+ offset, strlen, FALSE );
+ }
+ offset+= strlen;
- offset += 1;
- if ( (strlen = tvb_length_remaining(tvb,offset)) > 0 ) {
-
- if (check_col(pinfo->cinfo, COL_INFO)) {
- col_append_fstr(pinfo->cinfo, COL_INFO, " : %s",
- tvb_format_text(tvb,offset,strlen));
- }
-
+ /* 4 bytes little endian thread_id */
+ thread_id= tvb_get_letohl(tvb, offset);
+ if (tree) {
+ proto_tree_add_uint(greeting_tree, hf_mysql_thread_id, tvb,
+ offset, 4, thread_id);
+ }
+ offset+= 4;
+
+ /* salt string */
+ strlen= tvb_strsize(tvb,offset);
+ if (tree) {
+ proto_tree_add_item(greeting_tree, hf_mysql_salt, tvb,
+ offset, strlen, FALSE );
+ }
+ offset+=strlen;
+
+ /* rest is optional */
+ if (!tvb_length_remaining(tvb, offset)) return offset;
+
+ /* 2 bytes CAPS */
+ offset= mysql_dissect_caps(tvb, offset, greeting_tree, &server_caps, "Server");
+ conn_data->srv_caps= server_caps;
+
+ /* rest is optional */
+ if (!tvb_length_remaining(tvb, offset)) return offset;
+
+ offset= mysql_dissect_collation(tvb, offset, greeting_tree, server_caps);
+ offset= mysql_dissect_server_status(tvb, offset, greeting_tree);
+
+ /* 13 bytes unused */
+ if (tree) {
+ proto_tree_add_item(greeting_tree, hf_mysql_unused, tvb,
+ offset, 13, FALSE );
+ }
+ offset+= 13;
+
+ /* 4.1+ server: rest of salt */
+ if (tvb_length_remaining(tvb, offset)) {
+ strlen= tvb_strsize(tvb,offset);
if (tree) {
- proto_tree_add_item(req_tree, hf_mysql_parameter, tvb,
- offset, strlen, FALSE );
+ proto_tree_add_item(greeting_tree, hf_mysql_salt2, tvb,
+ offset, strlen, FALSE );
}
- offset +=strlen;
+ offset+= strlen;
}
return offset;
}
-static int
-mysql_dissect_authentication(tvbuff_t *tvb, packet_info *pinfo,
- int offset, proto_tree *tree)
+static int mysql_dissect_login(tvbuff_t *tvb, packet_info *pinfo, int offset,
+ proto_tree *tree, my_conn_data_t *conn_data)
{
- gint16 client_caps;
- gint32 max_packet;
- gint strlen;
+ guint16 client_caps;
+ guint16 ext_caps;
+ guint32 max_packet;
+ gint strlen;
proto_item *tf;
- proto_item *cap_tree;
- proto_item *login_tree=NULL;
+ proto_item *login_tree= NULL;
+
+ /* after login there can be OK or DENIED */
+ conn_data->state= RESPONSE_OK;
- if(tree) {
- tf=proto_tree_add_text(tree,tvb,offset,-1,"Login Packet");
- login_tree = proto_item_add_subtree(tf ,ett_server_greeting);
+ if (tree) {
+ tf= proto_tree_add_text(tree, tvb, offset, -1, "Login Request");
+ login_tree= proto_item_add_subtree(tf, ett_server_greeting);
}
- client_caps= tvb_get_letohs(tvb, offset);
+ offset= mysql_dissect_caps(tvb, offset, login_tree, &client_caps, "Client");
+ conn_data->clnt_caps= client_caps;
- if (check_col(pinfo->cinfo, COL_INFO)) {
- col_append_fstr(pinfo->cinfo, COL_INFO, " Caps: 0x%x",client_caps) ;
- }
- if(tree) {
- tf = proto_tree_add_uint_format(login_tree, hf_mysql_caps, tvb, offset , 1, client_caps, "Caps: 0x%04x ", client_caps );
- cap_tree = proto_item_add_subtree(tf, ett_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_long_password, tvb, offset, 2, client_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_found_rows, tvb, offset, 2, client_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_long_flag, tvb, offset, 2, client_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_connect_with_db, tvb, offset, 2, client_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_no_schema, tvb, offset, 2, client_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_compress, tvb, offset, 2, client_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_odbc, tvb, offset, 2, client_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_local_files, tvb, offset, 2, client_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_ignore_space, tvb, offset, 2, client_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_change_user, tvb, offset, 2, client_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_interactive, tvb, offset, 2, client_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_ssl, tvb, offset, 2, client_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_ignore_sigpipe, tvb, offset, 2, client_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_transactions, tvb, offset, 2, client_caps);
- }
-
-/* proto_tree_add_uint(tree, hf_mysql_client_caps, tvb,
- offset, 2, client_caps);
-*/
- offset +=2;
- /* 3 bytes max packet, 16777216 - x */
- max_packet = 0xffffff - tvb_get_letoh24(tvb, offset);
- if(tree) {
- proto_tree_add_uint(login_tree, hf_mysql_max_packet, tvb,
- offset, 3, max_packet);
- }
- offset +=3;
- /* User name */
- strlen = tvb_strsize(tvb,offset);
+ if (client_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);
+ if(tree) {
+ proto_tree_add_uint(login_tree, hf_mysql_max_packet, tvb,
+ offset, 4, max_packet);
+ }
+ offset+= 4;
+
+ offset= mysql_dissect_collation(tvb, offset, login_tree, client_caps);
+ offset+= 23; /* filler bytes */
+
+ } else { /* pre-4.1 */
+ max_packet= 0xffffff - tvb_get_letoh24(tvb, offset);
+ if (tree) {
+ proto_tree_add_uint(login_tree, hf_mysql_max_packet, tvb,
+ offset, 3, max_packet);
+ }
+ offset+= 3;
+ }
+
+ /* User name */
+ strlen= my_tvb_strsize(tvb, offset);
if (check_col(pinfo->cinfo, COL_INFO)) {
- col_append_fstr(pinfo->cinfo, COL_INFO, " ,user: %s",
+ col_append_fstr(pinfo->cinfo, COL_INFO, " user=%s",
tvb_get_ptr(tvb,offset,strlen));
}
if (tree) {
proto_tree_add_item(login_tree, hf_mysql_user, tvb,
- offset, strlen, FALSE );
+ offset, strlen, FALSE );
}
- offset +=strlen;
-
- /* Password */
- strlen = tvb_length_remaining(tvb,offset);
+ offset+= strlen;
- if (check_col(pinfo->cinfo, COL_INFO)) {
- col_append_fstr(pinfo->cinfo, COL_INFO, " ,password: %s",
- tvb_get_ptr(tvb,offset,strlen));
+ /* rest is optional */
+ if (!tvb_length_remaining(tvb, offset)) return offset;
+
+ /* password: asciiz or length+ascii */
+ if (client_caps & MYSQL_CAPS_SC) {
+ strlen= tvb_get_guint8(tvb, offset);
+ offset+= 1;
+ } else {
+ strlen= my_tvb_strsize(tvb, offset);
}
- if (tree) {
- proto_tree_add_item(login_tree, hf_mysql_password, tvb,
- offset, strlen, FALSE );
+ if (tree && strlen > 1) {
+ proto_tree_add_item(login_tree, hf_mysql_passwd,
+ tvb, offset, strlen, FALSE);
+ }
+ offset+= strlen;
+
+ /* optional: initial schema */
+ if (client_caps & MYSQL_CAPS_CD)
+ {
+ strlen= my_tvb_strsize(tvb,offset);
+
+ 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);
+ }
+ if (tree) {
+ proto_tree_add_item(login_tree, hf_mysql_schema, tvb,
+ offset, strlen, FALSE );
+ }
+ offset+= strlen;
}
- offset +=strlen;
-
-
+
return offset;
}
-static int
-mysql_dissect_server_greeting(tvbuff_t *tvb, packet_info *pinfo,
- int offset, proto_tree *tree)
+static int mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset,
+ proto_tree *tree, my_conn_data_t *conn_data)
{
- gint protocol;
+ gint opcode;
gint strlen;
- gint32 thread_id;
- gint16 server_caps;
- gint charset;
- gint16 status;
-
proto_item *tf;
- proto_item *greeting_tree=NULL;
- proto_item *cap_tree;
+ proto_item *req_tree= NULL;
+ guint16 option;
- protocol= tvb_get_guint8(tvb, offset);
+ if (tree) {
+ tf= proto_tree_add_text(tree, tvb, offset, -1, "Command");
+ req_tree= proto_item_add_subtree(tf, ett_request);
+ }
- if (protocol == 0xff) {
- offset += 1;
- return mysql_dissect_error_packet(tvb, pinfo, offset, 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)"));
+ }
+ if (req_tree) {
+ 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;
+
- if(tree) {
- tf = proto_tree_add_text(tree,tvb,offset,-1,"Server Greeting");
- greeting_tree = proto_item_add_subtree(tf ,ett_server_greeting);
+ switch (opcode) {
+
+ case MYSQL_QUIT:
+ if (conn_data->stmts) {
+ g_hash_table_destroy(conn_data->stmts);
+ conn_data->stmts= NULL;
+ }
+ break;
+
+ case MYSQL_PROCESS_INFO:
+ conn_data->state= RESPONSE_TABULAR;
+ break;
+
+ case MYSQL_DEBUG:
+ case MYSQL_PING:
+ conn_data->state= RESPONSE_OK;
+ break;
+
+ case MYSQL_STATISTICS:
+ conn_data->state= RESPONSE_MESSAGE;
+ break;
+
+ case MYSQL_INIT_DB:
+ case MYSQL_CREATE_DB:
+ case MYSQL_DROP_DB:
+ strlen= my_tvb_strsize(tvb, offset);
+ if (req_tree) {
+ proto_tree_add_item(req_tree, hf_mysql_schema, tvb,
+ offset, strlen, FALSE);
+ }
+ offset+= strlen;
+ conn_data->state= RESPONSE_OK;
+ break;
+
+ case MYSQL_QUERY:
+ strlen= my_tvb_strsize(tvb, offset);
+ if (req_tree) {
+ proto_tree_add_item(req_tree, hf_mysql_query, tvb,
+ offset, strlen, FALSE);
+ }
+ offset+= strlen;
+ conn_data->state= RESPONSE_TABULAR;
+ break;
+
+ case MYSQL_STMT_PREPARE:
+ strlen= my_tvb_strsize(tvb, offset);
+ if (req_tree) {
+ proto_tree_add_item(req_tree, hf_mysql_query, tvb,
+ offset, strlen, FALSE);
+ }
+ offset+= strlen;
+ conn_data->state= RESPONSE_PREPARE;
+ break;
+
+ case MYSQL_STMT_CLOSE:
+ if (conn_data->stmts) {
+ gint stmt= tvb_get_letohl(tvb, offset);
+ g_hash_table_remove(conn_data->stmts, &stmt);
+ }
+ if (req_tree) {
+ proto_tree_add_item(req_tree, hf_mysql_stmt_id,
+ tvb, offset, 4, TRUE);
+ }
+ offset+= 4;
+ conn_data->state= REQUEST;
+ break;
+
+ case MYSQL_STMT_RESET:
+ if (req_tree) {
+ 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);
+ if (req_tree) {
+ proto_tree_add_text(req_tree, tvb, offset, strlen, "Table name: %s",
+ tvb_get_string(tvb, offset, strlen));
+ }
+ offset+= strlen;
+ conn_data->state= RESPONSE_TABULAR;
+ break;
+
+ case MYSQL_PROCESS_KILL:
+ if (req_tree) {
+ proto_tree_add_item(req_tree, hf_mysql_thd_id,
+ tvb, offset, 4, TRUE);
+ }
+ offset+= 4;
+ conn_data->state= RESPONSE_OK;
+ break;
+
+ case MYSQL_CHANGE_USER:
+ strlen= tvb_strsize(tvb, offset);
+ if (req_tree) {
+ proto_tree_add_item(req_tree, hf_mysql_user, tvb,
+ offset, strlen, FALSE);
+ }
+ offset+= strlen;
+
+ strlen= tvb_strsize(tvb, offset);
+ if (req_tree) {
+ proto_tree_add_item(req_tree, hf_mysql_passwd, tvb,
+ offset, strlen, FALSE);
+ }
+ offset+= strlen;
+
+ strlen= my_tvb_strsize(tvb, offset);
+ if (req_tree) {
+ 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 *tf;
+ proto_item *rfsh_tree;
+ gint refresh= tvb_get_guint8(tvb, offset);
+
+ if (req_tree) {
+ tf= 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(tf, 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);
+ if (req_tree) {
+ 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;
+
+ case MYSQL_SET_OPTION:
+ option= tvb_get_letohs(tvb, offset);
+ if (req_tree) {
+ 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;
+
+ case MYSQL_STMT_FETCH:
+ if (req_tree) {
+ proto_tree_add_item(req_tree, hf_mysql_stmt_id,
+ tvb, offset, 4, TRUE);
+ }
+ offset+= 4;
+
+ if (req_tree) {
+ proto_tree_add_item(req_tree, hf_mysql_num_rows,
+ tvb, offset, 4, TRUE);
+ }
+ offset+= 4;
+ conn_data->state= RESPONSE_TABULAR;
+ break;
+
+ case MYSQL_STMT_SEND_LONG_DATA:
+ if (req_tree) {
+ proto_tree_add_item(req_tree, hf_mysql_stmt_id,
+ tvb, offset, 4, TRUE);
+ }
+ offset+= 4;
+
+ if (req_tree) {
+ 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;
+
+ case MYSQL_STMT_EXECUTE:
+ if (req_tree) {
+ proto_tree_add_item(req_tree, hf_mysql_stmt_id,
+ tvb, offset, 4, TRUE);
+ }
+ offset+= 4;
+
+ if (req_tree) {
+ proto_tree_add_item(req_tree, hf_mysql_exec_flags,
+ tvb, offset, 1, TRUE);
+ }
+ offset+= 1;
+
+ if (req_tree) {
+ proto_tree_add_item(req_tree, hf_mysql_exec_iter,
+ tvb, offset, 4, TRUE);
+ }
+ offset+= 4;
+
+#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;
+
+/* FIXME: implement replication packets */
+ case MYSQL_BINLOG_DUMP:
+ case MYSQL_TABLE_DUMP:
+ case MYSQL_CONNECT_OUT:
+ case MYSQL_REGISTER_SLAVE:
+ if (req_tree) {
+ proto_tree_add_string(req_tree, hf_mysql_payload, tvb, offset, -1,
+ "FIXME: implement replication packets");
+ }
+ offset+= tvb_length_remaining(tvb, offset);
+ conn_data->state= REQUEST;
+ break;
+
+ default:
+ if (req_tree) {
+ proto_tree_add_string(req_tree, hf_mysql_payload, tvb, offset, -1,
+ "unknown/invalid command code");
+ }
+ offset+= tvb_length_remaining(tvb, offset);
+ conn_data->state= UNDEFINED;
+ }
+
+ return offset;
+}
+
+
+static int mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo, int offset,
+ proto_tree *tree, my_conn_data_t *conn_data)
+{
+ gint response_code;
+ gint strlen;
+
+ response_code= tvb_get_guint8(tvb, offset);
+
+ if (response_code == 0xff ) {
+ offset= mysql_dissect_error_packet(tvb, pinfo, offset+1, tree);
+ conn_data->state= REQUEST;
}
-
+
+ else if (response_code == 0xfe && tvb_length_remaining(tvb, offset) < 9) {
+
+ if (tree) {
+ proto_tree_add_uint_format(tree, hf_mysql_eof, tvb, offset, 1,
+ response_code, "EOF marker (%u)",
+ response_code);
+ }
+ offset+= 1;
+
+ /* pre-4.1 packet ends here */
+ if (tvb_length_remaining(tvb, offset)) {
+ if (tree) {
+ proto_tree_add_item(tree, hf_mysql_num_warn,
+ tvb, offset, 2, FALSE);
+ }
+ offset= mysql_dissect_server_status(tvb, offset+2, tree);
+ }
+
+ if (conn_data->state == FIELD_PACKET) {
+ conn_data->state= ROW_PACKET;
+ } else {
+ conn_data->state= REQUEST;
+ }
+ }
+
+ else if (response_code == 0) {
+ if (tvb_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);
+ }
+ }
+
+ else {
+ switch (conn_data->state) {
+ case RESPONSE_MESSAGE:
+ if ((strlen= tvb_length_remaining(tvb, offset))) {
+ if (tree) {
+ proto_tree_add_item(tree, hf_mysql_message, tvb,
+ offset, strlen, FALSE);
+ }
+ offset+= strlen;
+ }
+ conn_data->state= REQUEST;
+ break;
+
+ case RESPONSE_TABULAR:
+ offset= mysql_dissect_result_header(tvb, pinfo, offset,
+ tree, conn_data);
+ break;
+
+ case FIELD_PACKET:
+ offset= mysql_dissect_field_packet(tvb, pinfo, offset,
+ tree, conn_data);
+ break;
+
+ case ROW_PACKET:
+ offset= mysql_dissect_row_packet(tvb, pinfo, offset,
+ tree, conn_data);
+ break;
+
+ case RESPONSE_PREPARE:
+ offset= mysql_dissect_response_prepare(tvb, pinfo, offset,
+ tree, conn_data);
+ break;
+
+ case PARAM_PACKET:
+ offset= mysql_dissect_param_packet(tvb, pinfo, offset,
+ tree, conn_data);
+ break;
+
+ default:
+ if (tree) {
+ proto_tree_add_string(tree, hf_mysql_payload, tvb, offset, -1,
+ "unknown/invalid response");
+ }
+ offset+= tvb_length_remaining(tvb, offset);
+ conn_data->state= UNDEFINED;
+ }
+ }
+
+ return offset;
+}
+
+
+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);
+
if (check_col(pinfo->cinfo, COL_INFO)) {
- col_append_fstr(pinfo->cinfo, COL_INFO, " Protocol : %d",protocol) ;
+ col_append_fstr(pinfo->cinfo, COL_INFO, " Error %d", error_code);
}
if (tree) {
- proto_tree_add_uint(greeting_tree, hf_mysql_protocol, tvb,
- offset, 1, protocol);
+ proto_tree_add_uint(tree, hf_mysql_error_code, tvb,
+ offset, 2, error_code);
}
- offset +=1;
- /* version string */
+ offset+= 2;
+
+ if (tvb_get_guint8(tvb, offset) == '#')
+ {
+ offset+= 1;
+ proto_tree_add_item(tree, hf_mysql_sqlstate, tvb, offset, 5, FALSE);
+ offset+= 5;
+ }
+
+ proto_tree_add_item(tree, hf_mysql_error_string, tvb, offset, -1, FALSE);
+ offset+= tvb_reported_length_remaining(tvb, offset);
- strlen = tvb_strsize(tvb,offset);
+ return offset;
+}
+
+
+static int mysql_dissect_ok_packet(tvbuff_t *tvb, packet_info *pinfo, int offset,
+ proto_tree *tree, my_conn_data_t *conn_data)
+{
+ gint strlen;
+ guint64 affected_rows;
+ guint64 insert_id;
+ int fle;
if (check_col(pinfo->cinfo, COL_INFO)) {
- col_append_fstr(pinfo->cinfo, COL_INFO, " ,version: %s",
- tvb_get_ptr(tvb,offset,strlen));
+ col_append_str(pinfo->cinfo, COL_INFO, " OK" );
}
+
+ fle= tvb_get_fle(tvb, offset, &affected_rows, NULL);
if (tree) {
- proto_tree_add_item(greeting_tree, hf_mysql_version, tvb,
- offset, strlen, FALSE );
+ proto_tree_add_uint64(tree, hf_mysql_affected_rows,
+ tvb, offset, fle, affected_rows);
}
- offset +=strlen;
-
- /* 4 bytes little endian thread_id */
- thread_id = tvb_get_letohl(tvb, offset);
- if(tree) {
- proto_tree_add_uint(greeting_tree, hf_mysql_thread_id, tvb,
- offset, 4, thread_id);
+ offset+= fle;
+
+ 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 +=4;
- /* salt string */
- strlen = tvb_strsize(tvb,offset);
+ offset+= fle;
+
+ 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) {
+ if (tree) {
+ proto_tree_add_item(tree, hf_mysql_num_warn, tvb,
+ offset, 2, FALSE);
+ }
+ offset+= 2;
+ }
+
+ /* optional: message string */
+ if ((strlen= tvb_length_remaining(tvb, offset))) {
+ if (tree) {
+ proto_tree_add_item(tree, hf_mysql_message, tvb,
+ offset, strlen, FALSE);
+ }
+ offset+= strlen;
+ }
+
+ conn_data->state= REQUEST;
+ return offset;
+}
+
+
+static int mysql_dissect_server_status(tvbuff_t *tvb, int offset, proto_tree *tree)
+{
+ guint16 status;
+ proto_item *tf;
+ proto_item *stat_tree;
+
+ status= tvb_get_letohs(tvb, offset);
if (tree) {
- proto_tree_add_item(greeting_tree, hf_mysql_salt, tvb,
- offset, strlen, FALSE );
+ 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 +=strlen;
- /* 2 bytes CAPS */
- server_caps= tvb_get_letohs(tvb, offset);
+ offset+= 2;
- if (check_col(pinfo->cinfo, COL_INFO)) {
- col_append_fstr(pinfo->cinfo, COL_INFO, " Caps: 0x%x",server_caps) ;
- }
- if(tree) {
- tf = proto_tree_add_uint_format(greeting_tree, hf_mysql_caps, tvb, offset , 1, server_caps, "Caps: 0x%04x ", server_caps );
- cap_tree = proto_item_add_subtree(tf, ett_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_long_password, tvb, offset, 2, server_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_found_rows, tvb, offset, 2, server_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_long_flag, tvb, offset, 2, server_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_connect_with_db, tvb, offset, 2, server_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_no_schema, tvb, offset, 2, server_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_compress, tvb, offset, 2, server_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_odbc, tvb, offset, 2, server_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_local_files, tvb, offset, 2, server_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_ignore_space, tvb, offset, 2, server_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_change_user, tvb, offset, 2, server_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_interactive, tvb, offset, 2, server_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_ssl, tvb, offset, 2, server_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_ignore_sigpipe, tvb, offset, 2, server_caps);
- proto_tree_add_boolean(cap_tree, hf_mysql_cap_transactions, tvb, offset, 2, server_caps);
- }
- offset+=2;
- /* 1 byte charset */
- charset = tvb_get_guint8(tvb, offset);
+ return offset;
+}
+
+
+static int mysql_dissect_collation(tvbuff_t *tvb, int offset, proto_tree *tree, guint16 caps)
+{
+ gint charset= tvb_get_guint8(tvb, offset);
if (tree) {
- proto_tree_add_uint_format(greeting_tree, hf_mysql_charset, tvb,
- offset, 1, charset, "Charset: %s (%u)",
- val_to_str(charset, mysql_charset_vals, "Unknown (%u)"), charset);
+ proto_tree_add_uint_format(tree, hf_mysql_charset, tvb, offset, 1,
+ charset, "Charset: %s (%u)",
+ val_to_str(charset,
+ caps & MYSQL_CAPS_CU
+ ? mysql_collation_vals
+ : mysql_charset_vals,
+ "Unknown (%u)"), charset);
}
- offset +=1;
- /* 2 byte status */
- status = tvb_get_letohs(tvb, offset);
+ offset+= 1;
+ return offset;
+}
+
+
+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_tree_add_uint_format(greeting_tree, hf_mysql_status, tvb,
- offset, 2, status, "Status: %s (%u)",
- val_to_str(status, mysql_status_vals, "Unknown (%u)"), status);
+ 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);
}
- offset +=2;
- /* other unused */
- strlen = tvb_length_remaining(tvb,offset);
+ offset+= 2;
+ return offset;
+}
+
+
+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_tree_add_item(greeting_tree, hf_mysql_unused, tvb,
- offset, strlen, FALSE );
+ 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);
}
- offset +=strlen;
-
+
+ offset+= 2;
return offset;
}
-static int
-mysql_dissect_error_packet(tvbuff_t *tvb, packet_info *pinfo,
- int offset, proto_tree *tree)
+
+static int mysql_dissect_result_header(tvbuff_t *tvb, packet_info *pinfo, int offset,
+ proto_tree *tree, my_conn_data_t *conn_data)
{
- gint error_code;
+ gint fle;
+ guint64 num_fields, extra;
- /* error code */
- error_code = tvb_get_letohs(tvb, offset);
if (check_col(pinfo->cinfo, COL_INFO)) {
- col_append_fstr(pinfo->cinfo, COL_INFO, " Error Code: %u", error_code);
+ col_append_str(pinfo->cinfo, COL_INFO, " TABULAR" );
}
+
+ fle= tvb_get_fle(tvb, offset, &num_fields, NULL);
if (tree) {
- proto_tree_add_uint(tree, hf_mysql_error_code, tvb,
- offset, 2, error_code);
+ proto_tree_add_uint64(tree, hf_mysql_num_fields,
+ tvb, offset, fle, num_fields);
}
- offset += 2;
+ offset+= fle;
- if (tvb_get_guint8(tvb, offset) == '#')
- {
- offset += 1;
- proto_tree_add_item(tree, hf_mysql_sqlstate, tvb, offset, 5, FALSE);
- offset += 5;
+ if (tvb_length_remaining(tvb, offset)) {
+ fle= tvb_get_fle(tvb, offset, &extra, NULL);
+ if (tree) {
+ proto_tree_add_uint64(tree, hf_mysql_extra,
+ tvb, offset, fle, extra);
+ }
+ offset+= fle;
}
- proto_tree_add_item(tree, hf_mysql_error_string, tvb, offset, -1, FALSE);
+ if (num_fields) {
+ conn_data->state= FIELD_PACKET;
+ } else {
+ conn_data->state= ROW_PACKET;
+ }
- return offset + tvb_reported_length_remaining(tvb, offset);
+ return offset;
}
-void
-proto_register_mysql(void)
+
+static int mysql_dissect_field_packet(tvbuff_t *tvb, packet_info *pinfo, int offset,
+ proto_tree *tree, my_conn_data_t *conn_data)
{
- static hf_register_info hf[] = {
- { &hf_mysql_packet_length,
- { "Packet Length", "mysql.packet_length",
- FT_UINT24, BASE_DEC, NULL, 0x0,
- "MySQL packet length", HFILL }},
-
- { &hf_mysql_packet_number,
- { "Packet Number", "mysql.packet_number",
- FT_UINT8, BASE_DEC, NULL, 0x0,
- "MySQL Packet Number", HFILL }},
-
- { &hf_mysql_opcode,
- { "Command", "mysql.opcode",
- FT_UINT8, BASE_DEC, NULL, 0x0,
- "MySQL OPCODE", HFILL }},
-
- { &hf_mysql_response_code,
- { "Response Code", "mysql.response_code",
- FT_UINT8, BASE_DEC, NULL, 0x0,
- "MySQL Respone Code", HFILL }},
-#if 0
- { &hf_mysql_error_code,
- { "Error Code", "mysql.error_code",
- FT_UINT16, BASE_DEC, NULL, 0x0,
- "MySQL Error CODE", HFILL }},
-#endif
- { &hf_mysql_protocol,
- { "Protocol", "mysql.protocol",
- FT_UINT8, BASE_DEC, NULL, 0x0,
- "MySQL Protocol", HFILL }},
-
- { &hf_mysql_version,
- { "Version", "mysql.version",
- FT_STRINGZ, BASE_DEC, NULL, 0x0,
- "MySQL Version", HFILL }},
-
- { &hf_mysql_caps,
- { "Caps", "mysql.caps",
- FT_UINT16, BASE_DEC, NULL, 0x0,
- "MySQL Capabilities", HFILL }},
-
- { &hf_mysql_cap_long_password,
- { "Long Password","mysql.caps.lp",
- FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_LP,
- "", HFILL }},
-
- { &hf_mysql_cap_found_rows,
- { "Found Rows","mysql.caps.fr",
- FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_FR,
- "", HFILL }},
+ proto_tree_add_text(tree, tvb, offset, -1, "FIXME: write mysql_dissect_field_packet()");
+ return offset + tvb_length_remaining(tvb, offset);
+}
-
- { &hf_mysql_cap_long_flag,
- { "Long Flag","mysql.caps.lf",
- FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_LF,
- "", HFILL }},
-
- { &hf_mysql_cap_connect_with_db,
- { "Connect With Database","mysql.caps.cd",
- FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_CD,
- "", HFILL }},
-
- { &hf_mysql_cap_no_schema,
- { "Dont Allow database.table.column","mysql.caps.ns",
- FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_NS,
- "", HFILL }},
+static int mysql_dissect_row_packet(tvbuff_t *tvb, packet_info *pinfo, int offset,
+ proto_tree *tree, my_conn_data_t *conn_data)
+{
+ proto_tree_add_text(tree, tvb, offset, -1, "FIXME: write mysql_dissect_row_packet()");
+ return offset + tvb_length_remaining(tvb, offset);
+}
- { &hf_mysql_cap_compress,
- { "Can use compression protocol","mysql.caps.CP",
- FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_CP,
- "", HFILL }},
-
- { &hf_mysql_cap_odbc,
- { "ODBC Client","mysql.caps.ob",
- FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_OB,
- "", HFILL }},
-
- { &hf_mysql_cap_local_files,
- { "Can Use LOAD DATA LOCAL","mysql.caps.li",
- FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_LI,
- "", HFILL }},
-
- { &hf_mysql_cap_ignore_space,
- { "Ignore Spaces before (","mysql.caps.is",
- FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_IS,
- "", HFILL }},
+static int mysql_dissect_response_prepare(tvbuff_t *tvb, packet_info *pinfo, int offset,
+ proto_tree *tree, my_conn_data_t *conn_data)
+{
+ proto_tree_add_text(tree, tvb, offset, -1, "FIXME: write mysql_dissect_response_prepare()");
+ return offset + tvb_length_remaining(tvb, offset);
+}
-
- { &hf_mysql_cap_change_user,
- { "Support the mysql_change_user()","mysql.caps.cu",
- FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_CU,
- "", HFILL }},
-
- { &hf_mysql_cap_interactive,
- { "an Interactive Client","mysql.caps.ia",
- FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_IA,
- "", HFILL }},
+static int mysql_dissect_param_packet(tvbuff_t *tvb, packet_info *pinfo, int offset,
+ proto_tree *tree, my_conn_data_t *conn_data)
+{
+ proto_tree_add_text(tree, tvb, offset, -1, "FIXME: write mysql_dissect_param_packet()");
+ return offset + tvb_length_remaining(tvb, offset);
+}
-
- { &hf_mysql_cap_ssl,
- { "Switch to SSL after handshake","mysql.caps.sl",
- FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_SL,
- "", HFILL }},
- { &hf_mysql_cap_ignore_sigpipe,
- { "Ignore sigpipes","mysql.caps.ii",
- FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_II,
- "", HFILL }},
-
- { &hf_mysql_cap_transactions,
- { "Client knows about transactions","mysql.caps.ta",
- FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_TA,
- "", HFILL }},
- { &hf_mysql_max_packet,
- { "MAX Packet", "mysql.max_packet",
- FT_UINT24, BASE_DEC, NULL, 0x0,
- "MySQL Max packet", HFILL }},
-
- { &hf_mysql_user,
- { "Username", "mysql.user",
- FT_STRINGZ, BASE_DEC, NULL, 0x0,
- "Login Username", HFILL }},
-
- { &hf_mysql_password,
- { "Password", "mysql.password",
- FT_STRING, BASE_DEC, NULL, 0x0,
- "Login Password", HFILL }},
-
- { &hf_mysql_salt,
- { "Salt", "mysql.salt",
- FT_STRINGZ, BASE_DEC, NULL, 0x0,
- "Salt", HFILL }},
-
- { &hf_mysql_thread_id,
- { "Thread ID", "mysql.thread_id",
- FT_UINT32, BASE_DEC, NULL, 0x0,
- "MySQL Thread ID", HFILL }},
-
- { &hf_mysql_charset,
- { "Charset", "mysql.charset",
- FT_UINT8, BASE_DEC, NULL, 0x0,
- "MySQL Charset", HFILL }},
-
- { &hf_mysql_status,
- { "Status", "mysql.status",
- FT_UINT16, BASE_DEC, NULL, 0x0,
- "MySQL Status", HFILL }},
-
- { &hf_mysql_unused,
- { "Unused", "mysql.unused",
- FT_STRING, BASE_DEC, NULL, 0x0,
- "Unused", HFILL }},
-
- { &hf_mysql_parameter,
- { "Parameter", "mysql.parameter",
- FT_STRING, BASE_DEC, NULL, 0x0,
- "Parameter", HFILL }},
- { &hf_mysql_payload,
- { "Payload", "mysql.payload",
- FT_STRING, BASE_DEC, NULL, 0x0,
- "MySQL Payload", HFILL }},
-#if 0
- { &hf_mysql_destination_file,
- { "DESTINATION File", "mysql.destination_file",
- FT_STRINGZ, BASE_DEC, NULL, 0x0,
- "MySQL source file name", HFILL }},
-
- { &hf_mysql_blocknum,
- { "Block", "mysql.block",
- FT_UINT16, BASE_DEC, NULL, 0x0,
- "Block number", HFILL }},
-#endif
- { &hf_mysql_error_code,
- { "Error code", "mysql.error.code",
- FT_UINT16, BASE_DEC, NULL, 0x0,
- "Error code in case of MySQL error message", HFILL }},
-
- { &hf_mysql_error_string,
- { "Error message", "mysql.error.message",
- FT_STRING, BASE_DEC, NULL, 0x0,
- "Error string in case of MySQL error message", HFILL }},
-
- { &hf_mysql_sqlstate,
- { "SQL state", "mysql.sqlstate",
- FT_STRING, BASE_NONE, NULL, 0x0,
- "", HFILL }}
- };
- static gint *ett[] = {
- &ett_mysql,
- &ett_server_greeting,
- &ett_caps,
- &ett_request,
- };
- module_t *mysql_module;
+/*
+ get length of string in packet buffer
- 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));
+ SYNOPSIS
+ my_tvb_strsize()
+ tvb packet buffer
+ offset current offset
- 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);
+ DESCRIPTION
+ deliver length of string, delimited by either \0 or end of buffer
+
+ RETURN VALUE
+ length of string found, including \0 (if present)
+
+*/
+static gint my_tvb_strsize(tvbuff_t *tvb, int offset)
+{
+ gint len = tvb_strnlen(tvb, offset, -1);
+ if (len == -1) {
+ len = tvb_length_remaining(tvb, offset);
+ } else {
+ len++; /* the trailing \0 */
+ }
+ return len;
}
-void
-proto_reg_handoff_mysql(void)
+
+/*
+ read "field length encoded" value from packet buffer
+
+ 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
+
+ DESCRIPTION
+ read FLE from packet buffer and store its value and ISNULL flag
+ in caller provided variables
+
+ RETURN VALUE
+ length of FLE
+*/
+static int tvb_get_fle(tvbuff_t *tvb, int offset, guint64 *res, guint8 *is_null)
{
- dissector_handle_t mysql_handle;
+ guint8 prefix= tvb_get_guint8(tvb, offset);
+
+ 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:
+ if (res)
+ *res= prefix;
+ }
- mysql_handle = create_dissector_handle(dissect_mysql, proto_mysql);
-
- dissector_add("tcp.port", TCP_PORT_MySQL, mysql_handle);
+ return 1;
}
+
+