From a6050e5f59d5eb78ccb26b145175aa35c01009e3 Mon Sep 17 00:00:00 2001 From: lubeilin <1791778603@qq.com> Date: Wed, 5 Jul 2023 23:37:30 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0jni=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 2 +- switch-jni/Cargo.toml | 13 ++ switch-jni/src/lib.rs | 2 + switch-jni/src/switch.rs | 113 ++++++++++++++++ switch-jni/src/switch_util.rs | 245 ++++++++++++++++++++++++++++++++++ 5 files changed, 374 insertions(+), 1 deletion(-) create mode 100644 switch-jni/Cargo.toml create mode 100644 switch-jni/src/lib.rs create mode 100644 switch-jni/src/switch.rs create mode 100644 switch-jni/src/switch_util.rs diff --git a/Cargo.toml b/Cargo.toml index e9267e2..8cac4b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["switch","switch-desktop"] +members = ["switch","switch-desktop","switch-mini","switch-jni"] [profile.release] opt-level = 'z' diff --git a/switch-jni/Cargo.toml b/switch-jni/Cargo.toml new file mode 100644 index 0000000..f0d2119 --- /dev/null +++ b/switch-jni/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "switch-jni" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +switch = {path="../switch"} + +jni = { version = "0.21.1", default-features = false } +[lib] +crate-type = ["staticlib", "cdylib"] \ No newline at end of file diff --git a/switch-jni/src/lib.rs b/switch-jni/src/lib.rs new file mode 100644 index 0000000..71a4409 --- /dev/null +++ b/switch-jni/src/lib.rs @@ -0,0 +1,2 @@ +pub mod switch_util; +pub mod switch; \ No newline at end of file diff --git a/switch-jni/src/switch.rs b/switch-jni/src/switch.rs new file mode 100644 index 0000000..1d3217f --- /dev/null +++ b/switch-jni/src/switch.rs @@ -0,0 +1,113 @@ +use std::ptr; +use jni::errors::Error; +use jni::JNIEnv; +use jni::objects::{JClass, JObject, JValue}; +use jni::sys::{jbyte, jint, jlong, jobject, jobjectArray, jsize}; +use switch::channel::Route; +use switch::core::sync::SwitchSync; +use switch::handle::PeerDeviceInfo; + +#[no_mangle] +pub unsafe extern "C" fn Java_top_wherewego_switchjni_Switch_stop0( + _env: JNIEnv, + _class: JClass, + raw_switch: jlong, +) { + let switch = raw_switch as *mut SwitchSync; + let _ = (&*switch).stop(); +} + +#[no_mangle] +pub unsafe extern "C" fn Java_top_wherewego_switchjni_Switch_waitStop0( + _env: JNIEnv, + _class: JClass, + raw_switch: jlong, +) { + let switch = raw_switch as *mut SwitchSync; + let _ = (&mut *switch).wait_stop(); +} + +#[no_mangle] +pub unsafe extern "C" fn Java_top_wherewego_switchjni_Switch_list0( + mut env: JNIEnv, + _class: JClass, + raw_switch: jlong, +) -> jobjectArray { + let switch = raw_switch as *mut SwitchSync; + let switch = &mut *switch; + let list = switch.device_list(); + + let arr = match env.new_object_array( + list.len() as jsize, + "top/wherewego/switchjni/PeerDeviceInfo", + JObject::null(), + ) { + Ok(arr) => { arr } + Err(e) => { + env.throw_new("java/lang/RuntimeException", format!("error:{:?}", e)) + .expect("throw"); + return ptr::null_mut(); + } + }; + for (index, peer) in list.into_iter().enumerate() { + let route = if let Some(route) = switch.route(&peer.virtual_ip) { + match route_parse(&mut env, route) { + Ok(route) => { + JObject::from_raw(route) + } + Err(_) => { + JObject::null() + } + } + } else { + JObject::null() + }; + match peer_device_info_parse(&mut env, peer, route) { + Ok(peer) => { + match env.set_object_array_element(&arr, index as jsize, JObject::from_raw(peer)) { + Ok(_) => {} + Err(e) => { + env.throw_new("java/lang/RuntimeException", format!("error:{:?}", e)) + .expect("throw"); + return ptr::null_mut() + } + } + } + Err(e) => { + env.throw_new("java/lang/RuntimeException", format!("error:{:?}", e)) + .expect("throw"); + return ptr::null_mut() + } + } + } + arr.as_raw() +} + +fn route_parse(env: &mut JNIEnv, route: Route) -> Result { + let address = route.addr.to_string(); + let metric = route.metric; + let rt = route.rt; + let rs = env.new_object( + "top/wherewego/switchjni/Route", + "(Ljava/lang/String;BI)V", + &[JValue::Object(&env.new_string(address)?.into()), + JValue::Byte(metric as jbyte), + JValue::Int(rt as jint)], + )?; + Ok(rs.as_raw()) +} + +fn peer_device_info_parse(env: &mut JNIEnv, peer: PeerDeviceInfo, route: JObject) -> Result { + let virtual_ip = u32::from(peer.virtual_ip); + let name = peer.name.to_string(); + let status = format!("{:?}", peer.status); + let rs = env.new_object( + "top/wherewego/switchjni/PeerDeviceInfo", + "(ILjava/lang/String;Ljava/lang/String;Ltop/wherewego/switchjni/Route;)V", + &[JValue::Int(virtual_ip as jint), + JValue::Object(&env.new_string(name)?.into()), + JValue::Object(&env.new_string(status)?.into()), + JValue::Object(&route)], + )?; + Ok(rs.as_raw()) +} \ No newline at end of file diff --git a/switch-jni/src/switch_util.rs b/switch-jni/src/switch_util.rs new file mode 100644 index 0000000..492f2a5 --- /dev/null +++ b/switch-jni/src/switch_util.rs @@ -0,0 +1,245 @@ +use std::net::ToSocketAddrs; +use std::ptr; + +use jni::errors::Error; +use jni::objects::{JClass, JObject, JString, JValue}; +#[cfg(not(target_os = "android"))] +use jni::sys::jboolean; +use jni::sys::{jint, jlong, jobject}; +use jni::JNIEnv; +use switch::core::Config; +use switch::core::sync::SwitchUtilSync; +use switch::handle::registration_handler::{RegResponse, ReqEnum}; +#[cfg(not(target_os = "android"))] +use switch::tun_tap_device::DriverInfo; + +fn to_string_not_null(env: &mut JNIEnv, config: &JObject, name: &'static str) -> Result { + let value = env.get_field(config, name, "Ljava/lang/String;")?.l()?; + if value.is_null() { + env.throw_new("Ljava/lang/NullPointerException", name) + .expect("throw"); + return Err(Error::NullPtr(name)); + } + let binding = JString::from(value); + let value = env.get_string(binding.as_ref())?; + match value.to_str() { + Ok(value) => Ok(value.to_string()), + Err(_) => { + env.throw_new("Ljava/lang/RuntimeException", "not utf-8") + .expect("throw"); + return Err(Error::JavaException); + } + } +} + +fn to_string(env: &mut JNIEnv, config: &JObject, name: &str) -> Result, Error> { + let value = env.get_field(config, name, "Ljava/lang/String;")?.l()?; + if value.is_null() { + return Ok(None); + } + let tmp = JString::from(value); + let value = env.get_string(tmp.as_ref())?; + match value.to_str() { + Ok(value) => Ok(Some(value.to_string())), + Err(_) => { + env.throw_new("Ljava/lang/RuntimeException", "not utf-8") + .expect("throw"); + return Err(Error::JavaException); + } + } +} + +fn new_sync(env: &mut JNIEnv, config: JObject) -> Result { + let token = to_string_not_null(env, &config, "token")?; + let name = to_string_not_null(env, &config, "name")?; + let device_id = to_string_not_null(env, &config, "deviceId")?; + let server = to_string_not_null(env, &config, "server")?; + let nat_test_server = to_string_not_null(env, &config, "natTestServer")?; + let server_address = match server.to_socket_addrs() { + Ok(mut rs) => { + if let Some(addr) = rs.next() { + addr + } else { + env.throw_new("Ljava/lang/RuntimeException", "server address err") + .expect("throw"); + return Err(Error::JavaException); + } + } + Err(e) => { + env.throw_new("Ljava/lang/RuntimeException", format!("server address {}", e)) + .expect("throw"); + return Err(Error::JavaException); + } + }; + let nat_test_server = nat_test_server.split(",").flat_map(|a| a.trim().to_socket_addrs()).flatten() + .collect::>(); + let config = Config::new(false, + token, device_id, name, + server_address, + nat_test_server, vec![], + vec![], None, false, ); + match SwitchUtilSync::new(config) { + Ok(switch_util) => { + Ok(switch_util) + } + Err(e) => { + env.throw_new("Ljava/lang/RuntimeException", format!("switch start error {}", e)) + .expect("throw"); + return Err(Error::JavaException); + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_new0( + mut env: JNIEnv, + _class: JClass, + config: JObject, +) -> jlong { + match new_sync(&mut env, config) { + Ok(switch_util) => { + let ptr = Box::into_raw(Box::new(switch_util)); + return ptr as jlong; + } + Err(_) => {} + } + return 0; +} + +#[no_mangle] +pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_connect0( + mut env: JNIEnv, + _class: JClass, + raw_switch_util: jlong, +) -> jobject { + let raw_switch_util = raw_switch_util as *mut SwitchUtilSync; + match (&mut *raw_switch_util).connect() { + Ok(response) => { + match reg_response(&mut env, response) { + Ok(res) => { + return res; + } + Err(e) => { + env.throw(format!("switch connect error {}", e)).expect("throw"); + } + } + } + Err(e) => { + match e { + ReqEnum::TokenError => { + env.throw_new("top/wherewego/switchjni/exception/TokenErrorException", "TokenError") + .expect("throw"); + } + ReqEnum::AddressExhausted => { + env.throw_new("top/wherewego/switchjni/exception/AddressExhaustedException", "AddressExhausted") + .expect("throw"); + } + ReqEnum::Timeout => { + env.throw_new("top/wherewego/switchjni/exception/TimeoutException", "Timeout") + .expect("throw"); + } + ReqEnum::ServerError(str) => { + env.throw_new("java/lang/RuntimeException", format!("switch connect error {}", str)) + .expect("throw"); + } + ReqEnum::Other(str) => { + env.throw_new("java/lang/RuntimeException", format!("switch connect error {}", str)) + .expect("throw"); + } + } + } + } + return ptr::null_mut(); +} + +#[cfg(target_os = "android")] +#[no_mangle] +pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_createIface0( + _env: JNIEnv, + _class: JClass, + raw_switch_util: jlong, + fd: jint, +) { + let raw_switch_util = raw_switch_util as *mut SwitchUtilSync; + + (&mut *raw_switch_util).create_iface(fd as i32); +} + +#[cfg(not(target_os = "android"))] +#[no_mangle] +pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_createIface0( + mut env: JNIEnv, + _class: JClass, + raw_switch_util: jlong, +) -> jobject { + let raw_switch_util = raw_switch_util as *mut SwitchUtilSync; + #[cfg(not(target_os = "android"))] + let rs = (&mut *raw_switch_util).create_iface(); + match rs { + Ok(driver_info) => { + match driver_info_e(&mut env, driver_info) { + Ok(res) => { + return res; + } + Err(e) => { + env.throw(format!("switch create iface error {}", e)).expect("throw"); + } + } + } + Err(e) => { + env.throw_new("java/lang/RuntimeException", format!("switch create iface error {}", e)) + .expect("throw"); + } + } + return ptr::null_mut(); +} + +#[no_mangle] +pub unsafe extern "C" fn Java_top_wherewego_switchjni_SwitchUtil_build0( + mut env: JNIEnv, + _class: JClass, + raw_switch_util: jlong, +) -> jlong { + let raw_switch_util = Box::from_raw(raw_switch_util as *mut SwitchUtilSync); + match raw_switch_util.build() { + Ok(rs) => { + return Box::into_raw(Box::new(rs)) as jlong; + } + Err(e) => { + env.throw_new("java/lang/RuntimeException", format!("switch start error:{:?}", e)) + .expect("throw"); + } + } + return 0; +} + +fn reg_response(env: &mut JNIEnv, response: RegResponse) -> Result { + let virtual_ip = u32::from(response.virtual_ip); + let virtual_gateway = u32::from(response.virtual_gateway); + let virtual_netmask = u32::from(response.virtual_netmask); + let response = env.new_object( + "top/wherewego/switchjni/RegResponse", + "(III)V", + &[JValue::Int(virtual_ip as jint), + JValue::Int(virtual_gateway as jint), + JValue::Int(virtual_netmask as jint)], + )?; + Ok(response.into_raw()) +} + +#[cfg(not(target_os = "android"))] +fn driver_info_e(env: &mut JNIEnv, driver_info: DriverInfo) -> Result { + let is_tun = driver_info.device_type.is_tun(); + let name = driver_info.name; + let version = driver_info.version; + let mac = driver_info.mac.unwrap_or(String::new()); + let response = env.new_object( + "top/wherewego/switchjni/DriverInfo", + "(ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", + &[JValue::Bool(is_tun as jboolean), + JValue::Object(&env.new_string(name)?.into()), + JValue::Object(&env.new_string(version)?.into()), + JValue::Object(&env.new_string(mac)?.into()), ], + )?; + Ok(response.into_raw()) +}