From be8009c62509322682299bfbe969a62cee03f4d5 Mon Sep 17 00:00:00 2001 From: Meow <197331664+Meo597@users.noreply.github.com> Date: Fri, 19 Jun 2026 20:02:27 +0800 Subject: [PATCH] Geodata: Cleanup unneeded matchers & `domain:` ignore case (#6342) Completes https://github.com/XTLS/Xray-core/pull/6139 --- common/geodata/domain_matcher.go | 2 +- common/geodata/domain_registry.go | 22 +++++++++++++++------- common/geodata/ip_registry.go | 30 +++++++++++++++++++----------- common/geodata/rule_parser.go | 4 ++-- common/utils/weak_cache.go | 14 ++++++++++++++ 5 files changed, 51 insertions(+), 21 deletions(-) diff --git a/common/geodata/domain_matcher.go b/common/geodata/domain_matcher.go index 2c231d98..a4d3564e 100644 --- a/common/geodata/domain_matcher.go +++ b/common/geodata/domain_matcher.go @@ -220,7 +220,7 @@ func parseDomain(d *Domain) (strmatcher.Matcher, error) { case Domain_Regex: return strmatcher.Regex.New(d.Value) case Domain_Domain: - return strmatcher.Domain.New(d.Value) + return strmatcher.Domain.New(strings.ToLower(d.Value)) case Domain_Full: return strmatcher.Full.New(strings.ToLower(d.Value)) default: diff --git a/common/geodata/domain_registry.go b/common/geodata/domain_registry.go index 7b6c6527..1bcc333a 100644 --- a/common/geodata/domain_registry.go +++ b/common/geodata/domain_registry.go @@ -6,12 +6,14 @@ import ( "sync/atomic" "github.com/xtls/xray-core/common/errors" + "github.com/xtls/xray-core/common/utils" + "github.com/xtls/xray-core/common/uuid" ) type DomainRegistry struct { mu sync.Mutex factory DomainMatcherFactory - matchers []*DynamicDomainMatcher + matchers *utils.WeakCacheMap[uuid.UUID, DynamicDomainMatcher] } func (r *DomainRegistry) BuildDomainMatcher(rules []*DomainRule) (DomainMatcher, error) { @@ -24,7 +26,7 @@ func (r *DomainRegistry) BuildDomainMatcher(rules []*DomainRule) (DomainMatcher, } d := NewDynamicDomainMatcher(rules, m) - r.matchers = append(r.matchers, d) + r.matchers.Store(uuid.New(), d) return d, nil } @@ -32,15 +34,20 @@ func (r *DomainRegistry) Reload() error { r.mu.Lock() defer r.mu.Unlock() - errors.LogInfo(context.Background(), "reloading GeoSite data for ", len(r.matchers), " domain matcher(s)") + var matchers []*DynamicDomainMatcher + r.matchers.Range(func(_ uuid.UUID, matcher *DynamicDomainMatcher) bool { + matchers = append(matchers, matcher) + return true + }) + errors.LogInfo(context.Background(), "reloading GeoSite data for ", len(matchers), " domain matcher(s)") factory := newDomainMatcherFactory() type reloadEntry struct { dynamic *DynamicDomainMatcher matcher DomainMatcher } - reloaded := make([]reloadEntry, len(r.matchers)) - for i, d := range r.matchers { + reloaded := make([]reloadEntry, len(matchers)) + for i, d := range matchers { m, err := factory.BuildMatcher(d.rules) if err != nil { errors.LogErrorInner(context.Background(), err, "failed to reload GeoSite data for domain matcher ", i) @@ -52,13 +59,14 @@ func (r *DomainRegistry) Reload() error { entry.dynamic.Reload(entry.matcher) } r.factory = factory - errors.LogInfo(context.Background(), "reloaded GeoSite data for ", len(r.matchers), " domain matcher(s)") + errors.LogInfo(context.Background(), "reloaded GeoSite data for ", len(matchers), " domain matcher(s)") return nil } func newDomainRegistry() *DomainRegistry { return &DomainRegistry{ - factory: newDomainMatcherFactory(), + factory: newDomainMatcherFactory(), + matchers: utils.NewWeakCacheMap[uuid.UUID, DynamicDomainMatcher](), } } diff --git a/common/geodata/ip_registry.go b/common/geodata/ip_registry.go index 21fbdf9e..a02adf6d 100644 --- a/common/geodata/ip_registry.go +++ b/common/geodata/ip_registry.go @@ -7,25 +7,27 @@ import ( "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/utils" + "github.com/xtls/xray-core/common/uuid" ) type IPRegistry struct { - mu sync.Mutex - ipsetFactory *IPSetFactory - matchers []*DynamicIPMatcher + mu sync.Mutex + factory *IPSetFactory + matchers *utils.WeakCacheMap[uuid.UUID, DynamicIPMatcher] } func (r *IPRegistry) BuildIPMatcher(rules []*IPRule) (IPMatcher, error) { r.mu.Lock() defer r.mu.Unlock() - m, err := buildOptimizedIPMatcher(r.ipsetFactory, rules) + m, err := buildOptimizedIPMatcher(r.factory, rules) if err != nil { return nil, err } d := NewDynamicIPMatcher(rules, m) - r.matchers = append(r.matchers, d) + r.matchers.Store(uuid.New(), d) return d, nil } @@ -33,15 +35,20 @@ func (r *IPRegistry) Reload() error { r.mu.Lock() defer r.mu.Unlock() - errors.LogInfo(context.Background(), "reloading GeoIP data for ", len(r.matchers), " IP matcher(s)") + var matchers []*DynamicIPMatcher + r.matchers.Range(func(_ uuid.UUID, matcher *DynamicIPMatcher) bool { + matchers = append(matchers, matcher) + return true + }) + errors.LogInfo(context.Background(), "reloading GeoIP data for ", len(matchers), " IP matcher(s)") factory := newIPSetFactory() type reloadEntry struct { dynamic *DynamicIPMatcher matcher IPMatcher } - reloaded := make([]reloadEntry, len(r.matchers)) - for i, d := range r.matchers { + reloaded := make([]reloadEntry, len(matchers)) + for i, d := range matchers { m, err := buildOptimizedIPMatcher(factory, d.rules) if err != nil { errors.LogErrorInner(context.Background(), err, "failed to reload GeoIP data for IP matcher ", i) @@ -52,14 +59,15 @@ func (r *IPRegistry) Reload() error { for _, entry := range reloaded { entry.dynamic.Reload(entry.matcher) } - r.ipsetFactory = factory - errors.LogInfo(context.Background(), "reloaded GeoIP data for ", len(r.matchers), " IP matcher(s)") + r.factory = factory + errors.LogInfo(context.Background(), "reloaded GeoIP data for ", len(matchers), " IP matcher(s)") return nil } func newIPRegistry() *IPRegistry { return &IPRegistry{ - ipsetFactory: newIPSetFactory(), + factory: newIPSetFactory(), + matchers: utils.NewWeakCacheMap[uuid.UUID, DynamicIPMatcher](), } } diff --git a/common/geodata/rule_parser.go b/common/geodata/rule_parser.go index c05fed31..16b80b76 100644 --- a/common/geodata/rule_parser.go +++ b/common/geodata/rule_parser.go @@ -138,7 +138,7 @@ func ParseDomainRule(r string, defaultType Domain_Type) (*DomainRule, error) { } prefix := 0 - for _, ext := range [...]string{"ext:", "ext-domain:"} { + for _, ext := range [...]string{"ext:", "ext-domain:", "ext-site:"} { if strings.HasPrefix(r, ext) { prefix = len(ext) break @@ -167,7 +167,7 @@ func ParseDomainRules(rules []string, defaultType Domain_Type) ([]*DomainRule, e } prefix := 0 - for _, ext := range [...]string{"ext:", "ext-domain:"} { + for _, ext := range [...]string{"ext:", "ext-domain:", "ext-site:"} { if strings.HasPrefix(r, ext) { prefix = len(ext) break diff --git a/common/utils/weak_cache.go b/common/utils/weak_cache.go index a9aaacca..48cc8ce0 100644 --- a/common/utils/weak_cache.go +++ b/common/utils/weak_cache.go @@ -1,6 +1,7 @@ package utils import ( + "maps" "runtime" "sync" "weak" @@ -43,3 +44,16 @@ func (c *WeakCacheMap[K, V]) Store(key K, value *V) { } }, struct{}{}) } + +func (c *WeakCacheMap[K, V]) Range(f func(K, *V) bool) { + c.mu.Lock() + snapshot := maps.Clone(c.m) + c.mu.Unlock() + for k, v := range snapshot { + if value := v.Value(); value != nil { + if !f(k, value) { + break + } + } + } +}