mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-23 09:06:24 +00:00
When using the resolve.conf file for setting DNS, it is possible that some other services will trample the file and overwrite our set DNS server. Experiments has shown this to be a racy error depending on how quickly processes start. Make an attempt to trample back the file a limited number of times if the file is changed. Updates #16635 Signed-off-by: Claus Lensbøl <claus@tailscale.com>
81 lines
2.0 KiB
Go
81 lines
2.0 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package dns
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
|
|
"tailscale.com/control/controlknobs"
|
|
"tailscale.com/health"
|
|
"tailscale.com/types/logger"
|
|
"tailscale.com/util/eventbus"
|
|
"tailscale.com/util/syspolicy/policyclient"
|
|
)
|
|
|
|
type kv struct {
|
|
k, v string
|
|
}
|
|
|
|
func (kv kv) String() string {
|
|
return fmt.Sprintf("%s=%s", kv.k, kv.v)
|
|
}
|
|
|
|
// NewOSConfigurator created a new OS configurator.
|
|
//
|
|
// The health tracker may be nil; the knobs may be nil and are ignored on this platform.
|
|
func NewOSConfigurator(logf logger.Logf, health *health.Tracker, bus *eventbus.Bus, _ policyclient.Client, _ *controlknobs.Knobs, interfaceName string) (OSConfigurator, error) {
|
|
return newOSConfigurator(logf, health, bus, interfaceName,
|
|
newOSConfigEnv{
|
|
rcIsResolvd: rcIsResolvd,
|
|
fs: directFS{},
|
|
})
|
|
}
|
|
|
|
// newOSConfigEnv are the funcs newOSConfigurator needs, pulled out for testing.
|
|
type newOSConfigEnv struct {
|
|
fs directFS
|
|
rcIsResolvd func(resolvConfContents []byte) bool
|
|
}
|
|
|
|
func newOSConfigurator(logf logger.Logf, health *health.Tracker, bus *eventbus.Bus, interfaceName string, env newOSConfigEnv) (ret OSConfigurator, err error) {
|
|
var debug []kv
|
|
dbg := func(k, v string) {
|
|
debug = append(debug, kv{k, v})
|
|
}
|
|
defer func() {
|
|
if ret != nil {
|
|
dbg("ret", fmt.Sprintf("%T", ret))
|
|
}
|
|
logf("dns: %v", debug)
|
|
}()
|
|
|
|
bs, err := env.fs.ReadFile(resolvConf)
|
|
if os.IsNotExist(err) {
|
|
dbg("rc", "missing")
|
|
return newDirectManager(logf, health, bus), nil
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("reading /etc/resolv.conf: %w", err)
|
|
}
|
|
|
|
if env.rcIsResolvd(bs) {
|
|
dbg("resolvd", "yes")
|
|
return newResolvdManager(logf, interfaceName)
|
|
}
|
|
|
|
dbg("resolvd", "missing")
|
|
return newDirectManager(logf, health, bus), nil
|
|
}
|
|
|
|
func rcIsResolvd(resolvConfContents []byte) bool {
|
|
// If we have the string "# resolvd:" in resolv.conf resolvd(8) is
|
|
// managing things.
|
|
if bytes.Contains(resolvConfContents, []byte("# resolvd:")) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|