MySQL analyzer

This commit is contained in:
Vlad Grigorescu 2014-07-24 15:52:42 -04:00
parent 3cea6ab1eb
commit 101d340b18
11 changed files with 714 additions and 0 deletions

View file

@ -0,0 +1,348 @@
###
#
# All information is from the MySQL internals documentation at:
# <http://dev.mysql.com/doc/internals/en/connection-phase.html>
#
###
type uint24le = record {
byte3 : uint8;
byte2 : uint8;
byte1 : uint8;
};
type LengthEncodedInteger = record {
i1: uint8;
val: case i1 of {
0xfb -> i0: empty;
0xfc -> i2: uint16;
0xfd -> i3: uint24le;
0xfe -> i4: uint64;
0xff -> err_packet: empty;
default -> one: empty;
};
};
type LengthEncodedString = record {
len: LengthEncodedInteger;
val: bytestring &length=to_int()(len);
};
%header{
class to_int
{
public:
int operator()(uint24le * num) const
{
return (num->byte1() << 16) | (num->byte2() << 8) | num->byte3();
}
int operator()(LengthEncodedInteger* lei) const
{
if ( lei->i1() < 0xfb )
return lei->i1();
else if ( lei->i1() == 0xfc )
return lei->i2();
else if ( lei->i1() == 0xfd )
return to_int()(lei->i3());
else if ( lei->i1() == 0xfe )
return lei->i4();
else
return 0;
}
};
%}
extern type to_int;
enum command_consts {
COM_SLEEP = 0x00,
COM_QUIT = 0x01,
COM_INIT_DB = 0x02,
COM_QUERY = 0x03,
COM_FIELD_LIST = 0x04,
COM_CREATE_DB = 0x05,
COM_DROP_DB = 0x06,
COM_REFRESH = 0x07,
COM_SHUTDOWN = 0x08,
COM_STATISTICS = 0x09,
COM_PROCESS_INFO = 0x0a,
COM_CONNECT = 0x0b,
COM_PROCESS_KILL = 0x0c,
COM_DEBUG = 0x0d,
COM_PING = 0x0e,
COM_TIME = 0x0f,
COM_DELAYED_INSERT = 0x10,
COM_CHANGE_USER = 0x11,
COM_BINLOG_DUMP = 0x12,
COM_TABLE_DUMP = 0x13,
COM_CONNECT_OUT = 0x14,
COM_REGISTER_SLAVE = 0x15,
COM_STMT_PREPARE = 0x16,
COM_STMT_EXECUTE = 0x17,
COM_STMT_SEND_LONG_DATA = 0x18,
COM_STMT_CLOSE = 0x19,
COM_STMT_RESET = 0x1a,
COM_SET_OPTION = 0x1b,
COM_STMT_FETCH = 0x1c,
COM_DAEMON = 0x1d,
COM_BINLOG_DUMP_GTID = 0x1e
};
enum state {
CONNECTION_PHASE = 0,
COMMAND_PHASE = 1,
};
enum Expected {
NO_EXPECTATION,
EXPECT_STATUS,
EXPECT_COLUMN_DEFINITION,
EXPECT_COLUMN_COUNT,
EXPECT_EOF1,
EXPECT_EOF2,
EXPECT_RESULTSET,
};
type NUL_String = RE/[^\0]*/;
type Header = record {
le_len: uint24le;
seq_id: uint8;
} &let {
len: uint32 = to_int()(le_len) + 4;
} &length=4;
type MySQL_PDU(is_orig: bool) = record {
hdr: Header;
# todo: bytestring &length=56;
msg: case is_orig of {
false -> server_msg: Server_Message(hdr.seq_id);
true -> client_msg: Client_Message(state);
} &requires(state);
# In case there is trash left over from not parsing something completely.
#blah: bytestring &restofdata;
} &let {
state = $context.connection.get_state();
} &length=hdr.len &byteorder=bigendian;
type Client_Message(state: int) = case state of {
CONNECTION_PHASE -> connection_phase: Handshake_Response_Packet;
COMMAND_PHASE -> command_phase: Command_Request_Packet;
};
type Server_Message(seq_id: uint8) = case seq_id of {
0 -> initial_handshake: Initial_Handshake_Packet;
default -> command_response: Command_Response;
};
type Initial_Handshake_Packet = record {
protocol_version: uint8;
pkt: case protocol_version of {
10 -> handshake10 : Handshake_v10;
9 -> handshake9 : Handshake_v9;
default -> error : ERR_Packet;
};
};
type Handshake_v10 = record {
server_version : NUL_String;
connection_id : uint32;
auth_plugin_data_part_1 : bytestring &length=8;
filler_1 : uint8;
capability_flag_1 : uint16;
character_set : uint8;
status_flags : uint16;
capability_flags_2 : uint16;
auth_plugin_data_len : uint8;
auth_plugin_name : NUL_String;
};
type Handshake_v9 = record {
todo: bytestring &restofdata;
};
type Handshake_Response_Packet = record {
cap_flags : uint32;
max_pkt_size : uint32;
char_set : uint8;
pad : padding[23];
username : NUL_String;
password : bytestring &restofdata;
} &byteorder=bigendian;
type Command_Request_Packet = record {
command: uint8;
arg: bytestring &restofdata;
} &let {
update_expectation: bool = $context.connection.set_next_expected(EXPECT_COLUMN_COUNT);
};
type Command_Response = case $context.connection.get_expectation() of {
EXPECT_COLUMN_COUNT -> col_count : ColumnCount;
EXPECT_COLUMN_DEFINITION -> col_defs : ColumnDefinitions;
EXPECT_RESULTSET -> resultset : Resultset;
# EXPECT_RESULTSETROW -> resultsetrow : ResultsetRow;
EXPECT_STATUS -> status : Command_Response_Status;
EXPECT_EOF1 -> eof1 : EOF1;
EXPECT_EOF2 -> eof2 : EOF2;
default -> unknown : empty;
};
type Command_Response_Status = record {
pkt_type: uint8;
response: case pkt_type of {
0x00 -> data_ok: OK_Packet;
0xfe -> data_eof: EOF_Packet;
0xff -> data_err: ERR_Packet;
default -> unknown: empty;
};
};
type ColumnCount = record {
le_column_count : LengthEncodedInteger;
} &let {
col_num: uint32 = to_int()(le_column_count);
update_col_num: bool = $context.connection.set_col_count(col_num);
update_expectation: bool = $context.connection.set_next_expected(EXPECT_COLUMN_DEFINITION);
};
type ColumnDefinitions = record {
# defs: ColumnDefinition41[$context.connection.get_col_count()];
defs: ColumnDefinition41[1];
} &let {
update_expectation: bool = $context.connection.set_next_expected(EXPECT_EOF1);
};
type EOF1 = record {
eof : EOF_Packet;
} &let {
update_expectation: bool = $context.connection.set_next_expected(EXPECT_RESULTSET);
};
type EOF2 = record {
eof : EOF_Packet;
} &let {
update_expectation: bool = $context.connection.set_next_expected(NO_EXPECTATION);
};
type Resultset = record {
rows : ResultsetRow[] &until($input.length()==0);
} &let {
update_expectation: bool = $context.connection.set_next_expected(EXPECT_EOF2);
};
type ResultsetRow = record {
fields: LengthEncodedString[$context.connection.get_col_count()];
};
type ColumnDefinition41 = record {
catalog: LengthEncodedString;
# todo: bytestring &length=2;
schema: LengthEncodedString;
table: LengthEncodedString;
org_table: LengthEncodedString;
name: LengthEncodedString;
org_name: LengthEncodedString;
next_len: LengthEncodedInteger;
char_set: uint16;
col_len: uint32;
type: uint8;
flags: uint16;
decimals: uint8;
filler: padding[2];
#if command was COM_FIELD_LIST {
# lenenc_int length of default-values
# string[$len] default values
#}
};
type ColumnDefinition320 = record {
table: LengthEncodedString;
name: LengthEncodedString;
length_of_col_len: LengthEncodedInteger;
col_len: uint24le;
type_len: LengthEncodedInteger;
type: uint8;
#if capabilities & CLIENT_LONG_FLAG {
#lenenc_int [03] length of flags+decimals fields
#2 flags
#1 decimals
# } else {
#1 [02] length of flags+decimals fields
#1 flags
#1 decimals
# }
# if command was COM_FIELD_LIST {
#lenenc_int length of default-values
#string[$len] default values
# }
};
type OK_Packet = record {
le_rows: LengthEncodedInteger;
todo: bytestring &restofdata;
} &let {
rows: uint32 = to_int()(le_rows);
update_state: bool = $context.connection.update_state(COMMAND_PHASE);
};
type ERR_Packet = record {
code: uint16;
state: bytestring &length=6;
msg: bytestring &restofdata;
};
type EOF_Packet = record {
warnings: uint16;
status : uint16;
};
refine connection MySQL_Conn += {
%member{
int state_;
Expected expected_;
uint32 col_count_;
%}
%init{
state_ = CONNECTION_PHASE;
expected_ = EXPECT_STATUS;
col_count_ = 0;
%}
function get_state(): int
%{
return state_;
%}
function update_state(s: state): bool
%{
state_ = s;
return true;
%}
function get_expectation(): Expected
%{
return expected_;
%}
function set_next_expected(e: Expected): bool
%{
expected_ = e;
return true;
%}
function get_col_count(): uint32
%{
return col_count_;
%}
function set_col_count(i: uint32): bool
%{
col_count_ = i;
return true;
%}
};