From bc8ff4cf38d3544849f974b0a9cc1659085a6620 Mon Sep 17 00:00:00 2001 From: Alexander Olkhovoy Date: Tue, 7 Apr 2026 13:40:53 +0300 Subject: [PATCH] Refactor: enable session info via username marker (+info) instead of separate flag - Remove embed_session_info field from Args (fixes semver breaking change) - Remove runWithSessionInfo JNI function - Detect +info suffix in username to enable session info embedding - Format: username+info becomes username|protocol|src_ip|src_port --- src/android.rs | 52 -------------------------------------------------- src/args.rs | 13 ------------- src/lib.rs | 5 ++--- src/socks.rs | 16 +++++++--------- 4 files changed, 9 insertions(+), 77 deletions(-) diff --git a/src/android.rs b/src/android.rs index 8b6ce7e..837485c 100644 --- a/src/android.rs +++ b/src/android.rs @@ -58,58 +58,6 @@ pub unsafe extern "C" fn Java_com_github_shadowsocks_bg_Tun2proxy_run( .resolve::() } -/// # Safety -/// -/// Running tun2proxy with some arguments including embed_session_info option -/// Parameters: -/// - proxy_url: the proxy url, e.g. "socks5://user:pass@127.0.0.1:1080" -/// - tun_fd: the tun file descriptor, it will be owned by tun2proxy -/// - close_fd_on_drop: whether close the tun_fd on drop -/// - tun_mtu: the tun mtu -/// - dns_strategy: the dns strategy, see ArgDns enum -/// - verbosity: the verbosity level, see ArgVerbosity enum -/// - embed_session_info: if true, embeds session info in SOCKS5 username field -/// Format: "original_username|protocol|src_ip|src_port" -/// Use this with getConnectionOwnerUid() for per-app routing on Android 10+ -#[unsafe(no_mangle)] -pub unsafe extern "C" fn Java_com_github_shadowsocks_bg_Tun2proxy_runWithSessionInfo( - mut env: EnvUnowned<'_>, - _clazz: JClass<'_>, - proxy_url: JString<'_>, - tun_fd: jint, - close_fd_on_drop: jboolean, - tun_mtu: jchar, - verbosity: jint, - dns_strategy: jint, - embed_session_info: jboolean, -) -> jint { - let dns = dns_strategy.try_into().unwrap(); - let verbosity = verbosity.try_into().unwrap(); - let filter_str = &format!("off,tun2proxy={verbosity}"); - let filter = android_logger::FilterBuilder::new().parse(filter_str).build(); - android_logger::init_once( - android_logger::Config::default() - .with_tag("tun2proxy") - .with_max_level(log::LevelFilter::Trace) - .with_filter(filter), - ); - env.with_env(|env: &mut Env| -> Result { - let proxy_url = get_java_string(env, &proxy_url).unwrap(); - let proxy = ArgProxy::try_from(proxy_url.as_str()).unwrap(); - - let mut args = Args::default(); - args.proxy(proxy) - .tun_fd(Some(tun_fd)) - .close_fd_on_drop(close_fd_on_drop) - .dns(dns) - .verbosity(verbosity) - .embed_session_info(embed_session_info != jboolean::from(false)); - let v = crate::general_api::general_run_for_api(args, tun_mtu, false); - Ok::(v) - }) - .resolve::() -} - /// # Safety /// /// Shutdown tun2proxy diff --git a/src/args.rs b/src/args.rs index 405fca1..fe87fac 100644 --- a/src/args.rs +++ b/src/args.rs @@ -20,7 +20,6 @@ fn about_info() -> &'static str { } #[derive(Debug, Clone, clap::Parser, serde::Serialize, serde::Deserialize)] -#[non_exhaustive] #[command(author, version = version_info!(), about = about_info(), long_about = None)] pub struct Args { /// Proxy URL in the form proto://[username[:password]@]host:port, @@ -129,12 +128,6 @@ pub struct Args { #[arg(long, value_name = "number", default_value = "200")] pub max_sessions: usize, - /// Embed session info (protocol, src_ip, src_port) in SOCKS5 username field. - /// Format: "original_username|protocol|src_ip|src_port" - /// Useful for per-app routing on Android via getConnectionOwnerUid(). - #[arg(long)] - pub embed_session_info: bool, - /// UDP gateway server address, forwards UDP packets via specified TCP server #[cfg(feature = "udpgw")] #[arg(long, value_name = "IP:PORT")] @@ -195,7 +188,6 @@ impl Default for Args { daemonize: false, exit_on_fatal_error: false, max_sessions: 200, - embed_session_info: false, #[cfg(feature = "udpgw")] udpgw_server: None, #[cfg(feature = "udpgw")] @@ -281,11 +273,6 @@ impl Args { self.setup = setup; self } - - pub fn embed_session_info(&mut self, embed_session_info: bool) -> &mut Self { - self.embed_session_info = embed_session_info; - self - } } #[repr(C)] diff --git a/src/lib.rs b/src/lib.rs index 6f4d68d..90f5de5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -225,10 +225,9 @@ where let socket_queue = None; use socks5_impl::protocol::Version::{V4, V5}; - let embed_session_info = args.embed_session_info; let mgr: Arc = match args.proxy.proxy_type { - ProxyType::Socks5 => Arc::new(SocksProxyManager::new(server_addr, V5, key, embed_session_info)), - ProxyType::Socks4 => Arc::new(SocksProxyManager::new(server_addr, V4, key, embed_session_info)), + ProxyType::Socks5 => Arc::new(SocksProxyManager::new(server_addr, V5, key)), + ProxyType::Socks4 => Arc::new(SocksProxyManager::new(server_addr, V4, key)), ProxyType::Http => Arc::new(HttpManager::new(server_addr, key)), ProxyType::None => Arc::new(NoProxyManager::new()), }; diff --git a/src/socks.rs b/src/socks.rs index 224b48a..6177a20 100644 --- a/src/socks.rs +++ b/src/socks.rs @@ -32,7 +32,6 @@ struct SocksProxyImpl { credentials: Option, command: protocol::Command, udp_associate: Option, - embed_session_info: bool, } impl SocksProxyImpl { @@ -43,7 +42,6 @@ impl SocksProxyImpl { credentials: Option, version: Version, command: protocol::Command, - embed_session_info: bool, ) -> Result { let mut result = Self { server_addr, @@ -58,7 +56,6 @@ impl SocksProxyImpl { credentials, command, udp_associate: None, - embed_session_info, }; result.send_client_hello()?; Ok(result) @@ -173,16 +170,20 @@ impl SocksProxyImpl { fn send_auth_data(&mut self) -> std::io::Result<()> { let tmp = UserKey::default(); let credentials = self.credentials.as_ref().unwrap_or(&tmp); - let username = if self.embed_session_info { + + const SESSION_INFO_MARKER: &str = "+info"; + let username = if credentials.username.ends_with(SESSION_INFO_MARKER) { + let base_username = &credentials.username[..credentials.username.len() - SESSION_INFO_MARKER.len()]; let proto = match self.command { protocol::Command::Connect => "tcp", protocol::Command::UdpAssociate => "udp", _ => "unknown", }; - format!("{}|{}|{}|{}", credentials.username, proto, self.info.src.ip(), self.info.src.port()) + format!("{}|{}|{}|{}", base_username, proto, self.info.src.ip(), self.info.src.port()) } else { credentials.username.clone() }; + let request = password_method::Request::new(&username, &credentials.password); request.write_to_stream(&mut self.server_outbuf)?; self.state = SocksState::ReceiveAuthResponse; @@ -345,7 +346,6 @@ pub(crate) struct SocksProxyManager { server: SocketAddr, credentials: Option, version: Version, - embed_session_info: bool, } #[async_trait::async_trait] @@ -366,18 +366,16 @@ impl ProxyHandlerManager for SocksProxyManager { credentials, self.version, command, - self.embed_session_info, )?))) } } impl SocksProxyManager { - pub(crate) fn new(server: SocketAddr, version: Version, credentials: Option, embed_session_info: bool) -> Self { + pub(crate) fn new(server: SocketAddr, version: Version, credentials: Option) -> Self { Self { server, credentials, version, - embed_session_info, } } }