github.com/metacubex/mihomo@v1.18.5/component/geodata/router/condition.go (about)

     1  package router
     2  
     3  import (
     4  	"fmt"
     5  	"net/netip"
     6  	"strings"
     7  
     8  	"github.com/metacubex/mihomo/component/cidr"
     9  	"github.com/metacubex/mihomo/component/geodata/strmatcher"
    10  	"github.com/metacubex/mihomo/component/trie"
    11  )
    12  
    13  var matcherTypeMap = map[Domain_Type]strmatcher.Type{
    14  	Domain_Plain:  strmatcher.Substr,
    15  	Domain_Regex:  strmatcher.Regex,
    16  	Domain_Domain: strmatcher.Domain,
    17  	Domain_Full:   strmatcher.Full,
    18  }
    19  
    20  func domainToMatcher(domain *Domain) (strmatcher.Matcher, error) {
    21  	matcherType, f := matcherTypeMap[domain.Type]
    22  	if !f {
    23  		return nil, fmt.Errorf("unsupported domain type %v", domain.Type)
    24  	}
    25  
    26  	matcher, err := matcherType.New(domain.Value)
    27  	if err != nil {
    28  		return nil, fmt.Errorf("failed to create domain matcher, base error: %s", err.Error())
    29  	}
    30  
    31  	return matcher, nil
    32  }
    33  
    34  type DomainMatcher interface {
    35  	ApplyDomain(string) bool
    36  }
    37  
    38  type succinctDomainMatcher struct {
    39  	set           *trie.DomainSet
    40  	otherMatchers []strmatcher.Matcher
    41  	not           bool
    42  }
    43  
    44  func (m *succinctDomainMatcher) ApplyDomain(domain string) bool {
    45  	isMatched := m.set.Has(domain)
    46  	if !isMatched {
    47  		for _, matcher := range m.otherMatchers {
    48  			isMatched = matcher.Match(domain)
    49  			if isMatched {
    50  				break
    51  			}
    52  		}
    53  	}
    54  	if m.not {
    55  		isMatched = !isMatched
    56  	}
    57  	return isMatched
    58  }
    59  
    60  func NewSuccinctMatcherGroup(domains []*Domain, not bool) (DomainMatcher, error) {
    61  	t := trie.New[struct{}]()
    62  	m := &succinctDomainMatcher{
    63  		not: not,
    64  	}
    65  	for _, d := range domains {
    66  		switch d.Type {
    67  		case Domain_Plain, Domain_Regex:
    68  			matcher, err := matcherTypeMap[d.Type].New(d.Value)
    69  			if err != nil {
    70  				return nil, err
    71  			}
    72  			m.otherMatchers = append(m.otherMatchers, matcher)
    73  
    74  		case Domain_Domain:
    75  			err := t.Insert("+."+d.Value, struct{}{})
    76  			if err != nil {
    77  				return nil, err
    78  			}
    79  
    80  		case Domain_Full:
    81  			err := t.Insert(d.Value, struct{}{})
    82  			if err != nil {
    83  				return nil, err
    84  			}
    85  		}
    86  	}
    87  	m.set = t.NewDomainSet()
    88  	return m, nil
    89  }
    90  
    91  type v2rayDomainMatcher struct {
    92  	matchers strmatcher.IndexMatcher
    93  	not      bool
    94  }
    95  
    96  func NewMphMatcherGroup(domains []*Domain, not bool) (DomainMatcher, error) {
    97  	g := strmatcher.NewMphMatcherGroup()
    98  	for _, d := range domains {
    99  		matcherType, f := matcherTypeMap[d.Type]
   100  		if !f {
   101  			return nil, fmt.Errorf("unsupported domain type %v", d.Type)
   102  		}
   103  		_, err := g.AddPattern(d.Value, matcherType)
   104  		if err != nil {
   105  			return nil, err
   106  		}
   107  	}
   108  	g.Build()
   109  	return &v2rayDomainMatcher{
   110  		matchers: g,
   111  		not:      not,
   112  	}, nil
   113  }
   114  
   115  func (m *v2rayDomainMatcher) ApplyDomain(domain string) bool {
   116  	isMatched := len(m.matchers.Match(strings.ToLower(domain))) > 0
   117  	if m.not {
   118  		isMatched = !isMatched
   119  	}
   120  	return isMatched
   121  }
   122  
   123  type GeoIPMatcher struct {
   124  	countryCode  string
   125  	reverseMatch bool
   126  	cidrSet      *cidr.IpCidrSet
   127  }
   128  
   129  func (m *GeoIPMatcher) Init(cidrs []*CIDR) error {
   130  	for _, cidr := range cidrs {
   131  		addr, ok := netip.AddrFromSlice(cidr.Ip)
   132  		if !ok {
   133  			return fmt.Errorf("error when loading GeoIP: invalid IP: %s", cidr.Ip)
   134  		}
   135  		err := m.cidrSet.AddIpCidr(netip.PrefixFrom(addr, int(cidr.Prefix)))
   136  		if err != nil {
   137  			return fmt.Errorf("error when loading GeoIP: %w", err)
   138  		}
   139  	}
   140  	return m.cidrSet.Merge()
   141  }
   142  
   143  func (m *GeoIPMatcher) SetReverseMatch(isReverseMatch bool) {
   144  	m.reverseMatch = isReverseMatch
   145  }
   146  
   147  // Match returns true if the given ip is included by the GeoIP.
   148  func (m *GeoIPMatcher) Match(ip netip.Addr) bool {
   149  	match := m.cidrSet.IsContain(ip)
   150  	if m.reverseMatch {
   151  		return !match
   152  	}
   153  	return match
   154  }
   155  
   156  // GeoIPMatcherContainer is a container for GeoIPMatchers. It keeps unique copies of GeoIPMatcher by country code.
   157  type GeoIPMatcherContainer struct {
   158  	matchers []*GeoIPMatcher
   159  }
   160  
   161  // Add adds a new GeoIP set into the container.
   162  // If the country code of GeoIP is not empty, GeoIPMatcherContainer will try to find an existing one, instead of adding a new one.
   163  func (c *GeoIPMatcherContainer) Add(geoip *GeoIP) (*GeoIPMatcher, error) {
   164  	if len(geoip.CountryCode) > 0 {
   165  		for _, m := range c.matchers {
   166  			if m.countryCode == geoip.CountryCode && m.reverseMatch == geoip.ReverseMatch {
   167  				return m, nil
   168  			}
   169  		}
   170  	}
   171  
   172  	m := &GeoIPMatcher{
   173  		countryCode:  geoip.CountryCode,
   174  		reverseMatch: geoip.ReverseMatch,
   175  		cidrSet:      cidr.NewIpCidrSet(),
   176  	}
   177  	if err := m.Init(geoip.Cidr); err != nil {
   178  		return nil, err
   179  	}
   180  	if len(geoip.CountryCode) > 0 {
   181  		c.matchers = append(c.matchers, m)
   182  	}
   183  	return m, nil
   184  }
   185  
   186  var globalGeoIPContainer GeoIPMatcherContainer
   187  
   188  type MultiGeoIPMatcher struct {
   189  	matchers []*GeoIPMatcher
   190  }
   191  
   192  func NewGeoIPMatcher(geoip *GeoIP) (*GeoIPMatcher, error) {
   193  	matcher, err := globalGeoIPContainer.Add(geoip)
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  
   198  	return matcher, nil
   199  }
   200  
   201  func (m *MultiGeoIPMatcher) ApplyIp(ip netip.Addr) bool {
   202  	for _, matcher := range m.matchers {
   203  		if matcher.Match(ip) {
   204  			return true
   205  		}
   206  	}
   207  
   208  	return false
   209  }
   210  
   211  func NewMultiGeoIPMatcher(geoips []*GeoIP) (*MultiGeoIPMatcher, error) {
   212  	var matchers []*GeoIPMatcher
   213  	for _, geoip := range geoips {
   214  		matcher, err := globalGeoIPContainer.Add(geoip)
   215  		if err != nil {
   216  			return nil, err
   217  		}
   218  		matchers = append(matchers, matcher)
   219  	}
   220  
   221  	matcher := &MultiGeoIPMatcher{
   222  		matchers: matchers,
   223  	}
   224  
   225  	return matcher, nil
   226  }