Merge pull request #49 from miekg/zonefile-fix

Zonefile fix
This commit is contained in:
Miek Gieben 2016-03-28 10:17:52 +01:00
commit 4be4e7430c
10 changed files with 983 additions and 86 deletions

View file

@ -1,13 +1,10 @@
package setup package setup
import ( import (
"log"
"os" "os"
"github.com/miekg/coredns/middleware" "github.com/miekg/coredns/middleware"
"github.com/miekg/coredns/middleware/file" "github.com/miekg/coredns/middleware/file"
"github.com/miekg/dns"
) )
// File sets up the file middleware. // File sets up the file middleware.
@ -23,8 +20,7 @@ func File(c *Controller) (middleware.Middleware, error) {
} }
func fileParse(c *Controller) (file.Zones, error) { func fileParse(c *Controller) (file.Zones, error) {
// Maybe multiple, each for each zone. z := make(map[string]*file.Zone)
z := make(map[string]file.Zone)
names := []string{} names := []string{}
for c.Next() { for c.Next() {
if c.Val() == "file" { if c.Val() == "file" {
@ -42,7 +38,12 @@ func fileParse(c *Controller) (file.Zones, error) {
// normalize this origin // normalize this origin
origin = middleware.Host(origin).StandardHost() origin = middleware.Host(origin).StandardHost()
zone, err := parseZone(origin, fileName) reader, err := os.Open(fileName)
if err != nil {
return file.Zones{}, err
}
zone, err := file.Parse(reader, origin, fileName)
if err == nil { if err == nil {
z[origin] = zone z[origin] = zone
} }
@ -51,24 +52,3 @@ func fileParse(c *Controller) (file.Zones, error) {
} }
return file.Zones{Z: z, Names: names}, nil return file.Zones{Z: z, Names: names}, nil
} }
//
// parsrZone parses the zone in filename and returns a []RR or an error.
func parseZone(origin, fileName string) (file.Zone, error) {
f, err := os.Open(fileName)
if err != nil {
return nil, err
}
tokens := dns.ParseZone(f, origin, fileName)
zone := make([]dns.RR, 0, defaultZoneSize)
for x := range tokens {
if x.Error != nil {
log.Printf("[ERROR] failed to parse %s: %v", origin, x.Error)
return nil, x.Error
}
zone = append(zone, x.RR)
}
return file.Zone(zone), nil
}
const defaultZoneSize = 20 // A made up number.

View file

@ -14,7 +14,8 @@ func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i
return e.Next.ServeDNS(ctx, w, r) return e.Next.ServeDNS(ctx, w, r)
} }
m := state.AnswerMessage() m := new(dns.Msg)
m.SetReply(r)
m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
var ( var (

View file

@ -6,12 +6,13 @@ package file
// have some fluff for DNSSEC (and be memory efficient). // have some fluff for DNSSEC (and be memory efficient).
import ( import (
"strings" "io"
"log"
"golang.org/x/net/context"
"github.com/miekg/coredns/middleware" "github.com/miekg/coredns/middleware"
"github.com/miekg/dns" "github.com/miekg/dns"
"golang.org/x/net/context"
) )
type ( type (
@ -21,9 +22,8 @@ type (
// Maybe a list of all zones as well, as a []string? // Maybe a list of all zones as well, as a []string?
} }
Zone []dns.RR
Zones struct { Zones struct {
Z map[string]Zone // utterly braindead impl. TODO(miek): fix Z map[string]*Zone
Names []string Names []string
} }
) )
@ -35,57 +35,51 @@ func (f File) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i
if zone == "" { if zone == "" {
return f.Next.ServeDNS(ctx, w, r) return f.Next.ServeDNS(ctx, w, r)
} }
z, ok := f.Zones.Z[zone]
if !ok {
return f.Next.ServeDNS(ctx, w, r)
}
names, nodata := f.Zones.Z[zone].lookup(qname, state.QType()) rrs, extra, result := z.Lookup(qname, state.QType(), state.Do())
var answer *dns.Msg
switch { m := new(dns.Msg)
case nodata: m.SetReply(r)
answer = state.AnswerMessage() m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
answer.Ns = names
case len(names) == 0: switch result {
answer = state.AnswerMessage() case Success:
answer.Ns = names // case?
answer.Rcode = dns.RcodeNameError m.Answer = rrs
case len(names) > 0: m.Extra = extra
answer = state.AnswerMessage() // Ns section
answer.Answer = names case NameError:
m.Rcode = dns.RcodeNameError
fallthrough
case NoData:
// case?
m.Ns = rrs
default: default:
answer = state.ErrorMessage(dns.RcodeServerFailure) // TODO
} }
// Check return size, etc. TODO(miek) // sizing and Do bit RRSIG
w.WriteMsg(answer) w.WriteMsg(m)
return 0, nil return dns.RcodeSuccess, nil
} }
// Lookup will try to find qname and qtype in z. It returns the // Parse parses the zone in filename and returns a new Zone or an error.
// records found *or* a boolean saying NODATA. If the answer func Parse(f io.Reader, origin, fileName string) (*Zone, error) {
// is NODATA then the RR returned is the SOA record. tokens := dns.ParseZone(f, dns.Fqdn(origin), fileName)
// z := NewZone(origin)
// TODO(miek): EXTREMELY STUPID IMPLEMENTATION. for x := range tokens {
// Doesn't do much, no delegation, no cname, nothing really, etc. if x.Error != nil {
// TODO(miek): even NODATA looks broken log.Printf("[ERROR] failed to parse %s: %v", origin, x.Error)
func (z Zone) lookup(qname string, qtype uint16) ([]dns.RR, bool) { return nil, x.Error
var (
nodata bool
rep []dns.RR
soa dns.RR
)
for _, rr := range z {
if rr.Header().Rrtype == dns.TypeSOA {
soa = rr
} }
// Match function in Go DNS? if x.RR.Header().Rrtype == dns.TypeSOA {
if strings.ToLower(rr.Header().Name) == qname { z.SOA = x.RR.(*dns.SOA)
if rr.Header().Rrtype == qtype { continue
rep = append(rep, rr)
nodata = false
}
} }
z.Insert(x.RR)
} }
if nodata { return z, nil
return []dns.RR{soa}, true
}
return rep, false
} }

