dns: recovery race condition fix

Three changes to reduce worst-case recovery from ~30s to <3s:

1. debounceRecovery() for network changes (500ms window) — coalesces
   rapid consecutive network changes into a single recovery pass,
   eliminating the cancel-and-restart race.

2. ForceReBootstrap() on recovery entry — closes dead connections and
   creates fresh transports synchronously before probing, replacing
   the lazy ReBootstrap() flag that left stale connections.

3. Combined effect: recovery probes never inherit dead connections
   from a canceled prior recovery attempt.
This commit is contained in:
Codescribe
2026-04-29 04:01:04 -04:00
committed by Cuong Manh Le
parent 8cb383d87e
commit 2b27c148be
2 changed files with 51 additions and 1 deletions
+6
View File
@@ -146,6 +146,12 @@ type prog struct {
recoveryCancel context.CancelFunc
recoveryRunning atomic.Bool
// recoveryDebounceTimer coalesces rapid NetworkChange recovery triggers
// into a single handleRecovery call. Only handleRecovery is debounced —
// all other state updates (IP, pf anchor, VPN DNS) run immediately.
recoveryDebounceMu sync.Mutex
recoveryDebounceTimer *time.Timer
// recoveryBypass is set when dns-intercept mode enters recovery.
// When true, proxy() forwards all queries to OS/DHCP resolver
// instead of using the normal upstream flow.