diff --git a/app/router/rules/config/json/fieldrule.go b/app/router/rules/config/json/fieldrule.go index 726c6167..328047d6 100644 --- a/app/router/rules/config/json/fieldrule.go +++ b/app/router/rules/config/json/fieldrule.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "net" + "regexp" "strings" v2net "github.com/v2ray/v2ray-core/common/net" @@ -41,9 +42,39 @@ func (this *StringList) Len() int { return len([]string(*this)) } +type DomainMatcher interface { + Match(domain string) bool +} + +type PlainDomainMatcher struct { + pattern string +} + +func (this *PlainDomainMatcher) Match(domain string) bool { + return strings.Contains(this.pattern, domain) +} + +type RegexpDomainMatcher struct { + pattern *regexp.Regexp +} + +func NewRegexpDomainMatcher(pattern string) (*RegexpDomainMatcher, error) { + r, err := regexp.Compile(pattern) + if err != nil { + return nil, err + } + return &RegexpDomainMatcher{ + pattern: r, + }, nil +} + +func (this *RegexpDomainMatcher) Match(domain string) bool { + return this.pattern.MatchString(domain) +} + type FieldRule struct { Rule - Domain *StringList + Domain []DomainMatcher IP []*net.IPNet Port v2net.PortRange Network v2net.NetworkList @@ -51,13 +82,13 @@ type FieldRule struct { func (this *FieldRule) Apply(dest v2net.Destination) bool { address := dest.Address() - if this.Domain != nil && this.Domain.Len() > 0 { + if len(this.Domain) > 0 { if !address.IsDomain() { return false } foundMatch := false - for _, domain := range *this.Domain { - if strings.Contains(address.Domain(), domain) { + for _, domain := range this.Domain { + if domain.Match(address.Domain()) { foundMatch = true break } @@ -117,7 +148,20 @@ func (this *FieldRule) UnmarshalJSON(data []byte) error { hasField := false if rawFieldRule.Domain != nil && rawFieldRule.Domain.Len() > 0 { - this.Domain = rawFieldRule.Domain + this.Domain = make([]DomainMatcher, rawFieldRule.Domain.Len()) + for idx, rawDomain := range *(rawFieldRule.Domain) { + var matcher DomainMatcher + if strings.HasPrefix(rawDomain, "regexp:") { + rawMatcher, err := NewRegexpDomainMatcher(rawDomain[7:]) + if err != nil { + return err + } + matcher = rawMatcher + } else { + matcher = &PlainDomainMatcher{pattern: rawDomain} + } + this.Domain[idx] = matcher + } hasField = true } diff --git a/app/router/rules/config/json/fieldrule_test.go b/app/router/rules/config/json/fieldrule_test.go index da2090f6..4653e0b2 100644 --- a/app/router/rules/config/json/fieldrule_test.go +++ b/app/router/rules/config/json/fieldrule_test.go @@ -33,9 +33,12 @@ func TestStringListParsingString(t *testing.T) { func TestDomainMatching(t *testing.T) { v2testing.Current(t) - rule := &FieldRule{ - Domain: NewStringList("v2ray.com"), - } + rawJson := `{ + "type": "field", + "domain": ["google.com", "regexp:v2ray.com$"], + "tag": "test" + }` + rule := parseRule([]byte(rawJson)) dest := v2net.NewTCPDestination(v2net.DomainAddress("www.v2ray.com", 80)) assert.Bool(rule.Apply(dest)).IsTrue() }