* write cname answer to client even if target lookup is servfail Signed-off-by: Chris O'Haver <cohaver@infoblox.com> * fix existing unit test expectations Signed-off-by: Chris O'Haver <cohaver@infoblox.com>
100 lines
2.6 KiB
Go
100 lines
2.6 KiB
Go
// Package auto implements an on-the-fly loading file backend.
|
|
package auto
|
|
|
|
import (
|
|
"context"
|
|
"regexp"
|
|
"time"
|
|
|
|
"github.com/coredns/coredns/plugin"
|
|
"github.com/coredns/coredns/plugin/file"
|
|
"github.com/coredns/coredns/plugin/metrics"
|
|
"github.com/coredns/coredns/plugin/pkg/upstream"
|
|
"github.com/coredns/coredns/plugin/transfer"
|
|
"github.com/coredns/coredns/request"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
type (
|
|
// Auto holds the zones and the loader configuration for automatically loading zones.
|
|
Auto struct {
|
|
Next plugin.Handler
|
|
*Zones
|
|
|
|
metrics *metrics.Metrics
|
|
transfer *transfer.Transfer
|
|
loader
|
|
}
|
|
|
|
loader struct {
|
|
directory string
|
|
template string
|
|
re *regexp.Regexp
|
|
|
|
ReloadInterval time.Duration
|
|
upstream *upstream.Upstream // Upstream for looking up names during the resolution process.
|
|
}
|
|
)
|
|
|
|
// ServeDNS implements the plugin.Handler interface.
|
|
func (a Auto) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
|
state := request.Request{W: w, Req: r}
|
|
qname := state.Name()
|
|
|
|
// Precheck with the origins, i.e. are we allowed to look here?
|
|
zone := plugin.Zones(a.Zones.Origins()).Matches(qname)
|
|
if zone == "" {
|
|
return plugin.NextOrFailure(a.Name(), a.Next, ctx, w, r)
|
|
}
|
|
|
|
// Now the real zone.
|
|
zone = plugin.Zones(a.Zones.Names()).Matches(qname)
|
|
if zone == "" {
|
|
return plugin.NextOrFailure(a.Name(), a.Next, ctx, w, r)
|
|
}
|
|
|
|
a.Zones.RLock()
|
|
z, ok := a.Zones.Z[zone]
|
|
a.Zones.RUnlock()
|
|
|
|
if !ok || z == nil {
|
|
return dns.RcodeServerFailure, nil
|
|
}
|
|
|
|
// If transfer is not loaded, we'll see these, answer with refused (no transfer allowed).
|
|
if state.QType() == dns.TypeAXFR || state.QType() == dns.TypeIXFR {
|
|
return dns.RcodeRefused, nil
|
|
}
|
|
|
|
answer, ns, extra, result := z.Lookup(ctx, state, qname)
|
|
|
|
m := new(dns.Msg)
|
|
m.SetReply(r)
|
|
m.Authoritative = true
|
|
m.Answer, m.Ns, m.Extra = answer, ns, extra
|
|
|
|
switch result {
|
|
case file.Success:
|
|
case file.NoData:
|
|
case file.NameError:
|
|
m.Rcode = dns.RcodeNameError
|
|
case file.Delegation:
|
|
m.Authoritative = false
|
|
case file.ServerFailure:
|
|
// If the result is SERVFAIL and the answer is non-empty, then the SERVFAIL came from an
|
|
// external CNAME lookup and the answer contains the CNAME with no target record. We should
|
|
// write the CNAME record to the client instead of sending an empty SERVFAIL response.
|
|
if len(m.Answer) == 0 {
|
|
return dns.RcodeServerFailure, nil
|
|
}
|
|
// The rcode in the response should be the rcode received from the target lookup. RFC 6604 section 3
|
|
m.Rcode = dns.RcodeServerFailure
|
|
}
|
|
|
|
w.WriteMsg(m)
|
|
return dns.RcodeSuccess, nil
|
|
}
|
|
|
|
// Name implements the Handler interface.
|
|
func (a Auto) Name() string { return "auto" }
|