~john-koepi/ubuntu/trusty/golang/default

« back to all changes in this revision

Viewing changes to src/pkg/net/dnsclient.go

  • Committer: Bazaar Package Importer
  • Author(s): Ondřej Surý
  • Date: 2011-08-03 17:04:59 UTC
  • mfrom: (14.1.2 sid)
  • Revision ID: james.westby@ubuntu.com-20110803170459-wzd99m3567y80ila
Tags: 1:59-1
* Imported Upstream version 59
* Refresh patches to a new release
* Fix FTBFS on ARM (Closes: #634270)
* Update version.bash to work with Debian packaging and not hg
  repository

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
// Use of this source code is governed by a BSD-style
3
3
// license that can be found in the LICENSE file.
4
4
 
5
 
// DNS client: see RFC 1035.
6
 
// 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.
14
 
 
15
5
package net
16
6
 
17
7
import (
19
9
        "fmt"
20
10
        "os"
21
11
        "rand"
22
 
        "sync"
23
 
        "time"
24
 
        "sort"
25
12
)
26
13
 
27
14
// DNSError represents a DNS lookup error.
49
36
 
50
37
const noSuchHost = "no such host"
51
38
 
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) {
55
 
        if len(name) >= 256 {
56
 
                return nil, &DNSError{Error: "name too long", Name: name}
57
 
        }
58
 
        out := new(dnsMsg)
59
 
        out.id = uint16(rand.Int()) ^ uint16(time.Nanoseconds())
60
 
        out.question = []dnsQuestion{
61
 
                {name, qtype, dnsClassINET},
62
 
        }
63
 
        out.recursion_desired = true
64
 
        msg, ok := out.Pack()
65
 
        if !ok {
66
 
                return nil, &DNSError{Error: "internal error - cannot pack message", Name: name}
67
 
        }
68
 
 
69
 
        for attempt := 0; attempt < cfg.attempts; attempt++ {
70
 
                n, err := c.Write(msg)
71
 
                if err != nil {
72
 
                        return nil, err
73
 
                }
74
 
 
75
 
                c.SetReadTimeout(int64(cfg.timeout) * 1e9) // nanoseconds
76
 
 
77
 
                buf := make([]byte, 2000) // More than enough.
78
 
                n, err = c.Read(buf)
79
 
                if err != nil {
80
 
                        if e, ok := err.(Error); ok && e.Timeout() {
81
 
                                continue
82
 
                        }
83
 
                        return nil, err
84
 
                }
85
 
                buf = buf[0:n]
86
 
                in := new(dnsMsg)
87
 
                if !in.Unpack(buf) || in.id != out.id {
88
 
                        continue
89
 
                }
90
 
                return in, nil
91
 
        }
92
 
        var server string
93
 
        if a := c.RemoteAddr(); a != nil {
94
 
                server = a.String()
95
 
        }
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) {
 
43
        ip := ParseIP(addr)
 
44
        if ip == nil {
 
45
                return "", &DNSError{Error: "unrecognized address", Name: addr}
 
46
        }
 
47
        if ip.To4() != nil {
 
48
                return fmt.Sprintf("%d.%d.%d.%d.in-addr.arpa.", ip[15], ip[14], ip[13], ip[12]), nil
 
49
        }
 
50
        // Must be IPv6
 
51
        var buf bytes.Buffer
 
52
        // Add it, in reverse, to the buffer
 
53
        for i := len(ip) - 1; i >= 0; i-- {
 
54
                s := fmt.Sprintf("%02x", ip[i])
 
55
                buf.WriteByte(s[1])
 
56
                buf.WriteByte('.')
 
57
                buf.WriteByte(s[0])
 
58
                buf.WriteByte('.')
 
59
        }
 
60
        // Append "ip6.arpa." and return (buf already has the final .)
 
61
        return buf.String() + "ip6.arpa.", nil
97
62
}
98
63
 
99
 
 
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}
151
115
}
152
116
 
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}
158
 
        }
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)
168
 
                if cerr != nil {
169
 
                        err = cerr
170
 
                        continue
171
 
                }
172
 
                msg, merr := exchange(cfg, c, name, qtype)
173
 
                c.Close()
174
 
                if merr != nil {
175
 
                        err = merr
176
 
                        continue
177
 
                }
