mirror of
https://github.com/therealaleph/MasterHttpRelayVPN-RUST.git
synced 2026-05-17 21:24:48 +03:00
Three user-reported fixes / features in one release. === PR #9 — dynamic Google IP discovery (@v4g4b0nd-0x76) === Already merged in the previous commit. Opt-in via 'fetch_ips_from_api' in config. Pulls goog.json from www.gstatic.com, maps it against resolved IPs of well-known Google domains, samples from matching CIDRs, and validates each candidate with gws / x-google / alt-svc response-header checks. Graceful fallback to the static list if the fetch fails or nothing passes validation. Default is off so existing users are unaffected. Closes #10. === Issue #8 — OpenWRT: 'accept: No file descriptors available' === OpenWRT routers ship a very low RLIMIT_NOFILE (often 1024, sometimes 256 on constrained devices). A browser's burst of ~30 parallel sub- resource requests can fill the limit within seconds, after which accept(2) returns EMFILE and the proxy is effectively dead. Two-fold fix: 1. New assets/openwrt/mhrv-rs.init now sets procd limits nofile= "16384 16384" on the service. procd raises the per-process fd limit before the binary even starts. 2. New src/rlimit.rs best-effort-raises RLIMIT_NOFILE in the binary itself (Unix only, no new runtime deps — libc is already transitively present via tokio). Targets 16384 soft, capped to whatever hard limit the kernel already allows the user (so it doesn't need root). Both layers mean the fix applies whether the user runs via /etc/init.d/mhrv-rs start (procd limits kick in) or ./mhrv-rs --config ... (in-binary bump kicks in) or any other invocation path. Closes #8. === Issue #7 — Windows UI crashes silently === User report: on Win 11, run.bat prints 'Starting mhrv-rs UI...' and exits clean, but no UI window ever appears. Root cause: the old run.bat used 'start "" "mhrv-rs-ui.exe"' which returns immediately — if the UI binary dies at launch time (missing GPU driver, RDP without GL accel, AV blocking, …), the crash is invisible because start already disowned the child. Fix: run the UI in-place (not via 'start'), so its stderr and exit code land in the run.bat cmd window. On non-zero exit print a helpful checklist of common Windows launch failures and pause so the user can screenshot the output for an issue report. This doesn't fix the underlying crash for affected users, but it turns a ghost-crash bug into a self-diagnosing one so the next report includes actionable info. Closes-via-diag #7. === Fixes folded into the PR #9 merge === - src/scan_ips.rs: rand::thread_rng() held across an .await tripped the Send bound on the async fn. Scoped the rng in a block so it drops before the subsequent awaits. - src/scan_ips.rs: defend /0 and /32 CIDRs in cidr_to_ips and ip_in_cidr against 1u32 << 32 shift panic. All 36 unit tests pass.
This commit is contained in:
Generated
+2
-1
@@ -1317,7 +1317,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mhrv-rs"
|
name = "mhrv-rs"
|
||||||
version = "0.7.1"
|
version = "0.8.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -1328,6 +1328,7 @@ dependencies = [
|
|||||||
"h2",
|
"h2",
|
||||||
"http",
|
"http",
|
||||||
"httparse",
|
"httparse",
|
||||||
|
"libc",
|
||||||
"rand",
|
"rand",
|
||||||
"rcgen",
|
"rcgen",
|
||||||
"rustls",
|
"rustls",
|
||||||
|
|||||||
+7
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "mhrv-rs"
|
name = "mhrv-rs"
|
||||||
version = "0.7.1"
|
version = "0.8.0"
|
||||||
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"
|
||||||
@@ -46,6 +46,12 @@ flate2 = "1"
|
|||||||
directories = "5"
|
directories = "5"
|
||||||
futures-util = { version = "0.3", default-features = false, features = ["std"] }
|
futures-util = { version = "0.3", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
|
# libc is only referenced for the RLIMIT_NOFILE bump on Unix (issue #8 —
|
||||||
|
# OpenWRT routers ship a very low fd limit that gets hit quickly under normal
|
||||||
|
# traffic). Already pulled in transitively via tokio, so zero new weight.
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
libc = "0.2"
|
||||||
|
|
||||||
# Optional UI dep: only pulled in when --features ui is set.
|
# Optional UI dep: only pulled in when --features ui is set.
|
||||||
eframe = { version = "0.28", default-features = false, features = [
|
eframe = { version = "0.28", default-features = false, features = [
|
||||||
"default_fonts",
|
"default_fonts",
|
||||||
|
|||||||
@@ -19,12 +19,40 @@ if errorlevel 1 (
|
|||||||
echo but HTTPS sites may show certificate warnings until the CA is trusted.
|
echo but HTTPS sites may show certificate warnings until the CA is trusted.
|
||||||
)
|
)
|
||||||
|
|
||||||
if exist "mhrv-rs-ui.exe" (
|
if not exist "mhrv-rs-ui.exe" (
|
||||||
echo Starting mhrv-rs UI...
|
|
||||||
start "" "mhrv-rs-ui.exe"
|
|
||||||
) else (
|
|
||||||
echo UI binary not found. Running CLI proxy instead.
|
echo UI binary not found. Running CLI proxy instead.
|
||||||
mhrv-rs.exe
|
mhrv-rs.exe
|
||||||
|
goto :eof
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Starting mhrv-rs UI...
|
||||||
|
echo (A new window should open. If nothing appears, the UI crashed — the
|
||||||
|
echo error is shown in this terminal below. Take a screenshot of it and
|
||||||
|
echo open an issue on github.)
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM Run in-place (not via `start`) so if the UI dies on launch, its stderr
|
||||||
|
REM and non-zero exit code are visible in this window. Previously we used
|
||||||
|
REM `start "" "mhrv-rs-ui.exe"` which returns immediately and swallows any
|
||||||
|
REM launch-time crash (issue #7).
|
||||||
|
mhrv-rs-ui.exe
|
||||||
|
set UI_EXIT=%ERRORLEVEL%
|
||||||
|
if not "%UI_EXIT%"=="0" (
|
||||||
|
echo.
|
||||||
|
echo ---------------------------------------------------
|
||||||
|
echo UI exited with error code %UI_EXIT%.
|
||||||
|
echo.
|
||||||
|
echo If this is the first time and you just saw the UI crash immediately,
|
||||||
|
echo common causes on Windows are:
|
||||||
|
echo - missing or outdated graphics drivers (try updating)
|
||||||
|
echo - running inside RDP or a VM without GPU acceleration
|
||||||
|
echo - antivirus blocking the exe — whitelist the folder and retry
|
||||||
|
echo.
|
||||||
|
echo Copy everything above and open an issue on:
|
||||||
|
echo https://github.com/therealaleph/MasterHttpRelayVPN-RUST/issues
|
||||||
|
echo ---------------------------------------------------
|
||||||
|
pause
|
||||||
)
|
)
|
||||||
|
|
||||||
endlocal
|
endlocal
|
||||||
|
|||||||
@@ -24,6 +24,11 @@ start_service() {
|
|||||||
procd_set_param stdout 1
|
procd_set_param stdout 1
|
||||||
procd_set_param stderr 1
|
procd_set_param stderr 1
|
||||||
procd_set_param file "$CONFIG"
|
procd_set_param file "$CONFIG"
|
||||||
|
# Issue #8 — OpenWRT's default fd limit is tiny (often 1024 and sometimes
|
||||||
|
# as low as 256 on constrained devices). A browser's parallel sub-resource
|
||||||
|
# burst fills it in seconds and accept(2) starts returning EMFILE. 16k is
|
||||||
|
# plenty for a local proxy and costs virtually no kernel memory.
|
||||||
|
procd_set_param limits nofile="16384 16384"
|
||||||
procd_close_instance
|
procd_close_instance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const LOG_MAX: usize = 200;
|
|||||||
|
|
||||||
fn main() -> eframe::Result<()> {
|
fn main() -> eframe::Result<()> {
|
||||||
let _ = rustls::crypto::ring::default_provider().install_default();
|
let _ = rustls::crypto::ring::default_provider().install_default();
|
||||||
|
mhrv_rs::rlimit::raise_nofile_limit_best_effort();
|
||||||
|
|
||||||
let shared = Arc::new(Shared::default());
|
let shared = Arc::new(Shared::default());
|
||||||
let (cmd_tx, cmd_rx) = std::sync::mpsc::channel::<Cmd>();
|
let (cmd_tx, cmd_rx) = std::sync::mpsc::channel::<Cmd>();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ pub mod data_dir;
|
|||||||
pub mod domain_fronter;
|
pub mod domain_fronter;
|
||||||
pub mod mitm;
|
pub mod mitm;
|
||||||
pub mod proxy_server;
|
pub mod proxy_server;
|
||||||
|
pub mod rlimit;
|
||||||
pub mod scan_ips;
|
pub mod scan_ips;
|
||||||
pub mod scan_sni;
|
pub mod scan_sni;
|
||||||
pub mod test_cmd;
|
pub mod test_cmd;
|
||||||
|
|||||||
@@ -121,6 +121,10 @@ async fn main() -> ExitCode {
|
|||||||
// Install default rustls crypto provider (ring).
|
// Install default rustls crypto provider (ring).
|
||||||
let _ = rustls::crypto::ring::default_provider().install_default();
|
let _ = rustls::crypto::ring::default_provider().install_default();
|
||||||
|
|
||||||
|
// Bump RLIMIT_NOFILE where possible — OpenWRT/Alpine hosts often ship a
|
||||||
|
// default so low the proxy runs out of fds under normal browser load.
|
||||||
|
mhrv_rs::rlimit::raise_nofile_limit_best_effort();
|
||||||
|
|
||||||
let args = match parse_args() {
|
let args = match parse_args() {
|
||||||
Ok(a) => a,
|
Ok(a) => a,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
//! Best-effort file descriptor limit bump on Unix.
|
||||||
|
//!
|
||||||
|
//! Context (issue #8): on OpenWRT routers — and some minimal Alpine / BSD
|
||||||
|
//! installs — the default `RLIMIT_NOFILE` is so low (often 1024 or even
|
||||||
|
//! 512) that a browser's burst of ~30 parallel subresource requests fills
|
||||||
|
//! the limit within seconds. Once the limit is hit `accept(2)` returns
|
||||||
|
//! `EMFILE` and the user sees:
|
||||||
|
//!
|
||||||
|
//! ERROR accept (socks): No file descriptors available (os error 24)
|
||||||
|
//!
|
||||||
|
//! This helper raises the soft limit up to the hard limit (without
|
||||||
|
//! requiring root), so the user gets whatever headroom the kernel
|
||||||
|
//! already allows them. Failures are logged and swallowed.
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub fn raise_nofile_limit_best_effort() {
|
||||||
|
// Target: 16384 if the hard limit allows it, else whatever the hard
|
||||||
|
// limit is. 16k matches what most modern desktop distros default to and
|
||||||
|
// is plenty for a local proxy.
|
||||||
|
const DESIRED: u64 = 16_384;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let mut rl = libc::rlimit {
|
||||||
|
rlim_cur: 0,
|
||||||
|
rlim_max: 0,
|
||||||
|
};
|
||||||
|
if libc::getrlimit(libc::RLIMIT_NOFILE, &mut rl) != 0 {
|
||||||
|
let err = std::io::Error::last_os_error();
|
||||||
|
tracing::debug!("getrlimit(RLIMIT_NOFILE) failed: {}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Already high enough? Leave it.
|
||||||
|
let current = rl.rlim_cur as u64;
|
||||||
|
let hard = rl.rlim_max as u64;
|
||||||
|
if current >= DESIRED {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_soft = DESIRED.min(hard);
|
||||||
|
if new_soft <= current {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rl.rlim_cur = new_soft as libc::rlim_t;
|
||||||
|
if libc::setrlimit(libc::RLIMIT_NOFILE, &rl) != 0 {
|
||||||
|
let err = std::io::Error::last_os_error();
|
||||||
|
tracing::debug!(
|
||||||
|
"setrlimit(RLIMIT_NOFILE) {} -> {} failed: {}",
|
||||||
|
current,
|
||||||
|
new_soft,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tracing::info!(
|
||||||
|
"raised RLIMIT_NOFILE: {} -> {} (hard={})",
|
||||||
|
current,
|
||||||
|
new_soft,
|
||||||
|
hard
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
pub fn raise_nofile_limit_best_effort() {}
|
||||||
Reference in New Issue
Block a user