aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Miek Gieben <miek@miek.nl> 2016-04-02 16:56:16 +0100
committerGravatar Miek Gieben <miek@miek.nl> 2016-04-02 16:56:16 +0100
commit9b21646954c8fea174be8b769f16ddb213286753 (patch)
tree026a7d0419640906905429459d663af4064467ad
parentd8ab95cd18144e8701b4bb9d5f2d96fc74ab1149 (diff)
downloadcoredns-9b21646954c8fea174be8b769f16ddb213286753.tar.gz
coredns-9b21646954c8fea174be8b769f16ddb213286753.tar.zst
coredns-9b21646954c8fea174be8b769f16ddb213286753.zip
empty non-terminal support
When looking for a name in tree, return wether we got to a longer one - if so we had an ent. Add tests + dnssec tests and refactor the tests as well a bit.
-rw-r--r--middleware/file/closest.go3
-rw-r--r--middleware/file/dnssec_test.go25
-rw-r--r--middleware/file/ent_test.go176
-rw-r--r--middleware/file/file_test.go11
-rw-r--r--middleware/file/lookup.go21
-rw-r--r--middleware/file/lookup_test.go25
-rw-r--r--middleware/file/tree/all.go27
-rw-r--r--middleware/file/tree/elem.go121
-rw-r--r--middleware/file/tree/tree.go183
-rw-r--r--middleware/file/wildcard_test.go29
-rw-r--r--middleware/file/zone.go13
-rw-r--r--middleware/file/zone_test.go29
-rw-r--r--middleware/testing/helpers.go27
13 files changed, 423 insertions, 267 deletions
diff --git a/middleware/file/closest.go b/middleware/file/closest.go
index 895b1a524..df74cad50 100644
--- a/middleware/file/closest.go
+++ b/middleware/file/closest.go
@@ -6,10 +6,9 @@ import "github.com/miekg/dns"
func (z *Zone) ClosestEncloser(rr dns.RR) string {
// tree/tree.go does not store a parent *Node pointer, so we can't
// just follow up the tree. TODO(miek): fix.
-
offset, end := dns.NextLabel(rr.Header().Name, 0)
for !end {
- elem := z.Tree.Get(rr)
+ elem, _ := z.Tree.Get(rr)
if elem != nil {
return elem.Name()
}
diff --git a/middleware/file/dnssec_test.go b/middleware/file/dnssec_test.go
index 44f8b0f66..c225a5612 100644
--- a/middleware/file/dnssec_test.go
+++ b/middleware/file/dnssec_test.go
@@ -121,36 +121,19 @@ func TestLookupDNSSEC(t *testing.T) {
sort.Sort(coretest.RRSet(resp.Ns))
sort.Sort(coretest.RRSet(resp.Extra))
- if resp.Rcode != tc.Rcode {
- t.Errorf("rcode is %q, expected %q", dns.RcodeToString[resp.Rcode], dns.RcodeToString[tc.Rcode])
+ if !coretest.Header(t, tc, resp) {
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
- }
-
- if !coretest.CheckSection(t, tc, coretest.Answer, resp.Answer) {
+ if !coretest.Section(t, tc, coretest.Answer, resp.Answer) {
t.Logf("%v\n", resp)
}
- if !coretest.CheckSection(t, tc, coretest.Ns, resp.Ns) {
+ if !coretest.Section(t, tc, coretest.Ns, resp.Ns) {
t.Logf("%v\n", resp)
}
- if !coretest.CheckSection(t, tc, coretest.Extra, resp.Extra) {
+ if !coretest.Section(t, tc, coretest.Extra, resp.Extra) {
t.Logf("%v\n", resp)
}
}
diff --git a/middleware/file/ent_test.go b/middleware/file/ent_test.go
new file mode 100644
index 000000000..38f68e8e1
--- /dev/null
+++ b/middleware/file/ent_test.go
@@ -0,0 +1,176 @@
+package file
+
+import (
+ "sort"
+ "strings"
+ "testing"
+
+ "github.com/miekg/coredns/middleware"
+ coretest "github.com/miekg/coredns/middleware/testing"
+
+ "github.com/miekg/dns"
+ "golang.org/x/net/context"
+)
+
+var entTestCases = []coretest.Case{
+ {
+ Qname: "b.c.miek.nl.", Qtype: dns.TypeA,
+ Ns: []dns.RR{
+ coretest.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
+ },
+ },
+ {
+ Qname: "b.c.miek.nl.", Qtype: dns.TypeA, Do: true,
+ Ns: []dns.RR{
+ coretest.NSEC("a.miek.nl. 14400 IN NSEC a.b.c.miek.nl. A RRSIG NSEC"),
+ coretest.RRSIG("a.miek.nl. 14400 IN RRSIG NSEC 8 3 14400 20160502144311 20160402144311 12051 miek.nl. d5XZEy6SUpq98ZKUlzqhAfkLI9pQPc="),
+ coretest.RRSIG("miek.nl. 1800 IN RRSIG SOA 8 2 1800 20160502144311 20160402144311 12051 miek.nl. KegoBxA3Tbrhlc4cEdkRiteIkOfsq"),
+ coretest.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
+ },
+ },
+}
+
+func TestLookupENT(t *testing.T) {
+ zone, err := Parse(strings.NewReader(dbMiekENTNL), testzone, "stdin")
+ if err != nil {
+ t.Fatalf("expect no error when reading zone, got %q", err)
+ }
+
+ fm := File{Next: coretest.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{testzone: zone}, Names: []string{testzone}}}
+ ctx := context.TODO()
+
+ for _, tc := range entTestCases {
+ m := tc.Msg()
+
+ 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(coretest.RRSet(resp.Answer))
+ sort.Sort(coretest.RRSet(resp.Ns))
+ sort.Sort(coretest.RRSet(resp.Extra))
+
+ if !coretest.Header(t, tc, resp) {
+ t.Logf("%v\n", resp)
+ continue
+ }
+
+ if !coretest.Section(t, tc, coretest.Answer, resp.Answer) {
+ t.Logf("%v\n", resp)
+ }
+ if !coretest.Section(t, tc, coretest.Ns, resp.Ns) {
+ t.Logf("%v\n", resp)
+
+ }
+ if !coretest.Section(t, tc, coretest.Extra, resp.Extra) {
+ t.Logf("%v\n", resp)
+ }
+ }
+}
+
+const dbMiekENTNL = `; File written on Sat Apr 2 16:43:11 2016
+; dnssec_signzone version 9.10.3-P4-Ubuntu
+miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. (
+ 1282630057 ; serial
+ 14400 ; refresh (4 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 14400 ; minimum (4 hours)
+ )
+ 1800 RRSIG SOA 8 2 1800 (
+ 20160502144311 20160402144311 12051 miek.nl.
+ KegoBxA3Tbrhlc4cEdkRiteIkOfsqD4oCLLM
+ ISJ5bChWy00LGHUlAnHVu5Ti96hUjVNmGSxa
+ xtGSuAAMFCr52W8pAB8LBIlu9B6QZUPHMccr
+ SuzxAX3ioawk2uTjm+k8AGPT4RoQdXemGLAp
+ zJTASolTVmeMTh5J0sZTZJrtvZ0= )
+ 1800 NS linode.atoom.net.
+ 1800 RRSIG NS 8 2 1800 (
+ 20160502144311 20160402144311 12051 miek.nl.
+ m0cOHL6Rre/0jZPXe+0IUjs/8AFASRCvDbSx
+ ZQsRDSlZgS6RoMP3OC77cnrKDVlfZ2Vhq3Ce
+ nYPoGe0/atB92XXsilmstx4HTSU64gsV9iLN
+ Xkzk36617t7zGOl/qumqfaUXeA9tihItzEim
+ 6SGnufVZI4o8xeyaVCNDDuN0bvY= )
+ 14400 NSEC a.miek.nl. NS SOA RRSIG NSEC DNSKEY
+ 14400 RRSIG NSEC 8 2 14400 (
+ 20160502144311 20160402144311 12051 miek.nl.
+ BCWVgwxWrs4tBjS9QXKkftCUbiLi40NyH1yA
+ nbFy1wCKQ2jDH00810+ia4b66QrjlAKgxE9z
+ 9U7MKSMV86sNkyAtlCi+2OnjtWF6sxPdJO7k
+ CHeg46XBjrQuiJRY8CneQX56+IEPdufLeqPR
+ l+ocBQ2UkGhXmQdWp3CFDn2/eqU= )
+ 1800 DNSKEY 256 3 8 (
+ AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6
+ E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5EC
+ IoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb
+ 2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXH
+ Py7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz
+ ) ; ZSK; alg = RSASHA256; key id = 12051
+ 1800 DNSKEY 257 3 8 (
+ AwEAAcWdjBl4W4wh/hPxMDcBytmNCvEngIgB
+ 9Ut3C2+QI0oVz78/WK9KPoQF7B74JQ/mjO4f
+ vIncBmPp6mFNxs9/WQX0IXf7oKviEVOXLjct
+ R4D1KQLX0wprvtUIsQFIGdXaO6suTT5eDbSd
+ 6tTwu5xIkGkDmQhhH8OQydoEuCwV245ZwF/8
+ AIsqBYDNQtQ6zhd6jDC+uZJXg/9LuPOxFHbi
+ MTjp6j3CCW0kHbfM/YHZErWWtjPj3U3Z7knQ
+ SIm5PO5FRKBEYDdr5UxWJ/1/20SrzI3iztvP
+ wHDsA2rdHm/4YRzq7CvG4N0t9ac/T0a0Sxba
+ /BUX2UVPWaIVBdTRBtgHi0s=
+ ) ; KSK; alg = RSASHA256; key id = 33694
+ 1800 RRSIG DNSKEY 8 2 1800 (
+ 20160502144311 20160402144311 12051 miek.nl.
+ YNpi1jRDQKpnsQEjIjxqy+kJGaYnV16e8Iug
+ 40c82y4pee7kIojFUllSKP44qiJpCArxF557
+ tfjfwBd6c4hkqCScGPZXJ06LMyG4u//rhVMh
+ 4hyKcxzQFKxmrFlj3oQGksCI8lxGX6RxiZuR
+ qv2ol2lUWrqetpAL+Zzwt71884E= )
+ 1800 RRSIG DNSKEY 8 2 1800 (
+ 20160502144311 20160402144311 33694 miek.nl.
+ jKpLDEeyadgM0wDgzEk6sBBdWr2/aCrkAOU/
+ w6dYIafN98f21oIYQfscV1gc7CTsA0vwzzUu
+ x0QgwxoNLMvSxxjOiW/2MzF8eozczImeCWbl
+ ad/pVCYH6Jn5UBrZ5RCWMVcs2RP5KDXWeXKs
+ jEN/0EmQg5qNd4zqtlPIQinA9I1HquJAnS56
+ pFvYyGIbZmGEbhR18sXVBeTWYr+zOMHn2quX
+ 0kkrx2udz+sPg7i4yRsLdhw138gPRy1qvbaC
+ 8ELs1xo1mC9pTlDOhz24Q3iXpVAU1lXLYOh9
+ nUP1/4UvZEYXHBUQk/XPRciojniWjAF825x3
+ QoSivMHblBwRdAKJSg== )
+a.miek.nl. 1800 IN A 127.0.0.1
+ 1800 RRSIG A 8 3 1800 (
+ 20160502144311 20160402144311 12051 miek.nl.
+ lUOYdSxScjyYz+Ebc+nb6iTNgCohqj7K+Dat
+ 97KE7haV2nP3LxdYuDCJYZpeyhsXDLHd4bFI
+ bInYPwJiC6DUCxPCuCWy0KYlZOWW8KCLX3Ia
+ BOPQbvIwLsJhnX+/tyMD9mXortoqATO79/6p
+ nNxvFeM8pFDwaih17fXMuFR/BsI= )
+ 14400 NSEC a.b.c.miek.nl. A RRSIG NSEC
+ 14400 RRSIG NSEC 8 3 14400 (
+ 20160502144311 20160402144311 12051 miek.nl.
+ d5XZEy6SUp+TPRJQED+0R65zf2Yeo/1dlEA2
+ jYYvkXGSHXke4sg9nH8U3nr1rLcuqA1DsQgH
+ uMIjdENvXuZ+WCSwvIbhC+JEI6AyQ6Gfaf/D
+ I3mfu60C730IRByTrKM5C2rt11lwRQlbdaUY
+ h23/nn/q98ZKUlzqhAfkLI9pQPc= )
+a.b.c.miek.nl. 1800 IN A 127.0.0.1
+ 1800 RRSIG A 8 5 1800 (
+ 20160502144311 20160402144311 12051 miek.nl.
+ FwgU5+fFD4hEebco3gvKQt3PXfY+dcOJr8dl
+ Ky4WLsONIdhP+4e9oprPisSLxImErY21BcrW
+ xzu1IZrYDsS8XBVV44lBx5WXEKvAOrUcut/S
+ OWhFZW7ncdIQCp32ZBIatiLRJEqXUjx+guHs
+ noFLiHix35wJWsRKwjGLIhH1fbs= )
+ 14400 NSEC miek.nl. A RRSIG NSEC
+ 14400 RRSIG NSEC 8 5 14400 (
+ 20160502144311 20160402144311 12051 miek.nl.
+ lXgOqm9/jRRYvaG5jC1CDvTtGYxMroTzf4t4
+ jeYGb60+qI0q9sHQKfAJvoQ5o8o1qfR7OuiF
+ f544ipYT9eTcJRyGAOoJ37yMie7ZIoVJ91tB
+ r8YdzZ9Q6x3v1cbwTaQiacwhPZhGYOw63qIs
+ q5IQErIPos2sNk+y9D8BEce2DO4= )`
diff --git a/middleware/file/file_test.go b/middleware/file/file_test.go
index 00f667d57..87314db43 100644
--- a/middleware/file/file_test.go
+++ b/middleware/file/file_test.go
@@ -1,5 +1,16 @@
package file
+import (
+ "strings"
+ "testing"
+)
+
+func BenchmarkParseInsert(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Parse(strings.NewReader(dbMiekENTNL), testzone, "stdin")
+ }
+}
+
/*
var testDir = filepath.Join(os.TempDir(), "caddy_testdir")
var ErrCustom = errors.New("Custom Error")
diff --git a/middleware/file/lookup.go b/middleware/file/lookup.go
index aaf29834d..bc048c66a 100644
--- a/middleware/file/lookup.go
+++ b/middleware/file/lookup.go
@@ -16,7 +16,7 @@ const (
ServerFailure
)
-// Lookup looks up qname and qtype in the zone, when do is true DNSSEC are included as well.
+// Lookup looks up qname and qtype in the zone. When do is true DNSSEC records are included.
// Three sets of records are returned, one for the answer, one for authority and one for the additional section.
func (z *Zone) Lookup(qname string, qtype uint16, do bool) ([]dns.RR, []dns.RR, []dns.RR, Result) {
var rr dns.RR
@@ -34,11 +34,12 @@ func (z *Zone) Lookup(qname string, qtype uint16, do bool) ([]dns.RR, []dns.RR,
rr.Header().Rrtype = qtype
rr.Header().Name = qname
- elem := z.Tree.Get(rr)
+ elem, res := z.Tree.Get(rr)
if elem == nil {
- if elem == nil {
- return z.nameError(rr, do)
+ if res == tree.EmptyNonTerminal {
+ return z.emptyNonTerminal(rr, do)
}
+ return z.nameError(rr, do)
}
rrs := elem.Types(dns.TypeCNAME)
@@ -66,6 +67,14 @@ func (z *Zone) noData(elem *tree.Elem, do bool) ([]dns.RR, []dns.RR, []dns.RR, R
return nil, append(soa, nsec...), nil, Success
}
+func (z *Zone) emptyNonTerminal(rr dns.RR, do bool) ([]dns.RR, []dns.RR, []dns.RR, Result) {
+ soa, _, _, _ := z.lookupSOA(do)
+
+ elem := z.Tree.Prev(rr)
+ nsec := z.lookupNSEC(elem, do)
+ return nil, append(soa, nsec...), nil, Success
+}
+
func (z *Zone) nameError(rr dns.RR, do bool) ([]dns.RR, []dns.RR, []dns.RR, Result) {
// Is there a wildcard?
rr1 := dns.Copy(rr)
@@ -73,7 +82,7 @@ func (z *Zone) nameError(rr dns.RR, do bool) ([]dns.RR, []dns.RR, []dns.RR, Resu
rr1.Header().Rrtype = rr.Header().Rrtype
ce := z.ClosestEncloser(rr1)
rr1.Header().Name = "*." + ce
- elem := z.Tree.Get(rr1)
+ elem, _ := z.Tree.Get(rr1) // use result here?
if elem != nil {
ret := elem.Types(rr1.Header().Rrtype) // there can only be one of these (or zero)
@@ -127,7 +136,7 @@ func (z *Zone) lookupNSEC(elem *tree.Elem, do bool) []dns.RR {
}
func (z *Zone) lookupCNAME(rrs []dns.RR, rr dns.RR, do bool) ([]dns.RR, []dns.RR, []dns.RR, Result) {
- elem := z.Tree.Get(rr)
+ elem, _ := z.Tree.Get(rr)
if elem == nil {
return rrs, nil, nil, Success
}
diff --git a/middleware/file/lookup_test.go b/middleware/file/lookup_test.go
index 6f9f59251..df65d0150 100644
--- a/middleware/file/lookup_test.go
+++ b/middleware/file/lookup_test.go
@@ -89,36 +89,19 @@ func TestLookup(t *testing.T) {
sort.Sort(coretest.RRSet(resp.Ns))
sort.Sort(coretest.RRSet(resp.Extra))
- if resp.Rcode != tc.Rcode {
- t.Errorf("rcode is %q, expected %q", dns.RcodeToString[resp.Rcode], dns.RcodeToString[tc.Rcode])
+ if !coretest.Header(t, tc, resp) {
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
- }
-
- if !coretest.CheckSection(t, tc, coretest.Answer, resp.Answer) {
+ if !coretest.Section(t, tc, coretest.Answer, resp.Answer) {
t.Logf("%v\n", resp)
}
- if !coretest.CheckSection(t, tc, coretest.Ns, resp.Ns) {
+ if !coretest.Section(t, tc, coretest.Ns, resp.Ns) {
t.Logf("%v\n", resp)
}
- if !coretest.CheckSection(t, tc, coretest.Extra, resp.Extra) {
+ if !coretest.Section(t, tc, coretest.Extra, resp.Extra) {
t.Logf("%v\n", resp)
}
}
diff --git a/middleware/file/tree/all.go b/middleware/file/tree/all.go
index f621e3465..fd806365f 100644
--- a/middleware/file/tree/all.go
+++ b/middleware/file/tree/all.go
@@ -19,3 +19,30 @@ func (n *Node) all(found []*Elem) []*Elem {
}
return found
}
+
+// Do performs fn on all values stored in the tree. A boolean is returned indicating whether the
+// Do traversal was interrupted by an Operation returning true. If fn alters stored values' sort
+// relationships, future tree operation behaviors are undefined.
+func (t *Tree) Do(fn func(e *Elem) bool) bool {
+ if t.Root == nil {
+ return false
+ }
+ return t.Root.do(fn)
+}
+
+func (n *Node) do(fn func(e *Elem) bool) (done bool) {
+ if n.Left != nil {
+ done = n.Left.do(fn)
+ if done {
+ return
+ }
+ }
+ done = fn(n.Elem)
+ if done {
+ return
+ }
+ if n.Right != nil {
+ done = n.Right.do(fn)
+ }
+ return
+}
diff --git a/middleware/file/tree/elem.go b/middleware/file/tree/elem.go
new file mode 100644
index 000000000..4008e8380
--- /dev/null
+++ b/middleware/file/tree/elem.go
@@ -0,0 +1,121 @@
+package tree
+
+import (
+ "github.com/miekg/coredns/middleware"
+ "github.com/miekg/dns"
+)
+
+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 RRs with type qtype from e.
+func (e *Elem) Types(qtype uint16) []dns.RR {
+ if rrs, ok := e.m[qtype]; ok {
+ return rrs
+ }
+ // nodata
+ return nil
+}
+
+// All returns all RRs from e, regardless of type.
+func (e *Elem) All() []dns.RR {
+ list := []dns.RR{}
+ for _, rrs := range e.m {
+ list = append(list, rrs...)
+ }
+ return list
+}
+
+// Name returns the name for this node.
+func (e *Elem) Name() string {
+ for _, rrs := range e.m {
+ return rrs[0].Header().Name
+ }
+ return ""
+}
+
+// Insert inserts rr into e. If rr is equal to existing rrs this is a noop.
+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) {
+ if e.m == nil {
+ return true
+ }
+
+ t := rr.Header().Rrtype
+ 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
+}
+
+func Less(a *Elem, rr dns.RR) int {
+ return middleware.Less(rr.Header().Name, a.Name())
+}
+
+// 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) {
+ // TODO(miek): more types, i.e. all types. + tests for this.
+ 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
+}
diff --git a/middleware/file/tree/tree.go b/middleware/file/tree/tree.go
index ca616ad67..d0ecfcd94 100644
--- a/middleware/file/tree/tree.go
+++ b/middleware/file/tree/tree.go
@@ -16,16 +16,22 @@ package tree
// TODO(miek): locking? lockfree would be nice. Will probably go for fine grained locking on the name level.
// TODO(miek): fix docs
-import (
- "github.com/miekg/coredns/middleware"
- "github.com/miekg/dns"
-)
+import "github.com/miekg/dns"
const (
TD234 = iota
BU23
)
+// Result is a result of a Get lookup.
+type Result int
+
+const (
+ Found Result = iota
+ NameError
+ EmptyNonTerminal
+)
+
// Operation mode of the LLRB tree.
const Mode = BU23
@@ -35,119 +41,6 @@ func init() {
}
}
-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 RRs 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
-}
-
-// All returns all RRs from e, regardless of type.
-func (e *Elem) All() []dns.RR {
- list := []dns.RR{}
- for _, rrs := range e.m {
- list = append(list, rrs...)
- }
- return list
-}
-
-// Return the domain name for this element.
-func (e *Elem) Name() string {
- for _, rrs := range e.m {
- return rrs[0].Header().Name
- }
- return ""
-}
-
-// Insert inserts rr into e. If rr is equal to existing rrs this is a noop.
-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
-}
-
-func Less(a *Elem, rr dns.RR) int {
- return middleware.Less(rr.Header().Name, a.Name())
-}
-
-// 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) {
- // TODO(miek): more types, i.e. all types.
- 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
@@ -257,30 +150,36 @@ func (t *Tree) Len() int {
}
// Get returns the first match of rr in the Tree.
-func (t *Tree) Get(rr dns.RR) *Elem {
+func (t *Tree) Get(rr dns.RR) (*Elem, Result) {
if t.Root == nil {
- return nil
+ return nil, NameError
}
- n := t.Root.search(rr)
+ n, res := t.Root.search(rr)
if n == nil {
- return nil
+ return nil, res
}
- return n.Elem
+ return n.Elem, res
}
-func (n *Node) search(rr dns.RR) *Node {
+func (n *Node) search(rr dns.RR) (*Node, Result) {
+ old := n
for n != nil {
switch c := Less(n.Elem, rr); {
case c == 0:
- return n
+ return n, Found
case c < 0:
+ old = n
n = n.Left
default:
+ old = n
n = n.Right
}
}
+ if dns.CountLabel(rr.Header().Name) < dns.CountLabel(old.Elem.Name()) {
+ return n, EmptyNonTerminal
+ }
- return n
+ return n, NameError
}
// Insert inserts rr into the Tree at the first match found
@@ -397,13 +296,13 @@ 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)
+
+ el, _ := t.Get(rr)
if el == nil {
t.DeleteNode(rr)
return
}
- // delete from this element
+ // Delete from this element.
empty := el.Delete(rr)
if empty {
t.DeleteNode(rr)
@@ -454,7 +353,6 @@ func (n *Node) delete(rr dns.RR) (root *Node, d int) {
}
root = n.fixUp()
-
return
}
@@ -544,33 +442,6 @@ func (n *Node) ceil(rr dns.RR) *Node {
return n
}
-// Do performs fn on all values stored in the tree. A boolean is returned indicating whether the
-// Do traversal was interrupted by an Operation returning true. If fn alters stored values' sort
-// relationships, future tree operation behaviors are undefined.
-func (t *Tree) Do(fn func(e *Elem) bool) bool {
- if t.Root == nil {
- return false
- }
- return t.Root.do(fn)
-}
-
-func (n *Node) do(fn func(e *Elem) bool) (done bool) {
- if n.Left != nil {
- done = n.Left.do(fn)
- if done {
- return
- }
- }
- done = fn(n.Elem)
- if done {
- return
- }
- if n.Right != nil {
- done = n.Right.do(fn)
- }
- return
-}
-
/*
Copyright ©2012 The bíogo Authors. All rights reserved.
diff --git a/middleware/file/wildcard_test.go b/middleware/file/wildcard_test.go
index 1dab78226..1b502acff 100644
--- a/middleware/file/wildcard_test.go
+++ b/middleware/file/wildcard_test.go
@@ -45,7 +45,7 @@ var wildcardTestCases = []coretest.Case{
}
func TestLookupWildcard(t *testing.T) {
- zone, err := Parse(strings.NewReader(dbDnssexNl_signed), testzone1, "stdin")
+ zone, err := Parse(strings.NewReader(dbDnssexNL_signed), testzone1, "stdin")
if err != nil {
t.Fatalf("expect no error when reading zone, got %q", err)
}
@@ -68,41 +68,24 @@ func TestLookupWildcard(t *testing.T) {
sort.Sort(coretest.RRSet(resp.Ns))
sort.Sort(coretest.RRSet(resp.Extra))
- if resp.Rcode != tc.Rcode {
- t.Errorf("rcode is %q, expected %q", dns.RcodeToString[resp.Rcode], dns.RcodeToString[tc.Rcode])
+ if !coretest.Header(t, tc, resp) {
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
- }
-
- if !coretest.CheckSection(t, tc, coretest.Answer, resp.Answer) {
+ if !coretest.Section(t, tc, coretest.Answer, resp.Answer) {
t.Logf("%v\n", resp)
}
- if !coretest.CheckSection(t, tc, coretest.Ns, resp.Ns) {
+ if !coretest.Section(t, tc, coretest.Ns, resp.Ns) {
t.Logf("%v\n", resp)
}
- if !coretest.CheckSection(t, tc, coretest.Extra, resp.Extra) {
+ if !coretest.Section(t, tc, coretest.Extra, resp.Extra) {
t.Logf("%v\n", resp)
}
}
}
-const dbDnssexNl_signed = `
+const dbDnssexNL_signed = `
; File written on Tue Mar 29 21:02:24 2016
; dnssec_signzone version 9.10.3-P4-Ubuntu
dnssex.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. (
diff --git a/middleware/file/zone.go b/middleware/file/zone.go
index bac420669..1547a23b4 100644
--- a/middleware/file/zone.go
+++ b/middleware/file/zone.go
@@ -10,7 +10,7 @@ import (
type Transfer struct {
Out bool
In bool
- // more later
+ // more later?
}
type Zone struct {
@@ -22,17 +22,16 @@ type Zone struct {
Transfer *Transfer
}
+// NewZone returns a new zone.
func NewZone(name string) *Zone {
return &Zone{name: dns.Fqdn(name), Tree: &tree.Tree{}, Transfer: &Transfer{}}
}
-func (z *Zone) Insert(r dns.RR) {
- z.Tree.Insert(r)
-}
+// Insert inserts r into z.
+func (z *Zone) Insert(r dns.RR) { z.Tree.Insert(r) }
-func (z *Zone) Delete(r dns.RR) {
- z.Tree.Delete(r)
-}
+// Delete deletes r from z.
+func (z *Zone) Delete(r dns.RR) { z.Tree.Delete(r) }
// It the transfer request allowed.
func (z *Zone) TransferAllowed(state middleware.State) bool {
diff --git a/middleware/file/zone_test.go b/middleware/file/zone_test.go
index 4e3997c46..5421d1dd3 100644
--- a/middleware/file/zone_test.go
+++ b/middleware/file/zone_test.go
@@ -1,30 +1,3 @@
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))
- }
-}
+// TODO tests here.
diff --git a/middleware/testing/helpers.go b/middleware/testing/helpers.go
index dc0fa4c38..36654d7ab 100644
--- a/middleware/testing/helpers.go
+++ b/middleware/testing/helpers.go
@@ -9,10 +9,10 @@ import (
"golang.org/x/net/context"
)
-type Section int
+type Sect int
const (
- Answer Section = iota
+ Answer Sect = iota
Ns
Extra
)
@@ -59,7 +59,28 @@ func MX(rr string) *dns.MX { r, _ := dns.NewRR(rr); return r.(*dns.MX) }
func RRSIG(rr string) *dns.RRSIG { r, _ := dns.NewRR(rr); return r.(*dns.RRSIG) }
func NSEC(rr string) *dns.NSEC { r, _ := dns.NewRR(rr); return r.(*dns.NSEC) }
-func CheckSection(t *testing.T, tc Case, sect Section, rr []dns.RR) bool {
+func Header(t *testing.T, tc Case, resp *dns.Msg) bool {
+ if resp.Rcode != tc.Rcode {
+ t.Errorf("rcode is %q, expected %q", dns.RcodeToString[resp.Rcode], dns.RcodeToString[tc.Rcode])
+ return false
+ }
+
+ 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))
+ return false
+ }
+ 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))
+ return false
+ }
+ 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))
+ return false
+ }
+ return true
+}
+
+func Section(t *testing.T, tc Case, sect Sect, rr []dns.RR) bool {
section := []dns.RR{}
switch sect {
case 0: