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 }