aboutsummaryrefslogtreecommitdiff
path: root/middleware/canonical.go
diff options
context:
space:
mode:
Diffstat (limited to 'middleware/canonical.go')
-rw-r--r--middleware/canonical.go57
1 files changed, 57 insertions, 0 deletions
diff --git a/middleware/canonical.go b/middleware/canonical.go
new file mode 100644
index 000000000..5d51231c2
--- /dev/null
+++ b/middleware/canonical.go
@@ -0,0 +1,57 @@
+package middleware
+
+import (
+ "bytes"
+
+ "github.com/miekg/dns"
+)
+
+// Less returns <0 when a is less than b, 0 when they are equal and
+// >0 when a is larger than b.
+// The function order names in DNSSEC canonical order.
+//
+// See http://bert-hubert.blogspot.co.uk/2015/10/how-to-do-fast-canonical-ordering-of.html
+// for a blog article on how we do this. And https://tools.ietf.org/html/rfc4034#section-6.1 .
+func Less(a, b string) int {
+ i := 1
+ aj := len(a)
+ bj := len(b)
+ for {
+ ai, _ := dns.PrevLabel(a, i)
+ bi, _ := dns.PrevLabel(b, i)
+ // sadly this []byte will allocate...
+ ab := []byte(a[ai:aj])
+ toLowerAndDDD(ab)
+ bb := []byte(b[bi:bj])
+ toLowerAndDDD(bb)
+
+ res := bytes.Compare(ab, bb)
+ if res != 0 {
+ return res
+ }
+
+ i++
+ aj, bj = ai, bi
+ }
+ return 0
+}
+
+func toLowerAndDDD(b []byte) {
+ lb := len(b)
+ for i := 0; i < lb; i++ {
+ if b[i] >= 'A' && b[i] <= 'Z' {
+ b[i] += 32
+ continue
+ }
+ if i+3 < lb && b[i] == '\\' && isDigit(b[i+1]) && isDigit(b[i+2]) && isDigit(b[i+3]) {
+ b[i] = dddToByte(b[i:])
+ for j := i + 1; j < lb-3; j++ {
+ b[j] = b[j+3]
+ }
+ lb -= 3
+ }
+ }
+}
+
+func isDigit(b byte) bool { return b >= '0' && b <= '9' }
+func dddToByte(s []byte) byte { return byte((s[1]-'0')*100 + (s[2]-'0')*10 + (s[3] - '0')) }