v1.2.6: rust-cache bin pruning fix + PR #83 scan-sni

v1.2.4 and v1.2.5 both cut clean tags but CI failed downstream for
different self-hosted reasons:

- v1.2.4 failed on parallel apt-lock race (fixed)
- v1.2.5 failed with "TOML parse error at line 5 column 9" because
  rust-cache v2's default cache-bin=true prunes $CARGO_HOME/bin of
  any binary not installed via `cargo install`. `rustup` itself is
  installed by rustup-init, not cargo install, so it got flagged as
  "unknown" and deleted on cache save. Next job hits the cargo
  symlink that points at a missing rustup, which resolves somehow
  to a very old cargo that can't parse our Cargo.toml.

Fix:
- Set `cache-bin: "false"` on every Swatinem/rust-cache@v2 call.
  We still cache target/ + registry (the big win), just not bin/.
  Binaries are stable across runs on our self-hosted box anyway.
- Reinstalled rustup inside each per-runner CARGO_HOME on the server
  to recover from the broken state.

Also in this release:
- PR #83: new `mhrv-rs scan-sni` subcommand. Pulls Google's
  published IP ranges, does PTR lookups via dns.google on each IP,
  filters to Google-related hostnames, then TLS-probes each
  discovered SNI against the configured google_ip to see which ones
  bypass DPI. Useful for rebuilding a working SNI pool on a new ISP.
  Adds the `url` crate dep.

