diff options
Diffstat (limited to 'src/pkg/net/dnsclient_unix.go')
-rw-r--r-- | src/pkg/net/dnsclient_unix.go | 103 |
1 files changed, 73 insertions, 30 deletions
diff --git a/src/pkg/net/dnsclient_unix.go b/src/pkg/net/dnsclient_unix.go index 16cf420dc..3713efd0e 100644 --- a/src/pkg/net/dnsclient_unix.go +++ b/src/pkg/net/dnsclient_unix.go @@ -2,13 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin dragonfly freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris // DNS client: see RFC 1035. // Has to be linked into package net for Dial. // TODO(rsc): -// Check periodically whether /etc/resolv.conf has changed. // Could potentially handle many outstanding lookups faster. // Could have a small cache. // Random UDP source port (net.Dial should do that for us). @@ -19,6 +18,7 @@ package net import ( "io" "math/rand" + "os" "sync" "time" ) @@ -156,32 +156,90 @@ func convertRR_AAAA(records []dnsRR) []IP { return addrs } -var cfg *dnsConfig -var dnserr error +var cfg struct { + ch chan struct{} + mu sync.RWMutex // protects dnsConfig and dnserr + dnsConfig *dnsConfig + dnserr error +} +var onceLoadConfig sync.Once -func loadConfig() { cfg, dnserr = dnsReadConfig() } +// Assume dns config file is /etc/resolv.conf here +func loadDefaultConfig() { + loadConfig("/etc/resolv.conf", 5*time.Second, nil) +} -var onceLoadConfig sync.Once +func loadConfig(resolvConfPath string, reloadTime time.Duration, quit <-chan chan struct{}) { + var mtime time.Time + cfg.ch = make(chan struct{}, 1) + if fi, err := os.Stat(resolvConfPath); err != nil { + cfg.dnserr = err + } else { + mtime = fi.ModTime() + cfg.dnsConfig, cfg.dnserr = dnsReadConfig(resolvConfPath) + } + go func() { + for { + time.Sleep(reloadTime) + select { + case qresp := <-quit: + qresp <- struct{}{} + return + case <-cfg.ch: + } + + // In case of error, we keep the previous config + fi, err := os.Stat(resolvConfPath) + if err != nil { + continue + } + // If the resolv.conf mtime didn't change, do not reload + m := fi.ModTime() + if m.Equal(mtime) { + continue + } + mtime = m + // In case of error, we keep the previous config + ncfg, err := dnsReadConfig(resolvConfPath) + if err != nil || len(ncfg.servers) == 0 { + continue + } + cfg.mu.Lock() + cfg.dnsConfig = ncfg + cfg.dnserr = nil + cfg.mu.Unlock() + } + }() +} func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error) { if !isDomainName(name) { return name, nil, &DNSError{Err: "invalid domain name", Name: name} } - onceLoadConfig.Do(loadConfig) - if dnserr != nil || cfg == nil { - err = dnserr + onceLoadConfig.Do(loadDefaultConfig) + + select { + case cfg.ch <- struct{}{}: + default: + } + + cfg.mu.RLock() + defer cfg.mu.RUnlock() + + if cfg.dnserr != nil || cfg.dnsConfig == nil { + err = cfg.dnserr return } // If name is rooted (trailing dot) or has enough dots, // try it by itself first. rooted := len(name) > 0 && name[len(name)-1] == '.' - if rooted || count(name, '.') >= cfg.ndots { + if rooted || count(name, '.') >= cfg.dnsConfig.ndots { rname := name if !rooted { rname += "." } // Can try as ordinary name. - cname, addrs, err = tryOneName(cfg, rname, qtype) + cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype) if err == nil { return } @@ -191,12 +249,12 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error) } // Otherwise, try suffixes. - for i := 0; i < len(cfg.search); i++ { - rname := name + "." + cfg.search[i] + for i := 0; i < len(cfg.dnsConfig.search); i++ { + rname := name + "." + cfg.dnsConfig.search[i] if rname[len(rname)-1] != '.' { rname += "." } - cname, addrs, err = tryOneName(cfg, rname, qtype) + cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype) if err == nil { return } @@ -207,7 +265,7 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error) if !rooted { rname += "." } - cname, addrs, err = tryOneName(cfg, rname, qtype) + cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype) if err == nil { return } @@ -232,11 +290,6 @@ func goLookupHost(name string) (addrs []string, err error) { if len(addrs) > 0 { return } - onceLoadConfig.Do(loadConfig) - if dnserr != nil || cfg == nil { - err = dnserr - return - } ips, err := goLookupIP(name) if err != nil { return @@ -267,11 +320,6 @@ func goLookupIP(name string) (addrs []IP, err error) { return } } - onceLoadConfig.Do(loadConfig) - if dnserr != nil || cfg == nil { - err = dnserr - return - } var records []dnsRR var cname string var err4, err6 error @@ -307,11 +355,6 @@ func goLookupIP(name string) (addrs []IP, err error) { // depending on our lookup code, so that Go and C get the same // answers. func goLookupCNAME(name string) (cname string, err error) { - onceLoadConfig.Do(loadConfig) - if dnserr != nil || cfg == nil { - err = dnserr - return - } _, rr, err := lookup(name, dnsTypeCNAME) if err != nil { return |