diff --git a/component/cidr/ipcidr_set.go b/component/cidr/ipcidr_set.go index 00ad7259b..4bde96711 100644 --- a/component/cidr/ipcidr_set.go +++ b/component/cidr/ipcidr_set.go @@ -46,6 +46,14 @@ func (set *IpCidrSet) IsContain(ip netip.Addr) bool { return set.ToIPSet().Contains(ip.WithZone("")) } +// MatchIp implements C.IpMatcher +func (set *IpCidrSet) MatchIp(ip netip.Addr) bool { + if set.IsEmpty() { + return false + } + return set.IsContain(ip) +} + func (set *IpCidrSet) Merge() error { var b netipx.IPSetBuilder b.AddSet(set.ToIPSet()) diff --git a/component/fakeip/pool.go b/component/fakeip/pool.go index 8096a868a..e2c107225 100644 --- a/component/fakeip/pool.go +++ b/component/fakeip/pool.go @@ -35,7 +35,7 @@ type Pool struct { offset netip.Addr cycle bool mux sync.Mutex - host []C.Rule + host []C.DomainMatcher ipnet netip.Prefix store store } @@ -66,8 +66,8 @@ func (p *Pool) LookBack(ip netip.Addr) (string, bool) { // ShouldSkipped return if domain should be skipped func (p *Pool) ShouldSkipped(domain string) bool { - for _, rule := range p.host { - if match, _ := rule.Match(&C.Metadata{Host: domain}); match { + for _, matcher := range p.host { + if matcher.MatchDomain(domain) { return true } } @@ -156,7 +156,7 @@ func (p *Pool) restoreState() { type Options struct { IPNet netip.Prefix - Host []C.Rule + Host []C.DomainMatcher // Size sets the maximum number of entries in memory // and does not work if Persistence is true diff --git a/component/fakeip/pool_test.go b/component/fakeip/pool_test.go index c2e465846..1d4fa05f0 100644 --- a/component/fakeip/pool_test.go +++ b/component/fakeip/pool_test.go @@ -10,7 +10,6 @@ import ( "github.com/metacubex/mihomo/component/profile/cachefile" "github.com/metacubex/mihomo/component/trie" C "github.com/metacubex/mihomo/constant" - RP "github.com/metacubex/mihomo/rules/provider" "github.com/metacubex/bbolt" "github.com/stretchr/testify/assert" @@ -157,7 +156,7 @@ func TestPool_Skip(t *testing.T) { pools, tempfile, err := createPools(Options{ IPNet: ipnet, Size: 10, - Host: []C.Rule{RP.NewDomainSet(tree.NewDomainSet(), "")}, + Host: []C.DomainMatcher{tree.NewDomainSet()}, }) assert.Nil(t, err) defer os.Remove(tempfile) diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index 8c9f692d0..4d13ddd06 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -22,23 +22,23 @@ var ( type Dispatcher struct { enable bool sniffers map[sniffer.Sniffer]SnifferConfig - forceDomain []C.Rule - skipSrcAddress []C.Rule - skipDstAddress []C.Rule - skipDomain []C.Rule + forceDomain []C.DomainMatcher + skipSrcAddress []C.IpMatcher + skipDstAddress []C.IpMatcher + skipDomain []C.DomainMatcher skipList *lru.LruCache[netip.AddrPort, uint8] forceDnsMapping bool parsePureIp bool } func (sd *Dispatcher) shouldOverride(metadata *C.Metadata) bool { - for _, rule := range sd.skipDstAddress { - if ok, _ := rule.Match(&C.Metadata{DstIP: metadata.DstIP}); ok { + for _, matcher := range sd.skipDstAddress { + if matcher.MatchIp(metadata.DstIP) { return false } } - for _, rule := range sd.skipSrcAddress { - if ok, _ := rule.Match(&C.Metadata{DstIP: metadata.SrcIP}); ok { + for _, matcher := range sd.skipSrcAddress { + if matcher.MatchIp(metadata.SrcIP) { return false } } @@ -48,8 +48,8 @@ func (sd *Dispatcher) shouldOverride(metadata *C.Metadata) bool { if metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping { return true } - for _, rule := range sd.forceDomain { - if ok, _ := rule.Match(&C.Metadata{Host: metadata.Host}); ok { + for _, matcher := range sd.forceDomain { + if matcher.MatchDomain(metadata.Host) { return true } } @@ -112,8 +112,8 @@ func (sd *Dispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) bool return false } - for _, rule := range sd.skipDomain { - if ok, _ := rule.Match(&C.Metadata{Host: host}); ok { + for _, matcher := range sd.skipDomain { + if matcher.MatchDomain(host) { log.Debugln("[Sniffer] Skip sni[%s]", host) return false } @@ -200,10 +200,10 @@ func (sd *Dispatcher) cacheSniffFailed(metadata *C.Metadata) { type Config struct { Enable bool Sniffers map[sniffer.Type]SnifferConfig - ForceDomain []C.Rule - SkipSrcAddress []C.Rule - SkipDstAddress []C.Rule - SkipDomain []C.Rule + ForceDomain []C.DomainMatcher + SkipSrcAddress []C.IpMatcher + SkipDstAddress []C.IpMatcher + SkipDomain []C.DomainMatcher ForceDnsMapping bool ParsePureIp bool } diff --git a/component/trie/domain_set.go b/component/trie/domain_set.go index 7778d1337..3fd8041fb 100644 --- a/component/trie/domain_set.go +++ b/component/trie/domain_set.go @@ -172,6 +172,11 @@ func (ss *DomainSet) Foreach(f func(key string) bool) { }) } +// MatchDomain implements C.DomainMatcher +func (ss *DomainSet) MatchDomain(domain string) bool { + return ss.Has(domain) +} + func setBit(bm *[]uint64, i int, v int) { for i>>6 >= len(*bm) { *bm = append(*bm, 0) diff --git a/config/config.go b/config/config.go index 7197f524b..cd8eb469b 100644 --- a/config/config.go +++ b/config/config.go @@ -143,8 +143,8 @@ type DNS struct { UseSystemHosts bool NameServer []dns.NameServer Fallback []dns.NameServer - FallbackIPFilter []C.Rule - FallbackDomainFilter []C.Rule + FallbackIPFilter []C.IpMatcher + FallbackDomainFilter []C.DomainMatcher Listen string EnhancedMode C.DNSMode DefaultNameserver []dns.NameServer @@ -640,7 +640,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } config.Hosts = hosts - dnsCfg, err := parseDNS(rawCfg, hosts, rules, ruleProviders) + dnsCfg, err := parseDNS(rawCfg, hosts, ruleProviders) if err != nil { return nil, err } @@ -1297,7 +1297,7 @@ func parsePureDNSServer(server string) string { } } -func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider, respectRules bool, preferH3 bool) ([]dns.Policy, error) { +func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]providerTypes.RuleProvider, respectRules bool, preferH3 bool) ([]dns.Policy, error) { var policy []dns.Policy re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`) @@ -1350,18 +1350,18 @@ func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], rules [ if strings.HasPrefix(domain, "rule-set:") { domainSetName := domain[9:] - rule, err := parseDomainRuleSet(domainSetName, "dns.nameserver-policy", ruleProviders) + matcher, err := parseDomainRuleSet(domainSetName, "dns.nameserver-policy", ruleProviders) if err != nil { return nil, err } - policy[idx] = dns.Policy{Rule: rule, NameServers: nameservers} + policy[idx] = dns.Policy{Matcher: matcher, NameServers: nameservers} } else if strings.HasPrefix(domain, "geosite:") { country := domain[8:] - rule, err := RC.NewGEOSITE(country, "dns.nameserver-policy") + matcher, err := RC.NewGEOSITE(country, "dns.nameserver-policy") if err != nil { return nil, err } - policy[idx] = dns.Policy{Rule: rule, NameServers: nameservers} + policy[idx] = dns.Policy{Matcher: matcher, NameServers: nameservers} } else { if _, valid := trie.ValidAndSplitDomain(domain); !valid { return nil, fmt.Errorf("DNS ResoverRule invalid domain: %s", domain) @@ -1372,7 +1372,7 @@ func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], rules [ return policy, nil } -func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (*DNS, error) { +func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], ruleProviders map[string]providerTypes.RuleProvider) (*DNS, error) { cfg := rawCfg.DNS if cfg.Enable && len(cfg.NameServer) == 0 { return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty") @@ -1400,7 +1400,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul return nil, err } - if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, rules, ruleProviders, cfg.RespectRules, cfg.PreferH3); err != nil { + if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, ruleProviders, cfg.RespectRules, cfg.PreferH3); err != nil { return nil, err } @@ -1467,14 +1467,13 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul dnsCfg.FakeIPRange = pool } - var rule C.Rule if len(cfg.Fallback) != 0 { if cfg.FallbackFilter.GeoIP { - rule, err = RC.NewGEOIP(cfg.FallbackFilter.GeoIPCode, "dns.fallback-filter.geoip", false, true) + matcher, err := RC.NewGEOIP(cfg.FallbackFilter.GeoIPCode, "dns.fallback-filter.geoip", false, true) if err != nil { return nil, fmt.Errorf("load GeoIP dns fallback filter error, %w", err) } - dnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, rule) + dnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, matcher) } if len(cfg.FallbackFilter.IPCIDR) > 0 { cidrSet := cidr.NewIpCidrSet() @@ -1488,8 +1487,8 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul if err != nil { return nil, err } - rule = RP.NewIpCidrSet(cidrSet, "dns.fallback-filter.ipcidr") - dnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, rule) + matcher := cidrSet // dns.fallback-filter.ipcidr + dnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, matcher) } if len(cfg.FallbackFilter.Domain) > 0 { domainTrie := trie.New[struct{}]() @@ -1499,17 +1498,17 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul return nil, fmt.Errorf("DNS FallbackDomain[%d] format error: %w", idx, err) } } - rule = RP.NewDomainSet(domainTrie.NewDomainSet(), "dns.fallback-filter.domain") - dnsCfg.FallbackDomainFilter = append(dnsCfg.FallbackDomainFilter, rule) + matcher := domainTrie.NewDomainSet() // dns.fallback-filter.domain + dnsCfg.FallbackDomainFilter = append(dnsCfg.FallbackDomainFilter, matcher) } if len(cfg.FallbackFilter.GeoSite) > 0 { log.Warnln("replace fallback-filter.geosite with nameserver-policy, it will be removed in the future") for idx, geoSite := range cfg.FallbackFilter.GeoSite { - rule, err = RC.NewGEOSITE(geoSite, "dns.fallback-filter.geosite") + matcher, err := RC.NewGEOSITE(geoSite, "dns.fallback-filter.geosite") if err != nil { return nil, fmt.Errorf("DNS FallbackGeosite[%d] format error: %w", idx, err) } - dnsCfg.FallbackDomainFilter = append(dnsCfg.FallbackDomainFilter, rule) + dnsCfg.FallbackDomainFilter = append(dnsCfg.FallbackDomainFilter, matcher) } } } @@ -1701,8 +1700,8 @@ func parseSniffer(snifferRaw RawSniffer, ruleProviders map[string]providerTypes. return snifferConfig, nil } -func parseIPCIDR(addresses []string, cidrSet *cidr.IpCidrSet, adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (ipRules []C.Rule, err error) { - var rule C.Rule +func parseIPCIDR(addresses []string, cidrSet *cidr.IpCidrSet, adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (matchers []C.IpMatcher, err error) { + var matcher C.IpMatcher for _, ipcidr := range addresses { ipcidrLower := strings.ToLower(ipcidr) if strings.Contains(ipcidrLower, "geoip:") { @@ -1710,22 +1709,22 @@ func parseIPCIDR(addresses []string, cidrSet *cidr.IpCidrSet, adapterName string subkeys = subkeys[1:] subkeys = strings.Split(subkeys[0], ",") for _, country := range subkeys { - rule, err = RC.NewGEOIP(country, adapterName, false, false) + matcher, err = RC.NewGEOIP(country, adapterName, false, false) if err != nil { return nil, err } - ipRules = append(ipRules, rule) + matchers = append(matchers, matcher) } } else if strings.Contains(ipcidrLower, "rule-set:") { subkeys := strings.Split(ipcidr, ":") subkeys = subkeys[1:] subkeys = strings.Split(subkeys[0], ",") for _, domainSetName := range subkeys { - rule, err = parseIPRuleSet(domainSetName, adapterName, ruleProviders) + matcher, err = parseIPRuleSet(domainSetName, adapterName, ruleProviders) if err != nil { return nil, err } - ipRules = append(ipRules, rule) + matchers = append(matchers, matcher) } } else { if cidrSet == nil { @@ -1742,14 +1741,14 @@ func parseIPCIDR(addresses []string, cidrSet *cidr.IpCidrSet, adapterName string if err != nil { return nil, err } - rule = RP.NewIpCidrSet(cidrSet, adapterName) - ipRules = append(ipRules, rule) + matcher = cidrSet + matchers = append(matchers, matcher) } return } -func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (domainRules []C.Rule, err error) { - var rule C.Rule +func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (matchers []C.DomainMatcher, err error) { + var matcher C.DomainMatcher for _, domain := range domains { domainLower := strings.ToLower(domain) if strings.Contains(domainLower, "geosite:") { @@ -1757,22 +1756,22 @@ func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], adapte subkeys = subkeys[1:] subkeys = strings.Split(subkeys[0], ",") for _, country := range subkeys { - rule, err = RC.NewGEOSITE(country, adapterName) + matcher, err = RC.NewGEOSITE(country, adapterName) if err != nil { return nil, err } - domainRules = append(domainRules, rule) + matchers = append(matchers, matcher) } } else if strings.Contains(domainLower, "rule-set:") { subkeys := strings.Split(domain, ":") subkeys = subkeys[1:] subkeys = strings.Split(subkeys[0], ",") for _, domainSetName := range subkeys { - rule, err = parseDomainRuleSet(domainSetName, adapterName, ruleProviders) + matcher, err = parseDomainRuleSet(domainSetName, adapterName, ruleProviders) if err != nil { return nil, err } - domainRules = append(domainRules, rule) + matchers = append(matchers, matcher) } } else { if domainTrie == nil { @@ -1785,13 +1784,13 @@ func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], adapte } } if !domainTrie.IsEmpty() { - rule = RP.NewDomainSet(domainTrie.NewDomainSet(), adapterName) - domainRules = append(domainRules, rule) + matcher = domainTrie.NewDomainSet() + matchers = append(matchers, matcher) } return } -func parseIPRuleSet(domainSetName string, adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (C.Rule, error) { +func parseIPRuleSet(domainSetName string, adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (C.IpMatcher, error) { if rp, ok := ruleProviders[domainSetName]; !ok { return nil, fmt.Errorf("not found rule-set: %s", domainSetName) } else { @@ -1806,7 +1805,7 @@ func parseIPRuleSet(domainSetName string, adapterName string, ruleProviders map[ return RP.NewRuleSet(domainSetName, adapterName, true) } -func parseDomainRuleSet(domainSetName string, adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (C.Rule, error) { +func parseDomainRuleSet(domainSetName string, adapterName string, ruleProviders map[string]providerTypes.RuleProvider) (C.DomainMatcher, error) { if rp, ok := ruleProviders[domainSetName]; !ok { return nil, fmt.Errorf("not found rule-set: %s", domainSetName) } else { diff --git a/constant/matcher.go b/constant/matcher.go new file mode 100644 index 000000000..107f843d9 --- /dev/null +++ b/constant/matcher.go @@ -0,0 +1,11 @@ +package constant + +import "net/netip" + +type DomainMatcher interface { + MatchDomain(domain string) bool +} + +type IpMatcher interface { + MatchIp(ip netip.Addr) bool +} diff --git a/constant/metadata.go b/constant/metadata.go index 381e2dd44..04537688f 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -133,7 +133,9 @@ type Metadata struct { Type Type `json:"type"` SrcIP netip.Addr `json:"sourceIP"` DstIP netip.Addr `json:"destinationIP"` + SrcGeoIP []string `json:"sourceGeoIP"` // can be nil if never queried, empty slice if got no result DstGeoIP []string `json:"destinationGeoIP"` // can be nil if never queried, empty slice if got no result + SrcIPASN string `json:"sourceIPASN"` DstIPASN string `json:"destinationIPASN"` SrcPort uint16 `json:"sourcePort,string"` // `,string` is used to compatible with old version json output DstPort uint16 `json:"destinationPort,string"` // `,string` is used to compatible with old version json output diff --git a/constant/rule.go b/constant/rule.go index 30cb2f8e1..31702ddc3 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -27,8 +27,6 @@ const ( ProcessNameRegex ProcessPathRegex RuleSet - DomainSet - IpCidrSet Network Uid SubRules @@ -92,10 +90,6 @@ func (rt RuleType) String() string { return "Match" case RuleSet: return "RuleSet" - case DomainSet: - return "DomainSet" - case IpCidrSet: - return "IpCidrSet" case Network: return "Network" case DSCP: diff --git a/dns/policy.go b/dns/policy.go index d872286c5..50dc17199 100644 --- a/dns/policy.go +++ b/dns/policy.go @@ -21,13 +21,13 @@ func (p domainTriePolicy) Match(domain string) []dnsClient { return nil } -type domainRulePolicy struct { - rule C.Rule +type domainMatcherPolicy struct { + matcher C.DomainMatcher dnsClients []dnsClient } -func (p domainRulePolicy) Match(domain string) []dnsClient { - if ok, _ := p.rule.Match(&C.Metadata{Host: domain}); ok { +func (p domainMatcherPolicy) Match(domain string) []dnsClient { + if p.matcher.MatchDomain(domain) { return p.dnsClients } return nil diff --git a/dns/resolver.go b/dns/resolver.go index 3cc7a41e3..232f3b336 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -42,8 +42,8 @@ type Resolver struct { hosts *trie.DomainTrie[resolver.HostValue] main []dnsClient fallback []dnsClient - fallbackDomainFilters []C.Rule - fallbackIPFilters []C.Rule + fallbackDomainFilters []C.DomainMatcher + fallbackIPFilters []C.IpMatcher group singleflight.Group[*D.Msg] cache dnsCache policy []dnsPolicy @@ -119,7 +119,7 @@ func (r *Resolver) LookupIPv6(ctx context.Context, host string) ([]netip.Addr, e func (r *Resolver) shouldIPFallback(ip netip.Addr) bool { for _, filter := range r.fallbackIPFilters { - if ok, _ := filter.Match(&C.Metadata{DstIP: ip}); ok { + if filter.MatchIp(ip) { return true } } @@ -275,7 +275,7 @@ func (r *Resolver) shouldOnlyQueryFallback(m *D.Msg) bool { } for _, df := range r.fallbackDomainFilters { - if ok, _ := df.Match(&C.Metadata{Host: domain}); ok { + if df.MatchDomain(domain) { return true } } @@ -398,7 +398,7 @@ func (ns NameServer) Equal(ns2 NameServer) bool { type Policy struct { Domain string - Rule C.Rule + Matcher C.DomainMatcher NameServers []NameServer } @@ -409,8 +409,8 @@ type Config struct { IPv6 bool IPv6Timeout uint EnhancedMode C.DNSMode - FallbackIPFilter []C.Rule - FallbackDomainFilter []C.Rule + FallbackIPFilter []C.IpMatcher + FallbackDomainFilter []C.DomainMatcher Pool *fakeip.Pool Hosts *trie.DomainTrie[resolver.HostValue] Policy []Policy @@ -495,8 +495,8 @@ func NewResolver(config Config) *Resolver { } for _, policy := range config.Policy { - if policy.Rule != nil { - insertPolicy(domainRulePolicy{rule: policy.Rule, dnsClients: cacheTransform(policy.NameServers)}) + if policy.Matcher != nil { + insertPolicy(domainMatcherPolicy{matcher: policy.Matcher, dnsClients: cacheTransform(policy.NameServers)}) } else { if triePolicy == nil { triePolicy = trie.New[[]dnsClient]() diff --git a/rules/common/geoip.go b/rules/common/geoip.go index 839253212..c4f7ecf32 100644 --- a/rules/common/geoip.go +++ b/rules/common/geoip.go @@ -3,6 +3,7 @@ package common import ( "errors" "fmt" + "net/netip" "strings" "github.com/metacubex/mihomo/component/geodata" @@ -11,6 +12,8 @@ import ( "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" + + "golang.org/x/exp/slices" ) type GEOIP struct { @@ -41,52 +44,84 @@ func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) { } if g.country == "lan" { - return ip.IsPrivate() || - ip.IsUnspecified() || - ip.IsLoopback() || - ip.IsMulticast() || - ip.IsLinkLocalUnicast() || - resolver.IsFakeBroadcastIP(ip), g.adapter - } - - for _, code := range metadata.DstGeoIP { - if g.country == code { - return true, g.adapter - } + return g.isLan(ip), g.adapter } - if !C.GeodataMode { + if C.GeodataMode { if g.isSourceIP { - codes := mmdb.IPInstance().LookupCode(ip.AsSlice()) - for _, code := range codes { - if g.country == code { - return true, g.adapter - } + if slices.Contains(metadata.SrcGeoIP, g.country) { + return true, g.adapter + } + } else { + if slices.Contains(metadata.DstGeoIP, g.country) { + return true, g.adapter } - return false, g.adapter } - - if metadata.DstGeoIP != nil { - return false, g.adapter + matcher, err := g.getIPMatcher() + if err != nil { + return false, "" } - metadata.DstGeoIP = mmdb.IPInstance().LookupCode(ip.AsSlice()) - for _, code := range metadata.DstGeoIP { - if g.country == code { - return true, g.adapter + match := matcher.Match(ip) + if match { + if g.isSourceIP { + metadata.SrcGeoIP = append(metadata.SrcGeoIP, g.country) + } else { + metadata.DstGeoIP = append(metadata.DstGeoIP, g.country) } } - return false, g.adapter + return match, g.adapter } - matcher, err := g.GetIPMatcher() - if err != nil { - return false, "" + if g.isSourceIP { + if metadata.SrcGeoIP != nil { + return slices.Contains(metadata.SrcGeoIP, g.country), g.adapter + } + } else { + if metadata.DstGeoIP != nil { + return slices.Contains(metadata.DstGeoIP, g.country), g.adapter + } + } + codes := mmdb.IPInstance().LookupCode(ip.AsSlice()) + if g.isSourceIP { + metadata.SrcGeoIP = codes + } else { + metadata.DstGeoIP = codes + } + if slices.Contains(codes, g.country) { + return true, g.adapter + } + return false, "" +} + +// MatchIp implements C.IpMatcher +func (g *GEOIP) MatchIp(ip netip.Addr) bool { + if !ip.IsValid() { + return false } - match := matcher.Match(ip) - if match && !g.isSourceIP { - metadata.DstGeoIP = append(metadata.DstGeoIP, g.country) + + if g.country == "lan" { + return g.isLan(ip) } - return match, g.adapter + + if C.GeodataMode { + matcher, err := g.getIPMatcher() + if err != nil { + return false + } + return matcher.Match(ip) + } + + codes := mmdb.IPInstance().LookupCode(ip.AsSlice()) + return slices.Contains(codes, g.country) +} + +func (g *GEOIP) isLan(ip netip.Addr) bool { + return ip.IsPrivate() || + ip.IsUnspecified() || + ip.IsLoopback() || + ip.IsMulticast() || + ip.IsLinkLocalUnicast() || + resolver.IsFakeBroadcastIP(ip) } func (g *GEOIP) Adapter() string { @@ -106,14 +141,19 @@ func (g *GEOIP) GetCountry() string { } func (g *GEOIP) GetIPMatcher() (router.IPMatcher, error) { - if g.geodata { - geoIPMatcher, err := geodata.LoadGeoIPMatcher(g.country) - if err != nil { - return nil, fmt.Errorf("[GeoIP] %w", err) - } - return geoIPMatcher, nil + if C.GeodataMode { + return g.getIPMatcher() } - return nil, errors.New("geoip country not set") + return nil, errors.New("not geodata mode") +} + +func (g *GEOIP) getIPMatcher() (router.IPMatcher, error) { + geoIPMatcher, err := geodata.LoadGeoIPMatcher(g.country) + if err != nil { + return nil, fmt.Errorf("[GeoIP] %w", err) + } + return geoIPMatcher, nil + } func (g *GEOIP) GetRecodeSize() int { @@ -141,12 +181,13 @@ func NewGEOIP(country string, adapter string, isSrc, noResolveIP bool) (*GEOIP, return geoip, nil } - geoip.geodata = true - geoIPMatcher, err := geoip.GetIPMatcher() // test load - if err != nil { - return nil, err + if C.GeodataMode { + geoIPMatcher, err := geoip.getIPMatcher() // test load + if err != nil { + return nil, err + } + log.Infoln("Finished initial GeoIP rule %s => %s, records: %d", country, adapter, geoIPMatcher.Count()) } - log.Infoln("Finished initial GeoIP rule %s => %s, records: %d", country, adapter, geoIPMatcher.Count()) return geoip, nil } diff --git a/rules/common/geosite.go b/rules/common/geosite.go index a728e9917..851bc8a43 100644 --- a/rules/common/geosite.go +++ b/rules/common/geosite.go @@ -23,15 +23,19 @@ func (gs *GEOSITE) RuleType() C.RuleType { } func (gs *GEOSITE) Match(metadata *C.Metadata) (bool, string) { - domain := metadata.RuleHost() + return gs.MatchDomain(metadata.RuleHost()), gs.adapter +} + +// MatchDomain implements C.DomainMatcher +func (gs *GEOSITE) MatchDomain(domain string) bool { if len(domain) == 0 { - return false, "" + return false } matcher, err := gs.GetDomainMatcher() if err != nil { - return false, "" + return false } - return matcher.ApplyDomain(domain), gs.adapter + return matcher.ApplyDomain(domain) } func (gs *GEOSITE) Adapter() string { diff --git a/rules/common/ipasn.go b/rules/common/ipasn.go index df4b6531c..bcff4e725 100644 --- a/rules/common/ipasn.go +++ b/rules/common/ipasn.go @@ -28,8 +28,11 @@ func (a *ASN) Match(metadata *C.Metadata) (bool, string) { result := mmdb.ASNInstance().LookupASN(ip.AsSlice()) asnNumber := strconv.FormatUint(uint64(result.AutonomousSystemNumber), 10) - if !a.isSourceIP { - metadata.DstIPASN = asnNumber + " " + result.AutonomousSystemOrganization + ipASN := asnNumber + " " + result.AutonomousSystemOrganization + if a.isSourceIP { + metadata.SrcIPASN = ipASN + } else { + metadata.DstIPASN = ipASN } match := a.asn == asnNumber diff --git a/rules/provider/domain_set.go b/rules/provider/domain_set.go deleted file mode 100644 index 573dd1057..000000000 --- a/rules/provider/domain_set.go +++ /dev/null @@ -1,40 +0,0 @@ -package provider - -import ( - "github.com/metacubex/mihomo/component/trie" - C "github.com/metacubex/mihomo/constant" -) - -type DomainSet struct { - *domainStrategy - adapter string -} - -func (d *DomainSet) ProviderNames() []string { - return nil -} - -func (d *DomainSet) RuleType() C.RuleType { - return C.DomainSet -} - -func (d *DomainSet) Match(metadata *C.Metadata) (bool, string) { - return d.domainStrategy.Match(metadata), d.adapter -} - -func (d *DomainSet) Adapter() string { - return d.adapter -} - -func (d *DomainSet) Payload() string { - return "" -} - -func NewDomainSet(domainSet *trie.DomainSet, adapter string) *DomainSet { - return &DomainSet{ - domainStrategy: &domainStrategy{domainSet: domainSet}, - adapter: adapter, - } -} - -var _ C.Rule = (*DomainSet)(nil) diff --git a/rules/provider/ipcidr_set.go b/rules/provider/ipcidr_set.go deleted file mode 100644 index 348b6ab0a..000000000 --- a/rules/provider/ipcidr_set.go +++ /dev/null @@ -1,40 +0,0 @@ -package provider - -import ( - "github.com/metacubex/mihomo/component/cidr" - C "github.com/metacubex/mihomo/constant" -) - -type IpCidrSet struct { - *ipcidrStrategy - adapter string -} - -func (d *IpCidrSet) ProviderNames() []string { - return nil -} - -func (d *IpCidrSet) RuleType() C.RuleType { - return C.IpCidrSet -} - -func (d *IpCidrSet) Match(metadata *C.Metadata) (bool, string) { - return d.ipcidrStrategy.Match(metadata), d.adapter -} - -func (d *IpCidrSet) Adapter() string { - return d.adapter -} - -func (d *IpCidrSet) Payload() string { - return "" -} - -func NewIpCidrSet(cidrSet *cidr.IpCidrSet, adapter string) *IpCidrSet { - return &IpCidrSet{ - ipcidrStrategy: &ipcidrStrategy{cidrSet: cidrSet}, - adapter: adapter, - } -} - -var _ C.Rule = (*IpCidrSet)(nil) diff --git a/rules/provider/rule_set.go b/rules/provider/rule_set.go index 04ab9943e..23864e124 100644 --- a/rules/provider/rule_set.go +++ b/rules/provider/rule_set.go @@ -1,6 +1,8 @@ package provider import ( + "net/netip" + C "github.com/metacubex/mihomo/constant" P "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/rules/common" @@ -35,6 +37,18 @@ func (rs *RuleSet) Match(metadata *C.Metadata) (bool, string) { return false, "" } +// MatchDomain implements C.DomainMatcher +func (rs *RuleSet) MatchDomain(domain string) bool { + ok, _ := rs.Match(&C.Metadata{Host: domain}) + return ok +} + +// MatchIp implements C.IpMatcher +func (rs *RuleSet) MatchIp(ip netip.Addr) bool { + ok, _ := rs.Match(&C.Metadata{DstIP: ip}) + return ok +} + func (rs *RuleSet) Adapter() string { return rs.adapter }