mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2025-01-07 09:53:58 +08:00
190 lines
4.0 KiB
Go
190 lines
4.0 KiB
Go
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
|
|
Count() int
|
|
}
|
|
|
|
type succinctDomainMatcher struct {
|
|
set *trie.DomainSet
|
|
otherMatchers []strmatcher.Matcher
|
|
count int
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
}
|
|
return isMatched
|
|
}
|
|
|
|
func (m *succinctDomainMatcher) Count() int {
|
|
return m.count
|
|
}
|
|
|
|
func NewSuccinctMatcherGroup(domains []*Domain) (DomainMatcher, error) {
|
|
t := trie.New[struct{}]()
|
|
m := &succinctDomainMatcher{
|
|
count: len(domains),
|
|
}
|
|
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
|
|
count int
|
|
}
|
|
|
|
func NewMphMatcherGroup(domains []*Domain) (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,
|
|
count: len(domains),
|
|
}, nil
|
|
}
|
|
|
|
func (m *v2rayDomainMatcher) ApplyDomain(domain string) bool {
|
|
return len(m.matchers.Match(strings.ToLower(domain))) > 0
|
|
}
|
|
|
|
func (m *v2rayDomainMatcher) Count() int {
|
|
return m.count
|
|
}
|
|
|
|
type notDomainMatcher struct {
|
|
DomainMatcher
|
|
}
|
|
|
|
func (m notDomainMatcher) ApplyDomain(domain string) bool {
|
|
return !m.DomainMatcher.ApplyDomain(domain)
|
|
}
|
|
|
|
func NewNotDomainMatcherGroup(matcher DomainMatcher) DomainMatcher {
|
|
return notDomainMatcher{matcher}
|
|
}
|
|
|
|
type IPMatcher interface {
|
|
Match(ip netip.Addr) bool
|
|
Count() int
|
|
}
|
|
|
|
type geoIPMatcher struct {
|
|
cidrSet *cidr.IpCidrSet
|
|
count int
|
|
}
|
|
|
|
// Match returns true if the given ip is included by the GeoIP.
|
|
func (m *geoIPMatcher) Match(ip netip.Addr) bool {
|
|
return m.cidrSet.IsContain(ip)
|
|
}
|
|
|
|
func (m *geoIPMatcher) Count() int {
|
|
return m.count
|
|
}
|
|
|
|
func NewGeoIPMatcher(cidrList []*CIDR) (IPMatcher, error) {
|
|
m := &geoIPMatcher{
|
|
cidrSet: cidr.NewIpCidrSet(),
|
|
count: len(cidrList),
|
|
}
|
|
for _, cidr := range cidrList {
|
|
addr, ok := netip.AddrFromSlice(cidr.Ip)
|
|
if !ok {
|
|
return nil, 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 nil, fmt.Errorf("error when loading GeoIP: %w", err)
|
|
}
|
|
}
|
|
err := m.cidrSet.Merge()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return m, nil
|
|
}
|
|
|
|
type notIPMatcher struct {
|
|
IPMatcher
|
|
}
|
|
|
|
func (m notIPMatcher) Match(ip netip.Addr) bool {
|
|
return !m.IPMatcher.Match(ip)
|
|
}
|
|
|
|
func NewNotIpMatcherGroup(matcher IPMatcher) IPMatcher {
|
|
return notIPMatcher{matcher}
|
|
}
|