diff --git a/client/internal/engine.go b/client/internal/engine.go index 4f3cf0998..beb2a411c 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -28,8 +28,8 @@ import ( "github.com/netbirdio/netbird/client/firewall" firewallManager "github.com/netbirdio/netbird/client/firewall/manager" "github.com/netbirdio/netbird/client/iface" - nbnetstack "github.com/netbirdio/netbird/client/iface/netstack" "github.com/netbirdio/netbird/client/iface/device" + nbnetstack "github.com/netbirdio/netbird/client/iface/netstack" "github.com/netbirdio/netbird/client/iface/udpmux" "github.com/netbirdio/netbird/client/internal/acl" "github.com/netbirdio/netbird/client/internal/debug" diff --git a/client/internal/routemanager/dnsinterceptor/handler.go b/client/internal/routemanager/dnsinterceptor/handler.go index 12c9ff4af..4bf0d5476 100644 --- a/client/internal/routemanager/dnsinterceptor/handler.go +++ b/client/internal/routemanager/dnsinterceptor/handler.go @@ -351,6 +351,11 @@ func (d *DnsInterceptor) writeMsg(w dns.ResponseWriter, r *dns.Msg, logger *log. logger.Errorf("failed to update domain prefixes: %v", err) } + // Allow time for route changes to be applied before sending + // the DNS response (relevant on iOS where setTunnelNetworkSettings + // is asynchronous). + waitForRouteSettlement(logger) + d.replaceIPsInDNSResponse(r, newPrefixes, logger) } } diff --git a/client/internal/routemanager/dnsinterceptor/handler_ios.go b/client/internal/routemanager/dnsinterceptor/handler_ios.go new file mode 100644 index 000000000..4cf80eb16 --- /dev/null +++ b/client/internal/routemanager/dnsinterceptor/handler_ios.go @@ -0,0 +1,20 @@ +//go:build ios + +package dnsinterceptor + +import ( + "time" + + log "github.com/sirupsen/logrus" +) + +const routeSettleDelay = 500 * time.Millisecond + +// waitForRouteSettlement introduces a short delay on iOS to allow +// setTunnelNetworkSettings to apply route changes before the DNS +// response reaches the application. Without this, the first request +// to a newly resolved domain may bypass the tunnel. +func waitForRouteSettlement(logger *log.Entry) { + logger.Tracef("waiting %v for iOS route settlement", routeSettleDelay) + time.Sleep(routeSettleDelay) +} diff --git a/client/internal/routemanager/dnsinterceptor/handler_nonios.go b/client/internal/routemanager/dnsinterceptor/handler_nonios.go new file mode 100644 index 000000000..68cd7330b --- /dev/null +++ b/client/internal/routemanager/dnsinterceptor/handler_nonios.go @@ -0,0 +1,12 @@ +//go:build !ios + +package dnsinterceptor + +import log "github.com/sirupsen/logrus" + +func waitForRouteSettlement(_ *log.Entry) { + // No-op on non-iOS platforms: route changes are applied synchronously by + // the kernel, so no settlement delay is needed before the DNS response + // reaches the application. The delay is only required on iOS where + // setTunnelNetworkSettings applies routes asynchronously. +}