package mysql import ( "bytes" "encoding/binary" "io" "strings" csm "git.loafle.net/commons/service_matcher-go" ) type MySqlMatcher struct { csm.Matchers meta csm.Metadata } func (m *MySqlMatcher) Key() string { return "MYSQL" } func (m *MySqlMatcher) Name() string { name := "MySQL" if v, ok := m.meta["version"]; ok { if strings.Contains(v, "MariaDB") { name = "MariaDB" } name = name + " (" + v + ")" } return name } func (m *MySqlMatcher) Meta() csm.Metadata { return m.meta } func (m *MySqlMatcher) IsPrePacket() bool { return true } func (m *MySqlMatcher) HasResponse(index int) bool { return true } func (m *MySqlMatcher) IsError(info csm.MatchInfo, index int, packet *csm.Packet) bool { return false } type serverSettings struct { protocol byte version string flags uint32 charset uint8 scrambleBuff []byte threadID uint32 keepalive int64 } func (m *MySqlMatcher) Match(info csm.MatchInfo, index int, packet *csm.Packet) error { if packet == nil || packet.Buffer == nil || packet.Len == 0 { return csm.NoPacketReceivedError() } buf := bytes.NewBuffer(packet.Buffer[:3]) packetLen, _ := binary.ReadUvarint(buf) if packetLen != uint64(packet.Len-4) { return csm.NotMatchedError() } pos := 4 p := new(serverSettings) p.protocol = packet.Buffer[pos] if p.protocol != 9 && p.protocol != 10 { return csm.NotMatchedError() } pos++ slice, err := readSlice(packet.Buffer[pos:], 0x00) if err != nil { return csm.NotMatchedError() } m.meta["version"] = string(slice) pos += len(slice) + 1 p.threadID = bytesToUint32(packet.Buffer[pos : pos+4]) pos += 4 return nil } func readSlice(data []byte, delim byte) (slice []byte, e error) { pos := bytes.IndexByte(data, delim) if pos > -1 { slice = data[:pos] } else { slice = data e = io.EOF } return } func bytesToUint32(b []byte) (n uint32) { for i := uint8(0); i < 4; i++ { n |= uint32(b[i]) << (i * 8) } return } func NewMatcher() csm.Matcher { m := &MySqlMatcher{} m.meta = csm.NewMetadata() return m }