cli: add network fallback and explicit-only Internal Domain modes

Internal Domains now support three modes. "os" is unchanged.
"resolvers" becomes explicit resolver with network fallback: the
configured resolvers are tried first, and a timeout, unreachable
resolver, SERVFAIL or NXDOMAIN hands the query to matching VPN DNS
servers, then domain-less VPN DNS servers, then the OS resolver's LAN
nameservers through the new LanOnlyQueryCtx. "resolvers_only" keeps
the previous strict behavior. Down fallback-mode resolvers are skipped
and re-checked in the background at most every 30s. Generated upstreams
are identified by the UpstreamConfig.InternalDomain marker instead of
the internal_ key prefix.

Master adaptations: the fallback state lives on proxyRequest and is
applied in tryUpstreams, shouldContinueWithNextUpstream and
handleAllUpstreamsFailure instead of v1.0's monolithic proxy loop.
Resolver construction uses master's context-aware NewResolver, and the
background re-check marks its context private so transport errors that
name the resolver stay at debug. Tests use master's zap JSON capture
and VPN manager logger.
This commit is contained in:
Dev Scribe authored and Cuong Manh Le committed 2026-10-07 14:24:00 +07:00
1 parent bfc46ef265
commit b0408e4d1c
17 files changed
+1334 -138

No files matched your search

+4 -3
View File
@@ -148,6 +148,7 @@ type prog struct {
internalLogWriter *logWriter
internalJournalWriter *logWriter
querySampler errorSampler
internalDomainProbes internalDomainProber
lastNetworkState atomic.Pointer[netmon.State]
logUpload logUploadGate
logSendMu sync.Mutex
@@ -982,8 +983,8 @@ func (p *prog) setupUpstream(cfg *ctrld.Config) {
// not independent user upstreams. Recompute eligibility on every setup/reload;
// uninstall still requires API confirmation that the device is gone.
managedUpstreams := 0
for n, uc := range cfg.Upstream {
if !isGeneratedInternalDomainUpstream(n, uc) {
for _, uc := range cfg.Upstream {
if !isGeneratedInternalDomainUpstream(uc) {
managedUpstreams++
}
}
@@ -1259,7 +1260,7 @@ func (p *prog) startNetworkJournal() func() {
close(stop)
}()
healthDone := p.health.startLoop(stop, func() (int, bool) {
return p.upstreamMonitorNow().countDownExcept(isInternalDomainUpstream), p.recoveryBypass.Load()
return p.upstreamMonitorNow().countDownExcept(p.isInternalDomainUpstream), p.recoveryBypass.Load()
})
var configDone <-chan struct{}
if p.dnsConfig != nil {