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.
5 // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
7 // DNS client: see RFC 1035.
8 // Has to be linked into package net for Dial.
11 // Could potentially handle many outstanding lookups faster.
12 // Could have a small cache.
13 // Random UDP source port (net.Dial should do that for us).
14 // Random request IDs.
28 // A dnsConn represents a DNS transport endpoint.
29 type dnsConn
interface {
32 SetDeadline(time
.Time
) error
34 // dnsRoundTrip executes a single DNS transaction, returning a
35 // DNS response message for the provided DNS query message.
36 dnsRoundTrip(query
*dnsMsg
) (*dnsMsg
, error
)
39 // dnsPacketConn implements the dnsConn interface for RFC 1035's
40 // "UDP usage" transport mechanism. Conn is a packet-oriented connection,
41 // such as a *UDPConn.
42 type dnsPacketConn
struct {
46 func (c
*dnsPacketConn
) dnsRoundTrip(query
*dnsMsg
) (*dnsMsg
, error
) {
49 return nil, errors
.New("cannot marshal DNS message")
51 if _
, err
:= c
.Write(b
); err
!= nil {
55 b
= make([]byte, 512) // see RFC 1035
62 if !resp
.Unpack(b
[:n
]) ||
!resp
.IsResponseTo(query
) {
63 // Ignore invalid responses as they may be malicious
64 // forgery attempts. Instead continue waiting until
65 // timeout. See golang.org/issue/13281.
72 // dnsStreamConn implements the dnsConn interface for RFC 1035's
73 // "TCP usage" transport mechanism. Conn is a stream-oriented connection,
74 // such as a *TCPConn.
75 type dnsStreamConn
struct {
79 func (c
*dnsStreamConn
) dnsRoundTrip(query
*dnsMsg
) (*dnsMsg
, error
) {
82 return nil, errors
.New("cannot marshal DNS message")
85 b
= append([]byte{byte(l
>> 8), byte(l
)}, b
...)
86 if _
, err
:= c
.Write(b
); err
!= nil {
90 b
= make([]byte, 1280) // 1280 is a reasonable initial size for IP over Ethernet, see RFC 4035
91 if _
, err
:= io
.ReadFull(c
, b
[:2]); err
!= nil {
94 l
= int(b
[0])<<8 |
int(b
[1])
98 n
, err
:= io
.ReadFull(c
, b
[:l
])
103 if !resp
.Unpack(b
[:n
]) {
104 return nil, errors
.New("cannot unmarshal DNS message")
106 if !resp
.IsResponseTo(query
) {
107 return nil, errors
.New("invalid DNS response")
112 // exchange sends a query on the connection and hopes for a response.
113 func (r
*Resolver
) exchange(ctx context
.Context
, server
, name
string, qtype
uint16, timeout time
.Duration
) (*dnsMsg
, error
) {
115 dnsMsgHdr
: dnsMsgHdr
{
116 recursion_desired
: true,
118 question
: []dnsQuestion
{
119 {name
, qtype
, dnsClassINET
},
122 for _
, network
:= range []string{"udp", "tcp"} {
123 // TODO(mdempsky): Refactor so defers from UDP-based
124 // exchanges happen before TCP-based exchange.
126 ctx
, cancel
:= context
.WithDeadline(ctx
, time
.Now().Add(timeout
))
129 c
, err
:= r
.dial(ctx
, network
, server
)
134 if d
, ok
:= ctx
.Deadline(); ok
&& !d
.IsZero() {
137 out
.id
= uint16(rand
.Int()) ^ uint16(time
.Now().UnixNano())
138 in
, err
:= c
.dnsRoundTrip(&out
)
140 return nil, mapErr(err
)
142 if in
.truncated
{ // see RFC 5966
147 return nil, errors
.New("no answer from DNS server")
150 // Do a lookup for a single name, which must be rooted
151 // (otherwise answer will not find the answers).
152 func (r
*Resolver
) tryOneName(ctx context
.Context
, cfg
*dnsConfig
, name
string, qtype
uint16) (string, []dnsRR
, error
) {
154 serverOffset
:= cfg
.serverOffset()
155 sLen
:= uint32(len(cfg
.servers
))
157 for i
:= 0; i
< cfg
.attempts
; i
++ {
158 for j
:= uint32(0); j
< sLen
; j
++ {
159 server
:= cfg
.servers
[(serverOffset
+j
)%sLen
]
161 msg
, err
:= r
.exchange(ctx
, server
, name
, qtype
, cfg
.timeout
)
168 if nerr
, ok
:= err
.(Error
); ok
&& nerr
.Timeout() {
169 lastErr
.(*DNSError
).IsTimeout
= true
171 // Set IsTemporary for socket-level errors. Note that this flag
172 // may also be used to indicate a SERVFAIL response.
173 if _
, ok
:= err
.(*OpError
); ok
{
174 lastErr
.(*DNSError
).IsTemporary
= true
178 // libresolv continues to the next server when it receives
179 // an invalid referral response. See golang.org/issue/15434.
180 if msg
.rcode
== dnsRcodeSuccess
&& !msg
.authoritative
&& !msg
.recursion_available
&& len(msg
.answer
) == 0 && len(msg
.extra
) == 0 {
181 lastErr
= &DNSError
{Err
: "lame referral", Name
: name
, Server
: server
}
184 cname
, rrs
, err
:= answer(name
, server
, msg
, qtype
)
185 // If answer errored for rcodes dnsRcodeSuccess or dnsRcodeNameError,
186 // it means the response in msg was not useful and trying another
187 // server probably won't help. Return now in those cases.
188 // TODO: indicate this in a more obvious way, such as a field on DNSError?
189 if err
== nil || msg
.rcode
== dnsRcodeSuccess || msg
.rcode
== dnsRcodeNameError
{
190 return cname
, rrs
, err
195 return "", nil, lastErr
198 // addrRecordList converts and returns a list of IP addresses from DNS
199 // address records (both A and AAAA). Other record types are ignored.
200 func addrRecordList(rrs
[]dnsRR
) []IPAddr
{
201 addrs
:= make([]IPAddr
, 0, 4)
202 for _
, rr
:= range rrs
{
203 switch rr
:= rr
.(type) {
205 addrs
= append(addrs
, IPAddr
{IP
: IPv4(byte(rr
.A
>>24), byte(rr
.A
>>16), byte(rr
.A
>>8), byte(rr
.A
))})
207 ip
:= make(IP
, IPv6len
)
209 addrs
= append(addrs
, IPAddr
{IP
: ip
})
215 // A resolverConfig represents a DNS stub resolver configuration.
216 type resolverConfig
struct {
217 initOnce sync
.Once
// guards init of resolverConfig
219 // ch is used as a semaphore that only allows one lookup at a
220 // time to recheck resolv.conf.
221 ch
chan struct{} // guards lastChecked and modTime
222 lastChecked time
.Time
// last time resolv.conf was checked
224 mu sync
.RWMutex
// protects dnsConfig
225 dnsConfig
*dnsConfig
// parsed resolv.conf structure used in lookups
228 var resolvConf resolverConfig
230 // init initializes conf and is only called via conf.initOnce.
231 func (conf
*resolverConfig
) init() {
232 // Set dnsConfig and lastChecked so we don't parse
233 // resolv.conf twice the first time.
234 conf
.dnsConfig
= systemConf().resolv
235 if conf
.dnsConfig
== nil {
236 conf
.dnsConfig
= dnsReadConfig("/etc/resolv.conf")
238 conf
.lastChecked
= time
.Now()
240 // Prepare ch so that only one update of resolverConfig may
242 conf
.ch
= make(chan struct{}, 1)
245 // tryUpdate tries to update conf with the named resolv.conf file.
246 // The name variable only exists for testing. It is otherwise always
247 // "/etc/resolv.conf".
248 func (conf
*resolverConfig
) tryUpdate(name
string) {
249 conf
.initOnce
.Do(conf
.init
)
251 // Ensure only one update at a time checks resolv.conf.
252 if !conf
.tryAcquireSema() {
255 defer conf
.releaseSema()
258 if conf
.lastChecked
.After(now
.Add(-5 * time
.Second
)) {
261 conf
.lastChecked
= now
264 if fi
, err
:= os
.Stat(name
); err
== nil {
267 if mtime
.Equal(conf
.dnsConfig
.mtime
) {
271 dnsConf
:= dnsReadConfig(name
)
273 conf
.dnsConfig
= dnsConf
277 func (conf
*resolverConfig
) tryAcquireSema() bool {
279 case conf
.ch
<- struct{}{}:
286 func (conf
*resolverConfig
) releaseSema() {
290 func (r
*Resolver
) lookup(ctx context
.Context
, name
string, qtype
uint16) (cname
string, rrs
[]dnsRR
, err error
) {
291 if !isDomainName(name
) {
292 // We used to use "invalid domain name" as the error,
293 // but that is a detail of the specific lookup mechanism.
294 // Other lookups might allow broader name syntax
295 // (for example Multicast DNS allows UTF-8; see RFC 6762).
296 // For consistency with libc resolvers, report no such host.
297 return "", nil, &DNSError
{Err
: errNoSuchHost
.Error(), Name
: name
}
299 resolvConf
.tryUpdate("/etc/resolv.conf")
300 resolvConf
.mu
.RLock()
301 conf
:= resolvConf
.dnsConfig
302 resolvConf
.mu
.RUnlock()
303 for _
, fqdn
:= range conf
.nameList(name
) {
304 cname
, rrs
, err
= r
.tryOneName(ctx
, conf
, fqdn
, qtype
)
308 if nerr
, ok
:= err
.(Error
); ok
&& nerr
.Temporary() && r
.StrictErrors
{
309 // If we hit a temporary error with StrictErrors enabled,
310 // stop immediately instead of trying more names.
314 if err
, ok
:= err
.(*DNSError
); ok
{
315 // Show original name passed to lookup, not suffixed one.
316 // In general we might have tried many suffixes; showing
317 // just one is misleading. See also golang.org/issue/6324.
323 // avoidDNS reports whether this is a hostname for which we should not
324 // use DNS. Currently this includes only .onion, per RFC 7686. See
325 // golang.org/issue/13705. Does not cover .local names (RFC 6762),
326 // see golang.org/issue/16739.
327 func avoidDNS(name
string) bool {
331 if name
[len(name
)-1] == '.' {
332 name
= name
[:len(name
)-1]
334 return stringsHasSuffixFold(name
, ".onion")
337 // nameList returns a list of names for sequential DNS queries.
338 func (conf
*dnsConfig
) nameList(name
string) []string {
343 // Check name length (see isDomainName).
345 rooted
:= l
> 0 && name
[l
-1] == '.'
346 if l
> 254 || l
== 254 && rooted
{
350 // If name is rooted (trailing dot), try only that name.
352 return []string{name
}
355 hasNdots
:= count(name
, '.') >= conf
.ndots
359 // Build list of search choices.
360 names
:= make([]string, 0, 1+len(conf
.search
))
361 // If name has enough dots, try unsuffixed first.
363 names
= append(names
, name
)
365 // Try suffixes that are not too long (see isDomainName).
366 for _
, suffix
:= range conf
.search
{
367 if l
+len(suffix
) <= 254 {
368 names
= append(names
, name
+suffix
)
371 // Try unsuffixed, if not tried first above.
373 names
= append(names
, name
)
378 // hostLookupOrder specifies the order of LookupHost lookup strategies.
379 // It is basically a simplified representation of nsswitch.conf.
380 // "files" means /etc/hosts.
381 type hostLookupOrder
int
384 // hostLookupCgo means defer to cgo.
385 hostLookupCgo hostLookupOrder
= iota
386 hostLookupFilesDNS
// files first
387 hostLookupDNSFiles
// dns first
388 hostLookupFiles
// only files
389 hostLookupDNS
// only DNS
392 var lookupOrderName
= map[hostLookupOrder
]string{
393 hostLookupCgo
: "cgo",
394 hostLookupFilesDNS
: "files,dns",
395 hostLookupDNSFiles
: "dns,files",
396 hostLookupFiles
: "files",
397 hostLookupDNS
: "dns",
400 func (o hostLookupOrder
) String() string {
401 if s
, ok
:= lookupOrderName
[o
]; ok
{
404 return "hostLookupOrder=" + itoa(int(o
)) + "??"
407 // goLookupHost is the native Go implementation of LookupHost.
408 // Used only if cgoLookupHost refuses to handle the request
409 // (that is, only if cgoLookupHost is the stub in cgo_stub.go).
410 // Normally we let cgo use the C library resolver instead of
411 // depending on our lookup code, so that Go and C get the same
413 func (r
*Resolver
) goLookupHost(ctx context
.Context
, name
string) (addrs
[]string, err error
) {
414 return r
.goLookupHostOrder(ctx
, name
, hostLookupFilesDNS
)
417 func (r
*Resolver
) goLookupHostOrder(ctx context
.Context
, name
string, order hostLookupOrder
) (addrs
[]string, err error
) {
418 if order
== hostLookupFilesDNS || order
== hostLookupFiles
{
419 // Use entries from /etc/hosts if they match.
420 addrs
= lookupStaticHost(name
)
421 if len(addrs
) > 0 || order
== hostLookupFiles
{
425 ips
, _
, err
:= r
.goLookupIPCNAMEOrder(ctx
, name
, order
)
429 addrs
= make([]string, 0, len(ips
))
430 for _
, ip
:= range ips
{
431 addrs
= append(addrs
, ip
.String())
436 // lookup entries from /etc/hosts
437 func goLookupIPFiles(name
string) (addrs
[]IPAddr
) {
438 for _
, haddr
:= range lookupStaticHost(name
) {
439 haddr
, zone
:= splitHostZone(haddr
)
440 if ip
:= ParseIP(haddr
); ip
!= nil {
441 addr
:= IPAddr
{IP
: ip
, Zone
: zone
}
442 addrs
= append(addrs
, addr
)
449 // goLookupIP is the native Go implementation of LookupIP.
450 // The libc versions are in cgo_*.go.
451 func (r
*Resolver
) goLookupIP(ctx context
.Context
, host
string) (addrs
[]IPAddr
, err error
) {
452 order
:= systemConf().hostLookupOrder(host
)
453 addrs
, _
, err
= r
.goLookupIPCNAMEOrder(ctx
, host
, order
)
457 func (r
*Resolver
) goLookupIPCNAMEOrder(ctx context
.Context
, name
string, order hostLookupOrder
) (addrs
[]IPAddr
, cname
string, err error
) {
458 if order
== hostLookupFilesDNS || order
== hostLookupFiles
{
459 addrs
= goLookupIPFiles(name
)
460 if len(addrs
) > 0 || order
== hostLookupFiles
{
461 return addrs
, name
, nil
464 if !isDomainName(name
) {
465 // See comment in func lookup above about use of errNoSuchHost.
466 return nil, "", &DNSError
{Err
: errNoSuchHost
.Error(), Name
: name
}
468 resolvConf
.tryUpdate("/etc/resolv.conf")
469 resolvConf
.mu
.RLock()
470 conf
:= resolvConf
.dnsConfig
471 resolvConf
.mu
.RUnlock()
477 lane
:= make(chan racer
, 1)
478 qtypes
:= [...]uint16{dnsTypeA
, dnsTypeAAAA
}
480 for _
, fqdn
:= range conf
.nameList(name
) {
481 for _
, qtype
:= range qtypes
{
482 go func(qtype
uint16) {
483 cname
, rrs
, err
:= r
.tryOneName(ctx
, conf
, fqdn
, qtype
)
484 lane
<- racer
{cname
, rrs
, err
}
487 hitStrictError
:= false
490 if racer
.error
!= nil {
491 if nerr
, ok
:= racer
.error
.(Error
); ok
&& nerr
.Temporary() && r
.StrictErrors
{
492 // This error will abort the nameList loop.
493 hitStrictError
= true
494 lastErr
= racer
.error
495 } else if lastErr
== nil || fqdn
== name
+"." {
496 // Prefer error for original name.
497 lastErr
= racer
.error
501 addrs
= append(addrs
, addrRecordList(racer
.rrs
)...)
507 // If either family hit an error with StrictErrors enabled,
508 // discard all addresses. This ensures that network flakiness
509 // cannot turn a dualstack hostname IPv4/IPv6-only.
517 if lastErr
, ok
:= lastErr
.(*DNSError
); ok
{
518 // Show original name passed to lookup, not suffixed one.
519 // In general we might have tried many suffixes; showing
520 // just one is misleading. See also golang.org/issue/6324.
525 if order
== hostLookupDNSFiles
{
526 addrs
= goLookupIPFiles(name
)
528 if len(addrs
) == 0 && lastErr
!= nil {
529 return nil, "", lastErr
532 return addrs
, cname
, nil
535 // goLookupCNAME is the native Go (non-cgo) implementation of LookupCNAME.
536 func (r
*Resolver
) goLookupCNAME(ctx context
.Context
, host
string) (cname
string, err error
) {
537 order
:= systemConf().hostLookupOrder(host
)
538 _
, cname
, err
= r
.goLookupIPCNAMEOrder(ctx
, host
, order
)
542 // goLookupPTR is the native Go implementation of LookupAddr.
543 // Used only if cgoLookupPTR refuses to handle the request (that is,
544 // only if cgoLookupPTR is the stub in cgo_stub.go).
545 // Normally we let cgo use the C library resolver instead of depending
546 // on our lookup code, so that Go and C get the same answers.
547 func (r
*Resolver
) goLookupPTR(ctx context
.Context
, addr
string) ([]string, error
) {
548 names
:= lookupStaticAddr(addr
)
552 arpa
, err
:= reverseaddr(addr
)
556 _
, rrs
, err
:= r
.lookup(ctx
, arpa
, dnsTypePTR
)
560 ptrs
:= make([]string, len(rrs
))
561 for i
, rr
:= range rrs
{
562 ptrs
[i
] = rr
.(*dnsRR_PTR
).Ptr