package redis import ( "bufio" "strings" csm "git.loafle.net/commons/service_matcher-go" ) const REDIS_PING string = "*1\r\n$4\r\nPING\r\n" const REDIS_INFO string = "*1\r\n$4\r\nINFO\r\n" const REDIS_QUIT string = "*1\r\n$4\r\nQUIT\r\n" type RedisMatcher struct { csm.Matchers meta csm.Metadata protected bool } func (r *RedisMatcher) Key() string { return "REDIS" } func (r *RedisMatcher) Name() string { name := "Redis" if r.protected { return name + " (protected)" } if v, ok := r.meta["redis_mode"]; ok { name = name + " " + v } if v, ok := r.meta["redis_version"]; ok { name = name + " (" + v + ")" } return name } func (r *RedisMatcher) Meta() csm.Metadata { return r.meta } func (r *RedisMatcher) IsPrePacket() bool { return false } func (r *RedisMatcher) HasResponse(index int) bool { return true } func (r *RedisMatcher) IsError(info csm.MatchInfo, index int, packet *csm.Packet) bool { return false } func (r *RedisMatcher) Match(info csm.MatchInfo, index int, packet *csm.Packet) error { if packet == nil || packet.Buffer == nil || packet.Len == 0 { return csm.NoPacketReceivedError() } resp := strings.Split(string(packet.Buffer), "\r\n")[0] if len(resp) <= 0 { return csm.NotMatchedError() } switch index { case 0: sign := string([]rune(resp)[0]) if len(sign) <= 0 { return csm.NotMatchedError() } if sign == "+" { if resp == "+PONG" || resp == "+OK" { return nil } } if sign == "-" { if resp == "-NOAUTH" || resp == "-ERR" { return nil } } r.protected = r.checkProtectedMode(packet) if r.protected { return nil } case 1: // INFO info := string(packet.Buffer) if !r.protected { r.parseInfo(info) } return nil case 2: sign := string([]rune(resp)[0]) if sign == "+" || sign == "-" { return nil } return nil default: return csm.NotMatchedError() } return csm.NotMatchedError() } func (r *RedisMatcher) checkProtectedMode(packet *csm.Packet) bool { var ( compareSign = "-" compareMsg = "DENIED" ) str := string(packet.Buffer[:packet.Len]) if str == "" { return false } if 0 >= len(str) || len(compareMsg)+2 > len(str) { return false } firstCompare := str[0:1] seconcdCompare := str[1 : len(compareMsg)+1] if firstCompare != compareSign { return false } if seconcdCompare != compareMsg { return false } r.protected = true return true } func (r *RedisMatcher) parseInfo(info string) { scanner := bufio.NewScanner(strings.NewReader(info)) for scanner.Scan() { line := scanner.Text() if strings.Compare(line, "") == 0 { break } if len(line) > 0 && strings.Contains(line, ":") { kv := strings.Split(line, ":") if len(kv[0]) > 0 && len(kv[1]) > 0 { r.meta[kv[0]] = kv[1] } } } } func (r *RedisMatcher) PacketCount() int { if r.protected { return 1 } return 3 } func NewMatcher() csm.Matcher { m := &RedisMatcher{} m.meta = csm.NewMetadata() m.AddPacket(csm.NewPacket([]byte(REDIS_PING), len(REDIS_PING))) m.AddPacket(csm.NewPacket([]byte(REDIS_INFO), len(REDIS_INFO))) m.AddPacket(csm.NewPacket([]byte(REDIS_QUIT), len(REDIS_QUIT))) return m }