Feature: move hosts to the top

This commit is contained in:
Dreamacro
2019-09-11 17:00:55 +08:00
parent 16e3090ee8
commit 96a4abf46c
6 changed files with 164 additions and 122 deletions

View File

@ -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)

View File

@ -1,8 +1,6 @@
package dns
import (
"fmt"
"net"
"strings"
"github.com/Dreamacro/clash/component/fakeip"
@ -12,34 +10,40 @@ import (
)
type handler func(w D.ResponseWriter, r *D.Msg)
type middleware func(next handler) handler
func withFakeIP(pool *fakeip.Pool) handler {
return func(w D.ResponseWriter, r *D.Msg) {
q := r.Question[0]
host := strings.TrimRight(q.Name, ".")
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
}
rr := &D.A{}
rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeA, Class: D.ClassINET, Ttl: dnsDefaultTTL}
ip := pool.Lookup(host)
rr.A = ip
msg := r.Copy()
msg.Answer = []D.RR{rr}
host := strings.TrimRight(q.Name, ".")
setMsgTTL(msg, 1)
msg.SetReply(r)
w.WriteMsg(msg)
return
rr := &D.A{}
rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeA, Class: D.ClassINET, Ttl: dnsDefaultTTL}
ip := fakePool.Lookup(host)
rr.A = ip
msg := r.Copy()
msg.Answer = []D.RR{rr}
setMsgTTL(msg, 1)
msg.SetReply(r)
w.WriteMsg(msg)
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))
}

View File

@ -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 {