diff --git a/cmd/cli/prog.go b/cmd/cli/prog.go index c745d79..00b8c0d 100644 --- a/cmd/cli/prog.go +++ b/cmd/cli/prog.go @@ -40,7 +40,7 @@ const ( upstreamPrefix = "upstream." upstreamOS = upstreamPrefix + "os" upstreamPrivate = upstreamPrefix + "private" - dnsWatchdogInterval = time.Minute + dnsWatchdogDefaultInterval = 20 * time.Second ) // ControlSocketName returns name for control unix socket. @@ -532,7 +532,27 @@ func (p *prog) setDNS() { return setDnsIgnoreUnusableInterface(i, nameservers) }) } - go p.dnsWatchdog(netIface, nameservers, allIfaces) + if p.dnsWatchdogEnabled() { + go p.dnsWatchdog(netIface, nameservers, allIfaces) + } +} + +// dnsWatchdogEnabled reports whether DNS watchdog is enabled. +func (p *prog) dnsWatchdogEnabled() bool { + if ptr := p.cfg.Service.DnsWatchdogEnabled; ptr != nil { + return *ptr + } + return true +} + +// dnsWatchdogDuration returns the time duration between each DNS watchdog loop. +func (p *prog) dnsWatchdogDuration() time.Duration { + if ptr := p.cfg.Service.DnsWatchdogInvterval; ptr != nil { + if (*ptr).Seconds() > 0 { + return *ptr + } + } + return dnsWatchdogDefaultInterval } // dnsWatchdog watches for DNS changes on Darwin and Windows then re-applying ctrld's settings. @@ -546,7 +566,7 @@ func (p *prog) dnsWatchdog(iface *net.Interface, nameservers []string, allIfaces mainLog.Load().Debug().Msg("start DNS settings watchdog") ns := nameservers slices.Sort(ns) - ticker := time.NewTicker(dnsWatchdogInterval) + ticker := time.NewTicker(p.dnsWatchdogDuration()) logger := mainLog.Load().With().Str("iface", iface.Name).Logger() for range ticker.C { if dnsChanged(iface, ns) { diff --git a/cmd/cli/prog_test.go b/cmd/cli/prog_test.go new file mode 100644 index 0000000..5f2f8e1 --- /dev/null +++ b/cmd/cli/prog_test.go @@ -0,0 +1,57 @@ +package cli + +import ( + "testing" + "time" + + "github.com/Control-D-Inc/ctrld" + "github.com/stretchr/testify/assert" +) + +func Test_prog_dnsWatchdogEnabled(t *testing.T) { + p := &prog{cfg: &ctrld.Config{}} + + // Default value is true. + assert.True(t, p.dnsWatchdogEnabled()) + + tests := []struct { + name string + enabled bool + }{ + {"enabled", true}, + {"disabled", false}, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + p.cfg.Service.DnsWatchdogEnabled = &tc.enabled + assert.Equal(t, tc.enabled, p.dnsWatchdogEnabled()) + }) + } +} + +func Test_prog_dnsWatchdogInterval(t *testing.T) { + p := &prog{cfg: &ctrld.Config{}} + + // Default value is 20s. + assert.Equal(t, dnsWatchdogDefaultInterval, p.dnsWatchdogDuration()) + + tests := []struct { + name string + duration time.Duration + expected time.Duration + }{ + {"valid", time.Minute, time.Minute}, + {"zero", 0, dnsWatchdogDefaultInterval}, + {"nagative", time.Duration(-1 * time.Minute), dnsWatchdogDefaultInterval}, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + p.cfg.Service.DnsWatchdogInvterval = &tc.duration + assert.Equal(t, tc.expected, p.dnsWatchdogDuration()) + }) + } +} diff --git a/config.go b/config.go index ae9f75b..5c531cd 100644 --- a/config.go +++ b/config.go @@ -188,27 +188,29 @@ func (c *Config) FirstUpstream() *UpstreamConfig { // ServiceConfig specifies the general ctrld config. type ServiceConfig struct { - LogLevel string `mapstructure:"log_level" toml:"log_level,omitempty"` - LogPath string `mapstructure:"log_path" toml:"log_path,omitempty"` - CacheEnable bool `mapstructure:"cache_enable" toml:"cache_enable,omitempty"` - CacheSize int `mapstructure:"cache_size" toml:"cache_size,omitempty"` - CacheTTLOverride int `mapstructure:"cache_ttl_override" toml:"cache_ttl_override,omitempty"` - CacheServeStale bool `mapstructure:"cache_serve_stale" toml:"cache_serve_stale,omitempty"` - CacheFlushDomains []string `mapstructure:"cache_flush_domains" toml:"cache_flush_domains" validate:"max=256"` - MaxConcurrentRequests *int `mapstructure:"max_concurrent_requests" toml:"max_concurrent_requests,omitempty" validate:"omitempty,gte=0"` - DHCPLeaseFile string `mapstructure:"dhcp_lease_file_path" toml:"dhcp_lease_file_path" validate:"omitempty,file"` - DHCPLeaseFileFormat string `mapstructure:"dhcp_lease_file_format" toml:"dhcp_lease_file_format" validate:"required_unless=DHCPLeaseFile '',omitempty,oneof=dnsmasq isc-dhcp"` - DiscoverMDNS *bool `mapstructure:"discover_mdns" toml:"discover_mdns,omitempty"` - DiscoverARP *bool `mapstructure:"discover_arp" toml:"discover_arp,omitempty"` - DiscoverDHCP *bool `mapstructure:"discover_dhcp" toml:"discover_dhcp,omitempty"` - DiscoverPtr *bool `mapstructure:"discover_ptr" toml:"discover_ptr,omitempty"` - DiscoverHosts *bool `mapstructure:"discover_hosts" toml:"discover_hosts,omitempty"` - DiscoverRefreshInterval int `mapstructure:"discover_refresh_interval" toml:"discover_refresh_interval,omitempty"` - ClientIDPref string `mapstructure:"client_id_preference" toml:"client_id_preference,omitempty" validate:"omitempty,oneof=host mac"` - MetricsQueryStats bool `mapstructure:"metrics_query_stats" toml:"metrics_query_stats,omitempty"` - MetricsListener string `mapstructure:"metrics_listener" toml:"metrics_listener,omitempty"` - Daemon bool `mapstructure:"-" toml:"-"` - AllocateIP bool `mapstructure:"-" toml:"-"` + LogLevel string `mapstructure:"log_level" toml:"log_level,omitempty"` + LogPath string `mapstructure:"log_path" toml:"log_path,omitempty"` + CacheEnable bool `mapstructure:"cache_enable" toml:"cache_enable,omitempty"` + CacheSize int `mapstructure:"cache_size" toml:"cache_size,omitempty"` + CacheTTLOverride int `mapstructure:"cache_ttl_override" toml:"cache_ttl_override,omitempty"` + CacheServeStale bool `mapstructure:"cache_serve_stale" toml:"cache_serve_stale,omitempty"` + CacheFlushDomains []string `mapstructure:"cache_flush_domains" toml:"cache_flush_domains" validate:"max=256"` + MaxConcurrentRequests *int `mapstructure:"max_concurrent_requests" toml:"max_concurrent_requests,omitempty" validate:"omitempty,gte=0"` + DHCPLeaseFile string `mapstructure:"dhcp_lease_file_path" toml:"dhcp_lease_file_path" validate:"omitempty,file"` + DHCPLeaseFileFormat string `mapstructure:"dhcp_lease_file_format" toml:"dhcp_lease_file_format" validate:"required_unless=DHCPLeaseFile '',omitempty,oneof=dnsmasq isc-dhcp"` + DiscoverMDNS *bool `mapstructure:"discover_mdns" toml:"discover_mdns,omitempty"` + DiscoverARP *bool `mapstructure:"discover_arp" toml:"discover_arp,omitempty"` + DiscoverDHCP *bool `mapstructure:"discover_dhcp" toml:"discover_dhcp,omitempty"` + DiscoverPtr *bool `mapstructure:"discover_ptr" toml:"discover_ptr,omitempty"` + DiscoverHosts *bool `mapstructure:"discover_hosts" toml:"discover_hosts,omitempty"` + DiscoverRefreshInterval int `mapstructure:"discover_refresh_interval" toml:"discover_refresh_interval,omitempty"` + ClientIDPref string `mapstructure:"client_id_preference" toml:"client_id_preference,omitempty" validate:"omitempty,oneof=host mac"` + MetricsQueryStats bool `mapstructure:"metrics_query_stats" toml:"metrics_query_stats,omitempty"` + MetricsListener string `mapstructure:"metrics_listener" toml:"metrics_listener,omitempty"` + DnsWatchdogEnabled *bool `mapstructure:"dns_watchdog_enabled" toml:"dns_watchdog_enabled,omitempty"` + DnsWatchdogInvterval *time.Duration `mapstructure:"dns_watchdog_interval" toml:"dns_watchdog_interval,omitempty"` + Daemon bool `mapstructure:"-" toml:"-"` + AllocateIP bool `mapstructure:"-" toml:"-"` } // NetworkConfig specifies configuration for networks where ctrld will handle requests. diff --git a/config_test.go b/config_test.go index 83a1e13..03a1a3f 100644 --- a/config_test.go +++ b/config_test.go @@ -5,6 +5,7 @@ import ( "os" "strings" "testing" + "time" "github.com/go-playground/validator/v10" "github.com/spf13/viper" @@ -22,6 +23,8 @@ func TestLoadConfig(t *testing.T) { assert.Equal(t, "info", cfg.Service.LogLevel) assert.Equal(t, "/path/to/log.log", cfg.Service.LogPath) + assert.Equal(t, false, *cfg.Service.DnsWatchdogEnabled) + assert.Equal(t, time.Duration(20*time.Second), *cfg.Service.DnsWatchdogInvterval) assert.Len(t, cfg.Network, 2) assert.Contains(t, cfg.Network, "0") diff --git a/docs/config.md b/docs/config.md index 5615d30..1ae4978 100644 --- a/docs/config.md +++ b/docs/config.md @@ -252,6 +252,27 @@ Specifying the `ip` and `port` of the Prometheus metrics server. The Prometheus - Required: no - Default: "" +### dns_watchdog_enabled +Checking DNS changes to network interfaces and reverting to ctrld's own settings. + +The DNS watchdog process only runs on Windows and MacOS. + +- Type: boolean +- Required: no +- Default: true + +### dns_watchdog_interval +Time duration between each DNS watchdog iteration. + +A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, +such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + +If the time duration is non-positive, default value will be used. + +- Type: time duration string +- Required: no +- Default: 20s + ## Upstream The `[upstream]` section specifies the DNS upstream servers that `ctrld` will forward DNS requests to. diff --git a/testhelper/config.go b/testhelper/config.go index 6199424..a39ac62 100644 --- a/testhelper/config.go +++ b/testhelper/config.go @@ -27,6 +27,8 @@ var sampleConfigContent = ` [service] log_level = "info" log_path = "/path/to/log.log" +dns_watchdog_enabled = false +dns_watchdog_interval = "20s" [network.0] name = "Home Wifi"