Files
MasterHttpRelayVPN-RUST/Cargo.toml
T
yyoyoian-pixel 1057797109 feat: native udpgw without QUIC/DNS - QUIC/DNS with udp associate — stable VoIP, faster browsing - needs new tunnel deployment for udpgw (#222)
* feat: native udpgw protocol alongside existing UDP associate

Why udpgw is needed even with UDP associate:

UDP associate (udp_open/udp_data) creates one tunnel session per UDP
destination and polls each independently. On high-latency or shaky
networks this compounds — N simultaneous UDP flows need N separate
polling loops, each paying its own batch round-trip overhead. Google
Meet calls, which fire dozens of concurrent STUN + RTP flows, stall
or fail entirely because the per-destination polling can't keep up.

udpgw multiplexes ALL UDP over one persistent TCP-like session using
conn_id framing. One batch op carries frames for many destinations.
Persistent sockets per (conn_id, dest) with continuous reader tasks
keep source ports stable — critical for protocols like Telegram VoIP
and STUN that expect replies on the same port.

Both paths coexist — they serve different traffic:
  - UDP associate (SOCKS5): apps that negotiate SOCKS5 UDP relay
  - udpgw (198.18.0.1:7300): TUN-captured UDP (DNS, QUIC, Meet, etc.)

tun2proxy vendored as git submodule at v0.7.20 with one transparent
commit adding udpgw_server to the Android JNI run() function.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: block QUIC (UDP 443) and DNS (UDP 53) from udpgw

QUIC through udpgw is slower than TCP/HTTP2 through the batch pipeline
— blocking it forces browsers to fall back to TCP, improving YouTube
and general browsing speed.

DNS is better handled by tun2proxy's virtual DNS / SOCKS5 UDP associate
path which is more reliable for single request-response exchanges.

VoIP (Telegram, Meet) still flows through udpgw normally.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: replace submodule with [patch.crates-io] for tun2proxy udpgw

Use the idiomatic Rust [patch.crates-io] mechanism instead of a git
submodule. Points to yyoyoian-pixel/tun2proxy fork with the udpgw
JNI parameter patch (upstream PR: tun2proxy/tun2proxy#247).

Will be removed once upstream ships the change in tun2proxy >= 0.8.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: pin tun2proxy patch SHA in Cargo.lock

Locks tun2proxy at dfc24ed1 so the patch resolution is recorded and
any branch rewrite is visible in the lockfile diff.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use AbortHandle for ConnSocket readers to prevent FD leaks

JoinHandle::drop detaches the task without aborting it. When
udpgw_server_task is cancelled (session close), the post-loop
cleanup never runs and per-(conn_id, dest) reader tasks become
zombies holding Arc<UdpSocket> file descriptors.

AbortHandle::drop aborts the task automatically, so cleanup is
correct by construction regardless of how the parent task exits.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: yyoyoian-pixel <279225925+yyoyoian-pixel@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-26 17:08:45 +03:00

115 lines
4.3 KiB
TOML

[package]
name = "mhrv-rs"
version = "1.6.5"
edition = "2021"
description = "Rust port of MasterHttpRelayVPN -- DPI bypass via Google Apps Script relay with domain fronting"
license = "MIT"
[lib]
name = "mhrv_rs"
path = "src/lib.rs"
# `cdylib` lets the Android app dlopen libmhrv_rs.so via System.loadLibrary.
# `rlib` keeps the desktop binaries linking normally — same .rlib is used
# for `mhrv-rs` and `mhrv-rs-ui` builds on macOS/Linux/Windows.
crate-type = ["rlib", "cdylib"]
[[bin]]
name = "mhrv-rs"
path = "src/main.rs"
[[bin]]
name = "mhrv-rs-ui"
path = "src/bin/ui.rs"
required-features = ["ui"]
[features]
default = []
ui = ["dep:eframe"]
[dependencies]
tokio = { version = "1", features = ["rt-multi-thread", "macros", "net", "time", "io-util", "signal", "sync"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12"] }
rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] }
rustls-pemfile = "2"
webpki-roots = "0.26"
rcgen = { version = "0.13", features = ["x509-parser"] }
rustls-pki-types = "1"
time = "0.3"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
thiserror = "2"
base64 = "0.22"
bytes = "1"
httparse = "1"
rand = "0.8"
h2 = "0.4"
http = "1"
flate2 = "1"
directories = "5"
futures-util = { version = "0.3", default-features = false, features = ["std"] }
# 64-bit atomics on 32-bit MIPS/ARMv5 targets. Rust's std AtomicU64 is
# only available on targets that expose native 64-bit atomics, which
# mipsel-unknown-linux-musl does not — `AtomicU64` resolves to "no
# such name in sync::atomic" and the whole crate fails to build. The
# `fallback` feature uses a global spinlock when the target can't do
# 64-bit atomically; on x86_64 / aarch64 / armv7 / etc. it compiles
# down to the native instructions with no overhead.
portable-atomic = { version = "1", features = ["fallback"] }
# Optional UI dep: only pulled in when --features ui is set.
# Both `glow` (OpenGL 2+) and `wgpu` (DX12/Vulkan/Metal) are compiled in;
# the binary picks one at startup — glow by default for compat with the
# egui look-and-feel we've been shipping, but falls back to wgpu when
# `MHRV_RENDERER=wgpu` is set. Issue #28: users on older Windows
# hardware / RDP / VMs without OpenGL 2.0 crash with
# `egui_glow requires opengl 2.0+` — the wgpu backend uses DX12/Vulkan
# instead and covers those boxes.
eframe = { version = "0.28", default-features = false, features = [
"default_fonts",
"glow",
"wgpu",
"persistence",
], optional = true }
url = "2.5.8"
# Unix-only deps. Must come after `[dependencies]` because starting a new
# table here otherwise ends the main one — anything below it (incl. eframe)
# would end up scoped to cfg(unix) and disappear on Windows builds.
# libc is referenced for the RLIMIT_NOFILE bump (issue #8 — OpenWRT routers
# ship a very low fd limit that fills up fast under browser load). Already
# pulled in transitively via tokio, so zero new weight.
[target.'cfg(unix)'.dependencies]
libc = "0.2"
# Android-only deps: jni gives us the extern "system" wrappers used in
# src/android_jni.rs; zero cost on any other platform because the whole
# module is `#[cfg(target_os = "android")]`.
#
# tun2proxy is the TUN <-> SOCKS5 bridge — it reads raw IP packets from the
# fd VpnService hands us, runs a userspace TCP/IP stack (smoltcp under the
# hood), and funnels every TCP/UDP flow through our local SOCKS5. Without
# this, VpnService establishes a TUN device nothing reads from and all
# traffic black-holes (symptom: Chrome shows DNS_PROBE_STARTED).
[target.'cfg(target_os = "android")'.dependencies]
jni = { version = "0.21", default-features = false }
tun2proxy = { version = "0.7", default-features = false, features = ["udpgw"] }
[dev-dependencies]
# Used in mitm tests to sanity-check the cert extensions we emit.
x509-parser = "0.16"
# Temporary patch: adds udpgw_server parameter to the Android JNI run()
# function. Upstream PR: https://github.com/tun2proxy/tun2proxy/pull/247
# Remove this section once tun2proxy >= 0.8 ships with the change.
[patch.crates-io]
tun2proxy = { git = "https://github.com/yyoyoian-pixel/tun2proxy", branch = "feat/udpgw-jni-param" }
[profile.release]
panic = "abort"
codegen-units = 1
lto = true
opt-level = 3
strip = true