blob: e9c73845d78d7eee4bacc8645ec33685f70cc4cb [file] [log] [blame]
Russ Cox83348f92008-12-18 15:42:39 -08001// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Russ Cox83348f92008-12-18 15:42:39 -08005package net
6
7import (
Alex Brainman5da14d12011-08-05 10:27:51 +10008 "sort"
Ian Gudger672729e2017-11-22 17:12:30 -08009
Bryan C. Millsc5cf6622019-03-01 10:12:30 -050010 "golang.org/x/net/dns/dnsmessage"
Russ Cox83348f92008-12-18 15:42:39 -080011)
12
Russ Cox39b52762020-07-07 09:07:16 -040013// provided by runtime
14func fastrand() uint32
15
16func randInt() int {
17 x, y := fastrand(), fastrand() // 32-bit halves
18 u := uint(x)<<31 ^ uint(int32(y)) // full uint, even on 64-bit systems; avoid 32-bit shift on 32-bit systems
19 i := int(u >> 1) // clear sign bit, even on 32-bit systems
20 return i
21}
22
23func randIntn(n int) int {
24 return randInt() % n
25}
26
Alex Brainman9ded9542011-06-13 10:22:31 +100027// reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
28// address addr suitable for rDNS (PTR) record lookup or an error if it fails
29// to parse the IP address.
Russ Coxeb692922011-11-01 22:05:34 -040030func reverseaddr(addr string) (arpa string, err error) {
Alex Brainman9ded9542011-06-13 10:22:31 +100031 ip := ParseIP(addr)
32 if ip == nil {
Russ Coxeb692922011-11-01 22:05:34 -040033 return "", &DNSError{Err: "unrecognized address", Name: addr}
Russ Cox83348f92008-12-18 15:42:39 -080034 }
Alex Brainman9ded9542011-06-13 10:22:31 +100035 if ip.To4() != nil {
Mikio Hara0fc582e82015-04-19 20:54:01 +090036 return uitoa(uint(ip[15])) + "." + uitoa(uint(ip[14])) + "." + uitoa(uint(ip[13])) + "." + uitoa(uint(ip[12])) + ".in-addr.arpa.", nil
Robert Griesemera3d10452009-12-15 15:35:38 -080037 }
Alex Brainman9ded9542011-06-13 10:22:31 +100038 // Must be IPv6
Brad Fitzpatrick610b5b22012-03-05 13:36:05 -080039 buf := make([]byte, 0, len(ip)*4+len("ip6.arpa."))
Alex Brainman9ded9542011-06-13 10:22:31 +100040 // Add it, in reverse, to the buffer
41 for i := len(ip) - 1; i >= 0; i-- {
Brad Fitzpatrick610b5b22012-03-05 13:36:05 -080042 v := ip[i]
Iskander Sharipovdc792062018-06-09 20:43:54 +030043 buf = append(buf, hexDigit[v&0xF],
44 '.',
45 hexDigit[v>>4],
46 '.')
Russ Cox83348f92008-12-18 15:42:39 -080047 }
Alex Brainman9ded9542011-06-13 10:22:31 +100048 // Append "ip6.arpa." and return (buf already has the final .)
Brad Fitzpatrick610b5b22012-03-05 13:36:05 -080049 buf = append(buf, "ip6.arpa."...)
50 return string(buf), nil
Russ Cox83348f92008-12-18 15:42:39 -080051}
52
Ian Gudger672729e2017-11-22 17:12:30 -080053func equalASCIIName(x, y dnsmessage.Name) bool {
54 if x.Length != y.Length {
Mikio Hara826fa062015-02-02 21:09:23 +090055 return false
56 }
Ian Gudger672729e2017-11-22 17:12:30 -080057 for i := 0; i < int(x.Length); i++ {
58 a := x.Data[i]
59 b := y.Data[i]
Mikio Hara826fa062015-02-02 21:09:23 +090060 if 'A' <= a && a <= 'Z' {
61 a += 0x20
62 }
63 if 'A' <= b && b <= 'Z' {
64 b += 0x20
65 }
66 if a != b {
67 return false
68 }
69 }
70 return true
71}
72
Richard Gibson9a5bddd2016-10-22 00:21:18 -040073// isDomainName checks if a string is a presentation-format domain name
74// (currently restricted to hostname-compatible "preferred name" LDH labels and
75// SRV-like "underscore labels"; see golang.org/issue/12421).
Russ Coxa0bcaf42009-06-25 20:24:55 -070076func isDomainName(s string) bool {
Russ Coxa0a77682010-10-06 11:54:53 -040077 // See RFC 1035, RFC 3696.
Richard Gibson9a5bddd2016-10-22 00:21:18 -040078 // Presentation format has dots before every label except the first, and the
79 // terminal empty label is optional here because we assume fully-qualified
80 // (absolute) input. We must therefore reserve space for the first and last
81 // labels' length octets in wire format, where they are necessary and the
82 // maximum total length is 255.
83 // So our _effective_ maximum is 253, but 254 is not rejected if the last
84 // character is a dot.
85 l := len(s)
86 if l == 0 || l > 254 || l == 254 && s[l-1] != '.' {
Chris Jones25b1e832010-11-04 10:30:39 -040087 return false
88 }
Russ Coxa0bcaf42009-06-25 20:24:55 -070089
Robert Griesemera3d10452009-12-15 15:35:38 -080090 last := byte('.')
Brad Fitzpatrick5b3aafe2018-09-24 17:30:48 +000091 nonNumeric := false // true once we've seen a letter or hyphen
Chris Jones25b1e832010-11-04 10:30:39 -040092 partlen := 0
Russ Coxa0bcaf42009-06-25 20:24:55 -070093 for i := 0; i < len(s); i++ {
Robert Griesemera3d10452009-12-15 15:35:38 -080094 c := s[i]
Russ Coxa0bcaf42009-06-25 20:24:55 -070095 switch {
96 default:
Robert Griesemer40621d52009-11-09 12:07:39 -080097 return false
Russ Cox2408a4b2010-10-07 06:45:50 -040098 case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_':
Brad Fitzpatrick5b3aafe2018-09-24 17:30:48 +000099 nonNumeric = true
Chris Jones25b1e832010-11-04 10:30:39 -0400100 partlen++
Russ Coxa0bcaf42009-06-25 20:24:55 -0700101 case '0' <= c && c <= '9':
102 // fine
Chris Jones25b1e832010-11-04 10:30:39 -0400103 partlen++
Russ Coxa0bcaf42009-06-25 20:24:55 -0700104 case c == '-':
Volker Dobler654f3582013-08-08 16:33:57 -0700105 // Byte before dash cannot be dot.
Russ Coxa0bcaf42009-06-25 20:24:55 -0700106 if last == '.' {
Robert Griesemer40621d52009-11-09 12:07:39 -0800107 return false
Russ Coxa0bcaf42009-06-25 20:24:55 -0700108 }
Chris Jones25b1e832010-11-04 10:30:39 -0400109 partlen++
Brad Fitzpatrick5b3aafe2018-09-24 17:30:48 +0000110 nonNumeric = true
Russ Coxa0bcaf42009-06-25 20:24:55 -0700111 case c == '.':
Volker Dobler654f3582013-08-08 16:33:57 -0700112 // Byte before dot cannot be dot, dash.
Russ Coxa0bcaf42009-06-25 20:24:55 -0700113 if last == '.' || last == '-' {
Robert Griesemer40621d52009-11-09 12:07:39 -0800114 return false
Russ Coxa0bcaf42009-06-25 20:24:55 -0700115 }
Chris Jones25b1e832010-11-04 10:30:39 -0400116 if partlen > 63 || partlen == 0 {
117 return false
118 }
119 partlen = 0
Russ Coxa0bcaf42009-06-25 20:24:55 -0700120 }
Robert Griesemera3d10452009-12-15 15:35:38 -0800121 last = c
Russ Coxa0bcaf42009-06-25 20:24:55 -0700122 }
Volker Dobler654f3582013-08-08 16:33:57 -0700123 if last == '-' || partlen > 63 {
124 return false
125 }
Russ Coxa0bcaf42009-06-25 20:24:55 -0700126
Brad Fitzpatrick5b3aafe2018-09-24 17:30:48 +0000127 return nonNumeric
Russ Coxa0bcaf42009-06-25 20:24:55 -0700128}
129
Martin Möhrmannfdd01792016-02-24 11:55:20 +0100130// absDomainName returns an absolute domain name which ends with a
Mikio Hara4d6a69f2015-11-27 12:09:14 +0900131// trailing dot to match pure Go reverse resolver and all other lookup
132// routines.
133// See golang.org/issue/12189.
Russ Cox6b9298a2016-01-06 20:37:05 -0500134// But we don't want to add dots for local names from /etc/hosts.
135// It's hard to tell so we settle on the heuristic that names without dots
136// (like "localhost" or "myhost") do not get trailing dots, but any other
137// names do.
Mikio Hara4d6a69f2015-11-27 12:09:14 +0900138func absDomainName(b []byte) string {
Russ Cox6b9298a2016-01-06 20:37:05 -0500139 hasDots := false
140 for _, x := range b {
141 if x == '.' {
142 hasDots = true
Mikio Hara57b1e552016-01-09 03:47:40 +0900143 break
Russ Cox6b9298a2016-01-06 20:37:05 -0500144 }
145 }
146 if hasDots && b[len(b)-1] != '.' {
Mikio Hara4d6a69f2015-11-27 12:09:14 +0900147 b = append(b, '.')
148 }
149 return string(b)
150}
151
Russ Cox41f93a42011-03-28 23:28:42 -0400152// An SRV represents a single DNS SRV record.
Kirklin McDonald0046d512010-06-30 10:54:24 +1000153type SRV struct {
154 Target string
155 Port uint16
156 Priority uint16
157 Weight uint16
158}
159
Gary Burdaea17572011-05-16 17:48:00 -0400160// byPriorityWeight sorts SRV records by ascending priority and weight.
161type byPriorityWeight []*SRV
162
163func (s byPriorityWeight) Len() int { return len(s) }
Gary Burdaea17572011-05-16 17:48:00 -0400164func (s byPriorityWeight) Less(i, j int) bool {
Mikio Hara0fc582e82015-04-19 20:54:01 +0900165 return s[i].Priority < s[j].Priority || (s[i].Priority == s[j].Priority && s[i].Weight < s[j].Weight)
Gary Burdaea17572011-05-16 17:48:00 -0400166}
Mikio Hara0fc582e82015-04-19 20:54:01 +0900167func (s byPriorityWeight) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
Gary Burdaea17572011-05-16 17:48:00 -0400168
Alex Brainman5da14d12011-08-05 10:27:51 +1000169// shuffleByWeight shuffles SRV records by weight using the algorithm
Robert Griesemer465b9c32012-10-30 13:38:01 -0700170// described in RFC 2782.
Alex Brainman5da14d12011-08-05 10:27:51 +1000171func (addrs byPriorityWeight) shuffleByWeight() {
Gary Burdaea17572011-05-16 17:48:00 -0400172 sum := 0
173 for _, addr := range addrs {
174 sum += int(addr.Weight)
175 }
176 for sum > 0 && len(addrs) > 1 {
177 s := 0
Russ Cox39b52762020-07-07 09:07:16 -0400178 n := randIntn(sum)
Gary Burdaea17572011-05-16 17:48:00 -0400179 for i := range addrs {
180 s += int(addrs[i].Weight)
Brad Fitzpatrickc45392b2014-04-17 13:28:40 -0700181 if s > n {
Gary Burdaea17572011-05-16 17:48:00 -0400182 if i > 0 {
Rui Ueyama38eea5b2014-06-16 18:00:28 -0700183 addrs[0], addrs[i] = addrs[i], addrs[0]
Gary Burdaea17572011-05-16 17:48:00 -0400184 }
185 break
186 }
187 }
188 sum -= int(addrs[0].Weight)
189 addrs = addrs[1:]
190 }
191}
192
Alex Brainman5da14d12011-08-05 10:27:51 +1000193// sort reorders SRV records as specified in RFC 2782.
194func (addrs byPriorityWeight) sort() {
195 sort.Sort(addrs)
196 i := 0
197 for j := 1; j < len(addrs); j++ {
198 if addrs[i].Priority != addrs[j].Priority {
199 addrs[i:j].shuffleByWeight()
200 i = j
201 }
202 }
203 addrs[i:].shuffleByWeight()
204}
205
Russ Cox41f93a42011-03-28 23:28:42 -0400206// An MX represents a single DNS MX record.
Corey Thomassonec72f9b2010-08-26 13:32:45 -0400207type MX struct {
208 Host string
209 Pref uint16
210}
211
Corey Thomasson785fbd92011-04-14 10:30:56 +1000212// byPref implements sort.Interface to sort MX records by preference
213type byPref []*MX
214
Mikio Hara0fc582e82015-04-19 20:54:01 +0900215func (s byPref) Len() int { return len(s) }
Corey Thomasson785fbd92011-04-14 10:30:56 +1000216func (s byPref) Less(i, j int) bool { return s[i].Pref < s[j].Pref }
Mikio Hara0fc582e82015-04-19 20:54:01 +0900217func (s byPref) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
Alex Brainman5da14d12011-08-05 10:27:51 +1000218
219// sort reorders MX records as specified in RFC 5321.
220func (s byPref) sort() {
221 for i := range s {
Russ Cox39b52762020-07-07 09:07:16 -0400222 j := randIntn(i + 1)
Alex Brainman5da14d12011-08-05 10:27:51 +1000223 s[i], s[j] = s[j], s[i]
224 }
225 sort.Sort(s)
226}
Stephen McQuaya5b0c672012-10-18 15:39:04 +0900227
228// An NS represents a single DNS NS record.
229type NS struct {
230 Host string
231}