@@ -25,7 +25,6 @@ import (
25
25
"tailscale.com/net/netmon"
26
26
"tailscale.com/net/tsdial"
27
27
"tailscale.com/syncs"
28
- "tailscale.com/tstime/rate"
29
28
"tailscale.com/types/dnstype"
30
29
"tailscale.com/types/logger"
31
30
"tailscale.com/util/clientmetric"
@@ -63,10 +62,8 @@ type Manager struct {
63
62
knobs * controlknobs.Knobs // or nil
64
63
goos string // if empty, gets set to runtime.GOOS
65
64
66
- mu sync.Mutex // guards following
67
- // config is the last configuration we successfully compiled or nil if there
68
- // was any failure applying the last configuration.
69
- config * Config
65
+ mu sync.Mutex // guards following
66
+ config * Config // Tracks the last viable DNS configuration set by Set. nil on failures other than compilation failures or if set has never been called.
70
67
}
71
68
72
69
// NewManagers created a new manager from the given config.
@@ -93,22 +90,6 @@ func NewManager(logf logger.Logf, oscfg OSConfigurator, health *health.Tracker,
93
90
goos : goos ,
94
91
}
95
92
96
- // Rate limit our attempts to correct our DNS configuration.
97
- // This is done on incoming queries, we don't want to spam it.
98
- limiter := rate .NewLimiter (1.0 / 5.0 , 1 )
99
-
100
- // This will recompile the DNS config, which in turn will requery the system
101
- // DNS settings. The recovery func should triggered only when we are missing
102
- // upstream nameservers and require them to forward a query.
103
- m .resolver .SetMissingUpstreamRecovery (func () {
104
- if limiter .Allow () {
105
- m .logf ("resolution failed due to missing upstream nameservers. Recompiling DNS configuration." )
106
- if err := m .RecompileDNSConfig (); err != nil {
107
- m .logf ("config recompilation failed: %v" , err )
108
- }
109
- }
110
- })
111
-
112
93
m .ctx , m .ctxCancel = context .WithCancel (context .Background ())
113
94
m .logf ("using %T" , m .os )
114
95
return m
@@ -117,7 +98,7 @@ func NewManager(logf logger.Logf, oscfg OSConfigurator, health *health.Tracker,
117
98
// Resolver returns the Manager's DNS Resolver.
118
99
func (m * Manager ) Resolver () * resolver.Resolver { return m .resolver }
119
100
120
- // RecompileDNSConfig sets the DNS config to the current value , which has
101
+ // RecompileDNSConfig recompiles the last attempted DNS configuration , which has
121
102
// the side effect of re-querying the OS's interface nameservers. This should be used
122
103
// on platforms where the interface nameservers can change. Darwin, for example,
123
104
// where the nameservers aren't always available when we process a major interface
@@ -127,14 +108,14 @@ func (m *Manager) Resolver() *resolver.Resolver { return m.resolver }
127
108
// give a better or different result than when [Manager.Set] was last called. The
128
109
// logic for making that determination is up to the caller.
129
110
//
130
- // It returns [ErrNoDNSConfig] if the [Manager] has no existing DNS configuration .
111
+ // It returns [ErrNoDNSConfig] if [Manager.Set ] has never been called .
131
112
func (m * Manager ) RecompileDNSConfig () error {
132
113
m .mu .Lock ()
133
114
defer m .mu .Unlock ()
134
- if m .config = = nil {
135
- return ErrNoDNSConfig
115
+ if m .config ! = nil {
116
+ return m . setLocked ( * m . config )
136
117
}
137
- return m . setLocked ( * m . config )
118
+ return ErrNoDNSConfig
138
119
}
139
120
140
121
func (m * Manager ) Set (cfg Config ) error {
@@ -154,15 +135,15 @@ func (m *Manager) GetBaseConfig() (OSConfig, error) {
154
135
func (m * Manager ) setLocked (cfg Config ) error {
155
136
syncs .AssertLocked (& m .mu )
156
137
157
- // On errors, the 'set' config is cleared.
158
- m .config = nil
159
-
160
138
m .logf ("Set: %v" , logger .ArgWriter (func (w * bufio.Writer ) {
161
139
cfg .WriteToBufioWriter (w )
162
140
}))
163
141
164
142
rcfg , ocfg , err := m .compileConfig (cfg )
165
143
if err != nil {
144
+ // On a compilation failure, set m.config set for later reuse by
145
+ // [Manager.RecompileDNSConfig] and return the error.
146
+ m .config = & cfg
166
147
return err
167
148
}
168
149
@@ -174,9 +155,11 @@ func (m *Manager) setLocked(cfg Config) error {
174
155
}))
175
156
176
157
if err := m .resolver .SetConfig (rcfg ); err != nil {
158
+ m .config = nil
177
159
return err
178
160
}
179
161
if err := m .os .SetDNS (ocfg ); err != nil {
162
+ m .config = nil
180
163
m .health .SetUnhealthy (osConfigurationSetWarnable , health.Args {health .ArgError : err .Error ()})
181
164
return err
182
165
}
@@ -355,7 +338,10 @@ func (m *Manager) compileConfig(cfg Config) (rcfg resolver.Config, ocfg OSConfig
355
338
// that as the forwarder for all DNS traffic that quad-100 doesn't handle.
356
339
if isApple || ! m .os .SupportsSplitDNS () {
357
340
// If the OS can't do native split-dns, read out the underlying
358
- // resolver config and blend it into our config.
341
+ // resolver config and blend it into our config. On apple platforms, [OSConfigurator.GetBaseConfig]
342
+ // has a tendency to temporarily fail if called immediately following
343
+ // an interface change. These failures should be retried if/when the OS
344
+ // indicates that the DNS configuration has changed via [RecompileDNSConfig].
359
345
cfg , err := m .os .GetBaseConfig ()
360
346
if err == nil {
361
347
baseCfg = & cfg
0 commit comments