소스 검색

Add domain_regex rule

世界 3 년 전
부모
커밋
ca5b782106
5개의 변경된 파일76개의 추가작업 그리고 6개의 파일을 삭제
  1. 2 0
      option/route.go
  2. 9 2
      route/rule.go
  3. 3 2
      route/rule_cidr.go
  4. 2 2
      route/rule_domain.go
  5. 60 0
      route/rule_domain_regex.go

+ 2 - 0
option/route.go

@@ -83,6 +83,7 @@ type DefaultRule struct {
 	Domain        Listable[string] `json:"domain,omitempty"`
 	DomainSuffix  Listable[string] `json:"domain_suffix,omitempty"`
 	DomainKeyword Listable[string] `json:"domain_keyword,omitempty"`
+	DomainRegex   Listable[string] `json:"domain_regex,omitempty"`
 	SourceGeoIP   Listable[string] `json:"source_geoip,omitempty"`
 	GeoIP         Listable[string] `json:"geoip,omitempty"`
 	SourceIPCIDR  Listable[string] `json:"source_ip_cidr,omitempty"`
@@ -108,6 +109,7 @@ func (r DefaultRule) Equals(other DefaultRule) bool {
 		common.ComparableSliceEquals(r.Domain, other.Domain) &&
 		common.ComparableSliceEquals(r.DomainSuffix, other.DomainSuffix) &&
 		common.ComparableSliceEquals(r.DomainKeyword, other.DomainKeyword) &&
+		common.ComparableSliceEquals(r.DomainRegex, other.DomainRegex) &&
 		common.ComparableSliceEquals(r.SourceGeoIP, other.SourceGeoIP) &&
 		common.ComparableSliceEquals(r.GeoIP, other.GeoIP) &&
 		common.ComparableSliceEquals(r.SourceIPCIDR, other.SourceIPCIDR) &&

+ 9 - 2
route/rule.go

@@ -83,6 +83,13 @@ func NewDefaultRule(router adapter.Router, logger log.Logger, options option.Def
 	if len(options.DomainKeyword) > 0 {
 		rule.items = append(rule.items, NewDomainKeywordItem(options.DomainKeyword))
 	}
+	if len(options.DomainRegex) > 0 {
+		item, err := NewDomainRegexItem(options.DomainRegex)
+		if err != nil {
+			return nil, E.Cause(err, "domain_regex")
+		}
+		rule.items = append(rule.items, item)
+	}
 	if len(options.SourceGeoIP) > 0 {
 		rule.items = append(rule.items, NewGeoIPItem(router, logger, true, options.SourceGeoIP))
 	}
@@ -92,14 +99,14 @@ func NewDefaultRule(router adapter.Router, logger log.Logger, options option.Def
 	if len(options.SourceIPCIDR) > 0 {
 		item, err := NewIPCIDRItem(true, options.SourceIPCIDR)
 		if err != nil {
-			return nil, err
+			return nil, E.Cause(err, "source_ipcidr")
 		}
 		rule.items = append(rule.items, item)
 	}
 	if len(options.IPCIDR) > 0 {
 		item, err := NewIPCIDRItem(false, options.IPCIDR)
 		if err != nil {
-			return nil, err
+			return nil, E.Cause(err, "ipcidr")
 		}
 		rule.items = append(rule.items, item)
 	}

+ 3 - 2
route/rule_cidr.go

@@ -6,6 +6,7 @@ import (
 
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing/common"
+	E "github.com/sagernet/sing/common/exceptions"
 	F "github.com/sagernet/sing/common/format"
 )
 
@@ -18,10 +19,10 @@ type IPCIDRItem struct {
 
 func NewIPCIDRItem(isSource bool, prefixStrings []string) (*IPCIDRItem, error) {
 	prefixes := make([]netip.Prefix, 0, len(prefixStrings))
-	for _, prefixString := range prefixStrings {
+	for i, prefixString := range prefixStrings {
 		prefix, err := netip.ParsePrefix(prefixString)
 		if err != nil {
-			return nil, err
+			return nil, E.Cause(err, "parse prefix [", i, "]")
 		}
 		prefixes = append(prefixes, prefix)
 	}

+ 2 - 2
route/rule_domain.go

@@ -11,8 +11,8 @@ import (
 var _ RuleItem = (*DomainItem)(nil)
 
 type DomainItem struct {
-	description string
 	matcher     *domain.Matcher
+	description string
 }
 
 func NewDomainItem(domains []string, domainSuffixes []string) *DomainItem {
@@ -41,8 +41,8 @@ func NewDomainItem(domains []string, domainSuffixes []string) *DomainItem {
 		}
 	}
 	return &DomainItem{
-		description,
 		domain.NewMatcher(domains, domainSuffixes),
+		description,
 	}
 }
 

+ 60 - 0
route/rule_domain_regex.go

@@ -0,0 +1,60 @@
+package route
+
+import (
+	"regexp"
+	"strings"
+
+	"github.com/sagernet/sing-box/adapter"
+	E "github.com/sagernet/sing/common/exceptions"
+	F "github.com/sagernet/sing/common/format"
+)
+
+var _ RuleItem = (*DomainRegexItem)(nil)
+
+type DomainRegexItem struct {
+	matchers    []*regexp.Regexp
+	description string
+}
+
+func NewDomainRegexItem(expressions []string) (*DomainRegexItem, error) {
+	matchers := make([]*regexp.Regexp, 0, len(expressions))
+	for i, regex := range expressions {
+		matcher, err := regexp.Compile(regex)
+		if err != nil {
+			return nil, E.Cause(err, "parse expression ", i)
+		}
+		matchers = append(matchers, matcher)
+	}
+	description := "domain_regex="
+	eLen := len(expressions)
+	if eLen == 1 {
+		description = expressions[0]
+	} else if eLen > 3 {
+		description = F.ToString("[", strings.Join(expressions[:3], " "), "]")
+	} else {
+		description = F.ToString("[", strings.Join(expressions, " "), "]")
+	}
+	return &DomainRegexItem{matchers, description}, nil
+}
+
+func (r *DomainRegexItem) Match(metadata *adapter.InboundContext) bool {
+	var domainHost string
+	if metadata.Domain != "" {
+		domainHost = metadata.Domain
+	} else {
+		domainHost = metadata.Destination.Fqdn
+	}
+	if domainHost == "" {
+		return false
+	}
+	for _, matcher := range r.matchers {
+		if matcher.MatchString(domainHost) {
+			return true
+		}
+	}
+	return false
+}
+
+func (r *DomainRegexItem) String() string {
+	return r.description
+}