40
middleware/file/file.md Normal file
View file

@ -0,0 +1,40 @@
# file
`file` enabled reading zone data from a RFC-1035 styled file.
The etcd middleware makes extensive use of the proxy middleware to forward and query
other servers in the network.
## Syntax
~~~
file dbfile [zones...]
~~~
* `dbfile` the database file to read and parse.
* `zones` zones it should be authoritative for. If empty the zones from the configuration block
are used.
If you want to `round robin` A and AAAA responses look at the `loadbalance` middleware.
~~~
file {
db <dsds>
masters [...masters...]
}
~~~
* `path` /skydns
* `endpoint` endpoints...
* `stubzones`
## Examples
dnssec {
file blaat, transparant allow already signed responses
ksk bliep.dsdsk
}

75
middleware/file/lookup.go Normal file
View file

@ -0,0 +1,75 @@
package file
import "github.com/miekg/dns"
// Result is the result of a Lookup
type Result int
const (
Success Result = iota
NameError
NoData // aint no offical NoData return code.
)
// Lookup looks up qname and qtype in the zone, when do is true DNSSEC are included as well.
// Two sets of records are returned, one for the answer and one for the additional section.
func (z *Zone) Lookup(qname string, qtype uint16, do bool) ([]dns.RR, []dns.RR, Result) {
// TODO(miek): implement DNSSEC
var rr dns.RR
mk, known := dns.TypeToRR[qtype]
if !known {
return nil, nil, NameError
// Uhm...?
// rr = new(RFC3597)
} else {
rr = mk()
}
if qtype == dns.TypeSOA {
return z.lookupSOA(do)
}
rr.Header().Name = qname
elem := z.Tree.Get(rr)
if elem == nil {
return []dns.RR{z.SOA}, nil, NameError
}
rrs := elem.Types(dns.TypeCNAME)
if len(rrs) > 0 { // should only ever be 1 actually; TODO(miek) check for this?
// lookup target from the cname
rr.Header().Name = rrs[0].(*dns.CNAME).Target
elem := z.Tree.Get(rr)
if elem == nil {
return rrs, nil, Success
}
return rrs, elem.All(), Success
}
rrs = elem.Types(qtype)
if len(rrs) == 0 {
return []dns.RR{z.SOA}, nil, NoData
}
// Need to check sub-type on RRSIG records to only include the correctly
// typed ones.
return rrs, nil, Success
}
func (z *Zone) lookupSOA(do bool) ([]dns.RR, []dns.RR, Result) {
return []dns.RR{z.SOA}, nil, Success
}
// signatureForSubType range through the signature and return the correct
// ones for the subtype.
func (z *Zone) signatureForSubType(rrs []dns.RR, subtype uint16, do bool) []dns.RR {
if !do {
return nil
}
sigs := []dns.RR{}
for _, sig := range rrs {
if s, ok := sig.(*dns.RRSIG); ok {
if s.TypeCovered == subtype {
sigs = append(sigs, s)
}
}
}
return sigs
}

