Skip to content

Commit b1a5122

Browse files
committed
Add RFC4034 domain comparison + NSEC Cover
1 parent 3b8982c commit b1a5122

File tree

2 files changed

+107
-0
lines changed

2 files changed

+107
-0
lines changed

nsecx.go

+59
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,62 @@ func (rr *NSEC3) Match(name string) bool {
9393
}
9494
return false
9595
}
96+
97+
// compares domains according to the canonical ordering specified in RFC4034
98+
// returns an integer value similar to strcmp
99+
// (0 for equal values, -1 if s1 < s2, 1 if s1 > s2)
100+
func DomainCompare(s1, s2 string) int {
101+
s1 = strings.ToLower(s1)
102+
s2 = strings.ToLower(s2)
103+
104+
if s1 == s2 {
105+
return 0
106+
}
107+
108+
s1_labels := SplitDomainName(s1)
109+
s2_labels := SplitDomainName(s2)
110+
111+
if s1_labels == nil { // s1 is root
112+
return -1
113+
}
114+
if s2_labels == nil { // s2 is root
115+
return 1
116+
}
117+
118+
reverse_arr(s1_labels)
119+
reverse_arr(s2_labels)
120+
121+
s1_label_len := len(s1_labels)
122+
s2_label_len := len(s2_labels)
123+
124+
var min_label_len int
125+
var default_ret int // based on label count, if the common prefix matches
126+
127+
if s1_label_len < s2_label_len {
128+
min_label_len = s1_label_len
129+
default_ret = -1
130+
} else {
131+
min_label_len = s2_label_len
132+
default_ret = 1
133+
}
134+
135+
for i := 0; i < min_label_len; i++ {
136+
cmp := strings.Compare(s1_labels[i], s2_labels[i])
137+
if cmp != 0 {
138+
return cmp
139+
}
140+
}
141+
142+
return default_ret
143+
}
144+
145+
// Match returns true if the given name is covered by the NSEC record
146+
func (rr *NSEC) Cover(name string) bool {
147+
return DomainCompare(rr.Hdr.Name, name) <= 0 && DomainCompare(name, rr.NextDomain) == -1
148+
}
149+
150+
func reverse_arr(arr []string) {
151+
for i, j := 0, len(arr) - 1; i < j; i, j = i+1, j-1 {
152+
arr[i], arr[j] = arr[j], arr[i]
153+
}
154+
}

nsecx_test.go

+48
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,51 @@ func BenchmarkHashName(b *testing.B) {
168168
})
169169
}
170170
}
171+
172+
func TestDomainCompare(t *testing.T) {
173+
domains := []string{ // from RFC 4034
174+
"example.",
175+
"a.example.",
176+
"yljkjljk.a.example.",
177+
"Z.a.example.",
178+
"zABC.a.EXAMPLE.",
179+
"z.example.",
180+
"\x01.z.example.",
181+
"*.z.example.",
182+
"\xc8.z.example.",
183+
}
184+
185+
len_domains := len(domains)
186+
187+
for i, domain := range domains {
188+
if i != 0 {
189+
prev_domain := domains[i-1]
190+
if !(DomainCompare(prev_domain, domain) == -1 && DomainCompare(domain, prev_domain) == 1) {
191+
t.Fatalf("prev comparison failure between %s and %s", prev_domain, domain)
192+
}
193+
}
194+
195+
if DomainCompare(domain, domain) != 0 {
196+
t.Fatalf("self comparison failure for %s", domain)
197+
}
198+
199+
if i != len_domains-1 {
200+
next_domain := domains[i+1]
201+
if !(DomainCompare(domain, next_domain) == -1 && DomainCompare(next_domain, domain) == 1) {
202+
t.Fatalf("next comparison failure between %s and %s, %d and %d", domain, next_domain, DomainCompare(domain, next_domain), DomainCompare(next_domain, domain))
203+
}
204+
}
205+
}
206+
}
207+
208+
func TestNsecCover(t *testing.T) {
209+
nsec := testRR("aaa.ee. 3600 IN NSEC aac.ee. NS RRSIG NSEC").(*NSEC)
210+
211+
if !nsec.Cover("aaaa.ee.") {
212+
t.Fatal("nsec cover positive example failure")
213+
}
214+
215+
if nsec.Cover("aad.ee.") {
216+
t.Fatal("nsec cover negative example failure")
217+
}
218+
}

0 commit comments

Comments
 (0)