Feature: move hosts to the top
This commit is contained in:
parent
376516c7af
commit
4994510c87
6 changed files with 164 additions and 122 deletions
|
@ -44,7 +44,6 @@ type DNS struct {
|
|||
IPv6 bool `yaml:"ipv6"`
|
||||
NameServer []dns.NameServer `yaml:"nameserver"`
|
||||
Fallback []dns.NameServer `yaml:"fallback"`
|
||||
Hosts *trie.Trie `yaml:"-"`
|
||||
Listen string `yaml:"listen"`
|
||||
EnhancedMode dns.EnhancedMode `yaml:"enhanced-mode"`
|
||||
FakeIPRange *fakeip.Pool
|
||||
|
@ -60,6 +59,7 @@ type Config struct {
|
|||
General *General
|
||||
DNS *DNS
|
||||
Experimental *Experimental
|
||||
Hosts *trie.Trie
|
||||
Rules []C.Rule
|
||||
Users []auth.AuthUser
|
||||
Proxies map[string]C.Proxy
|
||||
|
@ -69,7 +69,6 @@ type rawDNS struct {
|
|||
Enable bool `yaml:"enable"`
|
||||
IPv6 bool `yaml:"ipv6"`
|
||||
NameServer []string `yaml:"nameserver"`
|
||||
Hosts map[string]string `yaml:"hosts"`
|
||||
Fallback []string `yaml:"fallback"`
|
||||
Listen string `yaml:"listen"`
|
||||
EnhancedMode dns.EnhancedMode `yaml:"enhanced-mode"`
|
||||
|
@ -89,6 +88,7 @@ type rawConfig struct {
|
|||
ExternalUI string `yaml:"external-ui"`
|
||||
Secret string `yaml:"secret"`
|
||||
|
||||
Hosts map[string]string `yaml:"hosts"`
|
||||
DNS rawDNS `yaml:"dns"`
|
||||
Experimental Experimental `yaml:"experimental"`
|
||||
Proxy []map[string]interface{} `yaml:"Proxy"`
|
||||
|
@ -135,6 +135,7 @@ func readConfig(path string) (*rawConfig, error) {
|
|||
Mode: T.Rule,
|
||||
Authentication: []string{},
|
||||
LogLevel: log.INFO,
|
||||
Hosts: map[string]string{},
|
||||
Rule: []string{},
|
||||
Proxy: []map[string]interface{}{},
|
||||
ProxyGroup: []map[string]interface{}{},
|
||||
|
@ -144,7 +145,6 @@ func readConfig(path string) (*rawConfig, error) {
|
|||
DNS: rawDNS{
|
||||
Enable: false,
|
||||
FakeIPRange: "198.18.0.1/16",
|
||||
Hosts: map[string]string{},
|
||||
},
|
||||
}
|
||||
err = yaml.Unmarshal([]byte(data), &rawConfig)
|
||||
|
@ -185,6 +185,12 @@ func Parse(path string) (*Config, error) {
|
|||
}
|
||||
config.DNS = dnsCfg
|
||||
|
||||
hosts, err := parseHosts(rawCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Hosts = hosts
|
||||
|
||||
config.Users = parseAuthentication(rawCfg.Authentication)
|
||||
|
||||
return config, nil
|
||||
|
@ -460,6 +466,21 @@ func parseRules(cfg *rawConfig, proxies map[string]C.Proxy) ([]C.Rule, error) {
|
|||
return rules, nil
|
||||
}
|
||||
|
||||
func parseHosts(cfg *rawConfig) (*trie.Trie, error) {
|
||||
tree := trie.New()
|
||||
if len(cfg.Hosts) != 0 {
|
||||
for domain, ipStr := range cfg.Hosts {
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("%s is not a valid IP", ipStr)
|
||||
}
|
||||
tree.Insert(domain, ip)
|
||||
}
|
||||
}
|
||||
|
||||
return tree, nil
|
||||
}
|
||||
|
||||
func hostWithDefaultPort(host string, defPort string) (string, error) {
|
||||
if !strings.Contains(host, ":") {
|
||||
host += ":"
|
||||
|
@ -544,18 +565,6 @@ func parseDNS(cfg rawDNS) (*DNS, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if len(cfg.Hosts) != 0 {
|
||||
tree := trie.New()
|
||||
for domain, ipStr := range cfg.Hosts {
|
||||
ip := net.ParseIP(ipStr)
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("%s is not a valid IP", ipStr)
|
||||
}
|
||||
tree.Insert(domain, ip)
|
||||
}
|
||||
dnsCfg.Hosts = tree
|
||||
}
|
||||
|
||||
if cfg.EnhancedMode == dns.FAKEIP {
|
||||
_, ipnet, err := net.ParseCIDR(cfg.FakeIPRange)
|
||||
if err != nil {
|
||||
|
|
|
@ -9,8 +9,74 @@ var (
|
|||
errIPNotFound = errors.New("cannot found ip")
|
||||
)
|
||||
|
||||
// ResolveIPv4 with a host, return ipv4
|
||||
func ResolveIPv4(host string) (net.IP, error) {
|
||||
if node := DefaultHosts.Search(host); node != nil {
|
||||
if ip := node.Data.(net.IP).To4(); ip != nil {
|
||||
return ip, nil
|
||||
}
|
||||
}
|
||||
|
||||
ip := net.ParseIP(host)
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
return ip4, nil
|
||||
}
|
||||
|
||||
if DefaultResolver != nil {
|
||||
return DefaultResolver.ResolveIPv4(host)
|
||||
}
|
||||
|
||||
ipAddrs, err := net.LookupIP(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, ip := range ipAddrs {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
return ip4, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errIPNotFound
|
||||
}
|
||||
|
||||
// ResolveIPv6 with a host, return ipv6
|
||||
func ResolveIPv6(host string) (net.IP, error) {
|
||||
if node := DefaultHosts.Search(host); node != nil {
|
||||
if ip := node.Data.(net.IP).To16(); ip != nil {
|
||||
return ip, nil
|
||||
}
|
||||
}
|
||||
|
||||
ip := net.ParseIP(host)
|
||||
if ip6 := ip.To16(); ip6 != nil {
|
||||
return ip6, nil
|
||||
}
|
||||
|
||||
if DefaultResolver != nil {
|
||||
return DefaultResolver.ResolveIPv6(host)
|
||||
}
|
||||
|
||||
ipAddrs, err := net.LookupIP(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, ip := range ipAddrs {
|
||||
if ip6 := ip.To16(); ip6 != nil {
|
||||
return ip6, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errIPNotFound
|
||||
}
|
||||
|
||||
// ResolveIP with a host, return ip
|
||||
func ResolveIP(host string) (net.IP, error) {
|
||||
if node := DefaultHosts.Search(host); node != nil {
|
||||
return node.Data.(net.IP), nil
|
||||
}
|
||||
|
||||
if DefaultResolver != nil {
|
||||
if DefaultResolver.ipv6 {
|
||||
return DefaultResolver.ResolveIP(host)
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/Dreamacro/clash/component/fakeip"
|
||||
|
@ -12,15 +10,22 @@ import (
|
|||
)
|
||||
|
||||
type handler func(w D.ResponseWriter, r *D.Msg)
|
||||
type middleware func(next handler) handler
|
||||
|
||||
func withFakeIP(pool *fakeip.Pool) handler {
|
||||
func withFakeIP(fakePool *fakeip.Pool) middleware {
|
||||
return func(next handler) handler {
|
||||
return func(w D.ResponseWriter, r *D.Msg) {
|
||||
q := r.Question[0]
|
||||
if q.Qtype != D.TypeA && q.Qtype != D.TypeAAAA {
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
host := strings.TrimRight(q.Name, ".")
|
||||
|
||||
rr := &D.A{}
|
||||
rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeA, Class: D.ClassINET, Ttl: dnsDefaultTTL}
|
||||
ip := pool.Lookup(host)
|
||||
ip := fakePool.Lookup(host)
|
||||
rr.A = ip
|
||||
msg := r.Copy()
|
||||
msg.Answer = []D.RR{rr}
|
||||
|
@ -31,15 +36,14 @@ func withFakeIP(pool *fakeip.Pool) handler {
|
|||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func withResolver(resolver *Resolver) handler {
|
||||
return func(w D.ResponseWriter, r *D.Msg) {
|
||||
msg, err := resolver.Exchange(r)
|
||||
|
||||
if err != nil {
|
||||
q := r.Question[0]
|
||||
qString := fmt.Sprintf("%s %s %s", q.Name, D.Class(q.Qclass).String(), D.Type(q.Qtype).String())
|
||||
log.Debugln("[DNS Server] Exchange %s failed: %v", qString, err)
|
||||
log.Debugln("[DNS Server] Exchange %s failed: %v", q.String(), err)
|
||||
D.HandleFailed(w, r)
|
||||
return
|
||||
}
|
||||
|
@ -49,64 +53,23 @@ func withResolver(resolver *Resolver) handler {
|
|||
}
|
||||
}
|
||||
|
||||
func withHost(resolver *Resolver, next handler) handler {
|
||||
hosts := resolver.hosts
|
||||
if hosts == nil {
|
||||
panic("dns/withHost: hosts should not be nil")
|
||||
func compose(middlewares []middleware, endpoint handler) handler {
|
||||
length := len(middlewares)
|
||||
h := endpoint
|
||||
for i := length - 1; i >= 0; i-- {
|
||||
middleware := middlewares[i]
|
||||
h = middleware(h)
|
||||
}
|
||||
|
||||
return func(w D.ResponseWriter, r *D.Msg) {
|
||||
q := r.Question[0]
|
||||
if q.Qtype != D.TypeA && q.Qtype != D.TypeAAAA {
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
domain := strings.TrimRight(q.Name, ".")
|
||||
host := hosts.Search(domain)
|
||||
if host == nil {
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
ip := host.Data.(net.IP)
|
||||
if q.Qtype == D.TypeAAAA && ip.To16() == nil {
|
||||
next(w, r)
|
||||
return
|
||||
} else if q.Qtype == D.TypeA && ip.To4() == nil {
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
var rr D.RR
|
||||
if q.Qtype == D.TypeAAAA {
|
||||
record := &D.AAAA{}
|
||||
record.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeAAAA, Class: D.ClassINET, Ttl: dnsDefaultTTL}
|
||||
record.AAAA = ip
|
||||
rr = record
|
||||
} else {
|
||||
record := &D.A{}
|
||||
record.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeA, Class: D.ClassINET, Ttl: dnsDefaultTTL}
|
||||
record.A = ip
|
||||
rr = record
|
||||
}
|
||||
|
||||
msg := r.Copy()
|
||||
msg.Answer = []D.RR{rr}
|
||||
msg.SetReply(r)
|
||||
w.WriteMsg(msg)
|
||||
return
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func newHandler(resolver *Resolver) handler {
|
||||
middlewares := []middleware{}
|
||||
|
||||
if resolver.IsFakeIP() {
|
||||
return withFakeIP(resolver.pool)
|
||||
middlewares = append(middlewares, withFakeIP(resolver.pool))
|
||||
}
|
||||
|
||||
if resolver.hosts != nil {
|
||||
return withHost(resolver, withResolver(resolver))
|
||||
}
|
||||
|
||||
return withResolver(resolver)
|
||||
return compose(middlewares, withResolver(resolver))
|
||||
}
|
||||
|
|
|
@ -21,8 +21,11 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
// DefaultResolver aim to resolve ip with host
|
||||
// DefaultResolver aim to resolve ip
|
||||
DefaultResolver *Resolver
|
||||
|
||||
// DefaultHosts aim to resolve hosts
|
||||
DefaultHosts = trie.New()
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -46,7 +49,6 @@ type Resolver struct {
|
|||
ipv6 bool
|
||||
mapping bool
|
||||
fakeip bool
|
||||
hosts *trie.Trie
|
||||
pool *fakeip.Pool
|
||||
fallback []resolver
|
||||
main []resolver
|
||||
|
@ -56,11 +58,6 @@ type Resolver struct {
|
|||
|
||||
// ResolveIP request with TypeA and TypeAAAA, priority return TypeAAAA
|
||||
func (r *Resolver) ResolveIP(host string) (ip net.IP, err error) {
|
||||
ip = net.ParseIP(host)
|
||||
if ip != nil {
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
ch := make(chan net.IP)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
|
@ -89,26 +86,12 @@ func (r *Resolver) ResolveIP(host string) (ip net.IP, err error) {
|
|||
|
||||
// ResolveIPv4 request with TypeA
|
||||
func (r *Resolver) ResolveIPv4(host string) (ip net.IP, err error) {
|
||||
ip = net.ParseIP(host)
|
||||
if ip != nil {
|
||||
return ip, nil
|
||||
return r.resolveIP(host, D.TypeA)
|
||||
}
|
||||
|
||||
query := &D.Msg{}
|
||||
query.SetQuestion(D.Fqdn(host), D.TypeA)
|
||||
|
||||
msg, err := r.Exchange(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ips := r.msgToIP(msg)
|
||||
if len(ips) == 0 {
|
||||
return nil, errIPNotFound
|
||||
}
|
||||
|
||||
ip = ips[0]
|
||||
return
|
||||
// ResolveIPv6 request with TypeAAAA
|
||||
func (r *Resolver) ResolveIPv6(host string) (ip net.IP, err error) {
|
||||
return r.resolveIP(host, D.TypeAAAA)
|
||||
}
|
||||
|
||||
// Exchange a batch of dns request, and it use cache
|
||||
|
@ -232,6 +215,17 @@ func (r *Resolver) fallbackExchange(m *D.Msg) (msg *D.Msg, err error) {
|
|||
}
|
||||
|
||||
func (r *Resolver) resolveIP(host string, dnsType uint16) (ip net.IP, err error) {
|
||||
ip = net.ParseIP(host)
|
||||
if dnsType == D.TypeAAAA {
|
||||
if ip6 := ip.To16(); ip6 != nil {
|
||||
return ip6, nil
|
||||
}
|
||||
} else {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
return ip4, nil
|
||||
}
|
||||
}
|
||||
|
||||
query := &D.Msg{}
|
||||
query.SetQuestion(D.Fqdn(host), dnsType)
|
||||
|
||||
|
@ -282,7 +276,6 @@ type Config struct {
|
|||
Main, Fallback []NameServer
|
||||
IPv6 bool
|
||||
EnhancedMode EnhancedMode
|
||||
Hosts *trie.Trie
|
||||
Pool *fakeip.Pool
|
||||
}
|
||||
|
||||
|
@ -297,7 +290,6 @@ func New(config Config) *Resolver {
|
|||
cache: cache.New(time.Second * 60),
|
||||
mapping: config.EnhancedMode == MAPPING,
|
||||
fakeip: config.EnhancedMode == FAKEIP,
|
||||
hosts: config.Hosts,
|
||||
pool: config.Pool,
|
||||
}
|
||||
if len(config.Fallback) != 0 {
|
||||
|
|
|
@ -2,6 +2,7 @@ package executor
|
|||
|
||||
import (
|
||||
"github.com/Dreamacro/clash/component/auth"
|
||||
trie "github.com/Dreamacro/clash/component/domain-trie"
|
||||
"github.com/Dreamacro/clash/config"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/dns"
|
||||
|
@ -30,6 +31,7 @@ func ApplyConfig(cfg *config.Config, force bool) {
|
|||
updateProxies(cfg.Proxies)
|
||||
updateRules(cfg.Rules)
|
||||
updateDNS(cfg.DNS)
|
||||
updateHosts(cfg.Hosts)
|
||||
updateExperimental(cfg.Experimental)
|
||||
}
|
||||
|
||||
|
@ -68,7 +70,6 @@ func updateDNS(c *config.DNS) {
|
|||
Main: c.NameServer,
|
||||
Fallback: c.Fallback,
|
||||
IPv6: c.IPv6,
|
||||
Hosts: c.Hosts,
|
||||
EnhancedMode: c.EnhancedMode,
|
||||
Pool: c.FakeIPRange,
|
||||
})
|
||||
|
@ -83,6 +84,10 @@ func updateDNS(c *config.DNS) {
|
|||
}
|
||||
}
|
||||
|
||||
func updateHosts(tree *trie.Trie) {
|
||||
dns.DefaultHosts = tree
|
||||
}
|
||||
|
||||
func updateProxies(proxies map[string]C.Proxy) {
|
||||
tunnel := T.Instance()
|
||||
oldProxies := tunnel.Proxies()
|
||||
|
|
|
@ -213,6 +213,13 @@ func (t *Tunnel) match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
|
|||
defer t.configMux.RUnlock()
|
||||
|
||||
var resolved bool
|
||||
|
||||
if node := dns.DefaultHosts.Search(metadata.Host); node != nil {
|
||||
ip := node.Data.(net.IP)
|
||||
metadata.DstIP = &ip
|
||||
resolved = true
|
||||
}
|
||||
|
||||
for _, rule := range t.rules {
|
||||
if !resolved && t.shouldResolveIP(rule, metadata) {
|
||||
ip, err := t.resolveIP(metadata.Host)
|
||||
|
|
Loading…
Reference in a new issue