diff --git a/yt_downloader/vpn.py b/yt_downloader/vpn.py index 4f8cde8..23265f9 100644 --- a/yt_downloader/vpn.py +++ b/yt_downloader/vpn.py @@ -220,7 +220,7 @@ def down(cfg) -> None: _sudo(["rm", "-rf", f"/etc/netns/{ns}"]) -def wait_for_handshake(cfg, timeout: float = 15.0) -> bool: +def wait_for_handshake(cfg, timeout: float = 20.0) -> bool: import time deadline = time.monotonic() + timeout while time.monotonic() < deadline: @@ -230,6 +230,20 @@ def wait_for_handshake(cfg, timeout: float = 15.0) -> bool: return has_handshake(cfg) +def _diagnose(cfg) -> str: + """Collect tunnel state to explain why a handshake isn't happening.""" + ns, iface = netns_name(cfg), iface_name(cfg) + parts = [] + for title, args in ( + ("wg", ["ip", "netns", "exec", ns, "wg", "show", iface]), + ("routes", ["ip", "netns", "exec", ns, "ip", "route", "show", "default"]), + ("rules", ["ip", "netns", "exec", ns, "ip", "rule", "show"]), + ): + rc, out = _sudo_out(args) + parts.append(f"--- {title} ---\n{out or '(none)'}") + return "\n".join(parts) + + def ensure_up(cfg) -> None: if has_handshake(cfg): return @@ -237,11 +251,15 @@ def ensure_up(cfg) -> None: down(cfg) # converge from any stale half-configured state up(cfg) if not wait_for_handshake(cfg): + log.error("no WireGuard handshake within timeout — tunnel state:\n%s", + _diagnose(cfg)) down(cfg) raise RuntimeError( "WireGuard tunnel came up but no handshake was established — " - "check the endpoint/reachability and that the tunnel is actually " - "active. Refusing to download over a dead tunnel." + "see tunnel state above. If 'transfer' shows sent bytes but 0 " + "received, the endpoint is unreachable (try a different Mullvad " + "server); if 0 sent, the namespace has no route out. " + "Refusing to download over a dead tunnel." )