v0.3.1: IP-literal destinations -> plain TCP passthrough (always)

User reported log spam on Windows with many 'relay failed: خطای SSL'
errors for IP-literal targets like 172.105.237.214:443. Root cause:
xray/VLESS, torrent, SSH, and other app-level clients use raw IPs in
CONNECT/SOCKS5 targets. Our previous logic would MITM these, see
'POST /' inside the xhttp wrapping, forward to Apps Script, which
would then fail SSL-verifying the app's self-signed backend.

New heuristic: if the CONNECT target is an IP literal, skip MITM
entirely and do plain TCP passthrough. Reasoning: browsers never
use raw IPs in CONNECT -- they always have a domain. Any client
using an IP literal is using a custom protocol that we have no
business MITMing.

Effect: xray/VLESS tunnels now work through mhrv-rs SOCKS5 (the
app's own TLS wrap passes through untouched). Browser HTTPS still
MITM'd + relayed as before (domain CONNECTs).

Also downgraded 'relay failed' logs from error to warn so they don't
spam the ERROR channel on misrouted traffic.
This commit is contained in:
therealaleph
2026-04-21 20:58:48 +03:00
parent a2d0cece46
commit eed64caf87
4 changed files with 25 additions and 5 deletions
+18 -1
View File
@@ -308,7 +308,17 @@ async fn dispatch_tunnel(
return do_sni_rewrite_tunnel_from_tcp(sock, &host, port, mitm, rewrite_ctx).await;
}
// 2. Peek at the first byte to detect TLS vs plain. Time-bounded — if the
// 2. IP-literal destinations are almost always app-level custom protocols
// (xray/VLESS, torrent, SSH, VPN, raw TCP). Browsers never use raw IPs
// in CONNECT. MITMing these would break the app's own TLS/auth, and
// trying to relay opaque bytes through Apps Script always fails.
// Always plain TCP passthrough for IP literals.
if is_ip_literal(&host) {
plain_tcp_passthrough(sock, &host, port).await;
return Ok(());
}
// 3. Peek at the first byte to detect TLS vs plain. Time-bounded — if the
// client doesn't send anything within 300ms, assume server-first
// protocol (SMTP, POP3, FTP banner) and jump straight to plain TCP.
let mut peek_buf = [0u8; 8];
@@ -345,6 +355,13 @@ async fn dispatch_tunnel(
Ok(())
}
// ---------- IP literal detection ----------
fn is_ip_literal(host: &str) -> bool {
let h = host.trim_start_matches('[').trim_end_matches(']');
h.parse::<std::net::IpAddr>().is_ok()
}
// ---------- Plain TCP passthrough ----------
async fn plain_tcp_passthrough(mut sock: TcpStream, host: &str, port: u16) {