56 lines
1.4 KiB
Go
56 lines
1.4 KiB
Go
package protocol
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
)
|
|
|
|
func init() {
|
|
Register(Server, "\x0a", detectMySQL)
|
|
}
|
|
|
|
func detectMySQL(dir Direction, data []byte, srcPort, dstPort int) (proto *Protocol, confidence float64) {
|
|
if len(data) < 7 {
|
|
return nil, 0
|
|
}
|
|
|
|
// The first byte of the handshake packet is the protocol version.
|
|
// For MySQL, this is 10 (0x0A).
|
|
if data[0] != 0x0A {
|
|
return nil, 0
|
|
}
|
|
|
|
if srcPort == 3306 {
|
|
confidence = .1
|
|
}
|
|
|
|
// After the protocol version, there is a null-terminated server version string.
|
|
// We search for the null byte starting from the second byte (index 1).
|
|
nullIndex := bytes.IndexByte(data[1:], 0x00)
|
|
|
|
// If no null byte is found, it's not a valid banner.
|
|
if nullIndex == -1 {
|
|
return nil, 0
|
|
}
|
|
|
|
// The position of the null byte is relative to the start of the whole slice.
|
|
// It's 1 (for the protocol byte) + nullIndex.
|
|
serverVersionEndPos := 1 + nullIndex
|
|
|
|
// After the null-terminated version string, there must be at least 4 bytes
|
|
// for the connection ID, plus more data for capabilities, auth, etc.
|
|
// We'll check for the 4-byte connection ID as a minimum requirement.
|
|
const connectionIDLength = 4
|
|
if len(data) < serverVersionEndPos+1+connectionIDLength {
|
|
return nil, 0
|
|
}
|
|
|
|
var version Version
|
|
fmt.Sscanf(string(data[1:serverVersionEndPos]), "%d.%d.%d-%s", &version.Major, &version.Minor, &version.Patch, &version.Extra)
|
|
|
|
return &Protocol{
|
|
Name: ProtocolMySQL,
|
|
Version: version,
|
|
}, confidence + .75
|
|
}
|