fix(jni): 全部 18 个 JNI 导出函数加 catch_unwind 防护

问题:vnt-jni 的 extern "system" 导出函数没有任何 panic 防护,
core 内任何 panic(unwrap/索引越界/断言等)跨 FFI unwind 会直接
abort 宿主 JVM。

修复:新增 catch_jni_panic(catch_unwind + AssertUnwindSafe)与
jni_guard! 宏,包裹全部 18 个导出函数:panic 时提取 payload 消息、
向 JVM 抛 RuntimeException 并返回该类型的默认值(jboolean/jint 0、
jlong -1、指针 null_mut、void 直接返回)。业务逻辑与签名不变。

说明:排查中发现报告举例的 core/mod.rs:140 expect 在当前代码下
实际不可达(no_tun=true 时 internal_nat_inbound 必为 Some),
但 FFI panic 防护本身仍然必要。

测试:3 个 catch_jni_panic 单测(正常值透传、&str/String payload
消息提取);cargo clippy --workspace --all-targets -- -D warnings 通过。
This commit is contained in:
lbl
2026-08-21 01:45:30 +08:00
parent 1cc71c8d62
commit d49b7e6651
+603 -508
View File
@@ -1,7 +1,7 @@
use anyhow::Context; use anyhow::Context;
use jni::JNIEnv;
use jni::objects::{JClass, JObject, JString}; use jni::objects::{JClass, JObject, JString};
use jni::sys::{jboolean, jint, jlong, jstring}; use jni::sys::{jboolean, jint, jlong, jstring};
use jni::JNIEnv;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::collections::HashMap; use std::collections::HashMap;
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
@@ -49,34 +49,72 @@ lazy_static::lazy_static! {
static ref GLOBAL_STATE: Mutex<Option<GlobalState>> = Mutex::new(None); static ref GLOBAL_STATE: Mutex<Option<GlobalState>> = Mutex::new(None);
} }
/// 从 panic payload 中提取错误消息
fn panic_message(e: Box<dyn std::any::Any + Send>) -> String {
if let Some(s) = e.downcast_ref::<&str>() {
s.to_string()
} else if let Some(s) = e.downcast_ref::<String>() {
s.clone()
} else {
"unknown panic".to_string()
}
}
/// 捕获闭包内的 panic,转为 Err(消息),防止 panic 跨 FFI unwind 导致宿主 abort
fn catch_jni_panic<F, T>(f: F) -> Result<T, String>
where
F: FnOnce() -> T,
{
std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)).map_err(panic_message)
}
/// JNI 导出函数的 panic 防护:panic 时向 JVM 抛出异常并返回默认值
macro_rules! jni_guard {
($env:ident, $default_ret:expr, { $($body:tt)* }) => {{
match catch_jni_panic(|| {
$($body)*
}) {
Ok(v) => v,
Err(msg) => {
let _ = $env.throw(format!("VNT native panic: {}", msg));
$default_ret
}
}
}};
}
/// 初始化JNI模块 /// 初始化JNI模块
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntManager_nativeInit( pub extern "system" fn Java_com_vnt_VntManager_nativeInit(
mut env: JNIEnv, mut env: JNIEnv,
_class: JClass, _class: JClass,
) -> jboolean { ) -> jboolean {
let mut state = GLOBAL_STATE.lock(); jni_guard!(env, 0, {
if state.is_some() { let mut state = GLOBAL_STATE.lock();
return 1; // 已经初始化 if state.is_some() {
} return 1; // 已经初始化
}
match GlobalState::new() { match GlobalState::new() {
Ok(global_state) => { Ok(global_state) => {
*state = Some(global_state); *state = Some(global_state);
1 1
}
Err(e) => {
let _ = env.throw(format!("Failed to initialize VNT: {:?}", e));
0
}
} }
Err(e) => { })
let _ = env.throw(format!("Failed to initialize VNT: {:?}", e));
0
}
}
} }
/// 销毁JNI模块 /// 销毁JNI模块
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntManager_nativeDestroy(_env: JNIEnv, _class: JClass) { pub extern "system" fn Java_com_vnt_VntManager_nativeDestroy(_env: JNIEnv, _class: JClass) {
let mut state = GLOBAL_STATE.lock(); let _ = catch_jni_panic(|| {
*state = None; let mut state = GLOBAL_STATE.lock();
*state = None;
});
} }
/// 创建网络实例 /// 创建网络实例
@@ -86,49 +124,51 @@ pub extern "system" fn Java_com_vnt_VntManager_nativeCreateNetwork<'local>(
_class: JClass<'local>, _class: JClass<'local>,
config_json: JString<'local>, config_json: JString<'local>,
) -> jlong { ) -> jlong {
let result: anyhow::Result<i64> = (|| { jni_guard!(env, -1, {
let mut global_state = GLOBAL_STATE.lock(); let result: anyhow::Result<i64> = (|| {
let state = global_state.as_mut().context("VNT not initialized")?; let mut global_state = GLOBAL_STATE.lock();
let state = global_state.as_mut().context("VNT not initialized")?;
// 解析配置JSON // 解析配置JSON
let config_str: String = env.get_string(&config_json)?.into(); let config_str: String = env.get_string(&config_json)?.into();
let config = parse_config_from_json(&config_str)?; let config = parse_config_from_json(&config_str)?;
// 创建任务组 // 创建任务组
let task_group_manager = TaskGroupManager::new(); let task_group_manager = TaskGroupManager::new();
let (task_group, task_group_guard) = task_group_manager let (task_group, task_group_guard) = task_group_manager
.create_task() .create_task()
.context("create task group")?; .context("create task group")?;
// 获取runtime的clone // 获取runtime的clone
let runtime = state.runtime.clone(); let runtime = state.runtime.clone();
// 创建网络管理器 // 创建网络管理器
let network_manager = runtime.block_on(async { let network_manager = runtime.block_on(async {
NetworkManager::create_network(Box::new(config), task_group).await NetworkManager::create_network(Box::new(config), task_group).await
})?; })?;
// 分配ID // 分配ID
let id = state.next_id; let id = state.next_id;
state.next_id += 1; state.next_id += 1;
// 保存实例(task_group_guard 必须随实例一直持有,drop 会停止整个任务组) // 保存实例(task_group_guard 必须随实例一直持有,drop 会停止整个任务组)
state state
.network_managers .network_managers
.insert(id, Arc::new(Mutex::new(Some(network_manager)))); .insert(id, Arc::new(Mutex::new(Some(network_manager))));
state.task_group_managers.insert(id, task_group_manager); state.task_group_managers.insert(id, task_group_manager);
state.task_group_guards.insert(id, task_group_guard); state.task_group_guards.insert(id, task_group_guard);
Ok(id) Ok(id)
})(); })();
match result { match result {
Ok(id) => id, Ok(id) => id,
Err(e) => { Err(e) => {
let _ = env.throw(format!("Failed to create network: {:?}", e)); let _ = env.throw(format!("Failed to create network: {:?}", e));
-1 -1
}
} }
} })
} }
/// 注册网络 /// 注册网络
@@ -138,61 +178,63 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeRegister<'local>(
_class: JClass<'local>, _class: JClass<'local>,
handle: jlong, handle: jlong,
) -> jstring { ) -> jstring {
let result: anyhow::Result<String> = (|| { jni_guard!(env, std::ptr::null_mut(), {
let (network_manager_arc, runtime) = { let result: anyhow::Result<String> = (|| {
let mut global_state = GLOBAL_STATE.lock(); let (network_manager_arc, runtime) = {
let state = global_state.as_mut().context("VNT not initialized")?; let mut global_state = GLOBAL_STATE.lock();
let state = global_state.as_mut().context("VNT not initialized")?;
let network_manager_arc = state let network_manager_arc = state
.network_managers .network_managers
.get(&handle) .get(&handle)
.context("Invalid handle")? .context("Invalid handle")?
.clone(); .clone();
let runtime = state.runtime.clone(); let runtime = state.runtime.clone();
(network_manager_arc, runtime) (network_manager_arc, runtime)
}; };
let response = { let response = {
let mut manager_lock = network_manager_arc.lock(); let mut manager_lock = network_manager_arc.lock();
let manager = manager_lock let manager = manager_lock
.as_mut() .as_mut()
.context("Network manager already destroyed")?; .context("Network manager already destroyed")?;
runtime.block_on(async { manager.register().await })? runtime.block_on(async { manager.register().await })?
}; };
match response { match response {
RegisterResponse::Success(network_addr) => { RegisterResponse::Success(network_addr) => {
let response_json = serde_json::json!({ let response_json = serde_json::json!({
"success": true, "success": true,
"ip": network_addr.ip.to_string(), "ip": network_addr.ip.to_string(),
"prefix_len": network_addr.prefix_len, "prefix_len": network_addr.prefix_len,
"gateway": network_addr.gateway.to_string(), "gateway": network_addr.gateway.to_string(),
"broadcast": network_addr.broadcast.to_string(), "broadcast": network_addr.broadcast.to_string(),
}); });
Ok(response_json.to_string()) Ok(response_json.to_string())
}
RegisterResponse::Failed(error_msg) => {
let response_json = serde_json::json!({
"success": false,
"error": error_msg.message,
});
Ok(response_json.to_string())
}
} }
RegisterResponse::Failed(error_msg) => { })();
let response_json = serde_json::json!({
"success": false, match result {
"error": error_msg.message, Ok(json_str) => env
}); .new_string(json_str)
Ok(response_json.to_string()) .unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to register: {:?}", e));
JObject::null().into_raw()
} }
} }
})(); })
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to register: {:?}", e));
JObject::null().into_raw()
}
}
} }
/// 启动TUN设备(Android使用,需要传入fd) /// 启动TUN设备(Android使用,需要传入fd)
@@ -203,48 +245,50 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeStartTun(
handle: jlong, handle: jlong,
tun_fd: jint, tun_fd: jint,
) -> jboolean { ) -> jboolean {
let result: anyhow::Result<()> = (|| { jni_guard!(env, 0, {
let (network_manager_arc, runtime) = { let result: anyhow::Result<()> = (|| {
let mut global_state = GLOBAL_STATE.lock(); let (network_manager_arc, runtime) = {
let state = global_state.as_mut().context("VNT not initialized")?; let mut global_state = GLOBAL_STATE.lock();
let state = global_state.as_mut().context("VNT not initialized")?;
let network_manager_arc = state let network_manager_arc = state
.network_managers .network_managers
.get(&handle) .get(&handle)
.context("Invalid handle")? .context("Invalid handle")?
.clone(); .clone();
let runtime = state.runtime.clone(); let runtime = state.runtime.clone();
(network_manager_arc, runtime) (network_manager_arc, runtime)
}; };
let mut manager_lock = network_manager_arc.lock(); let mut manager_lock = network_manager_arc.lock();
let manager = manager_lock let manager = manager_lock
.as_mut() .as_mut()
.context("Network manager already destroyed")?; .context("Network manager already destroyed")?;
#[cfg(unix)] #[cfg(unix)]
{ {
let tun_fd = if tun_fd < 0 { None } else { Some(tun_fd) }; let tun_fd = if tun_fd < 0 { None } else { Some(tun_fd) };
runtime.block_on(async { manager.start_tun_fd(tun_fd).await })?; runtime.block_on(async { manager.start_tun_fd(tun_fd).await })?;
}
#[cfg(not(unix))]
{
let _ = tun_fd; // 避免未使用警告
runtime.block_on(async { manager.start_tun().await })?;
}
Ok(())
})();
match result {
Ok(_) => 1,
Err(e) => {
let _ = env.throw(format!("Failed to start TUN: {:?}", e));
0
}
} }
})
#[cfg(not(unix))]
{
let _ = tun_fd; // 避免未使用警告
runtime.block_on(async { manager.start_tun().await })?;
}
Ok(())
})();
match result {
Ok(_) => 1,
Err(e) => {
let _ = env.throw(format!("Failed to start TUN: {:?}", e));
0
}
}
} }
/// 设置网络IP(非Android系统) /// 设置网络IP(非Android系统)
@@ -256,51 +300,54 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeSetNetworkIp<'local>(
ip: JString<'local>, ip: JString<'local>,
prefix_len: jint, prefix_len: jint,
) -> jboolean { ) -> jboolean {
let result: anyhow::Result<()> = (|| { jni_guard!(env, 0, {
let (network_manager_arc, runtime) = { let result: anyhow::Result<()> = (|| {
let mut global_state = GLOBAL_STATE.lock(); let (network_manager_arc, runtime) = {
let state = global_state.as_mut().context("VNT not initialized")?; let mut global_state = GLOBAL_STATE.lock();
let state = global_state.as_mut().context("VNT not initialized")?;
let network_manager_arc = state let network_manager_arc = state
.network_managers .network_managers
.get(&handle) .get(&handle)
.context("Invalid handle")? .context("Invalid handle")?
.clone(); .clone();
let runtime = state.runtime.clone(); let runtime = state.runtime.clone();
(network_manager_arc, runtime) (network_manager_arc, runtime)
}; };
let ip_str: String = env.get_string(&ip)?.into(); let ip_str: String = env.get_string(&ip)?.into();
let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?; let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?;
let manager_lock = network_manager_arc.lock(); let manager_lock = network_manager_arc.lock();
let manager = manager_lock let manager = manager_lock
.as_ref() .as_ref()
.context("Network manager already destroyed")?; .context("Network manager already destroyed")?;
#[cfg(not(target_os = "android"))] #[cfg(not(target_os = "android"))]
{ {
runtime runtime.block_on(async {
.block_on(async { manager.set_tun_network_ip(ip_addr, prefix_len as u8).await })?; manager.set_tun_network_ip(ip_addr, prefix_len as u8).await
})?;
}
#[cfg(target_os = "android")]
{
let _ = (ip_addr, prefix_len); // 避免未使用警告
anyhow::bail!("set_network_ip is not supported on Android");
}
Ok(())
})();
match result {
Ok(_) => 1,
Err(e) => {
let _ = env.throw(format!("Failed to set network IP: {:?}", e));
0
}
} }
})
#[cfg(target_os = "android")]
{
let _ = (ip_addr, prefix_len); // 避免未使用警告
anyhow::bail!("set_network_ip is not supported on Android");
}
Ok(())
})();
match result {
Ok(_) => 1,
Err(e) => {
let _ = env.throw(format!("Failed to set network IP: {:?}", e));
0
}
}
} }
/// 获取VntApi实例 /// 获取VntApi实例
@@ -310,35 +357,37 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeGetApi(
_class: JClass, _class: JClass,
handle: jlong, handle: jlong,
) -> jlong { ) -> jlong {
let result: anyhow::Result<i64> = (|| { jni_guard!(env, -1, {
let mut global_state = GLOBAL_STATE.lock(); let result: anyhow::Result<i64> = (|| {
let state = global_state.as_mut().context("VNT not initialized")?; let mut global_state = GLOBAL_STATE.lock();
let state = global_state.as_mut().context("VNT not initialized")?;
let network_manager_arc = state let network_manager_arc = state
.network_managers .network_managers
.get(&handle) .get(&handle)
.context("Invalid handle")? .context("Invalid handle")?
.clone(); .clone();
let api = { let api = {
let manager_lock = network_manager_arc.lock(); let manager_lock = network_manager_arc.lock();
let manager = manager_lock let manager = manager_lock
.as_ref() .as_ref()
.context("Network manager already destroyed")?; .context("Network manager already destroyed")?;
manager.vnt_api() manager.vnt_api()
}; };
state.vnt_apis.insert(handle, api); state.vnt_apis.insert(handle, api);
Ok(handle) Ok(handle)
})(); })();
match result { match result {
Ok(id) => id, Ok(id) => id,
Err(e) => { Err(e) => {
let _ = env.throw(format!("Failed to get API: {:?}", e)); let _ = env.throw(format!("Failed to get API: {:?}", e));
-1 -1
}
} }
} })
} }
/// 检查是否为无TUN模式 /// 检查是否为无TUN模式
@@ -348,37 +397,39 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeIsNoTun(
_class: JClass, _class: JClass,
handle: jlong, handle: jlong,
) -> jboolean { ) -> jboolean {
let result: anyhow::Result<bool> = (|| { jni_guard!(env, 0, {
let global_state = GLOBAL_STATE.lock(); let result: anyhow::Result<bool> = (|| {
let state = global_state.as_ref().context("VNT not initialized")?; let global_state = GLOBAL_STATE.lock();
let state = global_state.as_ref().context("VNT not initialized")?;
let network_manager_arc = state let network_manager_arc = state
.network_managers .network_managers
.get(&handle) .get(&handle)
.context("Invalid handle")? .context("Invalid handle")?
.clone(); .clone();
let manager_lock = network_manager_arc.lock(); let manager_lock = network_manager_arc.lock();
let manager = manager_lock let manager = manager_lock
.as_ref() .as_ref()
.context("Network manager already destroyed")?; .context("Network manager already destroyed")?;
Ok(manager.is_no_tun()) Ok(manager.is_no_tun())
})(); })();
match result { match result {
Ok(is_no_tun) => { Ok(is_no_tun) => {
if is_no_tun { if is_no_tun {
1 1
} else { } else {
0
}
}
Err(e) => {
let _ = env.throw(format!("Failed to check no_tun: {:?}", e));
0 0
} }
} }
Err(e) => { })
let _ = env.throw(format!("Failed to check no_tun: {:?}", e));
0
}
}
} }
/// 关闭网络 /// 关闭网络
@@ -388,32 +439,34 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeStop(
_class: JClass, _class: JClass,
handle: jlong, handle: jlong,
) -> jboolean { ) -> jboolean {
let result: anyhow::Result<()> = (|| { jni_guard!(env, 0, {
let mut global_state = GLOBAL_STATE.lock(); let result: anyhow::Result<()> = (|| {
let state = global_state.as_mut().context("VNT not initialized")?; let mut global_state = GLOBAL_STATE.lock();
let state = global_state.as_mut().context("VNT not initialized")?;
// 停止任务组 // 停止任务组
if let Some(task_group_manager) = state.task_group_managers.get(&handle) { if let Some(task_group_manager) = state.task_group_managers.get(&handle) {
task_group_manager.stop(); task_group_manager.stop();
}
// 移除网络管理器
state.network_managers.remove(&handle);
state.vnt_apis.remove(&handle);
state.task_group_managers.remove(&handle);
// 最后释放守卫(drop 时会停止任务组)
state.task_group_guards.remove(&handle);
Ok(())
})();
match result {
Ok(_) => 1,
Err(e) => {
let _ = env.throw(format!("Failed to stop network: {:?}", e));
0
}
} }
})
// 移除网络管理器
state.network_managers.remove(&handle);
state.vnt_apis.remove(&handle);
state.task_group_managers.remove(&handle);
// 最后释放守卫(drop 时会停止任务组)
state.task_group_guards.remove(&handle);
Ok(())
})();
match result {
Ok(_) => 1,
Err(e) => {
let _ = env.throw(format!("Failed to stop network: {:?}", e));
0
}
}
} }
// ==================== VntApi 接口 ==================== // ==================== VntApi 接口 ====================
@@ -425,38 +478,40 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetClientList<'local>(
_class: JClass<'local>, _class: JClass<'local>,
api_handle: jlong, api_handle: jlong,
) -> jstring { ) -> jstring {
let result: anyhow::Result<String> = (|| { jni_guard!(env, std::ptr::null_mut(), {
let global_state = GLOBAL_STATE.lock(); let result: anyhow::Result<String> = (|| {
let state = global_state.as_ref().context("VNT not initialized")?; let global_state = GLOBAL_STATE.lock();
let state = global_state.as_ref().context("VNT not initialized")?;
let api = state let api = state
.vnt_apis .vnt_apis
.get(&api_handle) .get(&api_handle)
.context("Invalid API handle")?; .context("Invalid API handle")?;
let client_ips = api.client_ips(); let client_ips = api.client_ips();
let json_array: Vec<_> = client_ips let json_array: Vec<_> = client_ips
.into_iter() .into_iter()
.map(|client| { .map(|client| {
serde_json::json!({ serde_json::json!({
"ip": client.ip.to_string(), "ip": client.ip.to_string(),
"online": client.online, "online": client.online,
})
}) })
}) .collect();
.collect(); Ok(serde_json::to_string(&json_array)?)
Ok(serde_json::to_string(&json_array)?) })();
})();
match result { match result {
Ok(json_str) => env Ok(json_str) => env
.new_string(json_str) .new_string(json_str)
.unwrap_or_else(|_| JObject::null().into()) .unwrap_or_else(|_| JObject::null().into())
.into_raw(), .into_raw(),
Err(e) => { Err(e) => {
let _ = env.throw(format!("Failed to get client list: {:?}", e)); let _ = env.throw(format!("Failed to get client list: {:?}", e));
JObject::null().into_raw() JObject::null().into_raw()
}
} }
} })
} }
/// 获取网络配置信息 /// 获取网络配置信息
@@ -466,38 +521,40 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetNetwork<'local>(
_class: JClass<'local>, _class: JClass<'local>,
api_handle: jlong, api_handle: jlong,
) -> jstring { ) -> jstring {
let result: anyhow::Result<String> = (|| { jni_guard!(env, std::ptr::null_mut(), {
let global_state = GLOBAL_STATE.lock(); let result: anyhow::Result<String> = (|| {
let state = global_state.as_ref().context("VNT not initialized")?; let global_state = GLOBAL_STATE.lock();
let state = global_state.as_ref().context("VNT not initialized")?;
let api = state let api = state
.vnt_apis .vnt_apis
.get(&api_handle) .get(&api_handle)
.context("Invalid API handle")?; .context("Invalid API handle")?;
if let Some(network) = api.network() { if let Some(network) = api.network() {
let json = serde_json::json!({ let json = serde_json::json!({
"ip": network.ip.to_string(), "ip": network.ip.to_string(),
"prefix_len": network.prefix_len, "prefix_len": network.prefix_len,
"gateway": network.gateway.to_string(), "gateway": network.gateway.to_string(),
"broadcast": network.broadcast.to_string(), "broadcast": network.broadcast.to_string(),
}); });
Ok(json.to_string()) Ok(json.to_string())
} else { } else {
Ok("null".to_string()) Ok("null".to_string())
}
})();
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to get network info: {:?}", e));
JObject::null().into_raw()
}
} }
})(); })
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to get network info: {:?}", e));
JObject::null().into_raw()
}
}
} }
/// 获取NAT信息 /// 获取NAT信息
@@ -507,37 +564,39 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetNatInfo<'local>(
_class: JClass<'local>, _class: JClass<'local>,
api_handle: jlong, api_handle: jlong,
) -> jstring { ) -> jstring {
let result: anyhow::Result<String> = (|| { jni_guard!(env, std::ptr::null_mut(), {
let global_state = GLOBAL_STATE.lock(); let result: anyhow::Result<String> = (|| {
let state = global_state.as_ref().context("VNT not initialized")?; let global_state = GLOBAL_STATE.lock();
let state = global_state.as_ref().context("VNT not initialized")?;
let api = state let api = state
.vnt_apis .vnt_apis
.get(&api_handle) .get(&api_handle)
.context("Invalid API handle")?; .context("Invalid API handle")?;
if let Some(nat_info) = api.nat_info() { if let Some(nat_info) = api.nat_info() {
let json = serde_json::json!({ let json = serde_json::json!({
"nat_type": format!("{:?}", nat_info.nat_type), "nat_type": format!("{:?}", nat_info.nat_type),
"public_ips": nat_info.public_ips.iter().map(|ip| ip.to_string()).collect::<Vec<_>>(), "public_ips": nat_info.public_ips.iter().map(|ip| ip.to_string()).collect::<Vec<_>>(),
"ipv6": nat_info.ipv6.map(|ip| ip.to_string()), "ipv6": nat_info.ipv6.map(|ip| ip.to_string()),
}); });
Ok(json.to_string()) Ok(json.to_string())
} else { } else {
Ok("null".to_string()) Ok("null".to_string())
}
})();
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to get NAT info: {:?}", e));
JObject::null().into_raw()
}
} }
})(); })
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to get NAT info: {:?}", e));
JObject::null().into_raw()
}
}
} }
/// 获取服务器节点列表 /// 获取服务器节点列表
@@ -547,42 +606,44 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetServerList<'local>(
_class: JClass<'local>, _class: JClass<'local>,
api_handle: jlong, api_handle: jlong,
) -> jstring { ) -> jstring {
let result: anyhow::Result<String> = (|| { jni_guard!(env, std::ptr::null_mut(), {
let global_state = GLOBAL_STATE.lock(); let result: anyhow::Result<String> = (|| {
let state = global_state.as_ref().context("VNT not initialized")?; let global_state = GLOBAL_STATE.lock();
let state = global_state.as_ref().context("VNT not initialized")?;
let api = state let api = state
.vnt_apis .vnt_apis
.get(&api_handle) .get(&api_handle)
.context("Invalid API handle")?; .context("Invalid API handle")?;
let servers = api.server_node_list(); let servers = api.server_node_list();
let json_array: Vec<_> = servers let json_array: Vec<_> = servers
.into_iter() .into_iter()
.map(|server| { .map(|server| {
serde_json::json!({ serde_json::json!({
"server_id": server.server_id, "server_id": server.server_id,
"server_addr": server.server_addr.to_string(), "server_addr": server.server_addr.to_string(),
"connected": server.connected, "connected": server.connected,
"rtt": server.rtt, "rtt": server.rtt,
"data_version": server.data_version, "data_version": server.data_version,
"server_version": server.server_version, "server_version": server.server_version,
})
}) })
}) .collect();
.collect(); Ok(serde_json::to_string(&json_array)?)
Ok(serde_json::to_string(&json_array)?) })();
})();
match result { match result {
Ok(json_str) => env Ok(json_str) => env
.new_string(json_str) .new_string(json_str)
.unwrap_or_else(|_| JObject::null().into()) .unwrap_or_else(|_| JObject::null().into())
.into_raw(), .into_raw(),
Err(e) => { Err(e) => {
let _ = env.throw(format!("Failed to get server list: {:?}", e)); let _ = env.throw(format!("Failed to get server list: {:?}", e));
JObject::null().into_raw() JObject::null().into_raw()
}
} }
} })
} }
/// 获取路由表 /// 获取路由表
@@ -592,50 +653,52 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetRouteTable<'local>(
_class: JClass<'local>, _class: JClass<'local>,
api_handle: jlong, api_handle: jlong,
) -> jstring { ) -> jstring {
let result: anyhow::Result<String> = (|| { jni_guard!(env, std::ptr::null_mut(), {
let global_state = GLOBAL_STATE.lock(); let result: anyhow::Result<String> = (|| {
let state = global_state.as_ref().context("VNT not initialized")?; let global_state = GLOBAL_STATE.lock();
let state = global_state.as_ref().context("VNT not initialized")?;
let api = state let api = state
.vnt_apis .vnt_apis
.get(&api_handle) .get(&api_handle)
.context("Invalid API handle")?; .context("Invalid API handle")?;
let route_table = api.route_table(); let route_table = api.route_table();
let json_data: Vec<_> = route_table let json_data: Vec<_> = route_table
.into_iter() .into_iter()
.map(|(ip, routes)| { .map(|(ip, routes)| {
let routes_json: Vec<_> = routes let routes_json: Vec<_> = routes
.into_iter() .into_iter()
.map(|route| { .map(|route| {
serde_json::json!({ serde_json::json!({
"route_key": route.route_key().to_string(), "route_key": route.route_key().to_string(),
"protocol": route.route_key().protocol().to_string(), "protocol": route.route_key().protocol().to_string(),
"metric": route.metric(), "metric": route.metric(),
"rtt": route.rtt(), "rtt": route.rtt(),
})
}) })
.collect();
serde_json::json!({
"ip": ip.to_string(),
"routes": routes_json,
}) })
.collect();
serde_json::json!({
"ip": ip.to_string(),
"routes": routes_json,
}) })
}) .collect();
.collect();
Ok(serde_json::to_string(&json_data)?) Ok(serde_json::to_string(&json_data)?)
})(); })();
match result { match result {
Ok(json_str) => env Ok(json_str) => env
.new_string(json_str) .new_string(json_str)
.unwrap_or_else(|_| JObject::null().into()) .unwrap_or_else(|_| JObject::null().into())
.into_raw(), .into_raw(),
Err(e) => { Err(e) => {
let _ = env.throw(format!("Failed to get route table: {:?}", e)); let _ = env.throw(format!("Failed to get route table: {:?}", e));
JObject::null().into_raw() JObject::null().into_raw()
}
} }
} })
} }
/// 检查目标IP是否直连 /// 检查目标IP是否直连
@@ -646,34 +709,36 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeIsDirect<'local>(
api_handle: jlong, api_handle: jlong,
ip: JString<'local>, ip: JString<'local>,
) -> jboolean { ) -> jboolean {
let result: anyhow::Result<bool> = (|| { jni_guard!(env, 0, {
let global_state = GLOBAL_STATE.lock(); let result: anyhow::Result<bool> = (|| {
let state = global_state.as_ref().context("VNT not initialized")?; let global_state = GLOBAL_STATE.lock();
let state = global_state.as_ref().context("VNT not initialized")?;
let api = state let api = state
.vnt_apis .vnt_apis
.get(&api_handle) .get(&api_handle)
.context("Invalid API handle")?; .context("Invalid API handle")?;
let ip_str: String = env.get_string(&ip)?.into(); let ip_str: String = env.get_string(&ip)?.into();
let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?; let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?;
Ok(api.is_direct(&ip_addr)) Ok(api.is_direct(&ip_addr))
})(); })();
match result { match result {
Ok(is_direct) => { Ok(is_direct) => {
if is_direct { if is_direct {
1 1
} else { } else {
0
}
}
Err(e) => {
let _ = env.throw(format!("Failed to check direct: {:?}", e));
0 0
} }
} }
Err(e) => { })
let _ = env.throw(format!("Failed to check direct: {:?}", e));
0
}
}
} }
/// 获取对端NAT信息 /// 获取对端NAT信息
@@ -684,40 +749,42 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetPeerNatInfo<'local>(
api_handle: jlong, api_handle: jlong,
ip: JString<'local>, ip: JString<'local>,
) -> jstring { ) -> jstring {
let result: anyhow::Result<String> = (|| { jni_guard!(env, std::ptr::null_mut(), {
let global_state = GLOBAL_STATE.lock(); let result: anyhow::Result<String> = (|| {
let state = global_state.as_ref().context("VNT not initialized")?; let global_state = GLOBAL_STATE.lock();
let state = global_state.as_ref().context("VNT not initialized")?;
let api = state let api = state
.vnt_apis .vnt_apis
.get(&api_handle) .get(&api_handle)
.context("Invalid API handle")?; .context("Invalid API handle")?;
let ip_str: String = env.get_string(&ip)?.into(); let ip_str: String = env.get_string(&ip)?.into();
let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?; let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?;
if let Some(nat_info) = api.peer_nat_info(&ip_addr) { if let Some(nat_info) = api.peer_nat_info(&ip_addr) {
let json = serde_json::json!({ let json = serde_json::json!({
"nat_type": format!("{:?}", nat_info.nat_type), "nat_type": format!("{:?}", nat_info.nat_type),
"public_ips": nat_info.public_ips.iter().map(|ip| ip.to_string()).collect::<Vec<_>>(), "public_ips": nat_info.public_ips.iter().map(|ip| ip.to_string()).collect::<Vec<_>>(),
"ipv6": nat_info.ipv6.map(|ip| ip.to_string()), "ipv6": nat_info.ipv6.map(|ip| ip.to_string()),
}); });
Ok(json.to_string()) Ok(json.to_string())
} else { } else {
Ok("null".to_string()) Ok("null".to_string())
}
})();
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to get peer NAT info: {:?}", e));
JObject::null().into_raw()
}
} }
})(); })
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to get peer NAT info: {:?}", e));
JObject::null().into_raw()
}
}
} }
/// 获取丢包信息 /// 获取丢包信息
@@ -728,41 +795,43 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetPacketLoss<'local>(
api_handle: jlong, api_handle: jlong,
ip: JString<'local>, ip: JString<'local>,
) -> jstring { ) -> jstring {
let result: anyhow::Result<String> = (|| { jni_guard!(env, std::ptr::null_mut(), {
let global_state = GLOBAL_STATE.lock(); let result: anyhow::Result<String> = (|| {
let state = global_state.as_ref().context("VNT not initialized")?; let global_state = GLOBAL_STATE.lock();
let state = global_state.as_ref().context("VNT not initialized")?;
let api = state let api = state
.vnt_apis .vnt_apis
.get(&api_handle) .get(&api_handle)
.context("Invalid API handle")?; .context("Invalid API handle")?;
let ip_str: String = env.get_string(&ip)?.into(); let ip_str: String = env.get_string(&ip)?.into();
let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?; let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?;
if let Some(loss_info) = api.packet_loss_info(&ip_addr) { if let Some(loss_info) = api.packet_loss_info(&ip_addr) {
let json = serde_json::json!({ let json = serde_json::json!({
"ip": loss_info.ip.to_string(), "ip": loss_info.ip.to_string(),
"sent": loss_info.sent, "sent": loss_info.sent,
"received": loss_info.received, "received": loss_info.received,
"loss_rate": loss_info.loss_rate, "loss_rate": loss_info.loss_rate,
}); });
Ok(json.to_string()) Ok(json.to_string())
} else { } else {
Ok("null".to_string()) Ok("null".to_string())
}
})();
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to get packet loss: {:?}", e));
JObject::null().into_raw()
}
} }
})(); })
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to get packet loss: {:?}", e));
JObject::null().into_raw()
}
}
} }
/// 获取流量信息 /// 获取流量信息
@@ -773,40 +842,42 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetTrafficInfo<'local>(
api_handle: jlong, api_handle: jlong,
ip: JString<'local>, ip: JString<'local>,
) -> jstring { ) -> jstring {
let result: anyhow::Result<String> = (|| { jni_guard!(env, std::ptr::null_mut(), {
let global_state = GLOBAL_STATE.lock(); let result: anyhow::Result<String> = (|| {
let state = global_state.as_ref().context("VNT not initialized")?; let global_state = GLOBAL_STATE.lock();
let state = global_state.as_ref().context("VNT not initialized")?;
let api = state let api = state
.vnt_apis .vnt_apis
.get(&api_handle) .get(&api_handle)
.context("Invalid API handle")?; .context("Invalid API handle")?;
let ip_str: String = env.get_string(&ip)?.into(); let ip_str: String = env.get_string(&ip)?.into();
let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?; let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?;
if let Some(traffic_info) = api.traffic_info(&ip_addr) { if let Some(traffic_info) = api.traffic_info(&ip_addr) {
let json = serde_json::json!({ let json = serde_json::json!({
"ip": traffic_info.ip.to_string(), "ip": traffic_info.ip.to_string(),
"tx_bytes": traffic_info.tx_bytes, "tx_bytes": traffic_info.tx_bytes,
"rx_bytes": traffic_info.rx_bytes, "rx_bytes": traffic_info.rx_bytes,
}); });
Ok(json.to_string()) Ok(json.to_string())
} else { } else {
Ok("null".to_string()) Ok("null".to_string())
}
})();
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to get traffic info: {:?}", e));
JObject::null().into_raw()
}
} }
})(); })
match result {
Ok(json_str) => env
.new_string(json_str)
.unwrap_or_else(|_| JObject::null().into())
.into_raw(),
Err(e) => {
let _ = env.throw(format!("Failed to get traffic info: {:?}", e));
JObject::null().into_raw()
}
}
} }
// ==================== 辅助函数 ==================== // ==================== 辅助函数 ====================
@@ -938,3 +1009,27 @@ fn parse_config_from_json(json_str: &str) -> anyhow::Result<Config> {
tunnel_port: cfg.tunnel_port, tunnel_port: cfg.tunnel_port,
}) })
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn catch_jni_panic_returns_value_unchanged() {
let result = catch_jni_panic(|| 42);
assert_eq!(result, Ok(42));
}
#[test]
fn catch_jni_panic_captures_str_message() {
let result: Result<(), String> = catch_jni_panic(|| panic!("boom"));
let err = result.unwrap_err();
assert!(err.contains("boom"), "unexpected message: {}", err);
}
#[test]
fn catch_jni_panic_captures_string_message() {
let result: Result<(), String> = catch_jni_panic(|| panic!("{}", "kaboom"));
assert_eq!(result.unwrap_err(), "kaboom");
}
}