瀏覽代碼

Merge remote-tracking branch 'upstream/dns-geo-reverse-match' into dns-geo

JimhHan 4 年之前
父節點
當前提交
16017304b8

+ 20 - 4
common/matcher/domain/conf/domain.go

@@ -44,20 +44,36 @@ func ParseDomainRule(domain string) ([]*dm.Domain, error) {
 	domainRule := new(dm.Domain)
 	switch {
 	case strings.HasPrefix(domain, "regexp:"):
+		regexpVal := domain[7:]
+		if len(regexpVal) == 0 {
+			return nil, newError("empty regexp type of rule: ", domain)
+		}
 		domainRule.Type = dm.MatchingType_Regex
-		domainRule.Value = domain[7:]
+		domainRule.Value = regexpVal
 
 	case strings.HasPrefix(domain, "domain:"):
+		domainName := domain[7:]
+		if len(domainName) == 0 {
+			return nil, newError("empty domain type of rule: ", domain)
+		}
 		domainRule.Type = dm.MatchingType_Subdomain
-		domainRule.Value = domain[7:]
+		domainRule.Value = domainName
 
 	case strings.HasPrefix(domain, "full:"):
+		fullVal := domain[5:]
+		if len(fullVal) == 0 {
+			return nil, newError("empty full domain type of rule: ", domain)
+		}
 		domainRule.Type = dm.MatchingType_Full
-		domainRule.Value = domain[5:]
+		domainRule.Value = fullVal
 
 	case strings.HasPrefix(domain, "keyword:"):
+		keywordVal := domain[8:]
+		if len(keywordVal) == 0 {
+			return nil, newError("empty keyword type of rule: ", domain)
+		}
 		domainRule.Type = dm.MatchingType_Keyword
-		domainRule.Value = domain[8:]
+		domainRule.Value = keywordVal
 
 	case strings.HasPrefix(domain, "dotless:"):
 		domainRule.Type = dm.MatchingType_Regex

+ 22 - 5
common/matcher/geoip/conf.go

@@ -92,21 +92,28 @@ func find(data, code []byte) []byte {
 	}
 }
 
-func ParaseIPList(ips []string) ([]*GeoIP, error) {
+func ParseIPList(ips []string) ([]*GeoIP, error) {
 	var geoipList []*GeoIP
 	var customCidrs []*CIDR
 
 	for _, ip := range ips {
 		if strings.HasPrefix(ip, "geoip:") {
 			country := ip[6:]
+			isReverseMatch := false
+			if strings.HasPrefix(country, "!") {
+				country = country[1:]
+				isReverseMatch = true
+			}
+
 			geoipc, err := LoadGeoIP(strings.ToUpper(country))
 			if err != nil {
 				return nil, newError("failed to load GeoIP: ", country).Base(err)
 			}
 
 			geoipList = append(geoipList, &GeoIP{
-				CountryCode: strings.ToUpper(country),
-				Cidr:        geoipc,
+				CountryCode:  strings.ToUpper(country),
+				Cidr:         geoipc,
+				ReverseMatch: isReverseMatch,
 			})
 			continue
 		}
@@ -129,14 +136,24 @@ func ParaseIPList(ips []string) ([]*GeoIP, error) {
 
 			filename := kv[0]
 			country := kv[1]
+			if len(filename) == 0 || len(country) == 0 {
+				return nil, newError("empty filename or empty country in rule")
+			}
+
+			isReverseMatch := false
+			if strings.HasPrefix(country, "!") {
+				country = country[1:]
+				isReverseMatch = true
+			}
 			geoipc, err := LoadIPFile(filename, strings.ToUpper(country))
 			if err != nil {
 				return nil, newError("failed to load IPs: ", country, " from ", filename).Base(err)
 			}
 
 			geoipList = append(geoipList, &GeoIP{
-				CountryCode: strings.ToUpper(filename + "_" + country),
-				Cidr:        geoipc,
+				CountryCode:  strings.ToUpper(filename + "_" + country),
+				Cidr:         geoipc,
+				ReverseMatch: isReverseMatch,
 			})
 
 			continue

+ 22 - 7
common/matcher/geoip/geoip.go

@@ -15,11 +15,16 @@ type ipv6 struct {
 }
 
 type GeoIPMatcher struct {
-	countryCode string
-	ip4         []uint32
-	prefix4     []uint8
-	ip6         []ipv6
-	prefix6     []uint8
+	countryCode  string
+	reverseMatch bool
+	ip4          []uint32
+	prefix4      []uint8
+	ip6          []ipv6
+	prefix6      []uint8
+}
+
+func (m *GeoIPMatcher) SetReverseMatch(isReverseMatch bool) {
+	m.reverseMatch = isReverseMatch
 }
 
 func normalize4(ip uint32, prefix uint8) uint32 {
@@ -149,8 +154,17 @@ func (m *GeoIPMatcher) match6(ip ipv6) bool {
 func (m *GeoIPMatcher) Match(ip net.IP) bool {
 	switch len(ip) {
 	case 4:
+		if m.reverseMatch {
+			return !m.match4(binary.BigEndian.Uint32(ip))
+		}
 		return m.match4(binary.BigEndian.Uint32(ip))
 	case 16:
+		if m.reverseMatch {
+			return !m.match6(ipv6{
+				a: binary.BigEndian.Uint64(ip[0:8]),
+				b: binary.BigEndian.Uint64(ip[8:16]),
+			})
+		}
 		return m.match6(ipv6{
 			a: binary.BigEndian.Uint64(ip[0:8]),
 			b: binary.BigEndian.Uint64(ip[8:16]),
@@ -170,14 +184,15 @@ type GeoIPMatcherContainer struct {
 func (c *GeoIPMatcherContainer) Add(geoip *GeoIP) (*GeoIPMatcher, error) {
 	if len(geoip.CountryCode) > 0 {
 		for _, m := range c.matchers {
-			if m.countryCode == geoip.CountryCode {
+			if m.countryCode == geoip.CountryCode && m.reverseMatch == geoip.ReverseMatch {
 				return m, nil
 			}
 		}
 	}
 
 	m := &GeoIPMatcher{
-		countryCode: geoip.CountryCode,
+		countryCode:  geoip.CountryCode,
+		reverseMatch: geoip.ReverseMatch,
 	}
 	if err := m.Init(geoip.Cidr); err != nil {
 		return nil, err

+ 31 - 21
common/matcher/geoip/geoip.pb.go

@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.25.0
-// 	protoc        v3.15.6
+// 	protoc        v3.15.7
 // source: common/matcher/geoip/geoip.proto
 
 package geoip
@@ -88,8 +88,9 @@ type GeoIP struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	CountryCode string  `protobuf:"bytes,1,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"`
-	Cidr        []*CIDR `protobuf:"bytes,2,rep,name=cidr,proto3" json:"cidr,omitempty"`
+	CountryCode  string  `protobuf:"bytes,1,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"`
+	Cidr         []*CIDR `protobuf:"bytes,2,rep,name=cidr,proto3" json:"cidr,omitempty"`
+	ReverseMatch bool    `protobuf:"varint,3,opt,name=reverse_match,json=reverseMatch,proto3" json:"reverse_match,omitempty"`
 }
 
 func (x *GeoIP) Reset() {
@@ -138,6 +139,13 @@ func (x *GeoIP) GetCidr() []*CIDR {
 	return nil
 }
 
+func (x *GeoIP) GetReverseMatch() bool {
+	if x != nil {
+		return x.ReverseMatch
+	}
+	return false
+}
+
 type GeoIPList struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -194,25 +202,27 @@ var file_common_matcher_geoip_geoip_proto_rawDesc = []byte{
 	0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x22, 0x2e, 0x0a,
 	0x04, 0x43, 0x49, 0x44, 0x52, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28,
 	0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18,
-	0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x5f, 0x0a,
-	0x05, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72,
-	0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f,
-	0x75, 0x6e, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x33, 0x0a, 0x04, 0x63, 0x69, 0x64,
-	0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63,
-	0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x67, 0x65,
-	0x6f, 0x69, 0x70, 0x2e, 0x43, 0x49, 0x44, 0x52, 0x52, 0x04, 0x63, 0x69, 0x64, 0x72, 0x22, 0x43,
-	0x0a, 0x09, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x05, 0x65,
-	0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61,
-	0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72,
-	0x2e, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x05, 0x65, 0x6e,
-	0x74, 0x72, 0x79, 0x42, 0x6d, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
+	0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x84, 0x01,
+	0x0a, 0x05, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74,
+	0x72, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63,
+	0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x33, 0x0a, 0x04, 0x63, 0x69,
+	0x64, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
 	0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x67,
-	0x65, 0x6f, 0x69, 0x70, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
-	0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72,
-	0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72,
-	0x2f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0xaa, 0x02, 0x19, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x43, 0x6f,
-	0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f,
-	0x49, 0x50, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x65, 0x6f, 0x69, 0x70, 0x2e, 0x43, 0x49, 0x44, 0x52, 0x52, 0x04, 0x63, 0x69, 0x64, 0x72, 0x12,
+	0x23, 0x0a, 0x0d, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x4d,
+	0x61, 0x74, 0x63, 0x68, 0x22, 0x43, 0x0a, 0x09, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x4c, 0x69, 0x73,
+	0x74, 0x12, 0x36, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6d,
+	0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x2e, 0x47, 0x65, 0x6f,
+	0x49, 0x50, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x6d, 0x0a, 0x1d, 0x63, 0x6f, 0x6d,
+	0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6d, 0x61, 0x74,
+	0x63, 0x68, 0x65, 0x72, 0x2e, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69,
+	0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72,
+	0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6d,
+	0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0xaa, 0x02, 0x19, 0x58,
+	0x72, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68,
+	0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (

+ 1 - 0
common/matcher/geoip/geoip.proto

@@ -18,6 +18,7 @@ message CIDR {
 message GeoIP {
   string country_code = 1;
   repeated CIDR cidr = 2;
+  bool reverse_match =3;
 }
 
 message GeoIPList {

+ 57 - 1
common/matcher/geoip/geoip_test.go

@@ -5,12 +5,12 @@ import (
 	"path/filepath"
 	"testing"
 
-	"github.com/golang/protobuf/proto"
 	"github.com/xtls/xray-core/common"
 	. "github.com/xtls/xray-core/common/matcher/geoip"
 	"github.com/xtls/xray-core/common/net"
 	"github.com/xtls/xray-core/common/platform"
 	"github.com/xtls/xray-core/common/platform/filesystem"
+	"google.golang.org/protobuf/proto"
 )
 
 func init() {
@@ -20,11 +20,31 @@ func init() {
 	if _, err := os.Stat(platform.GetAssetLocation("geoip.dat")); err != nil && os.IsNotExist(err) {
 		common.Must(filesystem.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(wd, "..", "..", "..", "resources", "geoip.dat")))
 	}
+	if _, err := os.Stat(platform.GetAssetLocation("geoiptestrouter.dat")); err != nil && os.IsNotExist(err) {
+		common.Must(filesystem.CopyFile(platform.GetAssetLocation("geoiptestrouter.dat"), filepath.Join(wd, "..", "..", "..", "resources", "geoip.dat")))
+	}
 	if _, err := os.Stat(platform.GetAssetLocation("geosite.dat")); err != nil && os.IsNotExist(err) {
 		common.Must(filesystem.CopyFile(platform.GetAssetLocation("geosite.dat"), filepath.Join(wd, "..", "..", "..", "resources", "geosite.dat")))
 	}
 }
 
+func TestParseIPList(t *testing.T) {
+	ips := []string{
+		"geoip:us",
+		"geoip:cn",
+		"geoip:!cn",
+		"ext:geoiptestrouter.dat:!cn",
+		"ext:geoiptestrouter.dat:ca",
+		"ext-ip:geoiptestrouter.dat:!cn",
+		"ext-ip:geoiptestrouter.dat:!ca",
+	}
+
+	_, err := ParseIPList(ips)
+	if err != nil {
+		t.Fatalf("Failed to parse geoip list, got %s", err)
+	}
+}
+
 func TestGeoIPMatcherContainer(t *testing.T) {
 	container := &GeoIPMatcherContainer{}
 
@@ -123,6 +143,42 @@ func TestGeoIPMatcher(t *testing.T) {
 	}
 }
 
+func TestGeoIPReverseMatcher(t *testing.T) {
+	cidrList := CIDRList{
+		{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
+		{Ip: []byte{91, 108, 4, 0}, Prefix: 16},
+	}
+	matcher := &GeoIPMatcher{}
+	matcher.SetReverseMatch(true) // Reverse match
+	common.Must(matcher.Init(cidrList))
+
+	testCases := []struct {
+		Input  string
+		Output bool
+	}{
+		{
+			Input:  "8.8.8.8",
+			Output: false,
+		},
+		{
+			Input:  "2001:cdba::3257:9652",
+			Output: true,
+		},
+		{
+			Input:  "91.108.255.254",
+			Output: false,
+		},
+	}
+
+	for _, testCase := range testCases {
+		ip := net.ParseAddress(testCase.Input).IP()
+		actual := matcher.Match(ip)
+		if actual != testCase.Output {
+			t.Error("expect input", testCase.Input, "to be", testCase.Output, ", but actually", actual)
+		}
+	}
+}
+
 func TestGeoIPMatcher4CN(t *testing.T) {
 	ips, err := loadGeoIP("CN")
 	common.Must(err)

+ 3 - 1
common/matcher/geosite/attribute.go

@@ -1,5 +1,7 @@
 package geosite
 
+import "strings"
+
 type AttributeList struct {
 	matcher []AttributeMatcher
 }
@@ -25,7 +27,7 @@ type BooleanMatcher string
 
 func (m BooleanMatcher) Match(domain *Domain) bool {
 	for _, attr := range domain.Attribute {
-		if attr.Key == string(m) {
+		if strings.EqualFold(attr.GetKey(), string(m)) {
 			return true
 		}
 	}

+ 1 - 1
infra/conf/dns.go

@@ -74,7 +74,7 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
 		})
 	}
 
-	geoipList, err := geoip.ParaseIPList(c.ExpectIPs)
+	geoipList, err := geoip.ParseIPList(c.ExpectIPs)
 	if err != nil {
 		return nil, newError("invalid IP rule: ", c.ExpectIPs).Base(err)
 	}

+ 5 - 5
infra/conf/router.go

@@ -203,7 +203,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
 	}
 
 	if rawFieldRule.IP != nil {
-		geoipList, err := geoip.ParaseIPList(*rawFieldRule.IP)
+		geoipList, err := geoip.ParseIPList(*rawFieldRule.IP)
 		if err != nil {
 			return nil, err
 		}
@@ -219,7 +219,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
 	}
 
 	if rawFieldRule.SourceIP != nil {
-		geoipList, err := geoip.ParaseIPList(*rawFieldRule.SourceIP)
+		geoipList, err := geoip.ParseIPList(*rawFieldRule.SourceIP)
 		if err != nil {
 			return nil, err
 		}
@@ -261,21 +261,21 @@ func ParseRule(msg json.RawMessage) (*router.RoutingRule, error) {
 	if err != nil {
 		return nil, newError("invalid router rule").Base(err)
 	}
-	if rawRule.Type == "field" {
+	if strings.EqualFold(rawRule.Type, "field") {
 		fieldrule, err := parseFieldRule(msg)
 		if err != nil {
 			return nil, newError("invalid field rule").Base(err)
 		}
 		return fieldrule, nil
 	}
-	if rawRule.Type == "chinaip" {
+	if strings.EqualFold(rawRule.Type, "chinaip") {
 		chinaiprule, err := parseChinaIPRule(msg)
 		if err != nil {
 			return nil, newError("invalid chinaip rule").Base(err)
 		}
 		return chinaiprule, nil
 	}
-	if rawRule.Type == "chinasites" {
+	if strings.EqualFold(rawRule.Type, "chinasites") {
 		chinasitesrule, err := parseChinaSitesRule(msg)
 		if err != nil {
 			return nil, newError("invalid chinasites rule").Base(err)

+ 1 - 1
infra/conf/xray.go

@@ -101,7 +101,7 @@ func (c *SniffingConfig) Build() (*proxyman.SniffingConfig, error) {
 
 	var exIP []*geoip.GeoIP
 	if c.IPsExcluded != nil {
-		exip, err := geoip.ParaseIPList(*c.IPsExcluded)
+		exip, err := geoip.ParseIPList(*c.IPsExcluded)
 		if err != nil {
 			return nil, newError("failed to parse excluded ip").Base(err)
 		}