coredns/middleware/file/file.go
Miek Gieben 225cdd1ca3 Add AXFR test
Test shouldTransfer by upping a testserver and sending the SOA query.

Remove state from DefaultErrorHandler and just get it from the request.

Add more logging to show what is going on.



This also adds the infrastructure for future tests.
2016-04-06 22:29:33 +01:00

122 lines
2.8 KiB
Go

package file
import (
"fmt"
"io"
"log"
"github.com/miekg/coredns/middleware"
"github.com/miekg/dns"
"golang.org/x/net/context"
)
type (
File struct {
Next middleware.Handler
Zones Zones
}
Zones struct {
Z map[string]*Zone
Names []string
}
)
func (f File) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
state := middleware.State{W: w, Req: r}
if state.QClass() != dns.ClassINET {
return dns.RcodeServerFailure, fmt.Errorf("can only deal with ClassINET")
}
qname := state.Name()
zone := middleware.Zones(f.Zones.Names).Matches(qname)
if zone == "" {
return f.Next.ServeDNS(ctx, w, r)
}
z, ok := f.Zones.Z[zone]
if !ok {
return f.Next.ServeDNS(ctx, w, r)
}
if z == nil {
return dns.RcodeServerFailure, nil
}
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
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(qname, state.QType(), state.Do())
m := new(dns.Msg)
m.SetReply(r)
m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
switch result {
case Success:
m.Answer = answer
m.Ns = ns
m.Extra = extra
case NameError:
m.Ns = ns
m.Rcode = dns.RcodeNameError
fallthrough
case NoData:
m.Ns = ns
case ServerFailure:
return dns.RcodeServerFailure, nil
}
m, _ = state.Scrub(m)
w.WriteMsg(m)
return dns.RcodeSuccess, nil
}
// 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)
for x := range tokens {
if x.Error != nil {
log.Printf("[ERROR] Failed to parse %s: %v", origin, x.Error)
return nil, x.Error
}
if x.RR.Header().Rrtype == dns.TypeSOA {
z.SOA = x.RR.(*dns.SOA)
continue
}
if x.RR.Header().Rrtype == dns.TypeRRSIG {
if x, ok := x.RR.(*dns.RRSIG); ok && x.TypeCovered == dns.TypeSOA {
z.SIG = append(z.SIG, x)
}
}
z.Insert(x.RR)
}
return z, nil
}