package acl import ( "net" "strings" "github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/maxmind" ) type Matcher func(*maxmind.IPInfo) bool type Matchers []Matcher const ( MatcherTypeIP = "ip" MatcherTypeCIDR = "cidr" MatcherTypeTimeZone = "tz" MatcherTypeCountry = "country" ) var errMatcherFormat = gperr.Multiline().AddLines( "invalid matcher format, expect {type}:{value}", "Available types: ip|cidr|tz|country", "ip:127.0.0.1", "cidr:127.0.0.0/8", "tz:Asia/Shanghai", "country:GB", ) var ( errSyntax = gperr.New("syntax error") errInvalidIP = gperr.New("invalid IP") errInvalidCIDR = gperr.New("invalid CIDR") errMaxMindNotConfigured = gperr.New("MaxMind not configured") ) func ParseMatcher(s string) (Matcher, gperr.Error) { parts := strings.Split(s, ":") if len(parts) != 2 { return nil, errSyntax } switch parts[0] { case MatcherTypeIP: ip := net.ParseIP(parts[1]) if ip == nil { return nil, errInvalidIP } return matchIP(ip), nil case MatcherTypeCIDR: _, net, err := net.ParseCIDR(parts[1]) if err != nil { return nil, errInvalidCIDR } return matchCIDR(net), nil case MatcherTypeTimeZone: if !maxmind.HasInstance() { return nil, errMaxMindNotConfigured } return matchTimeZone(parts[1]), nil case MatcherTypeCountry: if !maxmind.HasInstance() { return nil, errMaxMindNotConfigured } return matchISOCode(parts[1]), nil default: return nil, errSyntax } } func (matchers Matchers) Match(ip *maxmind.IPInfo) bool { for _, m := range matchers { if m(ip) { return true } } return false } func matchIP(ip net.IP) Matcher { return func(ip2 *maxmind.IPInfo) bool { return ip.Equal(ip2.IP) } } func matchCIDR(n *net.IPNet) Matcher { return func(ip *maxmind.IPInfo) bool { return n.Contains(ip.IP) } } func matchTimeZone(tz string) Matcher { return func(ip *maxmind.IPInfo) bool { city, ok := maxmind.LookupCity(ip) if !ok { return false } return city.Location.TimeZone == tz } } func matchISOCode(iso string) Matcher { return func(ip *maxmind.IPInfo) bool { city, ok := maxmind.LookupCity(ip) if !ok { return false } return city.Country.IsoCode == iso } }