diff --git a/vnt-jni/src/lib.rs b/vnt-jni/src/lib.rs index 514af30..340675d 100644 --- a/vnt-jni/src/lib.rs +++ b/vnt-jni/src/lib.rs @@ -1,7 +1,7 @@ use anyhow::Context; +use jni::JNIEnv; use jni::objects::{JClass, JObject, JString}; use jni::sys::{jboolean, jint, jlong, jstring}; -use jni::JNIEnv; use parking_lot::Mutex; use std::collections::HashMap; use std::net::Ipv4Addr; @@ -49,34 +49,72 @@ lazy_static::lazy_static! { static ref GLOBAL_STATE: Mutex> = Mutex::new(None); } +/// 从 panic payload 中提取错误消息 +fn panic_message(e: Box) -> String { + if let Some(s) = e.downcast_ref::<&str>() { + s.to_string() + } else if let Some(s) = e.downcast_ref::() { + s.clone() + } else { + "unknown panic".to_string() + } +} + +/// 捕获闭包内的 panic,转为 Err(消息),防止 panic 跨 FFI unwind 导致宿主 abort +fn catch_jni_panic(f: F) -> Result +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模块 #[unsafe(no_mangle)] pub extern "system" fn Java_com_vnt_VntManager_nativeInit( mut env: JNIEnv, _class: JClass, ) -> jboolean { - let mut state = GLOBAL_STATE.lock(); - if state.is_some() { - return 1; // 已经初始化 - } + jni_guard!(env, 0, { + let mut state = GLOBAL_STATE.lock(); + if state.is_some() { + return 1; // 已经初始化 + } - match GlobalState::new() { - Ok(global_state) => { - *state = Some(global_state); - 1 + match GlobalState::new() { + Ok(global_state) => { + *state = Some(global_state); + 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模块 #[unsafe(no_mangle)] pub extern "system" fn Java_com_vnt_VntManager_nativeDestroy(_env: JNIEnv, _class: JClass) { - let mut state = GLOBAL_STATE.lock(); - *state = None; + let _ = catch_jni_panic(|| { + 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>, config_json: JString<'local>, ) -> jlong { - let result: anyhow::Result = (|| { - let mut global_state = GLOBAL_STATE.lock(); - let state = global_state.as_mut().context("VNT not initialized")?; + jni_guard!(env, -1, { + let result: anyhow::Result = (|| { + let mut global_state = GLOBAL_STATE.lock(); + let state = global_state.as_mut().context("VNT not initialized")?; - // 解析配置JSON - let config_str: String = env.get_string(&config_json)?.into(); - let config = parse_config_from_json(&config_str)?; + // 解析配置JSON + let config_str: String = env.get_string(&config_json)?.into(); + let config = parse_config_from_json(&config_str)?; - // 创建任务组 - let task_group_manager = TaskGroupManager::new(); - let (task_group, task_group_guard) = task_group_manager - .create_task() - .context("create task group")?; + // 创建任务组 + let task_group_manager = TaskGroupManager::new(); + let (task_group, task_group_guard) = task_group_manager + .create_task() + .context("create task group")?; - // 获取runtime的clone - let runtime = state.runtime.clone(); + // 获取runtime的clone + let runtime = state.runtime.clone(); - // 创建网络管理器 - let network_manager = runtime.block_on(async { - NetworkManager::create_network(Box::new(config), task_group).await - })?; + // 创建网络管理器 + let network_manager = runtime.block_on(async { + NetworkManager::create_network(Box::new(config), task_group).await + })?; - // 分配ID - let id = state.next_id; - state.next_id += 1; + // 分配ID + let id = state.next_id; + state.next_id += 1; - // 保存实例(task_group_guard 必须随实例一直持有,drop 会停止整个任务组) - state - .network_managers - .insert(id, Arc::new(Mutex::new(Some(network_manager)))); - state.task_group_managers.insert(id, task_group_manager); - state.task_group_guards.insert(id, task_group_guard); + // 保存实例(task_group_guard 必须随实例一直持有,drop 会停止整个任务组) + state + .network_managers + .insert(id, Arc::new(Mutex::new(Some(network_manager)))); + state.task_group_managers.insert(id, task_group_manager); + state.task_group_guards.insert(id, task_group_guard); - Ok(id) - })(); + Ok(id) + })(); - match result { - Ok(id) => id, - Err(e) => { - let _ = env.throw(format!("Failed to create network: {:?}", e)); - -1 + match result { + Ok(id) => id, + Err(e) => { + let _ = env.throw(format!("Failed to create network: {:?}", e)); + -1 + } } - } + }) } /// 注册网络 @@ -138,61 +178,63 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeRegister<'local>( _class: JClass<'local>, handle: jlong, ) -> jstring { - let result: anyhow::Result = (|| { - let (network_manager_arc, runtime) = { - let mut global_state = GLOBAL_STATE.lock(); - let state = global_state.as_mut().context("VNT not initialized")?; + jni_guard!(env, std::ptr::null_mut(), { + let result: anyhow::Result = (|| { + let (network_manager_arc, runtime) = { + let mut global_state = GLOBAL_STATE.lock(); + let state = global_state.as_mut().context("VNT not initialized")?; - let network_manager_arc = state - .network_managers - .get(&handle) - .context("Invalid handle")? - .clone(); + let network_manager_arc = state + .network_managers + .get(&handle) + .context("Invalid handle")? + .clone(); - let runtime = state.runtime.clone(); - (network_manager_arc, runtime) - }; + let runtime = state.runtime.clone(); + (network_manager_arc, runtime) + }; - let response = { - let mut manager_lock = network_manager_arc.lock(); - let manager = manager_lock - .as_mut() - .context("Network manager already destroyed")?; + let response = { + let mut manager_lock = network_manager_arc.lock(); + let manager = manager_lock + .as_mut() + .context("Network manager already destroyed")?; - runtime.block_on(async { manager.register().await })? - }; + runtime.block_on(async { manager.register().await })? + }; - match response { - RegisterResponse::Success(network_addr) => { - let response_json = serde_json::json!({ - "success": true, - "ip": network_addr.ip.to_string(), - "prefix_len": network_addr.prefix_len, - "gateway": network_addr.gateway.to_string(), - "broadcast": network_addr.broadcast.to_string(), - }); - Ok(response_json.to_string()) + match response { + RegisterResponse::Success(network_addr) => { + let response_json = serde_json::json!({ + "success": true, + "ip": network_addr.ip.to_string(), + "prefix_len": network_addr.prefix_len, + "gateway": network_addr.gateway.to_string(), + "broadcast": network_addr.broadcast.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, - "error": error_msg.message, - }); - Ok(response_json.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 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) @@ -203,48 +245,50 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeStartTun( handle: jlong, tun_fd: jint, ) -> jboolean { - let result: anyhow::Result<()> = (|| { - let (network_manager_arc, runtime) = { - let mut global_state = GLOBAL_STATE.lock(); - let state = global_state.as_mut().context("VNT not initialized")?; + jni_guard!(env, 0, { + let result: anyhow::Result<()> = (|| { + let (network_manager_arc, runtime) = { + let mut global_state = GLOBAL_STATE.lock(); + let state = global_state.as_mut().context("VNT not initialized")?; - let network_manager_arc = state - .network_managers - .get(&handle) - .context("Invalid handle")? - .clone(); + let network_manager_arc = state + .network_managers + .get(&handle) + .context("Invalid handle")? + .clone(); - let runtime = state.runtime.clone(); - (network_manager_arc, runtime) - }; + let runtime = state.runtime.clone(); + (network_manager_arc, runtime) + }; - let mut manager_lock = network_manager_arc.lock(); - let manager = manager_lock - .as_mut() - .context("Network manager already destroyed")?; + let mut manager_lock = network_manager_arc.lock(); + let manager = manager_lock + .as_mut() + .context("Network manager already destroyed")?; - #[cfg(unix)] - { - let tun_fd = if tun_fd < 0 { None } else { Some(tun_fd) }; - runtime.block_on(async { manager.start_tun_fd(tun_fd).await })?; + #[cfg(unix)] + { + let tun_fd = if tun_fd < 0 { None } else { Some(tun_fd) }; + 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系统) @@ -256,51 +300,54 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeSetNetworkIp<'local>( ip: JString<'local>, prefix_len: jint, ) -> jboolean { - let result: anyhow::Result<()> = (|| { - let (network_manager_arc, runtime) = { - let mut global_state = GLOBAL_STATE.lock(); - let state = global_state.as_mut().context("VNT not initialized")?; + jni_guard!(env, 0, { + let result: anyhow::Result<()> = (|| { + let (network_manager_arc, runtime) = { + let mut global_state = GLOBAL_STATE.lock(); + let state = global_state.as_mut().context("VNT not initialized")?; - let network_manager_arc = state - .network_managers - .get(&handle) - .context("Invalid handle")? - .clone(); + let network_manager_arc = state + .network_managers + .get(&handle) + .context("Invalid handle")? + .clone(); - let runtime = state.runtime.clone(); - (network_manager_arc, runtime) - }; + let runtime = state.runtime.clone(); + (network_manager_arc, runtime) + }; - let ip_str: String = env.get_string(&ip)?.into(); - let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?; + let ip_str: String = env.get_string(&ip)?.into(); + let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?; - let manager_lock = network_manager_arc.lock(); - let manager = manager_lock - .as_ref() - .context("Network manager already destroyed")?; + let manager_lock = network_manager_arc.lock(); + let manager = manager_lock + .as_ref() + .context("Network manager already destroyed")?; - #[cfg(not(target_os = "android"))] - { - runtime - .block_on(async { manager.set_tun_network_ip(ip_addr, prefix_len as u8).await })?; + #[cfg(not(target_os = "android"))] + { + runtime.block_on(async { + 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实例 @@ -310,35 +357,37 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeGetApi( _class: JClass, handle: jlong, ) -> jlong { - let result: anyhow::Result = (|| { - let mut global_state = GLOBAL_STATE.lock(); - let state = global_state.as_mut().context("VNT not initialized")?; + jni_guard!(env, -1, { + let result: anyhow::Result = (|| { + let mut global_state = GLOBAL_STATE.lock(); + let state = global_state.as_mut().context("VNT not initialized")?; - let network_manager_arc = state - .network_managers - .get(&handle) - .context("Invalid handle")? - .clone(); + let network_manager_arc = state + .network_managers + .get(&handle) + .context("Invalid handle")? + .clone(); - let api = { - let manager_lock = network_manager_arc.lock(); - let manager = manager_lock - .as_ref() - .context("Network manager already destroyed")?; - manager.vnt_api() - }; + let api = { + let manager_lock = network_manager_arc.lock(); + let manager = manager_lock + .as_ref() + .context("Network manager already destroyed")?; + manager.vnt_api() + }; - state.vnt_apis.insert(handle, api); - Ok(handle) - })(); + state.vnt_apis.insert(handle, api); + Ok(handle) + })(); - match result { - Ok(id) => id, - Err(e) => { - let _ = env.throw(format!("Failed to get API: {:?}", e)); - -1 + match result { + Ok(id) => id, + Err(e) => { + let _ = env.throw(format!("Failed to get API: {:?}", e)); + -1 + } } - } + }) } /// 检查是否为无TUN模式 @@ -348,37 +397,39 @@ pub extern "system" fn Java_com_vnt_VntNetwork_nativeIsNoTun( _class: JClass, handle: jlong, ) -> jboolean { - let result: anyhow::Result = (|| { - let global_state = GLOBAL_STATE.lock(); - let state = global_state.as_ref().context("VNT not initialized")?; + jni_guard!(env, 0, { + let result: anyhow::Result = (|| { + let global_state = GLOBAL_STATE.lock(); + let state = global_state.as_ref().context("VNT not initialized")?; - let network_manager_arc = state - .network_managers - .get(&handle) - .context("Invalid handle")? - .clone(); + let network_manager_arc = state + .network_managers + .get(&handle) + .context("Invalid handle")? + .clone(); - let manager_lock = network_manager_arc.lock(); - let manager = manager_lock - .as_ref() - .context("Network manager already destroyed")?; + let manager_lock = network_manager_arc.lock(); + let manager = manager_lock + .as_ref() + .context("Network manager already destroyed")?; - Ok(manager.is_no_tun()) - })(); + Ok(manager.is_no_tun()) + })(); - match result { - Ok(is_no_tun) => { - if is_no_tun { - 1 - } else { + match result { + Ok(is_no_tun) => { + if is_no_tun { + 1 + } else { + 0 + } + } + Err(e) => { + let _ = env.throw(format!("Failed to check no_tun: {:?}", e)); 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, handle: jlong, ) -> jboolean { - let result: anyhow::Result<()> = (|| { - let mut global_state = GLOBAL_STATE.lock(); - let state = global_state.as_mut().context("VNT not initialized")?; + jni_guard!(env, 0, { + let result: anyhow::Result<()> = (|| { + 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) { - task_group_manager.stop(); + // 停止任务组 + if let Some(task_group_manager) = state.task_group_managers.get(&handle) { + 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 接口 ==================== @@ -425,38 +478,40 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetClientList<'local>( _class: JClass<'local>, api_handle: jlong, ) -> jstring { - let result: anyhow::Result = (|| { - let global_state = GLOBAL_STATE.lock(); - let state = global_state.as_ref().context("VNT not initialized")?; + jni_guard!(env, std::ptr::null_mut(), { + let result: anyhow::Result = (|| { + let global_state = GLOBAL_STATE.lock(); + let state = global_state.as_ref().context("VNT not initialized")?; - let api = state - .vnt_apis - .get(&api_handle) - .context("Invalid API handle")?; + let api = state + .vnt_apis + .get(&api_handle) + .context("Invalid API handle")?; - let client_ips = api.client_ips(); - let json_array: Vec<_> = client_ips - .into_iter() - .map(|client| { - serde_json::json!({ - "ip": client.ip.to_string(), - "online": client.online, + let client_ips = api.client_ips(); + let json_array: Vec<_> = client_ips + .into_iter() + .map(|client| { + serde_json::json!({ + "ip": client.ip.to_string(), + "online": client.online, + }) }) - }) - .collect(); - Ok(serde_json::to_string(&json_array)?) - })(); + .collect(); + Ok(serde_json::to_string(&json_array)?) + })(); - 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 client list: {:?}", 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 client list: {:?}", e)); + JObject::null().into_raw() + } } - } + }) } /// 获取网络配置信息 @@ -466,38 +521,40 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetNetwork<'local>( _class: JClass<'local>, api_handle: jlong, ) -> jstring { - let result: anyhow::Result = (|| { - let global_state = GLOBAL_STATE.lock(); - let state = global_state.as_ref().context("VNT not initialized")?; + jni_guard!(env, std::ptr::null_mut(), { + let result: anyhow::Result = (|| { + let global_state = GLOBAL_STATE.lock(); + let state = global_state.as_ref().context("VNT not initialized")?; - let api = state - .vnt_apis - .get(&api_handle) - .context("Invalid API handle")?; + let api = state + .vnt_apis + .get(&api_handle) + .context("Invalid API handle")?; - if let Some(network) = api.network() { - let json = serde_json::json!({ - "ip": network.ip.to_string(), - "prefix_len": network.prefix_len, - "gateway": network.gateway.to_string(), - "broadcast": network.broadcast.to_string(), - }); - Ok(json.to_string()) - } else { - Ok("null".to_string()) + if let Some(network) = api.network() { + let json = serde_json::json!({ + "ip": network.ip.to_string(), + "prefix_len": network.prefix_len, + "gateway": network.gateway.to_string(), + "broadcast": network.broadcast.to_string(), + }); + Ok(json.to_string()) + } else { + 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信息 @@ -507,37 +564,39 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetNatInfo<'local>( _class: JClass<'local>, api_handle: jlong, ) -> jstring { - let result: anyhow::Result = (|| { - let global_state = GLOBAL_STATE.lock(); - let state = global_state.as_ref().context("VNT not initialized")?; + jni_guard!(env, std::ptr::null_mut(), { + let result: anyhow::Result = (|| { + let global_state = GLOBAL_STATE.lock(); + let state = global_state.as_ref().context("VNT not initialized")?; - let api = state - .vnt_apis - .get(&api_handle) - .context("Invalid API handle")?; + let api = state + .vnt_apis + .get(&api_handle) + .context("Invalid API handle")?; - if let Some(nat_info) = api.nat_info() { - let json = serde_json::json!({ - "nat_type": format!("{:?}", nat_info.nat_type), - "public_ips": nat_info.public_ips.iter().map(|ip| ip.to_string()).collect::>(), - "ipv6": nat_info.ipv6.map(|ip| ip.to_string()), - }); - Ok(json.to_string()) - } else { - Ok("null".to_string()) + if let Some(nat_info) = api.nat_info() { + let json = serde_json::json!({ + "nat_type": format!("{:?}", nat_info.nat_type), + "public_ips": nat_info.public_ips.iter().map(|ip| ip.to_string()).collect::>(), + "ipv6": nat_info.ipv6.map(|ip| ip.to_string()), + }); + Ok(json.to_string()) + } else { + 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>, api_handle: jlong, ) -> jstring { - let result: anyhow::Result = (|| { - let global_state = GLOBAL_STATE.lock(); - let state = global_state.as_ref().context("VNT not initialized")?; + jni_guard!(env, std::ptr::null_mut(), { + let result: anyhow::Result = (|| { + let global_state = GLOBAL_STATE.lock(); + let state = global_state.as_ref().context("VNT not initialized")?; - let api = state - .vnt_apis - .get(&api_handle) - .context("Invalid API handle")?; + let api = state + .vnt_apis + .get(&api_handle) + .context("Invalid API handle")?; - let servers = api.server_node_list(); - let json_array: Vec<_> = servers - .into_iter() - .map(|server| { - serde_json::json!({ - "server_id": server.server_id, - "server_addr": server.server_addr.to_string(), - "connected": server.connected, - "rtt": server.rtt, - "data_version": server.data_version, - "server_version": server.server_version, + let servers = api.server_node_list(); + let json_array: Vec<_> = servers + .into_iter() + .map(|server| { + serde_json::json!({ + "server_id": server.server_id, + "server_addr": server.server_addr.to_string(), + "connected": server.connected, + "rtt": server.rtt, + "data_version": server.data_version, + "server_version": server.server_version, + }) }) - }) - .collect(); - Ok(serde_json::to_string(&json_array)?) - })(); + .collect(); + Ok(serde_json::to_string(&json_array)?) + })(); - 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 server list: {:?}", 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 server list: {:?}", e)); + JObject::null().into_raw() + } } - } + }) } /// 获取路由表 @@ -592,50 +653,52 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetRouteTable<'local>( _class: JClass<'local>, api_handle: jlong, ) -> jstring { - let result: anyhow::Result = (|| { - let global_state = GLOBAL_STATE.lock(); - let state = global_state.as_ref().context("VNT not initialized")?; + jni_guard!(env, std::ptr::null_mut(), { + let result: anyhow::Result = (|| { + let global_state = GLOBAL_STATE.lock(); + let state = global_state.as_ref().context("VNT not initialized")?; - let api = state - .vnt_apis - .get(&api_handle) - .context("Invalid API handle")?; + let api = state + .vnt_apis + .get(&api_handle) + .context("Invalid API handle")?; - let route_table = api.route_table(); - let json_data: Vec<_> = route_table - .into_iter() - .map(|(ip, routes)| { - let routes_json: Vec<_> = routes - .into_iter() - .map(|route| { - serde_json::json!({ - "route_key": route.route_key().to_string(), - "protocol": route.route_key().protocol().to_string(), - "metric": route.metric(), - "rtt": route.rtt(), + let route_table = api.route_table(); + let json_data: Vec<_> = route_table + .into_iter() + .map(|(ip, routes)| { + let routes_json: Vec<_> = routes + .into_iter() + .map(|route| { + serde_json::json!({ + "route_key": route.route_key().to_string(), + "protocol": route.route_key().protocol().to_string(), + "metric": route.metric(), + "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 { - 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 route table: {:?}", 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 route table: {:?}", e)); + JObject::null().into_raw() + } } - } + }) } /// 检查目标IP是否直连 @@ -646,34 +709,36 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeIsDirect<'local>( api_handle: jlong, ip: JString<'local>, ) -> jboolean { - let result: anyhow::Result = (|| { - let global_state = GLOBAL_STATE.lock(); - let state = global_state.as_ref().context("VNT not initialized")?; + jni_guard!(env, 0, { + let result: anyhow::Result = (|| { + let global_state = GLOBAL_STATE.lock(); + let state = global_state.as_ref().context("VNT not initialized")?; - let api = state - .vnt_apis - .get(&api_handle) - .context("Invalid API handle")?; + let api = state + .vnt_apis + .get(&api_handle) + .context("Invalid API handle")?; - let ip_str: String = env.get_string(&ip)?.into(); - let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?; + let ip_str: String = env.get_string(&ip)?.into(); + 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 { - Ok(is_direct) => { - if is_direct { - 1 - } else { + match result { + Ok(is_direct) => { + if is_direct { + 1 + } else { + 0 + } + } + Err(e) => { + let _ = env.throw(format!("Failed to check direct: {:?}", e)); 0 } } - Err(e) => { - let _ = env.throw(format!("Failed to check direct: {:?}", e)); - 0 - } - } + }) } /// 获取对端NAT信息 @@ -684,40 +749,42 @@ pub extern "system" fn Java_com_vnt_VntApi_nativeGetPeerNatInfo<'local>( api_handle: jlong, ip: JString<'local>, ) -> jstring { - let result: anyhow::Result = (|| { - let global_state = GLOBAL_STATE.lock(); - let state = global_state.as_ref().context("VNT not initialized")?; + jni_guard!(env, std::ptr::null_mut(), { + let result: anyhow::Result = (|| { + let global_state = GLOBAL_STATE.lock(); + let state = global_state.as_ref().context("VNT not initialized")?; - let api = state - .vnt_apis - .get(&api_handle) - .context("Invalid API handle")?; + let api = state + .vnt_apis + .get(&api_handle) + .context("Invalid API handle")?; - let ip_str: String = env.get_string(&ip)?.into(); - let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?; + let ip_str: String = env.get_string(&ip)?.into(); + let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?; - if let Some(nat_info) = api.peer_nat_info(&ip_addr) { - let json = serde_json::json!({ - "nat_type": format!("{:?}", nat_info.nat_type), - "public_ips": nat_info.public_ips.iter().map(|ip| ip.to_string()).collect::>(), - "ipv6": nat_info.ipv6.map(|ip| ip.to_string()), - }); - Ok(json.to_string()) - } else { - Ok("null".to_string()) + if let Some(nat_info) = api.peer_nat_info(&ip_addr) { + let json = serde_json::json!({ + "nat_type": format!("{:?}", nat_info.nat_type), + "public_ips": nat_info.public_ips.iter().map(|ip| ip.to_string()).collect::>(), + "ipv6": nat_info.ipv6.map(|ip| ip.to_string()), + }); + Ok(json.to_string()) + } else { + 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, ip: JString<'local>, ) -> jstring { - let result: anyhow::Result = (|| { - let global_state = GLOBAL_STATE.lock(); - let state = global_state.as_ref().context("VNT not initialized")?; + jni_guard!(env, std::ptr::null_mut(), { + let result: anyhow::Result = (|| { + let global_state = GLOBAL_STATE.lock(); + let state = global_state.as_ref().context("VNT not initialized")?; - let api = state - .vnt_apis - .get(&api_handle) - .context("Invalid API handle")?; + let api = state + .vnt_apis + .get(&api_handle) + .context("Invalid API handle")?; - let ip_str: String = env.get_string(&ip)?.into(); - let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?; + let ip_str: String = env.get_string(&ip)?.into(); + let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?; - if let Some(loss_info) = api.packet_loss_info(&ip_addr) { - let json = serde_json::json!({ - "ip": loss_info.ip.to_string(), - "sent": loss_info.sent, - "received": loss_info.received, - "loss_rate": loss_info.loss_rate, - }); - Ok(json.to_string()) - } else { - Ok("null".to_string()) + if let Some(loss_info) = api.packet_loss_info(&ip_addr) { + let json = serde_json::json!({ + "ip": loss_info.ip.to_string(), + "sent": loss_info.sent, + "received": loss_info.received, + "loss_rate": loss_info.loss_rate, + }); + Ok(json.to_string()) + } else { + 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, ip: JString<'local>, ) -> jstring { - let result: anyhow::Result = (|| { - let global_state = GLOBAL_STATE.lock(); - let state = global_state.as_ref().context("VNT not initialized")?; + jni_guard!(env, std::ptr::null_mut(), { + let result: anyhow::Result = (|| { + let global_state = GLOBAL_STATE.lock(); + let state = global_state.as_ref().context("VNT not initialized")?; - let api = state - .vnt_apis - .get(&api_handle) - .context("Invalid API handle")?; + let api = state + .vnt_apis + .get(&api_handle) + .context("Invalid API handle")?; - let ip_str: String = env.get_string(&ip)?.into(); - let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?; + let ip_str: String = env.get_string(&ip)?.into(); + let ip_addr: Ipv4Addr = ip_str.parse().context("Invalid IP address")?; - if let Some(traffic_info) = api.traffic_info(&ip_addr) { - let json = serde_json::json!({ - "ip": traffic_info.ip.to_string(), - "tx_bytes": traffic_info.tx_bytes, - "rx_bytes": traffic_info.rx_bytes, - }); - Ok(json.to_string()) - } else { - Ok("null".to_string()) + if let Some(traffic_info) = api.traffic_info(&ip_addr) { + let json = serde_json::json!({ + "ip": traffic_info.ip.to_string(), + "tx_bytes": traffic_info.tx_bytes, + "rx_bytes": traffic_info.rx_bytes, + }); + Ok(json.to_string()) + } else { + 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 { 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"); + } +}