From 1008eeec1da9b853d492600a01c5b7d635681694 Mon Sep 17 00:00:00 2001 From: zonyitoo Date: Fri, 19 Dec 2025 03:26:17 +0800 Subject: [PATCH] feat(local-http): support HTTP Basic auth (#1994) --- Cargo.lock | 1 + README.md | 23 ++- bin/ssservice.rs | 15 +- crates/shadowsocks-service/Cargo.toml | 3 +- crates/shadowsocks-service/src/acl/mod.rs | 13 +- crates/shadowsocks-service/src/config.rs | 109 +++++++---- .../src/local/http/config.rs | 172 ++++++++++++++++++ .../src/local/http/http_service.rs | 37 ++++ .../shadowsocks-service/src/local/http/mod.rs | 1 + .../src/local/http/server.rs | 27 ++- .../src/local/loadbalancing/ping_balancer.rs | 7 +- crates/shadowsocks-service/src/local/mod.rs | 3 + .../src/local/net/udp/association.rs | 9 +- .../src/local/online_config/mod.rs | 15 +- .../src/local/redir/tcprelay/mod.rs | 7 +- .../local/redir/udprelay/sys/unix/linux.rs | 17 +- .../local/redir/udprelay/sys/unix/macos.rs | 15 +- .../src/local/socks/server/mod.rs | 14 ++ .../src/local/socks/server/server.rs | 14 +- .../shadowsocks-service/src/local/tun/tcp.rs | 48 +++-- .../shadowsocks-service/src/manager/server.rs | 25 +-- .../src/server/udprelay.rs | 22 ++- crates/shadowsocks/src/config.rs | 14 +- crates/shadowsocks/src/net/sys/mod.rs | 13 +- .../shadowsocks/src/net/sys/unix/bsd/macos.rs | 7 +- .../shadowsocks/src/net/sys/unix/linux/mod.rs | 7 +- crates/shadowsocks/src/net/udp.rs | 60 +++--- .../src/relay/tcprelay/proxy_stream/server.rs | 14 +- src/daemonize/daemonize/mod.rs | 3 +- src/service/local.rs | 14 +- src/service/manager.rs | 14 +- 31 files changed, 540 insertions(+), 203 deletions(-) create mode 100644 crates/shadowsocks-service/src/local/http/config.rs diff --git a/Cargo.lock b/Cargo.lock index f97cf9cb..a88633da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3120,6 +3120,7 @@ name = "shadowsocks-service" version = "1.24.0" dependencies = [ "arc-swap", + "base64", "brotli", "bson", "byte_string", diff --git a/README.md b/README.md index 5ef89230..11cb2f9b 100644 --- a/README.md +++ b/README.md @@ -614,7 +614,10 @@ Example configuration: "local_address": "127.0.0.1", "local_port": 3128, // OPTIONAL. macOS launchd activate socket - "launchd_tcp_socket_name": "TCPListener" + "launchd_tcp_socket_name": "TCPListener", + // OPTIONAL. Authentication configuration file + // Configuration file document could be found in the next section. + "http_auth_config_path": "/path/to/auth.json", }, { // DNS local server (feature = "local-dns") @@ -955,6 +958,24 @@ The configuration file is set by `socks5_auth_config_path` in `locals`. } ``` +### HTTP Authentication Configuration + +The configuration file is set by `http_auth_config_path` in `locals`. + +```jsonc +{ + // Basic Authentication (RFC9110) + "basic": { + "users": [ + { + "user_name": "USERNAME in UTF-8", + "password": "PASSWORD in UTF-8" + } + ] + } +} +``` + ### Environment Variables - `SS_SERVER_PASSWORD`: A default password for servers that created from command line argument (`--server-addr`) diff --git a/bin/ssservice.rs b/bin/ssservice.rs index 7e0b3247..e8e5b172 100644 --- a/bin/ssservice.rs +++ b/bin/ssservice.rs @@ -19,14 +19,15 @@ fn main() -> ExitCode { // Allow running `ssservice` as symlink of `sslocal`, `ssserver` and `ssmanager` if let Some(program_path) = env::args().next() - && let Some(program_name) = Path::new(&program_path).file_name() { - match program_name.to_str() { - Some("sslocal") => return local::main(&local::define_command_line_options(app).get_matches()), - Some("ssserver") => return server::main(&server::define_command_line_options(app).get_matches()), - Some("ssmanager") => return manager::main(&manager::define_command_line_options(app).get_matches()), - _ => {} - } + && let Some(program_name) = Path::new(&program_path).file_name() + { + match program_name.to_str() { + Some("sslocal") => return local::main(&local::define_command_line_options(app).get_matches()), + Some("ssserver") => return server::main(&server::define_command_line_options(app).get_matches()), + Some("ssmanager") => return manager::main(&manager::define_command_line_options(app).get_matches()), + _ => {} } + } let matches = app .subcommand_required(true) diff --git a/crates/shadowsocks-service/Cargo.toml b/crates/shadowsocks-service/Cargo.toml index 2a78e532..bfe5d235 100644 --- a/crates/shadowsocks-service/Cargo.toml +++ b/crates/shadowsocks-service/Cargo.toml @@ -60,7 +60,7 @@ local-dns-relay = ["local-dns"] # Currently is only used in Android local-flow-stat = ["local"] # Enable HTTP protocol for sslocal -local-http = ["local", "hyper", "http", "http-body-util"] +local-http = ["local", "hyper", "http", "http-body-util", "base64"] local-http-native-tls = ["local-http", "tokio-native-tls", "native-tls"] local-http-native-tls-vendored = [ "local-http-native-tls", @@ -179,6 +179,7 @@ mime = { version = "0.3", optional = true } flate2 = { version = "1.0", optional = true } brotli = { version = "8.0", optional = true } zstd = { version = "0.13", optional = true } +base64 = { version = "0.22", optional = true } etherparse = { version = "0.19", optional = true } smoltcp = { version = "0.12", optional = true, default-features = false, features = [ diff --git a/crates/shadowsocks-service/src/acl/mod.rs b/crates/shadowsocks-service/src/acl/mod.rs index c8e1e4ac..494bbbd6 100644 --- a/crates/shadowsocks-service/src/acl/mod.rs +++ b/crates/shadowsocks-service/src/acl/mod.rs @@ -209,13 +209,14 @@ impl ParsingRules { } } } else if let Some(set_rule) = caps.get(2) - && let Ok(set_rule) = str::from_utf8(set_rule.as_bytes()) { - let set_rule = set_rule.replace("\\.", "."); - if self.add_set_rule_inner(&set_rule).is_ok() { - trace!("REGEX-RULE {} => SET-RULE {}", rule, set_rule); - return; - } + && let Ok(set_rule) = str::from_utf8(set_rule.as_bytes()) + { + let set_rule = set_rule.replace("\\.", "."); + if self.add_set_rule_inner(&set_rule).is_ok() { + trace!("REGEX-RULE {} => SET-RULE {}", rule, set_rule); + return; } + } } trace!("REGEX-RULE {}", rule); diff --git a/crates/shadowsocks-service/src/config.rs b/crates/shadowsocks-service/src/config.rs index a449e942..09288f26 100644 --- a/crates/shadowsocks-service/src/config.rs +++ b/crates/shadowsocks-service/src/config.rs @@ -80,6 +80,8 @@ use shadowsocks::{ use crate::acl::AccessControl; #[cfg(feature = "local-dns")] use crate::local::dns::NameServerAddr; +#[cfg(feature = "local-http")] +use crate::local::http::config::HttpAuthConfig; #[cfg(feature = "local")] use crate::local::socks::config::Socks5AuthConfig; @@ -323,6 +325,11 @@ struct SSLocalExtConfig { #[serde(skip_serializing_if = "Option::is_none")] socks5_auth_config_path: Option, + /// HTTP + #[cfg(feature = "local")] + #[serde(skip_serializing_if = "Option::is_none")] + http_auth_config_path: Option, + /// Fake DNS #[cfg(feature = "local-fake-dns")] #[serde(skip_serializing_if = "Option::is_none")] @@ -1038,6 +1045,10 @@ pub struct LocalConfig { #[cfg(feature = "local")] pub socks5_auth: Socks5AuthConfig, + /// HTTP Authentication configuration + #[cfg(feature = "local")] + pub http_auth: HttpAuthConfig, + /// Fake DNS record expire seconds #[cfg(feature = "local-fake-dns")] pub fake_dns_record_expire_duration: Option, @@ -1108,6 +1119,9 @@ impl LocalConfig { #[cfg(feature = "local")] socks5_auth: Socks5AuthConfig::default(), + #[cfg(feature = "local-http")] + http_auth: HttpAuthConfig::default(), + #[cfg(feature = "local-fake-dns")] fake_dns_record_expire_duration: None, #[cfg(feature = "local-fake-dns")] @@ -1832,6 +1846,11 @@ impl Config { local_config.socks5_auth = Socks5AuthConfig::load_from_file(&socks5_auth_config_path)?; } + #[cfg(feature = "local-http")] + if let Some(http_auth_config_path) = local.http_auth_config_path { + local_config.http_auth = HttpAuthConfig::load_from_file(&http_auth_config_path)?; + } + #[cfg(feature = "local-fake-dns")] { if let Some(d) = local.fake_dns_record_expire_duration { @@ -2390,15 +2409,16 @@ impl Config { // Security if let Some(sec) = config.security && let Some(replay_attack) = sec.replay_attack - && let Some(policy) = replay_attack.policy { - match policy.parse::() { - Ok(p) => nconfig.security.replay_attack.policy = p, - Err(..) => { - let err = Error::new(ErrorKind::Invalid, "invalid replay attack policy", None); - return Err(err); - } - } + && let Some(policy) = replay_attack.policy + { + match policy.parse::() { + Ok(p) => nconfig.security.replay_attack.policy = p, + Err(..) => { + let err = Error::new(ErrorKind::Invalid, "invalid replay attack policy", None); + return Err(err); } + } + } if let Some(balancer) = config.balancer { nconfig.balancer = BalancerConfig { @@ -2619,16 +2639,18 @@ impl Config { // Balancer related checks if let Some(rtt) = self.balancer.max_server_rtt - && rtt.as_secs() == 0 { - let err = Error::new(ErrorKind::Invalid, "balancer.max_server_rtt must be > 0", None); - return Err(err); - } + && rtt.as_secs() == 0 + { + let err = Error::new(ErrorKind::Invalid, "balancer.max_server_rtt must be > 0", None); + return Err(err); + } if let Some(intv) = self.balancer.check_interval - && intv.as_secs() == 0 { - let err = Error::new(ErrorKind::Invalid, "balancer.check_interval must be > 0", None); - return Err(err); - } + && intv.as_secs() == 0 + { + let err = Error::new(ErrorKind::Invalid, "balancer.check_interval must be > 0", None); + return Err(err); + } } if self.config_type.is_server() && self.server.is_empty() { @@ -2664,10 +2686,11 @@ impl Config { // Plugin shouldn't be an empty string if let Some(plugin) = server.plugin() - && plugin.plugin.trim().is_empty() { - let err = Error::new(ErrorKind::Malformed, "`plugin` shouldn't be an empty string", None); - return Err(err); - } + && plugin.plugin.trim().is_empty() + { + let err = Error::new(ErrorKind::Malformed, "`plugin` shouldn't be an empty string", None); + return Err(err); + } // Server's domain name shouldn't be an empty string match server.addr() { @@ -2899,6 +2922,9 @@ impl fmt::Display for Config { #[cfg(feature = "local")] socks5_auth_config_path: None, + #[cfg(feature = "local-http")] + http_auth_config_path: None, + #[cfg(feature = "local-fake-dns")] fake_dns_record_expire_duration: local.fake_dns_record_expire_duration.map(|d| d.as_secs()), #[cfg(feature = "local-fake-dns")] @@ -3068,20 +3094,22 @@ impl fmt::Display for Config { } if jconf.method.is_none() - && let Some(ref m) = m.method { - jconf.method = Some(m.to_string()); - } + && let Some(ref m) = m.method + { + jconf.method = Some(m.to_string()); + } if jconf.plugin.is_none() - && let Some(ref p) = m.plugin { - jconf.plugin = Some(p.plugin.clone()); - if let Some(ref o) = p.plugin_opts { - jconf.plugin_opts = Some(o.clone()); - } - if !p.plugin_args.is_empty() { - jconf.plugin_args = Some(p.plugin_args.clone()); - } + && let Some(ref p) = m.plugin + { + jconf.plugin = Some(p.plugin.clone()); + if let Some(ref o) = p.plugin_opts { + jconf.plugin_opts = Some(o.clone()); } + if !p.plugin_args.is_empty() { + jconf.plugin_args = Some(p.plugin_args.clone()); + } + } } if self.no_delay { @@ -3188,17 +3216,18 @@ impl fmt::Display for Config { /// It will return the original value if fails to read `${VAR_NAME}`. pub fn read_variable_field_value(value: &str) -> Cow<'_, str> { if let Some(left_over) = value.strip_prefix("${") - && let Some(var_name) = left_over.strip_suffix('}') { - match env::var(var_name) { - Ok(value) => return value.into(), - Err(err) => { - warn!( - "couldn't read password from environment variable {}, error: {}", - var_name, err - ); - } + && let Some(var_name) = left_over.strip_suffix('}') + { + match env::var(var_name) { + Ok(value) => return value.into(), + Err(err) => { + warn!( + "couldn't read password from environment variable {}, error: {}", + var_name, err + ); } } + } value.into() } diff --git a/crates/shadowsocks-service/src/local/http/config.rs b/crates/shadowsocks-service/src/local/http/config.rs new file mode 100644 index 00000000..8ff1e5c0 --- /dev/null +++ b/crates/shadowsocks-service/src/local/http/config.rs @@ -0,0 +1,172 @@ +//! HTTP protocol configuration + +use std::{ + collections::HashMap, + fs::OpenOptions, + io::{self, Read}, + path::Path, +}; + +use base64::Engine as _; +use log::trace; +use serde::Deserialize; + +const BASIC_AUTH_BASE64_ENGINE: base64::engine::GeneralPurpose = base64::engine::GeneralPurpose::new( + &base64::alphabet::STANDARD, + base64::engine::GeneralPurposeConfig::new() + .with_encode_padding(true) + .with_decode_padding_mode(base64::engine::DecodePaddingMode::Indifferent), +); + +#[derive(Deserialize, Debug)] +struct SSHttpAuthBasicUserConfig { + user_name: String, + password: String, +} + +#[derive(Deserialize, Debug)] +struct SSHttpAuthBasicConfig { + users: Vec, +} + +#[derive(Deserialize, Debug)] +struct SSHttpAuthConfig { + #[serde(skip_serializing_if = "Option::is_none")] + basic: Option, +} + +/// HTTP Authentication method +#[derive(Debug, Clone)] +pub struct HttpAuthConfig { + pub basic: HttpAuthBasicConfig, +} + +impl HttpAuthConfig { + /// Create a new HTTP Authentication configuration + pub fn new() -> Self { + Self { + basic: HttpAuthBasicConfig::new(), + } + } + + /// Load from configuration file + /// + /// ```json + /// { + /// "basic": { + /// "users": [ + /// { + /// "user_name": "USER_NAME", + /// "password": "PASSWORD" + /// } + /// ] + /// } + /// } + pub fn load_from_file + ?Sized>(filename: &P) -> io::Result { + let filename = filename.as_ref(); + + trace!( + "loading socks5 authentication configuration from {}", + filename.display() + ); + + let mut reader = OpenOptions::new().read(true).open(filename)?; + let mut content = String::new(); + reader.read_to_string(&mut content)?; + + let jconf: SSHttpAuthConfig = match json5::from_str(&content) { + Ok(c) => c, + Err(err) => return Err(io::Error::other(err)), + }; + + let mut basic = HttpAuthBasicConfig::new(); + if let Some(p) = jconf.basic { + for user in p.users { + basic.add_user(user.user_name, user.password); + } + } + + Ok(Self { basic }) + } + + /// Check if authentication is required + pub fn auth_required(&self) -> bool { + self.basic.total_users() > 0 + } + + /// Check Basic Authentication + pub fn verify_basic_auth(&self, header_value: &str) -> bool { + const BASIC_PREFIX: &str = "Basic "; + + if !header_value.starts_with(BASIC_PREFIX) { + return false; + } + let b64_encoded = &header_value[BASIC_PREFIX.len()..]; + let decoded_bytes = match BASIC_AUTH_BASE64_ENGINE.decode(b64_encoded) { + Ok(b) => b, + Err(_) => return false, + }; + let decoded_str = match String::from_utf8(decoded_bytes) { + Ok(s) => s, + Err(_) => return false, + }; + let (user_name, password) = match decoded_str.split_once(':') { + Some((u, p)) => (u, p), + None => return false, + }; + self.basic.check_user(user_name, password) + } +} + +impl Default for HttpAuthConfig { + fn default() -> Self { + Self::new() + } +} + +/// HTTP server User/Password Authentication configuration +/// +/// RFC9110 https://httpwg.org/specs/rfc9110.html#auth.client.proxy +#[derive(Debug, Clone)] +pub struct HttpAuthBasicConfig { + users: HashMap, +} + +impl HttpAuthBasicConfig { + /// Create an empty `Passwd` configuration + pub fn new() -> Self { + Self { users: HashMap::new() } + } + + /// Add a user with password + pub fn add_user(&mut self, user_name: U, password: P) + where + U: Into, + P: Into, + { + self.users.insert(user_name.into(), password.into()); + } + + /// Check if `user_name` exists and validate `password` + pub fn check_user(&self, user_name: U, password: P) -> bool + where + U: AsRef, + P: AsRef, + { + match self.users.get(user_name.as_ref()) { + Some(pwd) => pwd == password.as_ref(), + None => false, + } + } + + /// Total users + pub fn total_users(&self) -> usize { + self.users.len() + } +} + +impl Default for HttpAuthBasicConfig { + fn default() -> Self { + Self::new() + } +} diff --git a/crates/shadowsocks-service/src/local/http/http_service.rs b/crates/shadowsocks-service/src/local/http/http_service.rs index 441f2fe9..8c3dfe56 100644 --- a/crates/shadowsocks-service/src/local/http/http_service.rs +++ b/crates/shadowsocks-service/src/local/http/http_service.rs @@ -21,6 +21,7 @@ use crate::local::{ }; use super::{ + config::HttpAuthConfig, http_client::HttpClient, utils::{authority_addr, check_keep_alive, connect_host, host_addr}, }; @@ -30,6 +31,7 @@ pub struct HttpService { peer_addr: SocketAddr, http_client: HttpClient, balancer: PingBalancer, + http_auth: Arc, } impl HttpService { @@ -38,12 +40,14 @@ impl HttpService { peer_addr: SocketAddr, http_client: HttpClient, balancer: PingBalancer, + http_auth: Arc, ) -> Self { Self { context, peer_addr, http_client, balancer, + http_auth, } } @@ -53,6 +57,31 @@ impl HttpService { ) -> hyper::Result>> { trace!("request {} {:?}", self.peer_addr, req); + if self.http_auth.auth_required() { + // Only support Basic authentication for now + match req.headers().get(header::PROXY_AUTHORIZATION) { + Some(header_value) => { + if let Ok(header_str) = header_value.to_str() { + if !self.http_auth.verify_basic_auth(header_str) { + debug!("HTTP {} invalid proxy authorization: {}", req.method(), header_str); + return make_proxy_authentication_required(); + } + } else { + debug!( + "HTTP {} invalid proxy authorization header encoding: {:?}", + req.method(), + header_value + ); + return make_proxy_authentication_required(); + } + } + None => { + debug!("HTTP {} missing proxy authorization", req.method()); + return make_proxy_authentication_required(); + } + } + } + // Parse URI // // Proxy request URI must contains a host @@ -204,6 +233,14 @@ fn make_bad_request() -> Result>, hyper::E .unwrap()) } +fn make_proxy_authentication_required() -> Result>, hyper::Error> { + Ok(Response::builder() + .status(StatusCode::PROXY_AUTHENTICATION_REQUIRED) + .header(header::PROXY_AUTHENTICATE, "Basic charset=\"UTF-8\"") + .body(empty_body()) + .unwrap()) +} + fn make_internal_server_error() -> Result>, hyper::Error> { Ok(Response::builder() .status(StatusCode::INTERNAL_SERVER_ERROR) diff --git a/crates/shadowsocks-service/src/local/http/mod.rs b/crates/shadowsocks-service/src/local/http/mod.rs index 6228e0e0..ee95faa3 100644 --- a/crates/shadowsocks-service/src/local/http/mod.rs +++ b/crates/shadowsocks-service/src/local/http/mod.rs @@ -7,6 +7,7 @@ pub use self::{ server::{Http, HttpBuilder, HttpConnectionHandler}, }; +pub mod config; mod http_client; mod http_service; mod http_stream; diff --git a/crates/shadowsocks-service/src/local/http/server.rs b/crates/shadowsocks-service/src/local/http/server.rs index 913e3b30..a392246b 100644 --- a/crates/shadowsocks-service/src/local/http/server.rs +++ b/crates/shadowsocks-service/src/local/http/server.rs @@ -16,7 +16,7 @@ use crate::local::{ context::ServiceContext, loadbalancing::PingBalancer, net::tcp::listener::create_standard_tcp_listener, }; -use super::{http_client::HttpClient, http_service::HttpService, tokio_rt::TokioIo}; +use super::{config::HttpAuthConfig, http_client::HttpClient, http_service::HttpService, tokio_rt::TokioIo}; /// HTTP Local server builder pub struct HttpBuilder { @@ -25,6 +25,7 @@ pub struct HttpBuilder { balancer: PingBalancer, #[cfg(target_os = "macos")] launchd_tcp_socket_name: Option, + http_auth: HttpAuthConfig, } impl HttpBuilder { @@ -42,6 +43,7 @@ impl HttpBuilder { balancer, #[cfg(target_os = "macos")] launchd_tcp_socket_name: None, + http_auth: HttpAuthConfig::default(), } } @@ -50,6 +52,10 @@ impl HttpBuilder { self.launchd_tcp_socket_name = Some(n); } + pub fn set_http_auth(&mut self, auth: HttpAuthConfig) { + self.http_auth = auth; + } + /// Build HTTP server instance pub async fn build(self) -> io::Result { cfg_if::cfg_if! { @@ -77,6 +83,7 @@ impl HttpBuilder { context: self.context, listener, balancer: self.balancer, + http_auth: Arc::new(self.http_auth), }) } } @@ -86,6 +93,7 @@ pub struct Http { context: Arc, listener: TcpListener, balancer: PingBalancer, + http_auth: Arc, } impl Http { @@ -104,7 +112,7 @@ impl Http { self.listener.local_addr().expect("http local_addr") ); - let handler = HttpConnectionHandler::new(self.context, self.balancer); + let handler = HttpConnectionHandler::new(self.context, self.balancer, self.http_auth); loop { let (stream, peer_addr) = match self.listener.accept().await { @@ -135,15 +143,17 @@ pub struct HttpConnectionHandler { context: Arc, balancer: PingBalancer, http_client: HttpClient, + http_auth: Arc, } impl HttpConnectionHandler { /// Create a new Handler - pub fn new(context: Arc, balancer: PingBalancer) -> Self { + pub fn new(context: Arc, balancer: PingBalancer, http_auth: Arc) -> Self { Self { context, balancer, http_client: HttpClient::new(), + http_auth, } } @@ -156,6 +166,7 @@ impl HttpConnectionHandler { context, balancer, http_client, + http_auth, } = self; let io = TokioIo::new(stream); @@ -169,8 +180,14 @@ impl HttpConnectionHandler { .serve_connection( io, service::service_fn(move |req| { - HttpService::new(context.clone(), peer_addr, http_client.clone(), balancer.clone()) - .serve_connection(req) + HttpService::new( + context.clone(), + peer_addr, + http_client.clone(), + balancer.clone(), + http_auth.clone(), + ) + .serve_connection(req) }), ) .with_upgrades() diff --git a/crates/shadowsocks-service/src/local/loadbalancing/ping_balancer.rs b/crates/shadowsocks-service/src/local/loadbalancing/ping_balancer.rs index 7a538f9a..e5fa4b24 100644 --- a/crates/shadowsocks-service/src/local/loadbalancing/ping_balancer.rs +++ b/crates/shadowsocks-service/src/local/loadbalancing/ping_balancer.rs @@ -164,9 +164,10 @@ impl PingBalancerBuilder { pub async fn build(self) -> io::Result { if let Some(intv) = self.check_best_interval - && intv > self.check_interval { - return Err(io::Error::other("check_interval must be >= check_best_interval")); - } + && intv > self.check_interval + { + return Err(io::Error::other("check_interval must be >= check_best_interval")); + } let (shared_context, task_abortable) = PingBalancerContext::new( self.servers, diff --git a/crates/shadowsocks-service/src/local/mod.rs b/crates/shadowsocks-service/src/local/mod.rs index 753f72dd..c234d065 100644 --- a/crates/shadowsocks-service/src/local/mod.rs +++ b/crates/shadowsocks-service/src/local/mod.rs @@ -279,6 +279,8 @@ impl Server { let mut server_builder = SocksBuilder::with_context(context.clone(), client_addr, balancer); server_builder.set_mode(local_config.mode); server_builder.set_socks5_auth(local_config.socks5_auth); + #[cfg(feature = "local-http")] + server_builder.set_http_auth(local_config.http_auth); if let Some(c) = config.udp_max_associations { server_builder.set_udp_capacity(c); @@ -349,6 +351,7 @@ impl Server { #[allow(unused_mut)] let mut builder = HttpBuilder::with_context(context.clone(), client_addr, balancer); + builder.set_http_auth(local_config.http_auth); #[cfg(target_os = "macos")] if let Some(n) = local_config.launchd_tcp_socket_name { diff --git a/crates/shadowsocks-service/src/local/net/udp/association.rs b/crates/shadowsocks-service/src/local/net/udp/association.rs index 066b0fb0..f3c75332 100644 --- a/crates/shadowsocks-service/src/local/net/udp/association.rs +++ b/crates/shadowsocks-service/src/local/net/udp/association.rs @@ -515,11 +515,10 @@ where } }; - if UDP_SOCKET_SUPPORT_DUAL_STACK - && let SocketAddr::V4(saddr) = target_addr { - let mapped_ip = saddr.ip().to_ipv6_mapped(); - target_addr = SocketAddr::V6(SocketAddrV6::new(mapped_ip, saddr.port(), 0, 0)); - } + if UDP_SOCKET_SUPPORT_DUAL_STACK && let SocketAddr::V4(saddr) = target_addr { + let mapped_ip = saddr.ip().to_ipv6_mapped(); + target_addr = SocketAddr::V6(SocketAddrV6::new(mapped_ip, saddr.port(), 0, 0)); + } let n = socket.send_to(data, target_addr).await?; if n != data.len() { diff --git a/crates/shadowsocks-service/src/local/online_config/mod.rs b/crates/shadowsocks-service/src/local/online_config/mod.rs index 277493f9..9a4dd01f 100644 --- a/crates/shadowsocks-service/src/local/online_config/mod.rs +++ b/crates/shadowsocks-service/src/local/online_config/mod.rs @@ -215,13 +215,14 @@ impl OnlineConfigService { if let Some(ref allowed_plugins) = self.allowed_plugins { for server in &online_config.server { if let Some(plugin) = server.config.plugin() - && !allowed_plugins.contains(&plugin.plugin) { - error!( - "server-loader task found not allowed plugin: {}, url: {}", - plugin.plugin, self.config_url - ); - return Err(io::Error::other(format!("not allowed plugin: {}", plugin.plugin))); - } + && !allowed_plugins.contains(&plugin.plugin) + { + error!( + "server-loader task found not allowed plugin: {}, url: {}", + plugin.plugin, self.config_url + ); + return Err(io::Error::other(format!("not allowed plugin: {}", plugin.plugin))); + } } } diff --git a/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs b/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs index 75a95b8a..bc8c1966 100644 --- a/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs +++ b/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs @@ -64,9 +64,10 @@ async fn handle_redir_client( // // Try to convert IPv4 mapped IPv6 address for dual-stack mode. if let SocketAddr::V6(ref a) = daddr - && let Some(v4) = to_ipv4_mapped(a.ip()) { - daddr = SocketAddr::new(IpAddr::from(v4), a.port()); - } + && let Some(v4) = to_ipv4_mapped(a.ip()) + { + daddr = SocketAddr::new(IpAddr::from(v4), a.port()); + } let target_addr = Address::from(daddr); establish_client_tcp_redir(context, balancer, s, peer_addr, &target_addr).await } diff --git a/crates/shadowsocks-service/src/local/redir/udprelay/sys/unix/linux.rs b/crates/shadowsocks-service/src/local/redir/udprelay/sys/unix/linux.rs index 84579d0d..c9e821aa 100644 --- a/crates/shadowsocks-service/src/local/redir/udprelay/sys/unix/linux.rs +++ b/crates/shadowsocks-service/src/local/redir/udprelay/sys/unix/linux.rs @@ -71,16 +71,15 @@ impl UdpRedirSocket { socket.set_nonblocking(true)?; socket.set_reuse_address(true)?; - if reuse_port - && let Err(err) = socket.set_reuse_port(true) { - if let Some(libc::ENOPROTOOPT) = err.raw_os_error() { - // SO_REUSEPORT is supported after 3.9 - trace!("failed to set SO_REUSEPORT, error: {}", err); - } else { - error!("failed to set SO_REUSEPORT, error: {}", err); - return Err(err); - } + if reuse_port && let Err(err) = socket.set_reuse_port(true) { + if let Some(libc::ENOPROTOOPT) = err.raw_os_error() { + // SO_REUSEPORT is supported after 3.9 + trace!("failed to set SO_REUSEPORT, error: {}", err); + } else { + error!("failed to set SO_REUSEPORT, error: {}", err); + return Err(err); } + } let sock_addr = SockAddr::from(addr); diff --git a/crates/shadowsocks-service/src/local/redir/udprelay/sys/unix/macos.rs b/crates/shadowsocks-service/src/local/redir/udprelay/sys/unix/macos.rs index cc6bc656..b5235624 100644 --- a/crates/shadowsocks-service/src/local/redir/udprelay/sys/unix/macos.rs +++ b/crates/shadowsocks-service/src/local/redir/udprelay/sys/unix/macos.rs @@ -52,15 +52,14 @@ impl UdpRedirSocket { socket.set_nonblocking(true)?; socket.set_reuse_address(true)?; - if reuse_port - && let Err(err) = socket.set_reuse_port(true) { - if let Some(libc::ENOPROTOOPT) = err.raw_os_error() { - trace!("failed to set SO_REUSEPORT, error: {}", err); - } else { - error!("failed to set SO_REUSEPORT, error: {}", err); - return Err(err); - } + if reuse_port && let Err(err) = socket.set_reuse_port(true) { + if let Some(libc::ENOPROTOOPT) = err.raw_os_error() { + trace!("failed to set SO_REUSEPORT, error: {}", err); + } else { + error!("failed to set SO_REUSEPORT, error: {}", err); + return Err(err); } + } let sock_addr = SockAddr::from(addr); diff --git a/crates/shadowsocks-service/src/local/socks/server/mod.rs b/crates/shadowsocks-service/src/local/socks/server/mod.rs index deecf514..843ae0fc 100644 --- a/crates/shadowsocks-service/src/local/socks/server/mod.rs +++ b/crates/shadowsocks-service/src/local/socks/server/mod.rs @@ -11,6 +11,8 @@ pub use self::server::{SocksTcpServer, SocksTcpServerBuilder, SocksUdpServer}; use self::socks5::Socks5UdpServerBuilder; use super::config::Socks5AuthConfig; +#[cfg(feature = "local-http")] +use crate::local::http::config::HttpAuthConfig; #[allow(clippy::module_inception)] mod server; @@ -33,6 +35,8 @@ pub struct SocksBuilder { launchd_tcp_socket_name: Option, #[cfg(target_os = "macos")] launchd_udp_socket_name: Option, + #[cfg(feature = "local-http")] + http_auth: HttpAuthConfig, } impl SocksBuilder { @@ -58,6 +62,8 @@ impl SocksBuilder { launchd_tcp_socket_name: None, #[cfg(target_os = "macos")] launchd_udp_socket_name: None, + #[cfg(feature = "local-http")] + http_auth: HttpAuthConfig::default(), } } @@ -93,6 +99,12 @@ impl SocksBuilder { self.socks5_auth = p; } + /// Set HTTP Authentication configuration + #[cfg(feature = "local-http")] + pub fn set_http_auth(&mut self, p: HttpAuthConfig) { + self.http_auth = p; + } + /// macOS launchd activate socket #[cfg(target_os = "macos")] pub fn set_launchd_tcp_socket_name(&mut self, n: String) { @@ -144,6 +156,8 @@ impl SocksBuilder { self.balancer.clone(), self.mode, self.socks5_auth, + #[cfg(feature = "local-http")] + self.http_auth, ); #[cfg(target_os = "macos")] diff --git a/crates/shadowsocks-service/src/local/socks/server/server.rs b/crates/shadowsocks-service/src/local/socks/server/server.rs index 31f26409..f6851dca 100644 --- a/crates/shadowsocks-service/src/local/socks/server/server.rs +++ b/crates/shadowsocks-service/src/local/socks/server/server.rs @@ -5,7 +5,7 @@ use shadowsocks::{ServerAddr, config::Mode, net::TcpListener as ShadowTcpListene use tokio::{net::TcpStream, time}; #[cfg(feature = "local-http")] -use crate::local::http::HttpConnectionHandler; +use crate::local::http::{HttpConnectionHandler, config::HttpAuthConfig}; use crate::local::{ context::ServiceContext, loadbalancing::PingBalancer, net::tcp::listener::create_standard_tcp_listener, socks::config::Socks5AuthConfig, @@ -24,6 +24,8 @@ pub struct SocksTcpServerBuilder { socks5_auth: Arc, #[cfg(target_os = "macos")] launchd_socket_name: Option, + #[cfg(feature = "local-http")] + http_auth: Arc, } impl SocksTcpServerBuilder { @@ -34,6 +36,7 @@ impl SocksTcpServerBuilder { balancer: PingBalancer, mode: Mode, socks5_auth: Socks5AuthConfig, + #[cfg(feature = "local-http")] http_auth: HttpAuthConfig, ) -> Self { Self { context, @@ -44,6 +47,8 @@ impl SocksTcpServerBuilder { socks5_auth: Arc::new(socks5_auth), #[cfg(target_os = "macos")] launchd_socket_name: None, + #[cfg(feature = "local-http")] + http_auth: Arc::new(http_auth), } } @@ -80,6 +85,8 @@ impl SocksTcpServerBuilder { balancer: self.balancer, mode: self.mode, socks5_auth: self.socks5_auth, + #[cfg(feature = "local-http")] + http_auth: self.http_auth, }) } } @@ -92,6 +99,8 @@ pub struct SocksTcpServer { balancer: PingBalancer, mode: Mode, socks5_auth: Arc, + #[cfg(feature = "local-http")] + http_auth: Arc, } impl SocksTcpServer { @@ -107,7 +116,8 @@ impl SocksTcpServer { // If UDP is enabled, SOCK5 UDP_ASSOCIATE command will let client to send requests to this address let udp_associate_addr = Arc::new(self.udp_associate_addr); #[cfg(feature = "local-http")] - let http_handler = HttpConnectionHandler::new(self.context.clone(), self.balancer.clone()); + let http_handler = + HttpConnectionHandler::new(self.context.clone(), self.balancer.clone(), self.http_auth.clone()); loop { let (stream, peer_addr) = match self.listener.accept().await { diff --git a/crates/shadowsocks-service/src/local/tun/tcp.rs b/crates/shadowsocks-service/src/local/tun/tcp.rs index dcbcc49e..df3f24a9 100644 --- a/crates/shadowsocks-service/src/local/tun/tcp.rs +++ b/crates/shadowsocks-service/src/local/tun/tcp.rs @@ -160,9 +160,10 @@ impl AsyncRead for TcpConnection { // Nothing could be read. Wait for notify. if let Some(old_waker) = control.recv_waker.replace(cx.waker().clone()) - && !old_waker.will_wake(cx.waker()) { - old_waker.wake(); - } + && !old_waker.will_wake(cx.waker()) + { + old_waker.wake(); + } return Poll::Pending; } @@ -191,9 +192,10 @@ impl AsyncWrite for TcpConnection { if control.send_buffer.is_full() { if let Some(old_waker) = control.send_waker.replace(cx.waker().clone()) - && !old_waker.will_wake(cx.waker()) { - old_waker.wake(); - } + && !old_waker.will_wake(cx.waker()) + { + old_waker.wake(); + } return Poll::Pending; } @@ -223,9 +225,10 @@ impl AsyncWrite for TcpConnection { } if let Some(old_waker) = control.send_waker.replace(cx.waker().clone()) - && !old_waker.will_wake(cx.waker()) { - old_waker.wake(); - } + && !old_waker.will_wake(cx.waker()) + { + old_waker.wake(); + } self.manager_notify.notify(); Poll::Pending @@ -418,10 +421,12 @@ impl TcpTun { wake_receiver = true; } - if wake_receiver && control.recv_waker.is_some() - && let Some(waker) = control.recv_waker.take() { - waker.wake(); - } + if wake_receiver + && control.recv_waker.is_some() + && let Some(waker) = control.recv_waker.take() + { + waker.wake(); + } // Check if writable let mut wake_sender = false; @@ -452,10 +457,12 @@ impl TcpTun { } } - if wake_sender && control.send_waker.is_some() - && let Some(waker) = control.send_waker.take() { - waker.wake(); - } + if wake_sender + && control.send_waker.is_some() + && let Some(waker) = control.send_waker.take() + { + waker.wake(); + } } for socket_handle in sockets_to_remove { @@ -597,9 +604,10 @@ async fn handle_redir_client( // // Try to convert IPv4 mapped IPv6 address for dual-stack mode. if let SocketAddr::V6(ref a) = daddr - && let Some(v4) = to_ipv4_mapped(a.ip()) { - daddr = SocketAddr::new(IpAddr::from(v4), a.port()); - } + && let Some(v4) = to_ipv4_mapped(a.ip()) + { + daddr = SocketAddr::new(IpAddr::from(v4), a.port()); + } let target_addr = Address::from(daddr); establish_client_tcp_redir(context, balancer, s, peer_addr, &target_addr).await } diff --git a/crates/shadowsocks-service/src/manager/server.rs b/crates/shadowsocks-service/src/manager/server.rs index c8d93922..2b5e3305 100644 --- a/crates/shadowsocks-service/src/manager/server.rs +++ b/crates/shadowsocks-service/src/manager/server.rs @@ -330,22 +330,23 @@ impl Manager { let pid_path = self.server_pid_path(port); if pid_path.exists() - && let Ok(mut pid_file) = File::open(&pid_path) { - let mut pid_content = String::new(); - if pid_file.read_to_string(&mut pid_content).is_ok() { - let pid_content = pid_content.trim(); + && let Ok(mut pid_file) = File::open(&pid_path) + { + let mut pid_content = String::new(); + if pid_file.read_to_string(&mut pid_content).is_ok() { + let pid_content = pid_content.trim(); - match pid_content.parse::() { - Ok(pid) => { - let _ = unsafe { libc::kill(pid, libc::SIGTERM) }; - debug!("killed standalone server port {}, pid: {}", port, pid); - } - Err(..) => { - warn!("failed to read pid from {}", pid_path.display()); - } + match pid_content.parse::() { + Ok(pid) => { + let _ = unsafe { libc::kill(pid, libc::SIGTERM) }; + debug!("killed standalone server port {}, pid: {}", port, pid); + } + Err(..) => { + warn!("failed to read pid from {}", pid_path.display()); } } } + } let server_config_path = self.server_config_path(port); diff --git a/crates/shadowsocks-service/src/server/udprelay.rs b/crates/shadowsocks-service/src/server/udprelay.rs index 4ee0bee9..0816388d 100644 --- a/crates/shadowsocks-service/src/server/udprelay.rs +++ b/crates/shadowsocks-service/src/server/udprelay.rs @@ -594,13 +594,14 @@ impl UdpAssociationContext { control: &Option, ) { if let Some(ref mut session) = self.client_session - && peer_addr != self.peer_addr { - debug!( - "udp relay for {} changed to {}, session: {:?}", - self.peer_addr, peer_addr, session.client_session_id - ); - self.peer_addr = peer_addr; - } + && peer_addr != self.peer_addr + { + debug!( + "udp relay for {} changed to {}, session: {:?}", + self.peer_addr, peer_addr, session.client_session_id + ); + self.peer_addr = peer_addr; + } trace!( "udp relay {} -> {} with {} bytes, control: {:?}", @@ -737,9 +738,10 @@ impl UdpAssociationContext { // But for some implementations, they may expect the target address to be IPv4, because // the peer address is IPv4 when calling `sendto`. if let Address::SocketAddress(SocketAddr::V6(ref v6)) = addr - && let Some(v4) = to_ipv4_mapped(v6.ip()) { - addr = Address::SocketAddress(SocketAddr::new(v4.into(), v6.port())); - } + && let Some(v4) = to_ipv4_mapped(v6.ip()) + { + addr = Address::SocketAddress(SocketAddr::new(v4.into(), v6.port())); + } match self.client_session { None => { diff --git a/crates/shadowsocks/src/config.rs b/crates/shadowsocks/src/config.rs index 4491c755..bedda615 100644 --- a/crates/shadowsocks/src/config.rs +++ b/crates/shadowsocks/src/config.rs @@ -696,18 +696,20 @@ impl ServerConfig { /// Get server's TCP external address pub fn tcp_external_addr(&self) -> &ServerAddr { if let Some(plugin) = self.plugin() - && plugin.plugin_mode.enable_tcp() { - return self.plugin_addr.as_ref().unwrap_or(&self.addr); - } + && plugin.plugin_mode.enable_tcp() + { + return self.plugin_addr.as_ref().unwrap_or(&self.addr); + } &self.addr } /// Get server's UDP external address pub fn udp_external_addr(&self) -> &ServerAddr { if let Some(plugin) = self.plugin() - && plugin.plugin_mode.enable_udp() { - return self.plugin_addr.as_ref().unwrap_or(&self.addr); - } + && plugin.plugin_mode.enable_udp() + { + return self.plugin_addr.as_ref().unwrap_or(&self.addr); + } &self.addr } diff --git a/crates/shadowsocks/src/net/sys/mod.rs b/crates/shadowsocks/src/net/sys/mod.rs index 7e2b1ade..ff62d3da 100644 --- a/crates/shadowsocks/src/net/sys/mod.rs +++ b/crates/shadowsocks/src/net/sys/mod.rs @@ -168,13 +168,14 @@ static IP_STACK_CAPABILITIES: LazyLock = LazyLock::new(|| { // Check IPv6 (::1) if let Ok(ipv6_socket) = Socket::new(Domain::IPV6, Type::STREAM, Some(Protocol::TCP)) - && ipv6_socket.set_only_v6(true).is_ok() { - let local_host = SockAddr::from(SocketAddr::new(Ipv6Addr::LOCALHOST.into(), 0)); - if ipv6_socket.bind(&local_host).is_ok() { - caps.support_ipv6 = true; - debug!("IpStackCapability support_ipv6=true"); - } + && ipv6_socket.set_only_v6(true).is_ok() + { + let local_host = SockAddr::from(SocketAddr::new(Ipv6Addr::LOCALHOST.into(), 0)); + if ipv6_socket.bind(&local_host).is_ok() { + caps.support_ipv6 = true; + debug!("IpStackCapability support_ipv6=true"); } + } // Check IPv4-mapped-IPv6 (127.0.0.1) if check_ipv4_mapped_ipv6_capability().is_ok() { diff --git a/crates/shadowsocks/src/net/sys/unix/bsd/macos.rs b/crates/shadowsocks/src/net/sys/unix/bsd/macos.rs index b760f9a7..fc97a298 100644 --- a/crates/shadowsocks/src/net/sys/unix/bsd/macos.rs +++ b/crates/shadowsocks/src/net/sys/unix/bsd/macos.rs @@ -391,9 +391,10 @@ pub async fn bind_outbound_udp_socket(bind_addr: &SocketAddr, config: &ConnectOp }; if !config.udp.allow_fragmentation - && let Err(err) = set_disable_ip_fragmentation(af, &socket) { - warn!("failed to disable IP fragmentation, error: {}", err); - } + && let Err(err) = set_disable_ip_fragmentation(af, &socket) + { + warn!("failed to disable IP fragmentation, error: {}", err); + } // Set IP_BOUND_IF for BSD-like if let Some(ref iface) = config.bind_interface { diff --git a/crates/shadowsocks/src/net/sys/unix/linux/mod.rs b/crates/shadowsocks/src/net/sys/unix/linux/mod.rs index b77c7bf5..4f13a584 100644 --- a/crates/shadowsocks/src/net/sys/unix/linux/mod.rs +++ b/crates/shadowsocks/src/net/sys/unix/linux/mod.rs @@ -311,9 +311,10 @@ pub async fn bind_outbound_udp_socket(bind_addr: &SocketAddr, config: &ConnectOp }; if !config.udp.allow_fragmentation - && let Err(err) = set_disable_ip_fragmentation(af, &socket) { - warn!("failed to disable IP fragmentation, error: {}", err); - } + && let Err(err) = set_disable_ip_fragmentation(af, &socket) + { + warn!("failed to disable IP fragmentation, error: {}", err); + } // Any traffic except localhost should be protected // This is a workaround for VPNService diff --git a/crates/shadowsocks/src/net/udp.rs b/crates/shadowsocks/src/net/udp.rs index 00ac8c1d..2bd6509e 100644 --- a/crates/shadowsocks/src/net/udp.rs +++ b/crates/shadowsocks/src/net/udp.rs @@ -35,11 +35,11 @@ use futures::ready; use tokio::io::Interest; use tokio::{io::ReadBuf, net::ToSocketAddrs}; -use crate::{context::Context, relay::socks5::Address, ServerAddr}; +use crate::{ServerAddr, context::Context, relay::socks5::Address}; use super::{ - sys::{bind_outbound_udp_socket, create_inbound_udp_socket, create_outbound_udp_socket}, AcceptOpts, AddrFamily, ConnectOpts, + sys::{bind_outbound_udp_socket, create_inbound_udp_socket, create_outbound_udp_socket}, }; /// Message struct for `batch_send` @@ -190,9 +190,10 @@ impl UdpSocket { pub fn poll_send(&self, cx: &mut TaskContext<'_>, buf: &[u8]) -> Poll> { // Check MTU if let Some(mtu) = self.mtu - && buf.len() > mtu { - return Err(make_mtu_error(buf.len(), mtu)).into(); - } + && buf.len() > mtu + { + return Err(make_mtu_error(buf.len(), mtu)).into(); + } self.socket.poll_send(cx, buf) } @@ -202,9 +203,10 @@ impl UdpSocket { pub async fn send(&self, buf: &[u8]) -> io::Result { // Check MTU if let Some(mtu) = self.mtu - && buf.len() > mtu { - return Err(make_mtu_error(buf.len(), mtu)); - } + && buf.len() > mtu + { + return Err(make_mtu_error(buf.len(), mtu)); + } self.socket.send(buf).await } @@ -213,9 +215,10 @@ impl UdpSocket { pub fn poll_send_to(&self, cx: &mut TaskContext<'_>, buf: &[u8], target: SocketAddr) -> Poll> { // Check MTU if let Some(mtu) = self.mtu - && buf.len() > mtu { - return Err(make_mtu_error(buf.len(), mtu)).into(); - } + && buf.len() > mtu + { + return Err(make_mtu_error(buf.len(), mtu)).into(); + } self.socket.poll_send_to(cx, buf, target) } @@ -225,9 +228,10 @@ impl UdpSocket { pub async fn send_to(&self, buf: &[u8], target: A) -> io::Result { // Check MTU if let Some(mtu) = self.mtu - && buf.len() > mtu { - return Err(make_mtu_error(buf.len(), mtu)); - } + && buf.len() > mtu + { + return Err(make_mtu_error(buf.len(), mtu)); + } self.socket.send_to(buf, target).await } @@ -238,9 +242,10 @@ impl UdpSocket { ready!(self.socket.poll_recv(cx, buf))?; if let Some(mtu) = self.mtu - && buf.filled().len() > mtu { - return Err(make_mtu_error(buf.filled().len(), mtu)).into(); - } + && buf.filled().len() > mtu + { + return Err(make_mtu_error(buf.filled().len(), mtu)).into(); + } Ok(()).into() } @@ -251,9 +256,10 @@ impl UdpSocket { let n = self.socket.recv(buf).await?; if let Some(mtu) = self.mtu - && n > mtu { - return Err(make_mtu_error(n, mtu)); - } + && n > mtu + { + return Err(make_mtu_error(n, mtu)); + } Ok(n) } @@ -264,9 +270,10 @@ impl UdpSocket { let addr = ready!(self.socket.poll_recv_from(cx, buf))?; if let Some(mtu) = self.mtu - && buf.filled().len() > mtu { - return Err(make_mtu_error(buf.filled().len(), mtu)).into(); - } + && buf.filled().len() > mtu + { + return Err(make_mtu_error(buf.filled().len(), mtu)).into(); + } Ok(addr).into() } @@ -277,9 +284,10 @@ impl UdpSocket { let (n, addr) = self.socket.recv_from(buf).await?; if let Some(mtu) = self.mtu - && n > mtu { - return Err(make_mtu_error(n, mtu)); - } + && n > mtu + { + return Err(make_mtu_error(n, mtu)); + } Ok((n, addr)) } diff --git a/crates/shadowsocks/src/relay/tcprelay/proxy_stream/server.rs b/crates/shadowsocks/src/relay/tcprelay/proxy_stream/server.rs index b8d41ee3..96b2aca3 100644 --- a/crates/shadowsocks/src/relay/tcprelay/proxy_stream/server.rs +++ b/crates/shadowsocks/src/relay/tcprelay/proxy_stream/server.rs @@ -160,9 +160,10 @@ where // Wakeup writer task because we have already received the salt #[cfg(feature = "aead-cipher-2022")] if let ProxyServerStreamWriteState::PrepareHeader(waker) = this.writer_state - && let Some(waker) = waker.take() { - waker.wake(); - } + && let Some(waker) = waker.take() + { + waker.wake(); + } Ok(()).into() } @@ -190,9 +191,10 @@ where } else { // Reader didn't receive the salt from client yet. if let Some(waker) = waker.take() - && !waker.will_wake(cx.waker()) { - waker.wake(); - } + && !waker.will_wake(cx.waker()) + { + waker.wake(); + } *waker = Some(cx.waker().clone()); return Poll::Pending; } diff --git a/src/daemonize/daemonize/mod.rs b/src/daemonize/daemonize/mod.rs index 7fa937c1..98b60993 100644 --- a/src/daemonize/daemonize/mod.rs +++ b/src/daemonize/daemonize/mod.rs @@ -6,7 +6,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! //! daemonize is a library for writing system daemons. Inspired by the Python library [thesharp/daemonize](https://github.com/thesharp/daemonize). //! //! The respository is located at . @@ -211,7 +210,6 @@ impl Outcome { /// * change root directory; /// * change the pid-file ownership to provided user (and/or) group; /// * execute any provided action just before dropping privileges. -/// pub struct Daemonize { directory: PathBuf, pid_file: Option, @@ -339,6 +337,7 @@ impl Daemonize { self.stderr = stdio.into(); self } + /// Start daemonization process, terminate parent after first fork, returns privileged action /// result to the child. pub fn start(self) -> Result { diff --git a/src/service/local.rs b/src/service/local.rs index 786f2ce5..507d9e91 100644 --- a/src/service/local.rs +++ b/src/service/local.rs @@ -756,14 +756,16 @@ pub fn create(matches: &ArgMatches) -> ShadowsocksResult<(Runtime, impl Future("TCP_REDIR") { - local_config.tcp_redir = tcp_redir.parse::().expect("tcp-redir"); - } + && let Some(tcp_redir) = matches.get_one::("TCP_REDIR") + { + local_config.tcp_redir = tcp_redir.parse::().expect("tcp-redir"); + } if RedirType::udp_default() != RedirType::NotSupported - && let Some(udp_redir) = matches.get_one::("UDP_REDIR") { - local_config.udp_redir = udp_redir.parse::().expect("udp-redir"); - } + && let Some(udp_redir) = matches.get_one::("UDP_REDIR") + { + local_config.udp_redir = udp_redir.parse::().expect("udp-redir"); + } } #[cfg(feature = "local-dns")] diff --git a/src/service/manager.rs b/src/service/manager.rs index c0e2d769..29b19216 100644 --- a/src/service/manager.rs +++ b/src/service/manager.rs @@ -405,14 +405,16 @@ pub fn create(matches: &ArgMatches) -> ShadowsocksResult<(Runtime, impl Future("ACL") { let acl = AccessControl::load_from_file(acl_file)