Commit Graph
14 Commits
Author SHA1 Message Date
Codescribe a92d20cef8 doq: configure QUIC keep-alive and retry on idle timeout
Pass a quic.Config with KeepAlivePeriod (15s) to DoQ dial calls instead
of nil, so pooled connections send periodic QUIC PINGs to stay alive and
detect dead paths proactively.

Also add IdleTimeoutError to the DoQ retry conditions alongside io.EOF,
so stale pooled connections trigger a transparent retry instead of
propagating as a query failure.
2026-04-30 19:19:19 +07:00
Codescribe a8821e6d00 fix(darwin): support non-standard listener port in intercept mode
When port 53 is taken (e.g. by mDNSResponder), ctrld failed with
'could not find available listen ip and port' instead of falling back
to port 5354. Root cause: tryUpdateListenerConfig() checked the
dnsIntercept bool, which is derived in prog.run() AFTER listener
config is resolved.

Fix: check interceptMode string directly (CLI flag + config fallback)
in a new tryUpdateListenerConfigIntercept() that tries 127.0.0.1:53
then 127.0.0.1:5354.

Also updates buildPFAnchorRules() to use the actual listener IP/port
from config instead of hardcoded 127.0.0.1:53, so pf rules redirect
to wherever ctrld is actually listening.
2026-04-30 19:19:19 +07:00
Codescribe a3880beec2 docs: port IPv6 learnings and comment fixes to master
- Update comment in ensurePFAnchorReference: pfctl -sn returns
  rdr-anchor only (nat-anchor not used by ctrld)
- Update nat-anchor table entry in pf-dns-intercept.md
- Add pf nuances 10-16 from investigation: cross-AF redirect,
  block return, sendmsg EINVAL, nat-on-lo0, raw sockets, DIOCNATLOOK,
  and the pragmatic IPv6 block solution
2026-04-30 19:19:19 +07:00
Codescribe d7124995d2 fix: bracket IPv6 addresses in VPN DNS upstream config
upstreamConfigFor() used strings.Contains(":") to decide whether to
append ":53", which always evaluates true for IPv6 addresses. This left
bare addresses like "2a0d:6fc0:9b0:3600::1" without brackets or port,
causing net.Dial to reject with "too many colons in address".

Use net.JoinHostPort() which handles IPv6 bracketing automatically,
producing "[2a0d:6fc0:9b0:3600::1]:53".
2026-04-30 19:19:19 +07:00
Codescribe 86dafc432d Add log tail command for live log streaming
This commit adds a new `ctrld log tail` subcommand that streams
runtime debug logs to the terminal in real-time, similar to `tail -f`.

Changes:
- log_writer.go: Add Subscribe/tailLastLines for fan-out to tail clients
- control_server.go: Add /log/tail endpoint with streaming response
  - Internal logging: subscribes to logWriter for live data
  - File-based logging: polls log file for new data (200ms interval)
  - Sends last N lines as initial context on connect
- commands.go: Add `log tail` cobra subcommand with --lines/-n flag
- control_client.go: Add postStream() with no timeout for long-lived connections

Usage:
  sudo ctrld log tail          # shows last 10 lines then follows
  sudo ctrld log tail -n 50    # shows last 50 lines then follows
  Ctrl+C to stop
2026-04-30 19:19:19 +07:00
Codescribe 0f2a930cf8 feat: robust username detection and CI updates
Add platform-specific username detection for Control D metadata:
- macOS: directory services (dscl) with console user fallback
- Linux: systemd loginctl, utmp, /etc/passwd traversal
- Windows: WTS session enumeration, registry, token lookup
2026-04-30 19:19:19 +07:00
Codescribe 5a6163142c feat: add VPN DNS split routing 2026-04-30 19:19:19 +07:00
Codescribe 402771bed6 feat: add Windows NRPT and WFP DNS interception 2026-04-30 19:19:19 +07:00
Codescribe a99dcca288 feat: add macOS pf DNS interception 2026-04-30 19:19:19 +07:00
Codescribe 395335162f feat: introduce DNS intercept mode infrastructure 2026-04-30 19:19:19 +07:00
Codescribe c56d4771de docs: add DNS Intercept Mode section to README 2026-04-30 19:19:19 +07:00
Codescribe ea48186d73 Fix dnsFromResolvConf not filtering loopback IPs
The continue statement only broke out of the inner loop, so
loopback/local IPs (e.g. 127.0.0.1) were never filtered.
This caused ctrld to use itself as bootstrap DNS when already
installed as the system resolver — a self-referential loop.

Use the same isLocal flag pattern as getDNSFromScutil() and
getAllDHCPNameservers().
2026-04-30 19:19:19 +07:00
Codescribe 846aaac27a fix: include hostname hints in metadata for API-side fallback
Send all available hostname sources (ComputerName, LocalHostName,
HostName, os.Hostname) in the metadata map when provisioning.
This allows the API to detect and repair generic hostnames like
'Mac' by picking the best available source server-side.

Belt and suspenders: preferredHostname() picks the right one
client-side, but metadata gives the API a second chance.
2026-04-30 19:19:19 +07:00
Codescribe f1e49a7ee6 fix(darwin): use scutil for provisioning hostname (#485)
macOS Sequoia with Private Wi-Fi Address enabled causes os.Hostname()
to return generic names like "Mac.lan" from DHCP instead of the real
computer name. The /utility provisioning endpoint sends this raw,
resulting in devices named "Mac-lan" in the dashboard.

Fallback chain: ComputerName → LocalHostName → os.Hostname()

LocalHostName can also be affected by DHCP. ComputerName is the
user-set display name from System Settings, fully immune to network state.
2026-04-30 19:19:19 +07:00