Minor Release
This contains bug fixes and a new diagnostic command.
Added
- Added
ctrld log tailcommand for live log streaming — streams runtime debug logs to the terminal in real-time, similar totail -f. Supports--lines/-nflag to control initial context lines - Added WFP loopback protect for VPN block-outside-dns in dns intercept mode.
Improved
- Configured QUIC keep-alive for DoQ connections — sends periodic PING frames (
KeepAlivePeriod: 15s) on idle pooled connections, keeping them alive across NAT rebinding and proactively detecting dead paths before the next query hits a stale connection
Fixed
-
Fixed handle leak in
hasLocalDnsServerRunning()on Windows — the process snapshot handle fromCreateToolhelp32Snapshotwas not being closed, leaking a handle on every call -
Fixed
dnsFromResolvConfnot filtering loopback IPs — thecontinuestatement only broke out of the inner loop, allowing loopback addresses (e.g. 127.0.0.1) through. This caused ctrld to use itself as bootstrap DNS when already installed as the system resolver, creating a self-referential loop -
Fixed IPv6 VPN DNS server addresses not formatted correctly on macOS —
upstreamConfigFor()passed bare IPv6 addresses tonet.Dialwithout brackets or port, causingtoo many colons in addresserrors and immediate failure for all IPv6 VPN DNS queries -
Fixed DNS responses failing with
sendmsg: invalid argumentfor IPv6-sourced clients on macOS — the pfnatrule onlo0 inet6did not match packets arriving via therdrchain as inet4, so the client's global IPv6 source address was preserved, and the kernel rejected responses from[::1]:53to non-loopback destinations -
Fixed VPN DNS queries routed over wrong source interface on macOS — when a VPN client (e.g. FortiClient) was active, DNS queries to LAN servers used the VPN tunnel IP as source, making responses unroutable. Combined with the IPv6 bugs, this cascaded into complete VPN DNS failure and VPN disconnection
-
Fixed DoQ queries failing with idle timeout errors — pooled QUIC connections that timed out server-side now trigger a transparent retry with a fresh connection instead of propagating the error
-
Fixed DoQ queries failing with
too many open streams— replaced non-blockingOpenStreamwithOpenStreamSync, which waits for the server'sMAX_STREAMScredit replenishment instead of racing against it. AddedStreamLimitReachedErroras a retry condition for defense-in-depth when the stream credit wait times outetS -
Fixed a crash in SetSelfIP triggered by network transitions before clientinfo initialization.
-
Fixed a recovery race condition, reduce worst-case recovery from ~30s to <3s.