?????????MYSQL是一个开源数据库,它自定义了一套客户端和服务器之间的底层通信协议,默认使用TCP 3306端口通信。
? ? ? ? 根据MYSQL客户端和服务器的通信协议规范定义,MYSQL通信协议的报文包括建立连接的服务端握手报文、客户端握手应答、客户端执行查询等操作命令报文以及服务端的相应的应答报文。协议文档官网地址如下:
官方在线文档https://dev.mysql.com/doc/internals/en/client-server-protocol.html? ? ? ? MYSQL报文结构如下图所示,其中的payload字段是每一种报文的载荷,每一种报文的载荷编码结构在官方在线文档中有详细说明。
????????建立连接的握手请求报文包括HandshakeV10和HandsharkV9。命令报文由命令等字段组成,命令字段标识了命令报文类型,命令可选值如下表所示:
命令 | 常量名称 | 说明 | 0x00 | COM_SLEEP | 仅服务器内部使用 | 0x01 | COM_QUIT | 客户端关闭连接 | 0x02 | COM_INIT_DB | 修改连接的默认数据库 | 0x03 | COM_QUERY | 请求执行一个查询语句 | 0x04 | COM_FIELD_LIST | 获取表格的所有字段名 | 0x05 | COM_CREATE_DB | 创建数据库 | 0x06 | COM_DROP_DB | 删除数据库 | 0x07 | COM_REFRESH | 执行REFRESH或者FLUSH命令 | 0x08 | COM_SHUTDOWN | 关闭服务器 | 0x09 | COM_STATICTICS | 获取线程信息 | 0x0a | COM_PROCESS_INFO | 获取线程信息 | 0x0b | COM_CONNECT | 仅服务器内部使用 | 0x0c | COM_PROCESS_KILL | 请求中断连接 | 0x0d | COM_DEBUG | 导出调试信息到标准输出 | 0x0e | COM_PING | 检查服务端是否存活 | 0x0f | COM_TIME | 仅服务器内部使用 | 0x10 | COM_DELAYED_INSERT | 仅服务器内部使用 | 0x11 | COM_CHANGE_USER | 修改当前连接的用户 | 0x12 | COM_BINGLOG_DUMP | 导出BINLOG | 0x13 | COM_TABLE_DUMP | 导出表 | 0x14 | COM_CONNECT_OUT | 仅服务器内部使用 | 0x15 | COM_REGISTER_SLAVE | 注册从节点到主节点 | 0x16 | COM_STMT_PREPARE | 创建预编译语句 | 0x17 | COM_STMT_EXECUTE | 执行预编译语句 | 0x18 | COM_STMT_SEND_LONG_DATA | 发送预编译列数据 | 0x19 | COM_STMT_CLOSE | 关闭预编译语句 | 0x1a | COM_STMT_RESET | 重置预编译语句的数据 | 0x1b | COM_SET_OPTION | 开启或关闭CLIENT_MULTI_STATEMENTS | 0x1c | COM_STMT_FETCH | 从结果集中取数据 | 0x1d | COM_DAEMON | 仅服务器内部使用 | 0x1e | COM_BINLOG_DUMP_GTID | 请求一个BINLOG网络流 | 0x1f | COM_RESET_CONNECTION | 重置会话状态 | 0xef | Semi-Synchronous | 半同步复制 | 0xfb | LOCAL_INFILE_Request | 加载本地数据文件到服务器 |
????????应答报文包括OK_Packet、ERR_Packet等通用的应答,以及特定命令的数据应答如HandshakeResponse41、HandshakeResponse320等,如下表所示:
类型 | 说明 | OK_Packet | 命令执行成功 | ERR_Packet | 出现异常 | EOF_Packet | 同OK_Packet,MySQL 5.7.5开始取消此报文 | HandshakeResponse41 | 客户端握手报文应答(双方支持CLIENT_PROTOCOL_41) | HandshakeResponse320 | 客户端握手报文应答(不支持CLIENT_PROTOCOL_41) | ColmunDefinition41 | 查询等操作应答(双方支持CLIENT_PROTOCOL_41) | ColmunDefinition320 | 查询等操作应答(不支持CLIENT_PROTOCOL_41) | COM_STMT_PREPARE_OK | 创建预编译语句应答 | Semi-Synchronous ACK | 半同步复制应答 |
? ? ? ? 每种报文的报文结构,在官方的在线文档中都有详细的说明。
????????下面是解析代码:
/**
* @file mysql.h
* @brief Mysql Protocol
* @author yangyiyin
* @date 2021/12
* @copyright
*/
#ifndef _MYSQL_H_
#define _MYSQL_H_
#include <string>
#include <vector>
#include <map>
#include "dissector.h"
#define MYSQL_DEFULT_PORT 3306
/* Command */
#define MYSQL_COM_SLEEP 0x00
#define MYSQL_COM_QUIT 0x01
#define MYSQL_COM_INIT_DB 0x02
#define MYSQL_COM_QUERY 0x03
#define MYSQL_COM_FIELD_LIST 0x04
#define MYSQL_COM_CREATE_DB 0x05
#define MYSQL_COM_DROP_DB 0x06
#define MYSQL_COM_REFRESH 0x07
#define MYSQL_COM_SHUTDOWN 0x08
#define MYSQL_COM_STATICTICS 0x09
#define MYSQL_COM_PROCESS_INFO 0x0a
#define MYSQL_COM_CONNECT 0x0b
#define MYSQL_COM_PROCESS_KILL 0x0c
#define MYSQL_COM_DEBUG 0x0d
#define MYSQL_COM_PING 0x0e
#define MYSQL_COM_TIME 0x0f
#define MYSQL_COM_DELAY_INSERT 0x10
#define MYSQL_COM_CHANGE_USER 0x11
#define MYSQL_COM_BINLOG_DUMP 0x12
#define MYSQL_COM_TABLE_DUMP 0x13
#define MYSQL_COM_CONNECT_OUT 0x14
#define MYSQL_COM_REGISTER_SLAVE 0x15
#define MYSQL_COM_STMT_PREPARE 0x16
#define MYSQL_COM_STMT_EXECUTE 0x17
#define MYSQL_COM_STMT_SEND_LONG_DATA 0x18
#define MYSQL_COM_STMT_CLOSE 0x19
#define MYSQL_COM_STMT_RESET 0x1a
#define MYSQL_COM_SET_OPTION 0x1b
#define MYSQL_COM_STMT_FETCH 0x1c
#define MYSQL_COM_DAEMON 0x1d
#define MYSQL_COM_BINLOG_DUMP_GTID 0x1e
#define MYSQL_COM_RESET_CONNECTION 0x1f
#define MYSQL_COM_SEMI_SYNC 0xef
#define MYSQL_OK_PACKET_HEADER_1 0x00
#define MYSQL_EOF_PACKET_HEADER 0xfe
#define MYSQL_TEXT_RESULT_RAW 0xfb
#define MYSQL_ERR_PACKET_HEADER 0xff
/* Handshake Protocol Version */
#define MYSQL_HANDSHAKE_PROTOCOL_V10 10
#define MYSQL_HANDSHAKE_PROTOCOL_V9 9
/* Capability Flags, Low 2 bytes
https://dev.mysql.com/doc/internals/en/capability-flags.html#packet-Protocol::CapabilityFlags : */
#define CLIENT_LONG_PASSWORD 0x0001
#define CLIENT_FOUND_ROWS 0x0002
#define CLIENT_LONG_FLAG 0x0004
#define CLIENT_CONNECT_WITH_DB 0x0008
#define CLIENT_NO_SCHEMA 0x0010
#define CLIENT_COMPRESS 0x0020
#define CLIENT_ODBC 0x0040
#define CLIENT_LOCAL_FILES 0x0080
#define CLIENT_IGNORE_SPACE 0x0100
#define CLIENT_PROTOCOL_41 0x0200
#define CLIENT_INTERACTIVE 0x0400
#define CLIENT_SSL 0x0800
#define CLIENT_IGNORE_SIGPIPE 0x1000
#define CLIENT_TRANSACTIONS 0x2000
#define CLIENT_RESERVED 0x4000
#define CLIENT_SECURE_CONNECTION 0x8000
/* Capability Flags, High 2 bytes */
#define CLIENT_MULTI_STATEMENTS 0x0001
#define CLIENT_MULTI_RESULTS 0x0002
#define CLIENT_PS_MULTI_RESULTS 0x0004
#define CLIENT_PLUGIN_AUTH 0x0008
#define CLIENT_CONNECT_ATTRS 0x0010
#define CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA 0x0020
#define CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS 0x0040
#define CLIENT_SESSION_TRACK 0x0080
#define CLIENT_DEPRECATE_EOF 0x0100
/* StatusFlags Flags
https://dev.mysql.com/doc/internals/en/status-flags.html#packet-Protocol::StatusFlags : */
#define SERVER_STATUS_IN_TRANS 0x0001
#define SERVER_STATUS_AUTOCOMMIT 0x0002
#define SERVER_STATUS_MUITI_QUERY 0x0004
#define SERVER_MORE_RESULTS_EXISTS 0x0008
#define SERVER_STATUS_NO_GOOD_INDEX_USED 0x0010
#define SERVER_STATUS_NO_INDEX_USED 0x0020
#define SERVER_STATUS_CURSOR_EXISTS 0x0040
#define SERVER_STATUS_LAST_ROW_SENT 0x0080
#define SERVER_STATUS_DB_DROPPED 0x0100
#define SERVER_STATUS_NO_BACKSLASH_ESCAPES 0x0200
#define SERVER_STATUS_METADATA_CHANGED 0x0400
#define SERVER_QUERY_WAS_SLOW 0x0800
#define SERVER_PS_OUT_PARAMS 0x1000
#define SERVER_STATUS_IN_TRANS_READONLY 0x2000
#define SERVER_SESSION_STATE_CHANGED 0x4000
/* OK_Packet: Session Track Type */
#define SESSION_TRACK_SYSTEM_VARIABLES 0x00
#define SESSION_TRACK_SCHEMA 0x01
#define SESSION_TRACK_STATE_CHANGE 0x02
#define SESSION_TRACK_GTIDS 0x03
/* Refresh Subcommand */
#define REFRESH_GRANT 0x01
#define REFRESH_LOG 0x02
#define REFRESH_TABLES 0x04
#define REFRESH_HOSTS 0x08
#define REFRESH_STATUS 0x10
#define REFRESH_THREADS 0x20
#define REFRESH_SLAVE 0x40
#define REFRESH_MASTER 0x80
/* Shutdown Type */
#define SHUTDOWN_DEFAULT 0x00
#define SHUTDOWN_WAIT_CONNECTIONS 0x01
#define SHUTDOWN_WAIT_TRANSACTIONS 0x02
#define SHUTDOWN_WAIT_UPDATES 0x08
#define SHUTDOWN_WAIT_ALL_BUFFERS 0x10
#define SHUTDOWN_WAIT_CRITICAL_BUFFERS 0x11
#define KILL_QUERY 0xfe
#define KILL_CONNECTION 0xff
/* Character Set */
#define DEC8_SWEDISH_CI 3
#define CP850_GENERAL_CI 4
#define KOI8R_GENERAL_CI 7
#define LATIN1_SWEDISH_CI 8
#define LATIN2_GENERAL_CI 9
#define SWE7_SWEDISH_CI 10
#define ASCII_GENERAL_CI 11
#define UJIS_JAPANESE_CI 12
#define SJIS_JAPANESE_CI 13
#define CP1251_BULGARIAN_CI 14
#define LATIN1_DANISH_CI 15
#define HEBREW_GENERAL_CI 16
#define LATIN7_ESTONIAN_CS 20
#define LATIN2_HUNGARIAN_CI 21
#define KOI8U_GENERAL_CI 22
#define CP1251_UKRAINIAN_CI 23
#define GREEK_GENERAL_CI 25
#define CP1250_GENERAL_CI 26
#define LATIN2_CROATIAN_CI 27
#define CP1257_LITHUANIAN_CI 29
#define LATIN5_TURKISH_CI 30
#define LATIN1_GERMAN2_CI 31
#define ARMSCII8_GENERAL_CI 32
#define UTF8_GENERAL_CI 33
#define CP866_GENERAL_CI 36
#define KEYBCS2_GENERAL_CI 37
#define MACCE_GENERAL_CI 38
#define MACROMAN_GENERAL_CI 39
#define CP852_GENERAL_CI 40
#define LATIN7_GENERAL_CI 41
#define LATIN7_GENERAL_CS 42
#define MACCE_BIN 43
#define CP1250_CROATIAN_CI 44
#define UTF8MB4_GENERAL_CI 45
#define UTF8MB4_BIN 46
#define LATIN1_BIN 47
#define LATIN1_GENERAL_CI 48
#define LATIN1_GENERAL_CS 49
#define CP1251_BIN 50
#define CP1251_GENERAL_CI 51
#define CP1251_GENERAL_CS 52
#define MACROMAN_BIN 53
#define CP1256_GENERAL_CI 57
#define CP1257_BIN 58
#define CP1257_GENERAL_CI 59
#define BINARY 63
#define ARMSCII8_BIN 64
#define ASCII_BIN 65
#define CP1250_BIN 66
#define CP1256_BIN 67
#define CP866_BIN 68
#define DEC8_BIN 69
#define GREEK_BIN 70
#define HEBREW_BIN 71
#define HP8_BIN 72
#define KEYBCS2_BIN 73
#define KOI8R_BIN 74
#define KOI8U_BIN 75
#define LATIN2_BIN 77
#define LATIN5_BIN 78
#define LATIN7_BIN 79
#define CP850_BIN 80
#define CP852_BIN 81
#define SWE7_BIN 82
#define UTF8_BIN 83
#define GEOSTD8_GENERAL_CI 92
#define GEOSTD8_BIN 93
#define LATIN1_SPANISH_CI 94
#define CP1250_POLISH_CI 99
#define UTF8_UNICODE_CI 192
#define UTF8_ICELANDIC_CI 193
#define UTF8_LATVIAN_CI 194
#define UTF8_ROMANIAN_CI 195
#define UTF8_SLOVENIAN_CI 196
#define UTF8_POLISH_CI 197
#define UTF8_ESTONIAN_CI 198
#define UTF8_SPANISH_CI 199
#define UTF8_SWEDISH_CI 200
#define UTF8_TURKISH_CI 201
#define UTF8_CZECH_CI 202
#define UTF8_DANISH_CI 203
#define UTF8_LITHUANIAN_CI 204
#define UTF8_SLOVAK_CI 205
#define UTF8_SPANISH2_CI 206
#define UTF8_ROMAN_CI 207
#define UTF8_PERSIAN_CI 208
#define UTF8_ESPERANTO_CI 209
#define UTF8_HUNGARIAN_CI 210
#define UTF8_SINHALA_CI 211
#define UTF8_GERMAN2_CI 212
#define UTF8_CROATIAN_CI 213
#define UTF8_UNICODE_520_CI 214
#define UTF8_VIETNAMESE_CI 215
#define UTF8_GENERAL_MYSQL500_CI 223
#define UTF8MB4_UNICODE_CI 224
#define UTF8MB4_ICELANDIC_CI 225
#define UTF8MB4_LATVIAN_CI 226
#define UTF8MB4_ROMANIAN_CI 227
#define UTF8MB4_SLOVENIAN_CI 228
#define UTF8MB4_POLISH_CI 229
#define UTF8MB4_ESTONIAN_CI 230
#define UTF8MB4_SPANISH_CI 231
#define UTF8MB4_SWEDISH_CI 232
#define UTF8MB4_TURKISH_CI 233
#define UTF8MB4_CZECH_CI 234
#define UTF8MB4_DANISH_CI 235
#define UTF8MB4_LITHUANIAN_CI 236
#define UTF8MB4_SLOVAK_CI 237
#define UTF8MB4_SPANISH2_CI 238
#define UTF8MB4_ROMAN_CI 239
#define UTF8MB4_PERSIAN_CI 240
#define UTF8MB4_ESPERANTO_CI 241
#define UTF8MB4_HUNGARIAN_CI 242
#define UTF8MB4_SINHALA_CI 243
#define UTF8MB4_GERMAN2_CI 244
#define UTF8MB4_CROATIAN_CI 245
#define UTF8MB4_UNICODE_520_CI 246
#define UTF8MB4_VIETNAMESE_CI 247
/* Field types */
#define MYSQL_TYPE_DECIMAL 0x00
#define MYSQL_TYPE_TINY 0x01
#define MYSQL_TYPE_SHORT 0x02
#define MYSQL_TYPE_LONG 0x03
#define MYSQL_TYPE_FLOAT 0x04
#define MYSQL_TYPE_DOUBLE 0x05
#define MYSQL_TYPE_NULL 0x06
#define MYSQL_TYPE_TIMESTAMP 0x07
#define MYSQL_TYPE_LONGLONG 0x08
#define MYSQL_TYPE_INT24 0x09
#define MYSQL_TYPE_DATE 0x0a
#define MYSQL_TYPE_TIME 0x0b
#define MYSQL_TYPE_DATETIME 0x0c
#define MYSQL_TYPE_YEAR 0x0d
#define MYSQL_TYPE_NEWDATE 0x0e
#define MYSQL_TYPE_VARCHAR 0x0f
#define MYSQL_TYPE_BIT 0x10
#define MYSQL_TYPE_TIMESTAMP2 0x11
#define MYSQL_TYPE_DATETIME2 0x12
#define MYSQL_TYPE_TIME2 0x13
#define MYSQL_TYPE_NEWDECIMAL 0xf6
#define MYSQL_TYPE_ENUM 0xf7
#define MYSQL_TYPE_SET 0xf8
#define MYSQL_TYPE_TINY_BLOB 0xf9
#define MYSQL_TYPE_MEDIUM_BLOB 0xfa
#define MYSQL_TYPE_LONG_BLOB 0xfb
#define MYSQL_TYPE_BLOB 0xfc
#define MYSQL_TYPE_VAR_STRING 0xfd
#define MYSQL_TYPE_STRING 0xfe
#define MYSQL_TYPE_GEOMETRY 0xff
#define MYSQL_OPTION_MULTI_STATEMENTS_ON 0
#define MYSQL_OPTION_MULTI_STATEMENTS_OFF 1
#define BINLOG_DUMP_NON_BLOCK 0x01
#define BINLOG_THROUGH_POSITION 0x02
#define BINLOG_THROUGH_GTID 0x04
struct LengthEncodedInteger
{
LengthEncodedInteger()
{
prefix = 0;
length = 0;
value = 0;
}
uint8_t prefix;
uint8_t length;
uint64_t value;
};
struct LengthEncodedString
{
uint64_t Length()
{
return length.length + length.value;
}
LengthEncodedInteger length;
String value;
};
using EOFString = String;
using NULString = String;
using FIXString = String;
#define MYSQL_PACKET_HEADER_LENGTH 4
/* MySQL Packet Header */
struct MySQLPacketHeaderPDU
{
uint8_t payload_len[3];
uint8_t sequence_id;
};
struct MySQLPacketHeader
{
MySQLPacketHeader()
{
payload_len = 0;
sequence_id = 0;
}
MySQLPacketHeaderPDU* pdu;
uint32_t payload_len;
uint8_t sequence_id;
};
#define MYSQL_HANDSHAKE_REQUEST_V10_LENGTH_MIN 17
#define MYSQL_HANDSHAKE_APDP_1_LENGTH 8
#define MYSQL_HANDSHAKE_RESERVED_LENGTH 10
#define MYSQL_HANDSHAKE_APDP_2_LENGTH_MIN 13
/* https://dev.mysql.com/doc/internals/en/connectio-phase-packets.html : HandshakeV10 */
struct HandshakeRequestV10
{
bool CapabilityFlagsBit(uint32_t bit)
{
static uint32_t flags = (capablity_flags_upper << 16) + capablity_flags_lower;
return flags & bit;
}
bool StatusFlagBit(uint16_t bit)
{
return status_flag & bit;
}
uint8_t protocol_version;
NULString server_version;
uint32_t connection_id;
//char auth_plugin_data_part1[MYSQL_HANDSHAKE_APDP_1_LENGTH];
FIXString auth_plugin_data_part1; // MYSQL_HANDSHAKE_APDP_1_LENGTH
uint8_t filler;
uint16_t capablity_flags_lower;
uint8_t character_set;
uint16_t status_flag;
uint16_t capablity_flags_upper;
uint8_t auth_plugin_data_len;
FIXString reserved; // MYSQL_HANDSHAKE_RESERVED_LENGTH
FIXString auth_plugin_data_part2;
EOFString auth_plugin_name;
};
#define MYSQL_HANDSHAKE_RESPONSE_41_LENGTH_MIN 32
#define MYSQL_HANDSHAKE_RESPONSE_320_LENGTH_MIN 33
#define MYSQL_HANDSHAKE_RESPONSE_41_RESERVED_LENGTH 23
struct MySQLConnectAttribute
{
LengthEncodedString key;
LengthEncodedString value;
};
/* https://dev.mysql.com/doc/internals/en/connectio-phase-packets.html : HandshakeResponse41 */
struct HandshakeResponse41
{
bool CapabilityFlagsBit(uint32_t bit)
{
return capability_flags & bit;
}
MySQLPacketHeader hdr;
uint32_t capability_flags;
uint32_t max_packet_size;
uint8_t character_set;
FIXString reserved; // MYSQL_HANDSHAKE_RESPONSE_41_RESERVED_LENGTH
NULString username;
LengthEncodedInteger auth_response_len;
FIXString auth_response;
NULString database;
NULString auth_plugin_name;
LengthEncodedInteger attr_len;
std::vector<MySQLConnectAttribute> attrs;
};
struct CommandRequest
{
const char* Command()
{
switch (command)
{
case MYSQL_COM_SLEEP: return "Sleep";
case MYSQL_COM_QUIT: return "Quit";
case MYSQL_COM_INIT_DB: return "Use Database";
case MYSQL_COM_QUERY: return "Query";
case MYSQL_COM_FIELD_LIST: return "Show Fields";
case MYSQL_COM_CREATE_DB: return "Create Database";
case MYSQL_COM_DROP_DB: return "Drop Database";
case MYSQL_COM_REFRESH: return "Refresh";
case MYSQL_COM_SHUTDOWN: return "Shutdown";
case MYSQL_COM_STATICTICS: return "Statictics";
case MYSQL_COM_PROCESS_INFO: return "Process Information";
case MYSQL_COM_CONNECT: return "Connect";
case MYSQL_COM_PROCESS_KILL: return "Process Kill";
case MYSQL_COM_DEBUG: return "Debug";
case MYSQL_COM_PING: return "Ping";
case MYSQL_COM_TIME: return "Time";
case MYSQL_COM_DELAY_INSERT: return "Delay Insert";
case MYSQL_COM_CHANGE_USER: return "Change User";
case MYSQL_COM_BINLOG_DUMP: return "Send Binlog";
case MYSQL_COM_TABLE_DUMP: return "Table Dump";
case MYSQL_COM_CONNECT_OUT: return "Connect Out";
case MYSQL_COM_REGISTER_SLAVE: return "Register Slave";
case MYSQL_COM_STMT_PREPARE: return "Prepare Statement";
case MYSQL_COM_STMT_EXECUTE: return "Execute Statement";
case MYSQL_COM_STMT_SEND_LONG_DATA: return "Send Statement Long Data";
case MYSQL_COM_STMT_CLOSE: return "Close Statement";
case MYSQL_COM_STMT_RESET: return "Reset Statement";
case MYSQL_COM_SET_OPTION: return "Set Option";
case MYSQL_COM_STMT_FETCH: return "Fetch Statement";
case MYSQL_COM_DAEMON: return "STMT Daemon";
case MYSQL_COM_BINLOG_DUMP_GTID: return "Binlog Dump GTID";
case MYSQL_COM_RESET_CONNECTION: return "Reset Connection";
case MYSQL_COM_SEMI_SYNC: return "SEMI Synchronous";
default: return "Unkonwn command";
}
}
/* COM_INIT_DB */
struct InitDB
{
EOFString schema_name;
};
/* COM_QUERY */
struct Query
{
EOFString statement;
};
/* COM_FIELD_LIST */
struct FieldList
{
NULString table;
EOFString field_wildcard;
};
/* COM_CREATE_DB */
struct CreateDB
{
EOFString schema_name;
};
/* COM_DROP_DB */
struct DropDB
{
EOFString schema_name;
};
/* COM_REFRESH */
struct Refresh
{
const char* SubCommand()
{
switch (sub_command)
{
case REFRESH_GRANT: return "Grant";
case REFRESH_LOG: return "Log";
case REFRESH_TABLES: return "Tables";
case REFRESH_HOSTS: return "Hosts";
case REFRESH_STATUS: return "Status";
case REFRESH_THREADS: return "Threads";
case REFRESH_SLAVE: return "Slave";
case REFRESH_MASTER: return "Master";
default: return "Unknown";
}
}
uint8_t sub_command;
};
/* COM_SHUTDOWN */
struct Shutdown
{
const char* Type()
{
switch (shutdown_type)
{
case SHUTDOWN_DEFAULT: return "Default";
case SHUTDOWN_WAIT_CONNECTIONS: return "Wait Connections";
case SHUTDOWN_WAIT_TRANSACTIONS: return "Wait Transactions";
case SHUTDOWN_WAIT_UPDATES: return "Wait Updates";
case SHUTDOWN_WAIT_ALL_BUFFERS: return "Wait All Buffers";
case SHUTDOWN_WAIT_CRITICAL_BUFFERS: return "Wait Critical Buffers";
case KILL_QUERY: return "Kill Query";
case KILL_CONNECTION: return "Kill Conection";
default: return "Unknown";
}
}
uint8_t shutdown_type;
};
/* COM_PROCESS_KILL */
struct ProcessKill
{
uint32_t connection_id;
};
/* COM_CHANGE_USER */
struct ChangeUser
{
NULString user;
uint8_t auth_response_len;
FIXString auth_response;
NULString schema_name;
uint16_t character_set;
NULString auth_plugin_name;
LengthEncodedInteger attr_len;
std::vector<MySQLConnectAttribute> attrs;
};
/* COM_BINLOG_DUMP */
struct BinLogDump
{
uint32_t binlog_pos;
uint16_t flags;
uint32_t server_id;
EOFString binlog_file_name;
};
/* COM_BINLOG_DUMP_GTID */
struct BinLogDumpGTID
{
uint16_t flags;
uint32_t server_id;
uint32_t binlog_filename_len;
FIXString binlog_filename;
uint64_t binlog_pos;
uint32_t data_size;
FIXString data;
};
/* COM_TABLE_DUMP */
struct TableDump
{
uint8_t database_len;
FIXString database_name;
uint8_t table_len;
FIXString table_name;
};
/* COM_REGISTER_SLAVE */
struct RegisterSlave
{
uint32_t server_id;
uint8_t hostname_len;
FIXString hostname;
uint8_t user_len;
FIXString user;
uint8_t password_len;
FIXString password;
uint16_t port;
uint32_t replication_rank;
uint32_t master_id;
};
/* MYSQL_COM_STMT_PREPARE */
struct STMTPrepare
{
EOFString query;
};
/* COM_STMT_SEND_LONG_DATA */
struct STMTSendLongData
{
uint32_t statement_id;
uint16_t param_id;
EOFString data;
};
/* COM_STMT_CLOSE */
struct STMTClose
{
uint32_t statement_id;
};
/* COM_STMT_RESET */
struct STMTReset
{
uint32_t statement_id;
};
/* COM_STMT_FETCH */
struct STMTFetch
{
uint32_t statement_id;
uint32_t num_rows;
};
/* COM_SET_OPTION */
struct SetOption
{
const char* OptionOperation()
{
switch (option_operation)
{
case MYSQL_OPTION_MULTI_STATEMENTS_ON: return "MYSQL_OPTION_MULTI_STATEMENTS_ON";
case MYSQL_OPTION_MULTI_STATEMENTS_OFF: return "MYSQL_OPTION_MULTI_STATEMENTS_OFF";
default: return "?";
}
}
uint16_t option_operation;
};
/* COM_SEMI_SYNC */
struct SEMISync
{
uint8_t flag;
};
uint8_t command;
InitDB initDB;
Query query;
FieldList fieldList;
CreateDB createDB;
DropDB dropDB;
Refresh refresh;
Shutdown shutdown;
ProcessKill processKill;
ChangeUser changeUser;
BinLogDump binLogDump;
BinLogDumpGTID binLogDumpGTID;
TableDump tableDump;
RegisterSlave registerSlave;
STMTPrepare stmtPrepare;
STMTSendLongData stmtSendData;
STMTClose stmtClose;
STMTReset stmtReset;
STMTFetch stmtFetch;
SetOption setOption;
SEMISync semiSync;
};
#define MYSQL_OK_PACKET_LENGTH_MIN 7
/* OK_Packet */
struct OKResponse
{
struct SessionStateInfoData
{
/* MYSQL_SESSION_TRACK_SYSTEM_VARIABLES */
struct SessionTrackSystemVariable
{
LengthEncodedString name;
LengthEncodedString value;
};
/* MYSQL_SESSION_TRACK_SCHEMA */
struct SessionTrackSchema
{
LengthEncodedString name;
};
/* MYSQL_SESSION_TRACK_STATE_CHANGE */
struct SessionTrackStateChange
{
LengthEncodedString is_tracked;
};
/* MYSQL_SESSION_TRACK_GTIDS */
struct SessionTrackGTIDS
{
LengthEncodedString grids;
};
SessionTrackSystemVariable track_sys_var;
SessionTrackSchema track_schema;
SessionTrackStateChange track_state_change;
SessionTrackGTIDS track_gtids;
};
struct SessionStateInfo
{
const char* Type()
{
switch (type)
{
case SESSION_TRACK_SYSTEM_VARIABLES: return "SESSION_TRACK_SYSTEM_VARIABLES";
case SESSION_TRACK_SCHEMA: return "SESSION_TRACK_SCHEMA";
case SESSION_TRACK_STATE_CHANGE: return "SESSION_TRACK_STATE_CHANGE";
case SESSION_TRACK_GTIDS: return "SESSION_TRACK_GTIDS";
default: return "?";
}
}
uint8_t type;
LengthEncodedString data_str; // data_str的字符串值就是data的字节流数据
SessionStateInfoData data; // data_str的字符串值解码后的结构
};
struct SesstionStateChanges
{
LengthEncodedString infos_str; // infos_str的字符串值就是info的字节流数据
SessionStateInfo infos; // infos_str的字符串值解码后的结构
};
bool StatusFlagBit(uint16_t bit)
{
return status_flag & bit;
}
uint8_t header;
LengthEncodedInteger affected_rows;
LengthEncodedInteger last_insert_id;
uint16_t status_flag;
uint16_t warnings;
LengthEncodedString status_info;
SesstionStateChanges state_changes;
};
#define MYSQL_ERROR_PACKET_LENGTH_MIN 4
#define MYSQL_ERROR_PACKET_SQL_STATE_LEN 5
/* ERR_Packet */
struct ERRResponse
{
uint8_t header;
uint16_t error_code;
uint8_t sql_state_marker;
//char sql_state[MYSQL_ERROR_PACKET_SQL_STATE_LEN];
FIXString sql_state;
EOFString error_msg;
};
/* EOF_Packet */
struct EOFResponse
{
uint8_t header;
uint16_t warnings;
uint16_t status_flags;
};
/* ColumnDefinition41 */
struct ColumnDefinition41
{
const char* Type()
{
switch (type)
{
case MYSQL_TYPE_DECIMAL: return "MYSQL_TYPE_DECIMAL";
case MYSQL_TYPE_TINY: return "MYSQL_TYPE_TINY";
case MYSQL_TYPE_SHORT: return "MYSQL_TYPE_SHORT";
case MYSQL_TYPE_LONG: return "MYSQL_TYPE_LONG";
case MYSQL_TYPE_FLOAT: return "MYSQL_TYPE_FLOAT";
case MYSQL_TYPE_DOUBLE: return "MYSQL_TYPE_DOUBLE";
case MYSQL_TYPE_NULL: return "MYSQL_TYPE_NULL";
case MYSQL_TYPE_TIMESTAMP: return "MYSQL_TYPE_TIMESTAMP";
case MYSQL_TYPE_LONGLONG: return "MYSQL_TYPE_LONGLONG";
case MYSQL_TYPE_INT24: return "MYSQL_TYPE_INT24";
case MYSQL_TYPE_DATE: return "MYSQL_TYPE_DATE";
case MYSQL_TYPE_TIME: return "MYSQL_TYPE_TIME";
case MYSQL_TYPE_DATETIME: return "MYSQL_TYPE_DATETIME";
case MYSQL_TYPE_YEAR: return "MYSQL_TYPE_YEAR";
case MYSQL_TYPE_NEWDATE: return "MYSQL_TYPE_NEWDATE";
case MYSQL_TYPE_VARCHAR: return "MYSQL_TYPE_VARCHAR";
case MYSQL_TYPE_BIT: return "MYSQL_TYPE_BIT";
case MYSQL_TYPE_TIMESTAMP2: return "MYSQL_TYPE_TIMESTAMP2";
case MYSQL_TYPE_DATETIME2: return "MYSQL_TYPE_DATETIME2";
case MYSQL_TYPE_TIME2: return "MYSQL_TYPE_TIME2";
case MYSQL_TYPE_NEWDECIMAL: return "MYSQL_TYPE_NEWDECIMAL";
case MYSQL_TYPE_ENUM: return "MYSQL_TYPE_ENUM";
case MYSQL_TYPE_SET: return "MYSQL_TYPE_SET";
case MYSQL_TYPE_TINY_BLOB: return "MYSQL_TYPE_TINY_BLOB";
case MYSQL_TYPE_MEDIUM_BLOB: return "MYSQL_TYPE_MEDIUM_BLOB";
case MYSQL_TYPE_LONG_BLOB: return "MYSQL_TYPE_LONG_BLOB";
case MYSQL_TYPE_BLOB: return "MYSQL_TYPE_BLOB";
case MYSQL_TYPE_VAR_STRING: return "MYSQL_TYPE_VAR_STRING";
case MYSQL_TYPE_STRING: return "MYSQL_TYPE_STRING";
case MYSQL_TYPE_GEOMETRY: return "MYSQL_TYPE_GEOMETRY";
default: return "?";
}
}
ColumnDefinition41()
{
character_set = 0;
column_len = 0;
type = 0;
flags = 0;
decimals = 0;
filler = 0;
}
LengthEncodedString catalog;
LengthEncodedString schema;
LengthEncodedString table;
LengthEncodedString org_table;
LengthEncodedString name;
LengthEncodedString org_name;
LengthEncodedInteger fixed_fields_len;
uint16_t character_set;
uint32_t column_len;
uint8_t type;
uint16_t flags;
uint8_t decimals;
uint16_t filler;
LengthEncodedInteger default_values_len;
FIXString default_values;
};
struct TextResultRow
{
bool null;
std::vector<LengthEncodedString> texts;
};
struct ColumnCount
{
LengthEncodedInteger column_count;
};
struct MySQLDecodedObject
{
const char* CharacterSet(uint8_t id)
{
switch (id)
{
case DEC8_SWEDISH_CI: return "dec8 COLLATE dec8_swedish_ci";
case CP850_GENERAL_CI: return "cp850 COLLATE cp850_general_ci";
case KOI8R_GENERAL_CI: return "koi8r COLLATE koi8r_general_ci";
case LATIN1_SWEDISH_CI: return "latin1 COLLATE latin1_swedish_ci";
case LATIN2_GENERAL_CI: return "latin2 COLLATE latin2_general_ci";
case SWE7_SWEDISH_CI: return "swe7 COLLATE swe7_swedish_ci";
case ASCII_GENERAL_CI: return "ascii COLLATE ascii_general_ci";
case UJIS_JAPANESE_CI: return "ujis COLLATE ujis_japanese_ci";
case SJIS_JAPANESE_CI: return "sjis COLLATE sjis_japanese_ci";
case CP1251_BULGARIAN_CI: return "cp1251 COLLATE cp1251_bulgarian_ci";
case LATIN1_DANISH_CI: return "latin1 COLLATE latin1_danish_ci";
case HEBREW_GENERAL_CI: return "hebrew COLLATE hebrew_general_ci";
case LATIN7_ESTONIAN_CS: return "latin7 COLLATE latin7_estonian_cs";
case LATIN2_HUNGARIAN_CI: return "latin2 COLLATE latin2_hungarian_ci";
case KOI8U_GENERAL_CI: return "koi8u COLLATE koi8u_general_ci";
case CP1251_UKRAINIAN_CI: return "cp1251 COLLATE cp1251_ukrainian_ci";
case GREEK_GENERAL_CI: return "greek COLLATE greek_general_ci";
case CP1250_GENERAL_CI: return "cp1250 COLLATE cp1250_general_ci";
case LATIN2_CROATIAN_CI: return "latin2 COLLATE latin2_croatian_ci";
case CP1257_LITHUANIAN_CI: return "cp1257 COLLATE cp1257_lithuanian_ci";
case LATIN5_TURKISH_CI: return "latin5 COLLATE latin5_turkish_ci";
case LATIN1_GERMAN2_CI: return "latin1 COLLATE latin1_german2_ci";
case ARMSCII8_GENERAL_CI: return "armscii8 COLLATE armscii8_general_ci";
case UTF8_GENERAL_CI: return "utf8 COLLATE utf8_general_ci";
case CP866_GENERAL_CI: return "cp866 COLLATE cp866_general_ci";
case KEYBCS2_GENERAL_CI: return "keybcs2 COLLATE keybcs2_general_ci";
case MACCE_GENERAL_CI: return "macce COLLATE macce_general_ci";
case MACROMAN_GENERAL_CI: return "macroman COLLATE macroman_general_ci";
case CP852_GENERAL_CI: return "cp852 COLLATE cp852_general_ci";
case LATIN7_GENERAL_CI: return "latin7 COLLATE latin7_general_ci";
case LATIN7_GENERAL_CS: return "latin7 COLLATE latin7_general_cs";
case MACCE_BIN: return "macce COLLATE macce_bin";
case CP1250_CROATIAN_CI: return "cp1250 COLLATE cp1250_croatian_ci";
case UTF8MB4_GENERAL_CI: return "utf8mb4 COLLATE utf8mb4_general_ci";
case UTF8MB4_BIN: return "utf8mb4 COLLATE utf8mb4_bin";
case LATIN1_BIN: return "latin1 COLLATE latin1_bin";
case LATIN1_GENERAL_CI: return "latin1 COLLATE latin1_general_ci";
case LATIN1_GENERAL_CS: return "latin1 COLLATE latin1_general_cs";
case CP1251_BIN: return "cp1251 COLLATE cp1251_bin";
case CP1251_GENERAL_CI: return "cp1251 COLLATE cp1251_general_ci";
case CP1251_GENERAL_CS: return "cp1251 COLLATE cp1251_general_cs";
case MACROMAN_BIN: return "macroman COLLATE macroman_bin";
case CP1256_GENERAL_CI: return "cp1256 COLLATE cp1256_general_ci";
case CP1257_BIN: return "cp1257 COLLATE cp1257_bin";
case CP1257_GENERAL_CI: return "cp1257 COLLATE cp1257_general_ci";
case BINARY: return "binary COLLATE binary";
case ARMSCII8_BIN: return "armscii8 COLLATE armscii8_bin";
case ASCII_BIN: return "ascii COLLATE ascii_bin";
case CP1250_BIN: return "cp1250 COLLATE cp1250_bin";
case CP1256_BIN: return "cp1256 COLLATE cp1256_bin";
case CP866_BIN: return "cp866 COLLATE cp866_bin";
case DEC8_BIN: return "dec8 COLLATE dec8_bin";
case GREEK_BIN: return "greek COLLATE greek_bin";
case HEBREW_BIN: return "hebrew COLLATE hebrew_bin";
case HP8_BIN: return "hp8 COLLATE hp8_bin";
case KEYBCS2_BIN: return "keybcs2 COLLATE keybcs2_bin";
case KOI8R_BIN: return "koi8r COLLATE koi8r_bin";
case KOI8U_BIN: return "koi8u COLLATE koi8u_bin";
case LATIN2_BIN: return "latin2 COLLATE latin2_bin";
case LATIN5_BIN: return "latin5 COLLATE latin5_bin";
case LATIN7_BIN: return "latin7 COLLATE latin7_bin";
case CP850_BIN: return "cp850 COLLATE cp850_bin";
case CP852_BIN: return "cp852 COLLATE cp852_bin";
case SWE7_BIN: return "swe7 COLLATE swe7_bin";
case UTF8_BIN: return "utf8 COLLATE utf8_bin";
case GEOSTD8_GENERAL_CI: return "geostd8 COLLATE geostd8_general_ci";
case GEOSTD8_BIN: return "geostd8 COLLATE geostd8_bin";
case LATIN1_SPANISH_CI: return "latin1 COLLATE latin1_spanish_ci";
case CP1250_POLISH_CI: return "cp1250 COLLATE cp1250_polish_ci";
case UTF8_UNICODE_CI: return "utf8 COLLATE utf8_unicode_ci";
case UTF8_ICELANDIC_CI: return "utf8 COLLATE utf8_icelandic_ci";
case UTF8_LATVIAN_CI: return "utf8 COLLATE utf8_latvian_ci";
case UTF8_ROMANIAN_CI: return "utf8 COLLATE utf8_romanian_ci";
case UTF8_SLOVENIAN_CI: return "utf8 COLLATE utf8_slovenian_ci";
case UTF8_POLISH_CI: return "utf8 COLLATE utf8_polish_ci";
case UTF8_ESTONIAN_CI: return "utf8 COLLATE utf8_estonian_ci";
case UTF8_SPANISH_CI: return "utf8 COLLATE utf8_spanish_ci";
case UTF8_SWEDISH_CI: return "utf8 COLLATE utf8_swedish_ci";
case UTF8_TURKISH_CI: return "utf8 COLLATE utf8_turkish_ci";
case UTF8_CZECH_CI: return "utf8 COLLATE utf8_czech_ci";
case UTF8_DANISH_CI: return "utf8 COLLATE utf8_danish_ci";
case UTF8_LITHUANIAN_CI: return "utf8 COLLATE utf8_lithuanian_ci";
case UTF8_SLOVAK_CI: return "utf8 COLLATE utf8_slovak_ci";
case UTF8_SPANISH2_CI: return "utf8 COLLATE utf8_spanish2_ci";
case UTF8_ROMAN_CI: return "utf8 COLLATE utf8_roman_ci";
case UTF8_PERSIAN_CI: return "utf8 COLLATE utf8_persian_ci";
case UTF8_ESPERANTO_CI: return "utf8 COLLATE utf8_esperanto_ci";
case UTF8_HUNGARIAN_CI: return "utf8 COLLATE utf8_hungarian_ci";
case UTF8_SINHALA_CI: return "utf8 COLLATE utf8_sinhala_ci";
case UTF8_GERMAN2_CI: return "utf8 COLLATE utf8_german2_ci";
case UTF8_CROATIAN_CI: return "utf8 COLLATE utf8_croatian_ci";
case UTF8_UNICODE_520_CI: return "utf8 COLLATE utf8_unicode_520_ci";
case UTF8_VIETNAMESE_CI: return "utf8 COLLATE utf8_vietnamese_ci";
case UTF8_GENERAL_MYSQL500_CI: return "utf8 COLLATE utf8_general_mysql500_ci";
case UTF8MB4_UNICODE_CI: return "utf8mb4 COLLATE utf8mb4_unicode_ci";
case UTF8MB4_ICELANDIC_CI: return "utf8mb4 COLLATE utf8mb4_icelandic_ci";
case UTF8MB4_LATVIAN_CI: return "utf8mb4 COLLATE utf8mb4_latvian_ci";
case UTF8MB4_ROMANIAN_CI: return "utf8mb4 COLLATE utf8mb4_romanian_ci";
case UTF8MB4_SLOVENIAN_CI: return "utf8mb4 COLLATE utf8mb4_slovenian_ci";
case UTF8MB4_POLISH_CI: return "utf8mb4 COLLATE utf8mb4_polish_ci";
case UTF8MB4_ESTONIAN_CI: return "utf8mb4 COLLATE utf8mb4_estonian_ci";
case UTF8MB4_SPANISH_CI: return "utf8mb4 COLLATE utf8mb4_spanish_ci";
case UTF8MB4_SWEDISH_CI: return "utf8mb4 COLLATE utf8mb4_swedish_ci";
case UTF8MB4_TURKISH_CI: return "utf8mb4 COLLATE utf8mb4_turkish_ci";
case UTF8MB4_CZECH_CI: return "utf8mb4 COLLATE utf8mb4_czech_ci";
case UTF8MB4_DANISH_CI: return "utf8mb4 COLLATE utf8mb4_danish_ci";
case UTF8MB4_LITHUANIAN_CI: return "utf8mb4 COLLATE utf8mb4_lithuanian_ci";
case UTF8MB4_SLOVAK_CI: return "utf8mb4 COLLATE utf8mb4_slovak_ci";
case UTF8MB4_SPANISH2_CI: return "utf8mb4 COLLATE utf8mb4_spanish2_ci";
case UTF8MB4_ROMAN_CI: return "utf8mb4 COLLATE utf8mb4_roman_ci";
case UTF8MB4_PERSIAN_CI: return "utf8mb4 COLLATE utf8mb4_persian_ci";
case UTF8MB4_ESPERANTO_CI: return "utf8mb4 COLLATE utf8mb4_esperanto_ci";
case UTF8MB4_HUNGARIAN_CI: return "utf8mb4 COLLATE utf8mb4_hungarian_ci";
case UTF8MB4_SINHALA_CI: return "utf8mb4 COLLATE utf8mb4_sinhala_ci";
case UTF8MB4_GERMAN2_CI: return "utf8mb4 COLLATE utf8mb4_german2_ci";
case UTF8MB4_CROATIAN_CI: return "utf8mb4 COLLATE utf8mb4_croatian_ci";
case UTF8MB4_UNICODE_520_CI: return "utf8mb4 COLLATE utf8mb4_unicode_520_ci";
case UTF8MB4_VIETNAMESE_CI: return "utf8mb4 COLLATE utf8mb4_vietnamese_ci";
default: return "Unkonwn";
}
}
enum MySQLPacketType
{
UNKNOWN_PACKET,
HANDSHAKE_REQUEST_V10,
HANDSHAKE_REQUEST_V9,
HANDSHAKE_RESPONSE_41,
COMMAND_REQUEST,
COLUMN_DEFINITION_41,
OK_RESPONSE,
ERR_RESPONSE,
EOF_RESPONSE,
TEXT_RESULT_ROW,
COLUMN_COUNT
};
MySQLDecodedObject()
{
type = UNKNOWN_PACKET;
}
MySQLPacketHeader header;
MySQLPacketType type;
HandshakeRequestV10 handshake_req;
HandshakeResponse41 handshake_rsp;
CommandRequest cmd_req;
OKResponse ok_rsp;
ERRResponse err_rsp;
EOFResponse eof_rsp;
ColumnDefinition41 column;
TextResultRow row;
ColumnCount column_count;
};
class MySQLPacket
{
public:
MySQLPacket(const uint8_t * pData, uint32_t nLength);
~MySQLPacket(void);
public:
bool Decode(void);
std::vector<MySQLDecodedObject*>& GetDecodedObject() { return m_objects; }
private:
bool ParseObject(uint32_t nOffset, MySQLDecodedObject* pObject, bool bTextRow = false);
bool ParseHeader(uint32_t nOffset, MySQLPacketHeader* pHeader);
bool ParseCommandRequest(uint32_t nOffset, uint32_t nPayloadLen, CommandRequest* pRequest);
bool ParseHandshakeRequestV10(uint32_t nOffset, HandshakeRequestV10* pRequest);
bool ParseHandshakeResponse41(uint32_t nOffset, uint32_t nPayloadLen, HandshakeResponse41* pResponse);
bool ParseOKResponse(uint32_t nOffset, uint32_t nPayloadLen, OKResponse* pResponse);
bool ParseErrorResponse(uint32_t nOffset, ERRResponse* pResponse);
bool ParseEOFResponse(uint32_t nOffset, uint32_t nPayloadLen, EOFResponse* pResponse);
bool ParseColumnCount(uint32_t nOffset, ColumnCount* pCount);
bool ParseTextResultRow(uint32_t nOffset, uint32_t nPayloadLen, TextResultRow* pRow);
bool ParseColumnDefinition41(uint32_t nOffset, uint32_t nPayloadLen, ColumnDefinition41* pColumn);
bool ParseChangeUser(uint32_t& nOffset, CommandRequest* pRequest);
bool ParseLenencInteger(uint32_t nOffset, LengthEncodedInteger& data);
bool ParseNULString(uint32_t nOffset, NULString& data, uint32_t* pDataLen);
bool ParseEOFString(uint32_t nOffset, EOFString& data, uint32_t* pDataLen);
bool ParseFIXString(uint32_t nOffset, uint32_t nLen, FIXString& data);
bool ParseLenencString(uint32_t nOffset, LengthEncodedString& data);
private:
const uint8_t * m_pData;
uint32_t m_nLength;
std::vector<MySQLDecodedObject*> m_objects;
};
#endif
/**
* @file mysql.cpp
* @brief Mysql Protocol
* @author yangyiyin
* @date 2021/12
* @copyright
*/
#include <string.h>
#include "mysql.h"
MySQLPacket::MySQLPacket(const uint8_t * pData, uint32_t nLength)
: m_pData(pData)
, m_nLength(nLength)
{
}
MySQLPacket::~MySQLPacket(void)
{
}
bool MySQLPacket::Decode(void)
{
if(m_nLength < MYSQL_PACKET_HEADER_LENGTH)
{
return false;
}
uint32_t nOffset = 0;
MySQLDecodedObject* pObject = NULL;
uint8_t nTextRowFlag = 0;
while (nOffset < m_nLength)
{
pObject = new MySQLDecodedObject;
if (!ParseObject(nOffset, pObject, 0x07 == nTextRowFlag))
{
delete pObject;
}
else
{
m_objects.push_back(pObject);
}
switch (pObject->type)
{
case MySQLDecodedObject::COLUMN_COUNT:
nTextRowFlag |= 0x01;
break;
case MySQLDecodedObject::COLUMN_DEFINITION_41:
nTextRowFlag |= 0x02;
break;
case MySQLDecodedObject::EOF_RESPONSE:
nTextRowFlag |= 0x04;
break;
default:
break;
}
nOffset += (MYSQL_PACKET_HEADER_LENGTH + pObject->header.payload_len);
}
return true;
}
bool MySQLPacket::ParseObject(uint32_t nOffset, MySQLDecodedObject* pObject, bool bTextRow)
{
if (!ParseHeader(nOffset, &pObject->header))
{
return false;
}
nOffset += MYSQL_PACKET_HEADER_LENGTH;
if (pObject->header.payload_len >= MYSQL_OK_PACKET_LENGTH_MIN && (MYSQL_OK_PACKET_HEADER_1 == m_pData[nOffset]))
{
if (ParseOKResponse(nOffset, pObject->header.payload_len, &pObject->ok_rsp))
{
pObject->type = MySQLDecodedObject::MySQLPacketType::OK_RESPONSE;
return true;
}
}
if (pObject->header.payload_len >= MYSQL_ERROR_PACKET_LENGTH_MIN && MYSQL_ERR_PACKET_HEADER == m_pData[nOffset])
{
if (ParseErrorResponse(nOffset, &pObject->err_rsp))
{
pObject->type = MySQLDecodedObject::MySQLPacketType::ERR_RESPONSE;
return true;
}
}
if (MYSQL_EOF_PACKET_HEADER == m_pData[nOffset])
{
if (ParseEOFResponse(nOffset, pObject->header.payload_len, &pObject->eof_rsp))
{
pObject->type = MySQLDecodedObject::MySQLPacketType::EOF_RESPONSE;
return true;
}
}
if (1 == pObject->header.payload_len && m_nLength - nOffset != pObject->header.payload_len)
{
if (ParseColumnCount(nOffset, &pObject->column_count))
{
pObject->type = MySQLDecodedObject::MySQLPacketType::COLUMN_COUNT;
return true;
}
}
if (!bTextRow && ParseColumnDefinition41(nOffset, pObject->header.payload_len, &pObject->column))
{
pObject->type = MySQLDecodedObject::MySQLPacketType::COLUMN_DEFINITION_41;
return true;
}
if (pObject->header.payload_len >= MYSQL_HANDSHAKE_REQUEST_V10_LENGTH_MIN && MYSQL_HANDSHAKE_PROTOCOL_V10 == m_pData[nOffset])
{
if (ParseHandshakeRequestV10(nOffset, &pObject->handshake_req))
{
pObject->type = MySQLDecodedObject::MySQLPacketType::HANDSHAKE_REQUEST_V10;
return true;
}
}
if (ParseCommandRequest(nOffset, pObject->header.payload_len, &pObject->cmd_req))
{
pObject->type = MySQLDecodedObject::MySQLPacketType::COMMAND_REQUEST;
return true;
}
if (pObject->header.payload_len >= MYSQL_HANDSHAKE_RESPONSE_41_LENGTH_MIN)
{
if (ParseHandshakeResponse41(nOffset, pObject->header.payload_len, &pObject->handshake_rsp))
{
pObject->type = MySQLDecodedObject::MySQLPacketType::HANDSHAKE_RESPONSE_41;
return true;
}
}
else if (ParseTextResultRow(nOffset, pObject->header.payload_len, &pObject->row))
{
pObject->type = MySQLDecodedObject::MySQLPacketType::TEXT_RESULT_ROW;
return true;
}
return false;
}
bool MySQLPacket::ParseHeader(uint32_t nOffset, MySQLPacketHeader* pHeader)
{
if (nOffset + MYSQL_PACKET_HEADER_LENGTH >= m_nLength)
{
return false;
}
pHeader->pdu = (MySQLPacketHeaderPDU*)(m_pData + nOffset);
pHeader->payload_len = (*(uint32_t*)(pHeader->pdu->payload_len));
pHeader->payload_len &= 0x00ffffff;
pHeader->sequence_id = pHeader->pdu->sequence_id;
return true;
}
bool MySQLPacket::ParseCommandRequest(uint32_t nOffset, uint32_t nPayloadLen, CommandRequest* pRequest)
{
uint32_t nStartOffset = nOffset;
uint32_t nLen = 0;
pRequest->command = m_pData[nOffset++];
switch (pRequest->command)
{
case MYSQL_COM_INIT_DB:
if (!ParseEOFString(nOffset, pRequest->initDB.schema_name, &nLen))
{
return false;
}
nOffset += nLen;
break;
case MYSQL_COM_QUERY:
if(!ParseEOFString(nOffset, pRequest->query.statement, &nLen))
{
return false;
}
nOffset += nLen;
break;
case MYSQL_COM_FIELD_LIST:
if (!ParseNULString(nOffset, pRequest->fieldList.table, &nLen))
{
return false;
}
nOffset += nLen;
if (!ParseEOFString(nOffset, pRequest->fieldList.field_wildcard, &nLen))
{
return false;
}
nOffset += nLen;
break;
case MYSQL_COM_CREATE_DB:
if(!ParseEOFString(nOffset, pRequest->createDB.schema_name, &nLen))
{
return false;
}
nOffset += nLen;
break;
case MYSQL_COM_DROP_DB:
if(!ParseEOFString(nOffset, pRequest->dropDB.schema_name, &nLen))
{
return false;
}
nOffset += nLen;
break;
case MYSQL_COM_REFRESH:
pRequest->refresh.sub_command = m_pData[nOffset++];
break;
case MYSQL_COM_SHUTDOWN:
pRequest->shutdown.shutdown_type = m_pData[nOffset++];
break;
case MYSQL_COM_PROCESS_KILL:
pRequest->processKill.connection_id = *(uint32_t*)(m_pData + nOffset);
nOffset += 4;
break;
case MYSQL_COM_CHANGE_USER:
if (!ParseChangeUser(nOffset, pRequest))
{
return false;
}
break;
case MYSQL_COM_BINLOG_DUMP:
pRequest->binLogDump.binlog_pos = *(uint32_t*)(m_pData + nOffset);
nOffset += 4;
pRequest->binLogDump.flags = *(uint16_t*)(m_pData + nOffset);
nOffset += 2;
pRequest->binLogDump.server_id = *(uint32_t*)(m_pData + nOffset);
nOffset += 4;
if(!ParseEOFString(nOffset, pRequest->binLogDump.binlog_file_name, &nLen))
{
return false;
}
nOffset += nLen;
break;
case MYSQL_COM_BINLOG_DUMP_GTID:
pRequest->binLogDumpGTID.flags = *(uint16_t*)(m_pData + nOffset);
nOffset += 2;
pRequest->binLogDumpGTID.server_id = *(uint32_t*)(m_pData + nOffset);
nOffset += 4;
pRequest->binLogDumpGTID.binlog_filename_len = *(uint32_t*)(m_pData + nOffset);
nOffset += 4;
if (!ParseFIXString(nOffset, pRequest->binLogDumpGTID.binlog_filename_len, pRequest->binLogDumpGTID.binlog_filename))
{
return false;
}
nOffset += pRequest->binLogDumpGTID.binlog_filename_len;
pRequest->binLogDumpGTID.binlog_pos = *(uint64_t*)(m_pData + nOffset);
nOffset += 8;
if (nOffset - nStartOffset < nPayloadLen && pRequest->binLogDumpGTID.flags & BINLOG_THROUGH_GTID)
{
pRequest->binLogDumpGTID.data_size = *(uint32_t*)(m_pData + nOffset);
nOffset += 4;
if (!ParseFIXString(nOffset, pRequest->binLogDumpGTID.data_size, pRequest->binLogDumpGTID.data))
{
return false;
}
nOffset += pRequest->binLogDumpGTID.data_size;
}
break;
case MYSQL_COM_TABLE_DUMP:
pRequest->tableDump.database_len = m_pData[nOffset++];
if (!ParseFIXString(nOffset, pRequest->tableDump.database_len, pRequest->tableDump.database_name))
{
return false;
}
nOffset += pRequest->tableDump.database_len;
pRequest->tableDump.table_len = m_pData[nOffset++];
if(!ParseFIXString(nOffset, pRequest->tableDump.table_len, pRequest->tableDump.table_name))
{
return false;
}
nOffset += pRequest->tableDump.table_len;
break;
case MYSQL_COM_REGISTER_SLAVE:
pRequest->registerSlave.server_id = *(uint32_t*)(m_pData + nOffset);
nOffset += 4;
pRequest->registerSlave.hostname_len = m_pData[nOffset++];
if (!ParseFIXString(nOffset, pRequest->registerSlave.hostname_len, pRequest->registerSlave.hostname))
{
return false;
}
nOffset += pRequest->registerSlave.hostname_len;
pRequest->registerSlave.user_len = m_pData[nOffset++];
if (!ParseFIXString(nOffset, pRequest->registerSlave.user_len, pRequest->registerSlave.user))
{
return false;
}
nOffset += pRequest->registerSlave.user_len;
pRequest->registerSlave.password_len = m_pData[nOffset++];
if (!ParseFIXString(nOffset, pRequest->registerSlave.password_len, pRequest->registerSlave.password))
{
return false;
}
nOffset += pRequest->registerSlave.password_len;
pRequest->registerSlave.port = *(uint16_t*)(m_pData + nOffset);
nOffset += 2;
pRequest->registerSlave.replication_rank = *(uint32_t*)(m_pData + nOffset);
nOffset += 4;
pRequest->registerSlave.master_id = *(uint32_t*)(m_pData + nOffset);
nOffset += 4;
break;
case MYSQL_COM_STMT_PREPARE:
if (!ParseEOFString(nOffset, pRequest->stmtPrepare.query, &nLen))
{
return false;
}
nOffset += nLen;
break;
case MYSQL_COM_STMT_SEND_LONG_DATA:
pRequest->stmtSendData.statement_id = *(uint32_t*)(m_pData + nOffset);
nOffset += 4;
pRequest->stmtSendData.param_id = *(uint16_t*)(m_pData + nOffset);
nOffset += 2;
if (!ParseEOFString(nOffset, pRequest->stmtSendData.data, &nLen))
{
return false;
}
nOffset += nLen;
break;
case MYSQL_COM_STMT_CLOSE:
pRequest->stmtClose.statement_id = *(uint32_t*)(m_pData + nOffset);
nOffset += 4;
break;
case MYSQL_COM_STMT_RESET:
pRequest->stmtReset.statement_id = *(uint32_t*)(m_pData + nOffset);
nOffset += 4;
break;
case MYSQL_COM_STMT_FETCH:
pRequest->stmtFetch.statement_id = *(uint32_t*)(m_pData + nOffset);
nOffset += 4;
pRequest->stmtFetch.num_rows = *(uint32_t*)(m_pData + nOffset);
nOffset += 4;
break;
case MYSQL_COM_SET_OPTION:
pRequest->setOption.option_operation = *(uint16_t*)(m_pData + nOffset);
nOffset += 2;
break;
case MYSQL_COM_SEMI_SYNC:
pRequest->semiSync.flag = m_pData[nOffset++];
break;
default:
break;
}
return nOffset - nStartOffset == nPayloadLen;
}
bool MySQLPacket::ParseHandshakeRequestV10(uint32_t nOffset, HandshakeRequestV10* pRequest)
{
pRequest->protocol_version = m_pData[nOffset];
nOffset++;
uint32_t nLen = 0;
if (!ParseNULString(nOffset, pRequest->server_version, &nLen))
{
return false;
}
nOffset += nLen;
pRequest->connection_id = *(uint32_t*)(m_pData + nOffset);
nOffset += 4;
if (!ParseFIXString(nOffset, MYSQL_HANDSHAKE_APDP_1_LENGTH, pRequest->auth_plugin_data_part1))
{
return false;
}
nOffset += MYSQL_HANDSHAKE_APDP_1_LENGTH;
// filler
nOffset++;
pRequest->capablity_flags_lower = *(uint16_t*)(m_pData + nOffset);
nOffset += 2;
// 没有更多的数据
if (nOffset >= m_nLength)
{
return true;
}
pRequest->character_set = m_pData[nOffset++];
pRequest->status_flag = *(uint16_t*)(m_pData + nOffset);
nOffset += 2;
pRequest->capablity_flags_upper = *(uint16_t*)(m_pData + nOffset);
nOffset += 2;
pRequest->auth_plugin_data_len = m_pData[nOffset++];
pRequest->reserved.assign((char*)m_pData + nOffset, MYSQL_HANDSHAKE_RESERVED_LENGTH);
nOffset += MYSQL_HANDSHAKE_RESERVED_LENGTH;
if (pRequest->CapabilityFlagsBit(CLIENT_SECURE_CONNECTION))
{
if (pRequest->auth_plugin_data_len <= MYSQL_HANDSHAKE_APDP_2_LENGTH_MIN + MYSQL_HANDSHAKE_APDP_1_LENGTH)
{
nLen = MYSQL_HANDSHAKE_APDP_2_LENGTH_MIN;
}
else
{
nLen = pRequest->auth_plugin_data_len - 8;
}
if (!ParseFIXString(nOffset, nLen, pRequest->auth_plugin_data_part2))
{
return false;
}
nOffset += nLen;
}
if (pRequest->CapabilityFlagsBit(CLIENT_PLUGIN_AUTH))
{
if (!ParseEOFString(nOffset, pRequest->auth_plugin_name, &nLen))
{
return false;
}
}
return true;
}
bool MySQLPacket::ParseChangeUser(uint32_t& nOffset, CommandRequest* pRequest)
{
uint32_t nLen = 0;
if (!ParseNULString(nOffset, pRequest->changeUser.user, &nLen))
{
return false;
}
nOffset += nLen;
// 8.x, 5.X
pRequest->changeUser.auth_response_len = m_pData[nOffset++];
if (!ParseFIXString(nOffset, pRequest->changeUser.auth_response_len, pRequest->changeUser.auth_response))
{
return false;
}
nOffset += pRequest->changeUser.auth_response_len;
if (!ParseNULString(nOffset, pRequest->changeUser.schema_name, &nLen))
{
return false;
}
nOffset += nLen;
// 8.x
if (nOffset < m_nLength)
{
pRequest->changeUser.character_set = *(uint16_t*)(m_pData + nOffset);
nOffset += 2;
if (!ParseNULString(nOffset, pRequest->changeUser.auth_plugin_name, &nLen))
{
return false;
}
nOffset += nLen;
if (!ParseLenencInteger(nOffset, pRequest->changeUser.attr_len))
{
return false;
}
nOffset += pRequest->changeUser.attr_len.length;
uint64_t n = 0;
while (n < pRequest->changeUser.attr_len.value && nOffset < m_nLength)
{
MySQLConnectAttribute attr;
if (!ParseLenencString(nOffset, attr.key))
{
return false;
}
nOffset += attr.key.Length();
n += attr.key.Length();
if (!ParseLenencString(nOffset, attr.value))
{
return false;
}
nOffset += attr.value.Length();
n += attr.key.Length();
pRequest->changeUser.attrs.push_back(attr);
}
}
return true;
}
bool MySQLPacket::ParseHandshakeResponse41(uint32_t nOffset, uint32_t nPayloadLen, HandshakeResponse41* pResponse)
{
uint32_t nStartOffset = nOffset;
pResponse->capability_flags = *(uint32_t*)(m_pData + nOffset);
nOffset += 4;
pResponse->max_packet_size = *(uint32_t*)(m_pData + nOffset);
nOffset += 4;
pResponse->character_set = m_pData[nOffset++];
pResponse->reserved.assign((char*)m_pData + nOffset, MYSQL_HANDSHAKE_RESPONSE_41_RESERVED_LENGTH);
nOffset += MYSQL_HANDSHAKE_RESPONSE_41_RESERVED_LENGTH;
// 有的版本握手应答不包含username及之后的字段
if (nOffset >= m_nLength && (nOffset - nStartOffset) == nPayloadLen)
{
return true;
}
uint32_t nLen = 0;
if (!ParseNULString(nOffset, pResponse->username, &nLen))
{
return false;
}
nOffset += nLen;
if (pResponse->CapabilityFlagsBit(CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA))
{
if (!ParseLenencInteger(nOffset, pResponse->auth_response_len))
{
return false;
}
nOffset += pResponse->auth_response_len.length;
if (!ParseFIXString(nOffset, pResponse->auth_response_len.value, pResponse->auth_response))
{
return false;
}
nOffset += pResponse->auth_response_len.value;
}
else if (pResponse->CapabilityFlagsBit(CLIENT_SECURE_CONNECTION))
{
pResponse->auth_response_len.length = 1;
pResponse->auth_response_len.value = m_pData[nOffset];
nOffset += pResponse->auth_response_len.length;
if (!ParseFIXString(nOffset, pResponse->auth_response_len.value, pResponse->auth_response))
{
return false;
}
nOffset += pResponse->auth_response_len.value;
}
else
{
if (!ParseNULString(nOffset, pResponse->auth_response, &nLen))
{
return false;
}
nOffset += nLen;
}
if (pResponse->CapabilityFlagsBit(CLIENT_CONNECT_WITH_DB))
{
if (!ParseNULString(nOffset, pResponse->database, &nLen))
{
return false;
}
nOffset += nLen;
}
if (nOffset < m_nLength && pResponse->CapabilityFlagsBit(CLIENT_PLUGIN_AUTH))
{
if (!ParseNULString(nOffset, pResponse->auth_plugin_name, &nLen))
{
return false;
}
nOffset += nLen;
}
if (nOffset < m_nLength && pResponse->CapabilityFlagsBit(CLIENT_CONNECT_ATTRS))
{
if (!ParseLenencInteger(nOffset, pResponse->attr_len))
{
return false;
}
nOffset += pResponse->attr_len.length;
uint64_t n = 0;
while (n < pResponse->attr_len.value && nOffset < m_nLength)
{
MySQLConnectAttribute attr;
if (!ParseLenencString(nOffset, attr.key))
{
return false;
}
nOffset += attr.key.Length();
n += attr.key.Length();
if (!ParseLenencString(nOffset, attr.value))
{
return false;
}
nOffset += attr.value.Length();
n += attr.value.Length();
pResponse->attrs.push_back(attr);
}
}
return nOffset - nStartOffset == nPayloadLen;
}
bool MySQLPacket::ParseOKResponse(uint32_t nOffset, uint32_t nPayloadLen, OKResponse* pResponse)
{
uint32_t nStartOffset = nOffset;
pResponse->header = m_pData[nOffset++];
if (!ParseLenencInteger(nOffset, pResponse->affected_rows))
{
return false;
}
nOffset += pResponse->affected_rows.length;
if (!ParseLenencInteger(nOffset, pResponse->last_insert_id))
{
return false;
}
nOffset += pResponse->last_insert_id.length;
pResponse->status_flag = *(uint16_t*)(m_pData + nOffset);
nOffset += 2;
pResponse->warnings = *(uint16_t*)(m_pData + nOffset);
nOffset += 2;
/* 单包无法判断capability flag的CLIENT_SESSION_TRACK比特,没有多余的数据就停止解析 */
if (nOffset >= m_nLength && (nOffset - nStartOffset) == nPayloadLen)
{
return true;
}
if (!ParseLenencString(nOffset, pResponse->status_info))
{
return false;
}
nOffset += pResponse->status_info.Length();
// 没有Session状态变化数据
if ((nOffset >= m_nLength || !pResponse->StatusFlagBit(SERVER_SESSION_STATE_CHANGED)) && (nOffset - nStartOffset) == nPayloadLen)
{
return true;
}
if (!ParseLenencString(nOffset, pResponse->state_changes.infos_str))
{
return false;
}
nOffset += pResponse->state_changes.infos_str.length.length;
pResponse->state_changes.infos.type = m_pData[nOffset++];
if(!ParseLenencString(nOffset, pResponse->state_changes.infos.data_str))
{
return false;
}
nOffset += pResponse->state_changes.infos.data_str.length.length;
switch (pResponse->state_changes.infos.type)
{
case SESSION_TRACK_SYSTEM_VARIABLES:
if (!ParseLenencString(nOffset, pResponse->state_changes.infos.data.track_sys_var.name))
{
return false;
}
nOffset += pResponse->state_changes.infos.data.track_sys_var.name.Length();
if (!ParseLenencString(nOffset, pResponse->state_changes.infos.data.track_sys_var.value))
{
return false;
}
break;
case SESSION_TRACK_SCHEMA:
if (!ParseLenencString(nOffset, pResponse->state_changes.infos.data.track_schema.name))
{
return false;
}
break;
case SESSION_TRACK_STATE_CHANGE:
if (!ParseLenencString(nOffset, pResponse->state_changes.infos.data.track_state_change.is_tracked))
{
return false;
}
break;
case SESSION_TRACK_GTIDS:
if (!ParseLenencString(nOffset, pResponse->state_changes.infos.data.track_gtids.grids))
{
return false;
}
break;
default:
break;
}
return (nOffset - nStartOffset) == nPayloadLen;
}
bool MySQLPacket::ParseErrorResponse(uint32_t nOffset, ERRResponse* pResponse)
{
pResponse->header = m_pData[nOffset++];
pResponse->error_code = *(uint16_t*)(m_pData + nOffset);
nOffset += 2;
pResponse->sql_state_marker = m_pData[nOffset++];
pResponse->sql_state.assign((char*)m_pData + nOffset, MYSQL_ERROR_PACKET_SQL_STATE_LEN);
uint32_t nLen = 0;
return ParseEOFString(nOffset, pResponse->error_msg, &nLen);
}
bool MySQLPacket::ParseEOFResponse(uint32_t nOffset, uint32_t nPayloadLen, EOFResponse* pResponse)
{
pResponse->header = m_pData[nOffset++];
if (nOffset + 2 <= m_nLength)
{
pResponse->warnings = *(uint16_t*)(m_pData + nOffset);
nOffset += 2;
}
if (nOffset + 2 <= m_nLength)
{
pResponse->status_flags = *(uint16_t*)(m_pData + nOffset);
nOffset += 2;
}
return nOffset <= m_nLength;
}
bool MySQLPacket::ParseColumnCount(uint32_t nOffset, ColumnCount* pCount)
{
if (!ParseLenencInteger(nOffset, pCount->column_count))
{
return false;
}
return true;
}
bool MySQLPacket::ParseTextResultRow(uint32_t nOffset, uint32_t nPayloadLen, TextResultRow* pRow)
{
if (m_pData[nOffset] == MYSQL_TEXT_RESULT_RAW)
{
pRow->null = true;
return true;
}
uint32_t nTmp = 0;
uint64_t nLen = 0;
pRow->null = false;
while (nTmp < nPayloadLen)
{
LengthEncodedString text;
if (!ParseLenencString(nOffset, text))
{
return false;
}
pRow->texts.push_back(text);
nLen = text.Length();
nTmp += nLen;
nOffset += nLen;
}
return nTmp == nPayloadLen;
}
bool MySQLPacket::ParseColumnDefinition41(uint32_t nOffset, uint32_t nPayloadLen, ColumnDefinition41* pColumn)
{
uint32_t nStartOffset = nOffset;
uint64_t nLen = 0;
std::vector<LengthEncodedString*> strs;
strs.push_back(&pColumn->catalog);
strs.push_back(&pColumn->schema);
strs.push_back(&pColumn->table);
strs.push_back(&pColumn->org_table);
strs.push_back(&pColumn->name);
strs.push_back(&pColumn->org_name);
for (auto& iter : strs)
{
if (!ParseLenencString(nOffset, *iter))
{
return false;
}
nLen = iter->Length();
nOffset += nLen;
if (nOffset - nStartOffset == nPayloadLen)
{
return true;
}
}
if (!ParseLenencInteger(nOffset, pColumn->fixed_fields_len))
{
return false;
}
nOffset += pColumn->fixed_fields_len.length;
pColumn->character_set = *(uint16_t*)(m_pData + nOffset);
nOffset += 2;
pColumn->column_len = *(uint32_t*)(m_pData + nOffset);
nOffset += 4;
pColumn->type = m_pData[nOffset++];
pColumn->flags = *(uint16_t*)(m_pData + nOffset);
nOffset += 2;
pColumn->decimals = m_pData[nOffset++];
pColumn->filler = *(uint16_t*)(m_pData + nOffset);
nOffset += 2;
if (nOffset - nStartOffset < nPayloadLen)
{
// 当表字段的默认值为NULL时使用0xfb表示
if (m_pData[nOffset] == 0xfb)
{
pColumn->default_values_len.value = 4;
pColumn->default_values.assign("NULL");
return true;
}
if (!ParseLenencInteger(nOffset, pColumn->default_values_len))
{
return false;
}
nOffset += pColumn->default_values_len.length;
if (!ParseFIXString(nOffset, pColumn->default_values_len.value, pColumn->default_values))
{
return false;
}
}
return nOffset - nStartOffset == nPayloadLen;
}
bool MySQLPacket::ParseLenencInteger(uint32_t nOffset, LengthEncodedInteger& data)
{
data.prefix = 0;
data.length = 0;
switch (m_pData[nOffset])
{
case 0xfc:
data.prefix = m_pData[nOffset++];
data.length = 3;
data.value = *(uint16_t*)(m_pData + nOffset);
break;
case 0xfd:
data.prefix = m_pData[nOffset++];
data.length = 4;
data.value = *(uint32_t*)(m_pData + nOffset);
data.value &= 0x00ffffff;
break;
case 0xfe:
data.prefix = m_pData[nOffset++];
data.length = 9;
data.value = *(uint64_t*)(m_pData + nOffset);
break;
default:
if (m_pData[nOffset] < 251)
{
data.length = 1;
data.value = m_pData[nOffset];
}
break;
}
nOffset += data.length;
return data.length > 0 && nOffset <= m_nLength;
}
bool MySQLPacket::ParseNULString(uint32_t nOffset, NULString& data, uint32_t* pDataLen)
{
uint32_t nStart = nOffset;
while (m_pData[nOffset] && nOffset < m_nLength)
{
nOffset++;
}
nOffset++; //0x00
*pDataLen = nOffset - nStart;
if (nOffset <= m_nLength)
{
data.assign((char*)m_pData + nStart, *pDataLen);
}
return nOffset <= m_nLength;
}
bool MySQLPacket::ParseEOFString(uint32_t nOffset, EOFString& data, uint32_t* pDataLen)
{
if (nOffset < m_nLength)
{
*pDataLen = m_nLength - nOffset;
data.assign((char*)m_pData + nOffset, *pDataLen);
}
return nOffset <= m_nLength;
}
bool MySQLPacket::ParseFIXString(uint32_t nOffset, uint32_t nLen, FIXString& data)
{
data.assign((char*)m_pData + nOffset, nLen);
return nOffset + nLen <= m_nLength;
}
bool MySQLPacket::ParseLenencString(uint32_t nOffset, LengthEncodedString& data)
{
if (!ParseLenencInteger(nOffset, data.length))
{
return false;
}
nOffset += data.length.length;
if (!ParseFIXString(nOffset, data.length.value, data.value))
{
return false;
}
nOffset += data.length.value;
return nOffset <= m_nLength;
}
? ? ? ??
|