mirror of
https://github.com/therealaleph/MasterHttpRelayVPN-RUST.git
synced 2026-05-17 21:24:48 +03:00
v0.3.0: SOCKS5 listener + smart TLS/HTTP/plain-TCP dispatch
Ports the SOCKS5 + fallback-chain design from @masterking32's
MasterHTTP-WithSOCKS branch so xray / Telegram / app-level TCP
clients work through this proxy.
Changes:
- New SOCKS5 listener on listen_port+1 (configurable via socks5_port)
- RFC 1928 CONNECT handshake (v5, no-auth, ATYP IPv4/domain/IPv6)
- Shared smart dispatch with the HTTP-CONNECT path
- Unified dispatch_tunnel() used by both CONNECT entry points:
1. If host matches SNI-rewrite suffix or hosts override: go direct
to google_ip via the MITM+TLS tunnel (fast path for google.com,
youtube, etc.)
2. Peek the first byte (300ms timeout for server-first protocols):
- 0x16: TLS client hello -> MITM + relay via Apps Script (scheme=https)
- HTTP method signature: HTTP relay via Apps Script (scheme=http)
- Anything else or timeout: plain TCP passthrough to the target
- handle_mitm_request() now takes a scheme arg (http/https) so the
same code path handles both MITM'd HTTPS and port-80 plain HTTP
- New plain_tcp_passthrough helper: bidirectional TCP bridge used as
the final fallback (covers MTProto / raw TCP / server-first protos)
Config:
- Added optional socks5_port field; defaults to listen_port+1
README:
- Added browser vs xray/Telegram instructions under 'Step 6'
Live-tested: HTTP proxy, HTTP proxy -> HTTPS, SOCKS5 -> HTTP,
SOCKS5 -> HTTPS, Google search via SNI-tunnel (now returns full
JS page) all pass.
This commit is contained in:
Generated
+1
-1
@@ -365,7 +365,7 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "mhrv-rs"
|
||||
version = "0.2.2"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mhrv-rs"
|
||||
version = "0.2.2"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
description = "Rust port of MasterHttpRelayVPN -- DPI bypass via Google Apps Script relay with domain fronting"
|
||||
license = "MIT"
|
||||
|
||||
@@ -98,14 +98,21 @@ mhrv-rs.exe --config config.json # Windows
|
||||
- **`mhrv-rs test`** — send one request through the relay and report success/timing. Useful when setting up or debugging. Does not need the proxy to be running.
|
||||
- **`mhrv-rs scan-ips`** — parallel TLS probe of known Google frontend IPs, sorted by latency. Swap the winning IP into your `google_ip` config field for best performance.
|
||||
|
||||
### Step 6: Point your browser at the proxy
|
||||
### Step 6: Point your client at the proxy
|
||||
|
||||
Configure your browser to use HTTP proxy `127.0.0.1:8085`.
|
||||
The tool listens on **two** ports:
|
||||
- **HTTP proxy** on `listen_port` (default `8085`) — for browsers / any HTTP-aware client
|
||||
- **SOCKS5 proxy** on `socks5_port` (default `listen_port + 1`, i.e. `8086`) — for xray / Telegram / app-level clients
|
||||
|
||||
- **Firefox**: Settings → Network Settings → Manual proxy → enter for HTTP, check "Also use this proxy for HTTPS"
|
||||
- **Chrome/Edge**: System proxy settings, or use SwitchyOmega extension
|
||||
**Browser (HTTP proxy):**
|
||||
- **Firefox**: Settings → Network Settings → Manual proxy → HTTP `127.0.0.1:8085`, check "Also use this proxy for HTTPS"
|
||||
- **Chrome/Edge**: System proxy settings, or SwitchyOmega
|
||||
- **macOS system-wide**: System Settings → Network → Wi-Fi → Details → Proxies → Web + Secure Web Proxy
|
||||
|
||||
**xray / Telegram (SOCKS5):**
|
||||
- Point the SOCKS5 setting at `127.0.0.1:8086`, no auth.
|
||||
- Non-HTTP protocols (MTProto, raw TCP) fall back to plain-TCP passthrough automatically.
|
||||
|
||||
## What's implemented vs not
|
||||
|
||||
This port focuses on the **`apps_script` mode** which is the only one that reliably works in 2026. Implemented:
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"auth_key": "CHANGE_ME_TO_A_STRONG_SECRET",
|
||||
"listen_host": "127.0.0.1",
|
||||
"listen_port": 8085,
|
||||
"socks5_port": 8086,
|
||||
"log_level": "info",
|
||||
"verify_ssl": true,
|
||||
"hosts": {}
|
||||
|
||||
@@ -44,6 +44,8 @@ pub struct Config {
|
||||
pub listen_host: String,
|
||||
#[serde(default = "default_listen_port")]
|
||||
pub listen_port: u16,
|
||||
#[serde(default)]
|
||||
pub socks5_port: Option<u16>,
|
||||
#[serde(default = "default_log_level")]
|
||||
pub log_level: String,
|
||||
#[serde(default = "default_verify_ssl")]
|
||||
|
||||
@@ -176,7 +176,10 @@ async fn main() -> ExitCode {
|
||||
Command::Serve => {}
|
||||
}
|
||||
|
||||
let socks5_port = config.socks5_port.unwrap_or(config.listen_port + 1);
|
||||
tracing::warn!("mhrv-rs {} starting (mode: apps_script)", VERSION);
|
||||
tracing::info!("HTTP proxy : {}:{}", config.listen_host, config.listen_port);
|
||||
tracing::info!("SOCKS5 proxy : {}:{}", config.listen_host, socks5_port);
|
||||
tracing::info!(
|
||||
"Apps Script relay: SNI={} -> script.google.com (via {})",
|
||||
config.front_domain,
|
||||
|
||||
+279
-44
@@ -63,6 +63,7 @@ pub enum ProxyError {
|
||||
pub struct ProxyServer {
|
||||
host: String,
|
||||
port: u16,
|
||||
socks5_port: u16,
|
||||
fronter: Arc<DomainFronter>,
|
||||
mitm: Arc<Mutex<MitmCertManager>>,
|
||||
rewrite_ctx: Arc<RewriteCtx>,
|
||||
@@ -101,9 +102,12 @@ impl ProxyServer {
|
||||
tls_connector,
|
||||
});
|
||||
|
||||
let socks5_port = config.socks5_port.unwrap_or(config.listen_port + 1);
|
||||
|
||||
Ok(Self {
|
||||
host: config.listen_host.clone(),
|
||||
port: config.listen_port,
|
||||
socks5_port,
|
||||
fronter: Arc::new(fronter),
|
||||
mitm,
|
||||
rewrite_ctx,
|
||||
@@ -111,19 +115,24 @@ impl ProxyServer {
|
||||
}
|
||||
|
||||
pub async fn run(self) -> Result<(), ProxyError> {
|
||||
let addr = format!("{}:{}", self.host, self.port);
|
||||
let listener = TcpListener::bind(&addr).await?;
|
||||
let http_addr = format!("{}:{}", self.host, self.port);
|
||||
let socks_addr = format!("{}:{}", self.host, self.socks5_port);
|
||||
let http_listener = TcpListener::bind(&http_addr).await?;
|
||||
let socks_listener = TcpListener::bind(&socks_addr).await?;
|
||||
tracing::warn!(
|
||||
"Listening on {} — set your browser HTTP proxy to this address.",
|
||||
addr
|
||||
"Listening HTTP on {} — set your browser HTTP proxy to this address.",
|
||||
http_addr
|
||||
);
|
||||
tracing::warn!(
|
||||
"Listening SOCKS5 on {} — xray / Telegram / app-level SOCKS5 clients use this.",
|
||||
socks_addr
|
||||
);
|
||||
|
||||
// Periodic stats log (every 60s at info level).
|
||||
let stats_fronter = self.fronter.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut ticker = tokio::time::interval(std::time::Duration::from_secs(60));
|
||||
ticker.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
|
||||
ticker.tick().await; // drop the immediate first tick
|
||||
ticker.tick().await;
|
||||
loop {
|
||||
ticker.tick().await;
|
||||
let s = stats_fronter.snapshot_stats();
|
||||
@@ -133,34 +142,65 @@ impl ProxyServer {
|
||||
}
|
||||
});
|
||||
|
||||
let http_fronter = self.fronter.clone();
|
||||
let http_mitm = self.mitm.clone();
|
||||
let http_ctx = self.rewrite_ctx.clone();
|
||||
let http_task = tokio::spawn(async move {
|
||||
loop {
|
||||
let (sock, peer) = match listener.accept().await {
|
||||
let (sock, peer) = match http_listener.accept().await {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
tracing::error!("accept error: {}", e);
|
||||
tracing::error!("accept (http): {}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let _ = sock.set_nodelay(true);
|
||||
let fronter = self.fronter.clone();
|
||||
let mitm = self.mitm.clone();
|
||||
let rewrite_ctx = self.rewrite_ctx.clone();
|
||||
let fronter = http_fronter.clone();
|
||||
let mitm = http_mitm.clone();
|
||||
let rewrite_ctx = http_ctx.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = handle_client(sock, fronter, mitm, rewrite_ctx).await {
|
||||
tracing::debug!("client {} closed: {}", peer, e);
|
||||
if let Err(e) = handle_http_client(sock, fronter, mitm, rewrite_ctx).await {
|
||||
tracing::debug!("http client {} closed: {}", peer, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let socks_fronter = self.fronter.clone();
|
||||
let socks_mitm = self.mitm.clone();
|
||||
let socks_ctx = self.rewrite_ctx.clone();
|
||||
let socks_task = tokio::spawn(async move {
|
||||
loop {
|
||||
let (sock, peer) = match socks_listener.accept().await {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
tracing::error!("accept (socks): {}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let _ = sock.set_nodelay(true);
|
||||
let fronter = socks_fronter.clone();
|
||||
let mitm = socks_mitm.clone();
|
||||
let rewrite_ctx = socks_ctx.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = handle_socks5_client(sock, fronter, mitm, rewrite_ctx).await {
|
||||
tracing::debug!("socks client {} closed: {}", peer, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let _ = tokio::join!(http_task, socks_task);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_client(
|
||||
async fn handle_http_client(
|
||||
mut sock: TcpStream,
|
||||
fronter: Arc<DomainFronter>,
|
||||
mitm: Arc<Mutex<MitmCertManager>>,
|
||||
rewrite_ctx: Arc<RewriteCtx>,
|
||||
) -> std::io::Result<()> {
|
||||
// Read the first request (head only).
|
||||
let (head, leftover) = match read_http_head(&mut sock).await? {
|
||||
Some(v) => v,
|
||||
None => return Ok(()),
|
||||
@@ -171,16 +211,195 @@ async fn handle_client(
|
||||
|
||||
if method.eq_ignore_ascii_case("CONNECT") {
|
||||
let (host, port) = parse_host_port(&target);
|
||||
if matches_sni_rewrite(&host) || hosts_override(&rewrite_ctx.hosts, &host).is_some() {
|
||||
do_sni_rewrite_connect(sock, &host, port, mitm, rewrite_ctx).await
|
||||
} else {
|
||||
do_connect(sock, &target, fronter, mitm).await
|
||||
}
|
||||
sock.write_all(b"HTTP/1.1 200 Connection Established\r\n\r\n").await?;
|
||||
sock.flush().await?;
|
||||
dispatch_tunnel(sock, host, port, fronter, mitm, rewrite_ctx).await
|
||||
} else {
|
||||
do_plain_http(sock, &head, &leftover, fronter).await
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- SOCKS5 ----------
|
||||
|
||||
async fn handle_socks5_client(
|
||||
mut sock: TcpStream,
|
||||
fronter: Arc<DomainFronter>,
|
||||
mitm: Arc<Mutex<MitmCertManager>>,
|
||||
rewrite_ctx: Arc<RewriteCtx>,
|
||||
) -> std::io::Result<()> {
|
||||
// RFC 1928 handshake: VER=5, NMETHODS, METHODS...
|
||||
let mut hdr = [0u8; 2];
|
||||
sock.read_exact(&mut hdr).await?;
|
||||
if hdr[0] != 0x05 {
|
||||
return Ok(());
|
||||
}
|
||||
let nmethods = hdr[1] as usize;
|
||||
let mut methods = vec![0u8; nmethods];
|
||||
sock.read_exact(&mut methods).await?;
|
||||
// Only "no auth" (0x00) is supported.
|
||||
if !methods.contains(&0x00) {
|
||||
sock.write_all(&[0x05, 0xff]).await?;
|
||||
return Ok(());
|
||||
}
|
||||
sock.write_all(&[0x05, 0x00]).await?;
|
||||
|
||||
// Request: VER=5, CMD, RSV=0, ATYP, DST.ADDR, DST.PORT
|
||||
let mut req = [0u8; 4];
|
||||
sock.read_exact(&mut req).await?;
|
||||
if req[0] != 0x05 {
|
||||
return Ok(());
|
||||
}
|
||||
let cmd = req[1];
|
||||
if cmd != 0x01 {
|
||||
// CONNECT only.
|
||||
sock.write_all(&[0x05, 0x07, 0x00, 0x01, 0, 0, 0, 0, 0, 0]).await?;
|
||||
return Ok(());
|
||||
}
|
||||
let atyp = req[3];
|
||||
let host: String = match atyp {
|
||||
0x01 => {
|
||||
let mut ip = [0u8; 4];
|
||||
sock.read_exact(&mut ip).await?;
|
||||
format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3])
|
||||
}
|
||||
0x03 => {
|
||||
let mut len = [0u8; 1];
|
||||
sock.read_exact(&mut len).await?;
|
||||
let mut name = vec![0u8; len[0] as usize];
|
||||
sock.read_exact(&mut name).await?;
|
||||
String::from_utf8_lossy(&name).into_owned()
|
||||
}
|
||||
0x04 => {
|
||||
let mut ip = [0u8; 16];
|
||||
sock.read_exact(&mut ip).await?;
|
||||
let addr = std::net::Ipv6Addr::from(ip);
|
||||
addr.to_string()
|
||||
}
|
||||
_ => {
|
||||
sock.write_all(&[0x05, 0x08, 0x00, 0x01, 0, 0, 0, 0, 0, 0]).await?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let mut port_buf = [0u8; 2];
|
||||
sock.read_exact(&mut port_buf).await?;
|
||||
let port = u16::from_be_bytes(port_buf);
|
||||
|
||||
tracing::info!("SOCKS5 CONNECT -> {}:{}", host, port);
|
||||
|
||||
// Success reply with zeroed BND.
|
||||
sock.write_all(&[0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0]).await?;
|
||||
sock.flush().await?;
|
||||
|
||||
dispatch_tunnel(sock, host, port, fronter, mitm, rewrite_ctx).await
|
||||
}
|
||||
|
||||
// ---------- Smart dispatch (used by both HTTP CONNECT and SOCKS5) ----------
|
||||
|
||||
async fn dispatch_tunnel(
|
||||
sock: TcpStream,
|
||||
host: String,
|
||||
port: u16,
|
||||
fronter: Arc<DomainFronter>,
|
||||
mitm: Arc<Mutex<MitmCertManager>>,
|
||||
rewrite_ctx: Arc<RewriteCtx>,
|
||||
) -> std::io::Result<()> {
|
||||
// 1. Explicit hosts override or SNI-rewrite suffix: always use the tunnel.
|
||||
if matches_sni_rewrite(&host) || hosts_override(&rewrite_ctx.hosts, &host).is_some() {
|
||||
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
|
||||
// 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];
|
||||
let peek_n = match tokio::time::timeout(
|
||||
std::time::Duration::from_millis(300),
|
||||
sock.peek(&mut peek_buf),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(n)) => n,
|
||||
Ok(Err(_)) => return Ok(()),
|
||||
Err(_) => {
|
||||
// Client silent: likely a server-first protocol.
|
||||
plain_tcp_passthrough(sock, &host, port).await;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
if peek_n >= 1 && peek_buf[0] == 0x16 {
|
||||
// Looks like TLS: MITM + relay via Apps Script.
|
||||
run_mitm_then_relay(sock, &host, port, mitm, &fronter).await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// 3. Not TLS. If bytes look like HTTP, relay on scheme=http. Otherwise
|
||||
// fall back to plain TCP passthrough.
|
||||
if peek_n > 0 && looks_like_http(&peek_buf[..peek_n]) {
|
||||
let scheme = if port == 443 { "https" } else { "http" };
|
||||
relay_http_stream_raw(sock, &host, port, scheme, &fronter).await;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
plain_tcp_passthrough(sock, &host, port).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ---------- Plain TCP passthrough ----------
|
||||
|
||||
async fn plain_tcp_passthrough(mut sock: TcpStream, host: &str, port: u16) {
|
||||
let target_host = host.trim_start_matches('[').trim_end_matches(']');
|
||||
let upstream = match tokio::time::timeout(
|
||||
std::time::Duration::from_secs(10),
|
||||
TcpStream::connect((target_host, port)),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Ok(s)) => s,
|
||||
Ok(Err(e)) => {
|
||||
tracing::debug!("plain-tcp connect {}:{} failed: {}", host, port, e);
|
||||
return;
|
||||
}
|
||||
Err(_) => {
|
||||
tracing::debug!("plain-tcp connect {}:{} timeout", host, port);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let _ = upstream.set_nodelay(true);
|
||||
tracing::info!("plain-tcp passthrough -> {}:{}", host, port);
|
||||
let (mut ar, mut aw) = sock.split();
|
||||
let (mut br, mut bw) = {
|
||||
let (r, w) = upstream.into_split();
|
||||
(r, w)
|
||||
};
|
||||
let t1 = tokio::io::copy(&mut ar, &mut bw);
|
||||
let t2 = tokio::io::copy(&mut br, &mut aw);
|
||||
tokio::select! {
|
||||
_ = t1 => {}
|
||||
_ = t2 => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn looks_like_http(first_bytes: &[u8]) -> bool {
|
||||
// Cheap sniff: must start with an ASCII HTTP method token followed by a space.
|
||||
for m in [
|
||||
"GET ",
|
||||
"POST ",
|
||||
"PUT ",
|
||||
"HEAD ",
|
||||
"DELETE ",
|
||||
"PATCH ",
|
||||
"OPTIONS ",
|
||||
"CONNECT ",
|
||||
"TRACE ",
|
||||
] {
|
||||
if first_bytes.starts_with(m.as_bytes()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Read an HTTP head (request line + headers) up to the first \r\n\r\n.
|
||||
/// Returns (head_bytes, leftover_after_head). The leftover may contain part
|
||||
/// of the request body already received.
|
||||
@@ -260,26 +479,22 @@ fn is_valid_http_method(m: &str) -> bool {
|
||||
|
||||
// ---------- CONNECT handling ----------
|
||||
|
||||
async fn do_connect(
|
||||
mut sock: TcpStream,
|
||||
target: &str,
|
||||
fronter: Arc<DomainFronter>,
|
||||
async fn run_mitm_then_relay(
|
||||
sock: TcpStream,
|
||||
host: &str,
|
||||
port: u16,
|
||||
mitm: Arc<Mutex<MitmCertManager>>,
|
||||
) -> std::io::Result<()> {
|
||||
let (host, port) = parse_host_port(target);
|
||||
tracing::info!("CONNECT -> {}:{}", host, port);
|
||||
fronter: &DomainFronter,
|
||||
) {
|
||||
tracing::info!("MITM TLS -> {}:{}", host, port);
|
||||
|
||||
sock.write_all(b"HTTP/1.1 200 Connection Established\r\n\r\n").await?;
|
||||
sock.flush().await?;
|
||||
|
||||
// MITM: build a server config for this domain and accept TLS.
|
||||
let server_config = {
|
||||
let mut m = mitm.lock().await;
|
||||
match m.get_server_config(&host) {
|
||||
match m.get_server_config(host) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
tracing::error!("cert gen failed for {}: {}", host, e);
|
||||
return Ok(());
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -289,13 +504,14 @@ async fn do_connect(
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
tracing::debug!("TLS accept failed for {}: {}", host, e);
|
||||
return Ok(());
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Keep-alive loop: read HTTP requests from the decrypted stream.
|
||||
// scheme=https because we MITM-terminated TLS.
|
||||
loop {
|
||||
match handle_mitm_request(&mut tls, &host, port, &fronter).await {
|
||||
match handle_mitm_request(&mut tls, host, port, fronter, "https").await {
|
||||
Ok(true) => continue,
|
||||
Ok(false) => break,
|
||||
Err(e) => {
|
||||
@@ -304,19 +520,36 @@ async fn do_connect(
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn do_sni_rewrite_connect(
|
||||
// ---------- Plain HTTP relay on a raw TCP stream (port 80 targets) ----------
|
||||
|
||||
async fn relay_http_stream_raw(
|
||||
mut sock: TcpStream,
|
||||
host: &str,
|
||||
port: u16,
|
||||
scheme: &str,
|
||||
fronter: &DomainFronter,
|
||||
) {
|
||||
loop {
|
||||
match handle_mitm_request(&mut sock, host, port, fronter, scheme).await {
|
||||
Ok(true) => continue,
|
||||
Ok(false) => break,
|
||||
Err(e) => {
|
||||
tracing::debug!("http relay error for {}: {}", host, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn do_sni_rewrite_tunnel_from_tcp(
|
||||
sock: TcpStream,
|
||||
host: &str,
|
||||
port: u16,
|
||||
mitm: Arc<Mutex<MitmCertManager>>,
|
||||
rewrite_ctx: Arc<RewriteCtx>,
|
||||
) -> std::io::Result<()> {
|
||||
sock.write_all(b"HTTP/1.1 200 Connection Established\r\n\r\n").await?;
|
||||
sock.flush().await?;
|
||||
|
||||
let target_ip = hosts_override(&rewrite_ctx.hosts, host)
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|| rewrite_ctx.google_ip.clone());
|
||||
@@ -457,6 +690,7 @@ async fn handle_mitm_request<S>(
|
||||
host: &str,
|
||||
port: u16,
|
||||
fronter: &DomainFronter,
|
||||
scheme: &str,
|
||||
) -> std::io::Result<bool>
|
||||
where
|
||||
S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin,
|
||||
@@ -474,13 +708,14 @@ where
|
||||
// Read body if content-length is set.
|
||||
let body = read_body(stream, &leftover, &headers).await?;
|
||||
|
||||
let url = if port == 443 {
|
||||
format!("https://{}{}", host, path)
|
||||
let default_port = if scheme == "https" { 443 } else { 80 };
|
||||
let url = if port == default_port {
|
||||
format!("{}://{}{}", scheme, host, path)
|
||||
} else {
|
||||
format!("https://{}:{}{}", host, port, path)
|
||||
format!("{}://{}:{}{}", scheme, host, port, path)
|
||||
};
|
||||
|
||||
tracing::info!("MITM {} {}", method, url);
|
||||
tracing::info!("relay {} {}", method, url);
|
||||
|
||||
let response = fronter.relay(&method, &url, &headers, &body).await;
|
||||
stream.write_all(&response).await?;
|
||||
|
||||
Reference in New Issue
Block a user