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