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.
12 // reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
13 // address addr suitable for rDNS (PTR) record lookup or an error if it fails
14 // to parse the IP address.
15 func reverseaddr(addr
string) (arpa
string, err error
) {
18 return "", &DNSError
{Err
: "unrecognized address", Name
: addr
}
21 return uitoa(uint(ip
[15])) + "." + uitoa(uint(ip
[14])) + "." + uitoa(uint(ip
[13])) + "." + uitoa(uint(ip
[12])) + ".in-addr.arpa.", nil
24 buf
:= make([]byte, 0, len(ip
)*4+len("ip6.arpa."))
25 // Add it, in reverse, to the buffer
26 for i
:= len(ip
) - 1; i
>= 0; i
-- {
28 buf
= append(buf
, hexDigit
[v
&0xF])
29 buf
= append(buf
, '.')
30 buf
= append(buf
, hexDigit
[v
>>4])
31 buf
= append(buf
, '.')
33 // Append "ip6.arpa." and return (buf already has the final .)
34 buf
= append(buf
, "ip6.arpa."...)
35 return string(buf
), nil
38 // Find answer for name in dns message.
39 // On return, if err == nil, addrs != nil.
40 func answer(name
, server
string, dns
*dnsMsg
, qtype
uint16) (cname
string, addrs
[]dnsRR
, err error
) {
41 addrs
= make([]dnsRR
, 0, len(dns
.answer
))
43 if dns
.rcode
== dnsRcodeNameError
{
44 return "", nil, &DNSError
{Err
: errNoSuchHost
.Error(), Name
: name
, Server
: server
}
46 if dns
.rcode
!= dnsRcodeSuccess
{
47 // None of the error codes make sense
48 // for the query we sent. If we didn't get
49 // a name error and we didn't get success,
50 // the server is behaving incorrectly or
51 // having temporary trouble.
52 err
:= &DNSError
{Err
: "server misbehaving", Name
: name
, Server
: server
}
53 if dns
.rcode
== dnsRcodeServerFailure
{
54 err
.IsTemporary
= true
60 // Presotto says it's okay to assume that servers listed in
61 // /etc/resolv.conf are recursive resolvers.
62 // We asked for recursion, so it should have included
63 // all the answers we need in this one packet.
65 for cnameloop
:= 0; cnameloop
< 10; cnameloop
++ {
67 for _
, rr
:= range dns
.answer
{
68 if _
, justHeader
:= rr
.(*dnsRR_Header
); justHeader
{
69 // Corrupt record: we only have a
70 // header. That header might say it's
71 // of type qtype, but we don't
72 // actually have it. Skip.
76 if h
.Class
== dnsClassINET
&& equalASCIILabel(h
.Name
, name
) {
79 addrs
= append(addrs
, rr
)
82 name
= rr
.(*dnsRR_CNAME
).Cname
88 return "", nil, &DNSError
{Err
: errNoSuchHost
.Error(), Name
: name
, Server
: server
}
90 return name
, addrs
, nil
93 return "", nil, &DNSError
{Err
: "too many redirects", Name
: name
, Server
: server
}
96 func equalASCIILabel(x
, y
string) bool {
100 for i
:= 0; i
< len(x
); i
++ {
103 if 'A' <= a
&& a
<= 'Z' {
106 if 'A' <= b
&& b
<= 'Z' {
116 // isDomainName checks if a string is a presentation-format domain name
117 // (currently restricted to hostname-compatible "preferred name" LDH labels and
118 // SRV-like "underscore labels"; see golang.org/issue/12421).
119 func isDomainName(s
string) bool {
120 // See RFC 1035, RFC 3696.
121 // Presentation format has dots before every label except the first, and the
122 // terminal empty label is optional here because we assume fully-qualified
123 // (absolute) input. We must therefore reserve space for the first and last
124 // labels' length octets in wire format, where they are necessary and the
125 // maximum total length is 255.
126 // So our _effective_ maximum is 253, but 254 is not rejected if the last
127 // character is a dot.
129 if l
== 0 || l
> 254 || l
== 254 && s
[l
-1] != '.' {
134 ok
:= false // Ok once we've seen a letter.
136 for i
:= 0; i
< len(s
); i
++ {
141 case 'a' <= c
&& c
<= 'z' ||
'A' <= c
&& c
<= 'Z' || c
== '_':
144 case '0' <= c
&& c
<= '9':
148 // Byte before dash cannot be dot.
154 // Byte before dot cannot be dot, dash.
155 if last
== '.' || last
== '-' {
158 if partlen
> 63 || partlen
== 0 {
165 if last
== '-' || partlen
> 63 {
172 // absDomainName returns an absolute domain name which ends with a
173 // trailing dot to match pure Go reverse resolver and all other lookup
175 // See golang.org/issue/12189.
176 // But we don't want to add dots for local names from /etc/hosts.
177 // It's hard to tell so we settle on the heuristic that names without dots
178 // (like "localhost" or "myhost") do not get trailing dots, but any other
180 func absDomainName(b
[]byte) string {
182 for _
, x
:= range b
{
188 if hasDots
&& b
[len(b
)-1] != '.' {
194 // An SRV represents a single DNS SRV record.
202 // byPriorityWeight sorts SRV records by ascending priority and weight.
203 type byPriorityWeight
[]*SRV
205 func (s byPriorityWeight
) Len() int { return len(s
) }
206 func (s byPriorityWeight
) Less(i
, j
int) bool {
207 return s
[i
].Priority
< s
[j
].Priority ||
(s
[i
].Priority
== s
[j
].Priority
&& s
[i
].Weight
< s
[j
].Weight
)
209 func (s byPriorityWeight
) Swap(i
, j
int) { s
[i
], s
[j
] = s
[j
], s
[i
] }
211 // shuffleByWeight shuffles SRV records by weight using the algorithm
212 // described in RFC 2782.
213 func (addrs byPriorityWeight
) shuffleByWeight() {
215 for _
, addr
:= range addrs
{
216 sum
+= int(addr
.Weight
)
218 for sum
> 0 && len(addrs
) > 1 {
221 for i
:= range addrs
{
222 s
+= int(addrs
[i
].Weight
)
225 addrs
[0], addrs
[i
] = addrs
[i
], addrs
[0]
230 sum
-= int(addrs
[0].Weight
)
235 // sort reorders SRV records as specified in RFC 2782.
236 func (addrs byPriorityWeight
) sort() {
239 for j
:= 1; j
< len(addrs
); j
++ {
240 if addrs
[i
].Priority
!= addrs
[j
].Priority
{
241 addrs
[i
:j
].shuffleByWeight()
245 addrs
[i
:].shuffleByWeight()
248 // An MX represents a single DNS MX record.
254 // byPref implements sort.Interface to sort MX records by preference
257 func (s byPref
) Len() int { return len(s
) }
258 func (s byPref
) Less(i
, j
int) bool { return s
[i
].Pref
< s
[j
].Pref
}
259 func (s byPref
) Swap(i
, j
int) { s
[i
], s
[j
] = s
[j
], s
[i
] }
261 // sort reorders MX records as specified in RFC 5321.
262 func (s byPref
) sort() {
264 j
:= rand
.Intn(i
+ 1)
265 s
[i
], s
[j
] = s
[j
], s
[i
]
270 // An NS represents a single DNS NS record.