Three follow-up fixes after the WinDivert→sing-box pivot:
1. Discord updater now routes through upstream. Previously only the
process-name rule matched, but sing-box's TUN-side process
detection on Windows mis-attributes the in-process Rust updater's
TLS connection to e.g. steam.exe — the connection went direct and
hit RKN block. Adding domain_suffix + ip_cidr rules for Cloudflare
(162.159/16, 104.16/13, 172.64/13) and Fastly (199.232/16,
151.101/16) catches updates.discord.com regardless of which PID
the kernel claims sent it. Verified via curl through mihomo:
updates.discord.com responds 400 in 393ms (i.e. TLS handshake
succeeds, only the path is wrong — proves the routing reaches it).
2. DiscordSystemHelper.exe added to TargetProcs alongside Update.exe
(modern Discord builds use it for elevated updates).
3. UDP voice quality test removed from the checker. The STUN-via-
relay burst measured private mihomo BND.ADDR (192.168.1.132)
which is unroutable from external clients, so the test reported
100% loss every time despite voice actually working through
sing-box's TUN+SOCKS5. The remaining 6 checks (TCP/greet/auth/
connect/UDP/api) cover what's actionable; voice quality is
verified empirically by joining a Discord call.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After 5+ hours of WinDivert NETWORK-layer NAT-rewrite debugging
(streamdump pattern, SOCKET-layer SYN preemption, lazy PID resolution,
UDP ASSOCIATE relay + manual reinject), Discord voice still wouldn't
connect. The fundamental issue is that WinDivert reinjected UDP
packets don't always reach connect()-bound application sockets — the
demux happens at a layer above the reinject point.
dvp/force-proxy avoids this entirely via DLL injection (above the
kernel demux). We avoid it the other way: embed sing-box, let it run
TUN inbound + per-process routing rule + SOCKS5 outbound. TUN packets
are read by sing-box from kernel as a normal flow; the application
socket sees a normal flow back. No reinject hairpin, no SYN race, no
spoofing concerns.
What this commit does:
- Drops internal/divert, internal/engine, internal/redirect,
internal/socks5, internal/procscan, plus cmd/drover/{proxy,
debugflow}_*.go subcommands (all WinDivert-only).
- Adds internal/sboxrun — embed sing-box.exe (1.12.25) + wintun.dll
(0.14.1) via //go:embed, install to %PROGRAMDATA%\Drover\sboxrun\
with SHA256 verify, generate JSON config from form, spawn as
subprocess, manage lifecycle.
- Wires sboxrun into internal/gui/app.go: StartEngine/StopEngine
now call sboxrun.Engine instead of windivert engine.
- Fixes Wails binding: StartEngine(cfg) now passes the form config
(was zero-arg, hit ProxyHost-required validation silently).
Manual test: Discord chat + voice work end-to-end through mihomo
upstream. Yandex Music / svchost / etc continue direct via
process_name routing rule.
Binary grew from 12 MB → 49 MB (37 MB sing-box embedded), but ships
fully self-contained. AV-friendly: wintun is Microsoft-signed, no
DLL injection.
WinDivert work preserved on experimental/windivert branch in case we
ever want to come back to that path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>