178
 
                cname, addrs, err = answer(name, server, msg, qtype)
179
 
                if err == nil || err.(*DNSError).Error == noSuchHost {
180
 
                        break
181
 
                }
182
 
        }
183
 
        return
184
 
}
185
 
 
186
 
func convertRR_A(records []dnsRR) []IP {
187
 
        addrs := make([]IP, len(records))
188
 
        for i, rr := range records {
189
 
                a := rr.(*dnsRR_A).A
190
 
                addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a))
191
 
        }
192
 
        return addrs
193
 
}
194
 
 
195
 
func convertRR_AAAA(records []dnsRR) []IP {
196
 
        addrs := make([]IP, len(records))
197
 
        for i, rr := range records {
198
 
                a := make(IP, 16)
199
 
                copy(a, rr.(*dnsRR_AAAA).AAAA[:])
200
 
                addrs[i] = a
201
 
        }
202
 
        return addrs
203
 
}
204
 
 
205
 
var cfg *dnsConfig
206
 
var dnserr os.Error
207
 
 
208
 
func loadConfig() { cfg, dnserr = dnsReadConfig() }
209
 
 
210
117
func isDomainName(s string) bool {
211
118
        // See RFC 1035, RFC 3696.
212
119
        if len(s) == 0 {
255
162
        return ok
256
163
}
257
164
 
258
 
var onceLoadConfig sync.Once
259
 
 
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}
263
 
        }
264
 
        onceLoadConfig.Do(loadConfig)
265
 
        if dnserr != nil || cfg == nil {
266
 
                err = dnserr
267
 
                return
268
 
        }
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 {
273
 
                rname := name
274
 
                if !rooted {
275
 
                        rname += "."
276
 
                }
277
 
                // Can try as ordinary name.
278
 
                cname, addrs, err = tryOneName(cfg, rname, qtype)
279
 
                if err == nil {
280
 
                        return
281
 
                }
282
 
        }
283
 
        if rooted {
284
 
                return
285
 
        }
286
 
 
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] != '.' {
291
 
                        rname += "."
292
 
                }
293
 
                cname, addrs, err = tryOneName(cfg, rname, qtype)
294
 
                if err == nil {
295
 
                        return
296
 
                }
297
 
        }
298
 
 
299
 
        // Last ditch effort: try unsuffixed.
300
 
        rname := name
301
 
        if !rooted {
302
 
                rname += "."
303
 
        }
304
 
        cname, addrs, err = tryOneName(cfg, rname, qtype)
305
 
        if err == nil {
306
 
                return
307
 
        }
308
 
        return
309
 
}
310
 
 
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
316
 
// answers.
317
 
func goLookupHost(name string) (addrs []string, err os.Error) {
318
 
        // Use entries from /etc/hosts if they match.
319
 
        addrs = lookupStaticHost(name)
320
 
        if len(addrs) > 0 {
321
 
                return
322
 
        }
323
 
        onceLoadConfig.Do(loadConfig)
324
 
        if dnserr != nil || cfg == nil {
325
 
                err = dnserr
326
 
                return
327
 
        }
328
 
        ips, err := goLookupIP(name)
329
 
        if err != nil {
330
 
                return
331
 
        }
332
 
        addrs = make([]string, 0, len(ips))
333
 
        for _, ip := range ips {
334
 
                addrs = append(addrs, ip.String())
335
 
        }
336
 
        return
337
 
}
338
 
 
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
344
 
// answers.
345
 
func goLookupIP(name string) (addrs []IP, err os.Error) {
346
 
        onceLoadConfig.Do(loadConfig)
347
 
        if dnserr != nil || cfg == nil {
348
 
                err = dnserr
349
 
                return
350
 
        }
351
 
        var records []dnsRR
352
 
        var cname string
353
 
        cname, records, err = lookup(name, dnsTypeA)
354
 
        if err != nil {
355
 
                return
356
 
        }
357
 
        addrs = convertRR_A(records)
358
 
        if cname != "" {
359
 
                name = cname
360
 
        }
361
 
        _, records, err = lookup(name, dnsTypeAAAA)
362
 
        if err != nil && len(addrs) > 0 {
363
 
                // Ignore error because A lookup succeeded.
364
 
                err = nil
365
 
        }
366
 
        if err != nil {
367
 
                return
368
 
        }
369
 
        addrs = append(addrs, convertRR_AAAA(records)...)
370
 
        return
371
 
}
372
 
 
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
378
 
