Skip to content

Commit 1601730

Browse files
JimhHanJimhHan
authored andcommitted
Merge remote-tracking branch 'upstream/dns-geo-reverse-match' into dns-geo
2 parents 9721ff7 + eeb40c9 commit 1601730

10 files changed

Lines changed: 163 additions & 46 deletions

File tree

common/matcher/domain/conf/domain.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,36 @@ func ParseDomainRule(domain string) ([]*dm.Domain, error) {
4444
domainRule := new(dm.Domain)
4545
switch {
4646
case strings.HasPrefix(domain, "regexp:"):
47+
regexpVal := domain[7:]
48+
if len(regexpVal) == 0 {
49+
return nil, newError("empty regexp type of rule: ", domain)
50+
}
4751
domainRule.Type = dm.MatchingType_Regex
48-
domainRule.Value = domain[7:]
52+
domainRule.Value = regexpVal
4953

5054
case strings.HasPrefix(domain, "domain:"):
55+
domainName := domain[7:]
56+
if len(domainName) == 0 {
57+
return nil, newError("empty domain type of rule: ", domain)
58+
}
5159
domainRule.Type = dm.MatchingType_Subdomain
52-
domainRule.Value = domain[7:]
60+
domainRule.Value = domainName
5361

5462
case strings.HasPrefix(domain, "full:"):
63+
fullVal := domain[5:]
64+
if len(fullVal) == 0 {
65+
return nil, newError("empty full domain type of rule: ", domain)
66+
}
5567
domainRule.Type = dm.MatchingType_Full
56-
domainRule.Value = domain[5:]
68+
domainRule.Value = fullVal
5769

5870
case strings.HasPrefix(domain, "keyword:"):
71+
keywordVal := domain[8:]
72+
if len(keywordVal) == 0 {
73+
return nil, newError("empty keyword type of rule: ", domain)
74+
}
5975
domainRule.Type = dm.MatchingType_Keyword
60-
domainRule.Value = domain[8:]
76+
domainRule.Value = keywordVal
6177

6278
case strings.HasPrefix(domain, "dotless:"):
6379
domainRule.Type = dm.MatchingType_Regex

common/matcher/geoip/conf.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,21 +92,28 @@ func find(data, code []byte) []byte {
9292
}
9393
}
9494

95-
func ParaseIPList(ips []string) ([]*GeoIP, error) {
95+
func ParseIPList(ips []string) ([]*GeoIP, error) {
9696
var geoipList []*GeoIP
9797
var customCidrs []*CIDR
9898

9999
for _, ip := range ips {
100100
if strings.HasPrefix(ip, "geoip:") {
101101
country := ip[6:]
102+
isReverseMatch := false
103+
if strings.HasPrefix(country, "!") {
104+
country = country[1:]
105+
isReverseMatch = true
106+
}
107+
102108
geoipc, err := LoadGeoIP(strings.ToUpper(country))
103109
if err != nil {
104110
return nil, newError("failed to load GeoIP: ", country).Base(err)
105111
}
106112

107113
geoipList = append(geoipList, &GeoIP{
108-
CountryCode: strings.ToUpper(country),
109-
Cidr: geoipc,
114+
CountryCode: strings.ToUpper(country),
115+
Cidr: geoipc,
116+
ReverseMatch: isReverseMatch,
110117
})
111118
continue
112119
}
@@ -129,14 +136,24 @@ func ParaseIPList(ips []string) ([]*GeoIP, error) {
129136

130137
filename := kv[0]
131138
country := kv[1]
139+
if len(filename) == 0 || len(country) == 0 {
140+
return nil, newError("empty filename or empty country in rule")
141+
}
142+
143+
isReverseMatch := false
144+
if strings.HasPrefix(country, "!") {
145+
country = country[1:]
146+
isReverseMatch = true
147+
}
132148
geoipc, err := LoadIPFile(filename, strings.ToUpper(country))
133149
if err != nil {
134150
return nil, newError("failed to load IPs: ", country, " from ", filename).Base(err)
135151
}
136152

137153
geoipList = append(geoipList, &GeoIP{
138-
CountryCode: strings.ToUpper(filename + "_" + country),
139-
Cidr: geoipc,
154+
CountryCode: strings.ToUpper(filename + "_" + country),
155+
Cidr: geoipc,
156+
ReverseMatch: isReverseMatch,
140157
})
141158

142159
continue

common/matcher/geoip/geoip.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,16 @@ type ipv6 struct {
1515
}
1616

1717
type GeoIPMatcher struct {
18-
countryCode string
19-
ip4 []uint32
20-
prefix4 []uint8
21-
ip6 []ipv6
22-
prefix6 []uint8
18+
countryCode string
19+
reverseMatch bool
20+
ip4 []uint32
21+
prefix4 []uint8
22+
ip6 []ipv6
23+
prefix6 []uint8
24+
}
25+
26+
func (m *GeoIPMatcher) SetReverseMatch(isReverseMatch bool) {
27+
m.reverseMatch = isReverseMatch
2328
}
2429

2530
func normalize4(ip uint32, prefix uint8) uint32 {
@@ -149,8 +154,17 @@ func (m *GeoIPMatcher) match6(ip ipv6) bool {
149154
func (m *GeoIPMatcher) Match(ip net.IP) bool {
150155
switch len(ip) {
151156
case 4:
157+
if m.reverseMatch {
158+
return !m.match4(binary.BigEndian.Uint32(ip))
159+
}
152160
return m.match4(binary.BigEndian.Uint32(ip))
153161
case 16:
162+
if m.reverseMatch {
163+
return !m.match6(ipv6{
164+
a: binary.BigEndian.Uint64(ip[0:8]),
165+
b: binary.BigEndian.Uint64(ip[8:16]),
166+
})
167+
}
154168
return m.match6(ipv6{
155169
a: binary.BigEndian.Uint64(ip[0:8]),
156170
b: binary.BigEndian.Uint64(ip[8:16]),
@@ -170,14 +184,15 @@ type GeoIPMatcherContainer struct {
170184
func (c *GeoIPMatcherContainer) Add(geoip *GeoIP) (*GeoIPMatcher, error) {
171185
if len(geoip.CountryCode) > 0 {
172186
for _, m := range c.matchers {
173-
if m.countryCode == geoip.CountryCode {
187+
if m.countryCode == geoip.CountryCode && m.reverseMatch == geoip.ReverseMatch {
174188
return m, nil
175189
}
176190
}
177191
}
178192

179193
m := &GeoIPMatcher{
180-
countryCode: geoip.CountryCode,
194+
countryCode: geoip.CountryCode,
195+
reverseMatch: geoip.ReverseMatch,
181196
}
182197
if err := m.Init(geoip.Cidr); err != nil {
183198
return nil, err

common/matcher/geoip/geoip.pb.go

Lines changed: 31 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

common/matcher/geoip/geoip.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ message CIDR {
1818
message GeoIP {
1919
string country_code = 1;
2020
repeated CIDR cidr = 2;
21+
bool reverse_match =3;
2122
}
2223

2324
message GeoIPList {

common/matcher/geoip/geoip_test.go

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ import (
55
"path/filepath"
66
"testing"
77

8-
"github.com/golang/protobuf/proto"
98
"github.com/xtls/xray-core/common"
109
. "github.com/xtls/xray-core/common/matcher/geoip"
1110
"github.com/xtls/xray-core/common/net"
1211
"github.com/xtls/xray-core/common/platform"
1312
"github.com/xtls/xray-core/common/platform/filesystem"
13+
"google.golang.org/protobuf/proto"
1414
)
1515

1616
func init() {
@@ -20,11 +20,31 @@ func init() {
2020
if _, err := os.Stat(platform.GetAssetLocation("geoip.dat")); err != nil && os.IsNotExist(err) {
2121
common.Must(filesystem.CopyFile(platform.GetAssetLocation("geoip.dat"), filepath.Join(wd, "..", "..", "..", "resources", "geoip.dat")))
2222
}
23+
if _, err := os.Stat(platform.GetAssetLocation("geoiptestrouter.dat")); err != nil && os.IsNotExist(err) {
24+
common.Must(filesystem.CopyFile(platform.GetAssetLocation("geoiptestrouter.dat"), filepath.Join(wd, "..", "..", "..", "resources", "geoip.dat")))
25+
}
2326
if _, err := os.Stat(platform.GetAssetLocation("geosite.dat")); err != nil && os.IsNotExist(err) {
2427
common.Must(filesystem.CopyFile(platform.GetAssetLocation("geosite.dat"), filepath.Join(wd, "..", "..", "..", "resources", "geosite.dat")))
2528
}
2629
}
2730

31+
func TestParseIPList(t *testing.T) {
32+
ips := []string{
33+
"geoip:us",
34+
"geoip:cn",
35+
"geoip:!cn",
36+
"ext:geoiptestrouter.dat:!cn",
37+
"ext:geoiptestrouter.dat:ca",
38+
"ext-ip:geoiptestrouter.dat:!cn",
39+
"ext-ip:geoiptestrouter.dat:!ca",
40+
}
41+
42+
_, err := ParseIPList(ips)
43+
if err != nil {
44+
t.Fatalf("Failed to parse geoip list, got %s", err)
45+
}
46+
}
47+
2848
func TestGeoIPMatcherContainer(t *testing.T) {
2949
container := &GeoIPMatcherContainer{}
3050

@@ -123,6 +143,42 @@ func TestGeoIPMatcher(t *testing.T) {
123143
}
124144
}
125145

146+
func TestGeoIPReverseMatcher(t *testing.T) {
147+
cidrList := CIDRList{
148+
{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
149+
{Ip: []byte{91, 108, 4, 0}, Prefix: 16},
150+
}
151+
matcher := &GeoIPMatcher{}
152+
matcher.SetReverseMatch(true) // Reverse match
153+
common.Must(matcher.Init(cidrList))
154+
155+
testCases := []struct {
156+
Input string
157+
Output bool
158+
}{
159+
{
160+
Input: "8.8.8.8",
161+
Output: false,
162+
},
163+
{
164+
Input: "2001:cdba::3257:9652",
165+
Output: true,
166+
},
167+
{
168+
Input: "91.108.255.254",
169+
Output: false,
170+
},
171+
}
172+
173+
for _, testCase := range testCases {
174+
ip := net.ParseAddress(testCase.Input).IP()
175+
actual := matcher.Match(ip)
176+
if actual != testCase.Output {
177+
t.Error("expect input", testCase.Input, "to be", testCase.Output, ", but actually", actual)
178+
}
179+
}
180+
}
181+
126182
func TestGeoIPMatcher4CN(t *testing.T) {
127183
ips, err := loadGeoIP("CN")
128184
common.Must(err)

common/matcher/geosite/attribute.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package geosite
22

3+
import "strings"
4+
35
type AttributeList struct {
46
matcher []AttributeMatcher
57
}
@@ -25,7 +27,7 @@ type BooleanMatcher string
2527

2628
func (m BooleanMatcher) Match(domain *Domain) bool {
2729
for _, attr := range domain.Attribute {
28-
if attr.Key == string(m) {
30+
if strings.EqualFold(attr.GetKey(), string(m)) {
2931
return true
3032
}
3133
}

infra/conf/dns.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
7474
})
7575
}
7676

77-
geoipList, err := geoip.ParaseIPList(c.ExpectIPs)
77+
geoipList, err := geoip.ParseIPList(c.ExpectIPs)
7878
if err != nil {
7979
return nil, newError("invalid IP rule: ", c.ExpectIPs).Base(err)
8080
}

0 commit comments

Comments
 (0)