mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-19 20:32:35 +02:00
113 lines
2.1 KiB
Go
113 lines
2.1 KiB
Go
package acl
|
|
|
|
import (
|
|
"errors"
|
|
"net"
|
|
"strings"
|
|
|
|
"github.com/yusing/go-proxy/internal/gperr"
|
|
"github.com/yusing/go-proxy/internal/maxmind"
|
|
)
|
|
|
|
type MatcherFunc func(*maxmind.IPInfo) bool
|
|
|
|
type Matcher struct {
|
|
match MatcherFunc
|
|
}
|
|
|
|
type Matchers []Matcher
|
|
|
|
const (
|
|
MatcherTypeIP = "ip"
|
|
MatcherTypeCIDR = "cidr"
|
|
MatcherTypeTimeZone = "tz"
|
|
MatcherTypeCountry = "country"
|
|
)
|
|
|
|
// TODO: use this error in the future
|
|
//
|
|
//nolint:unused
|
|
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 = errors.New("syntax error")
|
|
errInvalidIP = errors.New("invalid IP")
|
|
errInvalidCIDR = errors.New("invalid CIDR")
|
|
)
|
|
|
|
func (matcher *Matcher) Parse(s string) error {
|
|
parts := strings.Split(s, ":")
|
|
if len(parts) != 2 {
|
|
return errSyntax
|
|
}
|
|
|
|
switch parts[0] {
|
|
case MatcherTypeIP:
|
|
ip := net.ParseIP(parts[1])
|
|
if ip == nil {
|
|
return errInvalidIP
|
|
}
|
|
matcher.match = matchIP(ip)
|
|
case MatcherTypeCIDR:
|
|
_, net, err := net.ParseCIDR(parts[1])
|
|
if err != nil {
|
|
return errInvalidCIDR
|
|
}
|
|
matcher.match = matchCIDR(net)
|
|
case MatcherTypeTimeZone:
|
|
matcher.match = matchTimeZone(parts[1])
|
|
case MatcherTypeCountry:
|
|
matcher.match = matchISOCode(parts[1])
|
|
default:
|
|
return errSyntax
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (matchers Matchers) Match(ip *maxmind.IPInfo) bool {
|
|
for _, m := range matchers {
|
|
if m.match(ip) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func matchIP(ip net.IP) MatcherFunc {
|
|
return func(ip2 *maxmind.IPInfo) bool {
|
|
return ip.Equal(ip2.IP)
|
|
}
|
|
}
|
|
|
|
func matchCIDR(n *net.IPNet) MatcherFunc {
|
|
return func(ip *maxmind.IPInfo) bool {
|
|
return n.Contains(ip.IP)
|
|
}
|
|
}
|
|
|
|
func matchTimeZone(tz string) MatcherFunc {
|
|
return func(ip *maxmind.IPInfo) bool {
|
|
city, ok := maxmind.LookupCity(ip)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return city.Location.TimeZone == tz
|
|
}
|
|
}
|
|
|
|
func matchISOCode(iso string) MatcherFunc {
|
|
return func(ip *maxmind.IPInfo) bool {
|
|
city, ok := maxmind.LookupCity(ip)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return city.Country.IsoCode == iso
|
|
}
|
|
}
|