libnetwork: extract fn for external DNS forwarding

Signed-off-by: Cory Snider <csnider@mirantis.com>
This commit is contained in:
Cory Snider 2022-12-08 16:33:45 -05:00
parent 78792eae68
commit 92aa6e6282

View file

@ -371,9 +371,8 @@ func truncateResp(resp *dns.Msg, maxSize int, isTCP bool) {
func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
var (
extConn net.Conn
resp *dns.Msg
err error
resp *dns.Msg
err error
)
if query == nil || len(query.Question) == 0 {
@ -446,119 +445,7 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
truncateResp(resp, maxSize, proto == "tcp")
}
} else {
for i := 0; i < maxExtDNS; i++ {
extDNS := &r.extDNSList[i]
if extDNS.IPStr == "" {
break
}
extConnect := func() {
port := extDNS.port
if port == 0 {
port = 53
}
addr := fmt.Sprintf("%s:%d", extDNS.IPStr, port)
extConn, err = net.DialTimeout(proto, addr, extIOTimeout)
}
if extDNS.HostLoopback {
extConnect()
} else {
execErr := r.backend.ExecFunc(extConnect)
if execErr != nil {
logrus.Warn(execErr)
continue
}
}
if err != nil {
logrus.WithField("retries", i).Warnf("[resolver] connect failed: %s", err)
continue
}
logrus.Debugf("[resolver] query %s (%s) from %s, forwarding to %s:%s", queryName, dns.TypeToString[queryType],
extConn.LocalAddr().String(), proto, extDNS.IPStr)
// Timeout has to be set for every IO operation.
if err := extConn.SetDeadline(time.Now().Add(extIOTimeout)); err != nil {
logrus.WithError(err).Error("[resolver] error setting conn deadline")
}
co := &dns.Conn{
Conn: extConn,
UDPSize: uint16(maxSize),
}
defer co.Close()
// limits the number of outstanding concurrent queries.
if !r.forwardQueryStart() {
old := r.tStamp
r.tStamp = time.Now()
if r.tStamp.Sub(old) > logInterval {
logrus.Errorf("[resolver] more than %v concurrent queries from %s", maxConcurrent, extConn.LocalAddr().String())
}
continue
}
err = co.WriteMsg(query)
if err != nil {
r.forwardQueryEnd()
logrus.Debugf("[resolver] send to DNS server failed, %s", err)
continue
}
resp, err = co.ReadMsg()
// Truncated DNS replies should be sent to the client so that the
// client can retry over TCP
if err != nil && (resp == nil || !resp.Truncated) {
r.forwardQueryEnd()
logrus.WithError(err).Warnf("[resolver] failed to read from DNS server: %s, query: %s", extConn.RemoteAddr().String(), query.Question[0].String())
continue
}
r.forwardQueryEnd()
if resp == nil {
logrus.Debugf("[resolver] external DNS %s:%s returned empty response for %q", proto, extDNS.IPStr, queryName)
break
}
switch resp.Rcode {
case dns.RcodeServerFailure, dns.RcodeRefused:
// Server returned FAILURE: continue with the next external DNS server
// Server returned REFUSED: this can be a transitional status, so continue with the next external DNS server
logrus.Debugf("[resolver] external DNS %s:%s responded with %s for %q", proto, extDNS.IPStr, statusString(resp.Rcode), queryName)
continue
case dns.RcodeNameError:
// Server returned NXDOMAIN. Stop resolution if it's an authoritative answer (see RFC 8020: https://tools.ietf.org/html/rfc8020#section-2)
logrus.Debugf("[resolver] external DNS %s:%s responded with %s for %q", proto, extDNS.IPStr, statusString(resp.Rcode), queryName)
if resp.Authoritative {
break
}
continue
case dns.RcodeSuccess:
// All is well
default:
// Server gave some error. Log the error, and continue with the next external DNS server
logrus.Debugf("[resolver] external DNS %s:%s responded with %s (code %d) for %q", proto, extDNS.IPStr, statusString(resp.Rcode), resp.Rcode, queryName)
continue
}
answers := 0
for _, rr := range resp.Answer {
h := rr.Header()
switch h.Rrtype {
case dns.TypeA:
answers++
ip := rr.(*dns.A).A
logrus.Debugf("[resolver] received A record %q for %q from %s:%s", ip, h.Name, proto, extDNS.IPStr)
r.backend.HandleQueryResp(h.Name, ip)
case dns.TypeAAAA:
answers++
ip := rr.(*dns.AAAA).AAAA
logrus.Debugf("[resolver] received AAAA record %q for %q from %s:%s", ip, h.Name, proto, extDNS.IPStr)
r.backend.HandleQueryResp(h.Name, ip)
}
}
if resp.Answer == nil || answers == 0 {
logrus.Debugf("[resolver] external DNS %s:%s did not return any %s records for %q", proto, extDNS.IPStr, dns.TypeToString[queryType], queryName)
}
resp.Compress = true
break
}
resp = r.forwardExtDNS(proto, maxSize, query)
if resp == nil {
return
}
@ -569,6 +456,129 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
}
}
func (r *resolver) forwardExtDNS(proto string, maxSize int, query *dns.Msg) *dns.Msg {
queryName, queryType := query.Question[0].Name, query.Question[0].Qtype
var resp *dns.Msg
for i := 0; i < maxExtDNS; i++ {
extDNS := &r.extDNSList[i]
if extDNS.IPStr == "" {
break
}
var (
extConn net.Conn
err error
)
extConnect := func() {
port := extDNS.port
if port == 0 {
port = 53
}
addr := fmt.Sprintf("%s:%d", extDNS.IPStr, port)
extConn, err = net.DialTimeout(proto, addr, extIOTimeout)
}
if extDNS.HostLoopback {
extConnect()
} else {
execErr := r.backend.ExecFunc(extConnect)
if execErr != nil {
logrus.Warn(execErr)
continue
}
}
if err != nil {
logrus.WithField("retries", i).Warnf("[resolver] connect failed: %s", err)
continue
}
logrus.Debugf("[resolver] query %s (%s) from %s, forwarding to %s:%s", queryName, dns.TypeToString[queryType],
extConn.LocalAddr().String(), proto, extDNS.IPStr)
// Timeout has to be set for every IO operation.
if err := extConn.SetDeadline(time.Now().Add(extIOTimeout)); err != nil {
logrus.WithError(err).Error("[resolver] error setting conn deadline")
}
co := &dns.Conn{
Conn: extConn,
UDPSize: uint16(maxSize),
}
defer co.Close()
// limits the number of outstanding concurrent queries.
if !r.forwardQueryStart() {
old := r.tStamp
r.tStamp = time.Now()
if r.tStamp.Sub(old) > logInterval {
logrus.Errorf("[resolver] more than %v concurrent queries from %s", maxConcurrent, extConn.LocalAddr().String())
}
continue
}
err = co.WriteMsg(query)
if err != nil {
r.forwardQueryEnd()
logrus.Debugf("[resolver] send to DNS server failed, %s", err)
continue
}
resp, err = co.ReadMsg()
// Truncated DNS replies should be sent to the client so that the
// client can retry over TCP
if err != nil && (resp == nil || !resp.Truncated) {
r.forwardQueryEnd()
logrus.WithError(err).Warnf("[resolver] failed to read from DNS server: %s, query: %s", extConn.RemoteAddr().String(), query.Question[0].String())
continue
}
r.forwardQueryEnd()
if resp == nil {
logrus.Debugf("[resolver] external DNS %s:%s returned empty response for %q", proto, extDNS.IPStr, queryName)
break
}
switch resp.Rcode {
case dns.RcodeServerFailure, dns.RcodeRefused:
// Server returned FAILURE: continue with the next external DNS server
// Server returned REFUSED: this can be a transitional status, so continue with the next external DNS server
logrus.Debugf("[resolver] external DNS %s:%s responded with %s for %q", proto, extDNS.IPStr, statusString(resp.Rcode), queryName)
continue
case dns.RcodeNameError:
// Server returned NXDOMAIN. Stop resolution if it's an authoritative answer (see RFC 8020: https://tools.ietf.org/html/rfc8020#section-2)
logrus.Debugf("[resolver] external DNS %s:%s responded with %s for %q", proto, extDNS.IPStr, statusString(resp.Rcode), queryName)
if resp.Authoritative {
break
}
continue
case dns.RcodeSuccess:
// All is well
default:
// Server gave some error. Log the error, and continue with the next external DNS server
logrus.Debugf("[resolver] external DNS %s:%s responded with %s (code %d) for %q", proto, extDNS.IPStr, statusString(resp.Rcode), resp.Rcode, queryName)
continue
}
answers := 0
for _, rr := range resp.Answer {
h := rr.Header()
switch h.Rrtype {
case dns.TypeA:
answers++
ip := rr.(*dns.A).A
logrus.Debugf("[resolver] received A record %q for %q from %s:%s", ip, h.Name, proto, extDNS.IPStr)
r.backend.HandleQueryResp(h.Name, ip)
case dns.TypeAAAA:
answers++
ip := rr.(*dns.AAAA).AAAA
logrus.Debugf("[resolver] received AAAA record %q for %q from %s:%s", ip, h.Name, proto, extDNS.IPStr)
r.backend.HandleQueryResp(h.Name, ip)
}
}
if resp.Answer == nil || answers == 0 {
logrus.Debugf("[resolver] external DNS %s:%s did not return any %s records for %q", proto, extDNS.IPStr, dns.TypeToString[queryType], queryName)
}
resp.Compress = true
break
}
return resp
}
func statusString(responseCode int) string {
if s, ok := dns.RcodeToString[responseCode]; ok {
return s