-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Expand file tree
/
Copy pathzdnsutil.go
More file actions
222 lines (200 loc) · 5.71 KB
/
Copy pathzdnsutil.go
File metadata and controls
222 lines (200 loc) · 5.71 KB
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// Code generated by "go run dnsutil_generate.go"; DO NOT EDIT.
package deleg
import (
"strings"
"time"
)
// This is copied to zdnsutil.go in the main package to also have access to these functions and not have an
// import cycle. See dnsutil_generate.go.
//
// This file SHOULD NOT import dns things, as that leads to the impossibility to use it from svcb/ and/or
// deleg/.
// Labels returns the number of labels in the name s.
func dnsutilLabels(s string) (labels int) {
if s == "." {
return
}
off := 0
end := false
for {
off, end = dnsutilNext(s, off)
labels++
if end {
return
}
}
}
// Next returns the index of the start of the next label in the string s starting at offset. A negative offset
// will cause a panic. The bool end is true when the end of the string has been reached. Also see [Prev].
func dnsutilNext(s string, offset int) (i int, end bool) {
if s == "" {
return 0, true
}
for i = offset; i < len(s)-1; i++ {
if s[i] != '.' {
continue
}
return i + 1, false
}
return i + 1, true
}
// Prev returns the index of the label when starting from the right and jumping n labels to the left.
// The bool start is true when the start of the string has been overshot. Also see [Next].
func dnsutilPrev(s string, n int) (i int, start bool) {
if s == "" {
return 0, true
}
if n == 0 {
return len(s), false
}
l := len(s) - 1
if s[l] == '.' {
l--
}
for ; l >= 0 && n > 0; l-- {
if s[l] != '.' {
continue
}
n--
if n == 0 {
return l + 1, false
}
}
return 0, n > 1
}
// Fqdn return the fully qualified domain name from s. If s is already fully qualified, it behaves as the
// identity function.
func dnsutilFqdn(s string) string {
if dnsutilIsFqdn(s) {
return s
}
return s + "."
}
// IsFqdn checks if a domain name is fully qualified. As this library doesn't support escapes in names, this
// simply calls strings.HasSuffix.
func dnsutilIsFqdn(s string) bool { return strings.HasSuffix(s, ".") }
// Canonical returns the domain name in canonical form. A name in canonical form is lowercase and fully qualified.
// Only US-ASCII letters are affected. See Section 6.2 in RFC 4034.
func dnsutilCanonical(s string) string {
return strings.Map(func(r rune) rune {
if r >= 'A' && r <= 'Z' {
r += 'a' - 'A'
}
return r
}, dnsutilFqdn(s))
}
// IsName checks if s is a valid domain name. A non fully qualified domain name is considered valid.
// Note that this function is extremely liberal; almost any string is a valid domain name as the DNS is 8 bit
// protocol. It checks if each label fits in 63 characters and that the entire name will fit into the 255
// octet wire-format limit.
func dnsutilIsName(s string) bool {
// XXX: The logic in this function was copied from pack.Name and should be kept in sync with that function.
const lenmsg = 256
ls := uint16(len(s))
if ls == 1 && s[0] == '.' {
return true
}
if ls > 1 && s[0] == '.' {
return false
}
var (
off uint16
begin uint16
)
for i := uint16(0); i < ls; i++ {
switch s[i] {
case '.':
labelLen := i - begin
if labelLen >= 1<<6 { // top two bits of length must be clear
return false
}
if labelLen == 0 { // two dots back to back is not legal
return false
}
// off can already (we're in a loop) be bigger than lenmsg
// this happens when a name isn't fully qualified
off += 1 + labelLen
if off > lenmsg {
return false
}
begin = i + 1
}
}
return true
}
// compareLabel compares a and b while ignoring case. It returns 0 when equal, -1 when a is smaller than b,
// and +1 when a is greater then b. This ends up a compareLabel in the dns package too as generated by
// dnsutil_generate.go.
func compareLabel(a, b string) int {
la, lb := len(a), len(b)
for i := range min(la, lb) {
ai := a[i]
bi := b[i]
if ai >= 'A' && ai <= 'Z' {
ai |= 'a' - 'A'
}
if bi >= 'A' && bi <= 'Z' {
bi |= 'a' - 'A'
}
if ai < bi {
return -1
}
if ai > bi {
return +1
}
}
if la < lb {
return -1
}
if la > lb {
return +1
}
return 0
}
// TimeToString translates the RRSIG's incep. and expir. times to the
// string representation used when printing the record. It takes serial arithmetic (RFC 1982) into account.
func dnsutilTimeToString(t uint32) string {
mod := max((int64(t)-time.Now().Unix())/maxSerialIncrement-1, 0)
ti := time.Unix(int64(t)-mod*maxSerialIncrement, 0).UTC()
return ti.Format("20060102150405")
}
// StringToTime translates the RRSIG's incep. and expir. times from string values like "20110403154150" to an 32 bit integer.
// It takes serial arithmetic (RFC 1982) into account.
func dnsutilStringToTime(s string) (uint32, error) {
t, err := time.Parse("20060102150405", s)
if err != nil {
return 0, err
}
mod := max(t.Unix()/maxSerialIncrement-1, 0)
return uint32(t.Unix() - mod*maxSerialIncrement), nil
}
// Absolute takes the name and origin and appends the origin to the name. This takes the 1035 presentation
// format into account, i.e. "@" means the origin in a name.
// If s is not a valid domain name, the empty string is returned. If the origin is needed to be appended,
// but is empty the empty string is also returned.
func dnsutilAbsolute(s, origin string) string {
if s == "@" {
if origin == "" {
return ""
}
return origin
}
if s == "\n" || s == "" { // this can happen when a zone is parsed, internal quirk, should not be here...
return ""
}
if dnsutilIsName(s) == false { // done to make the conversion via dnsutil_generate.go work, instead of !IsName(s)
return ""
}
if dnsutilIsFqdn(s) {
return s
}
if origin == "" {
return ""
}
if origin == "." {
return s + origin
}
return s + "." + origin
}
// maxSerialIncrement is the maximum difference between two serial numbers. See RFC 1982.
const maxSerialIncrement = 2147483647