From e740636b38b7e6463ecf93259ad7bb6a1c38baf3 Mon Sep 17 00:00:00 2001 From: Ali Askari Date: Wed, 19 Feb 2025 16:39:29 +0330 Subject: [PATCH] fix: ss password decoding and enhance Shadowsocks parsing for encoding base64 method and password --- Cargo.lock | 1 + singbox/Cargo.toml | 1 + singbox/src/error/mod.rs | 2 ++ singbox/src/protocol/mod.rs | 32 +++++++++++++++++++++++++++----- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6e229ac..942307c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1999,6 +1999,7 @@ dependencies = [ "serde_json", "tokio", "url", + "urlencoding", ] [[package]] diff --git a/singbox/Cargo.toml b/singbox/Cargo.toml index 9784038..bf0301d 100644 --- a/singbox/Cargo.toml +++ b/singbox/Cargo.toml @@ -14,3 +14,4 @@ log = "0.4" pretty_env_logger = "0.5" chrono = "0.4" semver = "1.0" +urlencoding = "2.1.3" diff --git a/singbox/src/error/mod.rs b/singbox/src/error/mod.rs index 2e2b0d1..3104288 100644 --- a/singbox/src/error/mod.rs +++ b/singbox/src/error/mod.rs @@ -24,6 +24,7 @@ pub enum ConversionError { InvalidDnsObject, MissingServersArray, MissingTypeField, + UnsupportedShadowsocks, Other(String), } @@ -54,6 +55,7 @@ impl fmt::Display for ConversionError { write!(f, "Missing or invalid 'servers' array in DNS configuration") } Self::MissingTypeField => write!(f, "Missing type field"), + Self::UnsupportedShadowsocks => write!(f, "Unsupported Shadowsocks"), Self::Other(e) => write!(f, "{}", e), } } diff --git a/singbox/src/protocol/mod.rs b/singbox/src/protocol/mod.rs index a501ea7..cf228d5 100644 --- a/singbox/src/protocol/mod.rs +++ b/singbox/src/protocol/mod.rs @@ -86,12 +86,34 @@ impl Protocol { fn parse_shadowsocks(data: &str) -> Result { let url = Url::parse(&format!("ss://{}", data)).map_err(|_| ConversionError::InvalidUri)?; + + let mut password = url.password().map(|p| p.to_string()); + let mut method = url.username().to_string(); + + // If no password was provided, try to decode the method. + if password.is_none() { + let decrypted = match general_purpose::STANDARD.decode(&method) { + Ok(decoded_bytes) => match String::from_utf8(decoded_bytes) { + Ok(decoded_str) => decoded_str, + Err(_) => method.to_string(), + }, + Err(_) => method.to_string(), + }; + let decrypted_arr: Vec<&str> = decrypted.split(':').collect(); + if decrypted_arr.len() > 1 { + method = decrypted_arr[0].to_string(); + password = Some(decrypted_arr[1..].join(":")); + } else { + return Err(ConversionError::UnsupportedShadowsocks); + } + } + let password = password.unwrap(); + Ok(Self::Shadowsocks { - method: url.username().to_string(), - password: url - .password() - .ok_or(ConversionError::MissingPassword)? - .to_string(), + method, + password: urlencoding::decode(&password) + .map_err(|_| ConversionError::FailedDecode)? + .into_owned(), host: url .host_str() .ok_or(ConversionError::MissingHost)?