1 package dns
2 3 import (
4 "errors"
5 "net"
6 "strconv"
7 "strings"
8 )
9 10 const hexDigit = "0123456789abcdef"
11 12 // Everything is assumed in ClassINET.
13 14 // SetReply creates a reply message from a request message.
15 func (dns *Msg) SetReply(request *Msg) *Msg {
16 dns.Id = request.Id
17 dns.Response = true
18 dns.Opcode = request.Opcode
19 if dns.Opcode == OpcodeQuery {
20 dns.RecursionDesired = request.RecursionDesired // Copy rd bit
21 dns.CheckingDisabled = request.CheckingDisabled // Copy cd bit
22 }
23 dns.Rcode = RcodeSuccess
24 if len(request.Question) > 0 {
25 dns.Question = []Question{request.Question[0]}
26 }
27 return dns
28 }
29 30 // SetQuestion creates a question message, it sets the Question
31 // section, generates an Id and sets the RecursionDesired (RD)
32 // bit to true.
33 func (dns *Msg) SetQuestion(z string, t uint16) *Msg {
34 dns.Id = Id()
35 dns.RecursionDesired = true
36 dns.Question = make([]Question, 1)
37 dns.Question[0] = Question{z, t, ClassINET}
38 return dns
39 }
40 41 // SetNotify creates a notify message, it sets the Question
42 // section, generates an Id and sets the Authoritative (AA)
43 // bit to true.
44 func (dns *Msg) SetNotify(z string) *Msg {
45 dns.Opcode = OpcodeNotify
46 dns.Authoritative = true
47 dns.Id = Id()
48 dns.Question = make([]Question, 1)
49 dns.Question[0] = Question{z, TypeSOA, ClassINET}
50 return dns
51 }
52 53 // SetRcode creates an error message suitable for the request.
54 func (dns *Msg) SetRcode(request *Msg, rcode int) *Msg {
55 dns.SetReply(request)
56 dns.Rcode = rcode
57 return dns
58 }
59 60 // SetRcodeFormatError creates a message with FormError set.
61 func (dns *Msg) SetRcodeFormatError(request *Msg) *Msg {
62 dns.Rcode = RcodeFormatError
63 dns.Opcode = OpcodeQuery
64 dns.Response = true
65 dns.Authoritative = false
66 dns.Id = request.Id
67 return dns
68 }
69 70 // SetUpdate makes the message a dynamic update message. It
71 // sets the ZONE section to: z, TypeSOA, ClassINET.
72 func (dns *Msg) SetUpdate(z string) *Msg {
73 dns.Id = Id()
74 dns.Response = false
75 dns.Opcode = OpcodeUpdate
76 dns.Compress = false // BIND9 cannot handle compression
77 dns.Question = make([]Question, 1)
78 dns.Question[0] = Question{z, TypeSOA, ClassINET}
79 return dns
80 }
81 82 // SetIxfr creates message for requesting an IXFR.
83 func (dns *Msg) SetIxfr(z string, serial uint32, ns, mbox string) *Msg {
84 dns.Id = Id()
85 dns.Question = make([]Question, 1)
86 dns.Ns = make([]RR, 1)
87 s := new(SOA)
88 s.Hdr = RR_Header{z, TypeSOA, ClassINET, defaultTtl, 0}
89 s.Serial = serial
90 s.Ns = ns
91 s.Mbox = mbox
92 dns.Question[0] = Question{z, TypeIXFR, ClassINET}
93 dns.Ns[0] = s
94 return dns
95 }
96 97 // SetAxfr creates message for requesting an AXFR.
98 func (dns *Msg) SetAxfr(z string) *Msg {
99 dns.Id = Id()
100 dns.Question = make([]Question, 1)
101 dns.Question[0] = Question{z, TypeAXFR, ClassINET}
102 return dns
103 }
104 105 // SetTsig appends a TSIG RR to the message.
106 // This is only a skeleton TSIG RR that is added as the last RR in the
107 // additional section. The TSIG is calculated when the message is being send.
108 func (dns *Msg) SetTsig(z, algo string, fudge uint16, timesigned int64) *Msg {
109 t := new(TSIG)
110 t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0}
111 t.Algorithm = algo
112 t.Fudge = fudge
113 t.TimeSigned = uint64(timesigned)
114 t.OrigId = dns.Id
115 dns.Extra = append(dns.Extra, t)
116 return dns
117 }
118 119 // SetEdns0 appends a EDNS0 OPT RR to the message.
120 // TSIG should always the last RR in a message.
121 func (dns *Msg) SetEdns0(udpsize uint16, do bool) *Msg {
122 e := new(OPT)
123 e.Hdr.Name = "."
124 e.Hdr.Rrtype = TypeOPT
125 e.SetUDPSize(udpsize)
126 if do {
127 e.SetDo()
128 }
129 dns.Extra = append(dns.Extra, e)
130 return dns
131 }
132 133 // IsTsig checks if the message has a TSIG record as the last record
134 // in the additional section. It returns the TSIG record found or nil.
135 func (dns *Msg) IsTsig() *TSIG {
136 if len(dns.Extra) > 0 {
137 if dns.Extra[len(dns.Extra)-1].Header().Rrtype == TypeTSIG {
138 return dns.Extra[len(dns.Extra)-1].(*TSIG)
139 }
140 }
141 return nil
142 }
143 144 // IsEdns0 checks if the message has a EDNS0 (OPT) record, any EDNS0
145 // record in the additional section will do. It returns the OPT record
146 // found or nil.
147 func (dns *Msg) IsEdns0() *OPT {
148 // RFC 6891, Section 6.1.1 allows the OPT record to appear
149 // anywhere in the additional record section, but it's usually at
150 // the end so start there.
151 for i := len(dns.Extra) - 1; i >= 0; i-- {
152 if dns.Extra[i].Header().Rrtype == TypeOPT {
153 return dns.Extra[i].(*OPT)
154 }
155 }
156 return nil
157 }
158 159 // popEdns0 is like IsEdns0, but it removes the record from the message.
160 func (dns *Msg) popEdns0() *OPT {
161 // RFC 6891, Section 6.1.1 allows the OPT record to appear
162 // anywhere in the additional record section, but it's usually at
163 // the end so start there.
164 for i := len(dns.Extra) - 1; i >= 0; i-- {
165 if dns.Extra[i].Header().Rrtype == TypeOPT {
166 opt := dns.Extra[i].(*OPT)
167 dns.Extra = append(dns.Extra[:i], dns.Extra[i+1:]...)
168 return opt
169 }
170 }
171 return nil
172 }
173 174 // IsDomainName checks if s is a valid domain name, it returns the number of
175 // labels and true, when a domain name is valid. Note that non fully qualified
176 // domain name is considered valid, in this case the last label is counted in
177 // the number of labels. When false is returned the number of labels is not
178 // defined. Also note that this function is extremely liberal; almost any
179 // string is a valid domain name as the DNS is 8 bit protocol. It checks if each
180 // label fits in 63 characters and that the entire name will fit into the 255
181 // octet wire format limit.
182 func IsDomainName(s string) (labels int, ok bool) {
183 // XXX: The logic in this function was copied from packDomainName and
184 // should be kept in sync with that function.
185 186 const lenmsg = 256
187 188 if len(s) == 0 { // Ok, for instance when dealing with update RR without any rdata.
189 return 0, false
190 }
191 192 s = Fqdn(s)
193 194 // Each dot ends a segment of the name. Except for escaped dots (\.), which
195 // are normal dots.
196 197 var (
198 off int
199 begin int
200 wasDot bool
201 escape bool
202 )
203 for i := 0; i < len(s); i++ {
204 switch s[i] {
205 case '\\':
206 escape = !escape
207 if off+1 > lenmsg {
208 return labels, false
209 }
210 211 // check for \DDD
212 if isDDD(s[i+1:]) {
213 i += 3
214 begin += 3
215 } else {
216 i++
217 begin++
218 }
219 220 wasDot = false
221 case '.':
222 escape = false
223 if i == 0 && len(s) > 1 {
224 // leading dots are not legal except for the root zone
225 return labels, false
226 }
227 228 if wasDot {
229 // two dots back to back is not legal
230 return labels, false
231 }
232 wasDot = true
233 234 labelLen := i - begin
235 if labelLen >= 1<<6 { // top two bits of length must be clear
236 return labels, false
237 }
238 239 // off can already (we're in a loop) be bigger than lenmsg
240 // this happens when a name isn't fully qualified
241 off += 1 + labelLen
242 if off > lenmsg {
243 return labels, false
244 }
245 246 labels++
247 begin = i + 1
248 default:
249 escape = false
250 wasDot = false
251 }
252 }
253 if escape {
254 return labels, false
255 }
256 return labels, true
257 }
258 259 // IsSubDomain checks if child is indeed a child of the parent. If child and parent
260 // are the same domain true is returned as well.
261 func IsSubDomain(parent, child string) bool {
262 // Entire child is contained in parent
263 return CompareDomainName(parent, child) == CountLabel(parent)
264 }
265 266 // IsMsg sanity checks buf and returns an error if it isn't a valid DNS packet.
267 // The checking is performed on the binary payload.
268 func IsMsg(buf []byte) error {
269 // Header
270 if len(buf) < headerSize {
271 return errors.New("dns: bad message header")
272 }
273 // Header: Opcode
274 // TODO(miek): more checks here, e.g. check all header bits.
275 return nil
276 }
277 278 // IsFqdn checks if a domain name is fully qualified.
279 func IsFqdn(s string) bool {
280 // Check for (and remove) a trailing dot, returning if there isn't one.
281 if s == "" || s[len(s)-1] != '.' {
282 return false
283 }
284 s = s[:len(s)-1]
285 286 // If we don't have an escape sequence before the final dot, we know it's
287 // fully qualified and can return here.
288 if s == "" || s[len(s)-1] != '\\' {
289 return true
290 }
291 292 // Otherwise we have to check if the dot is escaped or not by checking if
293 // there are an odd or even number of escape sequences before the dot.
294 i := strings.LastIndexFunc(s, func(r rune) bool {
295 return r != '\\'
296 })
297 return (len(s)-i)%2 != 0
298 }
299 300 // IsRRset reports whether a set of RRs is a valid RRset as defined by RFC 2181.
301 // This means the RRs need to have the same type, name, and class.
302 func IsRRset(rrset []RR) bool {
303 if len(rrset) == 0 {
304 return false
305 }
306 307 baseH := rrset[0].Header()
308 for _, rr := range rrset[1:] {
309 curH := rr.Header()
310 if curH.Rrtype != baseH.Rrtype || curH.Class != baseH.Class || curH.Name != baseH.Name {
311 // Mismatch between the records, so this is not a valid rrset for
312 // signing/verifying
313 return false
314 }
315 }
316 317 return true
318 }
319 320 // Fqdn return the fully qualified domain name from s.
321 // If s is already fully qualified, it behaves as the identity function.
322 func Fqdn(s string) string {
323 if IsFqdn(s) {
324 return s
325 }
326 return s + "."
327 }
328 329 // CanonicalName returns the domain name in canonical form. A name in canonical
330 // form is lowercase and fully qualified. Only US-ASCII letters are affected. See
331 // Section 6.2 in RFC 4034.
332 func CanonicalName(s string) string {
333 return strings.Map(func(r rune) rune {
334 if r >= 'A' && r <= 'Z' {
335 r += 'a' - 'A'
336 }
337 return r
338 }, Fqdn(s))
339 }
340 341 // Copied from the official Go code.
342 343 // ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
344 // address suitable for reverse DNS (PTR) record lookups or an error if it fails
345 // to parse the IP address.
346 func ReverseAddr(addr string) (arpa string, err error) {
347 ip := net.ParseIP(addr)
348 if ip == nil {
349 return "", &Error{err: "unrecognized address: " + addr}
350 }
351 if v4 := ip.To4(); v4 != nil {
352 buf := make([]byte, 0, net.IPv4len*4+len("in-addr.arpa."))
353 // Add it, in reverse, to the buffer
354 for i := len(v4) - 1; i >= 0; i-- {
355 buf = strconv.AppendInt(buf, int64(v4[i]), 10)
356 buf = append(buf, '.')
357 }
358 // Append "in-addr.arpa." and return (buf already has the final .)
359 buf = append(buf, "in-addr.arpa."...)
360 return string(buf), nil
361 }
362 // Must be IPv6
363 buf := make([]byte, 0, net.IPv6len*4+len("ip6.arpa."))
364 // Add it, in reverse, to the buffer
365 for i := len(ip) - 1; i >= 0; i-- {
366 v := ip[i]
367 buf = append(buf, hexDigit[v&0xF], '.', hexDigit[v>>4], '.')
368 }
369 // Append "ip6.arpa." and return (buf already has the final .)
370 buf = append(buf, "ip6.arpa."...)
371 return string(buf), nil
372 }
373 374 // String returns the string representation for the type t.
375 func (t Type) String() string {
376 if t1, ok := TypeToString[uint16(t)]; ok {
377 return t1
378 }
379 return "TYPE" + strconv.Itoa(int(t))
380 }
381 382 // String returns the string representation for the class c.
383 func (c Class) String() string {
384 if s, ok := ClassToString[uint16(c)]; ok {
385 // Only emit mnemonics when they are unambiguous, specially ANY is in both.
386 if _, ok := StringToType[s]; !ok {
387 return s
388 }
389 }
390 return "CLASS" + strconv.Itoa(int(c))
391 }
392 393 // String returns the string representation for the name n.
394 func (n Name) String() string {
395 return sprintName(string(n))
396 }
397