131 lines
3.5 KiB
Go
131 lines
3.5 KiB
Go
package mysql
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
)
|
|
|
|
// parseHandshakeResponseHeader parses the common header of SSLRequest and HandshakeResponse41.
|
|
func parseHandshakeResponseHeader(packet *handshakeResponse41, data []byte) (parsedBytes int, err error) {
|
|
// Ensure there are enough data to read:
|
|
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest
|
|
if len(data) < 4+4+1+23 {
|
|
return 0, ErrMalformPacket
|
|
}
|
|
|
|
offset := 0
|
|
// capability
|
|
capability := binary.LittleEndian.Uint32(data[:4])
|
|
packet.Capability = capability
|
|
offset += 4
|
|
// skip max packet size
|
|
offset += 4
|
|
// charset, skip, if you want to use another charset, use set names
|
|
packet.Collation = data[offset]
|
|
offset++
|
|
// skip reserved 23[00]
|
|
offset += 23
|
|
|
|
return offset, nil
|
|
}
|
|
|
|
// parseHandshakeResponseBody parse the HandshakeResponse (except the common header part).
|
|
func parseHandshakeResponseBody(packet *handshakeResponse41, data []byte, offset int) (err error) {
|
|
defer func() {
|
|
// Check malformat packet cause out of range is disgusting, but don't panic!
|
|
if r := recover(); r != nil {
|
|
err = ErrMalformPacket
|
|
}
|
|
}()
|
|
// user name
|
|
packet.User = string(data[offset : offset+bytes.IndexByte(data[offset:], 0)])
|
|
offset += len(packet.User) + 1
|
|
|
|
if packet.Capability&ClientPluginAuthLenencClientData > 0 {
|
|
// MySQL client sets the wrong capability, it will set this bit even server doesn't
|
|
// support ClientPluginAuthLenencClientData.
|
|
// https://github.com/mysql/mysql-server/blob/5.7/sql-common/client.c#L3478
|
|
num, null, off := parseLengthEncodedInt(data[offset:])
|
|
offset += off
|
|
if !null {
|
|
packet.Auth = data[offset : offset+int(num)]
|
|
offset += int(num)
|
|
}
|
|
} else if packet.Capability&ClientSecureConnection > 0 {
|
|
// auth length and auth
|
|
authLen := int(data[offset])
|
|
offset++
|
|
packet.Auth = data[offset : offset+authLen]
|
|
offset += authLen
|
|
} else {
|
|
packet.Auth = data[offset : offset+bytes.IndexByte(data[offset:], 0)]
|
|
offset += len(packet.Auth) + 1
|
|
}
|
|
|
|
if packet.Capability&ClientConnectWithDB > 0 {
|
|
if len(data[offset:]) > 0 {
|
|
idx := bytes.IndexByte(data[offset:], 0)
|
|
packet.DBName = string(data[offset : offset+idx])
|
|
offset = offset + idx + 1
|
|
}
|
|
}
|
|
|
|
if packet.Capability&ClientPluginAuth > 0 {
|
|
// TODO: Support mysql.ClientPluginAuth, skip it now
|
|
idx := bytes.IndexByte(data[offset:], 0)
|
|
offset = offset + idx + 1
|
|
}
|
|
|
|
if packet.Capability&ClientConnectAtts > 0 {
|
|
if len(data[offset:]) == 0 {
|
|
// Defend some ill-formated packet, connection attribute is not important and can be ignored.
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func parseLengthEncodedInt(b []byte) (num uint64, isNull bool, n int) {
|
|
switch b[0] {
|
|
// 251: NULL
|
|
case 0xfb:
|
|
n = 1
|
|
isNull = true
|
|
return
|
|
|
|
// 252: value of following 2
|
|
case 0xfc:
|
|
num = uint64(b[1]) | uint64(b[2])<<8
|
|
n = 3
|
|
return
|
|
|
|
// 253: value of following 3
|
|
case 0xfd:
|
|
num = uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16
|
|
n = 4
|
|
return
|
|
|
|
// 254: value of following 8
|
|
case 0xfe:
|
|
num = uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16 |
|
|
uint64(b[4])<<24 | uint64(b[5])<<32 | uint64(b[6])<<40 |
|
|
uint64(b[7])<<48 | uint64(b[8])<<56
|
|
n = 9
|
|
return
|
|
}
|
|
|
|
// 0-250: value of first byte
|
|
num = uint64(b[0])
|
|
n = 1
|
|
return
|
|
}
|
|
|
|
func extractMysqlPayloadSize(payload []byte) int {
|
|
header := payload[:4]
|
|
return int(uint32(header[0]) | uint32(header[1])<<8 | uint32(header[2])<<16)
|
|
}
|
|
|
|
func bytesToInt(contents []byte) int {
|
|
return int(uint32(contents[0]) | uint32(contents[1])<<8 | uint32(contents[2])<<16 | uint32(contents[3])<<24)
|
|
} |