diff options
author | 2023-04-22 22:32:01 +0200 | |
---|---|---|
committer | 2023-04-22 22:32:01 +0200 | |
commit | 13e66918e3f1221a0da8f562bdafbec43c3e8f24 (patch) | |
tree | 52baddd90d177c8973072865386890d24ef6cf27 | |
parent | 0862dd1cb5609bf2d55c566a010d42568178b9ae (diff) | |
download | coredns-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.go | 23 | ||||
-rw-r--r-- | plugin/dnssec/black_lies_test.go | 176 | ||||
-rw-r--r-- | plugin/dnssec/dnssec.go | 20 | ||||
-rw-r--r-- | plugin/dnssec/dnssec_test.go | 82 | ||||
-rw-r--r-- | plugin/dnssec/handler_test.go | 73 |
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 +` |