mirror of
https://github.com/therealaleph/MasterHttpRelayVPN-RUST.git
synced 2026-05-19 08:04:39 +03:00
f5ebb2289527636b04cdcfbeb0d6d5bfe62a343f
54 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
64409f6b41 |
v1.0.2: stable release signature, idempotent Stop, top-level Settings for CA install (#33)
Three fixes + one behaviour change from v1.0.1 reports. APK signature is now stable (release.jks committed) ---------------------------------------------------- v1.0.0 and v1.0.1 signed release APKs with Gradle's auto-generated debug keystore, which is randomly generated per machine and per CI runner. Result: every upgrade failed with INSTALL_FAILED_UPDATE_INCOMPATIBLE and users had to uninstall first. Unfixable without a stable key. android/app/release.jks now holds that key, committed to the repo with the password in plaintext in build.gradle.kts. This is fine for a FOSS sideload project without a Play Store identity — the trust model is "trust the source tree you pulled from," not "trust the key we hold." Anyone forking and shipping a rebranded build should generate their own key. One-time cost: v1.0.1 → v1.0.2 STILL requires uninstall, because we're switching signature keys. Every upgrade from v1.0.2 onward is clean. Stop no longer (sometimes) closes the app ----------------------------------------- teardown() is reachable from three paths on two threads: 1. ACTION_STOP onStartCommand branch (mhrv-teardown worker) 2. onDestroy after stopSelf (main thread) 3. VpnService revocation out-of-band (main thread) Running the full native cleanup sequence twice races the two threads through Tun2proxy.stop() → fd.close() → Native.stopProxy(handle) on state that's already been nullified — SIGSEGV source, user-visible as "tap Stop, app disappears." New AtomicBoolean `tornDown` gates entry: first caller wins, every subsequent caller logs "teardown: already done" and returns. onDestroy also wraps the call in try/catch — crashing out of onDestroy takes the whole process with it, which is exactly the bug we're trying to fix. Smoke-tested on emulator: teardown now logs teardown: begin caller=mhrv-teardown ... clean sequence ... teardown: done onDestroy entered teardown: already done, skipping (caller=main) onDestroy done with PID unchanged throughout. CA install now routes to the Settings search -------------------------------------------- Old flow: `Settings.ACTION_SECURITY_SETTINGS` deep-link, then walk "Encryption & credentials → Install a certificate → CA certificate". That path varies wildly between OEMs (Samsung buries it under "Biometrics and security → Other security settings"; Xiaomi under "Passwords & Security → Privacy"; Pixel splits it between "More security settings" and "Privacy controls" depending on Android version). Users got lost. New flow: open the top-level Settings app (`Settings.ACTION_SETTINGS`) and instruct the user to use the Settings search bar to find "CA certificate". Search is consistent across OEMs and Android versions; the menu paths are not. Dialog, snackbar, and `docs/android.md` copy all updated to match. Version bump: 1.0.1 → 1.0.2 (versionCode 101 → 102). releases/mhrv-rs-android-universal-v1.0.1.apk replaced with the v1.0.2 build. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
b734f41faa |
v1.0.1: auto-resolve google_ip, robust Stop, Check-for-updates, front_domain repair (#31)
Three reported issues from v1.0.0 — one real bug, two UX gaps. google_ip auto-resolve (THE FIX) -------------------------------- Google rotates the A record for www.google.com across their anycast pool. A hardcoded default IP breaks new installs on any network that isn't geo-homed to the same edge — symptom is "all SNIs time out" even with a fresh deployment. On Start and via a new "Auto-detect" button, we now do a JVM-side InetAddress lookup BEFORE establishing the VPN (so the resolver uses the underlying network, not our own Virtual-DNS TUN — avoids a loop), update the config, and continue. The auto-resolve lives in the HomeScreen click handler (not MainActivity) so it goes through the same `persist(cfg)` the text fields use. Previous iteration did `ConfigStore.load → modify → save` directly to disk, which left Compose's in-memory cfg stale and a subsequent field edit would overwrite the fresh IP. One source of truth now. Also defensively repairs front_domain: if it's been corrupted into an IP literal (bad paste, whatever) we restore "www.google.com" — the TLS SNI on the outbound leg has to be a hostname or the handshake lands on the wrong vhost. Robust Stop ----------- The Stop button now dispatches both ACTION_STOP (graceful: runs teardown, stops tun2proxy, closes TUN fd, shuts down Rust runtime) AND stopService() (defensive: covers force-closed-then-reopened zombie state where Android auto-restarted our START_STICKY service in a fresh process and the in-memory TUN reference is gone). Check-for-updates ----------------- Tapping the version badge in the top bar now runs the same update_check that the desktop UI uses, via a new `Native.checkUpdate()` JNI entry point. Returns a JSON blob the Kotlin side parses into an "Up to date", "Update available: v→v <url>", "Offline: ...", or "Check failed: ..." snackbar. Mirrors the desktop's behavior so a user doesn't have to manually poll GitHub for new builds. Crash visibility ---------------- New MhrvApp.kt registers a process-wide uncaught exception handler. Crashes are now stamped into logcat under the `mhrv-crash` tag with the thread name before the default handler kills the process — previously the JVM crash in coroutines / the log drain / the tun2proxy worker was invisible unless you caught the dropoff in real time. Version bump: 1.0.0 → 1.0.1 (versionCode 100 → 101). Release APK rebuilt and replaces the 1.0.0 copy in releases/; CI will regenerate on the v1.0.1 tag push. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
91015b0594 |
v1.0.0: multi-arch Android APK + GitHub Actions release job + install docs (#30)
Version bump reflects the scope — a unified Rust core that now ships
for desktop (Linux/macOS/Windows) AND Android from the same crate.
Android changes:
- build.gradle.kts: ABI filters expanded to arm64-v8a + armeabi-v7a
+ x86_64 + x86. cargoBuild{Debug,Release} pass all four ABIs to
cargo-ndk in a single invocation. normalizeTun2proxySo() walks every
ABI dir now (was arm64-only).
- Release buildType signs with the debug keystore — no Play Store
target, so signature identity doesn't matter, installability does.
Gradle auto-provisions ~/.android/debug.keystore if absent, so CI
runners inherit this without extra setup.
- versionName 1.0.0, versionCode 100 (room to bump monotonically).
CI:
- release.yml gets a dedicated `android:` job that sets up JDK 17,
Android SDK/NDK 26, all four rust-android targets, installs
cargo-ndk, runs assembleRelease, and uploads a single universal APK
named `mhrv-rs-android-universal-v<version>.apk` into the same
`dist/` collected by the release job downstream.
- `release:` job now gates on `needs: [build, android]` so tagging
v1.0.0 triggers both build matrices before cutting the GitHub
release.
Docs:
- docs/android.md — full 10-step install walk-through: APK sideload,
Apps Script deployment (with "Advanced → Go to (unsafe) → Allow"
reality check), config paste, SNI reachability test, MITM CA
install with OEM-specific nav paths (Pixel / Samsung / Xiaomi),
Start, troubleshooting common failure modes. Also documents the
known limitations — Cloudflare Turnstile loops (inherent to the
Apps Script egress IP pool), UDP/QUIC not tunnelled, IPv6 leaks,
Apps Script daily quota — so users know what to expect before
trying it on a site that won't work.
- releases/README.md — APK row added to the English and Persian
tables, version bumped everywhere to v1.0.0.
- Top-level README — Android listed under Platforms with a link
to docs/android.md.
Release artifact:
- releases/mhrv-rs-android-universal-v1.0.0.apk — 38 MB universal
APK built locally from this tree. Installs + launches on API 24+.
The CI job will regenerate it on tag push; this is the copy
committed for users who can't reach GitHub Releases.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
96d1352728 |
Add Android app with full TUN bridge + two proxy fixes the desktop also wants (#29)
The app is a Kotlin/Compose front-end that reuses the mhrv-rs crate
via JNI. It speaks VpnService to get a TUN fd, hands that to tun2proxy,
and funnels every app's traffic through the in-process SOCKS5 listener —
no per-app proxy setup on the device.
Two fixes in `src/proxy_server.rs` apply to desktop builds too:
* SNI peek via `LazyConfigAcceptor`. When a browser uses DoH (Chrome's
default), tun2proxy hands us a raw IP in the SOCKS5 CONNECT. Minting
a MITM cert for the IP produced `ERR_CERT_COMMON_NAME_INVALID` on
Cloudflare-fronted sites. We now read the ClientHello's SNI first
and use that both as the cert subject and as the upstream host for
the Apps Script relay (fetching `https://<IP>/...` with an IP in the
Host header gets rejected by CF anyway).
* Short-circuit CORS preflight at the MITM boundary. `UrlFetchApp.fetch()`
rejects `OPTIONS` with a Swedish "Ett attribut med ogiltigt värde
har angetts: method" error, which silently broke every fetch()/XHR
preflight and was the root cause of "JS doesn't load" on Discord,
Yahoo, and similar. Since we already terminate the TLS the browser
talks to, answering the preflight with a permissive 204 is safe —
the real request still goes through the relay.
Android-side capabilities (feature-parity with `mhrv-rs-ui` where it
fits on a phone):
* multi-deployment ID editor
* SNI rotation pool + per-SNI "Test" + "Test all" (JNI into scan_sni)
* live logs panel (JNI ring buffer drained on a 500 ms poll)
* Advanced section: verify_ssl, parallel_relay, log_level, upstream_socks5
* CA install flow that matches modern Android's reality: saves
`Downloads/mhrv-ca.crt` via MediaStore, deep-links Security settings,
then verifies post-hoc by fingerprint lookup in AndroidCAStore (the
KeyChain intent dead-ends with a Close-only dialog on Android 11+)
* Start/Stop debounced to dodge an emulator EGL renderer crash on
rapid taps
Theme matches the desktop palette exactly — always-dark, accent
`#4678B4`, card fill `#1C1E22`, 4dp button / 6dp card radii.
No dynamic color, no light scheme: the desktop is always dark and
we follow.
Build wiring:
* `Cargo.toml`: `cdylib` crate-type added; `jni` + `tun2proxy`
scoped to `cfg(target_os = "android")` so desktop builds pay
nothing.
* `src/data_dir.rs`: `set_data_dir()` override so the Android app's
private filesDir replaces the `directories` crate's desktop default.
* `src/android_jni.rs`: JNI entry points for start/stop/exportCa plus
a ring buffer draining to `Native.drainLogs()` and `testSni()` that
wraps `scan_sni::probe_one`.
* Gradle task chain runs `cargo ndk` before each assemble; post-step
normalizes tun2proxy's hash-suffixed cdylib to a stable filename
so `System.loadLibrary("tun2proxy")` works.
Verified end-to-end on an API 34 emulator: ipleak, yahoo, discord,
cloudflare.com all render; TLS is MITM-ed under our user-installed
CA; service survives rapid Stop/Start cycles.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|