Add domain_regex rule

This commit is contained in:
世界 2022-07-04 15:51:41 +08:00
parent 8e7f215514
commit ca5b782106
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
5 changed files with 76 additions and 6 deletions

View file

@ -83,6 +83,7 @@ type DefaultRule struct {
Domain Listable[string] `json:"domain,omitempty"` Domain Listable[string] `json:"domain,omitempty"`
DomainSuffix Listable[string] `json:"domain_suffix,omitempty"` DomainSuffix Listable[string] `json:"domain_suffix,omitempty"`
DomainKeyword Listable[string] `json:"domain_keyword,omitempty"` DomainKeyword Listable[string] `json:"domain_keyword,omitempty"`
DomainRegex Listable[string] `json:"domain_regex,omitempty"`
SourceGeoIP Listable[string] `json:"source_geoip,omitempty"` SourceGeoIP Listable[string] `json:"source_geoip,omitempty"`
GeoIP Listable[string] `json:"geoip,omitempty"` GeoIP Listable[string] `json:"geoip,omitempty"`
SourceIPCIDR Listable[string] `json:"source_ip_cidr,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.Domain, other.Domain) &&
common.ComparableSliceEquals(r.DomainSuffix, other.DomainSuffix) && common.ComparableSliceEquals(r.DomainSuffix, other.DomainSuffix) &&
common.ComparableSliceEquals(r.DomainKeyword, other.DomainKeyword) && common.ComparableSliceEquals(r.DomainKeyword, other.DomainKeyword) &&
common.ComparableSliceEquals(r.DomainRegex, other.DomainRegex) &&
common.ComparableSliceEquals(r.SourceGeoIP, other.SourceGeoIP) && common.ComparableSliceEquals(r.SourceGeoIP, other.SourceGeoIP) &&
common.ComparableSliceEquals(r.GeoIP, other.GeoIP) && common.ComparableSliceEquals(r.GeoIP, other.GeoIP) &&
common.ComparableSliceEquals(r.SourceIPCIDR, other.SourceIPCIDR) && common.ComparableSliceEquals(r.SourceIPCIDR, other.SourceIPCIDR) &&

View file

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

View file

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

View file

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

View file

@ -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
}