~jan-kneschke/mysql-proxy/packet-tracking-assertions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
--[[ $%BEGINLICENSE%$
 Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved.

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as
 published by the Free Software Foundation; version 2 of the
 License.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 02110-1301  USA

 $%ENDLICENSE%$ --]]

module("proxy.commands", package.seeall)

---
-- map the constants to strings 
-- lua starts at 1
local command_names = {
	"COM_SLEEP",
	"COM_QUIT",
	"COM_INIT_DB",
	"COM_QUERY",
	"COM_FIELD_LIST",
	"COM_CREATE_DB",
	"COM_DROP_DB",
	"COM_REFRESH",
	"COM_SHUTDOWN",
	"COM_STATISTICS",
	"COM_PROCESS_INFO",
	"COM_CONNECT",
	"COM_PROCESS_KILL",
	"COM_DEBUG",
	"COM_PING",
	"COM_TIME",
	"COM_DELAYED_INSERT",
	"COM_CHANGE_USER",
	"COM_BINLOG_DUMP",
	"COM_TABLE_DUMP",
	"COM_CONNECT_OUT",
	"COM_REGISTER_SLAVE",
	"COM_STMT_PREPARE",
	"COM_STMT_EXECUTE",
	"COM_STMT_SEND_LONG_DATA",
	"COM_STMT_CLOSE",
	"COM_STMT_RESET",
	"COM_SET_OPTION",
	"COM_STMT_FETCH",
	"COM_DAEMON"
}

---
-- split a MySQL command packet into its parts
--
-- @param packet a network packet
-- @return a table with .type, .type_name and command specific fields 
function parse(packet)
	local cmd = {}

	cmd.type = packet:byte()
	cmd.type_name = command_names[cmd.type + 1]
	
	if cmd.type == proxy.COM_QUERY then
		cmd.query = packet:sub(2)
	elseif cmd.type == proxy.COM_QUIT or
	       cmd.type == proxy.COM_PING or
	       cmd.type == proxy.COM_SHUTDOWN then
		-- nothing to decode
	elseif cmd.type == proxy.COM_STMT_PREPARE then
		cmd.query = packet:sub(2)
	-- the stmt_handler_id is at the same position for both STMT_EXECUTE and STMT_CLOSE
	elseif cmd.type == proxy.COM_STMT_EXECUTE or cmd.type == proxy.COM_STMT_CLOSE then
		cmd.stmt_handler_id = string.byte(packet, 2) + (string.byte(packet, 3) * 256) + (string.byte(packet, 4) * 256 * 256) + (string.byte(packet, 5) * 256 * 256 * 256)
	elseif cmd.type == proxy.COM_FIELD_LIST then
		cmd.table = packet:sub(2)
	elseif cmd.type == proxy.COM_INIT_DB or
	       cmd.type == proxy.COM_CREATE_DB or
	       cmd.type == proxy.COM_DROP_DB then
		cmd.schema = packet:sub(2)
	elseif cmd.type == proxy.COM_SET_OPTION then
		cmd.option = packet:sub(2)
	else
		print("[debug] (command) unhandled type name:" .. tostring(cmd.type_name) .. " byte:" .. tostring(cmd.type))
	end

	return cmd
end

function pretty_print(cmd)
	if cmd.type == proxy.COM_QUERY or
	   cmd.type == proxy.COM_STMT_PREPARE then
		return ("[%s] %s"):format(cmd.type_name, cmd.query)
	elseif cmd.type == proxy.COM_INIT_DB then
		return ("[%s] %s"):format(cmd.type_name, cmd.schema)
	elseif cmd.type == proxy.COM_QUIT or
	       cmd.type == proxy.COM_PING or
	       cmd.type == proxy.COM_SHUTDOWN then
		return ("[%s]"):format(cmd.type_name)
	elseif cmd.type == proxy.COM_FIELD_LIST then
		-- should have a table-name
		return ("[%s]"):format(cmd.type_name)
	elseif cmd.type == proxy.COM_STMT_EXECUTE then
		return ("[%s] %s"):format(cmd.type_name, cmd.stmt_handler_id)
	end

	return ("[%s] ... no idea"):format(cmd.type_name)
end