// answers.
379
 
func goLookupCNAME(name string) (cname string, err os.Error) {
380
 
        onceLoadConfig.Do(loadConfig)
381
 
        if dnserr != nil || cfg == nil {
382
 
                err = dnserr
383
 
                return
384
 
        }
385
 
        _, rr, err := lookup(name, dnsTypeCNAME)
386
 
        if err != nil {
387
 
                return
388
 
        }
389
 
        cname = rr[0].(*dnsRR_CNAME).Cname
390
 
        return
391
 
}
392
 
 
393
165
// An SRV represents a single DNS SRV record.
394
166
type SRV struct {
395
167
        Target   string
436
208
        }
437
209
}
438
210
 
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
446
 
        var records []dnsRR
447
 
        cname, records, err = lookup(target, dnsTypeSRV)
448
 
        if err != nil {
449
 
                return
450
 
        }
451
 
        addrs = make([]*SRV, len(records))
452
 
        for i, rr := range records {
453
 
                r := rr.(*dnsRR_SRV)
454
 
                addrs[i] = &SRV{r.Target, r.Port, r.Priority, r.Weight}
455
 
        }
456
 
        sort.Sort(byPriorityWeight(addrs))
457
 
        i := 0
458
 
        for j := 1; j < len(addrs); j++ {
459
 
                if addrs[i].Priority != addrs[j].Priority {
460
 
                        shuffleSRVByWeight(addrs[i:j])
461
 
                        i = j
462
 
                }
463
 
        }
464
 
        shuffleSRVByWeight(addrs[i:len(addrs)])
465
 
        return
466
 
}
467
 
 
468
211
// An MX represents a single DNS MX record.
469
212
type MX struct {
470
213
        Host string
479
222
func (s byPref) Less(i, j int) bool { return s[i].Pref < s[j].Pref }
480
223
 
481
224
func (s byPref) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
482
 
 
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)
486
 
        if err != nil {
487
 
                return
488
 
        }
489
 
        mx = make([]*MX, len(rr))
490
 
        for i := range rr {
491
 
                r := rr[i].(*dnsRR_MX)
492
 
                mx[i] = &MX{r.Mx, r.Pref}
493
 
        }
494
 
        // Shuffle the records to match RFC 5321 when sorted
495
 
        for i := range mx {
496
 
                j := rand.Intn(i + 1)
497
 
                mx[i], mx[j] = mx[j], mx[i]
498
 
        }
499
 
        sort.Sort(byPref(mx))
500
 
        return
501
 
}
502
 
 
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) {
507
 
        ip := ParseIP(addr)
508
 
        if ip == nil {
509
 
                return "", &DNSError{Error: "unrecognized address", Name: addr}
510
 
        }
511
 
        if ip.To4() != nil {
512
 
                return fmt.Sprintf("%d.%d.%d.%d.in-addr.arpa.", ip[15], ip[14], ip[13], ip[12]), nil
513
 
        }
514
 
        // Must be IPv6
515
 
        var buf bytes.Buffer
516
 
        // Add it, in reverse, to the buffer
517
 
        for i := len(ip) - 1; i >= 0; i-- {
518
 
                s := fmt.Sprintf("%02x", ip[i])
519
 
                buf.WriteByte(s[1])
520
 
                buf.WriteByte('.')
521
 
                buf.WriteByte(s[0])
522
 
                buf.WriteByte('.')
523
 
        }
524
 
        // Append "ip6.arpa." and return (buf already has the final .)
525
 
        return buf.String() + "ip6.arpa.", nil
526
 
}
527
 
 
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)
532
 
        if len(name) > 0 {
533
 
                return
534
 
        }
535
 
        var arpa string
536
 
        arpa, err = reverseaddr(addr)
537
 
        if err != nil {
538
 
                return
539
 
        }
540
 
        var records []dnsRR
541
 
        _, records, err = lookup(arpa, dnsTypePTR)
542
 
        if err != nil {
543
 
                return
544
 
        }
545
 
        name = make([]string, len(records))
546
 
        for i := range records {
547
 
                r := records[i].(*dnsRR_PTR)
548
 
                name[i] = r.Ptr
549
 
        }
550
 
        return
551
 
}