aboutsummaryrefslogtreecommitdiff
path: root/src/net/dnsclient_unix.go
diff options
context:
space:
mode:
authorAlex A Skinner <alex@lx.lc>2015-05-12 23:56:56 -0400
committerBrad Fitzpatrick <bradfitz@golang.org>2015-05-15 18:14:47 +0000
commitef7e1085658de69b6a2e365e71a955105b3a4feb (patch)
tree71bb23ce1f9b65dbb7441e25b726d94ac3a91de5 /src/net/dnsclient_unix.go
parent40fad6c286ca57317e94aeca50b75fa3444ca1fa (diff)
downloadgo-ef7e1085658de69b6a2e365e71a955105b3a4feb.tar.gz
go-ef7e1085658de69b6a2e365e71a955105b3a4feb.zip
net: redo resolv.conf recheck implementation
The previous implementation spawned an extra goroutine to handle rechecking resolv.conf for changes. This change eliminates the extra goroutine, and has rechecking done as part of a lookup. A side effect of this change is that the first lookup after a resolv.conf change will now succeed, whereas previously it would have failed. It also fixes rechecking logic to ignore resolv.conf parsing errors as it should. Fixes #8652 Fixes #10576 Fixes #10649 Fixes #10650 Fixes #10845 Change-Id: I502b587c445fa8eca5207ca4f2c8ec8c339fec7f Reviewed-on: https://go-review.googlesource.com/9991 Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com> Reviewed-by: Mikio Hara <mikioh.mikioh@gmail.com> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src/net/dnsclient_unix.go')
-rw-r--r--src/net/dnsclient_unix.go117
1 files changed, 64 insertions, 53 deletions
diff --git a/src/net/dnsclient_unix.go b/src/net/dnsclient_unix.go
index 5a4411f5c7..fab515f5c2 100644
--- a/src/net/dnsclient_unix.go
+++ b/src/net/dnsclient_unix.go
@@ -214,95 +214,106 @@ func convertRR_AAAA(records []dnsRR) []IP {
return addrs
}
+// cfg is used for the storage and reparsing of /etc/resolv.conf
var cfg struct {
- ch chan struct{}
+ // ch is used as a semaphore that only allows one lookup at a time to
+ // recheck resolv.conf. It acts as guard for lastChecked and modTime.
+ ch chan struct{}
+ lastChecked time.Time // last time resolv.conf was checked
+ modTime time.Time // time of resolv.conf modification
+
mu sync.RWMutex // protects dnsConfig
- dnsConfig *dnsConfig
+ dnsConfig *dnsConfig // parsed resolv.conf structure used in lookups
}
var onceLoadConfig sync.Once
-// Assume dns config file is /etc/resolv.conf here
-func loadDefaultConfig() {
- loadConfig("/etc/resolv.conf", 5*time.Second, nil)
-}
+func initCfg() {
+ // Set dnsConfig, modTime, and lastChecked so we don't parse
+ // resolv.conf twice the first time.
+ cfg.dnsConfig = systemConf().resolv
+ if cfg.dnsConfig == nil {
+ cfg.dnsConfig = dnsReadConfig("/etc/resolv.conf")
+ }
-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 {
- mtime = fi.ModTime()
+ if fi, err := os.Stat("/etc/resolv.conf"); err == nil {
+ cfg.modTime = fi.ModTime()
}
+ cfg.lastChecked = time.Now()
- cfg.dnsConfig = dnsReadConfig(resolvConfPath)
+ // Prepare ch so that only one loadConfig may run at once
+ cfg.ch = make(chan struct{}, 1)
+ cfg.ch <- struct{}{}
+}
- go func() {
- for {
- time.Sleep(reloadTime)
- select {
- case qresp := <-quit:
- qresp <- struct{}{}
- return
- case <-cfg.ch:
- }
+func loadConfig(resolvConfPath string) {
+ onceLoadConfig.Do(initCfg)
- // 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
- if ncfg := dnsReadConfig(resolvConfPath); ncfg.err == nil {
- cfg.mu.Lock()
- cfg.dnsConfig = ncfg
- cfg.mu.Unlock()
- }
+ // ensure only one loadConfig at a time checks /etc/resolv.conf
+ select {
+ case <-cfg.ch:
+ defer func() { cfg.ch <- struct{}{} }()
+ default:
+ return
+ }
+
+ now := time.Now()
+ if cfg.lastChecked.After(now.Add(-5 * time.Second)) {
+ return
+ }
+ cfg.lastChecked = now
+
+ if fi, err := os.Stat(resolvConfPath); err == nil {
+ if fi.ModTime().Equal(cfg.modTime) {
+ return
}
- }()
+ cfg.modTime = fi.ModTime()
+ } else {
+ // If modTime wasn't set prior, assume nothing has changed.
+ if cfg.modTime.IsZero() {
+ return
+ }
+ cfg.modTime = time.Time{}
+ }
+
+ ncfg := dnsReadConfig(resolvConfPath)
+ cfg.mu.Lock()
+ cfg.dnsConfig = ncfg
+ cfg.mu.Unlock()
}
func lookup(name string, qtype uint16) (cname string, rrs []dnsRR, err error) {
if !isDomainName(name) {
return name, nil, &DNSError{Err: "invalid domain name", Name: name}
}
- onceLoadConfig.Do(loadDefaultConfig)
-
- select {
- case cfg.ch <- struct{}{}:
- default:
- }
+ loadConfig("/etc/resolv.conf")
cfg.mu.RLock()
- defer cfg.mu.RUnlock()
+ resolv := cfg.dnsConfig
+ cfg.mu.RUnlock()
// 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.dnsConfig.ndots {
+ if rooted || count(name, '.') >= resolv.ndots {
rname := name
if !rooted {
rname += "."
}
// Can try as ordinary name.
- cname, rrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
+ cname, rrs, err = tryOneName(resolv, rname, qtype)
if rooted || err == nil {
return
}
}
// Otherwise, try suffixes.
- for i := 0; i < len(cfg.dnsConfig.search); i++ {
- rname := name + "." + cfg.dnsConfig.search[i]
+ for _, suffix := range resolv.search {
+ rname := name + "." + suffix
if rname[len(rname)-1] != '.' {
rname += "."
}
- cname, rrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
+ cname, rrs, err = tryOneName(resolv, rname, qtype)
if err == nil {
return
}
@@ -310,8 +321,8 @@ func lookup(name string, qtype uint16) (cname string, rrs []dnsRR, err error) {
// Last ditch effort: try unsuffixed only if we haven't already,
// that is, name is not rooted and has less than ndots dots.
- if count(name, '.') < cfg.dnsConfig.ndots {
- cname, rrs, err = tryOneName(cfg.dnsConfig, name+".", qtype)
+ if count(name, '.') < resolv.ndots {
+ cname, rrs, err = tryOneName(resolv, name+".", qtype)
if err == nil {
return
}