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>
Replaces the single-packet `stun` test with two predictive voice tests:
- voice-quality: 30-packet STUN burst through the SOCKS5 UDP relay.
Computes loss%, jitter (RFC-3550-ish mean abs of inter-arrival
delta), p50/p95 RTT. Three-tier gating: pass (loss≤5%, jitter≤30,
p50≤250), warn (loss≤15%, jitter≤60, p50≤400 — voice glitches but
works), fail (anything worse, including 100% loss).
- voice-srv: parallel-DNS the 16-region <region>.discord.media
hostnames, then SOCKS5 CONNECT to :443 on each through the proxy.
Catches the very common Russian-DPI failure mode where the proxy
passes generic Discord.com TCP but blocks the .discord.media voice
CIDRs — a regression all 5 prior SOCKS5 sanity checks miss.
New StatusWarn = "warn" — soft pass with Hint kept visible. Counted as
passed in summary but flagged in UI.
Config gains VoiceBurstCount (default 30), VoiceBurstInterval (default
20ms), VoiceServerHostnames (default = built-in 16-region list).
Tests cover happy path, warn-tier (10% drop), fail-tier (100% drop),
voice-srv blocked, plus standalone unit tests on
runVoiceQualityBurst and runVoiceServerProbe with a fake UDP relay
and fake SOCKS5 server. Race + cover stays at 82.4%.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Public Run(ctx, cfg) <-chan Result streams diagnostic events for the seven
tests (tcp, greet, auth?, connect, udp, stun, api) wired through the
SOCKS5 primitives, STUN codec, retry classification and RU hints.
- Per-test attempt loop with running/passed/failed events, transient-only
retries (per-attempt timeout treated as transient, parent ctx cancel as
permanent), context-aware backoff sleep.
- Connection lifecycle: tcpConn shared across greet/auth/connect (closed
and redialed on retry); separate udpConn2 control channel for UDP
ASSOCIATE kept alive for the duration of the stun test.
- STUN-via-SOCKS5: builds 10-byte SOCKS5 UDP header + STUN binding
request, decodes reply with ATYP-aware header strip (1/3/4).
- runAPI plugs SOCKS5 dial into http.Transport.DialContext; passes on
HTTP 200 OR 401.
- Skip semantics: dependency-failed tests emit single skipped result;
cancellation latches and propagates as cancelled-failed (current) +
cancelled-skipped (remaining).
- Defaults applied to a copy of cfg; UseAuth=false suppresses any "auth"
result entirely.
Tests: 10 TestRun_* covering happy/auth-rejected/all-rejected/
connect-refused/udp-unsupported/timeout-then-ok/cancelled-mid-flight/
defaults plus extractRawHex unit. Fake SOCKS5 proxy + UDP relay echoing
synthetic STUN binding success responses; httptest stub for API splice.
Combined coverage 84.3% (>=80% target). go test -race clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>