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 "internal/bytealg" 9 "internal/itoa" 10 "sort" 11 12 "golang.org/x/net/dns/dnsmessage" 13) 14 15// provided by runtime 16func fastrand() uint32 17 18func randInt() int { 19 x, y := fastrand(), fastrand() // 32-bit halves 20 u := uint(x)<<31 ^ uint(int32(y)) // full uint, even on 64-bit systems; avoid 32-bit shift on 32-bit systems 21 i := int(u >> 1) // clear sign bit, even on 32-bit systems 22 return i 23} 24 25func randIntn(n int) int { 26 return randInt() % n 27} 28 29// reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP 30// address addr suitable for rDNS (PTR) record lookup or an error if it fails 31// to parse the IP address. 32func reverseaddr(addr string) (arpa string, err error) { 33 ip := ParseIP(addr) 34 if ip == nil { 35 return "", &DNSError{Err: "unrecognized address", Name: addr} 36 } 37 if ip.To4() != nil { 38 return itoa.Uitoa(uint(ip[15])) + "." + itoa.Uitoa(uint(ip[14])) + "." + itoa.Uitoa(uint(ip[13])) + "." + itoa.Uitoa(uint(ip[12])) + ".in-addr.arpa.", nil 39 } 40 // Must be IPv6 41 buf := make([]byte, 0, len(ip)*4+len("ip6.arpa.")) 42 // Add it, in reverse, to the buffer 43 for i := len(ip) - 1; i >= 0; i-- { 44 v := ip[i] 45 buf = append(buf, hexDigit[v&0xF], 46 '.', 47 hexDigit[v>>4], 48 '.') 49 } 50 // Append "ip6.arpa." and return (buf already has the final .) 51 buf = append(buf, "ip6.arpa."...) 52 return string(buf), nil 53} 54 55func equalASCIIName(x, y dnsmessage.Name) bool { 56 if x.Length != y.Length { 57 return false 58 } 59 for i := 0; i < int(x.Length); i++ { 60 a := x.Data[i] 61 b := y.Data[i] 62 if 'A' <= a && a <= 'Z' { 63 a += 0x20 64 } 65 if 'A' <= b && b <= 'Z' { 66 b += 0x20 67 } 68 if a != b { 69 return false 70 } 71 } 72 return true 73} 74 75// isDomainName checks if a string is a presentation-format domain name 76// (currently restricted to hostname-compatible "preferred name" LDH labels and 77// SRV-like "underscore labels"; see golang.org/issue/12421). 78func isDomainName(s string) bool { 79 // The root domain name is valid. See golang.org/issue/45715. 80 if s == "." { 81 return true 82 } 83 84 // See RFC 1035, RFC 3696. 85 // Presentation format has dots before every label except the first, and the 86 // terminal empty label is optional here because we assume fully-qualified 87 // (absolute) input. We must therefore reserve space for the first and last 88 // labels' length octets in wire format, where they are necessary and the 89 // maximum total length is 255. 90 // So our _effective_ maximum is 253, but 254 is not rejected if the last 91 // character is a dot. 92 l := len(s) 93 if l == 0 || l > 254 || l == 254 && s[l-1] != '.' { 94 return false 95 } 96 97 last := byte('.') 98 nonNumeric := false // true once we've seen a letter or hyphen 99 partlen := 0 100 for i := 0; i < len(s); i++ { 101 c := s[i] 102 switch { 103 default: 104 return false 105 case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_': 106 nonNumeric = true 107 partlen++ 108 case '0' <= c && c <= '9': 109 // fine 110 partlen++ 111 case c == '-': 112 // Byte before dash cannot be dot. 113 if last == '.' { 114 return false 115 } 116 partlen++ 117 nonNumeric = true 118 case c == '.': 119 // Byte before dot cannot be dot, dash. 120 if last == '.' || last == '-' { 121 return false 122 } 123 if partlen > 63 || partlen == 0 { 124 return false 125 } 126 partlen = 0 127 } 128 last = c 129 } 130 if last == '-' || partlen > 63 { 131 return false 132 } 133 134 return nonNumeric 135} 136 137// absDomainName returns an absolute domain name which ends with a 138// trailing dot to match pure Go reverse resolver and all other lookup 139// routines. 140// See golang.org/issue/12189. 141// But we don't want to add dots for local names from /etc/hosts. 142// It's hard to tell so we settle on the heuristic that names without dots 143// (like "localhost" or "myhost") do not get trailing dots, but any other 144// names do. 145func absDomainName(s string) string { 146 if bytealg.IndexByteString(s, '.') != -1 && s[len(s)-1] != '.' { 147 s += "." 148 } 149 return s 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