Russ Cox | 83348f9 | 2008-12-18 15:42:39 -0800 | [diff] [blame] | 1 | // 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 Cox | 83348f9 | 2008-12-18 15:42:39 -0800 | [diff] [blame] | 5 | package net |
| 6 | |
| 7 | import ( |
Alex Brainman | 5da14d1 | 2011-08-05 10:27:51 +1000 | [diff] [blame] | 8 | "sort" |
Ian Gudger | 672729e | 2017-11-22 17:12:30 -0800 | [diff] [blame] | 9 | |
Bryan C. Mills | c5cf662 | 2019-03-01 10:12:30 -0500 | [diff] [blame] | 10 | "golang.org/x/net/dns/dnsmessage" |
Russ Cox | 83348f9 | 2008-12-18 15:42:39 -0800 | [diff] [blame] | 11 | ) |
| 12 | |
Russ Cox | 39b5276 | 2020-07-07 09:07:16 -0400 | [diff] [blame] | 13 | // provided by runtime |
| 14 | func fastrand() uint32 |
| 15 | |
| 16 | func 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 | |
| 23 | func randIntn(n int) int { |
| 24 | return randInt() % n |
| 25 | } |
| 26 | |
Alex Brainman | 9ded954 | 2011-06-13 10:22:31 +1000 | [diff] [blame] | 27 | // 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 Cox | eb69292 | 2011-11-01 22:05:34 -0400 | [diff] [blame] | 30 | func reverseaddr(addr string) (arpa string, err error) { |
Alex Brainman | 9ded954 | 2011-06-13 10:22:31 +1000 | [diff] [blame] | 31 | ip := ParseIP(addr) |
| 32 | if ip == nil { |
Russ Cox | eb69292 | 2011-11-01 22:05:34 -0400 | [diff] [blame] | 33 | return "", &DNSError{Err: "unrecognized address", Name: addr} |
Russ Cox | 83348f9 | 2008-12-18 15:42:39 -0800 | [diff] [blame] | 34 | } |
Alex Brainman | 9ded954 | 2011-06-13 10:22:31 +1000 | [diff] [blame] | 35 | if ip.To4() != nil { |
Mikio Hara | 0fc582e8 | 2015-04-19 20:54:01 +0900 | [diff] [blame] | 36 | return uitoa(uint(ip[15])) + "." + uitoa(uint(ip[14])) + "." + uitoa(uint(ip[13])) + "." + uitoa(uint(ip[12])) + ".in-addr.arpa.", nil |
Robert Griesemer | a3d1045 | 2009-12-15 15:35:38 -0800 | [diff] [blame] | 37 | } |
Alex Brainman | 9ded954 | 2011-06-13 10:22:31 +1000 | [diff] [blame] | 38 | // Must be IPv6 |
Brad Fitzpatrick | 610b5b2 | 2012-03-05 13:36:05 -0800 | [diff] [blame] | 39 | buf := make([]byte, 0, len(ip)*4+len("ip6.arpa.")) |
Alex Brainman | 9ded954 | 2011-06-13 10:22:31 +1000 | [diff] [blame] | 40 | // Add it, in reverse, to the buffer |
| 41 | for i := len(ip) - 1; i >= 0; i-- { |
Brad Fitzpatrick | 610b5b2 | 2012-03-05 13:36:05 -0800 | [diff] [blame] | 42 | v := ip[i] |
Iskander Sharipov | dc79206 | 2018-06-09 20:43:54 +0300 | [diff] [blame] | 43 | buf = append(buf, hexDigit[v&0xF], |
| 44 | '.', |
| 45 | hexDigit[v>>4], |
| 46 | '.') |
Russ Cox | 83348f9 | 2008-12-18 15:42:39 -0800 | [diff] [blame] | 47 | } |
Alex Brainman | 9ded954 | 2011-06-13 10:22:31 +1000 | [diff] [blame] | 48 | // Append "ip6.arpa." and return (buf already has the final .) |
Brad Fitzpatrick | 610b5b2 | 2012-03-05 13:36:05 -0800 | [diff] [blame] | 49 | buf = append(buf, "ip6.arpa."...) |
| 50 | return string(buf), nil |
Russ Cox | 83348f9 | 2008-12-18 15:42:39 -0800 | [diff] [blame] | 51 | } |
| 52 | |
Ian Gudger | 672729e | 2017-11-22 17:12:30 -0800 | [diff] [blame] | 53 | func equalASCIIName(x, y dnsmessage.Name) bool { |
| 54 | if x.Length != y.Length { |
Mikio Hara | 826fa06 | 2015-02-02 21:09:23 +0900 | [diff] [blame] | 55 | return false |
| 56 | } |
Ian Gudger | 672729e | 2017-11-22 17:12:30 -0800 | [diff] [blame] | 57 | for i := 0; i < int(x.Length); i++ { |
| 58 | a := x.Data[i] |
| 59 | b := y.Data[i] |
Mikio Hara | 826fa06 | 2015-02-02 21:09:23 +0900 | [diff] [blame] | 60 | 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 Gibson | 9a5bddd | 2016-10-22 00:21:18 -0400 | [diff] [blame] | 73 | // 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 Cox | a0bcaf4 | 2009-06-25 20:24:55 -0700 | [diff] [blame] | 76 | func isDomainName(s string) bool { |
Russ Cox | a0a7768 | 2010-10-06 11:54:53 -0400 | [diff] [blame] | 77 | // See RFC 1035, RFC 3696. |
Richard Gibson | 9a5bddd | 2016-10-22 00:21:18 -0400 | [diff] [blame] | 78 | // 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 Jones | 25b1e83 | 2010-11-04 10:30:39 -0400 | [diff] [blame] | 87 | return false |
| 88 | } |
Russ Cox | a0bcaf4 | 2009-06-25 20:24:55 -0700 | [diff] [blame] | 89 | |
Robert Griesemer | a3d1045 | 2009-12-15 15:35:38 -0800 | [diff] [blame] | 90 | last := byte('.') |
Brad Fitzpatrick | 5b3aafe | 2018-09-24 17:30:48 +0000 | [diff] [blame] | 91 | nonNumeric := false // true once we've seen a letter or hyphen |
Chris Jones | 25b1e83 | 2010-11-04 10:30:39 -0400 | [diff] [blame] | 92 | partlen := 0 |
Russ Cox | a0bcaf4 | 2009-06-25 20:24:55 -0700 | [diff] [blame] | 93 | for i := 0; i < len(s); i++ { |
Robert Griesemer | a3d1045 | 2009-12-15 15:35:38 -0800 | [diff] [blame] | 94 | c := s[i] |
Russ Cox | a0bcaf4 | 2009-06-25 20:24:55 -0700 | [diff] [blame] | 95 | switch { |
| 96 | default: |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 97 | return false |
Russ Cox | 2408a4b | 2010-10-07 06:45:50 -0400 | [diff] [blame] | 98 | case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_': |
Brad Fitzpatrick | 5b3aafe | 2018-09-24 17:30:48 +0000 | [diff] [blame] | 99 | nonNumeric = true |
Chris Jones | 25b1e83 | 2010-11-04 10:30:39 -0400 | [diff] [blame] | 100 | partlen++ |
Russ Cox | a0bcaf4 | 2009-06-25 20:24:55 -0700 | [diff] [blame] | 101 | case '0' <= c && c <= '9': |
| 102 | // fine |
Chris Jones | 25b1e83 | 2010-11-04 10:30:39 -0400 | [diff] [blame] | 103 | partlen++ |
Russ Cox | a0bcaf4 | 2009-06-25 20:24:55 -0700 | [diff] [blame] | 104 | case c == '-': |
Volker Dobler | 654f358 | 2013-08-08 16:33:57 -0700 | [diff] [blame] | 105 | // Byte before dash cannot be dot. |
Russ Cox | a0bcaf4 | 2009-06-25 20:24:55 -0700 | [diff] [blame] | 106 | if last == '.' { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 107 | return false |
Russ Cox | a0bcaf4 | 2009-06-25 20:24:55 -0700 | [diff] [blame] | 108 | } |
Chris Jones | 25b1e83 | 2010-11-04 10:30:39 -0400 | [diff] [blame] | 109 | partlen++ |
Brad Fitzpatrick | 5b3aafe | 2018-09-24 17:30:48 +0000 | [diff] [blame] | 110 | nonNumeric = true |
Russ Cox | a0bcaf4 | 2009-06-25 20:24:55 -0700 | [diff] [blame] | 111 | case c == '.': |
Volker Dobler | 654f358 | 2013-08-08 16:33:57 -0700 | [diff] [blame] | 112 | // Byte before dot cannot be dot, dash. |
Russ Cox | a0bcaf4 | 2009-06-25 20:24:55 -0700 | [diff] [blame] | 113 | if last == '.' || last == '-' { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 114 | return false |
Russ Cox | a0bcaf4 | 2009-06-25 20:24:55 -0700 | [diff] [blame] | 115 | } |
Chris Jones | 25b1e83 | 2010-11-04 10:30:39 -0400 | [diff] [blame] | 116 | if partlen > 63 || partlen == 0 { |
| 117 | return false |
| 118 | } |
| 119 | partlen = 0 |
Russ Cox | a0bcaf4 | 2009-06-25 20:24:55 -0700 | [diff] [blame] | 120 | } |
Robert Griesemer | a3d1045 | 2009-12-15 15:35:38 -0800 | [diff] [blame] | 121 | last = c |
Russ Cox | a0bcaf4 | 2009-06-25 20:24:55 -0700 | [diff] [blame] | 122 | } |
Volker Dobler | 654f358 | 2013-08-08 16:33:57 -0700 | [diff] [blame] | 123 | if last == '-' || partlen > 63 { |
| 124 | return false |
| 125 | } |
Russ Cox | a0bcaf4 | 2009-06-25 20:24:55 -0700 | [diff] [blame] | 126 | |
Brad Fitzpatrick | 5b3aafe | 2018-09-24 17:30:48 +0000 | [diff] [blame] | 127 | return nonNumeric |
Russ Cox | a0bcaf4 | 2009-06-25 20:24:55 -0700 | [diff] [blame] | 128 | } |
| 129 | |
Martin Möhrmann | fdd0179 | 2016-02-24 11:55:20 +0100 | [diff] [blame] | 130 | // absDomainName returns an absolute domain name which ends with a |
Mikio Hara | 4d6a69f | 2015-11-27 12:09:14 +0900 | [diff] [blame] | 131 | // trailing dot to match pure Go reverse resolver and all other lookup |
| 132 | // routines. |
| 133 | // See golang.org/issue/12189. |
Russ Cox | 6b9298a | 2016-01-06 20:37:05 -0500 | [diff] [blame] | 134 | // 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 Hara | 4d6a69f | 2015-11-27 12:09:14 +0900 | [diff] [blame] | 138 | func absDomainName(b []byte) string { |
Russ Cox | 6b9298a | 2016-01-06 20:37:05 -0500 | [diff] [blame] | 139 | hasDots := false |
| 140 | for _, x := range b { |
| 141 | if x == '.' { |
| 142 | hasDots = true |
Mikio Hara | 57b1e55 | 2016-01-09 03:47:40 +0900 | [diff] [blame] | 143 | break |
Russ Cox | 6b9298a | 2016-01-06 20:37:05 -0500 | [diff] [blame] | 144 | } |
| 145 | } |
| 146 | if hasDots && b[len(b)-1] != '.' { |
Mikio Hara | 4d6a69f | 2015-11-27 12:09:14 +0900 | [diff] [blame] | 147 | b = append(b, '.') |
| 148 | } |
| 149 | return string(b) |
| 150 | } |
| 151 | |
Russ Cox | 41f93a4 | 2011-03-28 23:28:42 -0400 | [diff] [blame] | 152 | // An SRV represents a single DNS SRV record. |
Kirklin McDonald | 0046d51 | 2010-06-30 10:54:24 +1000 | [diff] [blame] | 153 | type SRV struct { |
| 154 | Target string |
| 155 | Port uint16 |
| 156 | Priority uint16 |
| 157 | Weight uint16 |
| 158 | } |
| 159 | |
Gary Burd | aea1757 | 2011-05-16 17:48:00 -0400 | [diff] [blame] | 160 | // byPriorityWeight sorts SRV records by ascending priority and weight. |
| 161 | type byPriorityWeight []*SRV |
| 162 | |
| 163 | func (s byPriorityWeight) Len() int { return len(s) } |
Gary Burd | aea1757 | 2011-05-16 17:48:00 -0400 | [diff] [blame] | 164 | func (s byPriorityWeight) Less(i, j int) bool { |
Mikio Hara | 0fc582e8 | 2015-04-19 20:54:01 +0900 | [diff] [blame] | 165 | return s[i].Priority < s[j].Priority || (s[i].Priority == s[j].Priority && s[i].Weight < s[j].Weight) |
Gary Burd | aea1757 | 2011-05-16 17:48:00 -0400 | [diff] [blame] | 166 | } |
Mikio Hara | 0fc582e8 | 2015-04-19 20:54:01 +0900 | [diff] [blame] | 167 | func (s byPriorityWeight) Swap(i, j int) { s[i], s[j] = s[j], s[i] } |
Gary Burd | aea1757 | 2011-05-16 17:48:00 -0400 | [diff] [blame] | 168 | |
Alex Brainman | 5da14d1 | 2011-08-05 10:27:51 +1000 | [diff] [blame] | 169 | // shuffleByWeight shuffles SRV records by weight using the algorithm |
Robert Griesemer | 465b9c3 | 2012-10-30 13:38:01 -0700 | [diff] [blame] | 170 | // described in RFC 2782. |
Alex Brainman | 5da14d1 | 2011-08-05 10:27:51 +1000 | [diff] [blame] | 171 | func (addrs byPriorityWeight) shuffleByWeight() { |
Gary Burd | aea1757 | 2011-05-16 17:48:00 -0400 | [diff] [blame] | 172 | sum := 0 |
| 173 | for _, addr := range addrs { |
| 174 | sum += int(addr.Weight) |
| 175 | } |
| 176 | for sum > 0 && len(addrs) > 1 { |
| 177 | s := 0 |
Russ Cox | 39b5276 | 2020-07-07 09:07:16 -0400 | [diff] [blame] | 178 | n := randIntn(sum) |
Gary Burd | aea1757 | 2011-05-16 17:48:00 -0400 | [diff] [blame] | 179 | for i := range addrs { |
| 180 | s += int(addrs[i].Weight) |
Brad Fitzpatrick | c45392b | 2014-04-17 13:28:40 -0700 | [diff] [blame] | 181 | if s > n { |
Gary Burd | aea1757 | 2011-05-16 17:48:00 -0400 | [diff] [blame] | 182 | if i > 0 { |
Rui Ueyama | 38eea5b | 2014-06-16 18:00:28 -0700 | [diff] [blame] | 183 | addrs[0], addrs[i] = addrs[i], addrs[0] |
Gary Burd | aea1757 | 2011-05-16 17:48:00 -0400 | [diff] [blame] | 184 | } |
| 185 | break |
| 186 | } |
| 187 | } |
| 188 | sum -= int(addrs[0].Weight) |
| 189 | addrs = addrs[1:] |
| 190 | } |
| 191 | } |
| 192 | |
Alex Brainman | 5da14d1 | 2011-08-05 10:27:51 +1000 | [diff] [blame] | 193 | // sort reorders SRV records as specified in RFC 2782. |
| 194 | func (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 Cox | 41f93a4 | 2011-03-28 23:28:42 -0400 | [diff] [blame] | 206 | // An MX represents a single DNS MX record. |
Corey Thomasson | ec72f9b | 2010-08-26 13:32:45 -0400 | [diff] [blame] | 207 | type MX struct { |
| 208 | Host string |
| 209 | Pref uint16 |
| 210 | } |
| 211 | |
Corey Thomasson | 785fbd9 | 2011-04-14 10:30:56 +1000 | [diff] [blame] | 212 | // byPref implements sort.Interface to sort MX records by preference |
| 213 | type byPref []*MX |
| 214 | |
Mikio Hara | 0fc582e8 | 2015-04-19 20:54:01 +0900 | [diff] [blame] | 215 | func (s byPref) Len() int { return len(s) } |
Corey Thomasson | 785fbd9 | 2011-04-14 10:30:56 +1000 | [diff] [blame] | 216 | func (s byPref) Less(i, j int) bool { return s[i].Pref < s[j].Pref } |
Mikio Hara | 0fc582e8 | 2015-04-19 20:54:01 +0900 | [diff] [blame] | 217 | func (s byPref) Swap(i, j int) { s[i], s[j] = s[j], s[i] } |
Alex Brainman | 5da14d1 | 2011-08-05 10:27:51 +1000 | [diff] [blame] | 218 | |
| 219 | // sort reorders MX records as specified in RFC 5321. |
| 220 | func (s byPref) sort() { |
| 221 | for i := range s { |
Russ Cox | 39b5276 | 2020-07-07 09:07:16 -0400 | [diff] [blame] | 222 | j := randIntn(i + 1) |
Alex Brainman | 5da14d1 | 2011-08-05 10:27:51 +1000 | [diff] [blame] | 223 | s[i], s[j] = s[j], s[i] |
| 224 | } |
| 225 | sort.Sort(s) |
| 226 | } |
Stephen McQuay | a5b0c67 | 2012-10-18 15:39:04 +0900 | [diff] [blame] | 227 | |
| 228 | // An NS represents a single DNS NS record. |
| 229 | type NS struct { |
| 230 | Host string |
| 231 | } |