Files
vnt/vnt-jni/src/lib.rs
T
lbl af4b5d9657 refactor(jni): remove legacy no_tun config field
fix(ui): clarify mapping exit description
2026-08-22 22:38:38 +08:00

1038 lines
32 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use anyhow::Context;
use jni::JNIEnv;
use jni::objects::{JClass, JObject, JString};
use jni::sys::{jboolean, jint, jlong, jstring};
use parking_lot::Mutex;
use std::collections::HashMap;
use std::net::Ipv4Addr;
use std::sync::Arc;
use tokio::runtime::Runtime;
use vnt_core::api::VntApi;
use vnt_core::context::config::{Config, DeviceMode};
use vnt_core::core::{NetworkManager, RegisterResponse};
use vnt_core::nat::NetInput;
use vnt_core::port_mapping::PortMapping;
use vnt_core::tls::verifier::CertValidationMode;
use vnt_core::tunnel_core::server::transport::config::ProtocolAddress;
use vnt_core::utils::task_control::{TaskGroupGuard, TaskGroupManager};
/// 全局状态管理
struct GlobalState {
/// Tokio运行时(Arc包装以便多线程访问)
runtime: Arc<Runtime>,
/// 网络管理器实例
network_managers: HashMap<i64, Arc<Mutex<Option<NetworkManager>>>>,
/// API实例
vnt_apis: HashMap<i64, VntApi>,
/// 任务组管理器
task_group_managers: HashMap<i64, TaskGroupManager>,
/// 任务组守卫(drop 时会停止任务组,必须持有到 nativeStop
task_group_guards: HashMap<i64, TaskGroupGuard>,
/// 下一个实例ID
next_id: i64,
}
impl GlobalState {
fn new() -> anyhow::Result<Self> {
Ok(Self {
runtime: Arc::new(Runtime::new()?),
network_managers: HashMap::new(),
vnt_apis: HashMap::new(),
task_group_managers: HashMap::new(),
task_group_guards: HashMap::new(),
next_id: 1,
})
}
}
lazy_static::lazy_static! {
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模块
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntManager_nativeInit(
mut env: JNIEnv,
_class: JClass,
) -> jboolean {
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
}
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 _ = catch_jni_panic(|| {
let mut state = GLOBAL_STATE.lock();
*state = None;
});
}
/// 创建网络实例
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntManager_nativeCreateNetwork<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
config_json: JString<'local>,
) -> jlong {
jni_guard!(env, -1, {
let result: anyhow::Result<i64> = (|| {
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)?;
// 创建任务组
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();
// 创建网络管理器
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;
// 保存实例(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)
})();
match result {
Ok(id) => id,
Err(e) => {
let _ = env.throw(format!("Failed to create network: {:?}", e));
-1
}
}
})
}
/// 注册网络
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntNetwork_nativeRegister<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
handle: jlong,
) -> jstring {
jni_guard!(env, std::ptr::null_mut(), {
let result: anyhow::Result<String> = (|| {
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 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")?;
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())
}
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()
}
}
})
}
/// 启动TUN设备(Android使用,需要传入fd)
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntNetwork_nativeStartTun(
mut env: JNIEnv,
_class: JClass,
handle: jlong,
tun_fd: jint,
) -> jboolean {
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 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")?;
#[cfg(unix)]
{
let tun_fd = if tun_fd < 0 { None } else { Some(tun_fd) };
runtime.block_on(async { manager.start_device_fd(tun_fd).await })?;
}
#[cfg(not(unix))]
{
let _ = tun_fd; // 避免未使用警告
runtime.block_on(async { manager.start_device().await })?;
}
Ok(())
})();
match result {
Ok(_) => 1,
Err(e) => {
let _ = env.throw(format!("Failed to start TUN: {:?}", e));
0
}
}
})
}
/// 设置网络IP(非Android系统)
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntNetwork_nativeSetNetworkIp<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
handle: jlong,
ip: JString<'local>,
prefix_len: jint,
) -> jboolean {
#[cfg(target_os = "android")]
{
let _ = (handle, ip, prefix_len);
let _ = env.throw("set_network_ip is not supported on Android");
0
}
#[cfg(not(target_os = "android"))]
{
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 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 manager_lock = network_manager_arc.lock();
let manager = manager_lock
.as_ref()
.context("Network manager already destroyed")?;
runtime.block_on(async {
manager
.set_device_network_ip(ip_addr, prefix_len as u8)
.await
})?;
Ok(())
})();
match result {
Ok(_) => 1,
Err(e) => {
let _ = env.throw(format!("Failed to set network IP: {:?}", e));
0
}
}
})
}
}
/// 获取VntApi实例
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntNetwork_nativeGetApi(
mut env: JNIEnv,
_class: JClass,
handle: jlong,
) -> jlong {
jni_guard!(env, -1, {
let result: anyhow::Result<i64> = (|| {
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 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)
})();
match result {
Ok(id) => id,
Err(e) => {
let _ = env.throw(format!("Failed to get API: {:?}", e));
-1
}
}
})
}
/// 检查是否为无TUN模式
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntNetwork_nativeIsNoTun(
mut env: JNIEnv,
_class: JClass,
handle: jlong,
) -> jboolean {
jni_guard!(env, 0, {
let result: anyhow::Result<bool> = (|| {
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 manager_lock = network_manager_arc.lock();
let manager = manager_lock
.as_ref()
.context("Network manager already destroyed")?;
Ok(manager.device_mode() == DeviceMode::No)
})();
match result {
Ok(is_no_device) => {
if is_no_device {
1
} else {
0
}
}
Err(e) => {
let _ = env.throw(format!("Failed to check device mode: {:?}", e));
0
}
}
})
}
/// 关闭网络
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntNetwork_nativeStop(
mut env: JNIEnv,
_class: JClass,
handle: jlong,
) -> jboolean {
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();
}
// 移除网络管理器
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 接口 ====================
/// 获取客户端列表
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntApi_nativeGetClientList<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
api_handle: jlong,
) -> jstring {
jni_guard!(env, std::ptr::null_mut(), {
let result: anyhow::Result<String> = (|| {
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 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)?)
})();
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()
}
}
})
}
/// 获取网络配置信息
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntApi_nativeGetNetwork<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
api_handle: jlong,
) -> jstring {
jni_guard!(env, std::ptr::null_mut(), {
let result: anyhow::Result<String> = (|| {
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")?;
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()
}
}
})
}
/// 获取NAT信息
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntApi_nativeGetNatInfo<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
api_handle: jlong,
) -> jstring {
jni_guard!(env, std::ptr::null_mut(), {
let result: anyhow::Result<String> = (|| {
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")?;
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::<Vec<_>>(),
"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()
}
}
})
}
/// 获取服务器节点列表
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntApi_nativeGetServerList<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
api_handle: jlong,
) -> jstring {
jni_guard!(env, std::ptr::null_mut(), {
let result: anyhow::Result<String> = (|| {
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 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)?)
})();
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()
}
}
})
}
/// 获取路由表
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntApi_nativeGetRouteTable<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
api_handle: jlong,
) -> jstring {
jni_guard!(env, std::ptr::null_mut(), {
let result: anyhow::Result<String> = (|| {
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 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();
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()
}
}
})
}
/// 检查目标IP是否直连
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntApi_nativeIsDirect<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
api_handle: jlong,
ip: JString<'local>,
) -> jboolean {
jni_guard!(env, 0, {
let result: anyhow::Result<bool> = (|| {
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 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))
})();
match result {
Ok(is_direct) => {
if is_direct {
1
} else {
0
}
}
Err(e) => {
let _ = env.throw(format!("Failed to check direct: {:?}", e));
0
}
}
})
}
/// 获取对端NAT信息
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntApi_nativeGetPeerNatInfo<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
api_handle: jlong,
ip: JString<'local>,
) -> jstring {
jni_guard!(env, std::ptr::null_mut(), {
let result: anyhow::Result<String> = (|| {
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 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::<Vec<_>>(),
"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()
}
}
})
}
/// 获取丢包信息
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntApi_nativeGetPacketLoss<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
api_handle: jlong,
ip: JString<'local>,
) -> jstring {
jni_guard!(env, std::ptr::null_mut(), {
let result: anyhow::Result<String> = (|| {
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 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())
}
})();
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()
}
}
})
}
/// 获取流量信息
#[unsafe(no_mangle)]
pub extern "system" fn Java_com_vnt_VntApi_nativeGetTrafficInfo<'local>(
mut env: JNIEnv<'local>,
_class: JClass<'local>,
api_handle: jlong,
ip: JString<'local>,
) -> jstring {
jni_guard!(env, std::ptr::null_mut(), {
let result: anyhow::Result<String> = (|| {
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 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())
}
})();
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()
}
}
})
}
// ==================== 辅助函数 ====================
/// 从JSON字符串解析配置
fn parse_config_from_json(json_str: &str) -> anyhow::Result<Config> {
#[derive(serde::Deserialize)]
struct ConfigJson {
server: Vec<String>,
network_code: String,
#[serde(default)]
device_id: Option<String>,
#[serde(default)]
device_name: Option<String>,
#[serde(default)]
tun_name: Option<String>,
#[serde(default)]
outbound_interface: Option<String>,
#[serde(default)]
ip: Option<Ipv4Addr>,
#[serde(default)]
password: Option<String>,
#[serde(default)]
cert_mode: Option<String>,
#[serde(default)]
no_punch: bool,
#[serde(default)]
compress: bool,
#[serde(default)]
rtx: bool,
#[serde(default)]
fec: bool,
#[serde(default)]
input: Vec<NetInput>,
#[serde(default)]
output: Vec<ipnet::Ipv4Net>,
#[serde(default)]
no_nat: bool,
#[serde(default)]
device_mode: DeviceMode,
#[serde(default)]
mtu: Option<u16>,
#[serde(default)]
port_mapping: Vec<String>,
#[serde(default)]
allow_mapping: bool,
#[serde(default)]
udp_stun: Vec<String>,
#[serde(default)]
tcp_stun: Vec<String>,
#[serde(default)]
tunnel_port: Option<u16>,
}
let cfg: ConfigJson = serde_json::from_str(json_str)?;
let server_addrs: Vec<ProtocolAddress> = cfg
.server
.iter()
.map(|s| {
s.parse()
.map_err(|e| anyhow::anyhow!("invalid server address '{}': {}", s, e))
})
.collect::<anyhow::Result<_>>()?;
let port_mapping: Vec<PortMapping> = cfg
.port_mapping
.iter()
.map(|s| {
s.parse()
.map_err(|e| anyhow::anyhow!("invalid port_mapping '{}': {}", s, e))
})
.collect::<anyhow::Result<_>>()?;
let cert_mode = match cfg.cert_mode.as_deref() {
Some(s) => s
.parse()
.map_err(|e| anyhow::anyhow!("invalid cert_mode '{}': {}", s, e))?,
None => CertValidationMode::InsecureSkipVerification,
};
let device_id = match cfg.device_id {
Some(id) => id,
None => vnt_core::utils::device_id::get_device_id()
.map_err(|e| anyhow::anyhow!("failed to get device_id: {}", e))?,
};
let device_name = cfg.device_name.unwrap_or_else(|| {
hostname::get()
.ok()
.and_then(|v| v.into_string().ok())
.unwrap_or_default()
});
let mut udp_stun = cfg.udp_stun;
for x in udp_stun.iter_mut() {
if !x.contains(':') {
x.push_str(":3478");
}
}
let mut tcp_stun = cfg.tcp_stun;
for x in tcp_stun.iter_mut() {
if !x.contains(':') {
x.push_str(":3478");
}
}
Ok(Config {
server_addr: server_addrs,
network_code: cfg.network_code,
ip: cfg.ip,
no_punch: cfg.no_punch,
rtx: cfg.rtx,
compress: cfg.compress,
device_id,
device_name,
tun_name: cfg.tun_name,
outbound_interface: cfg.outbound_interface,
password: cfg.password,
cert_mode,
input: cfg.input,
output: cfg.output,
no_nat: cfg.no_nat,
device_mode: cfg.device_mode,
mtu: cfg.mtu,
port_mapping,
allow_port_mapping: cfg.allow_mapping,
udp_stun,
tcp_stun,
fec: cfg.fec,
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");
}
}