aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar jeremiejig <me@jeremiejig.fr> 2023-04-22 22:32:01 +0200
committerGravatar GitHub <noreply@github.com> 2023-04-22 22:32:01 +0200
commit13e66918e3f1221a0da8f562bdafbec43c3e8f24 (patch)
tree52baddd90d177c8973072865386890d24ef6cf27
parent0862dd1cb5609bf2d55c566a010d42568178b9ae (diff)
downloadcoredns-13e66918e3f1221a0da8f562bdafbec43c3e8f24.tar.gz
coredns-13e66918e3f1221a0da8f562bdafbec43c3e8f24.tar.zst
coredns-13e66918e3f1221a0da8f562bdafbec43c3e8f24.zip
plugin/dnssec: on delegation, sign DS or NSEC of no DS. (#5899)
* When returning NS for delegation point, we sign any DS Record or if not found we generate a NSEC proving absence of DS. This follow behaviour describe in rfc4035 (Section 3.1.4) * DS request at apex behave as before. * Fix edge case of requesting NSEC which prove that NSEC does not exist. Signed-off-by: Jeremiejig <me@jeremiejig.fr>
-rw-r--r--plugin/dnssec/black_lies.go23
-rw-r--r--plugin/dnssec/black_lies_test.go176
-rw-r--r--plugin/dnssec/dnssec.go20
-rw-r--r--plugin/dnssec/dnssec_test.go82
-rw-r--r--plugin/dnssec/handler_test.go73
5 files changed, 365 insertions, 9 deletions
diff --git a/plugin/dnssec/black_lies.go b/plugin/dnssec/black_lies.go
index 68fae22bb..d01fa7c84 100644
--- a/plugin/dnssec/black_lies.go
+++ b/plugin/dnssec/black_lies.go
@@ -1,6 +1,8 @@
package dnssec
import (
+ "strings"
+
"github.com/coredns/coredns/plugin/pkg/response"
"github.com/coredns/coredns/request"
@@ -11,15 +13,27 @@ import (
// See https://tools.ietf.org/html/draft-valsorda-dnsop-black-lies-00
// For example, a request for the non-existing name a.example.com would
// cause the following NSEC record to be generated:
+//
// a.example.com. 3600 IN NSEC \000.a.example.com. ( RRSIG NSEC ... )
+//
// This inturn makes every NXDOMAIN answer a NODATA one, don't forget to flip
// the header rcode to NOERROR.
func (d Dnssec) nsec(state request.Request, mt response.Type, ttl, incep, expir uint32, server string) ([]dns.RR, error) {
nsec := &dns.NSEC{}
nsec.Hdr = dns.RR_Header{Name: state.QName(), Ttl: ttl, Class: dns.ClassINET, Rrtype: dns.TypeNSEC}
nsec.NextDomain = "\\000." + state.QName()
+ if state.QName() == "." {
+ nsec.NextDomain = "\\000." // If You want to play as root server
+ }
if state.Name() == state.Zone {
nsec.TypeBitMap = filter18(state.QType(), apexBitmap, mt)
+ } else if mt == response.Delegation || state.QType() == dns.TypeDS {
+ nsec.TypeBitMap = delegationBitmap[:]
+ if mt == response.Delegation {
+ labels := dns.SplitDomainName(state.QName())
+ labels[0] += "\\000"
+ nsec.NextDomain = strings.Join(labels, ".") + "."
+ }
} else {
nsec.TypeBitMap = filter14(state.QType(), zoneBitmap, mt)
}
@@ -34,13 +48,14 @@ func (d Dnssec) nsec(state request.Request, mt response.Type, ttl, incep, expir
// The NSEC bit maps we return.
var (
- zoneBitmap = [...]uint16{dns.TypeA, dns.TypeHINFO, dns.TypeTXT, dns.TypeAAAA, dns.TypeLOC, dns.TypeSRV, dns.TypeCERT, dns.TypeSSHFP, dns.TypeRRSIG, dns.TypeNSEC, dns.TypeTLSA, dns.TypeHIP, dns.TypeOPENPGPKEY, dns.TypeSPF}
- apexBitmap = [...]uint16{dns.TypeA, dns.TypeNS, dns.TypeSOA, dns.TypeHINFO, dns.TypeMX, dns.TypeTXT, dns.TypeAAAA, dns.TypeLOC, dns.TypeSRV, dns.TypeCERT, dns.TypeSSHFP, dns.TypeRRSIG, dns.TypeNSEC, dns.TypeDNSKEY, dns.TypeTLSA, dns.TypeHIP, dns.TypeOPENPGPKEY, dns.TypeSPF}
+ delegationBitmap = [...]uint16{dns.TypeA, dns.TypeNS, dns.TypeHINFO, dns.TypeTXT, dns.TypeAAAA, dns.TypeLOC, dns.TypeSRV, dns.TypeCERT, dns.TypeSSHFP, dns.TypeRRSIG, dns.TypeNSEC, dns.TypeTLSA, dns.TypeHIP, dns.TypeOPENPGPKEY, dns.TypeSPF}
+ zoneBitmap = [...]uint16{dns.TypeA, dns.TypeHINFO, dns.TypeTXT, dns.TypeAAAA, dns.TypeLOC, dns.TypeSRV, dns.TypeCERT, dns.TypeSSHFP, dns.TypeRRSIG, dns.TypeNSEC, dns.TypeTLSA, dns.TypeHIP, dns.TypeOPENPGPKEY, dns.TypeSPF}
+ apexBitmap = [...]uint16{dns.TypeA, dns.TypeNS, dns.TypeSOA, dns.TypeHINFO, dns.TypeMX, dns.TypeTXT, dns.TypeAAAA, dns.TypeLOC, dns.TypeSRV, dns.TypeCERT, dns.TypeSSHFP, dns.TypeRRSIG, dns.TypeNSEC, dns.TypeDNSKEY, dns.TypeTLSA, dns.TypeHIP, dns.TypeOPENPGPKEY, dns.TypeSPF}
)
// filter14 filters out t from bitmap (if it exists). If mt is not an NODATA response, just return the entire bitmap.
func filter14(t uint16, bitmap [14]uint16, mt response.Type) []uint16 {
- if mt != response.NoData && mt != response.NameError {
+ if mt != response.NoData && mt != response.NameError || t == dns.TypeNSEC {
return zoneBitmap[:]
}
for i := range bitmap {
@@ -52,7 +67,7 @@ func filter14(t uint16, bitmap [14]uint16, mt response.Type) []uint16 {
}
func filter18(t uint16, bitmap [18]uint16, mt response.Type) []uint16 {
- if mt != response.NoData && mt != response.NameError {
+ if mt != response.NoData && mt != response.NameError || t == dns.TypeNSEC {
return apexBitmap[:]
}
for i := range bitmap {
diff --git a/plugin/dnssec/black_lies_test.go b/plugin/dnssec/black_lies_test.go
index a9a29029e..de381e59f 100644
--- a/plugin/dnssec/black_lies_test.go
+++ b/plugin/dnssec/black_lies_test.go
@@ -71,16 +71,188 @@ func TestBlackLiesNoError(t *testing.T) {
}
}
+func TestBlackLiesApexNsec(t *testing.T) {
+ d, rm1, rm2 := newDnssec(t, []string{"miek.nl."})
+ defer rm1()
+ defer rm2()
+
+ m := testNsecMsg()
+ m.SetQuestion("miek.nl.", dns.TypeNSEC)
+ state := request.Request{Req: m, Zone: "miek.nl."}
+ m = d.Sign(state, time.Now().UTC(), server)
+ if len(m.Ns) > 0 {
+ t.Error("Authority section should be empty")
+ }
+ if len(m.Answer) != 2 {
+ t.Errorf("Answer section should have 2 RRs")
+ }
+ sig, nsec := false, false
+ for _, rr := range m.Answer {
+ if _, ok := rr.(*dns.RRSIG); ok {
+ sig = true
+ }
+ if rnsec, ok := rr.(*dns.NSEC); ok {
+ nsec = true
+ var bitpresent uint
+ for _, typeBit := range rnsec.TypeBitMap {
+ switch typeBit {
+ case dns.TypeSOA:
+ bitpresent |= 4
+ case dns.TypeNSEC:
+ bitpresent |= 1
+ case dns.TypeRRSIG:
+ bitpresent |= 2
+ }
+ }
+ if bitpresent != 7 {
+ t.Error("NSEC must have SOA, RRSIG and NSEC in its bitmap")
+ }
+ }
+ }
+ if !sig || !nsec {
+ t.Errorf("Expected RRSIG and NSEC in answer section")
+ }
+}
+
+func TestBlackLiesNsec(t *testing.T) {
+ d, rm1, rm2 := newDnssec(t, []string{"miek.nl."})
+ defer rm1()
+ defer rm2()
+
+ m := testNsecMsg()
+ m.SetQuestion("www.miek.nl.", dns.TypeNSEC)
+ state := request.Request{Req: m, Zone: "miek.nl."}
+ m = d.Sign(state, time.Now().UTC(), server)
+ if len(m.Ns) > 0 {
+ t.Error("Authority section should be empty")
+ }
+ if len(m.Answer) != 2 {
+ t.Errorf("Answer section should have 2 RRs")
+ }
+ sig, nsec := false, false
+ for _, rr := range m.Answer {
+ if _, ok := rr.(*dns.RRSIG); ok {
+ sig = true
+ }
+ if rnsec, ok := rr.(*dns.NSEC); ok {
+ nsec = true
+ var bitpresent uint
+ for _, typeBit := range rnsec.TypeBitMap {
+ switch typeBit {
+ case dns.TypeNSEC:
+ bitpresent |= 1
+ case dns.TypeRRSIG:
+ bitpresent |= 2
+ }
+ }
+ if bitpresent != 3 {
+ t.Error("NSEC must have RRSIG and NSEC in its bitmap")
+ }
+ }
+ }
+ if !sig || !nsec {
+ t.Errorf("Expected RRSIG and NSEC in answer section")
+ }
+}
+
+func TestBlackLiesApexDS(t *testing.T) {
+ d, rm1, rm2 := newDnssec(t, []string{"miek.nl."})
+ defer rm1()
+ defer rm2()
+
+ m := testApexDSMsg()
+ m.SetQuestion("miek.nl.", dns.TypeDS)
+ state := request.Request{Req: m, Zone: "miek.nl."}
+ m = d.Sign(state, time.Now().UTC(), server)
+ if !section(m.Ns, 2) {
+ t.Errorf("Authority section should have 2 sigs")
+ }
+ var nsec *dns.NSEC
+ for _, r := range m.Ns {
+ if r.Header().Rrtype == dns.TypeNSEC {
+ nsec = r.(*dns.NSEC)
+ }
+ }
+ if nsec == nil {
+ t.Error("Expected NSEC, got none")
+ } else if correctNsecForDS(nsec) {
+ t.Error("NSEC DS at the apex zone should cover all apex type.")
+ }
+}
+
+func TestBlackLiesDS(t *testing.T) {
+ d, rm1, rm2 := newDnssec(t, []string{"miek.nl."})
+ defer rm1()
+ defer rm2()
+
+ m := testApexDSMsg()
+ m.SetQuestion("sub.miek.nl.", dns.TypeDS)
+ state := request.Request{Req: m, Zone: "miek.nl."}
+ m = d.Sign(state, time.Now().UTC(), server)
+ if !section(m.Ns, 2) {
+ t.Errorf("Authority section should have 2 sigs")
+ }
+ var nsec *dns.NSEC
+ for _, r := range m.Ns {
+ if r.Header().Rrtype == dns.TypeNSEC {
+ nsec = r.(*dns.NSEC)
+ }
+ }
+ if nsec == nil {
+ t.Error("Expected NSEC, got none")
+ } else if !correctNsecForDS(nsec) {
+ t.Error("NSEC DS should cover delegation type only.")
+ }
+}
+
+func correctNsecForDS(nsec *dns.NSEC) bool {
+ var bitmask uint
+ /* Coherent TypeBitMap for NSEC of DS should contain at least:
+ * {TypeNS, TypeNSEC, TypeRRSIG} and no SOA.
+ * Any missing type will confuse resolver because
+ * it will prove that the dns query cannot be a delegation point,
+ * which will break trust resolution for unsigned delegated domain.
+ * No SOA is obvious for none apex query.
+ */
+ for _, typeBitmask := range nsec.TypeBitMap {
+ switch typeBitmask {
+ case dns.TypeNS:
+ bitmask |= 1
+ case dns.TypeNSEC:
+ bitmask |= 2
+ case dns.TypeRRSIG:
+ bitmask |= 4
+ case dns.TypeSOA:
+ return false
+ }
+ }
+ return bitmask == 7
+}
+
func testNxdomainMsg() *dns.Msg {
return &dns.Msg{MsgHdr: dns.MsgHdr{Rcode: dns.RcodeNameError},
Question: []dns.Question{{Name: "ww.miek.nl.", Qclass: dns.ClassINET, Qtype: dns.TypeTXT}},
- Ns: []dns.RR{test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1461471181 14400 3600 604800 14400")},
+ Ns: []dns.RR{test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1461471181 14400 3600 604800 14400")},
}
}
func testSuccessMsg() *dns.Msg {
return &dns.Msg{MsgHdr: dns.MsgHdr{Rcode: dns.RcodeSuccess},
Question: []dns.Question{{Name: "www.miek.nl.", Qclass: dns.ClassINET, Qtype: dns.TypeTXT}},
- Answer: []dns.RR{test.TXT(`www.miek.nl. 1800 IN TXT "response"`)},
+ Answer: []dns.RR{test.TXT(`www.miek.nl. 1800 IN TXT "response"`)},
+ }
+}
+
+func testNsecMsg() *dns.Msg {
+ return &dns.Msg{MsgHdr: dns.MsgHdr{Rcode: dns.RcodeNameError},
+ Question: []dns.Question{{Name: "www.miek.nl.", Qclass: dns.ClassINET, Qtype: dns.TypeNSEC}},
+ Ns: []dns.RR{test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1461471181 14400 3600 604800 14400")},
+ }
+}
+
+func testApexDSMsg() *dns.Msg {
+ return &dns.Msg{MsgHdr: dns.MsgHdr{Rcode: dns.RcodeNameError},
+ Question: []dns.Question{{Name: "miek.nl.", Qclass: dns.ClassINET, Qtype: dns.TypeDS}},
+ Ns: []dns.RR{test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1461471181 14400 3600 604800 14400")},
}
}
diff --git a/plugin/dnssec/dnssec.go b/plugin/dnssec/dnssec.go
index 9e050f590..edda7a809 100644
--- a/plugin/dnssec/dnssec.go
+++ b/plugin/dnssec/dnssec.go
@@ -48,6 +48,22 @@ func (d Dnssec) Sign(state request.Request, now time.Time, server string) *dns.M
mt, _ := response.Typify(req, time.Now().UTC()) // TODO(miek): need opt record here?
if mt == response.Delegation {
+ // We either sign DS or NSEC of DS.
+ ttl := req.Ns[0].Header().Ttl
+
+ ds := []dns.RR{}
+ for i := range req.Ns {
+ if req.Ns[i].Header().Rrtype == dns.TypeDS {
+ ds = append(ds, req.Ns[i])
+ }
+ }
+ if len(ds) == 0 {
+ if sigs, err := d.nsec(state, mt, ttl, incep, expir, server); err == nil {
+ req.Ns = append(req.Ns, sigs...)
+ }
+ } else if sigs, err := d.sign(ds, state.Zone, ttl, incep, expir, server); err == nil {
+ req.Ns = append(req.Ns, sigs...)
+ }
return req
}
@@ -66,6 +82,10 @@ func (d Dnssec) Sign(state request.Request, now time.Time, server string) *dns.M
}
if len(req.Ns) > 1 { // actually added nsec and sigs, reset the rcode
req.Rcode = dns.RcodeSuccess
+ if state.QType() == dns.TypeNSEC { // If original query was NSEC move Ns to Answer without SOA
+ req.Answer = req.Ns[len(req.Ns)-2 : len(req.Ns)]
+ req.Ns = nil
+ }
}
return req
}
diff --git a/plugin/dnssec/dnssec_test.go b/plugin/dnssec/dnssec_test.go
index fb8a128de..8b55ea308 100644
--- a/plugin/dnssec/dnssec_test.go
+++ b/plugin/dnssec/dnssec_test.go
@@ -123,6 +123,60 @@ func TestSigningEmpty(t *testing.T) {
}
}
+func TestDelegationSigned(t *testing.T) {
+ d, rm1, rm2 := newDnssec(t, []string{"miek.nl."})
+ defer rm1()
+ defer rm2()
+
+ m := testMsgDelegationSigned()
+ m.SetQuestion("sub.miek.nl.", dns.TypeNS)
+ state := request.Request{Req: m, Zone: "miek.nl."}
+ m = d.Sign(state, time.Now().UTC(), server)
+ if !section(m.Ns, 1) {
+ t.Errorf("Authority section should have 1 RRSIGs")
+ }
+ if !section(m.Extra, 0) {
+ t.Error("Extra section should not have RRSIGs")
+ }
+}
+
+func TestDelegationUnSigned(t *testing.T) {
+ d, rm1, rm2 := newDnssec(t, []string{"miek.nl."})
+ defer rm1()
+ defer rm2()
+
+ m := testMsgDelegationUnSigned()
+ m.SetQuestion("sub.miek.nl.", dns.TypeNS)
+ state := request.Request{Req: m, Zone: "miek.nl."}
+ m = d.Sign(state, time.Now().UTC(), server)
+ if !section(m.Ns, 1) {
+ t.Errorf("Authority section should have 1 RRSIG")
+ }
+ if !section(m.Extra, 0) {
+ t.Error("Extra section should not have RRSIG")
+ }
+ var nsec *dns.NSEC
+ var rrsig *dns.RRSIG
+ for _, r := range m.Ns {
+ if r.Header().Rrtype == dns.TypeNSEC {
+ nsec = r.(*dns.NSEC)
+ }
+ if r.Header().Rrtype == dns.TypeRRSIG {
+ rrsig = r.(*dns.RRSIG)
+ }
+ }
+ if nsec == nil {
+ t.Error("Authority section should hold a NSEC record")
+ }
+ if rrsig.TypeCovered != dns.TypeNSEC {
+ t.Errorf("RRSIG should cover type %s, got %s",
+ dns.TypeToString[dns.TypeNSEC], dns.TypeToString[rrsig.TypeCovered])
+ }
+ if !correctNsecForDS(nsec) {
+ t.Error("NSEC as invalid TypeBitMap for a DS")
+ }
+}
+
func section(rss []dns.RR, nrSigs int) bool {
i := 0
for _, r := range rss {
@@ -137,13 +191,13 @@ func testMsg() *dns.Msg {
// don't care about the message header
return &dns.Msg{
Answer: []dns.RR{test.MX("miek.nl. 1703 IN MX 1 aspmx.l.google.com.")},
- Ns: []dns.RR{test.NS("miek.nl. 1703 IN NS omval.tednet.nl.")},
+ Ns: []dns.RR{test.NS("miek.nl. 1703 IN NS omval.tednet.nl.")},
}
}
func testMsgEx() *dns.Msg {
return &dns.Msg{
Answer: []dns.RR{test.MX("example.org. 1703 IN MX 1 aspmx.l.google.com.")},
- Ns: []dns.RR{test.NS("example.org. 1703 IN NS omval.tednet.nl.")},
+ Ns: []dns.RR{test.NS("example.org. 1703 IN NS omval.tednet.nl.")},
}
}
@@ -163,6 +217,29 @@ func testMsgDname() *dns.Msg {
}
}
+func testMsgDelegationSigned() *dns.Msg {
+ return &dns.Msg{
+ Ns: []dns.RR{
+ test.NS("sub.miek.nl. 1800 IN NS ns1.sub.miek.nl."),
+ test.DS("sub." + dsKey),
+ },
+ Extra: []dns.RR{
+ test.A("ns1.sub.miek.nl. 1800 IN A 192.0.2.1"),
+ },
+ }
+}
+
+func testMsgDelegationUnSigned() *dns.Msg {
+ return &dns.Msg{
+ Ns: []dns.RR{
+ test.NS("sub.miek.nl. 1800 IN NS ns1.sub.miek.nl."),
+ },
+ Extra: []dns.RR{
+ test.A("ns1.sub.miek.nl. 1800 IN A 192.0.2.1"),
+ },
+ }
+}
+
func testEmptyMsg() *dns.Msg {
// don't care about the message header
return &dns.Msg{
@@ -197,6 +274,7 @@ Created: 20160423195532
Publish: 20160423195532
Activate: 20160423195532
`
+ dsKey = `miek.nl. IN DS 18512 13 2 D4E806322598BC97A003EF1ACDFF352EEFF7B42DBB0D41B8224714C36AEF08D9`
pubKey1 = `example.org. IN DNSKEY 257 3 13 tVRWNSGpHZbCi7Pr7OmbADVUO3MxJ0Lb8Lk3o/HBHqCxf5K/J50lFqRa 98lkdAIiFOVRy8LyMvjwmxZKwB5MNw==`
privKey1 = `Private-key-format: v1.3
Algorithm: 13 (ECDSAP256SHA256)
diff --git a/plugin/dnssec/handler_test.go b/plugin/dnssec/handler_test.go
index a1e24b7bb..e82e546d3 100644
--- a/plugin/dnssec/handler_test.go
+++ b/plugin/dnssec/handler_test.go
@@ -38,6 +38,73 @@ var dnsTestCases = []test.Case{
},
},
{
+ Qname: "miek.nl.", Qtype: dns.TypeNS, Do: true,
+ Answer: []dns.RR{
+ test.NS("miek.nl. 1800 IN NS linode.atoom.net."),
+ test.RRSIG("miek.nl. 1800 IN RRSIG NS 13 2 1800 20220101121212 20220201121212 18512 miek.nl. RandomNotChecked"),
+ },
+ },
+ {
+ Qname: "deleg.miek.nl.", Qtype: dns.TypeNS, Do: true,
+ Ns: []dns.RR{
+ test.DS("deleg.miek.nl. 1800 IN DS 18512 13 2 D4E806322598BC97A003EF1ACDFF352EEFF7B42DBB0D41B8224714C36AEF08D9"),
+ test.NS("deleg.miek.nl. 1800 IN NS ns01.deleg.miek.nl."),
+ test.RRSIG("deleg.miek.nl. 1800 IN RRSIG DS 13 3 1800 20220101121212 20220201121212 18512 miek.nl. RandomNotChecked"),
+ },
+ },
+ {
+ Qname: "unsigned.miek.nl.", Qtype: dns.TypeNS, Do: true,
+ Ns: []dns.RR{
+ test.NS("unsigned.miek.nl. 1800 IN NS ns01.deleg.miek.nl."),
+ test.NSEC("unsigned.miek.nl. 1800 IN NSEC unsigned\\000.miek.nl. NS RRSIG NSEC"),
+ test.RRSIG("unsigned.miek.nl. 1800 IN RRSIG NSEC 13 3 1800 20220101121212 20220201121212 18512 miek.nl. RandomNotChecked"),
+ },
+ },
+ { // DS should not come from dnssec plugin
+ Qname: "deleg.miek.nl.", Qtype: dns.TypeDS,
+ Answer: []dns.RR{
+ test.DS("deleg.miek.nl. 1800 IN DS 18512 13 2 D4E806322598BC97A003EF1ACDFF352EEFF7B42DBB0D41B8224714C36AEF08D9"),
+ },
+ Ns: []dns.RR{
+ test.NS("miek.nl. 1800 IN NS linode.atoom.net."),
+ },
+ },
+ {
+ Qname: "unsigned.miek.nl.", Qtype: dns.TypeDS,
+ Ns: []dns.RR{
+ test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
+ },
+ },
+ {
+ Qname: "miek.nl.", Qtype: dns.TypeDS, Do: true,
+ Ns: []dns.RR{
+ test.NSEC("miek.nl. 1800 IN NSEC \\000.miek.nl. A HINFO NS SOA MX TXT AAAA LOC SRV CERT SSHFP RRSIG NSEC DNSKEY TLSA HIP OPENPGPKEY SPF"),
+ test.RRSIG("miek.nl. 1800 IN RRSIG NSEC 13 2 1800 20220101121212 20220201121212 18512 miek.nl. RandomNotChecked"),
+ test.RRSIG("miek.nl. 1800 IN RRSIG SOA 13 2 3600 20171220141741 20171212111741 18512 miek.nl. 8bLTReqmuQtw=="),
+ test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
+ },
+ },
+ {
+ Qname: "deleg.miek.nl.", Qtype: dns.TypeDS, Do: true,
+ Answer: []dns.RR{
+ test.DS("deleg.miek.nl. 1800 IN DS 18512 13 2 D4E806322598BC97A003EF1ACDFF352EEFF7B42DBB0D41B8224714C36AEF08D9"),
+ test.RRSIG("deleg.miek.nl. 1800 IN RRSIG DS 13 3 1800 20220101121212 20220201121212 18512 miek.nl. RandomNotChecked"),
+ },
+ Ns: []dns.RR{
+ test.NS("miek.nl. 1800 IN NS linode.atoom.net."),
+ test.RRSIG("miek.nl. 1800 IN RRSIG NS 13 2 3600 20161217114912 20161209084912 18512 miek.nl. ad9gA8VWgF1H8ze9/0Rk2Q=="),
+ },
+ },
+ {
+ Qname: "unsigned.miek.nl.", Qtype: dns.TypeDS, Do: true,
+ Ns: []dns.RR{
+ test.RRSIG("miek.nl. 1800 IN RRSIG SOA 13 2 3600 20171220141741 20171212111741 18512 miek.nl. 8bLTReqmuQtw=="),
+ test.SOA("miek.nl. 1800 IN SOA linode.atoom.net. miek.miek.nl. 1282630057 14400 3600 604800 14400"),
+ test.NSEC("unsigned.miek.nl. 1800 IN NSEC \\000.unsigned.miek.nl. NS RRSIG NSEC"),
+ test.RRSIG("unsigned.miek.nl. 1800 IN RRSIG NSEC 13 3 1800 20220101121212 20220201121212 18512 miek.nl. RandomNotChecked"),
+ },
+ },
+ {
Qname: "miek.nl.", Qtype: dns.TypeMX,
Answer: []dns.RR{
test.MX("miek.nl. 1800 IN MX 1 aspmx.l.google.com."),
@@ -179,4 +246,8 @@ $ORIGIN miek.nl.
a IN A 139.162.196.78
IN AAAA 2a01:7e00::f03c:91ff:fef1:6735
-www IN CNAME a`
+www IN CNAME a
+deleg IN NS ns01.deleg
+ IN DS 18512 13 2 D4E806322598BC97A003EF1ACDFF352EEFF7B42DBB0D41B8224714C36AEF08D9
+unsigned IN NS ns01.deleg
+`