mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-04-23 00:27:06 +08:00
841d525913
This change introduces a major refactoring of the RPC service layer to improve modularity, unify the API, and simplify the overall architecture. Key changes: - Replaced per-network-instance RPC services with a single global RPC server, reducing resource usage and simplifying management. - All clients (CLI, Web UI, etc.) now interact with EasyTier core through a unified RPC entrypoint, enabling consistent authentication and control. - RPC implementation logic has been moved to `easytier/src/rpc_service/` and organized by functionality (e.g., `instance_manage.rs`, `peer_manage.rs`, `config.rs`) for better maintainability. - Standardized Protobuf API definitions under `easytier/src/proto/` with an `api_` prefix (e.g., `cli.proto` → `api_instance.proto`) to provide a consistent interface. - CLI commands now require explicit `--instance-id` or `--instance-name` when multiple network instances are running; the parameter is optional when only one instance exists. BREAKING CHANGE: RPC portal configuration (`rpc_portal` and `rpc_portal_whitelist`) has been removed from per-instance configs and the Web UI. The RPC listen address must now be specified globally via the `--rpc-portal` command-line flag or the `ET_RPC_PORTAL` environment variable, as there is only one RPC service for the entire application.
320 lines
9.1 KiB
Rust
320 lines
9.1 KiB
Rust
use easytier::proto::api::manage::{NetworkInstanceRunningInfo, NetworkInstanceRunningInfoMap};
|
|
use jni::objects::{JClass, JObjectArray, JString};
|
|
use jni::sys::{jint, jstring};
|
|
use jni::JNIEnv;
|
|
use once_cell::sync::Lazy;
|
|
use std::ffi::{CStr, CString};
|
|
use std::ptr;
|
|
|
|
// 定义 KeyValuePair 结构体
|
|
#[repr(C)]
|
|
#[derive(Clone, Copy)]
|
|
pub struct KeyValuePair {
|
|
pub key: *const std::ffi::c_char,
|
|
pub value: *const std::ffi::c_char,
|
|
}
|
|
|
|
// 声明外部 C 函数
|
|
extern "C" {
|
|
fn set_tun_fd(inst_name: *const std::ffi::c_char, fd: std::ffi::c_int) -> std::ffi::c_int;
|
|
fn get_error_msg(out: *mut *const std::ffi::c_char);
|
|
fn free_string(s: *const std::ffi::c_char);
|
|
fn parse_config(cfg_str: *const std::ffi::c_char) -> std::ffi::c_int;
|
|
fn run_network_instance(cfg_str: *const std::ffi::c_char) -> std::ffi::c_int;
|
|
fn retain_network_instance(
|
|
inst_names: *const *const std::ffi::c_char,
|
|
length: usize,
|
|
) -> std::ffi::c_int;
|
|
fn collect_network_infos(infos: *mut KeyValuePair, max_length: usize) -> std::ffi::c_int;
|
|
}
|
|
|
|
// 初始化 Android 日志
|
|
static LOGGER_INIT: Lazy<()> = Lazy::new(|| {
|
|
android_logger::init_once(
|
|
android_logger::Config::default()
|
|
.with_max_level(log::LevelFilter::Debug)
|
|
.with_tag("EasyTier-JNI"),
|
|
);
|
|
});
|
|
|
|
// 辅助函数:从 Java String 转换为 CString
|
|
fn jstring_to_cstring(env: &mut JNIEnv, jstr: &JString) -> Result<CString, String> {
|
|
let java_str = env
|
|
.get_string(jstr)
|
|
.map_err(|e| format!("Failed to get string: {:?}", e))?;
|
|
let rust_str = java_str.to_str().map_err(|_| "Invalid UTF-8".to_string())?;
|
|
CString::new(rust_str).map_err(|_| "String contains null byte".to_string())
|
|
}
|
|
|
|
// 辅助函数:获取错误消息
|
|
fn get_last_error() -> Option<String> {
|
|
unsafe {
|
|
let mut error_ptr: *const std::ffi::c_char = ptr::null();
|
|
get_error_msg(&mut error_ptr);
|
|
if error_ptr.is_null() {
|
|
None
|
|
} else {
|
|
let error_cstr = CStr::from_ptr(error_ptr);
|
|
let error_str = error_cstr.to_string_lossy().into_owned();
|
|
free_string(error_ptr);
|
|
Some(error_str)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 辅助函数:抛出 Java 异常
|
|
fn throw_exception(env: &mut JNIEnv, message: &str) {
|
|
let _ = env.throw_new("java/lang/RuntimeException", message);
|
|
}
|
|
|
|
/// 设置 TUN 文件描述符
|
|
#[no_mangle]
|
|
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_setTunFd(
|
|
mut env: JNIEnv,
|
|
_class: JClass,
|
|
inst_name: JString,
|
|
fd: jint,
|
|
) -> jint {
|
|
Lazy::force(&LOGGER_INIT);
|
|
|
|
let inst_name_cstr = match jstring_to_cstring(&mut env, &inst_name) {
|
|
Ok(cstr) => cstr,
|
|
Err(e) => {
|
|
throw_exception(&mut env, &format!("Invalid instance name: {}", e));
|
|
return -1;
|
|
}
|
|
};
|
|
|
|
unsafe {
|
|
let result = set_tun_fd(inst_name_cstr.as_ptr(), fd);
|
|
if result != 0 {
|
|
if let Some(error) = get_last_error() {
|
|
throw_exception(&mut env, &error);
|
|
}
|
|
}
|
|
result
|
|
}
|
|
}
|
|
|
|
/// 解析配置
|
|
#[no_mangle]
|
|
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_parseConfig(
|
|
mut env: JNIEnv,
|
|
_class: JClass,
|
|
config: JString,
|
|
) -> jint {
|
|
Lazy::force(&LOGGER_INIT);
|
|
|
|
let config_cstr = match jstring_to_cstring(&mut env, &config) {
|
|
Ok(cstr) => cstr,
|
|
Err(e) => {
|
|
throw_exception(&mut env, &format!("Invalid config string: {}", e));
|
|
return -1;
|
|
}
|
|
};
|
|
|
|
unsafe {
|
|
let result = parse_config(config_cstr.as_ptr());
|
|
if result != 0 {
|
|
if let Some(error) = get_last_error() {
|
|
throw_exception(&mut env, &error);
|
|
}
|
|
}
|
|
result
|
|
}
|
|
}
|
|
|
|
/// 运行网络实例
|
|
#[no_mangle]
|
|
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_runNetworkInstance(
|
|
mut env: JNIEnv,
|
|
_class: JClass,
|
|
config: JString,
|
|
) -> jint {
|
|
Lazy::force(&LOGGER_INIT);
|
|
|
|
let config_cstr = match jstring_to_cstring(&mut env, &config) {
|
|
Ok(cstr) => cstr,
|
|
Err(e) => {
|
|
throw_exception(&mut env, &format!("Invalid config string: {}", e));
|
|
return -1;
|
|
}
|
|
};
|
|
|
|
unsafe {
|
|
let result = run_network_instance(config_cstr.as_ptr());
|
|
if result != 0 {
|
|
if let Some(error) = get_last_error() {
|
|
throw_exception(&mut env, &error);
|
|
}
|
|
}
|
|
result
|
|
}
|
|
}
|
|
|
|
/// 保持网络实例
|
|
#[no_mangle]
|
|
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_retainNetworkInstance(
|
|
mut env: JNIEnv,
|
|
_class: JClass,
|
|
instance_names: JObjectArray,
|
|
) -> jint {
|
|
Lazy::force(&LOGGER_INIT);
|
|
|
|
// 处理 null 数组的情况
|
|
if instance_names.is_null() {
|
|
unsafe {
|
|
let result = retain_network_instance(ptr::null(), 0);
|
|
if result != 0 {
|
|
if let Some(error) = get_last_error() {
|
|
throw_exception(&mut env, &error);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// 获取数组长度
|
|
let array_length = match env.get_array_length(&instance_names) {
|
|
Ok(len) => len as usize,
|
|
Err(e) => {
|
|
throw_exception(&mut env, &format!("Failed to get array length: {:?}", e));
|
|
return -1;
|
|
}
|
|
};
|
|
|
|
// 如果数组为空,停止所有实例
|
|
if array_length == 0 {
|
|
unsafe {
|
|
let result = retain_network_instance(ptr::null(), 0);
|
|
if result != 0 {
|
|
if let Some(error) = get_last_error() {
|
|
throw_exception(&mut env, &error);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// 转换 Java 字符串数组为 C 字符串数组
|
|
let mut c_strings = Vec::with_capacity(array_length);
|
|
let mut c_string_ptrs = Vec::with_capacity(array_length);
|
|
|
|
for i in 0..array_length {
|
|
let java_string = match env.get_object_array_element(&instance_names, i as i32) {
|
|
Ok(obj) => obj,
|
|
Err(e) => {
|
|
throw_exception(
|
|
&mut env,
|
|
&format!("Failed to get array element {}: {:?}", i, e),
|
|
);
|
|
return -1;
|
|
}
|
|
};
|
|
|
|
if java_string.is_null() {
|
|
continue; // 跳过 null 元素
|
|
}
|
|
|
|
let jstring = JString::from(java_string);
|
|
let c_string = match jstring_to_cstring(&mut env, &jstring) {
|
|
Ok(cstr) => cstr,
|
|
Err(e) => {
|
|
throw_exception(
|
|
&mut env,
|
|
&format!("Invalid instance name at index {}: {}", i, e),
|
|
);
|
|
return -1;
|
|
}
|
|
};
|
|
|
|
c_string_ptrs.push(c_string.as_ptr());
|
|
c_strings.push(c_string); // 保持 CString 的所有权
|
|
}
|
|
|
|
unsafe {
|
|
let result = retain_network_instance(c_string_ptrs.as_ptr(), c_string_ptrs.len());
|
|
if result != 0 {
|
|
if let Some(error) = get_last_error() {
|
|
throw_exception(&mut env, &error);
|
|
}
|
|
}
|
|
result
|
|
}
|
|
}
|
|
|
|
/// 收集网络信息
|
|
#[no_mangle]
|
|
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_collectNetworkInfos(
|
|
mut env: JNIEnv,
|
|
_class: JClass,
|
|
) -> jstring {
|
|
Lazy::force(&LOGGER_INIT);
|
|
|
|
const MAX_INFOS: usize = 100;
|
|
let mut infos = vec![
|
|
KeyValuePair {
|
|
key: ptr::null(),
|
|
value: ptr::null(),
|
|
};
|
|
MAX_INFOS
|
|
];
|
|
|
|
unsafe {
|
|
let count = collect_network_infos(infos.as_mut_ptr(), MAX_INFOS);
|
|
if count < 0 {
|
|
if let Some(error) = get_last_error() {
|
|
throw_exception(&mut env, &error);
|
|
}
|
|
return ptr::null_mut();
|
|
}
|
|
|
|
let mut ret = NetworkInstanceRunningInfoMap::default();
|
|
|
|
// 使用 serde_json 构建 JSON
|
|
for info in infos.iter().take(count as usize) {
|
|
let key_ptr = info.key;
|
|
let val_ptr = info.value;
|
|
if key_ptr.is_null() || val_ptr.is_null() {
|
|
break;
|
|
}
|
|
|
|
let key = CStr::from_ptr(key_ptr).to_string_lossy();
|
|
let val = CStr::from_ptr(val_ptr).to_string_lossy();
|
|
let value = match serde_json::from_str::<NetworkInstanceRunningInfo>(val.as_ref()) {
|
|
Ok(v) => v,
|
|
Err(_) => {
|
|
throw_exception(&mut env, "Failed to parse JSON");
|
|
continue;
|
|
}
|
|
};
|
|
ret.map.insert(key.to_string(), value);
|
|
}
|
|
|
|
let json_str = serde_json::to_string(&ret).unwrap_or_else(|_| "{}".to_string());
|
|
|
|
match env.new_string(&json_str) {
|
|
Ok(jstr) => jstr.into_raw(),
|
|
Err(_) => {
|
|
throw_exception(&mut env, "Failed to create JSON string");
|
|
ptr::null_mut()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 获取最后的错误信息
|
|
#[no_mangle]
|
|
pub extern "system" fn Java_com_easytier_jni_EasyTierJNI_getLastError(
|
|
env: JNIEnv,
|
|
_class: JClass,
|
|
) -> jstring {
|
|
match get_last_error() {
|
|
Some(error) => match env.new_string(&error) {
|
|
Ok(jstr) => jstr.into_raw(),
|
|
Err(_) => ptr::null_mut(),
|
|
},
|
|
None => ptr::null_mut(),
|
|
}
|
|
}
|