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