mirror of
https://github.com/therealaleph/MasterHttpRelayVPN-RUST.git
synced 2026-05-18 23:54:48 +03:00
v0.2.1: fix PRI/HTTP2-preface leak + shrink SNI-rewrite list
Two bug fixes surfaced in user testing:
1. Invalid HTTP methods forwarded to Apps Script
- Browser/xray sent HTTP/2 PRI preface through our MITM despite ALPN
being set to http/1.1 only (some clients ignore ALPN).
- Our parser accepted 'PRI' as a method and forwarded to Apps Script,
which rejected it: 'Exception: parameter provided with invalid value: method'.
- Fix: validate method against the standard list (GET/POST/PUT/DELETE/
HEAD/OPTIONS/PATCH/TRACE/CONNECT) at parse time. Non-matching requests
close the connection cleanly instead of forwarding garbage.
2. YouTube video playback broken by over-broad SNI-rewrite list
- Previous list included googlevideo.com, ytimg.com, doubleclick.net,
etc. -- but these are served from SEPARATE CDN pools, NOT from
Google's 216.239.38.120 frontend. Rewriting sent traffic to the
wrong backend, which Google dropped.
- Shrunk to a conservative list that's actually served from the
main Google frontend: youtube.com, youtu.be, youtube-nocookie.com,
fonts.googleapis.com. Everything else falls through to MITM+relay
(slower but actually works).
- YouTube video chunks now route through Apps Script which is slow
and quota-limited. This is a known limitation inherent to the
approach; same issue exists in the original Python version.
This commit is contained in:
Generated
+1
-1
@@ -365,7 +365,7 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mhrv-rs"
|
name = "mhrv-rs"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mhrv-rs"
|
name = "mhrv-rs"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Rust port of MasterHttpRelayVPN -- DPI bypass via Google Apps Script relay with domain fronting"
|
description = "Rust port of MasterHttpRelayVPN -- DPI bypass via Google Apps Script relay with domain fronting"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|||||||
+26
-12
@@ -14,22 +14,16 @@ use crate::config::Config;
|
|||||||
use crate::domain_fronter::DomainFronter;
|
use crate::domain_fronter::DomainFronter;
|
||||||
use crate::mitm::MitmCertManager;
|
use crate::mitm::MitmCertManager;
|
||||||
|
|
||||||
|
// Domains that are served from Google's core frontend IP pool and therefore
|
||||||
|
// respond correctly when we connect to `google_ip` with SNI=`front_domain`
|
||||||
|
// and Host=<the real domain>. Kept conservative: anything on a separate CDN
|
||||||
|
// (googlevideo, ytimg, doubleclick, etc.) is DROPPED because routing to the
|
||||||
|
// wrong backend breaks rather than helps. Those fall through to the normal
|
||||||
|
// MITM+relay path, where they'll work (slower) through Apps Script.
|
||||||
const SNI_REWRITE_SUFFIXES: &[&str] = &[
|
const SNI_REWRITE_SUFFIXES: &[&str] = &[
|
||||||
"youtube.com",
|
"youtube.com",
|
||||||
"youtu.be",
|
"youtu.be",
|
||||||
"youtube-nocookie.com",
|
"youtube-nocookie.com",
|
||||||
"youtubeeducation.com",
|
|
||||||
"googlevideo.com",
|
|
||||||
"ytimg.com",
|
|
||||||
"ggpht.com",
|
|
||||||
"gvt1.com",
|
|
||||||
"gvt2.com",
|
|
||||||
"doubleclick.net",
|
|
||||||
"googlesyndication.com",
|
|
||||||
"googleadservices.com",
|
|
||||||
"google-analytics.com",
|
|
||||||
"googletagmanager.com",
|
|
||||||
"googletagservices.com",
|
|
||||||
"fonts.googleapis.com",
|
"fonts.googleapis.com",
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -229,6 +223,11 @@ fn parse_request_head(head: &[u8]) -> Option<(String, String, String, Vec<(Strin
|
|||||||
let method = parts.next()?.to_string();
|
let method = parts.next()?.to_string();
|
||||||
let target = parts.next()?.to_string();
|
let target = parts.next()?.to_string();
|
||||||
let version = parts.next().unwrap_or("HTTP/1.1").to_string();
|
let version = parts.next().unwrap_or("HTTP/1.1").to_string();
|
||||||
|
|
||||||
|
if !is_valid_http_method(&method) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let mut headers = Vec::new();
|
let mut headers = Vec::new();
|
||||||
for l in lines {
|
for l in lines {
|
||||||
if l.is_empty() {
|
if l.is_empty() {
|
||||||
@@ -241,6 +240,21 @@ fn parse_request_head(head: &[u8]) -> Option<(String, String, String, Vec<(Strin
|
|||||||
Some((method, target, version, headers))
|
Some((method, target, version, headers))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_valid_http_method(m: &str) -> bool {
|
||||||
|
matches!(
|
||||||
|
m,
|
||||||
|
"GET"
|
||||||
|
| "POST"
|
||||||
|
| "PUT"
|
||||||
|
| "DELETE"
|
||||||
|
| "HEAD"
|
||||||
|
| "OPTIONS"
|
||||||
|
| "PATCH"
|
||||||
|
| "TRACE"
|
||||||
|
| "CONNECT"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// ---------- CONNECT handling ----------
|
// ---------- CONNECT handling ----------
|
||||||
|
|
||||||
async fn do_connect(
|
async fn do_connect(
|
||||||
|
|||||||
Reference in New Issue
Block a user