blob: 18bd72c3f16c5e98885689ed07863bb705aa9b4b [file] [log] [blame]
Russ Cox83348f92008-12-18 15:42:39 -08001// 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
Russ Coxa0bcaf42009-06-25 20:24:55 -07005// DNS client: see RFC 1035.
Russ Cox83348f92008-12-18 15:42:39 -08006// Has to be linked into package net for Dial.
7
8// TODO(rsc):
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.
Russ Cox83348f92008-12-18 15:42:39 -080014
15package net
16
17import (
Robert Griesemera3d10452009-12-15 15:35:38 -080018 "os"
Russ Coxc312d0e2010-02-10 16:35:35 -080019 "rand"
Rob Pikec78be462010-08-06 06:14:41 +100020 "sync"
Russ Coxc312d0e2010-02-10 16:35:35 -080021 "time"
Russ Cox83348f92008-12-18 15:42:39 -080022)
23
Russ Coxa0bcaf42009-06-25 20:24:55 -070024// DNSError represents a DNS lookup error.
Russ Cox1b301ba2009-05-08 14:40:20 -070025type DNSError struct {
Russ Cox47a05332010-04-26 22:15:25 -070026 Error string // description of the error
27 Name string // name looked for
28 Server string // server used
29 IsTimeout bool
Russ Cox1b301ba2009-05-08 14:40:20 -070030}
Russ Coxa0bcaf42009-06-25 20:24:55 -070031
32func (e *DNSError) String() string {
Andrew Gerrandfc4ba152010-07-27 17:22:22 +100033 if e == nil {
34 return "<nil>"
35 }
Robert Griesemera3d10452009-12-15 15:35:38 -080036 s := "lookup " + e.Name
Russ Coxa0bcaf42009-06-25 20:24:55 -070037 if e.Server != "" {
Robert Griesemer40621d52009-11-09 12:07:39 -080038 s += " on " + e.Server
Russ Coxa0bcaf42009-06-25 20:24:55 -070039 }
Robert Griesemera3d10452009-12-15 15:35:38 -080040 s += ": " + e.Error
41 return s
Russ Coxa0bcaf42009-06-25 20:24:55 -070042}
43
Russ Cox47a05332010-04-26 22:15:25 -070044func (e *DNSError) Timeout() bool { return e.IsTimeout }
45func (e *DNSError) Temporary() bool { return e.IsTimeout }
46
Russ Coxa0bcaf42009-06-25 20:24:55 -070047const noSuchHost = "no such host"
Russ Cox83348f92008-12-18 15:42:39 -080048
49// Send a request on the connection and hope for a reply.
50// Up to cfg.attempts attempts.
Kirklin McDonald0046d512010-06-30 10:54:24 +100051func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, os.Error) {
Russ Cox83348f92008-12-18 15:42:39 -080052 if len(name) >= 256 {
Russ Cox47a05332010-04-26 22:15:25 -070053 return nil, &DNSError{Error: "name too long", Name: name}
Russ Cox83348f92008-12-18 15:42:39 -080054 }
Russ Cox47a05332010-04-26 22:15:25 -070055 out := new(dnsMsg)
Russ Coxc312d0e2010-02-10 16:35:35 -080056 out.id = uint16(rand.Int()) ^ uint16(time.Nanoseconds())
Russ Cox47a05332010-04-26 22:15:25 -070057 out.question = []dnsQuestion{
Kirklin McDonald0046d512010-06-30 10:54:24 +100058 dnsQuestion{name, qtype, dnsClassINET},
Robert Griesemera3d10452009-12-15 15:35:38 -080059 }
60 out.recursion_desired = true
61 msg, ok := out.Pack()
Russ Cox83348f92008-12-18 15:42:39 -080062 if !ok {
Russ Cox47a05332010-04-26 22:15:25 -070063 return nil, &DNSError{Error: "internal error - cannot pack message", Name: name}
Russ Cox83348f92008-12-18 15:42:39 -080064 }
65
66 for attempt := 0; attempt < cfg.attempts; attempt++ {
Robert Griesemera3d10452009-12-15 15:35:38 -080067 n, err := c.Write(msg)
Russ Cox83348f92008-12-18 15:42:39 -080068 if err != nil {
Robert Griesemer40621d52009-11-09 12:07:39 -080069 return nil, err
Russ Cox83348f92008-12-18 15:42:39 -080070 }
71
Stephen Mab73e5922010-03-03 15:25:26 +110072 c.SetReadTimeout(int64(cfg.timeout) * 1e9) // nanoseconds
Russ Cox83348f92008-12-18 15:42:39 -080073
Robert Griesemera3d10452009-12-15 15:35:38 -080074 buf := make([]byte, 2000) // More than enough.
75 n, err = c.Read(buf)
Russ Cox83348f92008-12-18 15:42:39 -080076 if err != nil {
Russ Cox47a05332010-04-26 22:15:25 -070077 if e, ok := err.(Error); ok && e.Timeout() {
78 continue
79 }
Robert Griesemer40621d52009-11-09 12:07:39 -080080 return nil, err
Russ Cox83348f92008-12-18 15:42:39 -080081 }
Robert Griesemera3d10452009-12-15 15:35:38 -080082 buf = buf[0:n]
Russ Cox47a05332010-04-26 22:15:25 -070083 in := new(dnsMsg)
Russ Cox83348f92008-12-18 15:42:39 -080084 if !in.Unpack(buf) || in.id != out.id {
Robert Griesemer40621d52009-11-09 12:07:39 -080085 continue
Russ Cox83348f92008-12-18 15:42:39 -080086 }
Robert Griesemera3d10452009-12-15 15:35:38 -080087 return in, nil
Russ Cox83348f92008-12-18 15:42:39 -080088 }
Robert Griesemera3d10452009-12-15 15:35:38 -080089 var server string
Russ Coxc83b8382009-11-02 18:37:30 -080090 if a := c.RemoteAddr(); a != nil {
Robert Griesemer40621d52009-11-09 12:07:39 -080091 server = a.String()
Russ Coxc83b8382009-11-02 18:37:30 -080092 }
Russ Cox47a05332010-04-26 22:15:25 -070093 return nil, &DNSError{Error: "no answer from server", Name: name, Server: server, IsTimeout: true}
Russ Cox83348f92008-12-18 15:42:39 -080094}
95
Russ Coxd47d8882008-12-18 22:37:22 -080096
Russ Cox83348f92008-12-18 15:42:39 -080097// Find answer for name in dns message.
98// On return, if err == nil, addrs != nil.
Kirklin McDonald0046d512010-06-30 10:54:24 +100099func answer(name, server string, dns *dnsMsg, qtype uint16) (addrs []dnsRR, err os.Error) {
100 addrs = make([]dnsRR, 0, len(dns.answer))
Russ Cox83348f92008-12-18 15:42:39 -0800101
Russ Cox47a05332010-04-26 22:15:25 -0700102 if dns.rcode == dnsRcodeNameError && dns.recursion_available {
103 return nil, &DNSError{Error: noSuchHost, Name: name}
Russ Cox83348f92008-12-18 15:42:39 -0800104 }
Russ Cox47a05332010-04-26 22:15:25 -0700105 if dns.rcode != dnsRcodeSuccess {
Russ Cox83348f92008-12-18 15:42:39 -0800106 // 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.
Russ Cox47a05332010-04-26 22:15:25 -0700110 return nil, &DNSError{Error: "server misbehaving", Name: name, Server: server}
Russ Cox83348f92008-12-18 15:42:39 -0800111 }
112
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.
118Cname:
119 for cnameloop := 0; cnameloop < 10; cnameloop++ {
Robert Griesemera3d10452009-12-15 15:35:38 -0800120 addrs = addrs[0:0]
Russ Cox83348f92008-12-18 15:42:39 -0800121 for i := 0; i < len(dns.answer); i++ {
Robert Griesemera3d10452009-12-15 15:35:38 -0800122 rr := dns.answer[i]
123 h := rr.Header()
Russ Cox47a05332010-04-26 22:15:25 -0700124 if h.Class == dnsClassINET && h.Name == name {
Russ Coxf1bc7122009-07-07 11:04:26 -0700125 switch h.Rrtype {
Kirklin McDonald0046d512010-06-30 10:54:24 +1000126 case qtype:
Robert Griesemera3d10452009-12-15 15:35:38 -0800127 n := len(addrs)
Robert Griesemera3d10452009-12-15 15:35:38 -0800128 addrs = addrs[0 : n+1]
Kirklin McDonald0046d512010-06-30 10:54:24 +1000129 addrs[n] = rr
Russ Cox47a05332010-04-26 22:15:25 -0700130 case dnsTypeCNAME:
Russ Cox83348f92008-12-18 15:42:39 -0800131 // redirect to cname
Russ Cox47a05332010-04-26 22:15:25 -0700132 name = rr.(*dnsRR_CNAME).Cname
Robert Griesemera3d10452009-12-15 15:35:38 -0800133 continue Cname
Russ Cox83348f92008-12-18 15:42:39 -0800134 }
135 }
136 }
137 if len(addrs) == 0 {
Russ Cox47a05332010-04-26 22:15:25 -0700138 return nil, &DNSError{Error: noSuchHost, Name: name, Server: server}
Russ Cox83348f92008-12-18 15:42:39 -0800139 }
Robert Griesemera3d10452009-12-15 15:35:38 -0800140 return addrs, nil
Russ Cox83348f92008-12-18 15:42:39 -0800141 }
142
Russ Cox47a05332010-04-26 22:15:25 -0700143 return nil, &DNSError{Error: "too many redirects", Name: name, Server: server}
Russ Cox83348f92008-12-18 15:42:39 -0800144}
145
146// Do a lookup for a single name, which must be rooted
Russ Coxd8921c52009-02-15 14:18:39 -0800147// (otherwise answer will not find the answers).
Kirklin McDonald0046d512010-06-30 10:54:24 +1000148func tryOneName(cfg *dnsConfig, name string, qtype uint16) (addrs []dnsRR, err os.Error) {
Russ Coxa0bcaf42009-06-25 20:24:55 -0700149 if len(cfg.servers) == 0 {
Russ Cox47a05332010-04-26 22:15:25 -0700150 return nil, &DNSError{Error: "no DNS servers", Name: name}
Russ Coxa0bcaf42009-06-25 20:24:55 -0700151 }
Russ Cox83348f92008-12-18 15:42:39 -0800152 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.
Robert Griesemera3d10452009-12-15 15:35:38 -0800159 server := cfg.servers[i] + ":53"
160 c, cerr := Dial("udp", "", server)
Russ Cox83348f92008-12-18 15:42:39 -0800161 if cerr != nil {
Robert Griesemera3d10452009-12-15 15:35:38 -0800162 err = cerr
163 continue
Russ Cox83348f92008-12-18 15:42:39 -0800164 }
Kirklin McDonald0046d512010-06-30 10:54:24 +1000165 msg, merr := exchange(cfg, c, name, qtype)
Robert Griesemera3d10452009-12-15 15:35:38 -0800166 c.Close()
Russ Cox83348f92008-12-18 15:42:39 -0800167 if merr != nil {
Robert Griesemera3d10452009-12-15 15:35:38 -0800168 err = merr
169 continue
Russ Cox83348f92008-12-18 15:42:39 -0800170 }
Kirklin McDonald0046d512010-06-30 10:54:24 +1000171 addrs, err = answer(name, server, msg, qtype)
Russ Cox47a05332010-04-26 22:15:25 -0700172 if err == nil || err.(*DNSError).Error == noSuchHost {
Robert Griesemer40621d52009-11-09 12:07:39 -0800173 break
Russ Coxa0bcaf42009-06-25 20:24:55 -0700174 }
Russ Cox83348f92008-12-18 15:42:39 -0800175 }
Robert Griesemera3d10452009-12-15 15:35:38 -0800176 return
Russ Cox83348f92008-12-18 15:42:39 -0800177}
178
Kirklin McDonald0046d512010-06-30 10:54:24 +1000179func convertRR_A(records []dnsRR) []string {
180 addrs := make([]string, len(records))
181 for i := 0; i < len(records); i++ {
182 rr := records[i]
183 a := rr.(*dnsRR_A).A
184 addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a)).String()
185 }
186 return addrs
187}
188
Russ Cox47a05332010-04-26 22:15:25 -0700189var cfg *dnsConfig
Rob Pikeaaf63f82009-04-17 00:08:24 -0700190var dnserr os.Error
Russ Cox83348f92008-12-18 15:42:39 -0800191
Russ Cox47a05332010-04-26 22:15:25 -0700192func loadConfig() { cfg, dnserr = dnsReadConfig() }
Russ Cox83348f92008-12-18 15:42:39 -0800193
Russ Coxa0bcaf42009-06-25 20:24:55 -0700194func isDomainName(s string) bool {
195 // Requirements on DNS name:
196 // * must not be empty.
197 // * must be alphanumeric plus - and .
198 // * each of the dot-separated elements must begin
199 // and end with a letter or digit.
200 // RFC 1035 required the element to begin with a letter,
201 // but RFC 3696 says this has been relaxed to allow digits too.
202 // still, there must be a letter somewhere in the entire name.
203 if len(s) == 0 {
Robert Griesemer40621d52009-11-09 12:07:39 -0800204 return false
Russ Coxa0bcaf42009-06-25 20:24:55 -0700205 }
Robert Griesemera3d10452009-12-15 15:35:38 -0800206 if s[len(s)-1] != '.' { // simplify checking loop: make name end in dot
Robert Griesemer40621d52009-11-09 12:07:39 -0800207 s += "."
Russ Coxa0bcaf42009-06-25 20:24:55 -0700208 }
209
Robert Griesemera3d10452009-12-15 15:35:38 -0800210 last := byte('.')
211 ok := false // ok once we've seen a letter
Russ Coxa0bcaf42009-06-25 20:24:55 -0700212 for i := 0; i < len(s); i++ {
Robert Griesemera3d10452009-12-15 15:35:38 -0800213 c := s[i]
Russ Coxa0bcaf42009-06-25 20:24:55 -0700214 switch {
215 default:
Robert Griesemer40621d52009-11-09 12:07:39 -0800216 return false
Russ Coxa0bcaf42009-06-25 20:24:55 -0700217 case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
Robert Griesemer40621d52009-11-09 12:07:39 -0800218 ok = true
Russ Coxa0bcaf42009-06-25 20:24:55 -0700219 case '0' <= c && c <= '9':
220 // fine
221 case c == '-':
222 // byte before dash cannot be dot
223 if last == '.' {
Robert Griesemer40621d52009-11-09 12:07:39 -0800224 return false
Russ Coxa0bcaf42009-06-25 20:24:55 -0700225 }
226 case c == '.':
227 // byte before dot cannot be dot, dash
228 if last == '.' || last == '-' {
Robert Griesemer40621d52009-11-09 12:07:39 -0800229 return false
Russ Coxa0bcaf42009-06-25 20:24:55 -0700230 }
231 }
Robert Griesemera3d10452009-12-15 15:35:38 -0800232 last = c
Russ Coxa0bcaf42009-06-25 20:24:55 -0700233 }
234
Robert Griesemera3d10452009-12-15 15:35:38 -0800235 return ok
Russ Coxa0bcaf42009-06-25 20:24:55 -0700236}
237
Rob Pikec78be462010-08-06 06:14:41 +1000238var onceLoadConfig sync.Once
239
Kirklin McDonald0046d512010-06-30 10:54:24 +1000240func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Error) {
Russ Coxa0bcaf42009-06-25 20:24:55 -0700241 if !isDomainName(name) {
Russ Cox47a05332010-04-26 22:15:25 -0700242 return name, nil, &DNSError{Error: "invalid domain name", Name: name}
Russ Coxa0bcaf42009-06-25 20:24:55 -0700243 }
Rob Pikec78be462010-08-06 06:14:41 +1000244 onceLoadConfig.Do(loadConfig)
Russ Coxc93da7c72009-03-05 15:48:12 -0800245 if dnserr != nil || cfg == nil {
Robert Griesemera3d10452009-12-15 15:35:38 -0800246 err = dnserr
247 return
Russ Cox83348f92008-12-18 15:42:39 -0800248 }
Russ Cox83348f92008-12-18 15:42:39 -0800249 // If name is rooted (trailing dot) or has enough dots,
250 // try it by itself first.
Robert Griesemera3d10452009-12-15 15:35:38 -0800251 rooted := len(name) > 0 && name[len(name)-1] == '.'
Russ Cox35ace1d2009-11-01 11:15:34 -0800252 if rooted || count(name, '.') >= cfg.ndots {
Robert Griesemera3d10452009-12-15 15:35:38 -0800253 rname := name
Russ Cox83348f92008-12-18 15:42:39 -0800254 if !rooted {
Robert Griesemer40621d52009-11-09 12:07:39 -0800255 rname += "."
Russ Cox83348f92008-12-18 15:42:39 -0800256 }
257 // Can try as ordinary name.
Kirklin McDonald0046d512010-06-30 10:54:24 +1000258 addrs, err = tryOneName(cfg, rname, qtype)
Russ Coxa0bcaf42009-06-25 20:24:55 -0700259 if err == nil {
Robert Griesemera3d10452009-12-15 15:35:38 -0800260 cname = rname
261 return
Russ Cox83348f92008-12-18 15:42:39 -0800262 }
Russ Cox83348f92008-12-18 15:42:39 -0800263 }
264 if rooted {
Robert Griesemer40621d52009-11-09 12:07:39 -0800265 return
Russ Cox83348f92008-12-18 15:42:39 -0800266 }
267
268 // Otherwise, try suffixes.
269 for i := 0; i < len(cfg.search); i++ {
Robert Griesemera3d10452009-12-15 15:35:38 -0800270 rname := name + "." + cfg.search[i]
Russ Coxa0bcaf42009-06-25 20:24:55 -0700271 if rname[len(rname)-1] != '.' {
Robert Griesemer40621d52009-11-09 12:07:39 -0800272 rname += "."
Russ Cox83348f92008-12-18 15:42:39 -0800273 }
Kirklin McDonald0046d512010-06-30 10:54:24 +1000274 addrs, err = tryOneName(cfg, rname, qtype)
Russ Coxa0bcaf42009-06-25 20:24:55 -0700275 if err == nil {
Robert Griesemera3d10452009-12-15 15:35:38 -0800276 cname = rname
277 return
Russ Cox83348f92008-12-18 15:42:39 -0800278 }
Russ Cox83348f92008-12-18 15:42:39 -0800279 }
Russ Cox484f46d2009-11-10 17:09:33 -0800280
281 // Last ditch effort: try unsuffixed.
Robert Griesemera3d10452009-12-15 15:35:38 -0800282 rname := name
Russ Cox484f46d2009-11-10 17:09:33 -0800283 if !rooted {
284 rname += "."
285 }
Kirklin McDonald0046d512010-06-30 10:54:24 +1000286 addrs, err = tryOneName(cfg, rname, qtype)
Russ Cox484f46d2009-11-10 17:09:33 -0800287 if err == nil {
Robert Griesemera3d10452009-12-15 15:35:38 -0800288 cname = rname
289 return
Russ Cox484f46d2009-11-10 17:09:33 -0800290 }
Robert Griesemera3d10452009-12-15 15:35:38 -0800291 return
Russ Cox83348f92008-12-18 15:42:39 -0800292}
Kirklin McDonald0046d512010-06-30 10:54:24 +1000293
294// LookupHost looks for name using the local hosts file and DNS resolver.
295// It returns the canonical name for the host and an array of that
296// host's addresses.
297func LookupHost(name string) (cname string, addrs []string, err os.Error) {
Rob Pikec78be462010-08-06 06:14:41 +1000298 onceLoadConfig.Do(loadConfig)
Kirklin McDonald0046d512010-06-30 10:54:24 +1000299 if dnserr != nil || cfg == nil {
300 err = dnserr
301 return
302 }
303 // Use entries from /etc/hosts if they match.
304 addrs = lookupStaticHost(name)
305 if len(addrs) > 0 {
306 cname = name
307 return
308 }
309 var records []dnsRR
310 cname, records, err = lookup(name, dnsTypeA)
311 if err != nil {
312 return
313 }
314 addrs = convertRR_A(records)
315 return
316}
317
318type SRV struct {
319 Target string
320 Port uint16
321 Priority uint16
322 Weight uint16
323}
324
325func LookupSRV(name string) (cname string, addrs []*SRV, err os.Error) {
326 var records []dnsRR
327 cname, records, err = lookup(name, dnsTypeSRV)
328 if err != nil {
329 return
330 }
331 addrs = make([]*SRV, len(records))
332 for i := 0; i < len(records); i++ {
333 r := records[i].(*dnsRR_SRV)
334 addrs[i] = &SRV{r.Target, r.Port, r.Priority, r.Weight}
335 }
336 return
337}
Corey Thomassonec72f9b2010-08-26 13:32:45 -0400338
339type MX struct {
340 Host string
341 Pref uint16
342}
343
344func LookupMX(name string) (entries []*MX, err os.Error) {
345 var records []dnsRR
346 _, records, err = lookup(name, dnsTypeMX)
347 if err != nil {
348 return
349 }
350 entries = make([]*MX, len(records))
351 for i := 0; i < len(records); i++ {
352 r := records[i].(*dnsRR_MX)
353 entries[i] = &MX{r.Mx, r.Pref}
354 }
355 return
356}