Better resolver support

This commit is contained in:
Tyler 2018-07-01 20:47:22 -04:00
parent d1f11f1773
commit 725f298f2e
1 changed files with 56 additions and 29 deletions

View File

@ -11,6 +11,8 @@ import (
"time" "time"
"github.com/miekg/dns" "github.com/miekg/dns"
"errors"
"crypto/tls"
) )
type ResolvError struct { type ResolvError struct {
@ -33,6 +35,10 @@ type Resolver struct {
servers []string servers []string
domain_server *suffixTreeNode domain_server *suffixTreeNode
config *ResolvSettings config *ResolvSettings
tcpClient *dns.Client
udpClient *dns.Client
httpsClient *dns.Client
} }
func NewResolver(c ResolvSettings) *Resolver { func NewResolver(c ResolvSettings) *Resolver {
@ -64,6 +70,26 @@ func NewResolver(c ResolvSettings) *Resolver {
r.servers = append([]string{c.DOHServer}, r.servers...) r.servers = append([]string{c.DOHServer}, r.servers...)
} }
timeout := r.Timeout()
r.udpClient = &dns.Client{
Net: "udp",
ReadTimeout: timeout,
WriteTimeout: timeout,
}
r.tcpClient = &dns.Client{
Net: "tcp",
ReadTimeout: timeout,
WriteTimeout: timeout,
}
r.httpsClient = &dns.Client{
Net: "https",
ReadTimeout: timeout,
WriteTimeout: timeout,
}
return r return r
} }
@ -149,24 +175,6 @@ func (r *Resolver) ReadServerListFile(path string) {
// in every second, and return as early as possbile (have an answer). // in every second, and return as early as possbile (have an answer).
// It returns an error if no request has succeeded. // It returns an error if no request has succeeded.
func (r *Resolver) Lookup(net string, req *dns.Msg) (message *dns.Msg, err error) { func (r *Resolver) Lookup(net string, req *dns.Msg) (message *dns.Msg, err error) {
c := &dns.Client{
Net: net,
ReadTimeout: r.Timeout(),
WriteTimeout: r.Timeout(),
}
httpC := &dns.Client{
Net: "https",
ReadTimeout: r.Timeout(),
WriteTimeout: r.Timeout(),
}
tlsClient := &dns.Client{
Net: "tcp-tls",
ReadTimeout: r.Timeout(),
WriteTimeout: r.Timeout(),
}
if net == "udp" && settings.ResolvConfig.SetEDNS0 { if net == "udp" && settings.ResolvConfig.SetEDNS0 {
req = req.SetEdns0(65535, true) req = req.SetEdns0(65535, true)
} }
@ -175,21 +183,18 @@ func (r *Resolver) Lookup(net string, req *dns.Msg) (message *dns.Msg, err error
res := make(chan *RResp, 1) res := make(chan *RResp, 1)
var wg sync.WaitGroup var wg sync.WaitGroup
L := func(nameserver string) { L := func(resolver *Resolver, nameserver string) {
defer wg.Done() defer wg.Done()
var r *dns.Msg c, err := resolver.resolverFor(net, nameserver)
var rtt time.Duration
var err error
if strings.HasPrefix(nameserver, "https") { if err != nil {
r, rtt, err = httpC.Exchange(req, nameserver) logger.Warn("error:%s", err.Error())
} else if strings.HasPrefix(nameserver, ":853") { return
r, rtt, err = tlsClient.Exchange(req, nameserver)
} else {
r, rtt, err = c.Exchange(req, nameserver)
} }
r, rtt, err := c.Exchange(req, nameserver)
if err != nil { if err != nil {
logger.Warn("%s socket error on %s", qname, nameserver) logger.Warn("%s socket error on %s", qname, nameserver)
logger.Warn("error:%s", err.Error()) logger.Warn("error:%s", err.Error())
@ -218,7 +223,7 @@ func (r *Resolver) Lookup(net string, req *dns.Msg) (message *dns.Msg, err error
nameservers := r.Nameservers(qname) nameservers := r.Nameservers(qname)
for _, nameserver := range nameservers { for _, nameserver := range nameservers {
wg.Add(1) wg.Add(1)
go L(nameserver) go L(r, nameserver)
// but exit early, if we have an answer // but exit early, if we have an answer
select { select {
case re := <-res: case re := <-res:
@ -239,6 +244,28 @@ func (r *Resolver) Lookup(net string, req *dns.Msg) (message *dns.Msg, err error
} }
} }
func (r *Resolver) resolverFor(net, nameserver string) (*dns.Client, error) {
if strings.HasPrefix(nameserver, "https") {
return r.httpsClient, nil
} else if strings.HasSuffix(nameserver, ":853") {
// TODO We need to set the server name so we can confirm the TLS connection. This may require a rewrite of storing nameservers.
return &dns.Client{
Net: "tcp-tls",
ReadTimeout: r.Timeout(),
WriteTimeout: r.Timeout(),
TLSConfig: &tls.Config{
ServerName: "",
},
}, nil
} else if net == "udp" {
return r.udpClient, nil
} else if net == "tcp" {
return r.tcpClient, nil
}
return nil, errors.New("no client for nameserver")
}
// Namservers return the array of nameservers, with port number appended. // Namservers return the array of nameservers, with port number appended.
// '#' in the name is treated as port separator, as with dnsmasq. // '#' in the name is treated as port separator, as with dnsmasq.