diff options
Diffstat (limited to 'plugin/file/zone.go')
-rw-r--r-- | plugin/file/zone.go | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/plugin/file/zone.go b/plugin/file/zone.go new file mode 100644 index 000000000..1cef9dc3a --- /dev/null +++ b/plugin/file/zone.go @@ -0,0 +1,190 @@ +package file + +import ( + "fmt" + "net" + "path" + "strings" + "sync" + + "github.com/coredns/coredns/plugin/file/tree" + "github.com/coredns/coredns/plugin/proxy" + "github.com/coredns/coredns/request" + + "github.com/miekg/dns" +) + +// Zone defines a structure that contains all data related to a DNS zone. +type Zone struct { + origin string + origLen int + file string + *tree.Tree + Apex Apex + + TransferTo []string + StartupOnce sync.Once + TransferFrom []string + Expired *bool + + NoReload bool + reloadMu sync.RWMutex + ReloadShutdown chan bool + Proxy proxy.Proxy // Proxy for looking up names during the resolution process +} + +// Apex contains the apex records of a zone: SOA, NS and their potential signatures. +type Apex struct { + SOA *dns.SOA + NS []dns.RR + SIGSOA []dns.RR + SIGNS []dns.RR +} + +// NewZone returns a new zone. +func NewZone(name, file string) *Zone { + z := &Zone{ + origin: dns.Fqdn(name), + origLen: dns.CountLabel(dns.Fqdn(name)), + file: path.Clean(file), + Tree: &tree.Tree{}, + Expired: new(bool), + ReloadShutdown: make(chan bool), + } + *z.Expired = false + + return z +} + +// Copy copies a zone. +func (z *Zone) Copy() *Zone { + z1 := NewZone(z.origin, z.file) + z1.TransferTo = z.TransferTo + z1.TransferFrom = z.TransferFrom + z1.Expired = z.Expired + + z1.Apex = z.Apex + return z1 +} + +// Insert inserts r into z. +func (z *Zone) Insert(r dns.RR) error { + r.Header().Name = strings.ToLower(r.Header().Name) + + switch h := r.Header().Rrtype; h { + case dns.TypeNS: + r.(*dns.NS).Ns = strings.ToLower(r.(*dns.NS).Ns) + + if r.Header().Name == z.origin { + z.Apex.NS = append(z.Apex.NS, r) + return nil + } + case dns.TypeSOA: + r.(*dns.SOA).Ns = strings.ToLower(r.(*dns.SOA).Ns) + r.(*dns.SOA).Mbox = strings.ToLower(r.(*dns.SOA).Mbox) + + z.Apex.SOA = r.(*dns.SOA) + return nil + case dns.TypeNSEC3, dns.TypeNSEC3PARAM: + return fmt.Errorf("NSEC3 zone is not supported, dropping RR: %s for zone: %s", r.Header().Name, z.origin) + case dns.TypeRRSIG: + x := r.(*dns.RRSIG) + switch x.TypeCovered { + case dns.TypeSOA: + z.Apex.SIGSOA = append(z.Apex.SIGSOA, x) + return nil + case dns.TypeNS: + if r.Header().Name == z.origin { + z.Apex.SIGNS = append(z.Apex.SIGNS, x) + return nil + } + } + case dns.TypeCNAME: + r.(*dns.CNAME).Target = strings.ToLower(r.(*dns.CNAME).Target) + case dns.TypeMX: + r.(*dns.MX).Mx = strings.ToLower(r.(*dns.MX).Mx) + case dns.TypeSRV: + r.(*dns.SRV).Target = strings.ToLower(r.(*dns.SRV).Target) + } + + z.Tree.Insert(r) + return nil +} + +// Delete deletes r from z. +func (z *Zone) Delete(r dns.RR) { z.Tree.Delete(r) } + +// TransferAllowed checks if incoming request for transferring the zone is allowed according to the ACLs. +func (z *Zone) TransferAllowed(state request.Request) bool { + for _, t := range z.TransferTo { + if t == "*" { + return true + } + // If remote IP matches we accept. + remote := state.IP() + to, _, err := net.SplitHostPort(t) + if err != nil { + continue + } + if to == remote { + return true + } + } + // TODO(miek): future matching against IP/CIDR notations + return false +} + +// All returns all records from the zone, the first record will be the SOA record, +// otionally followed by all RRSIG(SOA)s. +func (z *Zone) All() []dns.RR { + if !z.NoReload { + z.reloadMu.RLock() + defer z.reloadMu.RUnlock() + } + + records := []dns.RR{} + allNodes := z.Tree.All() + for _, a := range allNodes { + records = append(records, a.All()...) + } + + if len(z.Apex.SIGNS) > 0 { + records = append(z.Apex.SIGNS, records...) + } + records = append(z.Apex.NS, records...) + + if len(z.Apex.SIGSOA) > 0 { + records = append(z.Apex.SIGSOA, records...) + } + return append([]dns.RR{z.Apex.SOA}, records...) +} + +// Print prints the zone's tree to stdout. +func (z *Zone) Print() { + z.Tree.Print() +} + +// NameFromRight returns the labels from the right, staring with the +// origin and then i labels extra. When we are overshooting the name +// the returned boolean is set to true. +func (z *Zone) nameFromRight(qname string, i int) (string, bool) { + if i <= 0 { + return z.origin, false + } + + for j := 1; j <= z.origLen; j++ { + if _, shot := dns.PrevLabel(qname, j); shot { + return qname, shot + } + } + + k := 0 + shot := false + for j := 1; j <= i; j++ { + k, shot = dns.PrevLabel(qname, j+z.origLen) + if shot { + return qname, shot + } + } + return qname[k:], false +} |