From 09c94fa443f337a58131d8220b0a8e13c9698bff Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Thu, 31 Mar 2016 09:25:22 +0000 Subject: [PATCH] Positive wildcare replies Reply to queries when you have a wildcard in the zone. This works for DNS and DNSSEC. Thing missing is NODATA responses for that specific wildcard. Add wildcard_test.go as well. --- middleware/file/closest.go | 1 + middleware/file/lookup.go | 58 +++++++++++++++++++++++++++++--- middleware/file/wildcard_test.go | 39 +++++++++++++++++---- 3 files changed, 86 insertions(+), 12 deletions(-) diff --git a/middleware/file/closest.go b/middleware/file/closest.go index 0c5fd722d..895b1a524 100644 --- a/middleware/file/closest.go +++ b/middleware/file/closest.go @@ -37,6 +37,7 @@ func (z *Zone) nameErrorProof(rr dns.RR) []dns.RR { } } + // We do this lookup twice, once for wildcard and once for the name proof. TODO(miek): fix ce := z.ClosestEncloser(rr) wildcard := "*." + ce rr.Header().Name = wildcard diff --git a/middleware/file/lookup.go b/middleware/file/lookup.go index 7c4d92abe..aaf29834d 100644 --- a/middleware/file/lookup.go +++ b/middleware/file/lookup.go @@ -37,7 +37,7 @@ func (z *Zone) Lookup(qname string, qtype uint16, do bool) ([]dns.RR, []dns.RR, elem := z.Tree.Get(rr) if elem == nil { if elem == nil { - return z.nameError(elem, rr, do) + return z.nameError(rr, do) } } @@ -55,9 +55,7 @@ func (z *Zone) Lookup(qname string, qtype uint16, do bool) ([]dns.RR, []dns.RR, if do { sigs := elem.Types(dns.TypeRRSIG) sigs = signatureForSubType(sigs, qtype) - if len(sigs) > 0 { - rrs = append(rrs, sigs...) - } + rrs = append(rrs, sigs...) } return rrs, nil, nil, Success } @@ -68,7 +66,34 @@ 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) nameError(elem *tree.Elem, rr dns.RR, do bool) ([]dns.RR, []dns.RR, []dns.RR, Result) { +func (z *Zone) nameError(rr dns.RR, do bool) ([]dns.RR, []dns.RR, []dns.RR, Result) { + // Is there a wildcard? + rr1 := dns.Copy(rr) + rr1.Header().Name = rr.Header().Name + rr1.Header().Rrtype = rr.Header().Rrtype + ce := z.ClosestEncloser(rr1) + rr1.Header().Name = "*." + ce + elem := z.Tree.Get(rr1) + + if elem != nil { + ret := elem.Types(rr1.Header().Rrtype) // there can only be one of these (or zero) + switch { + case ret != nil: + if do { + sigs := elem.Types(dns.TypeRRSIG) + sigs = signatureForSubType(sigs, rr.Header().Rrtype) + ret = append(ret, sigs...) + } + ret = wildcardReplace(rr, ce, ret) + return ret, nil, nil, Success + case ret == nil: + // nodata, nsec from the wildcard - type does not exist + // nsec proof that name does not exist + // TODO(miek) + } + } + + // name error ret := []dns.RR{z.SOA} if do { ret = append(ret, z.SIG...) @@ -140,3 +165,26 @@ func signatureForSubType(rrs []dns.RR, subtype uint16) []dns.RR { } return sigs } + +// wildcardReplace replaces the first wildcard with label. +func wildcardReplace(rr dns.RR, ce string, rrs []dns.RR) []dns.RR { + // Get how many labels the ce is off from the fullname, this is how much of the + // original rr's '*' we must replace. + labels := dns.CountLabel(rr.Header().Name) - dns.CountLabel(ce) // can not be 0, TODO(miek): check + + indexes := dns.Split(rr.Header().Name) + if labels >= len(indexes) { + // TODO(miek): yes then what? + // Is the == right here? + return nil + } + replacement := rr.Header().Name[:indexes[labels]] + + // need to copy here, otherwise we change in zone stuff + ret := make([]dns.RR, len(rrs)) + for i, r := range rrs { + ret[i] = dns.Copy(r) + ret[i].Header().Name = replacement + r.Header().Name[2:] + } + return ret +} diff --git a/middleware/file/wildcard_test.go b/middleware/file/wildcard_test.go index 81b799955..1dab78226 100644 --- a/middleware/file/wildcard_test.go +++ b/middleware/file/wildcard_test.go @@ -12,15 +12,40 @@ import ( "golang.org/x/net/context" ) -var dnssecWildcardTestCases = []coretest.Case{ +var wildcardTestCases = []coretest.Case{ { - Qname: "blaat.dnssex.nl.", Qtype: dns.TypeTXT, Do: true, - Answer: []dns.RR{}, + Qname: "wild.dnssex.nl.", Qtype: dns.TypeTXT, + Answer: []dns.RR{ + coretest.TXT(`wild.dnssex.nl. 1800 IN TXT "Doing It Safe Is Better"`), + }, }, + { + Qname: "wild.dnssex.nl.", Qtype: dns.TypeTXT, Do: true, + Answer: []dns.RR{ + coretest.RRSIG("wild.dnssex.nl. 1800 IN RRSIG TXT 8 2 1800 20160428190224 20160329190224 14460 dnssex.nl. FUZSTyvZfeuuOpCm"), + coretest.TXT(`wild.dnssex.nl. 1800 IN TXT "Doing It Safe Is Better"`), + }, + }, + // nodata reponse + /* + { + Qname: "wild.dnssex.nl.", Qtype: dns.TypeSRV, + Answer: []dns.RR{ + coretest.TXT(`wild.dnssex.nl. 1800 IN TXT "Doing It Safe Is Better"`), + }, + }, + { + Qname: "wild.dnssex.nl.", Qtype: dns.TypeSRV, Do: true, + Answer: []dns.RR{ + coretest.RRSIG("wild.dnssex.nl. 1800 IN RRSIG TXT 8 2 1800 20160428190224 20160329190224 14460 dnssex.nl. FUZSTyvZfeuuOpCm"), + coretest.TXT(`wild.dnssex.nl. 1800 IN TXT "Doing It Safe Is Better"`), + }, + }, + */ } -func testLookupDNSSECWildcard(t *testing.T) { - zone, err := Parse(strings.NewReader(dbMiekNL_signed), testzone1, "stdin") +func TestLookupWildcard(t *testing.T) { + zone, err := Parse(strings.NewReader(dbDnssexNl_signed), testzone1, "stdin") if err != nil { t.Fatalf("expect no error when reading zone, got %q", err) } @@ -28,7 +53,7 @@ func testLookupDNSSECWildcard(t *testing.T) { fm := File{Next: coretest.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{testzone1: zone}, Names: []string{testzone1}}} ctx := context.TODO() - for _, tc := range dnssecWildcardTestCases { + for _, tc := range wildcardTestCases { m := tc.Msg() rec := middleware.NewResponseRecorder(&middleware.TestResponseWriter{}) @@ -77,7 +102,7 @@ func testLookupDNSSECWildcard(t *testing.T) { } } -const dbMiekNL_wildcard_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. (