aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md25
-rw-r--r--middleware/canonical.go15
-rw-r--r--middleware/canonical_test.go9
-rw-r--r--middleware/etcd/README.md30
-rw-r--r--middleware/file/closest.go2
-rw-r--r--middleware/file/delegation_test.go106
-rw-r--r--middleware/file/file.go11
-rw-r--r--middleware/file/lookup.go35
-rw-r--r--middleware/file/lookup_test.go10
-rw-r--r--middleware/file/secondary.go13
-rw-r--r--middleware/file/secondary_test.go6
-rw-r--r--middleware/file/tree/elem.go2
-rw-r--r--middleware/file/tree/tree.go28
-rw-r--r--middleware/file/zone.go64
14 files changed, 287 insertions, 69 deletions
diff --git a/README.md b/README.md
index 083b4c58f..b5a458097 100644
--- a/README.md
+++ b/README.md
@@ -10,27 +10,27 @@ and a few others). CoreDNS should be stable enough to provide you with a good DN
Currently CoreDNS is able to:
-* Serve zone data from a file, both DNSSEC (NSEC only atm) and DNS is supported. Delegation are
- *not* supported as yet.
+* Serve zone data from a file, both DNSSEC (NSEC only) and DNS is supported.
* Retrieve zone data from primaries, i.e. act as a secondary server.
+* Loadbalancing of responses.
* Allow for zone transfers, i.e. act as a primary server.
-* Use Etcd as a backend, i.e. a 92% replacement for
+* Use etcd as a backend, i.e. a 94.5% replacement for
[SkyDNS](https://github.com/skynetservices/skydns).
* Serve as a proxy to forward queries to some other (recursive) nameserver.
* Rewrite queries (both qtype, qclass and qname).
* Provide metrics (by using Prometheus).
* Provide Logging.
-* Provide load-balancing of returned responses.
+* Provide load-balancing (A/AAAA shuffling) of returned responses.
* Has support for the CH class: `version.bind` and friends.
-There are corner cases not implemented and a few [issues](https://github.com/miekg/coredns/issues).
+There are still few [issues](https://github.com/miekg/coredns/issues), and work is ongoing on making
+things fast and reduce the memory usage.
-But all in all, CoreDNS should already be able to provide you with enough functionality to replace
-parts of BIND9, Knot, NSD or PowerDNS.
-
-However CoreDNS is still in the early stages of development. For now most documentation is in the
-source and some blog articles can be [found here](https://miek.nl/tags/coredns/). If you do want to
-use CoreDNS in production, please let us know and how we can help.
+All in all, CoreDNS should be able to provide you with enough functionality to replace parts of
+BIND9, Knot, NSD or PowerDNS.
+Most documentation is in the source and some blog articles can be [found
+here](https://miek.nl/tags/coredns/). If you do want to use CoreDNS in production, please let us
+know and how we can help.
<https://caddyserver.com/> is also full of examples on how to structure a Corefile (renamed from
Caddyfile when I forked it).
@@ -86,10 +86,9 @@ All the above examples are possible with the *current* CoreDNS.
* Website?
* Logo?
-* Code simplifications/refactors.
* Optimizations.
* Load testing.
-* All the [issues](https://github.com/miekg/coredns/issues).
+* The [issues](https://github.com/miekg/coredns/issues).
## Blog
diff --git a/middleware/canonical.go b/middleware/canonical.go
index 5806426ab..e26e3c6c4 100644
--- a/middleware/canonical.go
+++ b/middleware/canonical.go
@@ -12,6 +12,8 @@ import (
//
// See http://bert-hubert.blogspot.co.uk/2015/10/how-to-do-fast-canonical-ordering-of.html
// for a blog article on this implementation.
+//
+// The values of a and b are *not* lowercased before the comparison!
func Less(a, b string) int {
i := 1
aj := len(a)
@@ -22,11 +24,12 @@ func Less(a, b string) int {
if oka && okb {
return 0
}
- // sadly this []byte will allocate...
+ // sadly this []byte will allocate... TODO(miek): check if this is needed
+ // for a name, otherwise compare the strings.
ab := []byte(a[ai:aj])
- toLowerAndDDD(ab)
bb := []byte(b[bi:bj])
- toLowerAndDDD(bb)
+ doDDD(ab)
+ doDDD(bb)
res := bytes.Compare(ab, bb)
if res != 0 {
@@ -39,13 +42,9 @@ func Less(a, b string) int {
return 0
}
-func toLowerAndDDD(b []byte) {
+func doDDD(b []byte) {
lb := len(b)
for i := 0; i < lb; i++ {
- if b[i] >= 'A' && b[i] <= 'Z' {
- b[i] += 32
- continue
- }
if i+3 < lb && b[i] == '\\' && isDigit(b[i+1]) && isDigit(b[i+2]) && isDigit(b[i+3]) {
b[i] = dddToByte(b[i:])
for j := i + 1; j < lb-3; j++ {
diff --git a/middleware/canonical_test.go b/middleware/canonical_test.go
index 390878a67..1a9fd0859 100644
--- a/middleware/canonical_test.go
+++ b/middleware/canonical_test.go
@@ -2,6 +2,7 @@ package middleware
import (
"sort"
+ "strings"
"testing"
)
@@ -53,6 +54,14 @@ func TestLess(t *testing.T) {
Tests:
for j, test := range tests {
+ // Need to lowercase these example as the Less function does lowercase for us anymore.
+ for i, b := range test.in {
+ test.in[i] = strings.ToLower(b)
+ }
+ for i, b := range test.out {
+ test.out[i] = strings.ToLower(b)
+ }
+
sort.Sort(set(test.in))
for i := 0; i < len(test.in); i++ {
if test.in[i] != test.out[i] {
diff --git a/middleware/etcd/README.md b/middleware/etcd/README.md
index e5c1d3997..f700aece5 100644
--- a/middleware/etcd/README.md
+++ b/middleware/etcd/README.md
@@ -2,10 +2,10 @@
`etcd` enabled reading zone data from an etcd instance. The data in etcd has to be encoded as
a [message](https://github.com/skynetservices/skydns/blob/2fcff74cdc9f9a7dd64189a447ef27ac354b725f/msg/service.go#L26)
-like [SkyDNS](https//github.com/skynetservices/skydns).
+like [SkyDNS](https//github.com/skynetservices/skydns). It should also work just like SkyDNS.
-The etcd middleware makes extensive use of the proxy middleware to forward and query
-other servers in the network.
+The etcd middleware makes extensive use of the proxy middleware to forward and query other servers
+in the network.
## Syntax
@@ -15,7 +15,7 @@ etcd [zones...]
* `zones` zones etcd should be authoritative for.
-The will default to `/skydns` as the path and the local etcd proxy (http://127.0.0.1:2379).
+The path will default to `/skydns` the local etcd proxy (http://127.0.0.1:2379).
If no zones are specified the block's zone will be used as the zone.
If you want to `round robin` A and AAAA responses look at the `loadbalance` middleware.
@@ -30,10 +30,28 @@ etcd [zones...] {
}
~~~
-* `stubzones` enable the stub zones feature.
+* `stubzones` enable the stub zones feature. The stubzone is *only* done in the etcd tree located
+ under the *first* zone specified.
* `path` the path inside etcd, defaults to "/skydns".
* `endpoint` the etcd endpoints, default to "http://localhost:2397".
-* `upstream` upstream resolvers to be used resolve external names found in etcd.
+* `upstream` upstream resolvers to be used resolve external names found in etcd, think CNAMEs
+ pointing to external names. If you want CoreDNS to act as a proxy for clients you'll need to add
+ the proxy middleware.
* `tls` followed the cert, key and the CA's cert filenames.
## Examples
+
+This is the default SkyDNS setup, with everying specified in full:
+
+~~~
+.:53 {
+ etcd {
+ stubzones
+ path /skydns
+ endpoint http://localhost:2397
+ upstream 8.8.8.8:53 8.8.4.4:53
+ }
+ loadbalance
+ proxy . 8.8.8.8:53 8.8.4.4:53
+}
+~~~
diff --git a/middleware/file/closest.go b/middleware/file/closest.go
index ab0cbb00a..6af033e64 100644
--- a/middleware/file/closest.go
+++ b/middleware/file/closest.go
@@ -17,7 +17,7 @@ func (z *Zone) ClosestEncloser(qname string, qtype uint16) string {
offset, end = dns.NextLabel(qname, offset)
}
- return z.SOA.Header().Name
+ return z.Apex.SOA.Header().Name
}
// nameErrorProof finds the closest encloser and return an NSEC that proofs
diff --git a/middleware/file/delegation_test.go b/middleware/file/delegation_test.go
new file mode 100644
index 000000000..d7286a3bd
--- /dev/null
+++ b/middleware/file/delegation_test.go
@@ -0,0 +1,106 @@
+package file
+
+import (
+ "sort"
+ "strings"
+ "testing"
+
+ "github.com/miekg/coredns/middleware"
+ "github.com/miekg/coredns/middleware/test"
+
+ "github.com/miekg/dns"
+ "golang.org/x/net/context"
+)
+
+var delegationTestCases = []test.Case{
+ {
+ Qname: "a.delegated.miek.nl.", Qtype: dns.TypeTXT,
+ Ns: []dns.RR{
+ test.NS("delegated.miek.nl. 1800 IN NS a.delegated.miek.nl."),
+ test.NS("delegated.miek.nl. 1800 IN NS ns-ext.nlnetlabs.nl."),
+ },
+ Extra: []dns.RR{
+ test.A("a.delegated.miek.nl. 1800 IN A 139.162.196.78"),
+ test.AAAA("a.delegated.miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
+ },
+ },
+ {
+ Qname: "delegated.miek.nl.", Qtype: dns.TypeNS,
+ Answer: []dns.RR{
+ test.NS("delegated.miek.nl. 1800 IN NS a.delegated.miek.nl."),
+ test.NS("delegated.miek.nl. 1800 IN NS ns-ext.nlnetlabs.nl."),
+ },
+ },
+}
+
+func TestLookupDelegation(t *testing.T) {
+ zone, err := Parse(strings.NewReader(dbMiekNL_delegation), testzone, "stdin")
+ if err != nil {
+ t.Fatalf("expect no error when reading zone, got %q", err)
+ }
+
+ fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{testzone: zone}, Names: []string{testzone}}}
+ ctx := context.TODO()
+
+ for _, tc := range delegationTestCases {
+ m := tc.Msg()
+
+ rec := middleware.NewResponseRecorder(&test.ResponseWriter{})
+ _, err := fm.ServeDNS(ctx, rec, m)
+ if err != nil {
+ t.Errorf("expected no error, got %v\n", err)
+ return
+ }
+ resp := rec.Msg()
+
+ sort.Sort(test.RRSet(resp.Answer))
+ sort.Sort(test.RRSet(resp.Ns))
+ sort.Sort(test.RRSet(resp.Extra))
+
+ if !test.Header(t, tc, resp) {
+ t.Logf("%v\n", resp)
+ continue
+ }
+ if !test.Section(t, tc, test.Answer, resp.Answer) {
+ t.Logf("%v\n", resp)
+ }
+ if !test.Section(t, tc, test.Ns, resp.Ns) {
+ t.Logf("%v\n", resp)
+ }
+ if !test.Section(t, tc, test.Extra, resp.Extra) {
+ t.Logf("%v\n", resp)
+ }
+ }
+}
+
+const dbMiekNL_delegation = `
+$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.
+
+delegated IN NS a.delegated
+ IN NS ns-ext.nlnetlabs.nl.
+
+a.delegated IN TXT "obscured"
+ 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`
diff --git a/middleware/file/file.go b/middleware/file/file.go
index e44fa4cdb..441e8b94d 100644
--- a/middleware/file/file.go
+++ b/middleware/file/file.go
@@ -80,18 +80,15 @@ func (f File) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i
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:
- m.Answer = answer
- m.Ns = ns
- m.Extra = extra
+ case NoData:
case NameError:
- m.Ns = ns
m.Rcode = dns.RcodeNameError
- fallthrough
- case NoData:
- m.Ns = ns
+ case Delegation:
+ m.Authoritative = false
case ServerFailure:
return dns.RcodeServerFailure, nil
}
diff --git a/middleware/file/lookup.go b/middleware/file/lookup.go
index ddbfec6f5..f6f813d3d 100644
--- a/middleware/file/lookup.go
+++ b/middleware/file/lookup.go
@@ -12,6 +12,7 @@ type Result int
const (
Success Result = iota
NameError
+ Delegation
NoData
ServerFailure
)
@@ -22,6 +23,9 @@ func (z *Zone) Lookup(qname string, qtype uint16, do bool) ([]dns.RR, []dns.RR,
if qtype == dns.TypeSOA {
return z.lookupSOA(do)
}
+ if qtype == dns.TypeNS && qname == z.origin {
+ return z.lookupNS(do)
+ }
elem, res := z.Tree.Search(qname, qtype)
if elem == nil {
@@ -30,6 +34,21 @@ func (z *Zone) Lookup(qname string, qtype uint16, do bool) ([]dns.RR, []dns.RR,
}
return z.nameError(qname, qtype, do)
}
+ if res == tree.Delegation {
+ rrs := elem.Types(dns.TypeNS)
+ glue := []dns.RR{}
+ for _, ns := range rrs {
+ if dns.IsSubDomain(ns.Header().Name, ns.(*dns.NS).Ns) {
+ // even with Do, this should be unsigned.
+ elem, res := z.Tree.SearchGlue(ns.(*dns.NS).Ns)
+ if res == tree.Found {
+ glue = append(glue, elem.Types(dns.TypeAAAA)...)
+ glue = append(glue, elem.Types(dns.TypeA)...)
+ }
+ }
+ }
+ return nil, rrs, glue, Delegation
+ }
rrs := elem.Types(dns.TypeCNAME)
if len(rrs) > 0 { // should only ever be 1 actually; TODO(miek) check for this?
@@ -87,9 +106,9 @@ func (z *Zone) nameError(qname string, qtype uint16, do bool) ([]dns.RR, []dns.R
}
// name error
- ret := []dns.RR{z.SOA}
+ ret := []dns.RR{z.Apex.SOA}
if do {
- ret = append(ret, z.SIG...)
+ ret = append(ret, z.Apex.SIGSOA...)
ret = append(ret, z.nameErrorProof(qname, qtype)...)
}
return nil, ret, nil, NameError
@@ -97,10 +116,18 @@ func (z *Zone) nameError(qname string, qtype uint16, do bool) ([]dns.RR, []dns.R
func (z *Zone) lookupSOA(do bool) ([]dns.RR, []dns.RR, []dns.RR, Result) {
if do {
- ret := append([]dns.RR{z.SOA}, z.SIG...)
+ ret := append([]dns.RR{z.Apex.SOA}, z.Apex.SIGSOA...)
+ return ret, nil, nil, Success
+ }
+ return []dns.RR{z.Apex.SOA}, nil, nil, Success
+}
+
+func (z *Zone) lookupNS(do bool) ([]dns.RR, []dns.RR, []dns.RR, Result) {
+ if do {
+ ret := append(z.Apex.NS, z.Apex.SIGNS...)
return ret, nil, nil, Success
}
- return []dns.RR{z.SOA}, nil, nil, Success
+ return z.Apex.NS, nil, nil, Success
}
// lookupNSEC looks up nsec and sigs.
diff --git a/middleware/file/lookup_test.go b/middleware/file/lookup_test.go
index 53ca71001..cc5cd9db2 100644
--- a/middleware/file/lookup_test.go
+++ b/middleware/file/lookup_test.go
@@ -36,6 +36,12 @@ var dnsTestCases = []test.Case{
},
},
{
+ Qname: "mIeK.NL.", Qtype: dns.TypeAAAA,
+ Answer: []dns.RR{
+ test.AAAA("miek.nl. 1800 IN AAAA 2a01:7e00::f03c:91ff:fef1:6735"),
+ },
+ },
+ {
Qname: "miek.nl.", Qtype: dns.TypeMX,
Answer: []dns.RR{
test.MX("miek.nl. 1800 IN MX 1 aspmx.l.google.com."),
@@ -166,8 +172,8 @@ $ORIGIN miek.nl.
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
+ 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
diff --git a/middleware/file/secondary.go b/middleware/file/secondary.go
index 8e79549e0..5b1fe0cf2 100644
--- a/middleware/file/secondary.go
+++ b/middleware/file/secondary.go
@@ -55,8 +55,7 @@ Transfer:
}
z.Tree = z1.Tree
- z.SOA = z1.SOA
- z.SIG = z1.SIG
+ z.Apex = z1.Apex
*z.Expired = false
log.Printf("[INFO] Transferred: %s from %s", z.origin, tr)
return nil
@@ -91,7 +90,7 @@ Transfer:
if serial == -1 {
return false, Err
}
- return less(z.SOA.Serial, uint32(serial)), Err
+ return less(z.Apex.SOA.Serial, uint32(serial)), Err
}
// less return true of a is smaller than b when taking RFC 1982 serial arithmetic into account.
@@ -108,15 +107,15 @@ func less(a, b uint32) bool {
// will be marked expired.
func (z *Zone) Update() error {
// If we don't have a SOA, we don't have a zone, wait for it to appear.
- for z.SOA == nil {
+ for z.Apex.SOA == nil {
time.Sleep(1 * time.Second)
}
retryActive := false
Restart:
- refresh := time.Second * time.Duration(z.SOA.Refresh)
- retry := time.Second * time.Duration(z.SOA.Retry)
- expire := time.Second * time.Duration(z.SOA.Expire)
+ refresh := time.Second * time.Duration(z.Apex.SOA.Refresh)
+ retry := time.Second * time.Duration(z.Apex.SOA.Retry)
+ expire := time.Second * time.Duration(z.Apex.SOA.Expire)
if refresh < time.Hour {
refresh = time.Hour
diff --git a/middleware/file/secondary_test.go b/middleware/file/secondary_test.go
index ce0f4004f..aa408b37b 100644
--- a/middleware/file/secondary_test.go
+++ b/middleware/file/secondary_test.go
@@ -85,7 +85,7 @@ func TestShouldTransfer(t *testing.T) {
z.TransferFrom = []string{addrstr}
// Serial smaller
- z.SOA = test.SOA(fmt.Sprintf("%s IN SOA bla. bla. %d 0 0 0 0 ", testZone, soa.serial-1))
+ z.Apex.SOA = test.SOA(fmt.Sprintf("%s IN SOA bla. bla. %d 0 0 0 0 ", testZone, soa.serial-1))
should, err := z.shouldTransfer()
if err != nil {
t.Fatalf("unable to run shouldTransfer: %v", err)
@@ -94,7 +94,7 @@ func TestShouldTransfer(t *testing.T) {
t.Fatalf("shouldTransfer should return true for serial: %q", soa.serial-1)
}
// Serial equal
- z.SOA = test.SOA(fmt.Sprintf("%s IN SOA bla. bla. %d 0 0 0 0 ", testZone, soa.serial))
+ z.Apex.SOA = test.SOA(fmt.Sprintf("%s IN SOA bla. bla. %d 0 0 0 0 ", testZone, soa.serial))
should, err = z.shouldTransfer()
if err != nil {
t.Fatalf("unable to run shouldTransfer: %v", err)
@@ -125,7 +125,7 @@ func TestTransferIn(t *testing.T) {
if err != nil {
t.Fatalf("unable to run TransferIn: %v", err)
}
- if z.SOA.String() != fmt.Sprintf("%s 3600 IN SOA bla. bla. 250 0 0 0 0", testZone) {
+ if z.Apex.SOA.String() != fmt.Sprintf("%s 3600 IN SOA bla. bla. 250 0 0 0 0", testZone) {
t.Fatalf("unknown SOA transferred")
}
}
diff --git a/middleware/file/tree/elem.go b/middleware/file/tree/elem.go
index 8698a9317..6785a6849 100644
--- a/middleware/file/tree/elem.go
+++ b/middleware/file/tree/elem.go
@@ -91,7 +91,7 @@ func (e *Elem) Delete(rr dns.RR) (empty bool) {
return
}
-// Less is a tree helper function that calles middleware.Less.
+// Less is a tree helper function that calls middleware.Less.
func Less(a *Elem, name string) int { return middleware.Less(name, a.Name()) }
// Assuming the same type and name this will check if the rdata is equal as well.
diff --git a/middleware/file/tree/tree.go b/middleware/file/tree/tree.go
index c3e437ec4..99560cda1 100644
--- a/middleware/file/tree/tree.go
+++ b/middleware/file/tree/tree.go
@@ -30,6 +30,7 @@ const (
Found Result = iota
NameError
EmptyNonTerminal
+ Delegation
)
// Operation mode of the LLRB tree.
@@ -154,16 +155,35 @@ func (t *Tree) Search(qname string, qtype uint16) (*Elem, Result) {
if t.Root == nil {
return nil, NameError
}
- n, res := t.Root.search(qname, qtype)
+ n, res := t.Root.search(qname, qtype, false)
if n == nil {
return nil, res
}
return n.Elem, res
}
-func (n *Node) search(qname string, qtype uint16) (*Node, Result) {
+// SearchGlue returns the first match of qname/(A/AAAA) in the Tree.
+func (t *Tree) SearchGlue(qname string) (*Elem, Result) {
+ // TODO(miek): shouldn't need this, because when we *find* the delegation, we
+ // know for sure that any glue is under it. Should change the return values
+ // to return the node, so we can resume from those.
+ if t.Root == nil {
+ return nil, NameError
+ }
+ n, res := t.Root.search(qname, dns.TypeA, true)
+ if n == nil {
+ return nil, res
+ }
+ return n.Elem, res
+}
+
+// search searches the tree for qname and type. If glue is true the search *does* not
+// spot when hitting NS records, but descends in search of glue. The qtype for this
+// kind of search can only be AAAA or A.
+func (n *Node) search(qname string, qtype uint16, glue bool) (*Node, Result) {
old := n
for n != nil {
+
switch c := Less(n.Elem, qname); {
case c == 0:
return n, Found
@@ -171,6 +191,10 @@ func (n *Node) search(qname string, qtype uint16) (*Node, Result) {
old = n
n = n.Left
default:
+ if !glue && n.Elem.Types(dns.TypeNS) != nil {
+ return n, Delegation
+
+ }
old = n
n = n.Right
}
diff --git a/middleware/file/zone.go b/middleware/file/zone.go
index 043fd5c6a..155d7690c 100644
--- a/middleware/file/zone.go
+++ b/middleware/file/zone.go
@@ -5,6 +5,7 @@ import (
"log"
"os"
"path"
+ "strings"
"sync"
"github.com/miekg/coredns/middleware"
@@ -15,11 +16,10 @@ import (
)
type Zone struct {
- SOA *dns.SOA
- SIG []dns.RR
origin string
file string
*tree.Tree
+ Apex Apex
TransferTo []string
StartupOnce sync.Once
@@ -31,6 +31,13 @@ type Zone struct {
// TODO: shutdown watcher channel
}
+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), file: path.Clean(file), Tree: &tree.Tree{}, Expired: new(bool)}
@@ -44,28 +51,50 @@ func (z *Zone) Copy() *Zone {
z1.TransferTo = z.TransferTo
z1.TransferFrom = z.TransferFrom
z1.Expired = z.Expired
- z1.SOA = z.SOA
- z1.SIG = z.SIG
+ 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:
- z.SOA = r.(*dns.SOA)
+ 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")
case dns.TypeRRSIG:
- if x, ok := r.(*dns.RRSIG); ok && x.TypeCovered == dns.TypeSOA {
- z.SIG = append(z.SIG, x)
+ 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
+ }
}
- fallthrough
- default:
- z.Tree.Insert(r)
+ 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
}
@@ -88,16 +117,22 @@ func (z *Zone) TransferAllowed(state middleware.State) bool {
func (z *Zone) All() []dns.RR {
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.SIG) > 0 {
- records = append(z.SIG, records...)
+ 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.SOA}, records...)
+ return append([]dns.RR{z.Apex.SOA}, records...)
}
func (z *Zone) Reload(shutdown chan bool) error {
@@ -132,8 +167,7 @@ func (z *Zone) Reload(shutdown chan bool) error {
continue
}
// copy elements we need
- z.SOA = zone.SOA
- z.SIG = zone.SIG
+ z.Apex = zone.Apex
z.Tree = zone.Tree
z.reloadMu.Unlock()
log.Printf("[INFO] Successfully reloaded zone `%s'", z.origin)