123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- package router
- import (
- "fmt"
- "net/netip"
- "strings"
- "github.com/metacubex/mihomo/component/cidr"
- "github.com/metacubex/mihomo/component/geodata/strmatcher"
- "github.com/metacubex/mihomo/component/trie"
- )
- var matcherTypeMap = map[Domain_Type]strmatcher.Type{
- Domain_Plain: strmatcher.Substr,
- Domain_Regex: strmatcher.Regex,
- Domain_Domain: strmatcher.Domain,
- Domain_Full: strmatcher.Full,
- }
- func domainToMatcher(domain *Domain) (strmatcher.Matcher, error) {
- matcherType, f := matcherTypeMap[domain.Type]
- if !f {
- return nil, fmt.Errorf("unsupported domain type %v", domain.Type)
- }
- matcher, err := matcherType.New(domain.Value)
- if err != nil {
- return nil, fmt.Errorf("failed to create domain matcher, base error: %s", err.Error())
- }
- return matcher, nil
- }
- type DomainMatcher interface {
- ApplyDomain(string) bool
- }
- type succinctDomainMatcher struct {
- set *trie.DomainSet
- otherMatchers []strmatcher.Matcher
- not bool
- }
- func (m *succinctDomainMatcher) ApplyDomain(domain string) bool {
- isMatched := m.set.Has(domain)
- if !isMatched {
- for _, matcher := range m.otherMatchers {
- isMatched = matcher.Match(domain)
- if isMatched {
- break
- }
- }
- }
- if m.not {
- isMatched = !isMatched
- }
- return isMatched
- }
- func NewSuccinctMatcherGroup(domains []*Domain, not bool) (DomainMatcher, error) {
- t := trie.New[struct{}]()
- m := &succinctDomainMatcher{
- not: not,
- }
- for _, d := range domains {
- switch d.Type {
- case Domain_Plain, Domain_Regex:
- matcher, err := matcherTypeMap[d.Type].New(d.Value)
- if err != nil {
- return nil, err
- }
- m.otherMatchers = append(m.otherMatchers, matcher)
- case Domain_Domain:
- err := t.Insert("+."+d.Value, struct{}{})
- if err != nil {
- return nil, err
- }
- case Domain_Full:
- err := t.Insert(d.Value, struct{}{})
- if err != nil {
- return nil, err
- }
- }
- }
- m.set = t.NewDomainSet()
- return m, nil
- }
- type v2rayDomainMatcher struct {
- matchers strmatcher.IndexMatcher
- not bool
- }
- func NewMphMatcherGroup(domains []*Domain, not bool) (DomainMatcher, error) {
- g := strmatcher.NewMphMatcherGroup()
- for _, d := range domains {
- matcherType, f := matcherTypeMap[d.Type]
- if !f {
- return nil, fmt.Errorf("unsupported domain type %v", d.Type)
- }
- _, err := g.AddPattern(d.Value, matcherType)
- if err != nil {
- return nil, err
- }
- }
- g.Build()
- return &v2rayDomainMatcher{
- matchers: g,
- not: not,
- }, nil
- }
- func (m *v2rayDomainMatcher) ApplyDomain(domain string) bool {
- isMatched := len(m.matchers.Match(strings.ToLower(domain))) > 0
- if m.not {
- isMatched = !isMatched
- }
- return isMatched
- }
- type GeoIPMatcher struct {
- countryCode string
- reverseMatch bool
- cidrSet *cidr.IpCidrSet
- }
- func (m *GeoIPMatcher) Init(cidrs []*CIDR) error {
- for _, cidr := range cidrs {
- addr, ok := netip.AddrFromSlice(cidr.Ip)
- if !ok {
- return fmt.Errorf("error when loading GeoIP: invalid IP: %s", cidr.Ip)
- }
- err := m.cidrSet.AddIpCidr(netip.PrefixFrom(addr, int(cidr.Prefix)))
- if err != nil {
- return fmt.Errorf("error when loading GeoIP: %w", err)
- }
- }
- return m.cidrSet.Merge()
- }
- func (m *GeoIPMatcher) SetReverseMatch(isReverseMatch bool) {
- m.reverseMatch = isReverseMatch
- }
- // Match returns true if the given ip is included by the GeoIP.
- func (m *GeoIPMatcher) Match(ip netip.Addr) bool {
- match := m.cidrSet.IsContain(ip)
- if m.reverseMatch {
- return !match
- }
- return match
- }
- // GeoIPMatcherContainer is a container for GeoIPMatchers. It keeps unique copies of GeoIPMatcher by country code.
- type GeoIPMatcherContainer struct {
- matchers []*GeoIPMatcher
- }
- // Add adds a new GeoIP set into the container.
- // If the country code of GeoIP is not empty, GeoIPMatcherContainer will try to find an existing one, instead of adding a new one.
- func (c *GeoIPMatcherContainer) Add(geoip *GeoIP) (*GeoIPMatcher, error) {
- if len(geoip.CountryCode) > 0 {
- for _, m := range c.matchers {
- if m.countryCode == geoip.CountryCode && m.reverseMatch == geoip.ReverseMatch {
- return m, nil
- }
- }
- }
- m := &GeoIPMatcher{
- countryCode: geoip.CountryCode,
- reverseMatch: geoip.ReverseMatch,
- cidrSet: cidr.NewIpCidrSet(),
- }
- if err := m.Init(geoip.Cidr); err != nil {
- return nil, err
- }
- if len(geoip.CountryCode) > 0 {
- c.matchers = append(c.matchers, m)
- }
- return m, nil
- }
- var globalGeoIPContainer GeoIPMatcherContainer
- type MultiGeoIPMatcher struct {
- matchers []*GeoIPMatcher
- }
- func NewGeoIPMatcher(geoip *GeoIP) (*GeoIPMatcher, error) {
- matcher, err := globalGeoIPContainer.Add(geoip)
- if err != nil {
- return nil, err
- }
- return matcher, nil
- }
- func (m *MultiGeoIPMatcher) ApplyIp(ip netip.Addr) bool {
- for _, matcher := range m.matchers {
- if matcher.Match(ip) {
- return true
- }
- }
- return false
- }
- func NewMultiGeoIPMatcher(geoips []*GeoIP) (*MultiGeoIPMatcher, error) {
- var matchers []*GeoIPMatcher
- for _, geoip := range geoips {
- matcher, err := globalGeoIPContainer.Add(geoip)
- if err != nil {
- return nil, err
- }
- matchers = append(matchers, matcher)
- }
- matcher := &MultiGeoIPMatcher{
- matchers: matchers,
- }
- return matcher, nil
- }
|