View file

@ -0,0 +1,174 @@
package file
import (
"sort"
"strings"
"testing"
"github.com/miekg/coredns/middleware"
"github.com/miekg/dns"
"golang.org/x/net/context"
)
var dnsTestCases = []dnsTestCase{
{
Qname: "miek.nl.", Qtype: dns.TypeSOA,
Answer: []dns.RR{
newSOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
},
},
{
Qname: "miek.nl.", Qtype: dns.TypeAAAA,
Answer: []dns.RR{
newAAAA("miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
},
},
{
Qname: "miek.nl.", Qtype: dns.TypeMX,
Answer: []dns.RR{
newMX("miek.nl. 1800 IN MX 1 aspmx.l.google.com."),
newMX("miek.nl. 1800 IN MX 10 aspmx2.googlemail.com."),
newMX("miek.nl. 1800 IN MX 10 aspmx3.googlemail.com."),
newMX("miek.nl. 1800 IN MX 5 alt1.aspmx.l.google.com."),
newMX("miek.nl. 1800 IN MX 5 alt2.aspmx.l.google.com."),
},
},
{
Qname: "www.miek.nl.", Qtype: dns.TypeA,
Answer: []dns.RR{
newCNAME("www.miek.nl. 1800 IN CNAME a.miek.nl."),
},
Extra: []dns.RR{
newA("a.miek.nl. 1800 IN A 139.162.196.78"),
newAAAA("a.miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
},
},
{
Qname: "a.miek.nl.", Qtype: dns.TypeSRV,
Ns: []dns.RR{
newSOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
},
},
{
Qname: "b.miek.nl.", Qtype: dns.TypeA,
Rcode: dns.RcodeNameError,
Ns: []dns.RR{
newSOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
},
},
}
type rrSet []dns.RR
func (p rrSet) Len() int { return len(p) }
func (p rrSet) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p rrSet) Less(i, j int) bool { return p[i].String() < p[j].String() }
const testzone = "miek.nl."
func TestLookup(t *testing.T) {
zone, err := Parse(strings.NewReader(dbMiekNL), testzone, "stdin")
if err != nil {
t.Fatalf("expect no error when reading zone, got %q", err)
}
fm := File{Next: handler(), Zones: Zones{Z: map[string]*Zone{testzone: zone}, Names: []string{testzone}}}
ctx := context.TODO()
for _, tc := range dnsTestCases {
m := new(dns.Msg)
m.SetQuestion(dns.Fqdn(tc.Qname), tc.Qtype)
rec := middleware.NewResponseRecorder(&middleware.TestResponseWriter{})
_, err := fm.ServeDNS(ctx, rec, m)
if err != nil {
t.Errorf("expected no error, got %v\n", err)
return
}
resp := rec.Msg()
sort.Sort(rrSet(resp.Answer))
sort.Sort(rrSet(resp.Ns))
sort.Sort(rrSet(resp.Extra))
if resp.Rcode != tc.Rcode {
t.Errorf("rcode is %q, expected %q", dns.RcodeToString[resp.Rcode], dns.RcodeToString[tc.Rcode])
t.Logf("%v\n", resp)
continue
}
if len(resp.Answer) != len(tc.Answer) {
t.Errorf("answer for %q contained %d results, %d expected", tc.Qname, len(resp.Answer), len(tc.Answer))
t.Logf("%v\n", resp)
continue
}
if len(resp.Ns) != len(tc.Ns) {
t.Errorf("authority for %q contained %d results, %d expected", tc.Qname, len(resp.Ns), len(tc.Ns))
t.Logf("%v\n", resp)
continue
}
if len(resp.Extra) != len(tc.Extra) {
t.Errorf("additional for %q contained %d results, %d expected", tc.Qname, len(resp.Extra), len(tc.Extra))
t.Logf("%v\n", resp)
continue
}
}
}
type dnsTestCase struct {
Qname string
Qtype uint16
Rcode int
Answer []dns.RR
Ns []dns.RR
Extra []dns.RR
}
func newA(rr string) *dns.A { r, _ := dns.NewRR(rr); return r.(*dns.A) }
func newAAAA(rr string) *dns.AAAA { r, _ := dns.NewRR(rr); return r.(*dns.AAAA) }
func newCNAME(rr string) *dns.CNAME { r, _ := dns.NewRR(rr); return r.(*dns.CNAME) }
func newSRV(rr string) *dns.SRV { r, _ := dns.NewRR(rr); return r.(*dns.SRV) }
func newSOA(rr string) *dns.SOA { r, _ := dns.NewRR(rr); return r.(*dns.SOA) }
func newNS(rr string) *dns.NS { r, _ := dns.NewRR(rr); return r.(*dns.NS) }
func newPTR(rr string) *dns.PTR { r, _ := dns.NewRR(rr); return r.(*dns.PTR) }
func newTXT(rr string) *dns.TXT { r, _ := dns.NewRR(rr); return r.(*dns.TXT) }
func newMX(rr string) *dns.MX { r, _ := dns.NewRR(rr); return r.(*dns.MX) }
const dbMiekNL = `
$TTL 30M
$ORIGIN miek.nl.
@ IN SOA linode.atoom.net. miek.miek.nl. (
1282630057 ; Serial
4H ; Refresh
1H ; Retry
7D ; Expire
4H ) ; Negative Cache TTL
IN NS linode.atoom.net.
IN NS ns-ext.nlnetlabs.nl.
IN NS omval.tednet.nl.
IN NS ext.ns.whyscream.net.
IN MX 1 aspmx.l.google.com.
IN MX 5 alt1.aspmx.l.google.com.
IN MX 5 alt2.aspmx.l.google.com.
IN MX 10 aspmx2.googlemail.com.
IN MX 10 aspmx3.googlemail.com.
IN A 139.162.196.78
IN AAAA 2a01:7e00::f03c:91ff:fef1:6735
a IN A 139.162.196.78
IN AAAA 2a01:7e00::f03c:91ff:fef1:6735
www IN CNAME a
archive IN CNAME a`
func handler() middleware.Handler {
return middleware.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
m := new(dns.Msg)
m.SetRcode(r, dns.RcodeServerFailure)
w.WriteMsg(m)
return dns.RcodeServerFailure, nil
})
}

