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 // DNS client: see RFC 1035.
6 // Has to be linked into package net for Dial.
9 // Check periodically whether /etc/resolv.conf has changed.
10 // Could potentially handle many outstanding lookups faster.
11 // Could have a small cache.
12 // Random UDP source port (net.Dial should do that for us).
13 // Random request IDs.
24 // DNSError represents a DNS lookup error.
25 type DNSError
struct {
26 Error
string // description of the error
27 Name
string // name looked for
28 Server
string // server used
32 func (e
*DNSError
) String() string {
36 s
:= "lookup " + e
.Name
38 s
+= " on " + e
.Server
44 func (e
*DNSError
) Timeout() bool { return e
.IsTimeout
}
45 func (e
*DNSError
) Temporary() bool { return e
.IsTimeout
}
47 const noSuchHost
= "no such host"
49 // Send a request on the connection and hope for a reply.
50 // Up to cfg.attempts attempts.
51 func exchange(cfg
*dnsConfig
, c Conn
, name
string, qtype
uint16) (*dnsMsg
, os
.Error
) {
53 return nil, &DNSError
{Error
: "name too long", Name
: name
}
56 out
.id
= uint16(rand
.Int()) ^ uint16(time
.Nanoseconds())
57 out
.question
= []dnsQuestion
{
58 {name
, qtype
, dnsClassINET
},
60 out
.recursion_desired
= true
63 return nil, &DNSError
{Error
: "internal error - cannot pack message", Name
: name
}
66 for attempt
:= 0; attempt
< cfg
.attempts
; attempt
++ {
67 n
, err
:= c
.Write(msg
)
72 c
.SetReadTimeout(int64(cfg
.timeout
) * 1e9
) // nanoseconds
74 buf
:= make([]byte, 2000) // More than enough.
77 if e
, ok
:= err
.(Error
); ok
&& e
.Timeout() {
84 if !in
.Unpack(buf
) || in
.id
!= out
.id
{
90 if a
:= c
.RemoteAddr(); a
!= nil {
93 return nil, &DNSError
{Error
: "no answer from server", Name
: name
, Server
: server
, IsTimeout
: true}
97 // Find answer for name in dns message.
98 // On return, if err == nil, addrs != nil.
99 func answer(name
, server
string, dns
*dnsMsg
, qtype
uint16) (addrs
[]dnsRR
, err os
.Error
) {
100 addrs
= make([]dnsRR
, 0, len(dns
.answer
))
102 if dns
.rcode
== dnsRcodeNameError
&& dns
.recursion_available
{
103 return nil, &DNSError
{Error
: noSuchHost
, Name
: name
}
105 if dns
.rcode
!= dnsRcodeSuccess
{
106 // None of the error codes make sense
107 // for the query we sent. If we didn't get
108 // a name error and we didn't get success,
109 // the server is behaving incorrectly.
110 return nil, &DNSError
{Error
: "server misbehaving", Name
: name
, Server
: server
}
113 // Look for the name.
114 // Presotto says it's okay to assume that servers listed in
115 // /etc/resolv.conf are recursive resolvers.
116 // We asked for recursion, so it should have included
117 // all the answers we need in this one packet.
119 for cnameloop
:= 0; cnameloop
< 10; cnameloop
++ {
121 for i
:= 0; i
< len(dns
.answer
); i
++ {
124 if h
.Class
== dnsClassINET
&& h
.Name
== name
{
128 addrs
= addrs
[0 : n
+1]
132 name
= rr
.(*dnsRR_CNAME
).Cname
138 return nil, &DNSError
{Error
: noSuchHost
, Name
: name
, Server
: server
}
143 return nil, &DNSError
{Error
: "too many redirects", Name
: name
, Server
: server
}
146 // Do a lookup for a single name, which must be rooted
147 // (otherwise answer will not find the answers).
148 func tryOneName(cfg
*dnsConfig
, name
string, qtype
uint16) (addrs
[]dnsRR
, err os
.Error
) {
149 if len(cfg
.servers
) == 0 {
150 return nil, &DNSError
{Error
: "no DNS servers", Name
: name
}
152 for i
:= 0; i
< len(cfg
.servers
); i
++ {
153 // Calling Dial here is scary -- we have to be sure
154 // not to dial a name that will require a DNS lookup,
155 // or Dial will call back here to translate it.
156 // The DNS config parser has already checked that
157 // all the cfg.servers[i] are IP addresses, which
158 // Dial will use without a DNS lookup.
159 server
:= cfg
.servers
[i
] + ":53"
160 c
, cerr
:= Dial("udp", "", server
)
165 msg
, merr
:= exchange(cfg
, c
, name
, qtype
)
171 addrs
, err
= answer(name
, server
, msg
, qtype
)
172 if err
== nil || err
.(*DNSError
).Error
== noSuchHost
{
179 func convertRR_A(records
[]dnsRR
) []string {
180 addrs
:= make([]string, len(records
))
181 for i
:= 0; i
< len(records
); i
++ {
184 addrs
[i
] = IPv4(byte(a
>>24), byte(a
>>16), byte(a
>>8), byte(a
)).String()
192 func loadConfig() { cfg
, dnserr
= dnsReadConfig() }
194 func isDomainName(s
string) bool {
195 // See RFC 1035, RFC 3696.
202 if s
[len(s
)-1] != '.' { // simplify checking loop: make name end in dot
207 ok
:= false // ok once we've seen a letter
209 for i
:= 0; i
< len(s
); i
++ {
214 case 'a' <= c
&& c
<= 'z' ||
'A' <= c
&& c
<= 'Z' || c
== '_':
217 case '0' <= c
&& c
<= '9':
221 // byte before dash cannot be dot
227 // byte before dot cannot be dot, dash
228 if last
== '.' || last
== '-' {
231 if partlen
> 63 || partlen
== 0 {
242 var onceLoadConfig sync
.Once
244 func lookup(name
string, qtype
uint16) (cname
string, addrs
[]dnsRR
, err os
.Error
) {
245 if !isDomainName(name
) {
246 return name
, nil, &DNSError
{Error
: "invalid domain name", Name
: name
}
248 onceLoadConfig
.Do(loadConfig
)
249 if dnserr
!= nil || cfg
== nil {
253 // If name is rooted (trailing dot) or has enough dots,
254 // try it by itself first.
255 rooted
:= len(name
) > 0 && name
[len(name
)-1] == '.'
256 if rooted ||
count(name
, '.') >= cfg
.ndots
{
261 // Can try as ordinary name.
262 addrs
, err
= tryOneName(cfg
, rname
, qtype
)
272 // Otherwise, try suffixes.
273 for i
:= 0; i
< len(cfg
.search
); i
++ {
274 rname
:= name
+ "." + cfg
.search
[i
]
275 if rname
[len(rname
)-1] != '.' {
278 addrs
, err
= tryOneName(cfg
, rname
, qtype
)
285 // Last ditch effort: try unsuffixed.
290 addrs
, err
= tryOneName(cfg
, rname
, qtype
)
298 // LookupHost looks for name using the local hosts file and DNS resolver.
299 // It returns the canonical name for the host and an array of that
301 func LookupHost(name
string) (cname
string, addrs
[]string, err os
.Error
) {
302 onceLoadConfig
.Do(loadConfig
)
303 if dnserr
!= nil || cfg
== nil {
307 // Use entries from /etc/hosts if they match.
308 addrs
= lookupStaticHost(name
)
314 cname
, records
, err
= lookup(name
, dnsTypeA
)
318 addrs
= convertRR_A(records
)
329 // LookupSRV tries to resolve an SRV query of the given service,
330 // protocol, and domain name, as specified in RFC 2782. In most cases
331 // the proto argument can be the same as the corresponding
333 func LookupSRV(service
, proto
, name
string) (cname
string, addrs
[]*SRV
, err os
.Error
) {
334 target
:= "_" + service
+ "._" + proto
+ "." + name
336 cname
, records
, err
= lookup(target
, dnsTypeSRV
)
340 addrs
= make([]*SRV
, len(records
))
341 for i
:= 0; i
< len(records
); i
++ {
342 r
:= records
[i
].(*dnsRR_SRV
)
343 addrs
[i
] = &SRV
{r
.Target
, r
.Port
, r
.Priority
, r
.Weight
}
353 func LookupMX(name
string) (entries
[]*MX
, err os
.Error
) {
355 _
, records
, err
= lookup(name
, dnsTypeMX
)
359 entries
= make([]*MX
, len(records
))
360 for i
:= 0; i
< len(records
); i
++ {
361 r
:= records
[i
].(*dnsRR_MX
)
362 entries
[i
] = &MX
{r
.Mx
, r
.Pref
}