mirror of
https://github.com/therealaleph/MasterHttpRelayVPN-RUST.git
synced 2026-05-18 05:44:35 +03:00
feat: v1.9.0 — multi-edge fronting + edge DNS cache + DoH default flip + hotspot sharing
Three substantive PRs landed for this release plus an Iran-safe DoH default: - #488 by @dazzling-no-more (with credit to @patterniha): fronting_groups config field generalizes the Google-edge SNI-rewrite trick to any multi-tenant CDN edge (Vercel, Fastly, etc.). Renames `mode = "google_only"` → `mode = "direct"` with a deprecated alias keeping existing configs working. This is the v1.9.0 headline — new top-level config field + public mode-string rename are minor-bump territory. xmux moves to v1.10.0. - #494 by @dazzling-no-more: edge-cache DNS at Apps Script (CodeFull.gs) using CacheService. udp_open / port=53 ops served from cache or DoH fallback chain (Cloudflare → Google → Quad9). Cache hits drop typical first-hop DNS latency from 600-1200ms to 200-400ms. Default-on, opt-out via ENABLE_EDGE_DNS_CACHE; every failure mode falls through to existing tunnel-node forward path (zero regression). - #483 by @yyoyoian-pixel: default listen_host from 127.0.0.1 to 0.0.0.0 so an Android phone running the tunnel + an iPhone/laptop on the same hotspot can use it as proxy. Old configs with explicit 127.0.0.1 are honored (not overwritten). Plus: default `tunnel_doh: true` (flipped from false in v1.8.x) per #468 — Iran ISPs filter direct connections to dns.google, chrome.cloudflare-dns.com, and other pinned DoH hosts. The bypass-on default silently broke DNS for the dominant Iranian userbase. The safe default keeps DoH inside the tunnel; non-Iran users can opt back into the bypass for the latency win. Backwards-compatible — any config with explicit tunnel_doh keeps its setting. 169 mhrv-rs lib tests + 33 tunnel-node tests + 11 edge-DNS JS tests all passing. Clean release + UI builds.
This commit is contained in:
Generated
+1
-1
@@ -2222,7 +2222,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mhrv-rs"
|
||||
version = "1.8.5"
|
||||
version = "1.9.0"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mhrv-rs"
|
||||
version = "1.8.5"
|
||||
version = "1.9.0"
|
||||
edition = "2021"
|
||||
description = "Rust port of MasterHttpRelayVPN -- DPI bypass via Google Apps Script relay with domain fronting"
|
||||
license = "MIT"
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<!-- see docs/changelog/v1.1.0.md for the file format: Persian, then `---`, then English. -->
|
||||
• **شکستگی سازگاری minor: نامگذاری `mode = "google_only"` به `mode = "direct"` تغییر کرد** (PR [#488](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/pull/488) از @dazzling-no-more): نام قدیمی توصیف وضعیت رو بعد از اضافه شدن fronting_groups (که فراتر از Google میرسه) درست نمیداد. در Rust + Android + UI dropdown همه به `direct` تغییر کردهاند، ولی **`google_only` بهعنوان alias deprecated در parser قابل قبول مونده** — configها و saved settings قدیمی نمیشکنن. در Save بعدی، on-disk file خودکار به `direct` migrate میشه. در docs (README EN/FA, SF_README EN/FA, tunnel-node FA) note "تا قبل v1.9 نام `google_only` بود — هنوز کار میکنه" گذاشته شده برای کاربرانی که از راهنماهای قدیمی یا پستهای Telegram قدیمی استفاده میکنن.
|
||||
• fronting_groups: domain fronting چند-edge برای CDN غیر-Google (Vercel، Fastly، …) (PR [#488](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/pull/488) از @dazzling-no-more، با credit به [@patterniha/MITM-DomainFronting](https://github.com/patterniha/MITM-DomainFronting) برای technique اصلی): فیلد جدید config `fronting_groups: [{name, ip, sni, domains}]`. هر group شامل (edge IP، front SNI، domainهای member). وقتی CONNECT به یکی از domainهای member میرسه، proxy MITM میکنه + upstream با `ip` بهعنوان TCP destination + `sni` بهعنوان TLS SNI re-encrypt میکنه — همان trick که برای `google_ip` + `front_domain` میکنیم، حالا قابل تنظیم برای هر CDN multi-tenant. بر روی Google fronting (built-in) برتری داره؛ زیر `passthrough_hosts` و DoH bypass قرار داره. در `mode = full` غیر فعال (که end-to-end TLS رو حفظ میکنه + MITM نمیکنه). config مثال: `config.fronting-groups.example.json`. doc کامل: `docs/fronting-groups.md` شامل recipe انتخاب `(ip, sni)`، routing precedence، و warning صریح ⚠️ درباره cross-tenant Host-header leak failure mode (هرگز domainهایی که واقعاً روی edge نیستند رو list نکنید). reviews folded: SNI اعتبار رستورد روی config-load gate، `Vec<Arc<>>` بهجای clone-on-match، byte-level dot-anchored matcher، startup warnings برای inert combos.
|
||||
• edge-cache DNS در CodeFull.gs برای skip کردن round-trip tunnel-node (PR [#494](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/pull/494) از @dazzling-no-more): `udp_open` ops با port=53 در `_doTunnelBatch` intercept میشن + از `CacheService` (cache hit) یا DoH (cache miss) سرو میشن. cache hitها latency typical first-hop DNS رو از ~۶۰۰-۱۲۰۰ms به ~۲۰۰-۴۰۰ms پایین میآرن. تغییر pure server-side در CodeFull.gs (فقط Full mode — apps_script mode UDP path نداره). بدون تغییر Rust/client. DoH fallback chain: Cloudflare → Google → Quad9 روی RFC 8484 GET. cache key per-qtype برای جلوگیری از A/AAAA collision. TTL clamping در `[30s, 6h]`. NXDOMAIN/SERVFAIL با ۴۵s negative cache. NODATA-with-SOA بر اساس RFC 2308 §5 SOA TTL رو honor میکنه. default-on، opt-out با `ENABLE_EDGE_DNS_CACHE`. هر failure mode به path forward موجود tunnel-node fallback میکنه (zero regression). انتخاب CacheService بر روی Sheets به دلیل سرعت (~۱۰ms) + privacy (volatile، روی Drive log persist نمیکنه — برای کاربران در صحنههای censorship مهمه). ۱۱ تست pure-JS pass.
|
||||
• default `tunnel_doh: true` (flipped از `false` در v1.8.x) ([#468](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/468)): default قبلی (DoH bypass فعال) برای کاربران ایرانی بدون نشان دادن چیزی شکست میخورد چون Iran ISP direct connection به `dns.google`، `chrome.cloudflare-dns.com` و سایر pinned DoH hosts رو filter میکنن — همان hosts که bypass در حال route مستقیم میفرستاد. در نتیجه، DNS lookupها fail میگرفتن + browsing شکست میخورد. حالا default safe است (DoH داخل tunnel نگه داشته میشه، در یک شبکه فیلتر شده کار میکنه). کاربری روی شبکههایی که direct DoH کار میکنه (non-Iran)، میتونه `tunnel_doh: false` در config بگذاره برای latency win. تغییر backwards-compatible برای configs موجود — همهی configs دارای فیلد explicit `tunnel_doh` رفتار حفظ میشن.
|
||||
• اشتراکگذاری Hotspot iOS/laptop (PR [#483](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/pull/483) از @yyoyoian-pixel): default `listen_host` از `127.0.0.1` به `0.0.0.0` تغییر کرده. این workflow معمول رو enable میکنه — یک phone Android که tunnel run میکنه، iPhone یا laptop روی همان hotspot WiFi میتونه از proxy استفاده کنه. configs قدیمی با explicit `listen_host: "127.0.0.1"` honor میشن (بازنویسی نمیشن).
|
||||
---
|
||||
• **Minor breaking: `mode = "google_only"` renamed to `mode = "direct"`** (PR [#488](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/pull/488) by @dazzling-no-more): the old name no longer described the mode now that `fronting_groups` reaches more than Google. Rust + Android + UI dropdown all updated, but **`google_only` is preserved as a deprecated alias on parse** — existing configs and saved settings don't break. On the next Save, the on-disk file migrates automatically to `direct`. Docs (README EN+FA, SF_README EN+FA, tunnel-node FA) carry a "was named `google_only` before v1.9 — old name still works" note so users following older guides / Telegram posts find their way.
|
||||
• `fronting_groups`: multi-edge domain fronting for non-Google CDNs (PR [#488](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/pull/488) by @dazzling-no-more, credit to [@patterniha/MITM-DomainFronting](https://github.com/patterniha/MITM-DomainFronting) for the original technique): new config field `fronting_groups: [{name, ip, sni, domains}]`. Each group is `(edge IP, front SNI, member domains)`: when a CONNECT to one of the member domains arrives, the proxy MITMs at the local CA, then re-encrypts upstream against `ip` with `sni` as the TLS SNI — same trick we already do for `google_ip` + `front_domain`, now configurable for any multi-tenant CDN edge (Vercel, Fastly, etc.). Wins over the built-in Google SNI-rewrite suffix list; loses to `passthrough_hosts` and DoH bypass. Skipped in `mode = full` (which preserves end-to-end TLS and can't MITM). Working example at `config.fronting-groups.example.json`. Full doc at `docs/fronting-groups.md` including the recipe for picking `(ip, sni)`, routing precedence, and an explicit ⚠️ warning about the cross-tenant Host-header leak failure mode (don't list domains that aren't actually on the edge). Review fixes folded: SNI validated via rustls at config-load gate; `Vec<Arc<>>` refcount on per-CONNECT match; byte-level dot-anchored matcher (no per-match `format!()`); startup warnings for inert combos.
|
||||
• Edge-cache DNS in `CodeFull.gs` to skip the tunnel-node round-trip (PR [#494](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/pull/494) by @dazzling-no-more): intercepts `udp_open` / port=53 ops in `_doTunnelBatch` and serves them from `CacheService` (cache hit) or DoH (cache miss). Cache hits drop typical first-hop DNS latency from ~600-1200ms to ~200-400ms. Pure server-side change in `CodeFull.gs` (Full mode only — apps_script mode has no UDP path); zero Rust/client changes. DoH fallback chain: Cloudflare → Google → Quad9 over RFC 8484 GET. Per-qtype cache key keeps A and AAAA from colliding. Min RR TTL clamped to `[30s, 6h]`; NXDOMAIN/SERVFAIL get a 45s negative cache; NODATA-with-SOA honors the SOA TTL per RFC 2308 §5. Default-on, opt-out via `ENABLE_EDGE_DNS_CACHE`. Every failure mode (parse error, resolver outage, key-too-long, `cache.put` rejection) falls through to the existing tunnel-node forward path — zero regression on any failure. CacheService chosen over Sheets (#443's pattern) because Sheets reads/writes are 100-500ms per op (often slower than the DoH lookup we'd be caching), have a daily-quota hazard, and persist a Drive-listed log of every domain users resolve — a real privacy regression for users in censorship contexts. CacheService is ~10ms, volatile, free, no on-disk artifact. 11 pure-JS tests covering parsers, txid non-mutation, TTL clamp, NXDOMAIN-with-SOA TTL extraction, malformed/truncated input rejection, splice correctness for mixed batches.
|
||||
• Default `tunnel_doh: true` (flipped from `false` in v1.8.x) ([#468](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/468)): the previous default (DoH bypass active) silently broke for Iranian users because Iran ISPs filter direct connections to `dns.google`, `chrome.cloudflare-dns.com`, and other pinned DoH hosts — exactly the hosts the bypass was routing direct. DNS resolution failed and browsing broke. The safer default keeps DoH inside the tunnel; users on networks where direct DoH works can opt back into the bypass with `tunnel_doh: false`. Backwards-compatible for existing configs — anyone who explicitly set `tunnel_doh` keeps their behavior. Iranian users on pre-v1.8.6 versions hitting this regression should upgrade.
|
||||
• Hotspot sharing for iOS / laptop (PR [#483](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/pull/483) by @yyoyoian-pixel): default `listen_host` changed from `127.0.0.1` to `0.0.0.0`. Enables the common workflow where an Android phone runs the tunnel and an iPhone/iPad/laptop on the same hotspot uses it as a proxy (HTTP `192.168.43.1:8080` or SOCKS5 `:1081`). For full device-wide coverage on iOS, Shadowrocket or Potatso create a local VPN that routes all traffic through the SOCKS5 on the Android phone. Old configs with explicit `"listen_host": "127.0.0.1"` are honored (not overwritten).
|
||||
+1
-1
@@ -398,7 +398,7 @@ fn load_form() -> (FormState, Option<String>) {
|
||||
passthrough_hosts: Vec::new(),
|
||||
block_quic: false,
|
||||
disable_padding: false,
|
||||
tunnel_doh: false,
|
||||
tunnel_doh: true,
|
||||
bypass_doh_hosts: Vec::new(),
|
||||
fronting_groups: Vec::new(),
|
||||
}
|
||||
|
||||
+20
-2
@@ -232,15 +232,25 @@ pub struct Config {
|
||||
/// round-trip cost paid on every name lookup. In Full mode this
|
||||
/// was the dominant DNS slowdown source.
|
||||
///
|
||||
/// Set `tunnel_doh: true` to keep DoH inside the tunnel. With the
|
||||
/// Set `tunnel_doh: false` to enable the bypass and let DoH go
|
||||
/// direct (saves the ~2 s Apps Script round-trip per name on
|
||||
/// networks where the DoH endpoints are reachable). With the
|
||||
/// bypass off, browsers that find their pinned DoH host
|
||||
/// unreachable already fall back to OS DNS on their own, so
|
||||
/// failure modes are graceful in either direction.
|
||||
///
|
||||
/// **Default flipped to `true` in v1.9.0** (issue #468). The
|
||||
/// previous default (`false` = bypass active) silently broke for
|
||||
/// Iranian users because Iran ISPs filter direct connections to
|
||||
/// `dns.google`, `chrome.cloudflare-dns.com`, etc. — exactly the
|
||||
/// "pinned DoH" hosts that the bypass was sending through. The
|
||||
/// safe default keeps DoH inside the tunnel; users on networks
|
||||
/// where direct DoH works can opt back into the bypass.
|
||||
///
|
||||
/// Port-gated to TCP/443 only. A private DoH on a non-standard port
|
||||
/// (e.g. `doh.internal.example:8443`) won't take the bypass path —
|
||||
/// list it in `passthrough_hosts` instead, which has no port gate.
|
||||
#[serde(default)]
|
||||
#[serde(default = "default_tunnel_doh")]
|
||||
pub tunnel_doh: bool,
|
||||
|
||||
/// Extra hostnames to treat as DoH endpoints in addition to the
|
||||
@@ -324,6 +334,14 @@ fn default_max_ips_to_scan() -> usize { 100 }
|
||||
fn default_scan_batch_size() -> usize {500}
|
||||
fn default_google_ip_validation() -> bool {true}
|
||||
|
||||
/// Default for `tunnel_doh`: `true` (DoH stays inside the tunnel).
|
||||
/// Flipped from `false` in v1.9.0 per #468 — Iran ISPs filter direct
|
||||
/// connections to pinned DoH hosts (`dns.google`, `chrome.cloudflare-dns.com`,
|
||||
/// …) and the prior bypass-on default silently broke DNS for the
|
||||
/// dominant userbase. Users on networks where direct DoH works can
|
||||
/// opt back in with `tunnel_doh: false`.
|
||||
fn default_tunnel_doh() -> bool { true }
|
||||
|
||||
fn default_google_ip() -> String {
|
||||
"216.239.38.120".into()
|
||||
}
|
||||
|
||||
+4
-3
@@ -241,9 +241,10 @@ pub struct RewriteCtx {
|
||||
/// the trade-off. Issue #213.
|
||||
pub block_quic: bool,
|
||||
/// If true, route DoH CONNECTs around the Apps Script tunnel via
|
||||
/// plain TCP. Default true via `Config::tunnel_doh = false`. See
|
||||
/// `DEFAULT_DOH_HOSTS` and `matches_doh_host` for matching, and
|
||||
/// config.rs `tunnel_doh` for the trade-off.
|
||||
/// plain TCP. Default false via `Config::tunnel_doh = true` (flipped
|
||||
/// in v1.9.0, issue #468). See `DEFAULT_DOH_HOSTS` and
|
||||
/// `matches_doh_host` for matching, and config.rs `tunnel_doh` for
|
||||
/// the trade-off.
|
||||
pub bypass_doh: bool,
|
||||
/// User-supplied DoH hostnames added to the built-in default list.
|
||||
/// Same matching semantics as `passthrough_hosts`.
|
||||
|
||||
Reference in New Issue
Block a user