Skip to content

Commit

Permalink
Add RFC4034 domain comparison + NSEC Cover
Browse files Browse the repository at this point in the history
  • Loading branch information
monoidic committed Nov 22, 2021
1 parent 3b8982c commit c063466
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 0 deletions.
52 changes: 52 additions & 0 deletions nsecx.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dns

import (
"bytes"
"crypto/sha1"
"encoding/hex"
"strings"
Expand Down Expand Up @@ -93,3 +94,54 @@ func (rr *NSEC3) Match(name string) bool {
}
return false
}

// compares domains according to the canonical ordering specified in RFC4034
// returns an integer value similar to strcmp
// names have to have equal casing!
// (0 for equal values, -1 if s1 < s2, 1 if s1 > s2)
func CanonicalCompare(s1, s2 string) int {
s1b := []byte(s1)
s2b := []byte(s2)

doDDD(s1b)
doDDD(s2b)

s1lend := len(s1)
s2lend := len(s2)

for i := 0; ; i++ {
s1lstart, end1 := PrevLabel(s1, i)
s2lstart, end2 := PrevLabel(s2, i)

if end1 && end2 {
return 0
}

res := bytes.Compare(s1b[s1lstart:s1lend], s2b[s2lstart:s2lend])
if res != 0 {
return res
}

s1lend = s1lstart
s2lend = s2lstart

}
}

// Match returns true if the given name is covered by the NSEC record
func (rr *NSEC) Cover(name string) bool {
return CanonicalCompare(rr.Hdr.Name, name) <= 0 && CanonicalCompare(name, rr.NextDomain) == -1
}

func doDDD(b []byte) {
lb := len(b)
for i := 0; i < lb; i++ {
if i+3 < lb && b[i] == '\\' && isDigit(b[i+1]) && isDigit(b[i+2]) && isDigit(b[i+3]) {
b[i] = dddToByte(b[i : i+4])
for j := i + 1; j < lb-3; j++ {
b[j] = b[j+3]
}
lb -= 3
}
}
}
52 changes: 52 additions & 0 deletions nsecx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,55 @@ func BenchmarkHashName(b *testing.B) {
})
}
}

func TestCanonicalCompare(t *testing.T) {
domains := []string{ // from RFC 4034
"example.",
"a.example.",
"yljkjljk.a.example.",
"z.a.example.",
"zabc.a.example.",
"z.example.",
"\001.z.example.",
"*.z.example.",
"\200.z.example.",
}

len_domains := len(domains)

for i, domain := range domains {
if i != 0 {
prev_domain := domains[i-1]
if !(CanonicalCompare(prev_domain, domain) == -1 && CanonicalCompare(domain, prev_domain) == 1) {
t.Fatalf("prev comparison failure between %s and %s", prev_domain, domain)
}
}

if CanonicalCompare(domain, domain) != 0 {
t.Fatalf("self comparison failure for %s", domain)
}

if i != len_domains-1 {
next_domain := domains[i+1]
if !(CanonicalCompare(domain, next_domain) == -1 && CanonicalCompare(next_domain, domain) == 1) {
t.Fatalf("next comparison failure between %s and %s, %d and %d", domain, next_domain, CanonicalCompare(domain, next_domain), CanonicalCompare(next_domain, domain))
}
}
}
}

func TestNsecCover(t *testing.T) {
nsec := testRR("aaa.ee. 3600 IN NSEC aac.ee. NS RRSIG NSEC").(*NSEC)

if !nsec.Cover("aaaa.ee.") {
t.Fatal("nsec cover positive example failure")
}

if !nsec.Cover("aaa.ee.") {
t.Fatal("nsec cover self example failure")
}

if nsec.Cover("aad.ee.") {
t.Fatal("nsec cover negative example failure")
}
}

0 comments on commit c063466

Please sign in to comment.