From a1083273ee125c28f5e9f2252b46cf9105888004 Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Tue, 13 Feb 2024 10:46:13 +0800 Subject: [PATCH] Ffi2 (#90) --- Cargo.toml | 2 +- src/android.rs | 4 +- src/bin/main.rs | 69 +--------------- src/desktop_api.rs | 148 ++++++++++++++++++++++++++++++++++ src/dump_logger.rs | 9 ++- src/ffi.rs | 140 -------------------------------- src/ios.rs | 4 +- src/lib.rs | 10 ++- src/{api.rs => mobile_api.rs} | 31 +++---- 9 files changed, 187 insertions(+), 230 deletions(-) create mode 100644 src/desktop_api.rs delete mode 100644 src/ffi.rs rename src/{api.rs => mobile_api.rs} (70%) diff --git a/Cargo.toml b/Cargo.toml index 7bbd75b..f224f95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ tokio = { version = "1.36", features = ["full"] } tokio-util = "0.7" tproxy-config = { version = "0.1", features = ["log"] } trust-dns-proto = "0.23" -tun2 = { version = "1.0", features = ["async"] } +tun2 = { version = "1.1", features = ["async"] } udp-stream = { version = "0.0", default-features = false } unicase = "2.7" url = "2.5" diff --git a/src/android.rs b/src/android.rs index 7afee0f..f89bb00 100644 --- a/src/android.rs +++ b/src/android.rs @@ -39,7 +39,7 @@ pub unsafe extern "C" fn Java_com_github_shadowsocks_bg_Tun2proxy_run( let mut args = Args::default(); args.proxy(proxy).tun_fd(Some(tun_fd)).dns(dns).verbosity(verbosity); - crate::api::tun2proxy_internal_run(args, tun_mtu) + crate::mobile_api::mobile_run(args, tun_mtu) } /// # Safety @@ -47,7 +47,7 @@ pub unsafe extern "C" fn Java_com_github_shadowsocks_bg_Tun2proxy_run( /// Shutdown tun2proxy #[no_mangle] pub unsafe extern "C" fn Java_com_github_shadowsocks_bg_Tun2proxy_stop(_env: JNIEnv, _: JClass) -> jint { - crate::api::tun2proxy_internal_stop() + crate::mobile_api::mobile_stop() } unsafe fn get_java_string<'a>(env: &'a mut JNIEnv, string: &'a JString) -> Result<&'a str, Error> { diff --git a/src/bin/main.rs b/src/bin/main.rs index 675faee..d54748b 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -1,72 +1,16 @@ -use tokio_util::sync::CancellationToken; -use tproxy_config::{TproxyArgs, TUN_GATEWAY, TUN_IPV4, TUN_NETMASK}; -use tun2::DEFAULT_MTU as MTU; -use tun2proxy::{self, Args}; +use tun2proxy::{desktop_run_async, Args, BoxError}; #[tokio::main] -async fn main() -> Result<(), Box> { +async fn main() -> Result<(), BoxError> { dotenvy::dotenv().ok(); let args = Args::parse_args(); - let bypass_ips = args.bypass.clone(); - // let default = format!("{}={:?}", module_path!(), args.verbosity); let default = format!("{:?}", args.verbosity); env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(default)).init(); - let mut config = tun2::Configuration::default(); - config.address(TUN_IPV4).netmask(TUN_NETMASK).mtu(MTU).up(); - config.destination(TUN_GATEWAY); - if let Some(tun_fd) = args.tun_fd { - config.raw_fd(tun_fd); - } else { - config.name(&args.tun); - } - - #[cfg(target_os = "linux")] - config.platform_config(|config| { - #[allow(deprecated)] - config.packet_information(true); - config.ensure_root_privileges(args.setup); - }); - - #[cfg(target_os = "windows")] - config.platform_config(|config| { - config.device_guid(Some(12324323423423434234_u128)); - }); - - #[allow(unused_variables)] - let mut tproxy_args = TproxyArgs::new() - .tun_dns(args.dns_addr) - .proxy_addr(args.proxy.addr) - .bypass_ips(&bypass_ips); - #[allow(unused_assignments)] - if args.tun_fd.is_none() { - tproxy_args = tproxy_args.tun_name(&args.tun); - } - - #[allow(unused_mut, unused_assignments, unused_variables)] - let mut setup = true; - - #[cfg(target_os = "linux")] - { - setup = args.setup; - if setup { - tproxy_config::tproxy_setup(&tproxy_args)?; - } - } - - let device = tun2::create_as_async(&config)?; - - #[cfg(any(target_os = "windows", target_os = "macos"))] - if setup { - tproxy_config::tproxy_setup(&tproxy_args)?; - } - - log::info!("Proxy {} server: {}", args.proxy.proxy_type, args.proxy.addr); - - let shutdown_token = CancellationToken::new(); - let join_handle = tokio::spawn(tun2proxy::run(device, MTU, args, shutdown_token.clone())); + let shutdown_token = tokio_util::sync::CancellationToken::new(); + let join_handle = tokio::spawn(desktop_run_async(args, shutdown_token.clone())); ctrlc2::set_async_handler(async move { log::info!("Ctrl-C received, exiting..."); @@ -78,10 +22,5 @@ async fn main() -> Result<(), Box> { log::trace!("main_entry error {}", err); } - #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] - if setup { - tproxy_config::tproxy_remove(&tproxy_args)?; - } - Ok(()) } diff --git a/src/desktop_api.rs b/src/desktop_api.rs new file mode 100644 index 0000000..5eda1ee --- /dev/null +++ b/src/desktop_api.rs @@ -0,0 +1,148 @@ +#![cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))] + +use crate::{ + args::{ArgDns, ArgProxy}, + ArgVerbosity, Args, +}; +use std::os::raw::{c_char, c_int}; +use tproxy_config::{TproxyArgs, TUN_GATEWAY, TUN_IPV4, TUN_NETMASK}; +use tun2::DEFAULT_MTU as MTU; + +static TUN_QUIT: std::sync::Mutex> = std::sync::Mutex::new(None); + +/// # Safety +/// +/// Run the tun2proxy component with some arguments. +#[no_mangle] +pub unsafe extern "C" fn tun2proxy_run_with_name( + proxy_url: *const c_char, + tun: *const c_char, + bypass: *const c_char, + dns_strategy: ArgDns, + _root_privilege: bool, + verbosity: ArgVerbosity, +) -> c_int { + let shutdown_token = tokio_util::sync::CancellationToken::new(); + { + if let Ok(mut lock) = TUN_QUIT.lock() { + if lock.is_some() { + return -1; + } + *lock = Some(shutdown_token.clone()); + } else { + return -2; + } + } + + log::set_max_level(verbosity.into()); + log::set_boxed_logger(Box::::default()).unwrap(); + + let proxy_url = std::ffi::CStr::from_ptr(proxy_url).to_str().unwrap(); + let proxy = ArgProxy::from_url(proxy_url).unwrap(); + let tun = std::ffi::CStr::from_ptr(tun).to_str().unwrap().to_string(); + + let mut args = Args::default(); + args.proxy(proxy).tun(tun).dns(dns_strategy).verbosity(verbosity); + + #[cfg(target_os = "linux")] + args.setup(_root_privilege); + + if let Ok(bypass) = std::ffi::CStr::from_ptr(bypass).to_str() { + args.bypass(bypass.parse().unwrap()); + } + + let exit_code = match tokio::runtime::Builder::new_multi_thread().enable_all().build() { + Err(_e) => -3, + Ok(rt) => match rt.block_on(desktop_run_async(args, shutdown_token)) { + Ok(_) => 0, + Err(_e) => -4, + }, + }; + + // release shutdown token before exit. + if let Ok(mut lock) = TUN_QUIT.lock() { + let _ = lock.take(); + } + + exit_code +} + +/// Run the tun2proxy component with some arguments. +pub async fn desktop_run_async(args: Args, shutdown_token: tokio_util::sync::CancellationToken) -> std::io::Result<()> { + let bypass_ips = args.bypass.clone(); + + let mut config = tun2::Configuration::default(); + config.address(TUN_IPV4).netmask(TUN_NETMASK).mtu(MTU).up(); + config.destination(TUN_GATEWAY); + if let Some(tun_fd) = args.tun_fd { + config.raw_fd(tun_fd); + } else { + config.name(&args.tun); + } + + #[cfg(target_os = "linux")] + config.platform_config(|config| { + #[allow(deprecated)] + config.packet_information(true); + config.ensure_root_privileges(args.setup); + }); + + #[cfg(target_os = "windows")] + config.platform_config(|config| { + config.device_guid(Some(12324323423423434234_u128)); + }); + + #[allow(unused_variables)] + let mut tproxy_args = TproxyArgs::new() + .tun_dns(args.dns_addr) + .proxy_addr(args.proxy.addr) + .bypass_ips(&bypass_ips); + #[allow(unused_assignments)] + if args.tun_fd.is_none() { + tproxy_args = tproxy_args.tun_name(&args.tun); + } + + #[allow(unused_mut, unused_assignments, unused_variables)] + let mut setup = true; + + #[cfg(target_os = "linux")] + { + setup = args.setup; + if setup { + tproxy_config::tproxy_setup(&tproxy_args)?; + } + } + + let device = tun2::create_as_async(&config)?; + + #[cfg(any(target_os = "windows", target_os = "macos"))] + if setup { + tproxy_config::tproxy_setup(&tproxy_args)?; + } + + log::info!("Proxy {} server: {}", args.proxy.proxy_type, args.proxy.addr); + + let join_handle = tokio::spawn(crate::run(device, MTU, args, shutdown_token)); + join_handle.await.map_err(std::io::Error::from)??; + + #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] + if setup { + tproxy_config::tproxy_remove(&tproxy_args)?; + } + + Ok::<(), std::io::Error>(()) +} + +/// # Safety +/// +/// Shutdown the tun2proxy component. +#[no_mangle] +pub unsafe extern "C" fn tun2proxy_stop() -> c_int { + if let Ok(lock) = TUN_QUIT.lock() { + if let Some(shutdown_token) = lock.as_ref() { + shutdown_token.cancel(); + return 0; + } + } + -1 +} diff --git a/src/dump_logger.rs b/src/dump_logger.rs index 4eef541..b04ff09 100644 --- a/src/dump_logger.rs +++ b/src/dump_logger.rs @@ -6,18 +6,19 @@ use std::{ pub(crate) static DUMP_CALLBACK: Mutex> = Mutex::new(None); -pub type RawCallback = unsafe extern "C" fn(ArgVerbosity, *const c_char, *mut c_void); - /// # Safety /// /// set dump log info callback. #[no_mangle] -pub unsafe extern "C" fn tun2proxy_set_log_callback(callback: Option, ctx: *mut c_void) { +pub unsafe extern "C" fn tun2proxy_set_log_callback( + callback: Option, + ctx: *mut c_void, +) { *DUMP_CALLBACK.lock().unwrap() = Some(DumpCallback(callback, ctx)); } #[derive(Clone)] -pub struct DumpCallback(Option, *mut c_void); +pub struct DumpCallback(Option, *mut c_void); impl DumpCallback { unsafe fn call(self, dump_level: ArgVerbosity, info: *const c_char) { diff --git a/src/ffi.rs b/src/ffi.rs deleted file mode 100644 index ac96ee2..0000000 --- a/src/ffi.rs +++ /dev/null @@ -1,140 +0,0 @@ -#![cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))] - -use crate::{ - args::{ArgDns, ArgProxy}, - ArgVerbosity, Args, -}; -use std::os::raw::{c_char, c_int}; -use tokio_util::sync::CancellationToken; -use tproxy_config::{TproxyArgs, TUN_GATEWAY, TUN_IPV4, TUN_NETMASK}; -use tun2::DEFAULT_MTU as MTU; - -static TUN_QUIT: std::sync::Mutex> = std::sync::Mutex::new(None); - -/// # Safety -/// -/// Run the tun2proxy component with some arguments. -#[no_mangle] -pub unsafe extern "C" fn tun2proxy_run_with_name( - proxy_url: *const c_char, - tun: *const c_char, - bypass: *const c_char, - dns_strategy: ArgDns, - _root_privilege: bool, - verbosity: ArgVerbosity, -) -> c_int { - let shutdown_token = CancellationToken::new(); - { - if let Ok(mut lock) = TUN_QUIT.lock() { - if lock.is_some() { - log::error!("tun2proxy already started"); - return -1; - } - *lock = Some(shutdown_token.clone()); - } else { - log::error!("tun2proxy unknown error"); - return -1; - } - } - - log::set_max_level(verbosity.into()); - log::set_boxed_logger(Box::::default()).unwrap(); - - let proxy_url = std::ffi::CStr::from_ptr(proxy_url).to_str().unwrap(); - let proxy = ArgProxy::from_url(proxy_url).unwrap(); - let tun = std::ffi::CStr::from_ptr(tun).to_str().unwrap().to_string(); - - let mut args = Args::default(); - args.proxy(proxy).tun(tun).dns(dns_strategy).verbosity(verbosity); - - #[cfg(target_os = "linux")] - args.setup(_root_privilege); - - if let Ok(bypass) = std::ffi::CStr::from_ptr(bypass).to_str() { - args.bypass(bypass.parse().unwrap()); - } - - let block = async move { - let bypass_ips = args.bypass.clone(); - - let mut config = tun2::Configuration::default(); - config.address(TUN_IPV4).netmask(TUN_NETMASK).mtu(MTU).up(); - config.destination(TUN_GATEWAY); - if let Some(tun_fd) = args.tun_fd { - config.raw_fd(tun_fd); - } else { - config.name(&args.tun); - } - - #[cfg(target_os = "linux")] - config.platform_config(|config| { - #[allow(deprecated)] - config.packet_information(true); - config.ensure_root_privileges(args.setup); - }); - - #[cfg(target_os = "windows")] - config.platform_config(|config| { - config.device_guid(Some(12324323423423434234_u128)); - }); - - #[allow(unused_variables)] - let mut tproxy_args = TproxyArgs::new() - .tun_dns(args.dns_addr) - .proxy_addr(args.proxy.addr) - .bypass_ips(&bypass_ips); - #[allow(unused_assignments)] - if args.tun_fd.is_none() { - tproxy_args = tproxy_args.tun_name(&args.tun); - } - - #[cfg(target_os = "linux")] - tproxy_config::tproxy_setup(&tproxy_args)?; - - let device = tun2::create_as_async(&config)?; - - #[cfg(any(target_os = "windows", target_os = "macos"))] - tproxy_config::tproxy_setup(&tproxy_args)?; - - log::info!("Proxy {} server: {}", args.proxy.proxy_type, args.proxy.addr); - - let join_handle = tokio::spawn(crate::run(device, MTU, args, shutdown_token)); - join_handle.await.map_err(std::io::Error::from)??; - - #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] - if _root_privilege { - tproxy_config::tproxy_remove(&tproxy_args)?; - } - - Ok::<(), crate::BoxError>(()) - }; - - let exit_code = match tokio::runtime::Builder::new_multi_thread().enable_all().build() { - Err(_e) => -1, - Ok(rt) => match rt.block_on(block) { - Ok(_) => 0, - Err(_e) => -2, - }, - }; - - // release shutdown token before exit. - if let Ok(mut lock) = TUN_QUIT.lock() { - let _ = lock.take(); - } - - exit_code -} - -/// # Safety -/// -/// Shutdown the tun2proxy component. -#[no_mangle] -pub unsafe extern "C" fn tun2proxy_stop() -> c_int { - if let Ok(lock) = TUN_QUIT.lock() { - if let Some(shutdown_token) = lock.as_ref() { - shutdown_token.cancel(); - return 0; - } - } - -1 -} diff --git a/src/ios.rs b/src/ios.rs index 627c199..12ba6be 100644 --- a/src/ios.rs +++ b/src/ios.rs @@ -26,7 +26,7 @@ pub unsafe extern "C" fn tun2proxy_run_with_fd( let mut args = Args::default(); args.proxy(proxy).tun_fd(Some(tun_fd)).dns(dns_strategy).verbosity(verbosity); - crate::api::tun2proxy_internal_run(args, tun_mtu) + crate::mobile_api::mobile_run(args, tun_mtu) } /// # Safety @@ -34,5 +34,5 @@ pub unsafe extern "C" fn tun2proxy_run_with_fd( /// Shutdown the tun2proxy component. #[no_mangle] pub unsafe extern "C" fn tun2proxy_stop() -> c_int { - crate::api::tun2proxy_internal_stop() + crate::mobile_api::mobile_stop() } diff --git a/src/lib.rs b/src/lib.rs index 5c2a415..c54aa7f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,16 +23,22 @@ pub use { error::{BoxError, Error, Result}, }; +#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))] +pub use desktop_api::desktop_run_async; + +#[cfg(any(target_os = "ios", target_os = "android"))] +pub use mobile_api::{desktop_run_async, mobile_run, mobile_stop}; + mod android; -mod api; mod args; +mod desktop_api; mod directions; mod dns; mod dump_logger; mod error; -mod ffi; mod http; mod ios; +mod mobile_api; mod proxy_handler; mod session_info; mod socks; diff --git a/src/api.rs b/src/mobile_api.rs similarity index 70% rename from src/api.rs rename to src/mobile_api.rs index a416773..dabfa84 100644 --- a/src/api.rs +++ b/src/mobile_api.rs @@ -1,13 +1,18 @@ #![cfg(any(target_os = "ios", target_os = "android"))] use crate::Args; -use std::{os::raw::c_int, sync::Mutex}; -use tokio_util::sync::CancellationToken; +use std::os::raw::c_int; -static TUN_QUIT: Mutex> = Mutex::new(None); +static TUN_QUIT: std::sync::Mutex> = std::sync::Mutex::new(None); -pub(crate) fn tun2proxy_internal_run(args: Args, tun_mtu: u16) -> c_int { - let shutdown_token = CancellationToken::new(); +/// Dummy function to make the build pass. +#[doc(hidden)] +pub async fn desktop_run_async(_: Args, _: tokio_util::sync::CancellationToken) -> std::io::Result<()> { + Ok(()) +} + +pub fn mobile_run(args: Args, tun_mtu: u16) -> c_int { + let shutdown_token = tokio_util::sync::CancellationToken::new(); { let mut lock = TUN_QUIT.lock().unwrap(); if lock.is_some() { @@ -58,14 +63,12 @@ pub(crate) fn tun2proxy_internal_run(args: Args, tun_mtu: u16) -> c_int { exit_code } -pub(crate) fn tun2proxy_internal_stop() -> c_int { - let lock = TUN_QUIT.lock().unwrap(); - - if let Some(shutdown_token) = lock.as_ref() { - shutdown_token.cancel(); - 0 - } else { - log::error!("tun2proxy not started"); - -1 +pub fn mobile_stop() -> c_int { + if let Ok(lock) = TUN_QUIT.lock() { + if let Some(shutdown_token) = lock.as_ref() { + shutdown_token.cancel(); + return 0; + } } + -1 }