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 5package net 6 7import ( 8 "sort" 9 10 "golang.org/x/net/dns/dnsmessage" 11) 12 13// 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 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. 30func reverseaddr(addr string) (arpa string, err error) { 31 ip := ParseIP(addr) 32 if ip == nil { 33 return "", &DNSError{Err: "unrecognized address", Name: addr} 34 } 35 if ip.To4() != nil { 36 return uitoa(uint(ip[15])) + "." + uitoa(uint(ip[14])) + "." + uitoa(uint(ip[13])) + "." + uitoa(uint(ip[12])) + ".in-addr.arpa.", nil 37 } 38 // Must be IPv6 39 buf := make([]byte, 0, len(ip)*4+len("ip6.arpa.")) 40 // Add it, in reverse, to the buffer 41 for i := len(ip) - 1; i >= 0; i-- { 42 v := ip[i] 43 buf = append(buf, hexDigit[v&0xF], 44 '.', 45 hexDigit[v>>4], 46 '.') 47 } 48 // Append "ip6.arpa." and return (buf already has the final .) 49 buf = append(buf, "ip6.arpa."...) 50 return string(buf), nil 51} 52 53func equalASCIIName(x, y dnsmessage.Name) bool { 54 if x.Length != y.Length { 55 return false 56 } 57 for i := 0; i < int(x.Length); i++ { 58 a := x.Data[i] 59 b := y.Data[i] 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 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). 76func isDomainName(s string) bool { 77 // See RFC 1035, RFC 3696. 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] != '.' { 87 return false 88 } 89 90 last := byte('.') 91 nonNumeric := false // true once we've seen a letter or hyphen 92 partlen := 0 93 for i := 0; i < len(s); i++ { 94 c := s[i] 95 switch { 96 default: 97 return false 98 case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_': 99 nonNumeric = true 100 partlen++ 101 case '0' <= c && c <= '9': 102 // fine 103 partlen++ 104 case c == '-': 105 // Byte before dash cannot be dot. 106 if last == '.' { 107 return false 108 } 109 partlen++ 110 nonNumeric = true 111 case c == '.': 112 // Byte before dot cannot be dot, dash. 113 if last == '.' || last == '-' { 114 return false 115 } 116 if partlen > 63 || partlen == 0 { 117 return false 118 } 119 partlen = 0 120 } 121 last = c 122 } 123 if last == '-' || partlen > 63 { 124 return false 125 } 126 127 return nonNumeric 128} 129 130// absDomainName returns an absolute domain name which ends with a 131// trailing dot to match pure Go reverse resolver and all other lookup 132// routines. 133// See golang.org/issue/12189. 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. 138func absDomainName(b []byte) string { 139 hasDots := false 140 for _, x := range b { 141 if x == '.' { 142 hasDots = true 143 break 144 } 145 } 146 if hasDots && b[len(b)-1] != '.' { 147 b = append(b, '.') 148 } 149 return string(b) 150} 151 152// An SRV represents a single DNS SRV record. 153type SRV struct { 154 Target string 155 Port uint16 156 Priority uint16 157 Weight uint16 158} 159 160// byPriorityWeight sorts SRV records by ascending priority and weight. 161type byPriorityWeight []*SRV 162 163func (s byPriorityWeight) Len() int { return len(s) } 164func (s byPriorityWeight) Less(i, j int) bool { 165 return s[i].Priority < s[j].Priority || (s[i].Priority == s[j].Priority && s[i].Weight < s[j].Weight) 166} 167func (s byPriorityWeight) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 168 169// shuffleByWeight shuffles SRV records by weight using the algorithm 170// described in RFC 2782. 171func (addrs byPriorityWeight) shuffleByWeight() { 172 sum := 0 173 for _, addr := range addrs { 174 sum += int(addr.Weight) 175 } 176 for sum > 0 && len(addrs) > 1 { 177 s := 0 178 n := randIntn(sum) 179 for i := range addrs { 180 s += int(addrs[i].Weight) 181 if s > n { 182 if i > 0 { 183 addrs[0], addrs[i] = addrs[i], addrs[0] 184 } 185 break 186 } 187 } 188 sum -= int(addrs[0].Weight) 189 addrs = addrs[1:] 190 } 191} 192 193// 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 206// An MX represents a single DNS MX record. 207type MX struct { 208 Host string 209 Pref uint16 210} 211 212// byPref implements sort.Interface to sort MX records by preference 213type byPref []*MX 214 215func (s byPref) Len() int { return len(s) } 216func (s byPref) Less(i, j int) bool { return s[i].Pref < s[j].Pref } 217func (s byPref) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 218 219// sort reorders MX records as specified in RFC 5321. 220func (s byPref) sort() { 221 for i := range s { 222 j := randIntn(i + 1) 223 s[i], s[j] = s[j], s[i] 224 } 225 sort.Sort(s) 226} 227 228// An NS represents a single DNS NS record. 229type NS struct { 230 Host string 231} 232