From e6f29de0329296bc42e2e273881b91eaaa5a0900 Mon Sep 17 00:00:00 2001 From: user0988234421 Date: Thu, 14 Dec 2023 18:39:54 +0800 Subject: [PATCH] feat(fakeip-rules): add fakeip-rules as fakip-filter add fakeip-rules in config.yaml: fakeip-rules: - RULE-SET,Youku,DIRECT - RULE-SET,Netflix,Proxy - DOMAIN,dl.google.com,DIRECT fakeip-rules works as `rules` filed, but only accept the types: DOMAIN, DOMAIN-SUFFIX, DOMAIN-KEYWORD, RULE-SET, GEOSITE, MATCH In the rules, only `DIRECT` target will resolve the real-ip, other targets will resolve the fake-ip --- component/fakeip/pool.go | 37 ++++++++++++++------- config/config.go | 69 ++++++++++++++++++++++++++++++++++++++++ hub/executor/executor.go | 1 + 3 files changed, 95 insertions(+), 12 deletions(-) diff --git a/component/fakeip/pool.go b/component/fakeip/pool.go index 2b06fc0b..4e670062 100644 --- a/component/fakeip/pool.go +++ b/component/fakeip/pool.go @@ -9,6 +9,8 @@ import ( "github.com/metacubex/mihomo/common/nnip" "github.com/metacubex/mihomo/component/profile/cachefile" "github.com/metacubex/mihomo/component/trie" + C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/log" ) const ( @@ -29,15 +31,16 @@ type store interface { // Pool is an implementation about fake ip generator without storage type Pool struct { - gateway netip.Addr - first netip.Addr - last netip.Addr - offset netip.Addr - cycle bool - mux sync.Mutex - host *trie.DomainTrie[struct{}] - ipnet netip.Prefix - store store + gateway netip.Addr + first netip.Addr + last netip.Addr + offset netip.Addr + cycle bool + mux sync.Mutex + host *trie.DomainTrie[struct{}] + ipnet netip.Prefix + store store + FakeipRules []C.Rule } // Lookup return a fake ip with host @@ -66,10 +69,20 @@ func (p *Pool) LookBack(ip netip.Addr) (string, bool) { // ShouldSkipped return if domain should be skipped func (p *Pool) ShouldSkipped(domain string) bool { - if p.host == nil { - return false + + if p.host != nil && p.host.Search(domain) != nil { + return true } - return p.host.Search(domain) != nil + + metadata := C.Metadata{Host: domain} + for _, rule := range p.FakeipRules { + if matched, ada := rule.Match(&metadata); matched { + log.Debugln("`%s` matched fakeip-rules %s", domain, ada) + return ada == "DIRECT" + } + } + + return false } // Exist returns if given ip exists in fake-ip pool diff --git a/config/config.go b/config/config.go index 469a58ca..3cdf9e5a 100644 --- a/config/config.go +++ b/config/config.go @@ -37,6 +37,7 @@ import ( LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/log" R "github.com/metacubex/mihomo/rules" + RC "github.com/metacubex/mihomo/rules/common" RP "github.com/metacubex/mihomo/rules/provider" T "github.com/metacubex/mihomo/tunnel" @@ -179,6 +180,7 @@ type Config struct { Hosts *trie.DomainTrie[resolver.HostValue] Profile *Profile Rules []C.Rule + FakeipRules []C.Rule SubRules map[string][]C.Rule Users []auth.AuthUser Proxies map[string]C.Proxy @@ -334,6 +336,7 @@ type RawConfig struct { Proxy []map[string]any `yaml:"proxies"` ProxyGroup []map[string]any `yaml:"proxy-groups"` Rule []string `yaml:"rules"` + FakeipRules []string `yaml:"fakeip-rules"` SubRules map[string][]string `yaml:"sub-rules"` RawTLS TLS `yaml:"tls"` Listeners []map[string]any `yaml:"listeners"` @@ -555,6 +558,12 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } config.Rules = rules + fakeip_rules, err := parseFakeipRules(rawCfg.FakeipRules) + if err != nil { + return nil, err + } + config.FakeipRules = fakeip_rules + hosts, err := parseHosts(rawCfg) if err != nil { return nil, err @@ -938,6 +947,66 @@ func parseRules(rulesConfig []string, proxies map[string]C.Proxy, subRules map[s return rules, nil } +func parseFakeipRules(rulesConfig []string) ([]C.Rule, error) { + var rules []C.Rule + var parsed C.Rule + var parsedErr error + + for _, line := range rulesConfig { + rule := trimArr(strings.Split(line, ",")) + var ( + payload string + target string + ruleName = strings.ToUpper(rule[0]) + l = len(rule) + ) + + if l == 2 { + if ruleName == "MATCH" { + target = rule[1] + } else { + target = "DIRECT" + payload = rule[1] + } + } else if l == 3 { + if ruleName == "MATCH" { + return nil, fmt.Errorf("rule `%s` error: MATCH only accept 1 parameter", line) + } else { + target = rule[2] + payload = rule[1] + } + } else { + return nil, fmt.Errorf("rule `%s` formate error", line) + } + + switch ruleName { + case "DOMAIN": + parsed = RC.NewDomain(payload, target) + case "DOMAIN-SUFFIX": + parsed = RC.NewDomainSuffix(payload, target) + case "DOMAIN-KEYWORD": + parsed = RC.NewDomainKeyword(payload, target) + case "RULE-SET": + parsed, parsedErr = RP.NewRuleSet(payload, target, true) + case "GEOSITE": + parsed, parsedErr = RC.NewGEOSITE(payload, target) + case "MATCH": + parsed = RC.NewMatch(target) + parsedErr = nil + default: + parsedErr = fmt.Errorf("unsupported rule type %s", ruleName) + } + + if parsedErr != nil { + return nil, parsedErr + } + + rules = append(rules, parsed) + } + + return rules, nil +} + func parseHosts(cfg *RawConfig) (*trie.DomainTrie[resolver.HostValue], error) { tree := trie.New[resolver.HostValue]() diff --git a/hub/executor/executor.go b/hub/executor/executor.go index b9e27bfa..18e71208 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -98,6 +98,7 @@ func ApplyConfig(cfg *config.Config, force bool) { updateGeneral(cfg.General) updateNTP(cfg.NTP) updateDNS(cfg.DNS, cfg.RuleProviders, cfg.General.IPv6) + cfg.DNS.FakeIPRange.FakeipRules = cfg.FakeipRules updateListeners(cfg.General, cfg.Listeners, force) updateIPTables(cfg) updateTun(cfg.General)