package mysql import ( "bytes" "encoding/binary" "io" "strings" osm "git.loafle.net/overflow/service_matcher-go" ) type MySqlMatcher struct { osm.Matchers } func (m *MySqlMatcher) Key() string { return "MYSQL" } func (m *MySqlMatcher) Name(matchCtx *osm.MatchCtx) string { name := "MySQL" if v, ok := matchCtx.GetAttribute("version"); ok { if strings.Contains(v, "MariaDB") { name = "MariaDB" } name = name + " (" + v + ")" } return name } func (m *MySqlMatcher) IsPrePacket() bool { return true } func (m *MySqlMatcher) HasResponse(matchCtx *osm.MatchCtx, index int) bool { return true } func (m *MySqlMatcher) IsError(matchCtx *osm.MatchCtx, index int, packet *osm.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(matchCtx *osm.MatchCtx, index int, packet *osm.Packet) error { if packet == nil || !packet.Valid() { return osm.NoPacketReceivedError() } buf := bytes.NewBuffer(packet.Buffer[:3]) packetLen, _ := binary.ReadUvarint(buf) if packetLen != uint64(packet.Len-4) { return osm.NotMatchedError() } pos := 4 p := new(serverSettings) p.protocol = packet.Buffer[pos] if p.protocol != 9 && p.protocol != 10 { return osm.NotMatchedError() } pos++ slice, err := readSlice(packet.Buffer[pos:], 0x00) if err != nil { return osm.NotMatchedError() } matchCtx.SetAttribute("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() osm.Matcher { m := &MySqlMatcher{} return m }