feat: v1.8.0 — DPI evasion, active-probing defense, full-mode usage counters

Five user-visible changes shipping together. Each is independently
useful + bounded; bundled because they're all "small architectural
hardening" that benefits from one release announcement.

1. Random payload padding (#313, #365 §1)

   Every outbound Apps Script JSON request now carries a `_pad` field
   of uniform-random length 0..1024 bytes (base64). Defeats DPI that
   fingerprints on the tight length distribution of mhrv-rs's previous
   per-mode-bound packet sizes. ~25% bandwidth on a typical 2 KB batch,
   negligible against Apps Script's per-call latency floor. Backward-
   compatible — old `Code.gs` deployments ignore the unknown field.
   Applied at all three payload-build sites: single relay, single
   tunnel op, batch tunnel.

2. Active-probing decoy: GAS bad-auth → 200 HTML (#365 §3)

   `Code.gs` and `CodeFull.gs` now return a benign Apps-Script-style
   placeholder HTML page on bad/missing AUTH_KEY instead of the JSON
   `{"e":"unauthorized"}`. To an active scanner the deployment looks
   like one of the millions of forgotten public Apps Script projects
   rather than an obvious API endpoint. New `DIAGNOSTIC_MODE` const
   restores JSON errors during setup; default false (production-strong).

3. Active-probing decoy: tunnel-node bad-auth → 404 nginx (#365 §3)

   `tunnel-node` returns an HTTP 404 with an nginx-style HTML body on
   bad auth instead of `{"e":"unauthorized"}`. Active scanners cataloging
   the host see "static web server, nothing tunnel-shaped here." New
   `MHRV_DIAGNOSTIC=1` env var restores verbose JSON during setup.

4. Fix: Full-mode usage counter stuck at zero (#230, #362)

   `today_calls` / `today_bytes` were only being incremented on the
   apps_script-mode relay path. Full-mode batches go through
   `tunnel_client::fire_batch` which never wired into the counter.
   Now `fire_batch` calls `record_today(response_bytes)` after each
   successful batch — bytes estimated from the `d` (TCP payload) and
   `pkts` (UDP datagrams) sizes in the BatchTunnelResponse. Full-mode
   users now see real usage numbers.

5. Fix: quota reset countdown was UTC, should be PT (#230, #362)

   Apps Script's UrlFetchApp daily quota resets at midnight Pacific
   Time, not UTC. We were displaying the countdown to UTC midnight,
   off by 7-8h depending on DST. New `current_pt_day_key()` and
   `seconds_until_pacific_midnight()` helpers with hand-rolled US DST
   detection (2nd Sunday March → 1st Sunday November = PDT, else PST)
   so we don't pull `chrono-tz` and a ~3 MB IANA tzdb just for one
   helper. UI label "UTC day" → "PT day". Tests pin DST window
   boundaries against March/November of 2024, 2026, 2027 to catch
   regressions in the day-of-week math.

Tested:
- cargo test --lib: 154 passed (was 152, +2 for DST window + day-of-week)
- cargo build --release: clean
- cargo build --release --bin mhrv-rs-ui --features ui: clean (macOS arm64)
- tunnel-node cargo test: 30 passed
- Android: ./gradlew assembleDebug succeeds; APK installs + launches
  on mhrv_test emulator (arm64-v8a), no UnsatisfiedLink, no crash

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
therealaleph
2026-04-28 01:39:33 +03:00
parent f7da4f01cc
commit cb3732f920
10 changed files with 378 additions and 50 deletions
+12
View File
@@ -0,0 +1,12 @@
<!-- see docs/changelog/v1.1.0.md for the file format: Persian, then `---`, then English. -->
• Padding random برای پایلود Apps Script ([#313](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/313)، [#365](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/365) Section 1): هر request به Apps Script حالا یک فیلد `_pad` با طول uniform-random بین ۰-۱۰۲۴ بایت اضافه می‌کنه — به‌صورت base64 encoded. بدون این، طول request body در هر mode تقریباً ثابت می‌مونه + DPI ایران می‌تونه بر اساس distribution طول fingerprint بزنه. حالا packet sizes uniformly distributed هستن + length-clustering match نمی‌کنه. تأثیر bandwidth: متوسط ۵۱۲ بایت اضافه به batch ~۲KB = +۲۵٪، negligible در برابر floor latency Apps Script. backward-compatible: Code.gs قدیم هم کار می‌کنه (unknown JSON fields ignore می‌شن).
• Defense active probing: decoy 200 HTML در Code.gs / CodeFull.gs روی AUTH_KEY بد ([#365](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/365) Section 3): قبلاً request بدون auth `{"e":"unauthorized"}` JSON برمی‌گردوند — fingerprint مشخص "این یه API endpoint هست". حالا یه HTML benign placeholder برمی‌گردونه که شبیه یه Apps Script web app forgotten-but-public هست. scanner active که با AUTH_KEY ساختگی POST می‌کنه categorize می‌کنه به‌عنوان "non-tunnel، nothing interesting". flag `DIAGNOSTIC_MODE` برای setup که response قدیمی JSON رو برمی‌گردونه — default `false` (production-strong)
• Defense active probing: decoy 404 nginx در tunnel-node روی auth بد: tunnel-node قبلاً `{"e":"unauthorized"}` JSON برمی‌گردوند. حالا response 404 با body HTML شبیه nginx default error می‌فرسته (active scanners "static web server هست، tunnel نیست" تشخیص می‌دن). env var `MHRV_DIAGNOSTIC=1` برای setup behavior قدیمی رو فعال می‌کنه
• رفع باگ "Usage today (estimated) در Full mode همیشه ۰" ([#230](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/230)، [#362](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/362)): counter `today_calls` و `today_bytes` فقط روی apps_script-mode relay path در `domain_fronter::relay()` افزایش می‌یافت. Full mode از `tunnel_client::fire_batch` می‌گذره که کانتر رو زد. حالا fire_batch بعد از batch موفق `record_today(response_bytes)` رو صدا می‌زنه — bytes از sum طول `d` و `pkts` در BatchTunnelResponse تخمین زده می‌شه. Full mode users حالا "Usage today" واقعی می‌بینن
• رفع باگ "quota reset countdown با time UTC به‌جای PT نشون داده می‌شه" ([#230](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/230)، [#362](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/362)): Apps Script's UrlFetchApp quota در 00:00 **Pacific Time** ریست می‌شه (PST/PDT با DST)، نه UTC. ما UTC midnight رو نشون می‌دادیم — ۷-۸ ساعت off. fix: helpers جدید `current_pt_day_key()` + `seconds_until_pacific_midnight()` با hand-rolled DST detection (بدون اضافه کردن chrono-tz / 3MB tzdb). UI label "UTC day" → "PT day" تغییر کرد. ۲ test جدید برای DST window boundaries (مارس ۲۰۲۴/۲۰۲۶/۲۰۲۷، نوامبر ۲۰۲۴/۲۰۲۶) + Sakamoto's day-of-week
---
• Random payload padding for Apps Script requests ([#313](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/313), [#365](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/365) Section 1): every outbound request to Apps Script now carries a `_pad` field of uniform-random length 01024 bytes (base64 encoded). Before this, request body sizes within each mode were tightly clustered, giving ISP DPI a clean length-distribution fingerprint to match against. Now packet sizes are spread uniformly across the range so length-clustering DPI heuristics can't match. Bandwidth cost: ~512 bytes added to a typical 2 KB tunnel batch = +25%, negligible against Apps Script's per-call latency floor. Backward-compatible: old Code.gs deployments ignore the unknown field. Applied at all three payload-build sites: single relay, single tunnel op, batch tunnel.
• Active-probing defense: decoy 200 HTML on bad AUTH_KEY in `Code.gs` and `CodeFull.gs` ([#365](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/365) Section 3): previously a request with a missing/wrong AUTH_KEY got `{"e":"unauthorized"}` as a JSON body — a clear "this is some kind of API endpoint" signal that active scanners can fingerprint. Now bad-auth requests get a benign HTML placeholder page that looks like a forgotten-but-public Apps Script web app, indistinguishable from the millions of stale Apps Script projects on Google's infrastructure. New `DIAGNOSTIC_MODE` const (default `false`) restores the old JSON error response for setup/debugging — flip to `true` while configuring a misconfigured client, then back to `false` before sharing the deployment widely.
• Active-probing defense: decoy 404 nginx-style HTML on bad auth in `tunnel-node` ([#365](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/365) Section 3): previously a bad-auth request got `{"e":"unauthorized"}`. Now it gets an HTTP 404 with an `nginx`-style error page body, looking like a vanilla static web server. Active scanners that POST malformed payloads to `/tunnel` to discover proxy endpoints categorize this host as "boring" and move on. New `MHRV_DIAGNOSTIC=1` env var restores the verbose JSON error during setup; default is the production decoy.
• Fix "Usage today (estimated) is always 0 in Full mode" ([#230](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/230), [#362](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/362)): the daily-usage counters (`today_calls` / `today_bytes`) were incremented only on the `apps_script`-mode relay path inside `domain_fronter::relay()`. Full-mode traffic goes through `tunnel_client::fire_batch` which never wired the counter. Now `fire_batch` calls `record_today(response_bytes)` after each successful batch — bytes are estimated from the sum of per-session `d` (TCP payload) and `pkts` (UDP datagrams) lengths in the `BatchTunnelResponse`, which is a stable proxy for "how much did this batch move." Full mode users now see real usage numbers instead of stuck-at-zero.
• Fix "quota reset countdown shown in UTC instead of Pacific Time" ([#230](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/230), [#362](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues/362)): Apps Script's `UrlFetchApp` quota actually resets at midnight Pacific Time (PST/PDT — observes DST), not midnight UTC. We were displaying the countdown to UTC midnight, which is 78 hours off depending on DST. Fix: new `current_pt_day_key()` + `seconds_until_pacific_midnight()` helpers using a hand-rolled US DST detector (2nd Sunday of March → 1st Sunday of November = PDT, otherwise PST) so we don't pull `chrono-tz` and a ~3 MB IANA tzdb just for one helper. UI label updated from "UTC day" to "PT day". Two new tests pin down the DST window boundaries (March 2024 / 2026 / 2027, November 2024 / 2026) and Sakamoto's day-of-week formula.