Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 0 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,6 @@ tun2proxy = { version = "0.7", default-features = false, features = ["udpgw"] }
# 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ object Tun2proxy {
tunMtu: Char,
verbosity: Int,
dnsStrategy: Int,
udpgwServer: String,
): Int

/** Signals the running `run()` to shut down. Idempotent. */
Expand Down
25 changes: 12 additions & 13 deletions android/app/src/main/java/com/therealaleph/mhrv/MhrvVpnService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -259,21 +259,20 @@ class MhrvVpnService : VpnService() {
// the sole owner once it's running.
val detachedFd = parcelFd.detachFd()
tun2proxyRunning.set(true)
// In full mode, enable udpgw so UDP traffic (DNS, QUIC, …) is
// forwarded through the tunnel-node's native udpgw handler.
// 198.18.0.1:7300 is a magic address the tunnel-node intercepts.
val udpgwAddr = if (cfg.mode == Mode.FULL) "198.18.0.1:7300" else ""
// Use tun2proxy_run_with_cli_args C API via dlsym — gives full
// CLI flexibility including --udpgw-server, no fork needed.
val cliArgs = buildString {
append("tun2proxy")
append(" --proxy socks5://127.0.0.1:$socks5Port")
append(" --tun-fd $detachedFd")
append(" --dns virtual")
append(" --verbosity info")
append(" --close-fd-on-drop true")
if (cfg.mode == Mode.FULL) append(" --udpgw-server 198.18.0.1:7300")
}
val worker = Thread({
try {
val rc = Tun2proxy.run(
"socks5://127.0.0.1:$socks5Port",
detachedFd,
/* closeFdOnDrop = */ true,
MTU.toChar(),
/* verbosity = info */ 3,
/* dnsStrategy = virtual */ 0,
udpgwAddr,
)
val rc = Native.runTun2proxy(cliArgs, MTU)
Log.i(TAG, "tun2proxy exited rc=$rc")
} catch (t: Throwable) {
Log.e(TAG, "tun2proxy crashed: ${t.message}", t)
Expand Down
10 changes: 10 additions & 0 deletions android/app/src/main/java/com/therealaleph/mhrv/Native.kt
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,14 @@ object Native {
* Cheap — just reads atomics. Safe to poll on a second-scale timer.
*/
external fun statsJson(handle: Long): String

/**
* Start tun2proxy via its CLI args C API (`tun2proxy_run_with_cli_args`).
* Resolved at runtime via dlsym from libtun2proxy.so — no fork needed.
*
* @param cliArgs full CLI string, e.g. "tun2proxy --proxy socks5://... --tun-fd 42 --udpgw-server 198.18.0.1:7300"
* @param tunMtu TUN MTU (typically 1500)
* @return 0 on normal shutdown, negative on error. BLOCKS.
*/
external fun runTun2proxy(cliArgs: String, tunMtu: Int): Int
}
50 changes: 50 additions & 0 deletions src/android_jni.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,3 +482,53 @@ pub extern "system" fn Java_com_therealaleph_mhrv_Native_statsJson<'a>(
}));
env.new_string(out).map(|s| s.into_raw()).unwrap_or(std::ptr::null_mut())
}

// ---------------------------------------------------------------------------
// tun2proxy CLI API wrapper (dlsym — no fork or patch needed)
// ---------------------------------------------------------------------------

/// `Native.runTun2proxy(cliArgs, tunMtu)` -> int
///
/// Calls `tun2proxy_run_with_cli_args` from libtun2proxy.so via dlsym.
/// This is the C API the tun2proxy maintainer recommends for callers that
/// need full CLI flexibility (e.g. --udpgw-server). BLOCKS until shutdown.
#[no_mangle]
pub extern "system" fn Java_com_therealaleph_mhrv_Native_runTun2proxy<'a>(
mut env: JNIEnv<'a>,
_class: JClass,
cli_args: JString,
tun_mtu: jni::sys::jint,
) -> jni::sys::jint {
safe(-1, AssertUnwindSafe(|| {
let args_str = jstring_to_string(&mut env, &cli_args);
tracing::info!("runTun2proxy: cli={}", args_str);

unsafe {
use std::ffi::{CStr, CString};

let lib = CString::new("libtun2proxy.so").unwrap();
let handle = libc::dlopen(lib.as_ptr(), libc::RTLD_NOW);
if handle.is_null() {
let err = CStr::from_ptr(libc::dlerror());
tracing::error!("dlopen libtun2proxy.so failed: {:?}", err);
return -10;
}

let sym = CString::new("tun2proxy_run_with_cli_args").unwrap();
let func = libc::dlsym(handle, sym.as_ptr());
if func.is_null() {
let err = CStr::from_ptr(libc::dlerror());
tracing::error!("dlsym tun2proxy_run_with_cli_args: {:?}", err);
libc::dlclose(handle);
return -11;
}

type RunFn = unsafe extern "C" fn(*const std::ffi::c_char, u16, bool) -> i32;
let run: RunFn = std::mem::transmute(func);
let c_args = CString::new(args_str).unwrap();
let rc = run(c_args.as_ptr(), tun_mtu as u16, false);
libc::dlclose(handle);
rc
}
}))
}
Loading