This commit is contained in:
ssrlive 2024-02-12 21:36:18 +08:00 committed by GitHub
parent e9c378099e
commit a26621bbcd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 224 additions and 26 deletions

View file

@ -18,8 +18,8 @@
dns_over_tcp:(bool)dns_over_tcp dns_over_tcp:(bool)dns_over_tcp
verbose:(bool)verbose { verbose:(bool)verbose {
ArgDns dns_strategy = dns_over_tcp ? OverTcp : Direct; ArgDns dns_strategy = dns_over_tcp ? OverTcp : Direct;
ArgVerbosity verbosity = verbose ? Trace : Info; ArgVerbosity v = verbose ? Trace : Info;
tun2proxy_run(proxy_url.UTF8String, tun_fd, tun_mtu, dns_strategy, verbosity); tun2proxy_run_with_fd(proxy_url.UTF8String, tun_fd, tun_mtu, dns_strategy, v);
} }
+ (void)shutdown { + (void)shutdown {

View file

@ -1,5 +1,10 @@
[export] [export]
include = ["tun2proxy_run", "tun2proxy_stop", "tun2proxy_set_log_callback"] include = [
"tun2proxy_run_with_fd",
"tun2proxy_run_with_name",
"tun2proxy_stop",
"tun2proxy_set_log_callback",
]
exclude = [ exclude = [
"Java_com_github_shadowsocks_bg_Tun2proxy_run", "Java_com_github_shadowsocks_bg_Tun2proxy_run",
"Java_com_github_shadowsocks_bg_Tun2proxy_stop", "Java_com_github_shadowsocks_bg_Tun2proxy_stop",

View file

@ -37,7 +37,8 @@ pub unsafe extern "C" fn Java_com_github_shadowsocks_bg_Tun2proxy_run(
let proxy_url = get_java_string(&mut env, &proxy_url).unwrap(); let proxy_url = get_java_string(&mut env, &proxy_url).unwrap();
let proxy = ArgProxy::from_url(proxy_url).unwrap(); let proxy = ArgProxy::from_url(proxy_url).unwrap();
let args = Args::new(Some(tun_fd), proxy, dns, verbosity); 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::api::tun2proxy_internal_run(args, tun_mtu)
} }

View file

@ -18,10 +18,18 @@ pub(crate) fn tun2proxy_internal_run(args: Args, tun_mtu: u16) -> c_int {
} }
let block = async move { let block = async move {
log::info!("Proxying {}", args.proxy); log::info!("Proxy {} server: {}", args.proxy.proxy_type, args.proxy.addr);
let mut config = tun2::Configuration::default(); let mut config = tun2::Configuration::default();
config.raw_fd(args.tun_fd.ok_or(crate::Error::from("tun_fd"))?);
#[cfg(unix)]
if let Some(fd) = args.tun_fd {
config.raw_fd(fd);
} else {
config.name(&args.tun);
}
#[cfg(windows)]
config.name(&args.tun);
let device = tun2::create_as_async(&config).map_err(std::io::Error::from)?; let device = tun2::create_as_async(&config).map_err(std::io::Error::from)?;
let join_handle = tokio::spawn(crate::run(device, tun_mtu, args, shutdown_token)); let join_handle = tokio::spawn(crate::run(device, tun_mtu, args, shutdown_token));

View file

@ -70,14 +70,50 @@ impl Args {
Self::parse() Self::parse()
} }
pub fn new(tun_fd: Option<i32>, proxy: ArgProxy, dns: ArgDns, verbosity: ArgVerbosity) -> Self { pub fn proxy(&mut self, proxy: ArgProxy) -> &mut Self {
Args { self.proxy = proxy;
proxy, self
tun_fd, }
dns,
verbosity, pub fn dns(&mut self, dns: ArgDns) -> &mut Self {
..Args::default() self.dns = dns;
} self
}
pub fn tun_fd(&mut self, tun_fd: Option<i32>) -> &mut Self {
self.tun_fd = tun_fd;
self
}
pub fn verbosity(&mut self, verbosity: ArgVerbosity) -> &mut Self {
self.verbosity = verbosity;
self
}
pub fn tun(&mut self, tun: String) -> &mut Self {
self.tun = tun;
self
}
pub fn dns_addr(&mut self, dns_addr: IpAddr) -> &mut Self {
self.dns_addr = dns_addr;
self
}
pub fn bypass(&mut self, bypass: IpAddr) -> &mut Self {
self.bypass.push(bypass);
self
}
pub fn ipv6_enabled(&mut self, ipv6_enabled: bool) -> &mut Self {
self.ipv6_enabled = ipv6_enabled;
self
}
#[cfg(target_os = "linux")]
pub fn setup(&mut self, setup: bool) -> &mut Self {
self.setup = setup;
self
} }
} }

View file

@ -63,11 +63,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
tproxy_config::tproxy_setup(&tproxy_args)?; tproxy_config::tproxy_setup(&tproxy_args)?;
} }
log::info!("Proxying {}", args.proxy); log::info!("Proxy {} server: {}", args.proxy.proxy_type, args.proxy.addr);
let shutdown_token = CancellationToken::new(); let shutdown_token = CancellationToken::new();
let cloned_token = shutdown_token.clone(); let join_handle = tokio::spawn(tun2proxy::run(device, MTU, args, shutdown_token.clone()));
let join_handle = tokio::spawn(tun2proxy::run(device, MTU, args, cloned_token));
ctrlc2::set_async_handler(async move { ctrlc2::set_async_handler(async move {
log::info!("Ctrl-C received, exiting..."); log::info!("Ctrl-C received, exiting...");

View file

@ -6,19 +6,18 @@ use std::{
pub(crate) static DUMP_CALLBACK: Mutex<Option<DumpCallback>> = Mutex::new(None); pub(crate) static DUMP_CALLBACK: Mutex<Option<DumpCallback>> = Mutex::new(None);
pub type RawCallback = unsafe extern "C" fn(ArgVerbosity, *const c_char, *mut c_void);
/// # Safety /// # Safety
/// ///
/// set dump log info callback. /// set dump log info callback.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tun2proxy_set_log_callback( pub unsafe extern "C" fn tun2proxy_set_log_callback(callback: Option<RawCallback>, ctx: *mut c_void) {
callback: Option<unsafe extern "C" fn(ArgVerbosity, *const c_char, *mut c_void)>,
ctx: *mut c_void,
) {
*DUMP_CALLBACK.lock().unwrap() = Some(DumpCallback(callback, ctx)); *DUMP_CALLBACK.lock().unwrap() = Some(DumpCallback(callback, ctx));
} }
#[derive(Clone)] #[derive(Clone)]
pub struct DumpCallback(Option<unsafe extern "C" fn(ArgVerbosity, *const c_char, *mut c_void)>, *mut c_void); pub struct DumpCallback(Option<RawCallback>, *mut c_void);
impl DumpCallback { impl DumpCallback {
unsafe fn call(self, dump_level: ArgVerbosity, info: *const c_char) { unsafe fn call(self, dump_level: ArgVerbosity, info: *const c_char) {

View file

@ -68,4 +68,6 @@ impl From<Error> for std::io::Error {
} }
} }
pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
pub type Result<T, E = Error> = std::result::Result<T, E>; pub type Result<T, E = Error> = std::result::Result<T, E>;

140
src/ffi.rs Normal file
View file

@ -0,0 +1,140 @@
#![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<Option<CancellationToken>> = 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::<crate::dump_logger::DumpLogger>::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
}

View file

@ -10,7 +10,7 @@ use std::os::raw::{c_char, c_int, c_ushort};
/// ///
/// Run the tun2proxy component with some arguments. /// Run the tun2proxy component with some arguments.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn tun2proxy_run( pub unsafe extern "C" fn tun2proxy_run_with_fd(
proxy_url: *const c_char, proxy_url: *const c_char,
tun_fd: c_int, tun_fd: c_int,
tun_mtu: c_ushort, tun_mtu: c_ushort,
@ -23,7 +23,8 @@ pub unsafe extern "C" fn tun2proxy_run(
let proxy_url = std::ffi::CStr::from_ptr(proxy_url).to_str().unwrap(); let proxy_url = std::ffi::CStr::from_ptr(proxy_url).to_str().unwrap();
let proxy = ArgProxy::from_url(proxy_url).unwrap(); let proxy = ArgProxy::from_url(proxy_url).unwrap();
let args = Args::new(Some(tun_fd), proxy, dns_strategy, verbosity); 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::api::tun2proxy_internal_run(args, tun_mtu)
} }

View file

@ -14,13 +14,13 @@ use tokio::{
net::TcpStream, net::TcpStream,
sync::Mutex, sync::Mutex,
}; };
use tokio_util::sync::CancellationToken; pub use tokio_util::sync::CancellationToken;
use tproxy_config::is_private_ip; use tproxy_config::is_private_ip;
use udp_stream::UdpStream; use udp_stream::UdpStream;
pub use { pub use {
args::{ArgDns, ArgProxy, ArgVerbosity, Args, ProxyType}, args::{ArgDns, ArgProxy, ArgVerbosity, Args, ProxyType},
error::{Error, Result}, error::{BoxError, Error, Result},
}; };
mod android; mod android;
@ -30,6 +30,7 @@ mod directions;
mod dns; mod dns;
mod dump_logger; mod dump_logger;
mod error; mod error;
mod ffi;
mod http; mod http;
mod ios; mod ios;
mod proxy_handler; mod proxy_handler;
@ -44,6 +45,12 @@ const MAX_SESSIONS: u64 = 200;
static TASK_COUNT: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0); static TASK_COUNT: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
use std::sync::atomic::Ordering::Relaxed; use std::sync::atomic::Ordering::Relaxed;
/// Run the proxy server
/// # Arguments
/// * `device` - The network device to use
/// * `mtu` - The MTU of the network device
/// * `args` - The arguments to use
/// * `shutdown_token` - The token to exit the server
pub async fn run<D>(device: D, mtu: u16, args: Args, shutdown_token: CancellationToken) -> crate::Result<()> pub async fn run<D>(device: D, mtu: u16, args: Args, shutdown_token: CancellationToken) -> crate::Result<()>
where where
D: AsyncRead + AsyncWrite + Unpin + Send + 'static, D: AsyncRead + AsyncWrite + Unpin + Send + 'static,