From 1fc5b7aef56f0ea5437fc15daee4790c8cd50e07 Mon Sep 17 00:00:00 2001 From: Fangliding Date: Sat, 17 Jan 2026 20:06:42 +0800 Subject: [PATCH 1/2] Revert geofile change This reverts commit 36425d2a6e53c755c96d25ef2323b7fffd1fab3f. This reverts commit 961c352127f32c04034a88d021a683e6a80eac20. This reverts commit c715154309e41e759f544edd3667889e257fb3af. --- app/dns/dns.go | 95 ------------- app/dns/dns_test.go | 8 +- app/dns/nameserver.go | 15 -- app/router/condition.go | 44 +----- app/router/condition_geoip_test.go | 81 +++++++---- app/router/condition_test.go | 93 ++++--------- app/router/config.go | 103 +------------- common/platform/filesystem/asset_tools.go | 52 ------- common/platform/windows.go | 5 +- infra/conf/dns.go | 19 ++- infra/conf/router.go | 159 ++++++++++++++++------ 11 files changed, 224 insertions(+), 450 deletions(-) delete mode 100644 common/platform/filesystem/asset_tools.go diff --git a/app/dns/dns.go b/app/dns/dns.go index 5d6154f9fcc2..603640f1549f 100644 --- a/app/dns/dns.go +++ b/app/dns/dns.go @@ -12,15 +12,12 @@ import ( "sync" "time" - router "github.com/xtls/xray-core/app/router" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/platform/filesystem" "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/strmatcher" "github.com/xtls/xray-core/features/dns" - "google.golang.org/protobuf/proto" ) // DNS is a DNS rely server. @@ -100,25 +97,6 @@ func New(ctx context.Context, config *Config) (*DNS, error) { } for _, ns := range config.NameServer { - if runtime.GOOS != "windows" && runtime.GOOS != "wasm" { - err := parseDomains(ns) - if err != nil { - return nil, errors.New("failed to parse dns domain rules: ").Base(err) - } - - expectedGeoip, err := router.GetGeoIPList(ns.ExpectedGeoip) - if err != nil { - return nil, errors.New("failed to parse dns expectIPs rules: ").Base(err) - } - ns.ExpectedGeoip = expectedGeoip - - unexpectedGeoip, err := router.GetGeoIPList(ns.UnexpectedGeoip) - if err != nil { - return nil, errors.New("failed to parse dns unexpectedGeoip rules: ").Base(err) - } - ns.UnexpectedGeoip = unexpectedGeoip - - } domainRuleCount += len(ns.PrioritizedDomain) } @@ -602,76 +580,3 @@ func detectGUIPlatform() bool { } return false } - -func parseDomains(ns *NameServer) error { - pureDomains := []*router.Domain{} - - // convert to pure domain - for _, pd := range ns.PrioritizedDomain { - pureDomains = append(pureDomains, &router.Domain{ - Type: router.Domain_Type(pd.Type), - Value: pd.Domain, - }) - } - - domainList := []*router.Domain{} - for _, domain := range pureDomains { - val := strings.Split(domain.Value, "_") - if len(val) >= 2 { - - fileName := val[0] - code := val[1] - - bs, err := filesystem.ReadAsset(fileName) - if err != nil { - return errors.New("failed to load file: ", fileName).Base(err) - } - bs = filesystem.Find(bs, []byte(code)) - var geosite router.GeoSite - - if err := proto.Unmarshal(bs, &geosite); err != nil { - return errors.New("failed Unmarshal :").Base(err) - } - - // parse attr - if len(val) == 3 { - siteWithAttr := strings.Split(val[2], ",") - attrs := router.ParseAttrs(siteWithAttr) - if !attrs.IsEmpty() { - filteredDomains := make([]*router.Domain, 0, len(pureDomains)) - for _, domain := range geosite.Domain { - if attrs.Match(domain) { - filteredDomains = append(filteredDomains, domain) - } - } - geosite.Domain = filteredDomains - } - - } - - domainList = append(domainList, geosite.Domain...) - - // update ns.OriginalRules Size - ruleTag := strings.Join(val, ":") - for i, oRule := range ns.OriginalRules { - if oRule.Rule == strings.ToLower(ruleTag) { - ns.OriginalRules[i].Size = uint32(len(geosite.Domain)) - } - } - - } else { - domainList = append(domainList, domain) - } - } - - // convert back to NameServer_PriorityDomain - ns.PrioritizedDomain = []*NameServer_PriorityDomain{} - for _, pd := range domainList { - ns.PrioritizedDomain = append(ns.PrioritizedDomain, &NameServer_PriorityDomain{ - Type: ToDomainMatchingType(pd.Type), - Domain: pd.Value, - }) - } - - return nil -} diff --git a/app/dns/dns_test.go b/app/dns/dns_test.go index d103704c3f14..cb70b0b35e9c 100644 --- a/app/dns/dns_test.go +++ b/app/dns/dns_test.go @@ -541,7 +541,7 @@ func TestIPMatch(t *testing.T) { }, ExpectedGeoip: []*router.GeoIP{ { - // local + CountryCode: "local", Cidr: []*router.CIDR{ { // inner ip, will not match @@ -565,7 +565,7 @@ func TestIPMatch(t *testing.T) { }, ExpectedGeoip: []*router.GeoIP{ { - // test + CountryCode: "test", Cidr: []*router.CIDR{ { Ip: []byte{8, 8, 8, 8}, @@ -574,7 +574,7 @@ func TestIPMatch(t *testing.T) { }, }, { - // test + CountryCode: "test", Cidr: []*router.CIDR{ { Ip: []byte{8, 8, 8, 4}, @@ -669,7 +669,7 @@ func TestLocalDomain(t *testing.T) { }, ExpectedGeoip: []*router.GeoIP{ { // Will match localhost, localhost-a and localhost-b, - // local + CountryCode: "local", Cidr: []*router.CIDR{ {Ip: []byte{127, 0, 0, 2}, Prefix: 32}, {Ip: []byte{127, 0, 0, 3}, Prefix: 32}, diff --git a/app/dns/nameserver.go b/app/dns/nameserver.go index e57b74bfaa98..bad9277c7602 100644 --- a/app/dns/nameserver.go +++ b/app/dns/nameserver.go @@ -297,18 +297,3 @@ func ResolveIpOptionOverride(queryStrategy QueryStrategy, ipOption dns.IPOption) return ipOption } } - -func ToDomainMatchingType(t router.Domain_Type) DomainMatchingType { - switch t { - case router.Domain_Domain: - return DomainMatchingType_Subdomain - case router.Domain_Full: - return DomainMatchingType_Full - case router.Domain_Plain: - return DomainMatchingType_Keyword - case router.Domain_Regex: - return DomainMatchingType_Regex - default: - panic("unknown domain type") - } -} diff --git a/app/router/condition.go b/app/router/condition.go index 245ef6c2d225..ca4f9605b081 100644 --- a/app/router/condition.go +++ b/app/router/condition.go @@ -309,48 +309,6 @@ func (m *AttributeMatcher) Apply(ctx routing.Context) bool { return m.Match(attributes) } -// Geo attribute -type GeoAttributeMatcher interface { - Match(*Domain) bool -} - -type GeoBooleanMatcher string - -func (m GeoBooleanMatcher) Match(domain *Domain) bool { - for _, attr := range domain.Attribute { - if attr.Key == string(m) { - return true - } - } - return false -} - -type GeoAttributeList struct { - Matcher []GeoAttributeMatcher -} - -func (al *GeoAttributeList) Match(domain *Domain) bool { - for _, matcher := range al.Matcher { - if !matcher.Match(domain) { - return false - } - } - return true -} - -func (al *GeoAttributeList) IsEmpty() bool { - return len(al.Matcher) == 0 -} - -func ParseAttrs(attrs []string) *GeoAttributeList { - al := new(GeoAttributeList) - for _, attr := range attrs { - lc := strings.ToLower(attr) - al.Matcher = append(al.Matcher, GeoBooleanMatcher(lc)) - } - return al -} - type ProcessNameMatcher struct { ProcessNames []string AbsPaths []string @@ -439,4 +397,4 @@ func (m *ProcessNameMatcher) Apply(ctx routing.Context) bool { } } return false -} \ No newline at end of file +} diff --git a/app/router/condition_geoip_test.go b/app/router/condition_geoip_test.go index de289a712ad6..b712db9e1f68 100644 --- a/app/router/condition_geoip_test.go +++ b/app/router/condition_geoip_test.go @@ -1,17 +1,40 @@ package router_test import ( + "fmt" "os" "path/filepath" - "runtime" "testing" "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/infra/conf" + "github.com/xtls/xray-core/common/platform" + "github.com/xtls/xray-core/common/platform/filesystem" + "google.golang.org/protobuf/proto" ) +func getAssetPath(file string) (string, error) { + path := platform.GetAssetLocation(file) + _, err := os.Stat(path) + if os.IsNotExist(err) { + path := filepath.Join("..", "..", "resources", file) + _, err := os.Stat(path) + if os.IsNotExist(err) { + return "", fmt.Errorf("can't find %s in standard asset locations or {project_root}/resources", file) + } + if err != nil { + return "", fmt.Errorf("can't stat %s: %v", path, err) + } + return path, nil + } + if err != nil { + return "", fmt.Errorf("can't stat %s: %v", path, err) + } + + return path, nil +} + func TestGeoIPMatcher(t *testing.T) { cidrList := []*router.CIDR{ {Ip: []byte{0, 0, 0, 0}, Prefix: 8}, @@ -159,11 +182,12 @@ func TestGeoIPReverseMatcher(t *testing.T) { } func TestGeoIPMatcher4CN(t *testing.T) { - geo := "geoip:cn" - geoip, err := loadGeoIP(geo) + ips, err := loadGeoIP("CN") common.Must(err) - matcher, err := router.BuildOptimizedGeoIPMatcher(geoip) + matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{ + Cidr: ips, + }) common.Must(err) if matcher.Match([]byte{8, 8, 8, 8}) { @@ -172,11 +196,12 @@ func TestGeoIPMatcher4CN(t *testing.T) { } func TestGeoIPMatcher6US(t *testing.T) { - geo := "geoip:us" - geoip, err := loadGeoIP(geo) + ips, err := loadGeoIP("US") common.Must(err) - matcher, err := router.BuildOptimizedGeoIPMatcher(geoip) + matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{ + Cidr: ips, + }) common.Must(err) if !matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP()) { @@ -184,34 +209,37 @@ func TestGeoIPMatcher6US(t *testing.T) { } } -func loadGeoIP(geo string) (*router.GeoIP, error) { - os.Setenv("XRAY_LOCATION_ASSET", filepath.Join("..", "..", "resources")) - - geoip, err := conf.ToCidrList([]string{geo}) +func loadGeoIP(country string) ([]*router.CIDR, error) { + path, err := getAssetPath("geoip.dat") + if err != nil { + return nil, err + } + geoipBytes, err := filesystem.ReadFile(path) if err != nil { return nil, err } - if runtime.GOOS != "windows" && runtime.GOOS != "wasm" { - geoip, err = router.GetGeoIPList(geoip) - if err != nil { - return nil, err - } + var geoipList router.GeoIPList + if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil { + return nil, err } - if len(geoip) == 0 { - panic("country not found: " + geo) + for _, geoip := range geoipList.Entry { + if geoip.CountryCode == country { + return geoip.Cidr, nil + } } - return geoip[0], nil + panic("country not found: " + country) } func BenchmarkGeoIPMatcher4CN(b *testing.B) { - geo := "geoip:cn" - geoip, err := loadGeoIP(geo) + ips, err := loadGeoIP("CN") common.Must(err) - matcher, err := router.BuildOptimizedGeoIPMatcher(geoip) + matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{ + Cidr: ips, + }) common.Must(err) b.ResetTimer() @@ -222,11 +250,12 @@ func BenchmarkGeoIPMatcher4CN(b *testing.B) { } func BenchmarkGeoIPMatcher6US(b *testing.B) { - geo := "geoip:us" - geoip, err := loadGeoIP(geo) + ips, err := loadGeoIP("US") common.Must(err) - matcher, err := router.BuildOptimizedGeoIPMatcher(geoip) + matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{ + Cidr: ips, + }) common.Must(err) b.ResetTimer() diff --git a/app/router/condition_test.go b/app/router/condition_test.go index 283e672518c3..1272aef6e9f4 100644 --- a/app/router/condition_test.go +++ b/app/router/condition_test.go @@ -1,22 +1,20 @@ package router_test import ( - "os" - "path/filepath" - "runtime" "strconv" "testing" - "github.com/xtls/xray-core/app/router" . "github.com/xtls/xray-core/app/router" "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/platform/filesystem" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/protocol/http" "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/features/routing" routing_session "github.com/xtls/xray-core/features/routing/session" - "github.com/xtls/xray-core/infra/conf" + "google.golang.org/protobuf/proto" ) func withBackground() routing.Context { @@ -302,67 +300,32 @@ func TestRoutingRule(t *testing.T) { } } -func loadGeoSiteDomains(geo string) ([]*Domain, error) { - os.Setenv("XRAY_LOCATION_ASSET", filepath.Join("..", "..", "resources")) - - domains, err := conf.ParseDomainRule(geo) +func loadGeoSite(country string) ([]*Domain, error) { + path, err := getAssetPath("geosite.dat") if err != nil { return nil, err } - - if runtime.GOOS != "windows" && runtime.GOOS != "wasm" { - domains, err = router.GetDomainList(domains) - if err != nil { - return nil, err - } - } - return domains, nil -} - -func TestChinaSites(t *testing.T) { - domains, err := loadGeoSiteDomains("geosite:cn") - common.Must(err) - - acMatcher, err := NewMphMatcherGroup(domains) - common.Must(err) - - type TestCase struct { - Domain string - Output bool - } - testCases := []TestCase{ - { - Domain: "163.com", - Output: true, - }, - { - Domain: "163.com", - Output: true, - }, - { - Domain: "164.com", - Output: false, - }, - { - Domain: "164.com", - Output: false, - }, + geositeBytes, err := filesystem.ReadFile(path) + if err != nil { + return nil, err } - for i := 0; i < 1024; i++ { - testCases = append(testCases, TestCase{Domain: strconv.Itoa(i) + ".not-exists.com", Output: false}) + var geositeList GeoSiteList + if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil { + return nil, err } - for _, testCase := range testCases { - r := acMatcher.ApplyDomain(testCase.Domain) - if r != testCase.Output { - t.Error("ACDomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r) + for _, site := range geositeList.Entry { + if site.CountryCode == country { + return site.Domain, nil } } + + return nil, errors.New("country not found: " + country) } -func TestChinaSitesWithAttrs(t *testing.T) { - domains, err := loadGeoSiteDomains("geosite:google@cn") +func TestChinaSites(t *testing.T) { + domains, err := loadGeoSite("CN") common.Must(err) acMatcher, err := NewMphMatcherGroup(domains) @@ -374,11 +337,11 @@ func TestChinaSitesWithAttrs(t *testing.T) { } testCases := []TestCase{ { - Domain: "google.cn", + Domain: "163.com", Output: true, }, { - Domain: "recaptcha.net", + Domain: "163.com", Output: true, }, { @@ -404,7 +367,7 @@ func TestChinaSitesWithAttrs(t *testing.T) { } func BenchmarkMphDomainMatcher(b *testing.B) { - domains, err := loadGeoSiteDomains("geosite:cn") + domains, err := loadGeoSite("CN") common.Must(err) matcher, err := NewMphMatcherGroup(domains) @@ -449,11 +412,11 @@ func BenchmarkMultiGeoIPMatcher(b *testing.B) { var geoips []*GeoIP { - ips, err := loadGeoIP("geoip:cn") + ips, err := loadGeoIP("CN") common.Must(err) geoips = append(geoips, &GeoIP{ CountryCode: "CN", - Cidr: ips.Cidr, + Cidr: ips, }) } @@ -462,25 +425,25 @@ func BenchmarkMultiGeoIPMatcher(b *testing.B) { common.Must(err) geoips = append(geoips, &GeoIP{ CountryCode: "JP", - Cidr: ips.Cidr, + Cidr: ips, }) } { - ips, err := loadGeoIP("geoip:ca") + ips, err := loadGeoIP("CA") common.Must(err) geoips = append(geoips, &GeoIP{ CountryCode: "CA", - Cidr: ips.Cidr, + Cidr: ips, }) } { - ips, err := loadGeoIP("geoip:us") + ips, err := loadGeoIP("US") common.Must(err) geoips = append(geoips, &GeoIP{ CountryCode: "US", - Cidr: ips.Cidr, + Cidr: ips, }) } diff --git a/app/router/config.go b/app/router/config.go index 5399589bc51e..14310641cf1a 100644 --- a/app/router/config.go +++ b/app/router/config.go @@ -3,14 +3,11 @@ package router import ( "context" "regexp" - "runtime" "strings" "github.com/xtls/xray-core/common/errors" - "github.com/xtls/xray-core/common/platform/filesystem" "github.com/xtls/xray-core/features/outbound" "github.com/xtls/xray-core/features/routing" - "google.golang.org/protobuf/proto" ) type Rule struct { @@ -76,15 +73,7 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) { } if len(rr.Geoip) > 0 { - geoip := rr.Geoip - if runtime.GOOS != "windows" && runtime.GOOS != "wasm" { - var err error - geoip, err = GetGeoIPList(rr.Geoip) - if err != nil { - return nil, errors.New("failed to build geoip from mmap").Base(err) - } - } - cond, err := NewIPMatcher(geoip, MatcherAsType_Target) + cond, err := NewIPMatcher(rr.Geoip, MatcherAsType_Target) if err != nil { return nil, err } @@ -109,20 +98,11 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) { } if len(rr.Domain) > 0 { - domains := rr.Domain - if runtime.GOOS != "windows" && runtime.GOOS != "wasm" { - var err error - domains, err = GetDomainList(rr.Domain) - if err != nil { - return nil, errors.New("failed to build domains from mmap").Base(err) - } - } - - matcher, err := NewMphMatcherGroup(domains) + matcher, err := NewMphMatcherGroup(rr.Domain) if err != nil { return nil, errors.New("failed to build domain condition with MphDomainMatcher").Base(err) } - errors.LogDebug(context.Background(), "MphDomainMatcher is enabled for ", len(domains), " domain rule(s)") + errors.LogDebug(context.Background(), "MphDomainMatcher is enabled for ", len(rr.Domain), " domain rule(s)") conds.Add(matcher) } @@ -183,80 +163,3 @@ func (br *BalancingRule) Build(ohm outbound.Manager, dispatcher routing.Dispatch return nil, errors.New("unrecognized balancer type") } } - -func GetGeoIPList(ips []*GeoIP) ([]*GeoIP, error) { - geoipList := []*GeoIP{} - for _, ip := range ips { - if ip.CountryCode != "" { - val := strings.Split(ip.CountryCode, "_") - fileName := "geoip.dat" - if len(val) == 2 { - fileName = strings.ToLower(val[0]) - } - bs, err := filesystem.ReadAsset(fileName) - if err != nil { - return nil, errors.New("failed to load file: ", fileName).Base(err) - } - bs = filesystem.Find(bs, []byte(ip.CountryCode)) - - var geoip GeoIP - - if err := proto.Unmarshal(bs, &geoip); err != nil { - return nil, errors.New("failed Unmarshal :").Base(err) - } - geoipList = append(geoipList, &geoip) - - } else { - geoipList = append(geoipList, ip) - } - } - return geoipList, nil - -} - -func GetDomainList(domains []*Domain) ([]*Domain, error) { - domainList := []*Domain{} - for _, domain := range domains { - val := strings.Split(domain.Value, "_") - - if len(val) >= 2 { - - fileName := val[0] - code := val[1] - - bs, err := filesystem.ReadAsset(fileName) - if err != nil { - return nil, errors.New("failed to load file: ", fileName).Base(err) - } - bs = filesystem.Find(bs, []byte(code)) - var geosite GeoSite - - if err := proto.Unmarshal(bs, &geosite); err != nil { - return nil, errors.New("failed Unmarshal :").Base(err) - } - - // parse attr - if len(val) == 3 { - siteWithAttr := strings.Split(val[2], ",") - attrs := ParseAttrs(siteWithAttr) - - if !attrs.IsEmpty() { - filteredDomains := make([]*Domain, 0, len(domains)) - for _, domain := range geosite.Domain { - if attrs.Match(domain) { - filteredDomains = append(filteredDomains, domain) - } - } - geosite.Domain = filteredDomains - } - - } - - domainList = append(domainList, geosite.Domain...) - - } else { - domainList = append(domainList, domain) - } - } - return domainList, nil -} diff --git a/common/platform/filesystem/asset_tools.go b/common/platform/filesystem/asset_tools.go deleted file mode 100644 index 45316f8084e6..000000000000 --- a/common/platform/filesystem/asset_tools.go +++ /dev/null @@ -1,52 +0,0 @@ -package filesystem - -func DecodeVarint(buf []byte) (x uint64, n int) { - for shift := uint(0); shift < 64; shift += 7 { - if n >= len(buf) { - return 0, 0 - } - b := uint64(buf[n]) - n++ - x |= (b & 0x7F) << shift - if (b & 0x80) == 0 { - return x, n - } - } - - // The number is too large to represent in a 64-bit value. - return 0, 0 -} - -func Find(data, code []byte) []byte { - codeL := len(code) - if codeL == 0 { - return nil - } - for { - dataL := len(data) - if dataL < 2 { - return nil - } - x, y := DecodeVarint(data[1:]) - if x == 0 && y == 0 { - return nil - } - headL, bodyL := 1+y, int(x) - dataL -= headL - if dataL < bodyL { - return nil - } - data = data[headL:] - if int(data[1]) == codeL { - for i := 0; i < codeL && data[2+i] == code[i]; i++ { - if i+1 == codeL { - return data[:bodyL] - } - } - } - if dataL == bodyL { - return nil - } - data = data[bodyL:] - } -} diff --git a/common/platform/windows.go b/common/platform/windows.go index 5bd15520fa20..684ddc9c30ce 100644 --- a/common/platform/windows.go +++ b/common/platform/windows.go @@ -3,9 +3,7 @@ package platform -import ( - "path/filepath" -) +import "path/filepath" func LineSeparator() string { return "\r\n" @@ -14,7 +12,6 @@ func LineSeparator() string { // GetAssetLocation searches for `file` in the env dir and the executable dir func GetAssetLocation(file string) string { assetPath := NewEnvFlag(AssetLocation).GetValue(getExecutableDir) - return filepath.Join(assetPath, file) } diff --git a/infra/conf/dns.go b/infra/conf/dns.go index f6d56913e313..a65f0ee84a0f 100644 --- a/infra/conf/dns.go +++ b/infra/conf/dns.go @@ -80,6 +80,21 @@ func (c *NameServerConfig) UnmarshalJSON(data []byte) error { return errors.New("failed to parse name server: ", string(data)) } +func toDomainMatchingType(t router.Domain_Type) dns.DomainMatchingType { + switch t { + case router.Domain_Domain: + return dns.DomainMatchingType_Subdomain + case router.Domain_Full: + return dns.DomainMatchingType_Full + case router.Domain_Plain: + return dns.DomainMatchingType_Keyword + case router.Domain_Regex: + return dns.DomainMatchingType_Regex + default: + panic("unknown domain type") + } +} + func (c *NameServerConfig) Build() (*dns.NameServer, error) { if c.Address == nil { return nil, errors.New("NameServer address is not specified.") @@ -89,14 +104,14 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) { var originalRules []*dns.NameServer_OriginalRule for _, rule := range c.Domains { - parsedDomain, err := ParseDomainRule(rule) + parsedDomain, err := parseDomainRule(rule) if err != nil { return nil, errors.New("invalid domain rule: ", rule).Base(err) } for _, pd := range parsedDomain { domains = append(domains, &dns.NameServer_PriorityDomain{ - Type: dns.ToDomainMatchingType(pd.Type), + Type: toDomainMatchingType(pd.Type), Domain: pd.Value, }) } diff --git a/infra/conf/router.go b/infra/conf/router.go index c9f5b43bce2f..69217c7a01a6 100644 --- a/infra/conf/router.go +++ b/infra/conf/router.go @@ -203,23 +203,17 @@ func loadFile(file string) ([]byte, error) { func loadIP(file, code string) ([]*router.CIDR, error) { index := file + ":" + code if IPCache[index] == nil { + bs, err := loadFile(file) + if err != nil { + return nil, errors.New("failed to load file: ", file).Base(err) + } + bs = find(bs, []byte(code)) + if bs == nil { + return nil, errors.New("code not found in ", file, ": ", code) + } var geoip router.GeoIP - - if runtime.GOOS != "windows" && runtime.GOOS != "wasm" { - // dont pass code becuase we have country code in top level router.GeoIP - geoip = router.GeoIP{Cidr: []*router.CIDR{}} - } else { - bs, err := loadFile(file) - if err != nil { - return nil, errors.New("failed to load file: ", file).Base(err) - } - bs = filesystem.Find(bs, []byte(code)) - if bs == nil { - return nil, errors.New("code not found in ", file, ": ", code) - } - if err := proto.Unmarshal(bs, &geoip); err != nil { - return nil, errors.New("error unmarshal IP in ", file, ": ", code).Base(err) - } + if err := proto.Unmarshal(bs, &geoip); err != nil { + return nil, errors.New("error unmarshal IP in ", file, ": ", code).Base(err) } defer runtime.GC() // or debug.FreeOSMemory() return geoip.Cidr, nil // do not cache geoip @@ -231,33 +225,115 @@ func loadIP(file, code string) ([]*router.CIDR, error) { func loadSite(file, code string) ([]*router.Domain, error) { index := file + ":" + code if SiteCache[index] == nil { + bs, err := loadFile(file) + if err != nil { + return nil, errors.New("failed to load file: ", file).Base(err) + } + bs = find(bs, []byte(code)) + if bs == nil { + return nil, errors.New("list not found in ", file, ": ", code) + } var geosite router.GeoSite + if err := proto.Unmarshal(bs, &geosite); err != nil { + return nil, errors.New("error unmarshal Site in ", file, ": ", code).Base(err) + } + defer runtime.GC() // or debug.FreeOSMemory() + return geosite.Domain, nil // do not cache geosite + SiteCache[index] = &geosite + } + return SiteCache[index].Domain, nil +} - if runtime.GOOS != "windows" && runtime.GOOS != "wasm" { - // pass file:code so can build optimized matcher later - domain := router.Domain{Value: file + "_" + code} - geosite = router.GeoSite{Domain: []*router.Domain{&domain}} +func DecodeVarint(buf []byte) (x uint64, n int) { + for shift := uint(0); shift < 64; shift += 7 { + if n >= len(buf) { + return 0, 0 + } + b := uint64(buf[n]) + n++ + x |= (b & 0x7F) << shift + if (b & 0x80) == 0 { + return x, n + } + } - } else { + // The number is too large to represent in a 64-bit value. + return 0, 0 +} - bs, err := loadFile(file) - if err != nil { - return nil, errors.New("failed to load file: ", file).Base(err) - } - bs = filesystem.Find(bs, []byte(code)) - if bs == nil { - return nil, errors.New("list not found in ", file, ": ", code) - } - if err := proto.Unmarshal(bs, &geosite); err != nil { - return nil, errors.New("error unmarshal Site in ", file, ": ", code).Base(err) +func find(data, code []byte) []byte { + codeL := len(code) + if codeL == 0 { + return nil + } + for { + dataL := len(data) + if dataL < 2 { + return nil + } + x, y := DecodeVarint(data[1:]) + if x == 0 && y == 0 { + return nil + } + headL, bodyL := 1+y, int(x) + dataL -= headL + if dataL < bodyL { + return nil + } + data = data[headL:] + if int(data[1]) == codeL { + for i := 0; i < codeL && data[2+i] == code[i]; i++ { + if i+1 == codeL { + return data[:bodyL] + } } } + if dataL == bodyL { + return nil + } + data = data[bodyL:] + } +} - defer runtime.GC() // or debug.FreeOSMemory() - return geosite.Domain, nil // do not cache geosite - SiteCache[index] = &geosite +type AttributeMatcher interface { + Match(*router.Domain) bool +} + +type BooleanMatcher string + +func (m BooleanMatcher) Match(domain *router.Domain) bool { + for _, attr := range domain.Attribute { + if attr.Key == string(m) { + return true + } } - return SiteCache[index].Domain, nil + return false +} + +type AttributeList struct { + matcher []AttributeMatcher +} + +func (al *AttributeList) Match(domain *router.Domain) bool { + for _, matcher := range al.matcher { + if !matcher.Match(domain) { + return false + } + } + return true +} + +func (al *AttributeList) IsEmpty() bool { + return len(al.matcher) == 0 +} + +func parseAttrs(attrs []string) *AttributeList { + al := new(AttributeList) + for _, attr := range attrs { + lc := strings.ToLower(attr) + al.matcher = append(al.matcher, BooleanMatcher(lc)) + } + return al } func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, error) { @@ -266,7 +342,7 @@ func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, er return nil, errors.New("empty site") } country := strings.ToUpper(parts[0]) - attrs := router.ParseAttrs(parts[1:]) + attrs := parseAttrs(parts[1:]) domains, err := loadSite(file, country) if err != nil { return nil, err @@ -276,11 +352,6 @@ func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, er return domains, nil } - if runtime.GOOS != "windows" && runtime.GOOS != "wasm" { - domains[0].Value = domains[0].Value + "_" + strings.Join(parts[1:], ",") - return domains, nil - } - filteredDomains := make([]*router.Domain, 0, len(domains)) for _, domain := range domains { if attrs.Match(domain) { @@ -291,7 +362,7 @@ func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, er return filteredDomains, nil } -func ParseDomainRule(domain string) ([]*router.Domain, error) { +func parseDomainRule(domain string) ([]*router.Domain, error) { if strings.HasPrefix(domain, "geosite:") { country := strings.ToUpper(domain[8:]) domains, err := loadGeositeWithAttr("geosite.dat", country) @@ -489,7 +560,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) { if rawFieldRule.Domain != nil { for _, domain := range *rawFieldRule.Domain { - rules, err := ParseDomainRule(domain) + rules, err := parseDomainRule(domain) if err != nil { return nil, errors.New("failed to parse domain rule: ", domain).Base(err) } @@ -499,7 +570,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) { if rawFieldRule.Domains != nil { for _, domain := range *rawFieldRule.Domains { - rules, err := ParseDomainRule(domain) + rules, err := parseDomainRule(domain) if err != nil { return nil, errors.New("failed to parse domain rule: ", domain).Base(err) } From 0fc258edd3688d2458b6830a53b7b6c7ae82b9d0 Mon Sep 17 00:00:00 2001 From: Fangliding Date: Sat, 17 Jan 2026 20:14:01 +0800 Subject: [PATCH 2/2] Revert "Geofiles: Implement mmap in filesystem to reduce ram usage (#5480)" This reverts commit 5d94a62a83073bc4aa378fb7e2198e15878e90da. --- common/platform/filesystem/file.go | 26 ------------ common/platform/filesystem/file_other.go | 54 ------------------------ 2 files changed, 80 deletions(-) delete mode 100644 common/platform/filesystem/file_other.go diff --git a/common/platform/filesystem/file.go b/common/platform/filesystem/file.go index ebe9b0f10be0..e4fe2a9aaea1 100644 --- a/common/platform/filesystem/file.go +++ b/common/platform/filesystem/file.go @@ -1,12 +1,9 @@ -//go:build !windows && !wasm - package filesystem import ( "io" "os" "path/filepath" - "syscall" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/platform" @@ -19,29 +16,6 @@ var NewFileReader FileReaderFunc = func(path string) (io.ReadCloser, error) { } func ReadFile(path string) ([]byte, error) { - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - - stat, err := file.Stat() - if err != nil { - return nil, err - } - - size := stat.Size() - if size == 0 { - return []byte{}, nil - } - - // use mmap to save RAM - bs, err := syscall.Mmap(int(file.Fd()), 0, int(size), syscall.PROT_READ, syscall.MAP_SHARED) - if err == nil { - return bs, nil - } - - // fallback reader, err := NewFileReader(path) if err != nil { return nil, err diff --git a/common/platform/filesystem/file_other.go b/common/platform/filesystem/file_other.go deleted file mode 100644 index d3cbdcc7b8c6..000000000000 --- a/common/platform/filesystem/file_other.go +++ /dev/null @@ -1,54 +0,0 @@ -//go:build windows || wasm - -package filesystem - -import ( - "io" - "os" - "path/filepath" - - "github.com/xtls/xray-core/common/buf" - "github.com/xtls/xray-core/common/platform" -) - -type FileReaderFunc func(path string) (io.ReadCloser, error) - -var NewFileReader FileReaderFunc = func(path string) (io.ReadCloser, error) { - return os.Open(path) -} - -func ReadFile(path string) ([]byte, error) { - reader, err := NewFileReader(path) - if err != nil { - return nil, err - } - defer reader.Close() - - return buf.ReadAllToBytes(reader) -} - -func ReadAsset(file string) ([]byte, error) { - return ReadFile(platform.GetAssetLocation(file)) -} - -func ReadCert(file string) ([]byte, error) { - if filepath.IsAbs(file) { - return ReadFile(file) - } - return ReadFile(platform.GetCertLocation(file)) -} - -func CopyFile(dst string, src string) error { - bytes, err := ReadFile(src) - if err != nil { - return err - } - f, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY, 0o644) - if err != nil { - return err - } - defer f.Close() - - _, err = f.Write(bytes) - return err -}