|  | @@ -2,13 +2,12 @@ package router
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import (
 | 
	
		
			
				|  |  |  	"context"
 | 
	
		
			
				|  |  | -	"regexp"
 | 
	
		
			
				|  |  | -	"strings"
 | 
	
		
			
				|  |  |  	"sync"
 | 
	
		
			
				|  |  |  	"time"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	"v2ray.com/core/common/net"
 | 
	
		
			
				|  |  |  	"v2ray.com/core/common/protocol"
 | 
	
		
			
				|  |  | +	"v2ray.com/core/common/strmatcher"
 | 
	
		
			
				|  |  |  	"v2ray.com/core/proxy"
 | 
	
		
			
				|  |  |  )
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -73,44 +72,41 @@ type timedResult struct {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  type CachableDomainMatcher struct {
 | 
	
		
			
				|  |  |  	sync.Mutex
 | 
	
		
			
				|  |  | -	matchers []domainMatcher
 | 
	
		
			
				|  |  | +	matchers *strmatcher.MatcherGroup
 | 
	
		
			
				|  |  |  	cache    map[string]timedResult
 | 
	
		
			
				|  |  |  	lastScan time.Time
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  func NewCachableDomainMatcher() *CachableDomainMatcher {
 | 
	
		
			
				|  |  |  	return &CachableDomainMatcher{
 | 
	
		
			
				|  |  | -		matchers: make([]domainMatcher, 0, 64),
 | 
	
		
			
				|  |  | +		matchers: strmatcher.NewMatcherGroup(),
 | 
	
		
			
				|  |  |  		cache:    make(map[string]timedResult, 512),
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +var matcherTypeMap = map[Domain_Type]strmatcher.Type{
 | 
	
		
			
				|  |  | +	Domain_Plain:  strmatcher.Substr,
 | 
	
		
			
				|  |  | +	Domain_Regex:  strmatcher.Regex,
 | 
	
		
			
				|  |  | +	Domain_Domain: strmatcher.Domain,
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  func (m *CachableDomainMatcher) Add(domain *Domain) error {
 | 
	
		
			
				|  |  | -	switch domain.Type {
 | 
	
		
			
				|  |  | -	case Domain_Plain:
 | 
	
		
			
				|  |  | -		m.matchers = append(m.matchers, NewPlainDomainMatcher(domain.Value))
 | 
	
		
			
				|  |  | -	case Domain_Regex:
 | 
	
		
			
				|  |  | -		rm, err := NewRegexpDomainMatcher(domain.Value)
 | 
	
		
			
				|  |  | -		if err != nil {
 | 
	
		
			
				|  |  | -			return err
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -		m.matchers = append(m.matchers, rm)
 | 
	
		
			
				|  |  | -	case Domain_Domain:
 | 
	
		
			
				|  |  | -		m.matchers = append(m.matchers, NewSubDomainMatcher(domain.Value))
 | 
	
		
			
				|  |  | -	default:
 | 
	
		
			
				|  |  | -		return newError("unknown domain type: ", domain.Type).AtWarning()
 | 
	
		
			
				|  |  | +	matcherType, f := matcherTypeMap[domain.Type]
 | 
	
		
			
				|  |  | +	if !f {
 | 
	
		
			
				|  |  | +		return newError("unsupported domain type", domain.Type)
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	matcher, err := matcherType.New(domain.Value)
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return newError("failed to create domain matcher").Base(err)
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	m.matchers.Add(matcher)
 | 
	
		
			
				|  |  |  	return nil
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  func (m *CachableDomainMatcher) applyInternal(domain string) bool {
 | 
	
		
			
				|  |  | -	for _, matcher := range m.matchers {
 | 
	
		
			
				|  |  | -		if matcher.Apply(domain) {
 | 
	
		
			
				|  |  | -			return true
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	return false
 | 
	
		
			
				|  |  | +	return m.matchers.Match(domain) > 0
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  type cacheResult int
 | 
	
	
		
			
				|  | @@ -139,7 +135,7 @@ func (m *CachableDomainMatcher) findInCache(domain string) cacheResult {
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  func (m *CachableDomainMatcher) ApplyDomain(domain string) bool {
 | 
	
		
			
				|  |  | -	if len(m.matchers) < 64 {
 | 
	
		
			
				|  |  | +	if m.matchers.Size() < 64 {
 | 
	
		
			
				|  |  |  		return m.applyInternal(domain)
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -190,52 +186,6 @@ func (m *CachableDomainMatcher) Apply(ctx context.Context) bool {
 | 
	
		
			
				|  |  |  	return m.ApplyDomain(dest.Address.Domain())
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -type domainMatcher interface {
 | 
	
		
			
				|  |  | -	Apply(domain string) bool
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -type PlainDomainMatcher string
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -func NewPlainDomainMatcher(pattern string) PlainDomainMatcher {
 | 
	
		
			
				|  |  | -	return PlainDomainMatcher(pattern)
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -func (v PlainDomainMatcher) Apply(domain string) bool {
 | 
	
		
			
				|  |  | -	return strings.Contains(domain, string(v))
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -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 (v *RegexpDomainMatcher) Apply(domain string) bool {
 | 
	
		
			
				|  |  | -	return v.pattern.MatchString(strings.ToLower(domain))
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -type SubDomainMatcher string
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -func NewSubDomainMatcher(p string) SubDomainMatcher {
 | 
	
		
			
				|  |  | -	return SubDomainMatcher(p)
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -func (m SubDomainMatcher) Apply(domain string) bool {
 | 
	
		
			
				|  |  | -	pattern := string(m)
 | 
	
		
			
				|  |  | -	if !strings.HasSuffix(domain, pattern) {
 | 
	
		
			
				|  |  | -		return false
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -	return len(domain) == len(pattern) || domain[len(domain)-len(pattern)-1] == '.'
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  type CIDRMatcher struct {
 | 
	
		
			
				|  |  |  	cidr     *net.IPNet
 | 
	
		
			
				|  |  |  	onSource bool
 |