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.
27 "golang_org/x/net/dns/dnsmessage"
30 func newRequest(q dnsmessage
.Question
) (id
uint16, udpReq
, tcpReq
[]byte, err error
) {
31 id
= uint16(rand
.Int()) ^ uint16(time
.Now().UnixNano())
32 b
:= dnsmessage
.NewBuilder(make([]byte, 2, 514), dnsmessage
.Header
{ID
: id
, RecursionDesired
: true})
34 if err
:= b
.StartQuestions(); err
!= nil {
35 return 0, nil, nil, err
37 if err
:= b
.Question(q
); err
!= nil {
38 return 0, nil, nil, err
40 tcpReq
, err
= b
.Finish()
43 tcpReq
[0] = byte(l
>> 8)
45 return id
, udpReq
, tcpReq
, err
48 func checkResponse(reqID
uint16, reqQues dnsmessage
.Question
, respHdr dnsmessage
.Header
, respQues dnsmessage
.Question
) bool {
49 if !respHdr
.Response
{
52 if reqID
!= respHdr
.ID
{
55 if reqQues
.Type
!= respQues
.Type || reqQues
.Class
!= respQues
.Class ||
!equalASCIIName(reqQues
.Name
, respQues
.Name
) {
61 func dnsPacketRoundTrip(c Conn
, id
uint16, query dnsmessage
.Question
, b
[]byte) (dnsmessage
.Parser
, dnsmessage
.Header
, error
) {
62 if _
, err
:= c
.Write(b
); err
!= nil {
63 return dnsmessage
.Parser
{}, dnsmessage
.Header
{}, err
66 b
= make([]byte, 512) // see RFC 1035
70 return dnsmessage
.Parser
{}, dnsmessage
.Header
{}, err
72 var p dnsmessage
.Parser
73 // Ignore invalid responses as they may be malicious
74 // forgery attempts. Instead continue waiting until
75 // timeout. See golang.org/issue/13281.
76 h
, err
:= p
.Start(b
[:n
])
80 q
, err
:= p
.Question()
81 if err
!= nil ||
!checkResponse(id
, query
, h
, q
) {
88 func dnsStreamRoundTrip(c Conn
, id
uint16, query dnsmessage
.Question
, b
[]byte) (dnsmessage
.Parser
, dnsmessage
.Header
, error
) {
89 if _
, err
:= c
.Write(b
); err
!= nil {
90 return dnsmessage
.Parser
{}, dnsmessage
.Header
{}, err
93 b
= make([]byte, 1280) // 1280 is a reasonable initial size for IP over Ethernet, see RFC 4035
94 if _
, err
:= io
.ReadFull(c
, b
[:2]); err
!= nil {
95 return dnsmessage
.Parser
{}, dnsmessage
.Header
{}, err
97 l
:= int(b
[0])<<8 |
int(b
[1])
101 n
, err
:= io
.ReadFull(c
, b
[:l
])
103 return dnsmessage
.Parser
{}, dnsmessage
.Header
{}, err
105 var p dnsmessage
.Parser
106 h
, err
:= p
.Start(b
[:n
])
108 return dnsmessage
.Parser
{}, dnsmessage
.Header
{}, errors
.New("cannot unmarshal DNS message")
110 q
, err
:= p
.Question()
112 return dnsmessage
.Parser
{}, dnsmessage
.Header
{}, errors
.New("cannot unmarshal DNS message")
114 if !checkResponse(id
, query
, h
, q
) {
115 return dnsmessage
.Parser
{}, dnsmessage
.Header
{}, errors
.New("invalid DNS response")
120 // exchange sends a query on the connection and hopes for a response.
121 func (r
*Resolver
) exchange(ctx context
.Context
, server
string, q dnsmessage
.Question
, timeout time
.Duration
) (dnsmessage
.Parser
, dnsmessage
.Header
, error
) {
122 q
.Class
= dnsmessage
.ClassINET
123 id
, udpReq
, tcpReq
, err
:= newRequest(q
)
125 return dnsmessage
.Parser
{}, dnsmessage
.Header
{}, errors
.New("cannot marshal DNS message")
127 for _
, network
:= range []string{"udp", "tcp"} {
128 ctx
, cancel
:= context
.WithDeadline(ctx
, time
.Now().Add(timeout
))
131 c
, err
:= r
.dial(ctx
, network
, server
)
133 return dnsmessage
.Parser
{}, dnsmessage
.Header
{}, err
135 if d
, ok
:= ctx
.Deadline(); ok
&& !d
.IsZero() {
138 var p dnsmessage
.Parser
139 var h dnsmessage
.Header
140 if _
, ok
:= c
.(PacketConn
); ok
{
141 p
, h
, err
= dnsPacketRoundTrip(c
, id
, q
, udpReq
)
143 p
, h
, err
= dnsStreamRoundTrip(c
, id
, q
, tcpReq
)
147 return dnsmessage
.Parser
{}, dnsmessage
.Header
{}, mapErr(err
)
149 if err
:= p
.SkipQuestion(); err
!= dnsmessage
.ErrSectionDone
{
150 return dnsmessage
.Parser
{}, dnsmessage
.Header
{}, errors
.New("invalid DNS response")
152 if h
.Truncated
{ // see RFC 5966
157 return dnsmessage
.Parser
{}, dnsmessage
.Header
{}, errors
.New("no answer from DNS server")
160 // checkHeader performs basic sanity checks on the header.
161 func checkHeader(p
*dnsmessage
.Parser
, h dnsmessage
.Header
, name
, server
string) error
{
162 _
, err
:= p
.AnswerHeader()
163 if err
!= nil && err
!= dnsmessage
.ErrSectionDone
{
165 Err
: "cannot unmarshal DNS message",
171 // libresolv continues to the next server when it receives
172 // an invalid referral response. See golang.org/issue/15434.
173 if h
.RCode
== dnsmessage
.RCodeSuccess
&& !h
.Authoritative
&& !h
.RecursionAvailable
&& err
== dnsmessage
.ErrSectionDone
{
174 return &DNSError
{Err
: "lame referral", Name
: name
, Server
: server
}
177 if h
.RCode
!= dnsmessage
.RCodeSuccess
&& h
.RCode
!= dnsmessage
.RCodeNameError
{
178 // None of the error codes make sense
179 // for the query we sent. If we didn't get
180 // a name error and we didn't get success,
181 // the server is behaving incorrectly or
182 // having temporary trouble.
183 err
:= &DNSError
{Err
: "server misbehaving", Name
: name
, Server
: server
}
184 if h
.RCode
== dnsmessage
.RCodeServerFailure
{
185 err
.IsTemporary
= true
193 func skipToAnswer(p
*dnsmessage
.Parser
, qtype dnsmessage
.Type
, name
, server
string) error
{
195 h
, err
:= p
.AnswerHeader()
196 if err
== dnsmessage
.ErrSectionDone
{
198 Err
: errNoSuchHost
.Error(),
205 Err
: "cannot unmarshal DNS message",
213 if err
:= p
.SkipAnswer(); err
!= nil {
215 Err
: "cannot unmarshal DNS message",
223 // Do a lookup for a single name, which must be rooted
224 // (otherwise answer will not find the answers).
225 func (r
*Resolver
) tryOneName(ctx context
.Context
, cfg
*dnsConfig
, name
string, qtype dnsmessage
.Type
) (dnsmessage
.Parser
, string, error
) {
227 serverOffset
:= cfg
.serverOffset()
228 sLen
:= uint32(len(cfg
.servers
))
230 n
, err
:= dnsmessage
.NewName(name
)
232 return dnsmessage
.Parser
{}, "", errors
.New("cannot marshal DNS message")
234 q
:= dnsmessage
.Question
{
237 Class
: dnsmessage
.ClassINET
,
240 for i
:= 0; i
< cfg
.attempts
; i
++ {
241 for j
:= uint32(0); j
< sLen
; j
++ {
242 server
:= cfg
.servers
[(serverOffset
+j
)%sLen
]
244 p
, h
, err
:= r
.exchange(ctx
, server
, q
, cfg
.timeout
)
251 if nerr
, ok
:= err
.(Error
); ok
&& nerr
.Timeout() {
252 lastErr
.(*DNSError
).IsTimeout
= true
254 // Set IsTemporary for socket-level errors. Note that this flag
255 // may also be used to indicate a SERVFAIL response.
256 if _
, ok
:= err
.(*OpError
); ok
{
257 lastErr
.(*DNSError
).IsTemporary
= true
262 // The name does not exist, so trying another server won't help.
264 // TODO: indicate this in a more obvious way, such as a field on DNSError?
265 if h
.RCode
== dnsmessage
.RCodeNameError
{
266 return dnsmessage
.Parser
{}, "", &DNSError
{Err
: errNoSuchHost
.Error(), Name
: name
, Server
: server
}
269 lastErr
= checkHeader(&p
, h
, name
, server
)
274 lastErr
= skipToAnswer(&p
, qtype
, name
, server
)
276 return p
, server
, nil
280 return dnsmessage
.Parser
{}, "", lastErr
283 // A resolverConfig represents a DNS stub resolver configuration.
284 type resolverConfig
struct {
285 initOnce sync
.Once
// guards init of resolverConfig
287 // ch is used as a semaphore that only allows one lookup at a
288 // time to recheck resolv.conf.
289 ch
chan struct{} // guards lastChecked and modTime
290 lastChecked time
.Time
// last time resolv.conf was checked
292 mu sync
.RWMutex
// protects dnsConfig
293 dnsConfig
*dnsConfig
// parsed resolv.conf structure used in lookups
296 var resolvConf resolverConfig
298 // init initializes conf and is only called via conf.initOnce.
299 func (conf
*resolverConfig
) init() {
300 // Set dnsConfig and lastChecked so we don't parse
301 // resolv.conf twice the first time.
302 conf
.dnsConfig
= systemConf().resolv
303 if conf
.dnsConfig
== nil {
304 conf
.dnsConfig
= dnsReadConfig("/etc/resolv.conf")
306 conf
.lastChecked
= time
.Now()
308 // Prepare ch so that only one update of resolverConfig may
310 conf
.ch
= make(chan struct{}, 1)
313 // tryUpdate tries to update conf with the named resolv.conf file.
314 // The name variable only exists for testing. It is otherwise always
315 // "/etc/resolv.conf".
316 func (conf
*resolverConfig
) tryUpdate(name
string) {
317 conf
.initOnce
.Do(conf
.init
)
319 // Ensure only one update at a time checks resolv.conf.
320 if !conf
.tryAcquireSema() {
323 defer conf
.releaseSema()
326 if conf
.lastChecked
.After(now
.Add(-5 * time
.Second
)) {
329 conf
.lastChecked
= now
332 if fi
, err
:= os
.Stat(name
); err
== nil {
335 if mtime
.Equal(conf
.dnsConfig
.mtime
) {
339 dnsConf
:= dnsReadConfig(name
)
341 conf
.dnsConfig
= dnsConf
345 func (conf
*resolverConfig
) tryAcquireSema() bool {
347 case conf
.ch
<- struct{}{}:
354 func (conf
*resolverConfig
) releaseSema() {
358 func (r
*Resolver
) lookup(ctx context
.Context
, name
string, qtype dnsmessage
.Type
) (dnsmessage
.Parser
, string, error
) {
359 if !isDomainName(name
) {
360 // We used to use "invalid domain name" as the error,
361 // but that is a detail of the specific lookup mechanism.
362 // Other lookups might allow broader name syntax
363 // (for example Multicast DNS allows UTF-8; see RFC 6762).
364 // For consistency with libc resolvers, report no such host.
365 return dnsmessage
.Parser
{}, "", &DNSError
{Err
: errNoSuchHost
.Error(), Name
: name
}
367 resolvConf
.tryUpdate("/etc/resolv.conf")
368 resolvConf
.mu
.RLock()
369 conf
:= resolvConf
.dnsConfig
370 resolvConf
.mu
.RUnlock()
376 for _
, fqdn
:= range conf
.nameList(name
) {
377 p
, server
, err
= r
.tryOneName(ctx
, conf
, fqdn
, qtype
)
381 if nerr
, ok
:= err
.(Error
); ok
&& nerr
.Temporary() && r
.strictErrors() {
382 // If we hit a temporary error with StrictErrors enabled,
383 // stop immediately instead of trying more names.
388 return p
, server
, nil
390 if err
, ok
:= err
.(*DNSError
); ok
{
391 // Show original name passed to lookup, not suffixed one.
392 // In general we might have tried many suffixes; showing
393 // just one is misleading. See also golang.org/issue/6324.
396 return dnsmessage
.Parser
{}, "", err
399 // avoidDNS reports whether this is a hostname for which we should not
400 // use DNS. Currently this includes only .onion, per RFC 7686. See
401 // golang.org/issue/13705. Does not cover .local names (RFC 6762),
402 // see golang.org/issue/16739.
403 func avoidDNS(name
string) bool {
407 if name
[len(name
)-1] == '.' {
408 name
= name
[:len(name
)-1]
410 return stringsHasSuffixFold(name
, ".onion")
413 // nameList returns a list of names for sequential DNS queries.
414 func (conf
*dnsConfig
) nameList(name
string) []string {
419 // Check name length (see isDomainName).
421 rooted
:= l
> 0 && name
[l
-1] == '.'
422 if l
> 254 || l
== 254 && rooted
{
426 // If name is rooted (trailing dot), try only that name.
428 return []string{name
}
431 hasNdots
:= count(name
, '.') >= conf
.ndots
435 // Build list of search choices.
436 names
:= make([]string, 0, 1+len(conf
.search
))
437 // If name has enough dots, try unsuffixed first.
439 names
= append(names
, name
)
441 // Try suffixes that are not too long (see isDomainName).
442 for _
, suffix
:= range conf
.search
{
443 if l
+len(suffix
) <= 254 {
444 names
= append(names
, name
+suffix
)
447 // Try unsuffixed, if not tried first above.
449 names
= append(names
, name
)
454 // hostLookupOrder specifies the order of LookupHost lookup strategies.
455 // It is basically a simplified representation of nsswitch.conf.
456 // "files" means /etc/hosts.
457 type hostLookupOrder
int
460 // hostLookupCgo means defer to cgo.
461 hostLookupCgo hostLookupOrder
= iota
462 hostLookupFilesDNS
// files first
463 hostLookupDNSFiles
// dns first
464 hostLookupFiles
// only files
465 hostLookupDNS
// only DNS
468 var lookupOrderName
= map[hostLookupOrder
]string{
469 hostLookupCgo
: "cgo",
470 hostLookupFilesDNS
: "files,dns",
471 hostLookupDNSFiles
: "dns,files",
472 hostLookupFiles
: "files",
473 hostLookupDNS
: "dns",
476 func (o hostLookupOrder
) String() string {
477 if s
, ok
:= lookupOrderName
[o
]; ok
{
480 return "hostLookupOrder=" + itoa(int(o
)) + "??"
483 // goLookupHost is the native Go implementation of LookupHost.
484 // Used only if cgoLookupHost refuses to handle the request
485 // (that is, only if cgoLookupHost is the stub in cgo_stub.go).
486 // Normally we let cgo use the C library resolver instead of
487 // depending on our lookup code, so that Go and C get the same
489 func (r
*Resolver
) goLookupHost(ctx context
.Context
, name
string) (addrs
[]string, err error
) {
490 return r
.goLookupHostOrder(ctx
, name
, hostLookupFilesDNS
)
493 func (r
*Resolver
) goLookupHostOrder(ctx context
.Context
, name
string, order hostLookupOrder
) (addrs
[]string, err error
) {
494 if order
== hostLookupFilesDNS || order
== hostLookupFiles
{
495 // Use entries from /etc/hosts if they match.
496 addrs
= lookupStaticHost(name
)
497 if len(addrs
) > 0 || order
== hostLookupFiles
{
501 ips
, _
, err
:= r
.goLookupIPCNAMEOrder(ctx
, name
, order
)
505 addrs
= make([]string, 0, len(ips
))
506 for _
, ip
:= range ips
{
507 addrs
= append(addrs
, ip
.String())
512 // lookup entries from /etc/hosts
513 func goLookupIPFiles(name
string) (addrs
[]IPAddr
) {
514 for _
, haddr
:= range lookupStaticHost(name
) {
515 haddr
, zone
:= splitHostZone(haddr
)
516 if ip
:= ParseIP(haddr
); ip
!= nil {
517 addr
:= IPAddr
{IP
: ip
, Zone
: zone
}
518 addrs
= append(addrs
, addr
)
525 // goLookupIP is the native Go implementation of LookupIP.
526 // The libc versions are in cgo_*.go.
527 func (r
*Resolver
) goLookupIP(ctx context
.Context
, host
string) (addrs
[]IPAddr
, err error
) {
528 order
:= systemConf().hostLookupOrder(r
, host
)
529 addrs
, _
, err
= r
.goLookupIPCNAMEOrder(ctx
, host
, order
)
533 func (r
*Resolver
) goLookupIPCNAMEOrder(ctx context
.Context
, name
string, order hostLookupOrder
) (addrs
[]IPAddr
, cname dnsmessage
.Name
, err error
) {
534 if order
== hostLookupFilesDNS || order
== hostLookupFiles
{
535 addrs
= goLookupIPFiles(name
)
536 if len(addrs
) > 0 || order
== hostLookupFiles
{
537 return addrs
, dnsmessage
.Name
{}, nil
540 if !isDomainName(name
) {
541 // See comment in func lookup above about use of errNoSuchHost.
542 return nil, dnsmessage
.Name
{}, &DNSError
{Err
: errNoSuchHost
.Error(), Name
: name
}
544 resolvConf
.tryUpdate("/etc/resolv.conf")
545 resolvConf
.mu
.RLock()
546 conf
:= resolvConf
.dnsConfig
547 resolvConf
.mu
.RUnlock()
553 lane
:= make(chan racer
, 1)
554 qtypes
:= [...]dnsmessage
.Type
{dnsmessage
.TypeA
, dnsmessage
.TypeAAAA
}
556 for _
, fqdn
:= range conf
.nameList(name
) {
557 for _
, qtype
:= range qtypes
{
559 go func(qtype dnsmessage
.Type
) {
560 p
, server
, err
:= r
.tryOneName(ctx
, conf
, fqdn
, qtype
)
561 lane
<- racer
{p
, server
, err
}
565 hitStrictError
:= false
568 if racer
.error
!= nil {
569 if nerr
, ok
:= racer
.error
.(Error
); ok
&& nerr
.Temporary() && r
.strictErrors() {
570 // This error will abort the nameList loop.
571 hitStrictError
= true
572 lastErr
= racer
.error
573 } else if lastErr
== nil || fqdn
== name
+"." {
574 // Prefer error for original name.
575 lastErr
= racer
.error
580 // Presotto says it's okay to assume that servers listed in
581 // /etc/resolv.conf are recursive resolvers.
583 // We asked for recursion, so it should have included all the
584 // answers we need in this one packet.
586 // Further, RFC 1035 section 4.3.1 says that "the recursive
587 // response to a query will be... The answer to the query,
588 // possibly preface by one or more CNAME RRs that specify
589 // aliases encountered on the way to an answer."
591 // Therefore, we should be able to assume that we can ignore
592 // CNAMEs and that the A and AAAA records we requested are
593 // for the canonical name.
597 h
, err
:= racer
.p
.AnswerHeader()
598 if err
!= nil && err
!= dnsmessage
.ErrSectionDone
{
600 Err
: "cannot marshal DNS message",
602 Server
: racer
.server
,
609 case dnsmessage
.TypeA
:
610 a
, err
:= racer
.p
.AResource()
613 Err
: "cannot marshal DNS message",
615 Server
: racer
.server
,
619 addrs
= append(addrs
, IPAddr
{IP
: IP(a
.A
[:])})
621 case dnsmessage
.TypeAAAA
:
622 aaaa
, err
:= racer
.p
.AAAAResource()
625 Err
: "cannot marshal DNS message",
627 Server
: racer
.server
,
631 addrs
= append(addrs
, IPAddr
{IP
: IP(aaaa
.AAAA
[:])})
634 if err
:= racer
.p
.SkipAnswer(); err
!= nil {
636 Err
: "cannot marshal DNS message",
638 Server
: racer
.server
,
644 if cname
.Length
== 0 && h
.Name
.Length
!= 0 {
650 // If either family hit an error with StrictErrors enabled,
651 // discard all addresses. This ensures that network flakiness
652 // cannot turn a dualstack hostname IPv4/IPv6-only.
660 if lastErr
, ok
:= lastErr
.(*DNSError
); ok
{
661 // Show original name passed to lookup, not suffixed one.
662 // In general we might have tried many suffixes; showing
663 // just one is misleading. See also golang.org/issue/6324.
668 if order
== hostLookupDNSFiles
{
669 addrs
= goLookupIPFiles(name
)
671 if len(addrs
) == 0 && lastErr
!= nil {
672 return nil, dnsmessage
.Name
{}, lastErr
675 return addrs
, cname
, nil
678 // goLookupCNAME is the native Go (non-cgo) implementation of LookupCNAME.
679 func (r
*Resolver
) goLookupCNAME(ctx context
.Context
, host
string) (string, error
) {
680 order
:= systemConf().hostLookupOrder(r
, host
)
681 _
, cname
, err
:= r
.goLookupIPCNAMEOrder(ctx
, host
, order
)
682 return cname
.String(), err
685 // goLookupPTR is the native Go implementation of LookupAddr.
686 // Used only if cgoLookupPTR refuses to handle the request (that is,
687 // only if cgoLookupPTR is the stub in cgo_stub.go).
688 // Normally we let cgo use the C library resolver instead of depending
689 // on our lookup code, so that Go and C get the same answers.
690 func (r
*Resolver
) goLookupPTR(ctx context
.Context
, addr
string) ([]string, error
) {
691 names
:= lookupStaticAddr(addr
)
695 arpa
, err
:= reverseaddr(addr
)
699 p
, server
, err
:= r
.lookup(ctx
, arpa
, dnsmessage
.TypePTR
)
705 h
, err
:= p
.AnswerHeader()
706 if err
== dnsmessage
.ErrSectionDone
{
710 return nil, &DNSError
{
711 Err
: "cannot marshal DNS message",
716 if h
.Type
!= dnsmessage
.TypePTR
{
719 ptr
, err
:= p
.PTRResource()
721 return nil, &DNSError
{
722 Err
: "cannot marshal DNS message",
727 ptrs
= append(ptrs
, ptr
.PTR
.String())