diff options
-rw-r--r-- | README.md | 25 | ||||
-rw-r--r-- | middleware/canonical.go | 15 | ||||
-rw-r--r-- | middleware/canonical_test.go | 9 | ||||
-rw-r--r-- | middleware/etcd/README.md | 30 | ||||
-rw-r--r-- | middleware/file/closest.go | 2 | ||||
-rw-r--r-- | middleware/file/delegation_test.go | 106 | ||||
-rw-r--r-- | middleware/file/file.go | 11 | ||||
-rw-r--r-- | middleware/file/lookup.go | 35 | ||||
-rw-r--r-- | middleware/file/lookup_test.go | 10 | ||||
-rw-r--r-- | middleware/file/secondary.go | 13 | ||||
-rw-r--r-- | middleware/file/secondary_test.go | 6 | ||||
-rw-r--r-- | middleware/file/tree/elem.go | 2 | ||||
-rw-r--r-- | middleware/file/tree/tree.go | 28 | ||||
-rw-r--r-- | middleware/file/zone.go | 64 |
14 files changed, 287 insertions, 69 deletions
@@ -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) |