net/dns: cache dns.Config for reuse when compileConfig fails (#16059)
fixes tailscale/corp#25612 We now keep track of any dns configurations which we could not compile. This gives RecompileDNSConfig a configuration to attempt to recompile and apply when the OS pokes us to indicate that the interface dns servers have changed/updated. The manager config will remain unset until we have the required information to compile it correctly which should eliminate the problematic SERVFAIL responses (especially on macOS 15). This also removes the missingUpstreamRecovery func in the forwarder which is no longer required now that we have proper error handling and recovery manager and the client. Signed-off-by: Jonathan Nobels <jonathan@tailscale.com>
This commit is contained in:
+54
-2
@@ -4,6 +4,7 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/netip"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -24,8 +25,9 @@ type fakeOSConfigurator struct {
|
||||
SplitDNS bool
|
||||
BaseConfig OSConfig
|
||||
|
||||
OSConfig OSConfig
|
||||
ResolverConfig resolver.Config
|
||||
OSConfig OSConfig
|
||||
ResolverConfig resolver.Config
|
||||
GetBaseConfigErr *error
|
||||
}
|
||||
|
||||
func (c *fakeOSConfigurator) SetDNS(cfg OSConfig) error {
|
||||
@@ -45,6 +47,9 @@ func (c *fakeOSConfigurator) SupportsSplitDNS() bool {
|
||||
}
|
||||
|
||||
func (c *fakeOSConfigurator) GetBaseConfig() (OSConfig, error) {
|
||||
if c.GetBaseConfigErr != nil {
|
||||
return OSConfig{}, *c.GetBaseConfigErr
|
||||
}
|
||||
return c.BaseConfig, nil
|
||||
}
|
||||
|
||||
@@ -1019,3 +1024,50 @@ func upstreams(strs ...string) (ret map[dnsname.FQDN][]*dnstype.Resolver) {
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func TestConfigRecompilation(t *testing.T) {
|
||||
fakeErr := errors.New("fake os configurator error")
|
||||
f := &fakeOSConfigurator{}
|
||||
f.GetBaseConfigErr = &fakeErr
|
||||
f.BaseConfig = OSConfig{
|
||||
Nameservers: mustIPs("1.1.1.1"),
|
||||
}
|
||||
|
||||
config := Config{
|
||||
Routes: upstreams("ts.net", "69.4.2.0", "foo.ts.net", ""),
|
||||
SearchDomains: fqdns("foo.ts.net"),
|
||||
}
|
||||
|
||||
m := NewManager(t.Logf, f, new(health.Tracker), tsdial.NewDialer(netmon.NewStatic()), nil, nil, "darwin")
|
||||
|
||||
var managerConfig *resolver.Config
|
||||
m.resolver.TestOnlySetHook(func(cfg resolver.Config) {
|
||||
managerConfig = &cfg
|
||||
})
|
||||
|
||||
// Initial set should error out and store the config
|
||||
if err := m.Set(config); err == nil {
|
||||
t.Fatalf("Want non-nil error. Got nil")
|
||||
}
|
||||
if m.config == nil {
|
||||
t.Fatalf("Want persisted config. Got nil.")
|
||||
}
|
||||
if managerConfig != nil {
|
||||
t.Fatalf("Want nil managerConfig. Got %v", managerConfig)
|
||||
}
|
||||
|
||||
// Clear the error. We should take the happy path now and
|
||||
// set m.manager's Config.
|
||||
f.GetBaseConfigErr = nil
|
||||
|
||||
// Recompilation without an error should succeed and set m.config and m.manager's [resolver.Config]
|
||||
if err := m.RecompileDNSConfig(); err != nil {
|
||||
t.Fatalf("Want nil error. Got err %v", err)
|
||||
}
|
||||
if m.config == nil {
|
||||
t.Fatalf("Want non-nil config. Got nil")
|
||||
}
|
||||
if managerConfig == nil {
|
||||
t.Fatalf("Want non nil managerConfig. Got nil")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user