This checks if the next middleware to be called is nil, and if so returns ServerFailure and an error. This makes the next calling more robust and saves some lines of code. Also prefix the error with the name of the middleware to aid in debugging.
125 lines
3.2 KiB
Go
125 lines
3.2 KiB
Go
// Package file implements a file backend.
|
|
package file
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"log"
|
|
|
|
"github.com/miekg/coredns/middleware"
|
|
"github.com/miekg/coredns/request"
|
|
|
|
"github.com/miekg/dns"
|
|
"golang.org/x/net/context"
|
|
)
|
|
|
|
type (
|
|
// File is the middleware that reads zone data from disk.
|
|
File struct {
|
|
Next middleware.Handler
|
|
Zones Zones
|
|
}
|
|
|
|
// Zones maps zone names to a *Zone.
|
|
Zones struct {
|
|
Z map[string]*Zone // A map mapping zone (origin) to the Zone's data
|
|
Names []string // All the keys from the map Z as a string slice.
|
|
}
|
|
)
|
|
|
|
// ServeDNS implements the middleware.Handle interface.
|
|
func (f File) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
|
state := request.Request{W: w, Req: r}
|
|
|
|
if state.QClass() != dns.ClassINET {
|
|
return dns.RcodeServerFailure, middleware.Error(f.Name(), errors.New("can only deal with ClassINET"))
|
|
}
|
|
qname := state.Name()
|
|
// TODO(miek): match the qname better in the map
|
|
zone := middleware.Zones(f.Zones.Names).Matches(qname)
|
|
if zone == "" {
|
|
return middleware.NextOrFailure(f.Name(), f.Next, ctx, w, r)
|
|
}
|
|
|
|
z, ok := f.Zones.Z[zone]
|
|
if !ok || z == nil {
|
|
return dns.RcodeServerFailure, nil
|
|
}
|
|
|
|
// This is only for when we are a secondary zones.
|
|
if r.Opcode == dns.OpcodeNotify {
|
|
if z.isNotify(state) {
|
|
m := new(dns.Msg)
|
|
m.SetReply(r)
|
|
m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
|
|
state.SizeAndDo(m)
|
|
w.WriteMsg(m)
|
|
|
|
log.Printf("[INFO] Notify from %s for %s: checking transfer", state.IP(), zone)
|
|
ok, err := z.shouldTransfer()
|
|
if ok {
|
|
z.TransferIn()
|
|
} else {
|
|
log.Printf("[INFO] Notify from %s for %s: no serial increase seen", state.IP(), zone)
|
|
}
|
|
if err != nil {
|
|
log.Printf("[WARNING] Notify from %s for %s: failed primary check: %s", state.IP(), zone, err)
|
|
}
|
|
return dns.RcodeSuccess, nil
|
|
}
|
|
log.Printf("[INFO] Dropping notify from %s for %s", state.IP(), zone)
|
|
return dns.RcodeSuccess, nil
|
|
}
|
|
|
|
if z.Expired != nil && *z.Expired {
|
|
log.Printf("[ERROR] Zone %s is expired", zone)
|
|
return dns.RcodeServerFailure, nil
|
|
}
|
|
|
|
if state.QType() == dns.TypeAXFR || state.QType() == dns.TypeIXFR {
|
|
xfr := Xfr{z}
|
|
return xfr.ServeDNS(ctx, w, r)
|
|
}
|
|
|
|
answer, ns, extra, result := z.Lookup(state, qname)
|
|
|
|
m := new(dns.Msg)
|
|
m.SetReply(r)
|
|
m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
|
|
m.Answer, m.Ns, m.Extra = answer, ns, extra
|
|
|
|
switch result {
|
|
case Success:
|
|
case NoData:
|
|
case NameError:
|
|
m.Rcode = dns.RcodeNameError
|
|
case Delegation:
|
|
m.Authoritative = false
|
|
case ServerFailure:
|
|
return dns.RcodeServerFailure, nil
|
|
}
|
|
|
|
state.SizeAndDo(m)
|
|
m, _ = state.Scrub(m)
|
|
w.WriteMsg(m)
|
|
return dns.RcodeSuccess, nil
|
|
}
|
|
|
|
// Name implements the Handler interface.
|
|
func (f File) Name() string { return "file" }
|
|
|
|
// Parse parses the zone in filename and returns a new Zone or an error.
|
|
func Parse(f io.Reader, origin, fileName string) (*Zone, error) {
|
|
tokens := dns.ParseZone(f, dns.Fqdn(origin), fileName)
|
|
z := NewZone(origin, fileName)
|
|
for x := range tokens {
|
|
if x.Error != nil {
|
|
log.Printf("[ERROR] Failed to parse `%s': %v", origin, x.Error)
|
|
return nil, x.Error
|
|
}
|
|
if err := z.Insert(x.RR); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return z, nil
|
|
}
|