From c63a851bba09856adfc60f2a856eedc56b500d62 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 4 Oct 2024 13:19:41 +0800 Subject: [PATCH] feat: add `direct-nameserver` and `direct-nameserver-follow-policy` in `dns` section --- adapter/outbound/direct.go | 4 +-- adapter/outbound/util.go | 10 +++---- adapter/outbound/wireguard.go | 4 +-- component/dialer/dialer.go | 22 +++++--------- component/resolver/resolver.go | 53 +++------------------------------- config/config.go | 47 ++++++++++++++++++------------ dns/patch_android.go | 3 -- dns/resolver.go | 45 +++++++++++++++++++++++++---- dns/system.go | 2 +- docs/config.yaml | 11 +++++-- hub/executor/executor.go | 22 ++++++++++---- 11 files changed, 114 insertions(+), 109 deletions(-) diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index 15f081f2..9ee237fa 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -32,7 +32,7 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ... return nil, err } } - opts = append(opts, dialer.WithResolver(resolver.DefaultResolver)) + opts = append(opts, dialer.WithResolver(resolver.DirectHostResolver)) c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...) if err != nil { return nil, err @@ -49,7 +49,7 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, } // net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr if !metadata.Resolved() { - ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DefaultResolver) + ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DirectHostResolver) if err != nil { return nil, errors.New("can't resolve ip") } diff --git a/adapter/outbound/util.go b/adapter/outbound/util.go index 2c85c7c8..9f0636a6 100644 --- a/adapter/outbound/util.go +++ b/adapter/outbound/util.go @@ -55,7 +55,7 @@ func resolveUDPAddr(ctx context.Context, network, address string) (*net.UDPAddr, return nil, err } - ip, err := resolver.ResolveProxyServerHost(ctx, host) + ip, err := resolver.ResolveIPWithResolver(ctx, host, resolver.ProxyServerHostResolver) if err != nil { return nil, err } @@ -71,12 +71,12 @@ func resolveUDPAddrWithPrefer(ctx context.Context, network, address string, pref var fallback netip.Addr switch prefer { case C.IPv4Only: - ip, err = resolver.ResolveIPv4ProxyServerHost(ctx, host) + ip, err = resolver.ResolveIPv4WithResolver(ctx, host, resolver.ProxyServerHostResolver) case C.IPv6Only: - ip, err = resolver.ResolveIPv6ProxyServerHost(ctx, host) + ip, err = resolver.ResolveIPv6WithResolver(ctx, host, resolver.ProxyServerHostResolver) case C.IPv6Prefer: var ips []netip.Addr - ips, err = resolver.LookupIPProxyServerHost(ctx, host) + ips, err = resolver.LookupIPWithResolver(ctx, host, resolver.ProxyServerHostResolver) if err == nil { for _, addr := range ips { if addr.Is6() { @@ -92,7 +92,7 @@ func resolveUDPAddrWithPrefer(ctx context.Context, network, address string, pref default: // C.IPv4Prefer, C.DualStack and other var ips []netip.Addr - ips, err = resolver.LookupIPProxyServerHost(ctx, host) + ips, err = resolver.LookupIPWithResolver(ctx, host, resolver.ProxyServerHostResolver) if err == nil { for _, addr := range ips { if addr.Is4() { diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 3928ab1b..03145c37 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -44,7 +44,7 @@ type WireGuard struct { device wireguardGoDevice tunDevice wireguard.Device dialer proxydialer.SingDialer - resolver *dns.Resolver + resolver resolver.Resolver refP *refProxyAdapter initOk atomic.Bool @@ -296,7 +296,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { for i := range nss { nss[i].ProxyAdapter = refP } - outbound.resolver, _ = dns.NewResolver(dns.Config{ + outbound.resolver = dns.NewResolver(dns.Config{ Main: nss, IPv6: has6, }) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 3dfd3159..4fd051ef 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -340,26 +340,18 @@ func parseAddr(ctx context.Context, network, address string, preferResolver reso return nil, "-1", err } + if preferResolver == nil { + preferResolver = resolver.ProxyServerHostResolver + } + var ips []netip.Addr switch network { case "tcp4", "udp4": - if preferResolver == nil { - ips, err = resolver.LookupIPv4ProxyServerHost(ctx, host) - } else { - ips, err = resolver.LookupIPv4WithResolver(ctx, host, preferResolver) - } + ips, err = resolver.LookupIPv4WithResolver(ctx, host, preferResolver) case "tcp6", "udp6": - if preferResolver == nil { - ips, err = resolver.LookupIPv6ProxyServerHost(ctx, host) - } else { - ips, err = resolver.LookupIPv6WithResolver(ctx, host, preferResolver) - } + ips, err = resolver.LookupIPv6WithResolver(ctx, host, preferResolver) default: - if preferResolver == nil { - ips, err = resolver.LookupIPProxyServerHost(ctx, host) - } else { - ips, err = resolver.LookupIPWithResolver(ctx, host, preferResolver) - } + ips, err = resolver.LookupIPWithResolver(ctx, host, preferResolver) } if err != nil { return nil, "-1", fmt.Errorf("dns resolve failed: %w", err) diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index 8b180c1e..1eb3d642 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -19,9 +19,12 @@ var ( // DefaultResolver aim to resolve ip DefaultResolver Resolver - // ProxyServerHostResolver resolve ip to proxies server host + // ProxyServerHostResolver resolve ip for proxies server host, only nil when DefaultResolver is nil ProxyServerHostResolver Resolver + // DirectHostResolver resolve ip for direct outbound host, only nil when DefaultResolver is nil + DirectHostResolver Resolver + // SystemResolver always using system dns, and was init in dns module SystemResolver Resolver @@ -193,58 +196,10 @@ func ResolveIP(ctx context.Context, host string) (netip.Addr, error) { return ResolveIPWithResolver(ctx, host, DefaultResolver) } -// ResolveIPv4ProxyServerHost proxies server host only -func ResolveIPv4ProxyServerHost(ctx context.Context, host string) (netip.Addr, error) { - if ProxyServerHostResolver != nil { - return ResolveIPv4WithResolver(ctx, host, ProxyServerHostResolver) - } - return ResolveIPv4(ctx, host) -} - -// ResolveIPv6ProxyServerHost proxies server host only -func ResolveIPv6ProxyServerHost(ctx context.Context, host string) (netip.Addr, error) { - if ProxyServerHostResolver != nil { - return ResolveIPv6WithResolver(ctx, host, ProxyServerHostResolver) - } - return ResolveIPv6(ctx, host) -} - -// ResolveProxyServerHost proxies server host only -func ResolveProxyServerHost(ctx context.Context, host string) (netip.Addr, error) { - if ProxyServerHostResolver != nil { - return ResolveIPWithResolver(ctx, host, ProxyServerHostResolver) - } - return ResolveIP(ctx, host) -} - -func LookupIPv6ProxyServerHost(ctx context.Context, host string) ([]netip.Addr, error) { - if ProxyServerHostResolver != nil { - return LookupIPv6WithResolver(ctx, host, ProxyServerHostResolver) - } - return LookupIPv6(ctx, host) -} - -func LookupIPv4ProxyServerHost(ctx context.Context, host string) ([]netip.Addr, error) { - if ProxyServerHostResolver != nil { - return LookupIPv4WithResolver(ctx, host, ProxyServerHostResolver) - } - return LookupIPv4(ctx, host) -} - -func LookupIPProxyServerHost(ctx context.Context, host string) ([]netip.Addr, error) { - if ProxyServerHostResolver != nil { - return LookupIPWithResolver(ctx, host, ProxyServerHostResolver) - } - return LookupIP(ctx, host) -} - func ResetConnection() { if DefaultResolver != nil { go DefaultResolver.ResetConnection() } - if ProxyServerHostResolver != nil { - go ProxyServerHostResolver.ResetConnection() - } } func SortationAddr(ips []netip.Addr) (ipv4s, ipv6s []netip.Addr) { diff --git a/config/config.go b/config/config.go index 3117853c..3ca57a45 100644 --- a/config/config.go +++ b/config/config.go @@ -160,6 +160,8 @@ type DNS struct { Hosts *trie.DomainTrie[resolver.HostValue] NameServerPolicy []dns.Policy ProxyServerNameserver []dns.NameServer + DirectNameServer []dns.NameServer + DirectFollowPolicy bool } // Profile config @@ -203,25 +205,27 @@ type RawCors struct { } type RawDNS struct { - Enable bool `yaml:"enable" json:"enable"` - PreferH3 bool `yaml:"prefer-h3" json:"prefer-h3"` - IPv6 bool `yaml:"ipv6" json:"ipv6"` - IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"` - UseHosts bool `yaml:"use-hosts" json:"use-hosts"` - UseSystemHosts bool `yaml:"use-system-hosts" json:"use-system-hosts"` - RespectRules bool `yaml:"respect-rules" json:"respect-rules"` - NameServer []string `yaml:"nameserver" json:"nameserver"` - Fallback []string `yaml:"fallback" json:"fallback"` - FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"` - Listen string `yaml:"listen" json:"listen"` - EnhancedMode C.DNSMode `yaml:"enhanced-mode" json:"enhanced-mode"` - FakeIPRange string `yaml:"fake-ip-range" json:"fake-ip-range"` - FakeIPFilter []string `yaml:"fake-ip-filter" json:"fake-ip-filter"` - FakeIPFilterMode C.FilterMode `yaml:"fake-ip-filter-mode" json:"fake-ip-filter-mode"` - DefaultNameserver []string `yaml:"default-nameserver" json:"default-nameserver"` - CacheAlgorithm string `yaml:"cache-algorithm" json:"cache-algorithm"` - NameServerPolicy *orderedmap.OrderedMap[string, any] `yaml:"nameserver-policy" json:"nameserver-policy"` - ProxyServerNameserver []string `yaml:"proxy-server-nameserver" json:"proxy-server-nameserver"` + Enable bool `yaml:"enable" json:"enable"` + PreferH3 bool `yaml:"prefer-h3" json:"prefer-h3"` + IPv6 bool `yaml:"ipv6" json:"ipv6"` + IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"` + UseHosts bool `yaml:"use-hosts" json:"use-hosts"` + UseSystemHosts bool `yaml:"use-system-hosts" json:"use-system-hosts"` + RespectRules bool `yaml:"respect-rules" json:"respect-rules"` + NameServer []string `yaml:"nameserver" json:"nameserver"` + Fallback []string `yaml:"fallback" json:"fallback"` + FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"` + Listen string `yaml:"listen" json:"listen"` + EnhancedMode C.DNSMode `yaml:"enhanced-mode" json:"enhanced-mode"` + FakeIPRange string `yaml:"fake-ip-range" json:"fake-ip-range"` + FakeIPFilter []string `yaml:"fake-ip-filter" json:"fake-ip-filter"` + FakeIPFilterMode C.FilterMode `yaml:"fake-ip-filter-mode" json:"fake-ip-filter-mode"` + DefaultNameserver []string `yaml:"default-nameserver" json:"default-nameserver"` + CacheAlgorithm string `yaml:"cache-algorithm" json:"cache-algorithm"` + NameServerPolicy *orderedmap.OrderedMap[string, any] `yaml:"nameserver-policy" json:"nameserver-policy"` + ProxyServerNameserver []string `yaml:"proxy-server-nameserver" json:"proxy-server-nameserver"` + DirectNameServer []string `yaml:"direct-nameserver" json:"direct-nameserver"` + DirectNameServerFollowPolicy bool `yaml:"direct-nameserver-follow-policy" json:"direct-nameserver-follow-policy"` } type RawFallbackFilter struct { @@ -1423,6 +1427,11 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul return nil, err } + if dnsCfg.DirectNameServer, err = parseNameServer(cfg.DirectNameServer, false, cfg.PreferH3); err != nil { + return nil, err + } + dnsCfg.DirectFollowPolicy = cfg.DirectNameServerFollowPolicy + if len(cfg.DefaultNameserver) == 0 { return nil, errors.New("default nameserver should have at least one nameserver") } diff --git a/dns/patch_android.go b/dns/patch_android.go index 5a98e86c..8e744fcd 100644 --- a/dns/patch_android.go +++ b/dns/patch_android.go @@ -12,9 +12,6 @@ func FlushCacheWithDefaultResolver() { if r := resolver.DefaultResolver; r != nil { r.ClearCache() } - if r := resolver.ProxyServerHostResolver; r != nil { - r.ClearCache() - } if r := resolver.SystemResolver; r != nil { r.ClearCache() } diff --git a/dns/resolver.go b/dns/resolver.go index ea8888cc..9f7e28f3 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -427,6 +427,8 @@ type Config struct { Main, Fallback []NameServer Default []NameServer ProxyServer []NameServer + DirectServer []NameServer + DirectFollowPolicy bool IPv6 bool IPv6Timeout uint EnhancedMode C.DNSMode @@ -446,7 +448,25 @@ func (config Config) newCache() dnsCache { } } -func NewResolver(config Config) (r *Resolver, pr *Resolver) { +type Resolvers struct { + *Resolver + ProxyResolver *Resolver + DirectResolver *Resolver +} + +func (rs Resolvers) ClearCache() { + rs.Resolver.ClearCache() + rs.ProxyResolver.ClearCache() + rs.DirectResolver.ClearCache() +} + +func (rs Resolvers) ResetConnection() { + rs.Resolver.ResetConnection() + rs.ProxyResolver.ResetConnection() + rs.DirectResolver.ResetConnection() +} + +func NewResolver(config Config) (rs Resolvers) { defaultResolver := &Resolver{ main: transform(config.Default, nil), cache: config.newCache(), @@ -480,7 +500,7 @@ func NewResolver(config Config) (r *Resolver, pr *Resolver) { return } - r = &Resolver{ + r := &Resolver{ ipv6: config.IPv6, main: cacheTransform(config.Main), cache: config.newCache(), @@ -488,9 +508,10 @@ func NewResolver(config Config) (r *Resolver, pr *Resolver) { ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond, } r.defaultResolver = defaultResolver + rs.Resolver = r if len(config.ProxyServer) != 0 { - pr = &Resolver{ + rs.ProxyResolver = &Resolver{ ipv6: config.IPv6, main: cacheTransform(config.ProxyServer), cache: config.newCache(), @@ -499,8 +520,20 @@ func NewResolver(config Config) (r *Resolver, pr *Resolver) { } } + if len(config.DirectServer) != 0 { + rs.DirectResolver = &Resolver{ + ipv6: config.IPv6, + main: cacheTransform(config.DirectServer), + cache: config.newCache(), + hosts: config.Hosts, + ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond, + } + } + if len(config.Fallback) != 0 { r.fallback = cacheTransform(config.Fallback) + r.fallbackIPFilters = config.FallbackIPFilter + r.fallbackDomainFilters = config.FallbackDomainFilter } if len(config.Policy) != 0 { @@ -529,9 +562,11 @@ func NewResolver(config Config) (r *Resolver, pr *Resolver) { } } insertPolicy(nil) + + if rs.DirectResolver != nil && config.DirectFollowPolicy { + rs.DirectResolver.policy = r.policy + } } - r.fallbackIPFilters = config.FallbackIPFilter - r.fallbackDomainFilters = config.FallbackDomainFilter return } diff --git a/dns/system.go b/dns/system.go index f05cb0f3..9fb803dd 100644 --- a/dns/system.go +++ b/dns/system.go @@ -61,7 +61,7 @@ func newSystemClient() *systemClient { } func init() { - r, _ := NewResolver(Config{}) + r := NewResolver(Config{}) c := newSystemClient() c.defaultNS = transform([]NameServer{{Addr: "114.114.114.114:53"}, {Addr: "8.8.8.8:53"}}, nil) r.main = []dnsClient{c} diff --git a/docs/config.yaml b/docs/config.yaml index 15bcf607..e75e5bd5 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -294,10 +294,15 @@ dns: # - tcp://1.1.1.1 # - 'tcp://1.1.1.1#ProxyGroupName' # 指定 DNS 过代理查询,ProxyGroupName 为策略组名或节点名,过代理配置优先于配置出口网卡,当找不到策略组或节点名则设置为出口网卡 - # 专用于节点域名解析的 DNS 服务器,非必要配置项 + # 专用于节点域名解析的 DNS 服务器,非必要配置项,如果不填则遵循nameserver-policy、nameserver和fallback的配置 # proxy-server-nameserver: - # - https://dns.google/dns-query - # - tls://one.one.one.one + # - https://dns.google/dns-query + # - tls://one.one.one.one + + # 专用于direct出口域名解析的 DNS 服务器,非必要配置项,如果不填则遵循nameserver-policy、nameserver和fallback的配置 + # direct-nameserver: + # - system:// + # direct-nameserver-follow-policy: false # 是否遵循nameserver-policy,默认为不遵守,仅当direct-nameserver不为空时生效 # 配置 fallback 使用条件 # fallback-filter: diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 10ea21b0..b8d9cddb 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -235,6 +235,8 @@ func updateDNS(c *config.DNS, generalIPv6 bool) { resolver.DefaultResolver = nil resolver.DefaultHostMapper = nil resolver.DefaultLocalServer = nil + resolver.ProxyServerHostResolver = nil + resolver.DirectHostResolver = nil dns.ReCreateServer("", nil, nil) return } @@ -251,10 +253,12 @@ func updateDNS(c *config.DNS, generalIPv6 bool) { Default: c.DefaultNameserver, Policy: c.NameServerPolicy, ProxyServer: c.ProxyServerNameserver, + DirectServer: c.DirectNameServer, + DirectFollowPolicy: c.DirectFollowPolicy, CacheAlgorithm: c.CacheAlgorithm, } - r, pr := dns.NewResolver(cfg) + r := dns.NewResolver(cfg) m := dns.NewEnhancer(cfg) // reuse cache of old host mapper @@ -264,14 +268,22 @@ func updateDNS(c *config.DNS, generalIPv6 bool) { resolver.DefaultResolver = r resolver.DefaultHostMapper = m - resolver.DefaultLocalServer = dns.NewLocalServer(r, m) + resolver.DefaultLocalServer = dns.NewLocalServer(r.Resolver, m) resolver.UseSystemHosts = c.UseSystemHosts - if pr.Invalid() { - resolver.ProxyServerHostResolver = pr + if r.ProxyResolver.Invalid() { + resolver.ProxyServerHostResolver = r.ProxyResolver + } else { + resolver.ProxyServerHostResolver = r.Resolver } - dns.ReCreateServer(c.Listen, r, m) + if r.DirectResolver.Invalid() { + resolver.DirectHostResolver = r.DirectResolver + } else { + resolver.DirectHostResolver = r.Resolver + } + + dns.ReCreateServer(c.Listen, r.Resolver, m) } func updateHosts(tree *trie.DomainTrie[resolver.HostValue]) {