From 89cf06036d5f2ead79ba99528e815070d9c087c6 Mon Sep 17 00:00:00 2001 From: Kr328 Date: Tue, 11 Aug 2020 10:28:17 +0800 Subject: [PATCH] Feature: dns server could lookup hosts (#872) --- config/config.go | 21 ++++++++++------ dns/middleware.go | 52 ++++++++++++++++++++++++++++++++++++++++ dns/resolver.go | 4 ++++ hub/executor/executor.go | 1 + 4 files changed, 71 insertions(+), 7 deletions(-) diff --git a/config/config.go b/config/config.go index 3db0e5bd..c853cbd5 100644 --- a/config/config.go +++ b/config/config.go @@ -62,6 +62,7 @@ type DNS struct { EnhancedMode dns.EnhancedMode `yaml:"enhanced-mode"` DefaultNameserver []dns.NameServer `yaml:"default-nameserver"` FakeIPRange *fakeip.Pool + Hosts *trie.DomainTrie } // FallbackFilter config @@ -88,6 +89,7 @@ type Config struct { type RawDNS struct { Enable bool `yaml:"enable"` IPv6 bool `yaml:"ipv6"` + UseHosts bool `yaml:"use-hosts"` NameServer []string `yaml:"nameserver"` Fallback []string `yaml:"fallback"` FallbackFilter RawFallbackFilter `yaml:"fallback-filter"` @@ -152,6 +154,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { ProxyGroup: []map[string]interface{}{}, DNS: RawDNS{ Enable: false, + UseHosts: true, FakeIPRange: "198.18.0.1/16", FallbackFilter: RawFallbackFilter{ GeoIP: true, @@ -195,18 +198,18 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } config.Rules = rules - dnsCfg, err := parseDNS(rawCfg.DNS) - if err != nil { - return nil, err - } - config.DNS = dnsCfg - hosts, err := parseHosts(rawCfg) if err != nil { return nil, err } config.Hosts = hosts + dnsCfg, err := parseDNS(rawCfg.DNS, hosts) + if err != nil { + return nil, err + } + config.DNS = dnsCfg + config.Users = parseAuthentication(rawCfg.Authentication) return config, nil @@ -494,7 +497,7 @@ func parseFallbackIPCIDR(ips []string) ([]*net.IPNet, error) { return ipNets, nil } -func parseDNS(cfg RawDNS) (*DNS, error) { +func parseDNS(cfg RawDNS, hosts *trie.DomainTrie) (*DNS, error) { if cfg.Enable && len(cfg.NameServer) == 0 { return nil, fmt.Errorf("If DNS configuration is turned on, NameServer cannot be empty") } @@ -559,6 +562,10 @@ func parseDNS(cfg RawDNS) (*DNS, error) { dnsCfg.FallbackFilter.IPCIDR = fallbackip } + if cfg.UseHosts { + dnsCfg.Hosts = hosts + } + return dnsCfg, nil } diff --git a/dns/middleware.go b/dns/middleware.go index 7cafe7f9..ad04b856 100644 --- a/dns/middleware.go +++ b/dns/middleware.go @@ -1,9 +1,11 @@ package dns import ( + "net" "strings" "github.com/Dreamacro/clash/component/fakeip" + "github.com/Dreamacro/clash/component/trie" "github.com/Dreamacro/clash/log" D "github.com/miekg/dns" @@ -12,6 +14,52 @@ import ( type handler func(w D.ResponseWriter, r *D.Msg) type middleware func(next handler) handler +func withHosts(hosts *trie.DomainTrie) middleware { + return func(next handler) handler { + return func(w D.ResponseWriter, r *D.Msg) { + q := r.Question[0] + + if !isIPRequest(q) { + next(w, r) + return + } + + record := hosts.Search(strings.TrimRight(q.Name, ".")) + if record == nil { + next(w, r) + return + } + + ip := record.Data.(net.IP) + msg := r.Copy() + + if v4 := ip.To4(); v4 != nil && q.Qtype == D.TypeA { + rr := &D.A{} + rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeA, Class: D.ClassINET, Ttl: dnsDefaultTTL} + rr.A = v4 + + msg.Answer = []D.RR{rr} + } else if v6 := ip.To16(); v6 != nil && q.Qtype == D.TypeAAAA { + rr := &D.AAAA{} + rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeAAAA, Class: D.ClassINET, Ttl: dnsDefaultTTL} + rr.AAAA = v6 + + msg.Answer = []D.RR{rr} + } else { + next(w, r) + return + } + + msg.SetRcode(r, D.RcodeSuccess) + msg.Authoritative = true + msg.RecursionAvailable = true + + w.WriteMsg(msg) + return + } + } +} + func withFakeIP(fakePool *fakeip.Pool) middleware { return func(next handler) handler { return func(w D.ResponseWriter, r *D.Msg) { @@ -100,6 +148,10 @@ func compose(middlewares []middleware, endpoint handler) handler { func newHandler(resolver *Resolver) handler { middlewares := []middleware{} + if resolver.hosts != nil { + middlewares = append(middlewares, withHosts(resolver.hosts)) + } + if resolver.FakeIPEnabled() { middlewares = append(middlewares, withFakeIP(resolver.pool)) } diff --git a/dns/resolver.go b/dns/resolver.go index da7f5db8..dd3e2f47 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -14,6 +14,7 @@ import ( "github.com/Dreamacro/clash/common/picker" "github.com/Dreamacro/clash/component/fakeip" "github.com/Dreamacro/clash/component/resolver" + "github.com/Dreamacro/clash/component/trie" D "github.com/miekg/dns" "golang.org/x/sync/singleflight" @@ -37,6 +38,7 @@ type Resolver struct { ipv6 bool mapping bool fakeip bool + hosts *trie.DomainTrie pool *fakeip.Pool main []dnsClient fallback []dnsClient @@ -308,6 +310,7 @@ type Config struct { EnhancedMode EnhancedMode FallbackFilter FallbackFilter Pool *fakeip.Pool + Hosts *trie.DomainTrie } func New(config Config) *Resolver { @@ -323,6 +326,7 @@ func New(config Config) *Resolver { mapping: config.EnhancedMode == MAPPING, fakeip: config.EnhancedMode == FAKEIP, pool: config.Pool, + hosts: config.Hosts, } if len(config.Fallback) != 0 { diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 8131ad39..d2085a7b 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -113,6 +113,7 @@ func updateDNS(c *config.DNS) { IPv6: c.IPv6, EnhancedMode: c.EnhancedMode, Pool: c.FakeIPRange, + Hosts: c.Hosts, FallbackFilter: dns.FallbackFilter{ GeoIP: c.FallbackFilter.GeoIP, IPCIDR: c.FallbackFilter.IPCIDR,