Add output conversions; add debug tags to less-useful fields (not currently consumed)

This commit is contained in:
Justin Bastress 2017-12-07 15:57:40 -05:00
parent d561fcdba7
commit b9fdd24b1f

@ -20,6 +20,7 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"math"
"net"
"strings"
"time"
@ -109,6 +110,72 @@ type Config struct {
ReservedData []byte
}
// flagsToList() converts an integer flags variable to a list of consts corresponding to each bit.
// The [i]th entry of consts corresponds to the [i]th bit in flags.
// Example: flagsToList(0x11, { "a", "b", "c", "d", "e" }) returns { "a", "e" }.
func flagsToList(flags uint64, consts []string) (ret []string) {
for i := range consts {
v := uint64(math.Pow(2, float64(i)))
if uint64(flags)&v == v {
ret = append(ret, consts[i])
}
}
return ret
}
// Get the constants corresponding to the given server status flags
func getServerStatusFlags(flags uint16) []string {
consts := []string{
"SERVER_STATUS_IN_TRANS",
"SERVER_STATUS_AUTOCOMMIT",
"SERVER_MORE_RESULTS_EXISTS",
"SERVER_QUERY_NO_GOOD_INDEX_USED",
"SERVER_QUERY_NO_INDEX_USED",
"SERVER_STATUS_CURSOR_EXISTS",
"SERVER_STATUS_LAST_ROW_SENT",
"SERVER_STATUS_DB_DROPPED",
"SERVER_STATUS_NO_BACKSLASH_ESCAPES",
"SERVER_STATUS_METADATA_CHANGED",
"SERVER_QUERY_WAS_SLOW",
"SERVER_PS_OUT_PARAMS",
"SERVER_STATUS_IN_TRANS_READONLY",
"SERVER_SESSION_STATE_CHANGED",
}
return flagsToList(uint64(flags), consts)
}
// Get the constants corresponding to th egiven client capability flags
func getClientCapabilityFlags(flags uint32) []string {
consts := []string{
"CLIENT_LONG_PASSWORD",
"CLIENT_FOUND_ROWS",
"CLIENT_LONG_FLAG",
"CLIENT_CONNECT_WITH_DB",
"CLIENT_NO_SCHEMA",
"CLIENT_COMPRESS",
"CLIENT_ODBC",
"CLIENT_LOCAL_FILES",
"CLIENT_IGNORE_SPACE",
"CLIENT_PROTOCOL_41",
"CLIENT_INTERACTIVE",
"CLIENT_SSL",
"CLIENT_IGNORE_SIGPIPE",
"CLIENT_TRANSACTIONS",
"CLIENT_RESERVED",
"CLIENT_SECURE_CONNECTION",
"CLIENT_MULTI_STATEMENTS",
"CLIENT_MULTI_RESULTS",
"CLIENT_PS_MULTI_RESULTS",
"CLIENT_PLUGIN_AUTH",
"CLIENT_CONNECT_ATTRS",
"CLIENT_PLUGIN_AUTH_LEN_ENC_CLIENT_DATA",
"CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS",
"CLIENT_SESSION_TRACK",
"CLIENT_DEPRECATED_EOF",
}
return flagsToList(uint64(flags), consts)
}
// Fill in a (possibly newly-created) Config instance with the default values
func InitConfig(base *Config) *Config {
if base == nil {
@ -183,9 +250,9 @@ type WritablePacket interface {
// Entry in the ConnectionLog. Raw is the base64-encoded body, Parsed is the parsed packet.
// Either may be nil if there was an error reading/decoding the packet.
type ConnectionLogEntry struct {
Length uint32 `json:"length"`
SequenceNumber uint8 `json:"sequence_number"`
Raw string `json:"raw"`
Length uint32 `zgrab:"debug" json:"length"`
SequenceNumber uint8 `zgrab:"debug" json:"sequence_number"`
Raw string `zgrab:"debug" json:"raw"`
Parsed PacketInfo `json:"parsed,omitempty"`
}
@ -198,31 +265,31 @@ type HandshakePacket struct {
// server_version: string<NUL>
ServerVersion string `json:"server_version"`
// connection_id: int<4>
ConnectionID uint32 `json:"connection_id"` // [4]
ConnectionID uint32 `zgrab:"debug" json:"connection_id"` // [4]
// auth_plugin_data_part_1: string<8>
AuthPluginData1 string `json:"auth_plugin_data_part_1"`
AuthPluginData1 string `zgrab:"debug" json:"auth_plugin_data_part_1"`
// fillter_1: byte<1>
Filler1 byte `json:"filler_1,omitempty"`
Filler1 byte `zgrab:"debug" json:"filler_1,omitempty"`
// capability_flag_1: int<2> -- Stored as lower 16 bits of capability_flags
// character_set: int<1> (optional? 0 is a valid value, so omitempty doesn't seem like an option)
CharacterSet byte `json:"character_set"`
CharacterSet byte `zgrab:"debug" json:"character_set"`
// Synthetic field: if true, none of the following fields are present.
ShortHandshake bool `json:"short_handshake"`
ShortHandshake bool `zgrab:"debug" json:"short_handshake"`
// status_flags: int<> (optional? doing omitempty since no flags would be interpreted as all 0s)
StatusFlags uint16 `json:"status_flags,omitempty"`
// capability_flag_2: int<2> -- Stored as upper 16 bits of capability_flags
// auth_plugin_data_len: int<1>
AuthPluginDataLen byte `json:"auth_plugin_data_len"`
AuthPluginDataLen byte `zgrab:"debug" json:"auth_plugin_data_len"`
// if (capabilities & CLIENT_SECURE_CONNECTION) {
// reserved: string<10> all 0
Reserved []byte `json:"reserved,omitempty"`
Reserved []byte `zgrab:"debug" json:"reserved,omitempty"`
// auth_plugin_data_part_2: string<MAX(13, auth_plugin_data_len - 8)>
AuthPluginData2 string `json:"auth_plugin_data_part_2,omitempty"`
AuthPluginData2 string `zgrab:"debug" json:"auth_plugin_data_part_2,omitempty"`
// auth_plugin_name: string<NUL>, but old versions lacked null terminator, so returning string<EOF>
AuthPluginName string `json:"auth_plugin_name,omitempty"`
AuthPluginName string `zgrab:"debug" json:"auth_plugin_name,omitempty"`
// }
// Synthetic field built from capability_flags_1 || capability_flags_2 << 16
CapabilityFlags uint32 `json:"capability_flags"`
@ -237,10 +304,14 @@ func (p *HandshakePacket) MarshalJSON() ([]byte, error) {
// Hack around infinite MarshalJSON loop by aliasing parent type (http://choly.ca/post/go-json-marshalling/)
type Alias HandshakePacket
return json.Marshal(&struct {
ReservedOmitted []byte `json:"reserved,omitempty"`
ReservedOmitted []byte `zgrab:"debug", json:"reserved,omitempty"`
CapabilityFlags []string `json:"capability_flags"`
StatusFlags []string `json:"status_flags"`
*Alias
}{
ReservedOmitted: reserved,
CapabilityFlags: getClientCapabilityFlags(p.CapabilityFlags),
StatusFlags: getServerStatusFlags(p.StatusFlags),
Alias: (*Alias)(p),
})
}
@ -282,17 +353,15 @@ func (c *Connection) readHandshakePacket(body []byte) (*HandshakePacket, error)
type OKPacket struct {
// header: 0xfe or 0x00
Header byte `json:"header"`
Header byte `zgrab:"debug" json:"header"`
// affected_rows: int<lenenc>
AffectedRows uint64 `json:"affected_rows"`
AffectedRows uint64 `zgrab:"debug" json:"affected_rows"`
// last_insert_rowid: int<lenenc>
LastInsertId uint64 `json:"last_insert_id"`
// if (CLIENT_PROTOCOL_41 || CLIENT_TRANSACTIONS) {
// status_flags: int<2>
StatusFlags uint16 `json:"status_flags,omitempty"`
// if CLIENT_PROTOCOL_41 {
// warning_flags: int<2>
WarningFlags uint16 `json:"warning_flags,omitempty"`
// warnings: int<2>
Warnings uint16 `json:"warnings,omitempty"`
// }
@ -301,10 +370,23 @@ type OKPacket struct {
Info string `json:"info,omitempty"`
// if CLIENT_SESSION_TRACK && status_flags && SERVER_SESSION_STATE_CHANGED {
// session_state_changes: string<lenenc>
SessionStateChanges string `json:"session_state_changes,omitempty"`
SessionStateChanges string `zgrab:"debug" json:"session_state_changes,omitempty"`
// }
}
// Convert the StatusFlags to an array of consts
func (p *OKPacket) MarshalJSON() ([]byte, error) {
// Hack around infinite MarshalJSON loop by aliasing parent type (http://choly.ca/post/go-json-marshalling/)
type Alias OKPacket
return json.Marshal(&struct {
StatusFlags []string `json:"status_flags"`
*Alias
}{
StatusFlags: getServerStatusFlags(p.StatusFlags),
Alias: (*Alias)(p),
})
}
func (c *Connection) readOKPacket(body []byte) (*OKPacket, error) {
var rest []byte
var err error
@ -329,10 +411,9 @@ func (c *Connection) readOKPacket(body []byte) (*OKPacket, error) {
ret.StatusFlags = binary.LittleEndian.Uint16(rest[0:2])
rest = rest[2:]
if flags&CLIENT_PROTOCOL_41 != 0 {
log.Debugf("readOKPacket: CapabilityFlags = 0x%x, so reading WarningFlags / Warnings")
ret.WarningFlags = binary.LittleEndian.Uint16(rest[0:2])
ret.Warnings = binary.LittleEndian.Uint16(rest[2:4])
rest = rest[4:]
log.Debugf("readOKPacket: CapabilityFlags = 0x%x, so reading Warnings")
ret.Warnings = binary.LittleEndian.Uint16(rest[0:2])
rest = rest[2:]
}
}
ret.Info, rest, err = readLenString(rest[:])
@ -356,14 +437,14 @@ func (c *Connection) readOKPacket(body []byte) (*OKPacket, error) {
// ERRPacket defined at https://web.archive.org/web/20160316124241/https://dev.mysql.com/doc/internals/en/packet-ERRPacket.html
type ERRPacket struct {
// header: int<1>
Header byte `json:"header"`
Header byte `zgrab:"debug" json:"header"`
// error_code: int<2>
ErrorCode uint16 `json:"error_code"`
// if CLIENT_PROTOCOL_41 {
// sql_state_marker string<1>
SQLStateMarker string `json:"sql_state_marker,omitempty"`
SQLStateMarker string `zgrab:"debug" json:"sql_state_marker,omitempty"`
// sql_state string<5>
SQLState string `json:"sql_state,omitempty"`
SQLState string `zgrab:"debug" json:"sql_state,omitempty"`
// }
// error_messagestring<eof> (in the packet encoding, an absent value and an empty string have the same encoding)
ErrorMessage string `json:"error_message,omitempty"`
@ -394,11 +475,30 @@ type SSLRequestPacket struct {
// capability_flags int<4>: Would be weird to not set CLIENT_SSL (0x0800) in your SSLRequest packet
CapabilityFlags uint32 `json:"capability_flags"`
// max_packet_size int<4>
MaxPacketSize uint32 `json:"max_packet_size"`
MaxPacketSize uint32 `zgrab:"debug" json:"max_packet_size"`
// character_set int<1>
CharacterSet byte `json:"character_set"`
CharacterSet byte `zgrab:"debug" json:"character_set"`
// reserved string<23>: all \x00
Reserved []byte `json:"reserved,omitempty"`
Reserved []byte `zgrab:"debug" json:"reserved,omitempty"`
}
// Omit reserved from encoded packet if it is the default value (ten bytes of 0s)
func (p *SSLRequestPacket) MarshalJSON() ([]byte, error) {
reserved := p.Reserved
if base64.StdEncoding.EncodeToString(reserved) == "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" {
reserved = []byte{}
}
// Hack around infinite MarshalJSON loop by aliasing parent type (http://choly.ca/post/go-json-marshalling/)
type Alias SSLRequestPacket
return json.Marshal(&struct {
ReservedOmitted []byte `zgrab:"debug", json:"reserved,omitempty"`
CapabilityFlags []string `json:"capability_flags"`
*Alias
}{
ReservedOmitted: reserved,
CapabilityFlags: getClientCapabilityFlags(p.CapabilityFlags),
Alias: (*Alias)(p),
})
}
func (p *SSLRequestPacket) EncodeBody() []byte {