Skip to content

Commit 816a584

Browse files
committed
Add RFC4034 domain comparison + NSEC Cover
1 parent 5521648 commit 816a584

File tree

4 files changed

+112
-0
lines changed

4 files changed

+112
-0
lines changed

labels.go

+51
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package dns
22

3+
import (
4+
"strings"
5+
)
6+
37
// Holds a bunch of helper functions for dealing with labels.
48

59
// SplitDomainName splits a name string into it's labels.
@@ -186,6 +190,40 @@ func PrevLabel(s string, n int) (i int, start bool) {
186190
return 0, n > 1
187191
}
188192

193+
// compares domains according to the canonical ordering specified in RFC4034
194+
// returns an integer value similar to strcmp
195+
// names have to have equal casing!
196+
// (0 for equal values, -1 if s1 < s2, 1 if s1 > s2)
197+
func Compare(s1, s2 string) int {
198+
s1b := []byte(s1)
199+
s2b := []byte(s2)
200+
201+
doDDD(s1b)
202+
doDDD(s2b)
203+
204+
s1lend := len(s1)
205+
s2lend := len(s2)
206+
207+
for i := 0; ; i++ {
208+
s1lstart, end1 := PrevLabel(s1, i)
209+
s2lstart, end2 := PrevLabel(s2, i)
210+
211+
if end1 && end2 {
212+
return 0
213+
}
214+
215+
s1l := string(s1b[s1lstart:s1lend])
216+
s2l := string(s2b[s2lstart:s2lend])
217+
218+
if !equal(s1l, s2l) {
219+
return strings.Compare(strings.ToLower(s1l), strings.ToLower(s2l))
220+
}
221+
222+
s1lend = s1lstart
223+
s2lend = s2lstart
224+
}
225+
}
226+
189227
// equal compares a and b while ignoring case. It returns true when equal otherwise false.
190228
func equal(a, b string) bool {
191229
// might be lifted into API function.
@@ -210,3 +248,16 @@ func equal(a, b string) bool {
210248
}
211249
return true
212250
}
251+
252+
func doDDD(b []byte) {
253+
lb := len(b)
254+
for i := 0; i < lb; i++ {
255+
if i+3 < lb && b[i] == '\\' && isDigit(b[i+1]) && isDigit(b[i+2]) && isDigit(b[i+3]) {
256+
b[i] = dddToByte(b[i : i+4])
257+
for j := i + 1; j < lb-3; j++ {
258+
b[j] = b[j+3]
259+
}
260+
lb -= 3
261+
}
262+
}
263+
}

labels_test.go

+36
Original file line numberDiff line numberDiff line change
@@ -334,3 +334,39 @@ func BenchmarkPrevLabelMixed(b *testing.B) {
334334
PrevLabel(`www\\\.example.com`, 10)
335335
}
336336
}
337+
338+
func TestCompare(t *testing.T) {
339+
domains := []string{ // from RFC 4034
340+
"example.",
341+
"a.example.",
342+
"yljkjljk.a.example.",
343+
"Z.a.example.",
344+
"zABC.a.EXAMPLE.",
345+
"z.example.",
346+
"\001.z.example.",
347+
"*.z.example.",
348+
"\200.z.example.",
349+
}
350+
351+
len_domains := len(domains)
352+
353+
for i, domain := range domains {
354+
if i != 0 {
355+
prev_domain := domains[i-1]
356+
if !(Compare(prev_domain, domain) == -1 && Compare(domain, prev_domain) == 1) {
357+
t.Fatalf("prev comparison failure between %s and %s", prev_domain, domain)
358+
}
359+
}
360+
361+
if Compare(domain, domain) != 0 {
362+
t.Fatalf("self comparison failure for %s", domain)
363+
}
364+
365+
if i != len_domains-1 {
366+
next_domain := domains[i+1]
367+
if !(Compare(domain, next_domain) == -1 && Compare(next_domain, domain) == 1) {
368+
t.Fatalf("next comparison failure between %s and %s, %d and %d", domain, next_domain, Compare(domain, next_domain), Compare(next_domain, domain))
369+
}
370+
}
371+
}
372+
}

nsecx.go

+5
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,8 @@ func (rr *NSEC3) Match(name string) bool {
9393
}
9494
return false
9595
}
96+
97+
// Match returns true if the given name is covered by the NSEC record
98+
func (rr *NSEC) Cover(name string) bool {
99+
return Compare(rr.Hdr.Name, name) <= 0 && Compare(name, rr.NextDomain) == -1
100+
}

nsecx_test.go

+20
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,23 @@ func BenchmarkHashName(b *testing.B) {
168168
})
169169
}
170170
}
171+
172+
func TestNsecCover(t *testing.T) {
173+
nsec := testRR("aaa.ee. 3600 IN NSEC aac.ee. NS RRSIG NSEC").(*NSEC)
174+
175+
if !nsec.Cover("aaaa.ee.") {
176+
t.Fatal("nsec cover not covering in-range name")
177+
}
178+
179+
if !nsec.Cover("aaa.ee.") {
180+
t.Fatal("nsec cover not covering start of range")
181+
}
182+
183+
if nsec.Cover("aac.ee.") {
184+
t.Fatal("nsec cover range end failure")
185+
}
186+
187+
if nsec.Cover("aad.ee.") {
188+
t.Fatal("nsec cover covering out-of-range name")
189+
}
190+
}

0 commit comments

Comments
 (0)