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