github Control-D-Inc/ctrld v1.5.1
Release v1.5.1

6 hours ago

Minor Release

This contains bug fixes and a new diagnostic command.

Added

  • Added ctrld log tail command for live log streaming — streams runtime debug logs to the terminal in real-time, similar to tail -f. Supports --lines/-n flag 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 from CreateToolhelp32Snapshot was not being closed, leaking a handle on every call

  • Fixed dnsFromResolvConf not filtering loopback IPs — the continue statement 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 to net.Dial without brackets or port, causing too many colons in address errors and immediate failure for all IPv6 VPN DNS queries

  • Fixed DNS responses failing with sendmsg: invalid argument for IPv6-sourced clients on macOS — the pf nat rule on lo0 inet6 did not match packets arriving via the rdr chain as inet4, so the client's global IPv6 source address was preserved, and the kernel rejected responses from [::1]:53 to 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-blocking OpenStream with OpenStreamSync, which waits for the server's MAX_STREAMS credit replenishment instead of racing against it. Added StreamLimitReachedError as 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.

Don't miss a new ctrld release

NewReleases is sending notifications on new releases.