50
37
const noSuchHost = "no such host"
52
// Send a request on the connection and hope for a reply.
53
// Up to cfg.attempts attempts.
54
func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, os.Error) {
56
return nil, &DNSError{Error: "name too long", Name: name}
59
out.id = uint16(rand.Int()) ^ uint16(time.Nanoseconds())
60
out.question = []dnsQuestion{
61
{name, qtype, dnsClassINET},
63
out.recursion_desired = true
66
return nil, &DNSError{Error: "internal error - cannot pack message", Name: name}
69
for attempt := 0; attempt < cfg.attempts; attempt++ {
70
n, err := c.Write(msg)
75
c.SetReadTimeout(int64(cfg.timeout) * 1e9) // nanoseconds
77
buf := make([]byte, 2000) // More than enough.
80
if e, ok := err.(Error); ok && e.Timeout() {
87
if !in.Unpack(buf) || in.id != out.id {
93
if a := c.RemoteAddr(); a != nil {
96
return nil, &DNSError{Error: "no answer from server", Name: name, Server: server, IsTimeout: true}
39
// reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
40
// address addr suitable for rDNS (PTR) record lookup or an error if it fails
41
// to parse the IP address.
42
func reverseaddr(addr string) (arpa string, err os.Error) {
45
return "", &DNSError{Error: "unrecognized address", Name: addr}
48
return fmt.Sprintf("%d.%d.%d.%d.in-addr.arpa.", ip[15], ip[14], ip[13], ip[12]), nil
52
// Add it, in reverse, to the buffer
53
for i := len(ip) - 1; i >= 0; i-- {
54
s := fmt.Sprintf("%02x", ip[i])
60
// Append "ip6.arpa." and return (buf already has the final .)
61
return buf.String() + "ip6.arpa.", nil
100
64
// Find answer for name in dns message.
101
65
// On return, if err == nil, addrs != nil.
102
66
func answer(name, server string, dns *dnsMsg, qtype uint16) (cname string, addrs []dnsRR, err os.Error) {
150
114
return "", nil, &DNSError{Error: "too many redirects", Name: name, Server: server}
153
// Do a lookup for a single name, which must be rooted
154
// (otherwise answer will not find the answers).
155
func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs []dnsRR, err os.Error) {
156
if len(cfg.servers) == 0 {
157
return "", nil, &DNSError{Error: "no DNS servers", Name: name}
159
for i := 0; i < len(cfg.servers); i++ {
160
// Calling Dial here is scary -- we have to be sure
161
// not to dial a name that will require a DNS lookup,
162
// or Dial will call back here to translate it.
163
// The DNS config parser has already checked that
164
// all the cfg.servers[i] are IP addresses, which
165
// Dial will use without a DNS lookup.
166
server := cfg.servers[i] + ":53"
167
c, cerr := Dial("udp", server)
172
msg, merr := exchange(cfg, c, name, qtype)
178
cname, addrs, err = answer(name, server, msg, qtype)
179
if err == nil || err.(*DNSError).Error == noSuchHost {
186
func convertRR_A(records []dnsRR) []IP {
187
addrs := make([]IP, len(records))
188
for i, rr := range records {
190
addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a))
195
func convertRR_AAAA(records []dnsRR) []IP {
196
addrs := make([]IP, len(records))
197
for i, rr := range records {
199
copy(a, rr.(*dnsRR_AAAA).AAAA[:])
208
func loadConfig() { cfg, dnserr = dnsReadConfig() }
210
117
func isDomainName(s string) bool {
211
118
// See RFC 1035, RFC 3696.
258
var onceLoadConfig sync.Once
260
func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Error) {
261
if !isDomainName(name) {
262
return name, nil, &DNSError{Error: "invalid domain name", Name: name}
264
onceLoadConfig.Do(loadConfig)
265
if dnserr != nil || cfg == nil {
269
// If name is rooted (trailing dot) or has enough dots,
270
// try it by itself first.
271
rooted := len(name) > 0 && name[len(name)-1] == '.'
272
if rooted || count(name, '.') >= cfg.ndots {
277
// Can try as ordinary name.
278
cname, addrs, err = tryOneName(cfg, rname, qtype)
287
// Otherwise, try suffixes.
288
for i := 0; i < len(cfg.search); i++ {
289
rname := name + "." + cfg.search[i]
290
if rname[len(rname)-1] != '.' {
293
cname, addrs, err = tryOneName(cfg, rname, qtype)
299
// Last ditch effort: try unsuffixed.
304
cname, addrs, err = tryOneName(cfg, rname, qtype)
311
// goLookupHost is the native Go implementation of LookupHost.
312
// Used only if cgoLookupHost refuses to handle the request
313
// (that is, only if cgoLookupHost is the stub in cgo_stub.go).
314
// Normally we let cgo use the C library resolver instead of
315
// depending on our lookup code, so that Go and C get the same
317
func goLookupHost(name string) (addrs []string, err os.Error) {
318
// Use entries from /etc/hosts if they match.
319
addrs = lookupStaticHost(name)
323
onceLoadConfig.Do(loadConfig)
324
if dnserr != nil || cfg == nil {
328
ips, err := goLookupIP(name)
332
addrs = make([]string, 0, len(ips))
333
for _, ip := range ips {
334
addrs = append(addrs, ip.String())
339
// goLookupIP is the native Go implementation of LookupIP.
340
// Used only if cgoLookupIP refuses to handle the request
341
// (that is, only if cgoLookupIP is the stub in cgo_stub.go).
342
// Normally we let cgo use the C library resolver instead of
343
// depending on our lookup code, so that Go and C get the same
345
func goLookupIP(name string) (addrs []IP, err os.Error) {
346
onceLoadConfig.Do(loadConfig)
347
if dnserr != nil || cfg == nil {
353
cname, records, err = lookup(name, dnsTypeA)
357
addrs = convertRR_A(records)
361
_, records, err = lookup(name, dnsTypeAAAA)
362
if err != nil && len(addrs) > 0 {
363
// Ignore error because A lookup succeeded.
369
addrs = append(addrs, convertRR_AAAA(records)...)
373
// goLookupCNAME is the native Go implementation of LookupCNAME.
374
// Used only if cgoLookupCNAME refuses to handle the request
375
// (that is, only if cgoLookupCNAME is the stub in cgo_stub.go).
376
// Normally we let cgo use the C library resolver instead of
377
// depending on our lookup code, so that Go and C get the same
379
func goLookupCNAME(name string) (cname string, err os.Error) {
380
onceLoadConfig.Do(loadConfig)
381
if dnserr != nil || cfg == nil {
385
_, rr, err := lookup(name, dnsTypeCNAME)
389
cname = rr[0].(*dnsRR_CNAME).Cname
393
165
// An SRV represents a single DNS SRV record.
394
166
type SRV struct {
439
// LookupSRV tries to resolve an SRV query of the given service,
440
// protocol, and domain name, as specified in RFC 2782. In most cases
441
// the proto argument can be the same as the corresponding
442
// Addr.Network(). The returned records are sorted by priority
443
// and randomized by weight within a priority.
444
func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.Error) {
445
target := "_" + service + "._" + proto + "." + name
447
cname, records, err = lookup(target, dnsTypeSRV)
451
addrs = make([]*SRV, len(records))
452
for i, rr := range records {
454
addrs[i] = &SRV{r.Target, r.Port, r.Priority, r.Weight}
456
sort.Sort(byPriorityWeight(addrs))
458
for j := 1; j < len(addrs); j++ {
459
if addrs[i].Priority != addrs[j].Priority {
460
shuffleSRVByWeight(addrs[i:j])
464
shuffleSRVByWeight(addrs[i:len(addrs)])
468
211
// An MX represents a single DNS MX record.
479
222
func (s byPref) Less(i, j int) bool { return s[i].Pref < s[j].Pref }
481
224
func (s byPref) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
483
// LookupMX returns the DNS MX records for the given domain name sorted by preference.
484
func LookupMX(name string) (mx []*MX, err os.Error) {
485
_, rr, err := lookup(name, dnsTypeMX)
489
mx = make([]*MX, len(rr))
491
r := rr[i].(*dnsRR_MX)
492
mx[i] = &MX{r.Mx, r.Pref}
494
// Shuffle the records to match RFC 5321 when sorted
496
j := rand.Intn(i + 1)
497
mx[i], mx[j] = mx[j], mx[i]
499
sort.Sort(byPref(mx))
503
// reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
504
// address addr suitable for rDNS (PTR) record lookup or an error if it fails
505
// to parse the IP address.
506
func reverseaddr(addr string) (arpa string, err os.Error) {
509
return "", &DNSError{Error: "unrecognized address", Name: addr}
512
return fmt.Sprintf("%d.%d.%d.%d.in-addr.arpa.", ip[15], ip[14], ip[13], ip[12]), nil
516
// Add it, in reverse, to the buffer
517
for i := len(ip) - 1; i >= 0; i-- {
518
s := fmt.Sprintf("%02x", ip[i])
524
// Append "ip6.arpa." and return (buf already has the final .)
525
return buf.String() + "ip6.arpa.", nil
528
// LookupAddr performs a reverse lookup for the given address, returning a list
529
// of names mapping to that address.
530
func LookupAddr(addr string) (name []string, err os.Error) {
531
name = lookupStaticAddr(addr)
536
arpa, err = reverseaddr(addr)
541
_, records, err = lookup(arpa, dnsTypePTR)
545
name = make([]string, len(records))
546
for i := range records {
547
r := records[i].(*dnsRR_PTR)