Same user-facing code as v1.2.4/v1.2.5 (PRs #78, #79, README Android
note) plus PR #83 and the CI fixes on top.
This commit is contained in:
therealaleph
2026-04-23 21:22:17 +03:00
parent ca10f775dc
commit 658e72fe0d
6 changed files with 31 additions and 14 deletions
+14
View File
@@ -106,10 +106,19 @@ jobs:
# `git clean -ffdx` wipes target/ between runs and every build is # `git clean -ffdx` wipes target/ between runs and every build is
# cold. With it, warm builds are sub-minute even for the full # cold. With it, warm builds are sub-minute even for the full
# release profile. # release profile.
#
# cache-bin: false is MANDATORY on our self-hosted runners. With
# the default (true), rust-cache aggressively prunes $CARGO_HOME/bin
# of binaries it didn't install via `cargo install`, including the
# `rustup` binary that cargo/rustc/etc. are symlinked to. The next
# job then hits "command not found" or a broken-symlink TOML parse
# error from a stale cargo. We want target/ + registry caching, NOT
# bin pruning. rustup is pre-installed on the runners anyway.
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
if: matrix.mipsel_softfloat != true if: matrix.mipsel_softfloat != true
with: with:
key: ${{ matrix.target }} key: ${{ matrix.target }}
cache-bin: "false"
# eframe needs a few system libs on Linux for window management, keyboard, # eframe needs a few system libs on Linux for window management, keyboard,
# and OpenGL/X11/Wayland. Gated to GitHub-hosted runners only — the # and OpenGL/X11/Wayland. Gated to GitHub-hosted runners only — the
@@ -343,9 +352,14 @@ jobs:
# Cache cargo + target/ across Android release builds. Four cargo-ndk # Cache cargo + target/ across Android release builds. Four cargo-ndk
# release builds back-to-back with LTO is where the cold cost comes # release builds back-to-back with LTO is where the cold cost comes
# from; rust-cache brings warm runs down to ~34 min from ~9 min cold. # from; rust-cache brings warm runs down to ~34 min from ~9 min cold.
# cache-bin: false — see the rationale on the matrix build job above.
# On top of that, `cargo-ndk` lives in /usr/local/bin/ on our runners
# (not $CARGO_HOME/bin), specifically so rust-cache's default bin
# pruning can't delete it.
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
with: with:
key: android-universal key: android-universal
cache-bin: "false"
# cargo-ndk writes into `target/<android-triple>/release/`, all # cargo-ndk writes into `target/<android-triple>/release/`, all
# four of which we want to cache. # four of which we want to cache.
workspaces: | workspaces: |
Generated
+1 -1
View File
@@ -2186,7 +2186,7 @@ dependencies = [
[[package]] [[package]]
name = "mhrv-rs" name = "mhrv-rs"
version = "1.2.5" version = "1.2.6"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"bytes", "bytes",
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "mhrv-rs" name = "mhrv-rs"
version = "1.2.5" version = "1.2.6"
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"
+2 -2
View File
@@ -14,8 +14,8 @@ android {
applicationId = "com.therealaleph.mhrv" applicationId = "com.therealaleph.mhrv"
minSdk = 24 // Android 7.0 — covers 99%+ of live devices. minSdk = 24 // Android 7.0 — covers 99%+ of live devices.
targetSdk = 34 targetSdk = 34
versionCode = 125 versionCode = 126
versionName = "1.2.5" versionName = "1.2.6"
// Ship all four mainstream Android ABIs: // Ship all four mainstream Android ABIs:
// - arm64-v8a — 95%+ of real-world Android phones since 2019 // - arm64-v8a — 95%+ of real-world Android phones since 2019
-10
View File
@@ -1,10 +0,0 @@
<!-- see docs/changelog/v1.1.0.md for the file format: Persian, then `---`, then English. -->
• سخت‌کردن range-parallel: اعتبارسنجی هدر `Content-Range` قبل از دوختن پاسخ‌های ۲۰۶. پاسخ‌های نامعتبر دیگه به صورت ۲۰۰ OK جعلی ترکیب نمی‌شن — probe نامعتبر به GET تکی برمی‌گرده، چانک‌های نامعتبر به پاسخ probe برمی‌گرده (PR #78)
• رد configهایی که HTTP و SOCKS5 رو روی یک پورت تنظیم کرده‌اند قبل از bind failure زمان اجرا. هم در load config و هم در فرم UI چک می‌شه (PR #79)
• یادداشت README درباره محدودیت user-CA اندروید 7+ — اپ‌هایی مثل Telegram / WhatsApp / Instagram به CA ما اعتماد نمی‌کنن، برای اون‌ها از PROXY_ONLY یا upstream_socks5 استفاده کنید (issues #74 #81)
• رفع زیرساخت CI: مراحل apt-get در buildهای Linux فقط روی runnerهای GitHub-hosted اجرا می‌شن. روی runnerهای self-hosted جدید، چندین job موازی روی `/var/lib/apt/lists/lock` رقابت می‌کردن و همه fail می‌شدن. بسته‌ها اکنون در setup runner پیش‌نصب هستند
---
• Range-parallel hardening: validate `Content-Range` before stitching 206 responses. Invalid responses no longer combine into a fake 200 OK — invalid probe falls back to a normal single GET, invalid later chunks fall back to the probe response (PR #78)
• Reject configs that set HTTP and SOCKS5 to the same port before the runtime bind failure. Enforced both at config-load time and in the UI form (PR #79)
• README note on the Android 7+ user-CA trust limit — apps like Telegram / WhatsApp / Instagram don't trust user-installed CAs, use PROXY_ONLY or upstream_socks5 for those (issues #74 #81)
• CI infrastructure fix: apt-get steps on Linux build jobs gated to GitHub-hosted runners only. On the new self-hosted runners, multiple parallel matrix jobs were racing on `/var/lib/apt/lists/lock` and failing all at once. Packages now pre-installed at runner setup time
+13
View File
@@ -0,0 +1,13 @@
<!-- see docs/changelog/v1.1.0.md for the file format: Persian, then `---`, then English. -->
• سخت‌کردن range-parallel: اعتبارسنجی هدر `Content-Range` قبل از دوختن پاسخ‌های ۲۰۶. پاسخ‌های نامعتبر دیگه به صورت ۲۰۰ OK جعلی ترکیب نمی‌شن — probe نامعتبر به GET تکی برمی‌گرده، چانک‌های نامعتبر به پاسخ probe برمی‌گرده (PR #78)
• رد configهایی که HTTP و SOCKS5 رو روی یک پورت تنظیم کرده‌اند قبل از bind failure زمان اجرا. هم در load config و هم در فرم UI چک می‌شه (PR #79)
• یادداشت README درباره محدودیت user-CA اندروید 7+ — اپ‌هایی مثل Telegram / WhatsApp / Instagram به CA ما اعتماد نمی‌کنن، برای اون‌ها از PROXY_ONLY یا upstream_socks5 استفاده کنید (issues #74 #81)
• رفع زیرساخت CI (دور دوم): rust-cache v2 به صورت پیش‌فرض `$CARGO_HOME/bin` رو در پایان هر job pruneمی‌کرد و باینری `rustup` رو (که توسط rustup-init نصب شده، نه `cargo install`) پاک می‌کرد. این باعث می‌شد jobهای بعدی روی symlinkهای شکسته به TOML parse error برخورد کنن. حالا `cache-bin: "false"` تنظیم کردیم و `cargo-ndk` هم به `/usr/local/bin/` منتقل شده
• اسکن SNI خودکار: دستور جدید `mhrv-rs scan-sni` که از رنج IPهای Google با PTR lookup روی dns.google شروع می‌کنه، روی هر IP SNIهای Google-related رو کشف می‌کنه، و هر کدوم رو با TLS handshake علیه `google_ip` تست می‌کنه تا ببینه DPI رو رد می‌کنن یا نه (PR #83)
---
• Range-parallel hardening: validate `Content-Range` before stitching 206 responses. Invalid responses no longer combine into a fake 200 OK — invalid probe falls back to a normal single GET, invalid later chunks fall back to the probe response (PR #78)
• Reject configs that set HTTP and SOCKS5 to the same port before the runtime bind failure. Enforced both at config-load time and in the UI form (PR #79)
• README note on the Android 7+ user-CA trust limit — apps like Telegram / WhatsApp / Instagram don't trust user-installed CAs, use PROXY_ONLY or upstream_socks5 for those (issues #74 #81)
• CI infrastructure fix (round 1): apt-get steps on Linux build jobs gated to GitHub-hosted runners only. On the new self-hosted runners, multiple parallel matrix jobs were racing on `/var/lib/apt/lists/lock` and failing all at once. Packages now pre-installed at runner setup time
• CI infrastructure fix (round 2): rust-cache v2's default bin-pruning was wiping the `rustup` binary at end of each job (since rustup wasn't installed via `cargo install`, rust-cache considered it an "unknown" bin). Next job then hit TOML parse errors from broken cargo symlinks. Set `cache-bin: "false"` and moved `cargo-ndk` to `/usr/local/bin/` out of rust-cache's reach
• New `mhrv-rs scan-sni` subcommand: pulls Google's IP ranges, does PTR lookups via dns.google on each IP to discover Google-related hostnames, then TLS-probes each discovered SNI against the configured `google_ip` to see if it bypasses DPI. Useful for rebuilding a working SNI pool on a new ISP (PR #83)