Feat: add reverse match for GeoIP

(cherry picked from commit 3a50affa0a7316a9ad249f1b2b2996cb88948551)
This commit is contained in:
Loyalsoldier 2021-04-08 13:19:25 +08:00 committed by 世界
parent 13bc0432bc
commit 3fe61ed4a2
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
7 changed files with 232 additions and 112 deletions

View file

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

View file

@ -399,21 +399,30 @@ func parseDomainRule(domain string) ([]*router.Domain, error) {
return []*router.Domain{domainRule}, nil
}
func toCidrList(ips StringList) ([]*router.GeoIP, error) {
func ToCidrList(ips StringList) ([]*router.GeoIP, error) {
var geoipList []*router.GeoIP
var customCidrs []*router.CIDR
for _, ip := range ips {
if strings.HasPrefix(ip, "geoip:") {
country := ip[6:]
isReverseMatch := false
if strings.HasPrefix(ip, "geoip:!") {
country = ip[7:]
isReverseMatch = true
}
if len(country) == 0 {
return nil, newError("empty country name in rule")
}
geoip, err := loadGeoIP(strings.ToUpper(country))
if err != nil {
return nil, newError("failed to load GeoIP: ", country).Base(err)
}
geoipList = append(geoipList, &router.GeoIP{
CountryCode: strings.ToUpper(country),
Cidr: geoip,
CountryCode: strings.ToUpper(country),
Cidr: geoip,
ReverseMatch: isReverseMatch,
})
continue
}
@ -436,14 +445,24 @@ func toCidrList(ips StringList) ([]*router.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
}
geoip, err := loadIP(filename, strings.ToUpper(country))
if err != nil {
return nil, newError("failed to load IPs: ", country, " from ", filename).Base(err)
}
geoipList = append(geoipList, &router.GeoIP{
CountryCode: strings.ToUpper(filename + "_" + country),
Cidr: geoip,
CountryCode: strings.ToUpper(filename + "_" + country),
Cidr: geoip,
ReverseMatch: isReverseMatch,
})
continue
@ -525,7 +544,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
}
if rawFieldRule.IP != nil {
geoipList, err := toCidrList(*rawFieldRule.IP)
geoipList, err := ToCidrList(*rawFieldRule.IP)
if err != nil {
return nil, err
}
@ -541,7 +560,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
}
if rawFieldRule.SourceIP != nil {
geoipList, err := toCidrList(*rawFieldRule.SourceIP)
geoipList, err := ToCidrList(*rawFieldRule.SourceIP)
if err != nil {
return nil, err
}
@ -583,21 +602,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)

View file

@ -2,15 +2,53 @@ package conf_test
import (
"encoding/json"
"os"
"path/filepath"
"testing"
_ "unsafe"
"github.com/golang/protobuf/proto"
"github.com/xtls/xray-core/app/router"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/platform"
"github.com/xtls/xray-core/common/platform/filesystem"
. "github.com/xtls/xray-core/infra/conf"
)
func init() {
wd, err := os.Getwd()
common.Must(err)
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")))
}
os.Setenv("xray.location.asset", wd)
}
func TestToCidrList(t *testing.T) {
t.Log(os.Getenv("xray.location.asset"))
common.Must(filesystem.CopyFile(platform.GetAssetLocation("geoiptestrouter.dat"), "geoip.dat"))
ips := StringList([]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 := ToCidrList(ips)
if err != nil {
t.Fatalf("Failed to parse geoip list, got %s", err)
}
}
func TestRouterConfig(t *testing.T) {
createParser := func() func(string) (proto.Message, error) {
return func(s string) (proto.Message, error) {