internal/checker+gui: remove voice-srv test (Discord doesn't expose regional voice servers via public DNS)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+5
-167
@@ -2,20 +2,11 @@ package checker
|
||||
|
||||
// voice.go — predictive voice diagnostics.
|
||||
//
|
||||
// Two primitives sit on top of the SOCKS5/STUN building blocks already
|
||||
// in this package:
|
||||
//
|
||||
// - runVoiceQualityBurst: fire a burst of STUN binding requests through
|
||||
// an open SOCKS5 UDP relay, then derive packet-loss / jitter /
|
||||
// percentile-RTT from the replies. A single round-trip says the relay
|
||||
// accepts UDP; a 30-packet burst tells you whether voice will actually
|
||||
// hold together.
|
||||
//
|
||||
// - runVoiceServerProbe: parallel-DNS a list of <region>.discord.media
|
||||
// hostnames, then SOCKS5 CONNECT to :443 on each, recording which
|
||||
// regions are reachable through the proxy. DNS-resolves-but-CONNECT-fails
|
||||
// is a very common Russian-DPI failure mode that all five preceding
|
||||
// SOCKS5 sanity checks miss.
|
||||
// runVoiceQualityBurst fires a burst of STUN binding requests through
|
||||
// an open SOCKS5 UDP relay, then derives packet-loss / jitter /
|
||||
// percentile-RTT from the replies. A single round-trip says the relay
|
||||
// accepts UDP; a 30-packet burst tells you whether voice will actually
|
||||
// hold together.
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -283,156 +274,3 @@ sendLoop:
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// VoiceServerProbeResult lists Discord voice-domain hostnames split by
|
||||
// outcome. Resolved is ordered by input position; Reachable is too.
|
||||
type VoiceServerProbeResult struct {
|
||||
Resolved []string // hostnames that got at least one A record
|
||||
Reachable []string // hostnames that succeeded SOCKS5 CONNECT
|
||||
Unresolved []string // hostnames that didn't resolve at all
|
||||
UnreachableButResolved []string // resolved but proxy CONNECT failed
|
||||
}
|
||||
|
||||
// runVoiceServerProbe resolves each hostname and, for those that resolved
|
||||
// to ≥ 1 IPv4, performs a SOCKS5 CONNECT to host:443 through proxyAddr.
|
||||
// REP=00 is enough — we don't actually do TLS.
|
||||
//
|
||||
// DNS goes through the OS resolver (not the proxy). For tests, pass
|
||||
// hostnames that always resolve locally (e.g. "localhost").
|
||||
//
|
||||
// Returns an error only when ctx is cancelled.
|
||||
func runVoiceServerProbe(
|
||||
ctx context.Context,
|
||||
hostnames []string,
|
||||
proxyAddr string,
|
||||
useAuth bool,
|
||||
login, password string,
|
||||
dialTimeout time.Duration,
|
||||
) (VoiceServerProbeResult, error) {
|
||||
if dialTimeout <= 0 {
|
||||
dialTimeout = 1 * time.Second
|
||||
}
|
||||
|
||||
const dnsConcurrency = 16
|
||||
const connectConcurrency = 8
|
||||
|
||||
// Phase 1: parallel DNS.
|
||||
type dnsOut struct {
|
||||
idx int
|
||||
host string
|
||||
resolved bool
|
||||
}
|
||||
dnsResults := make([]dnsOut, len(hostnames))
|
||||
var wg sync.WaitGroup
|
||||
dnsSem := make(chan struct{}, dnsConcurrency)
|
||||
for i, h := range hostnames {
|
||||
wg.Add(1)
|
||||
dnsSem <- struct{}{}
|
||||
go func(i int, h string) {
|
||||
defer wg.Done()
|
||||
defer func() { <-dnsSem }()
|
||||
ips, err := net.DefaultResolver.LookupIP(ctx, "ip4", h)
|
||||
ok := err == nil && len(ips) > 0
|
||||
dnsResults[i] = dnsOut{idx: i, host: h, resolved: ok}
|
||||
}(i, h)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
if cerr := ctx.Err(); cerr != nil {
|
||||
return VoiceServerProbeResult{}, cerr
|
||||
}
|
||||
|
||||
res := VoiceServerProbeResult{}
|
||||
// Build resolved/unresolved lists in input order.
|
||||
resolvedHosts := make([]string, 0, len(hostnames))
|
||||
for _, dr := range dnsResults {
|
||||
if dr.resolved {
|
||||
res.Resolved = append(res.Resolved, dr.host)
|
||||
resolvedHosts = append(resolvedHosts, dr.host)
|
||||
} else {
|
||||
res.Unresolved = append(res.Unresolved, dr.host)
|
||||
}
|
||||
}
|
||||
|
||||
if len(resolvedHosts) == 0 {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Phase 2: parallel SOCKS5 CONNECT.
|
||||
reachable := make([]bool, len(resolvedHosts))
|
||||
connectSem := make(chan struct{}, connectConcurrency)
|
||||
wg = sync.WaitGroup{}
|
||||
for i, h := range resolvedHosts {
|
||||
wg.Add(1)
|
||||
connectSem <- struct{}{}
|
||||
go func(i int, h string) {
|
||||
defer wg.Done()
|
||||
defer func() { <-connectSem }()
|
||||
reachable[i] = probeSocks5Connect(ctx, proxyAddr, useAuth, login, password, h, 443, dialTimeout)
|
||||
}(i, h)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
if cerr := ctx.Err(); cerr != nil {
|
||||
return res, cerr
|
||||
}
|
||||
for i, h := range resolvedHosts {
|
||||
if reachable[i] {
|
||||
res.Reachable = append(res.Reachable, h)
|
||||
} else {
|
||||
res.UnreachableButResolved = append(res.UnreachableButResolved, h)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// probeSocks5Connect opens one fresh TCP connection to proxyAddr, runs
|
||||
// the SOCKS5 handshake and CONNECT request to host:port, and returns
|
||||
// true iff the proxy replied REP=00. Always closes the conn before
|
||||
// returning.
|
||||
func probeSocks5Connect(
|
||||
parent context.Context,
|
||||
proxyAddr string,
|
||||
useAuth bool,
|
||||
login, password string,
|
||||
host string,
|
||||
port uint16,
|
||||
dialTimeout time.Duration,
|
||||
) bool {
|
||||
ctx, cancel := context.WithTimeout(parent, dialTimeout)
|
||||
defer cancel()
|
||||
|
||||
var d net.Dialer
|
||||
conn, err := d.DialContext(ctx, "tcp", proxyAddr)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
if _, _, gerr := socks5Greeting(ctx, conn, useAuth); gerr != nil {
|
||||
return false
|
||||
}
|
||||
if useAuth {
|
||||
if _, aerr := socks5Auth(ctx, conn, login, password); aerr != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if _, cerr := socks5Connect(ctx, conn, host, port); cerr != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// defaultVoiceHostnames is the built-in list used when
|
||||
// Config.VoiceServerHostnames is empty. Order matters — the GUI shows
|
||||
// the first three reachable regions in the metric line.
|
||||
var defaultVoiceHostnames = []string{
|
||||
"russia.discord.media", "russia2.discord.media",
|
||||
"frankfurt.discord.media", "europe.discord.media",
|
||||
"singapore.discord.media", "japan.discord.media",
|
||||
"us-east.discord.media", "us-west.discord.media",
|
||||
"brazil.discord.media", "india.discord.media",
|
||||
"hongkong.discord.media", "southkorea.discord.media",
|
||||
"sydney.discord.media", "southafrica.discord.media",
|
||||
"dubai.discord.media", "atlanta.discord.media",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user