View file

@ -0,0 +1,585 @@
// Copyright ©2012 The bíogo Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found at the end of this file.
// Package tree implements Left-Leaning Red Black trees as described by Robert Sedgewick.
//
// More details relating to the implementation are available at the following locations:
//
// http://www.cs.princeton.edu/~rs/talks/LLRB/LLRB.pdf
// http://www.cs.princeton.edu/~rs/talks/LLRB/Java/RedBlackBST.java
// http://www.teachsolaisgames.com/articles/balanced_left_leaning.html
//
// Heavily modified by Miek Gieben for use in DNS zones.
package tree
// TODO(miek): locking? lockfree
// TODO(miek): fix docs
import (
"strings"
"github.com/miekg/dns"
)
const (
TD234 = iota
BU23
)
// Operation mode of the LLRB tree.
const Mode = BU23
func init() {
if Mode != TD234 && Mode != BU23 {
panic("tree: unknown mode")
}
}
type Elem struct {
m map[uint16][]dns.RR
}
// newElem returns a new elem
func newElem(rr dns.RR) *Elem {
e := Elem{m: make(map[uint16][]dns.RR)}
e.m[rr.Header().Rrtype] = []dns.RR{rr}
return &e
}
// Types returns the types from with type qtype from e.
func (e *Elem) Types(qtype uint16) []dns.RR {
if rrs, ok := e.m[qtype]; ok {
// TODO(miek): length should never be zero here.
return rrs
}
return nil
}
func (e *Elem) All() []dns.RR {
list := []dns.RR{}
for _, rrs := range e.m {
list = append(list, rrs...)
}
return list
}
func (e *Elem) Insert(rr dns.RR) {
t := rr.Header().Rrtype
if e.m == nil {
e.m = make(map[uint16][]dns.RR)
e.m[t] = []dns.RR{rr}
return
}
rrs, ok := e.m[t]
if !ok {
e.m[t] = []dns.RR{rr}
return
}
for _, er := range rrs {
if equalRdata(er, rr) {
return
}
}
rrs = append(rrs, rr)
e.m[t] = rrs
}
// Delete removes rr from e. When e is empty after the removal the returned bool is true.
func (e *Elem) Delete(rr dns.RR) (empty bool) {
t := rr.Header().Rrtype
if e.m == nil {
return
}
rrs, ok := e.m[t]
if !ok {
return
}
for i, er := range rrs {
if equalRdata(er, rr) {
rrs = removeFromSlice(rrs, i)
e.m[t] = rrs
empty = len(rrs) == 0
if empty {
delete(e.m, t)
}
return
}
}
return
}
// TODO(miek): need case ignore compare that is more efficient.
func Less(a *Elem, rr dns.RR) int {
aname := ""
for _, ar := range a.m {
aname = strings.ToLower(ar[0].Header().Name)
break
}
rname := strings.ToLower(rr.Header().Name)
if aname == rname {
return 0
}
if aname < rname {
return -1
}
return 1
}
// Assuming the same type and name this will check if the rdata is equal as well.
func equalRdata(a, b dns.RR) bool {
switch x := a.(type) {
case *dns.A:
return x.A.Equal(b.(*dns.A).A)
case *dns.AAAA:
return x.AAAA.Equal(b.(*dns.AAAA).AAAA)
case *dns.MX:
if x.Mx == b.(*dns.MX).Mx && x.Preference == b.(*dns.MX).Preference {
return true
}
}
return false
}
// removeFromSlice removes index i from the slice.
func removeFromSlice(rrs []dns.RR, i int) []dns.RR {
if i >= len(rrs) {
return rrs
}
rrs = append(rrs[:i], rrs[i+1:]...)
return rrs
}
// A Color represents the color of a Node.
type Color bool
const (
// Red as false give us the defined behaviour that new nodes are red. Although this
// is incorrect for the root node, that is resolved on the first insertion.
Red Color = false
Black Color = true
)
// A Node represents a node in the LLRB tree.
type Node struct {
Elem *Elem
Left, Right *Node
Color Color
}
// A Tree manages the root node of an LLRB tree. Public methods are exposed through this type.
type Tree struct {
Root *Node // Root node of the tree.
Count int // Number of elements stored.
}
// Helper methods
// color returns the effect color of a Node. A nil node returns black.
func (n *Node) color() Color {
if n == nil {
return Black
}
return n.Color
}
// (a,c)b -rotL-> ((a,)b,)c
func (n *Node) rotateLeft() (root *Node) {
// Assumes: n has two children.
root = n.Right
n.Right = root.Left
root.Left = n
root.Color = n.Color
n.Color = Red
return
}
// (a,c)b -rotR-> (,(,c)b)a
func (n *Node) rotateRight() (root *Node) {
// Assumes: n has two children.
root = n.Left
n.Left = root.Right
root.Right = n
root.Color = n.Color
n.Color = Red
return
}
// (aR,cR)bB -flipC-> (aB,cB)bR | (aB,cB)bR -flipC-> (aR,cR)bB
func (n *Node) flipColors() {
// Assumes: n has two children.
n.Color = !n.Color
n.Left.Color = !n.Left.Color
n.Right.Color = !n.Right.Color
}
// fixUp ensures that black link balance is correct, that red nodes lean left,
// and that 4 nodes are split in the case of BU23 and properly balanced in TD234.
func (n *Node) fixUp() *Node {
if n.Right.color() == Red {
if Mode == TD234 && n.Right.Left.color() == Red {
n.Right = n.Right.rotateRight()
}
n = n.rotateLeft()
}
if n.Left.color() == Red && n.Left.Left.color() == Red {
n = n.rotateRight()
}
if Mode == BU23 && n.Left.color() == Red && n.Right.color() == Red {
n.flipColors()
}
return n
}
func (n *Node) moveRedLeft() *Node {
n.flipColors()
if n.Right.Left.color() == Red {
n.Right = n.Right.rotateRight()
n = n.rotateLeft()
n.flipColors()
if Mode == TD234 && n.Right.Right.color() == Red {
n.Right = n.Right.rotateLeft()
}
}
return n
}
func (n *Node) moveRedRight() *Node {
n.flipColors()
if n.Left.Left.color() == Red {
n = n.rotateRight()
n.flipColors()
}
return n
}
// Len returns the number of elements stored in the Tree.
func (t *Tree) Len() int {
return t.Count
}
// Get returns the first match of q in the Tree. If insertion without
// replacement is used, this is probably not what you want.
func (t *Tree) Get(rr dns.RR) *Elem {
if t.Root == nil {
return nil
}
n := t.Root.search(rr)
if n == nil {
return nil
}
return n.Elem
}
func (n *Node) search(rr dns.RR) *Node {
for n != nil {
switch c := Less(n.Elem, rr); {
case c == 0:
return n
case c < 0:
n = n.Left
default:
n = n.Right
}
}
return n
}
// Insert inserts the Comparable e into the Tree at the first match found
// with e or when a nil node is reached. Insertion without replacement can
// specified by ensuring that e.Compare() never returns 0. If insert without
// replacement is performed, a distinct query Comparable must be used that
// can return 0 with a Compare() call.
func (t *Tree) Insert(rr dns.RR) {
var d int
t.Root, d = t.Root.insert(rr)
t.Count += d
t.Root.Color = Black
}
func (n *Node) insert(rr dns.RR) (root *Node, d int) {
if n == nil {
return &Node{Elem: newElem(rr)}, 1
} else if n.Elem == nil {
n.Elem = newElem(rr)
return n, 1
}
if Mode == TD234 {
if n.Left.color() == Red && n.Right.color() == Red {
n.flipColors()
}
}
switch c := Less(n.Elem, rr); {
case c == 0:
n.Elem.Insert(rr)
case c < 0:
n.Left, d = n.Left.insert(rr)
default:
n.Right, d = n.Right.insert(rr)
}
if n.Right.color() == Red && n.Left.color() == Black {
n = n.rotateLeft()
}
if n.Left.color() == Red && n.Left.Left.color() == Red {
n = n.rotateRight()
}
if Mode == BU23 {
if n.Left.color() == Red && n.Right.color() == Red {
n.flipColors()
}
}
root = n
return
}
// DeleteMin deletes the node with the minimum value in the tree. If insertion without
// replacement has been used, the left-most minimum will be deleted.
func (t *Tree) DeleteMin() {
if t.Root == nil {
return
}
var d int
t.Root, d = t.Root.deleteMin()
t.Count += d
if t.Root == nil {
return
}
t.Root.Color = Black
}
func (n *Node) deleteMin() (root *Node, d int) {
if n.Left == nil {
return nil, -1
}
if n.Left.color() == Black && n.Left.Left.color() == Black {
n = n.moveRedLeft()
}
n.Left, d = n.Left.deleteMin()
root = n.fixUp()
return
}
// DeleteMax deletes the node with the maximum value in the tree. If insertion without
// replacement has been used, the right-most maximum will be deleted.
func (t *Tree) DeleteMax() {
if t.Root == nil {
return
}
var d int
t.Root, d = t.Root.deleteMax()
t.Count += d
if t.Root == nil {
return
}
t.Root.Color = Black
}
func (n *Node) deleteMax() (root *Node, d int) {
if n.Left != nil && n.Left.color() == Red {
n = n.rotateRight()
}
if n.Right == nil {
return nil, -1
}
if n.Right.color() == Black && n.Right.Left.color() == Black {
n = n.moveRedRight()
}
n.Right, d = n.Right.deleteMax()
root = n.fixUp()
return
}
// Delete removes rr from the tree, is the node turns empty, that node is return with DeleteNode.
func (t *Tree) Delete(rr dns.RR) {
if t.Root == nil {
return
}
// If there is an element, remove the rr from it
el := t.Get(rr)
if el == nil {
t.DeleteNode(rr)
return
}
// delete from this element
empty := el.Delete(rr)
if empty {
t.DeleteNode(rr)
return
}
}
// DeleteNode deletes the node that matches e according to Compare(). Note that Compare must
// identify the target node uniquely and in cases where non-unique keys are used,
// attributes used to break ties must be used to determine tree ordering during insertion.
func (t *Tree) DeleteNode(rr dns.RR) {
if t.Root == nil {
return
}
var d int
t.Root, d = t.Root.delete(rr)
t.Count += d
if t.Root == nil {
return
}
t.Root.Color = Black
}
func (n *Node) delete(rr dns.RR) (root *Node, d int) {
if Less(n.Elem, rr) < 0 {
if n.Left != nil {
if n.Left.color() == Black && n.Left.Left.color() == Black {
n = n.moveRedLeft()
}
n.Left, d = n.Left.delete(rr)
}
} else {
if n.Left.color() == Red {
n = n.rotateRight()
}
if n.Right == nil && Less(n.Elem, rr) == 0 {
return nil, -1
}
if n.Right != nil {
if n.Right.color() == Black && n.Right.Left.color() == Black {
n = n.moveRedRight()
}
if Less(n.Elem, rr) == 0 {
n.Elem = n.Right.min().Elem
n.Right, d = n.Right.deleteMin()
} else {
n.Right, d = n.Right.delete(rr)
}
}
}
root = n.fixUp()
return
}
// Return the minimum value stored in the tree. This will be the left-most minimum value if
// insertion without replacement has been used.
func (t *Tree) Min() *Elem {
if t.Root == nil {
return nil
}
return t.Root.min().Elem
}
func (n *Node) min() *Node {
for ; n.Left != nil; n = n.Left {
}
return n
}
// Return the maximum value stored in the tree. This will be the right-most maximum value if
// insertion without replacement has been used.
func (t *Tree) Max() *Elem {
if t.Root == nil {
return nil
}
return t.Root.max().Elem
}
func (n *Node) max() *Node {
for ; n.Right != nil; n = n.Right {
}
return n
}
// Floor returns the greatest value equal to or less than the query q according to q.Compare().
func (t *Tree) Floor(rr dns.RR) *Elem {
if t.Root == nil {
return nil
}
n := t.Root.floor(rr)
if n == nil {
return nil
}
return n.Elem
}
func (n *Node) floor(rr dns.RR) *Node {
if n == nil {
return nil
}
switch c := Less(n.Elem, rr); {
case c == 0:
return n
case c < 0:
return n.Left.floor(rr)
default:
if r := n.Right.floor(rr); r != nil {
return r
}
}
return n
}
// Ceil returns the smallest value equal to or greater than the query q according to q.Compare().
func (t *Tree) Ceil(rr dns.RR) *Elem {
if t.Root == nil {
return nil
}
n := t.Root.ceil(rr)
if n == nil {
return nil
}
return n.Elem
}
func (n *Node) ceil(rr dns.RR) *Node {
if n == nil {
return nil
}
switch c := Less(n.Elem, rr); {
case c == 0:
return n
case c > 0:
return n.Right.ceil(rr)
default:
if l := n.Left.ceil(rr); l != nil {
return l
}
}
return n
}
/*
Copyright ©2012 The bíogo Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the bíogo project nor the names of its authors and
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

26
middleware/file/zone.go Normal file
View file

@ -0,0 +1,26 @@
package file
import (
"github.com/miekg/coredns/middleware/file/tree"
"github.com/miekg/dns"
)
type Zone struct {
SOA *dns.SOA
SIG []*dns.RRSIG
name string
*tree.Tree
}
func NewZone(name string) *Zone {
return &Zone{name: dns.Fqdn(name), Tree: &tree.Tree{}}
}
func (z *Zone) Insert(r dns.RR) {
z.Tree.Insert(r)
}
func (z *Zone) Delete(r dns.RR) {
z.Tree.Delete(r)
}

View file

@ -0,0 +1,30 @@
package file
import (
"testing"
"github.com/miekg/dns"
)
func TestZoneInsert(t *testing.T) {
z := NewZone("miek.nl")
rr, _ := dns.NewRR("miek.nl. IN A 127.0.0.1")
z.Insert(rr)
t.Logf("%+v\n", z)
elem := z.Get(rr)
t.Logf("%+v\n", elem)
if elem != nil {
t.Logf("%+v\n", elem.Types(dns.TypeA))
}
z.Delete(rr)
t.Logf("%+v\n", z)
elem = z.Get(rr)
t.Logf("%+v\n", elem)
if elem != nil {
t.Logf("%+v\n", elem.Types(dns.TypeA))
}
}

View file

@ -179,11 +179,3 @@ func (s State) ErrorMessage(rcode int) *dns.Msg {
m.SetRcode(s.Req, rcode) m.SetRcode(s.Req, rcode)
return m return m
} }
// AnswerMessage returns an error message suitable for sending
// back to the client.
func (s State) AnswerMessage() *dns.Msg {
m := new(dns.Msg)
m.SetReply(s.Req)
return m
}