/
nsec3.go
141 lines (124 loc) · 2.87 KB
/
nsec3.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package resolver
import (
"errors"
"github.com/miekg/dns"
)
var (
errNSECTypeExists = errors.New("NSEC3 record shows question type exists")
errNSECMissingCoverage = errors.New("NSEC3 record missing for expected encloser")
errNSECBadDelegation = errors.New("DS or SOA bit set in NSEC3 type map")
errNSECNSMissing = errors.New("NS bit not set in NSEC3 type map")
errNSECOptOut = errors.New("Opt-Out bit not set for NSEC3 record covering next closer")
)
func typesSet(set []uint16, types ...uint16) bool {
tm := make(map[uint16]struct{}, len(types))
for _, t := range types {
tm[t] = struct{}{}
}
for _, t := range set {
if _, ok := tm[t]; ok {
return true
}
}
return false
}
func findClosestEncloser(name string, nsec []dns.RR) (string, string) {
labelIndices := dns.Split(name)
nc := name
for i := 0; i < len(labelIndices); i++ {
z := name[labelIndices[i]:]
_, err := findMatching(z, nsec)
if err != nil {
continue
}
if i != 0 {
nc = name[labelIndices[i-1]:]
}
return z, nc
}
return "", ""
}
func findMatching(name string, nsec []dns.RR) ([]uint16, error) {
for _, rr := range nsec {
n := rr.(*dns.NSEC3)
if n.Match(name) {
return n.TypeBitMap, nil
}
}
return nil, errNSECMissingCoverage
}
func findCoverer(name string, nsec []dns.RR) ([]uint16, bool, error) {
for _, rr := range nsec {
n := rr.(*dns.NSEC3)
if n.Cover(name) {
return n.TypeBitMap, (n.Flags & 1) == 1, nil
}
}
return nil, false, errNSECMissingCoverage
}
func verifyNameError(msg *dns.Msg, nsec []dns.RR) error {
q := msg.Question[0]
qname := q.Name
if dname := getDnameTarget(msg); dname != "" {
qname = dname
}
ce, _ := findClosestEncloser(qname, nsec)
if ce == "" {
return errNSECMissingCoverage
}
_, _, err := findCoverer("*."+ce, nsec)
if err != nil {
return err
}
return nil
}
func verifyNODATA(msg *dns.Msg, nsec []dns.RR) error {
q := msg.Question[0]
qname := q.Name
if dname := getDnameTarget(msg); dname != "" {
qname = dname
}
types, err := findMatching(qname, nsec)
if err != nil {
if q.Qtype != dns.TypeDS {
return err
}
ce, nc := findClosestEncloser(qname, nsec)
if ce == "" {
return errNSECMissingCoverage
}
_, _, err := findCoverer(nc, nsec)
if err != nil {
return err
}
return nil
}
if typesSet(types, q.Qtype, dns.TypeCNAME) {
return errNSECTypeExists
}
return nil
}
func verifyDelegation(delegation string, nsec []dns.RR) error {
types, err := findMatching(delegation, nsec)
if err != nil {
ce, nc := findClosestEncloser(delegation, nsec)
if ce == "" {
return errNSECMissingCoverage
}
_, optOut, err := findCoverer(nc, nsec)
if err != nil {
return err
}
if !optOut {
return errNSECOptOut
}
return nil
}
if !typesSet(types, dns.TypeNS) {
return errNSECNSMissing
}
if typesSet(types, dns.TypeDS, dns.TypeSOA) {
return errNSECBadDelegation
}
return nil
}