diff --git a/bin/sslocal.rs b/bin/sslocal.rs index 2ce91959..368f35b6 100644 --- a/bin/sslocal.rs +++ b/bin/sslocal.rs @@ -17,10 +17,10 @@ use shadowsocks_service::config::RedirType; use shadowsocks_service::shadowsocks::relay::socks5::Address; use shadowsocks_service::{ acl::AccessControl, - config::{Config, ConfigType, LocalConfig, Mode, ProtocolType}, + config::{Config, ConfigType, LocalConfig, ProtocolType}, run_local, shadowsocks::{ - config::{ServerAddr, ServerConfig}, + config::{Mode, ServerAddr, ServerConfig}, crypto::v1::{available_ciphers, CipherKind}, plugin::PluginConfig, }, @@ -40,12 +40,11 @@ fn main() { (version: VERSION) (about: "A fast tunnel proxy that helps you bypass firewalls.") - (@arg UDP_ONLY: -u conflicts_with[TCP_AND_UDP] "Server mode UDP_ONLY") - (@arg TCP_AND_UDP: -U "Server mode TCP_AND_UDP") - (@arg CONFIG: -c --config +takes_value required_unless_all(&["LOCAL_ADDR", "SERVER_CONFIG"]) "Shadowsocks configuration file (https://shadowsocks.org/en/config/quick-guide.html)") (@arg LOCAL_ADDR: -b --("local-addr") +takes_value {validator::validate_server_addr} "Local address, listen only to this address if specified") + (@arg UDP_ONLY: -u conflicts_with[TCP_AND_UDP] requires[LOCAL_ADDR] "Server mode UDP_ONLY") + (@arg TCP_AND_UDP: -U requires[LOCAL_ADDR] "Server mode TCP_AND_UDP") (@arg SERVER_ADDR: -s --("server-addr") +takes_value {validator::validate_server_addr} requires[PASSWORD ENCRYPT_METHOD] "Server address") (@arg PASSWORD: -k --password +takes_value requires[SERVER_ADDR] "Server's password") @@ -324,18 +323,17 @@ fn main() { } } + if matches.is_present("UDP_ONLY") { + local_config.mode = Mode::UdpOnly; + } + + if matches.is_present("TCP_AND_UDP") { + local_config.mode = Mode::TcpAndUdp; + } + config.local.push(local_config); } - // override the config's mode if UDP_ONLY is set - if matches.is_present("UDP_ONLY") { - config.mode = Mode::UdpOnly; - } - - if matches.is_present("TCP_AND_UDP") { - config.mode = Mode::TcpAndUdp; - } - if matches.is_present("NO_DELAY") { config.no_delay = true; } diff --git a/bin/ssmanager.rs b/bin/ssmanager.rs index baad5c43..cb148f00 100644 --- a/bin/ssmanager.rs +++ b/bin/ssmanager.rs @@ -16,10 +16,10 @@ use tokio::{self, runtime::Builder}; use shadowsocks_service::{ acl::AccessControl, - config::{Config, ConfigType, ManagerConfig, ManagerServerHost, Mode}, + config::{Config, ConfigType, ManagerConfig, ManagerServerHost}, run_manager, shadowsocks::{ - config::ManagerAddr, + config::{ManagerAddr, Mode}, crypto::v1::{available_ciphers, CipherKind}, }, }; @@ -138,16 +138,17 @@ fn main() { config.local_addr = Some(bind_addr); } + // Overrides if matches.is_present("UDP_ONLY") { - if config.mode.enable_tcp() { - config.mode = Mode::TcpAndUdp; - } else { - config.mode = Mode::UdpOnly; + if let Some(ref mut m) = config.manager { + m.mode = m.mode.merge(Mode::UdpOnly); } } if matches.is_present("TCP_AND_UDP") { - config.mode = Mode::TcpAndUdp; + if let Some(ref mut m) = config.manager { + m.mode = Mode::TcpAndUdp; + } } if matches.is_present("NO_DELAY") { diff --git a/bin/ssserver.rs b/bin/ssserver.rs index b126f0ae..f207b506 100644 --- a/bin/ssserver.rs +++ b/bin/ssserver.rs @@ -16,10 +16,10 @@ use tokio::{self, runtime::Builder}; use shadowsocks_service::{ acl::AccessControl, - config::{Config, ConfigType, ManagerConfig, Mode}, + config::{Config, ConfigType, ManagerConfig}, run_server, shadowsocks::{ - config::{ManagerAddr, ServerAddr, ServerConfig}, + config::{ManagerAddr, Mode, ServerAddr, ServerConfig}, crypto::v1::{available_ciphers, CipherKind}, plugin::PluginConfig, }, @@ -40,9 +40,6 @@ fn main() { (version: VERSION) (about: "A fast tunnel proxy that helps you bypass firewalls.") - (@arg UDP_ONLY: -u conflicts_with[TCP_AND_UDP] "Server mode UDP_ONLY") - (@arg TCP_AND_UDP: -U "Server mode TCP_AND_UDP") - (@arg CONFIG: -c --config +takes_value required_unless("SERVER_ADDR") "Shadowsocks configuration file (https://shadowsocks.org/en/config/quick-guide.html)") (@arg BIND_ADDR: -b --("bind-addr") +takes_value {validator::validate_ip_addr} "Bind address, outbound socket will bind this address") @@ -51,6 +48,8 @@ fn main() { (@arg PASSWORD: -k --password +takes_value requires[SERVER_ADDR] "Server's password") (@arg ENCRYPT_METHOD: -m --("encrypt-method") +takes_value requires[SERVER_ADDR] possible_values(available_ciphers()) +next_line_help "Server's encryption method") (@arg TIMEOUT: --timeout +takes_value {validator::validate_u64} requires[SERVER_ADDR] "Server's timeout seconds for TCP relay") + (@arg UDP_ONLY: -u conflicts_with[TCP_AND_UDP] requires[SERVER_ADDR] "Server mode UDP_ONLY") + (@arg TCP_AND_UDP: -U requires[SERVER_ADDR] "Server mode TCP_AND_UDP") (@arg PLUGIN: --plugin +takes_value requires[SERVER_ADDR] "SIP003 (https://shadowsocks.org/en/spec/Plugin.html) plugin") (@arg PLUGIN_OPT: --("plugin-opts") +takes_value requires[PLUGIN] "Set SIP003 plugin options") @@ -168,6 +167,14 @@ fn main() { sc.set_plugin(plugin); } + if matches.is_present("UDP_ONLY") { + sc.set_mode(sc.mode().merge(Mode::UdpOnly)); + } + + if matches.is_present("TCP_AND_UDP") { + sc.set_mode(Mode::TcpAndUdp); + } + config.server.push(sc); } @@ -176,18 +183,6 @@ fn main() { config.local_addr = Some(bind_addr); } - if matches.is_present("UDP_ONLY") { - if config.mode.enable_tcp() { - config.mode = Mode::TcpAndUdp; - } else { - config.mode = Mode::UdpOnly; - } - } - - if matches.is_present("TCP_AND_UDP") { - config.mode = Mode::TcpAndUdp; - } - if matches.is_present("NO_DELAY") { config.no_delay = true; } diff --git a/crates/shadowsocks-service/src/config.rs b/crates/shadowsocks-service/src/config.rs index 147ff52d..ce9c9522 100644 --- a/crates/shadowsocks-service/src/config.rs +++ b/crates/shadowsocks-service/src/config.rs @@ -62,7 +62,7 @@ use serde::{Deserialize, Serialize}; #[cfg(any(feature = "local-tunnel", feature = "local-dns"))] use shadowsocks::relay::socks5::Address; use shadowsocks::{ - config::{ManagerAddr, ServerAddr, ServerConfig}, + config::{ManagerAddr, Mode, ServerAddr, ServerConfig}, crypto::v1::CipherKind, plugin::PluginConfig, }; @@ -132,6 +132,9 @@ struct SSLocalExtConfig { local_address: Option, local_port: u16, + #[serde(skip_serializing_if = "Option::is_none")] + mode: Option, + #[serde(skip_serializing_if = "Option::is_none")] local_udp_address: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -202,6 +205,8 @@ struct SSServerExtConfig { remarks: Option, #[serde(skip_serializing_if = "Option::is_none")] id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + mode: Option, } /// Server config type @@ -234,47 +239,6 @@ impl ConfigType { } } -/// Server mode -#[derive(Clone, Copy, Debug)] -pub enum Mode { - TcpOnly, - TcpAndUdp, - UdpOnly, -} - -impl Mode { - pub fn enable_udp(self) -> bool { - matches!(self, Mode::UdpOnly | Mode::TcpAndUdp) - } - - pub fn enable_tcp(self) -> bool { - matches!(self, Mode::TcpOnly | Mode::TcpAndUdp) - } -} - -impl fmt::Display for Mode { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Mode::TcpOnly => f.write_str("tcp_only"), - Mode::TcpAndUdp => f.write_str("tcp_and_udp"), - Mode::UdpOnly => f.write_str("udp_only"), - } - } -} - -impl FromStr for Mode { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "tcp_only" => Ok(Mode::TcpOnly), - "tcp_and_udp" => Ok(Mode::TcpAndUdp), - "udp_only" => Ok(Mode::UdpOnly), - _ => Err(()), - } - } -} - cfg_if! { if #[cfg(feature = "local-redir")] { use strum::IntoEnumIterator; @@ -502,6 +466,8 @@ pub struct ManagerConfig { /// /// Note: Outbound address is defined in Config.local_addr pub server_host: ManagerServerHost, + /// Server's mode + pub mode: Mode, } impl ManagerConfig { @@ -512,6 +478,7 @@ impl ManagerConfig { method: None, timeout: None, server_host: ManagerServerHost::default(), + mode: Mode::TcpOnly, } } } @@ -597,6 +564,10 @@ pub struct LocalConfig { pub addr: ServerAddr, pub protocol: ProtocolType, + /// Mode + /// Uses global `mode` if not specified + pub mode: Mode, + /// UDP server bind address. Uses `addr` if not specified /// /// Resolving Android's issue: [shadowsocks/shadowsocks-android#2571](https://github.com/shadowsocks/shadowsocks-android/issues/2571) @@ -631,6 +602,8 @@ impl LocalConfig { LocalConfig { addr, protocol, + + mode: Mode::TcpOnly, udp_addr: None, #[cfg(feature = "local-tunnel")] @@ -767,8 +740,6 @@ pub struct Config { /// Manager's configuration pub manager: Option, - /// Server mode, `tcp_only`, `tcp_and_udp`, and `udp_only` - pub mode: Mode, /// Config is for Client or Server pub config_type: ConfigType, @@ -872,7 +843,6 @@ impl Config { manager: None, - mode: Mode::TcpOnly, config_type, udp_timeout: None, @@ -919,6 +889,22 @@ impl Config { } } + // Mode + let mut global_mode = Mode::TcpOnly; + if let Some(m) = config.mode { + match m.parse::() { + Ok(xm) => global_mode = xm, + Err(..) => { + let e = Error::new( + ErrorKind::Malformed, + "malformed `mode`, must be one of `tcp_only`, `udp_only` and `tcp_and_udp`", + None, + ); + return Err(e); + } + } + } + match config_type { ConfigType::Local => { // Standard config @@ -985,6 +971,19 @@ impl Config { local_config.udp_addr = Some(local_udp_addr); } + match local.mode { + Some(mode) => match mode.parse::() { + Ok(mode) => local_config.mode = mode, + Err(..) => { + let err = Error::new(ErrorKind::Malformed, "invalid `mode`", None); + return Err(err); + } + }, + None => { + local_config.mode = global_mode; + } + } + #[cfg(feature = "local-tunnel")] if let Some(forward_address) = local.forward_address { let forward_port = match local.forward_port { @@ -1076,53 +1075,6 @@ impl Config { } } - // match config.local_address { - // Some(la) => { - // let local_port = if config_type.is_local() { - // let local_port = config.local_port.unwrap_or(0); - // if local_port == 0 { - // let err = Error::new(ErrorKind::MissingField, "missing `local_port`", None); - // return Err(err); - // } - // local_port - // } else if config_type.is_server() || config_type.is_manager() { - // // server's local_port is ignored - // 0 - // } else { - // config.local_port.unwrap_or(0) - // }; - - // let local_addr = match la.parse::() { - // Ok(ip) => ServerAddr::from(SocketAddr::new(ip, local_port)), - // Err(..) => { - // // treated as domain - // ServerAddr::from((la, local_port)) - // } - // }; - // nconfig.local_addr = Some(local_addr); - // } - // None => { - // if config_type.is_local() && config.local_port.is_some() { - // // Implementation note: This is not implemented like libev which will choose IPv6 or IPv6 LoopBack address - // // by checking all its remote servers if all of them supports IPv6. - // let ip = if config.ipv6_first.unwrap_or(false) { - // Ipv6Addr::LOCALHOST.into() - // } else { - // Ipv4Addr::LOCALHOST.into() - // }; - - // let local_port = config.local_port.unwrap_or(0); - // if local_port == 0 { - // let err = Error::new(ErrorKind::MissingField, "`local_port` shouldn't be 0", None); - // return Err(err); - // } - - // let local_addr = ServerAddr::from(SocketAddr::new(ip, local_port)); - // nconfig.local_addr = Some(local_addr); - // } - // } - // }; - // Standard config // Server match (config.server, config.server_port, config.password, config.method) { @@ -1148,6 +1100,7 @@ impl Config { }; let mut nsvr = ServerConfig::new(addr, pwd, method); + nsvr.set_mode(global_mode); if let Some(p) = config.plugin { // SIP008 allows "plugin" to be an empty string @@ -1212,6 +1165,17 @@ impl Config { let mut nsvr = ServerConfig::new(addr, svr.password, method); + match svr.mode { + Some(mode) => match mode.parse::() { + Ok(mode) => nsvr.set_mode(mode), + Err(..) => { + let err = Error::new(ErrorKind::Malformed, "invalid `mode`", None); + return Err(err); + } + }, + None => nsvr.set_mode(global_mode), + } + if let Some(p) = svr.plugin { // SIP008 allows "plugin" to be an empty string // Empty string implies "no plugin" @@ -1273,7 +1237,8 @@ impl Config { } }; - let manager_config = ManagerConfig::new(manager); + let mut manager_config = ManagerConfig::new(manager); + manager_config.mode = global_mode; nconfig.manager = Some(manager_config); } @@ -1304,21 +1269,6 @@ impl Config { }; } - // Mode - if let Some(m) = config.mode { - match m.parse::() { - Ok(xm) => nconfig.mode = xm, - Err(..) => { - let e = Error::new( - ErrorKind::Malformed, - "malformed `mode`, must be one of `tcp_only`, `udp_only` and `tcp_and_udp`", - None, - ); - return Err(e); - } - } - } - // TCP nodelay if let Some(b) = config.no_delay { nconfig.no_delay = b; @@ -1608,6 +1558,7 @@ impl fmt::Display for Config { ServerAddr::DomainName(.., port) => *port, }), }, + mode: Some(local.mode.to_string()), protocol: match local.protocol { ProtocolType::Socks => None, p => Some(p.as_str().to_owned()), @@ -1710,6 +1661,7 @@ impl fmt::Display for Config { } }); jconf.timeout = svr.timeout().map(|t| t.as_secs()); + jconf.mode = Some(svr.mode().to_string()); } // For >1 servers, uses extended multiple server format _ => { @@ -1740,6 +1692,7 @@ impl fmt::Display for Config { timeout: svr.timeout().map(|t| t.as_secs()), remarks: svr.remarks().map(ToOwned::to_owned), id: svr.id().map(ToOwned::to_owned), + mode: Some(svr.mode().to_string()), }); } @@ -1761,9 +1714,11 @@ impl fmt::Display for Config { #[cfg(unix)] ManagerAddr::UnixSocketAddr(..) => None, }; - } - jconf.mode = Some(self.mode.to_string()); + if jconf.mode.is_none() { + jconf.mode = Some(m.mode.to_string()); + } + } if self.no_delay { jconf.no_delay = Some(self.no_delay); diff --git a/crates/shadowsocks-service/src/dns/mod.rs b/crates/shadowsocks-service/src/dns/mod.rs index 8f54450d..eac1a1ff 100644 --- a/crates/shadowsocks-service/src/dns/mod.rs +++ b/crates/shadowsocks-service/src/dns/mod.rs @@ -41,8 +41,9 @@ pub async fn build_dns_resolver(dns: DnsConfig, ipv6_first: bool, connect_opts: }, #[cfg(feature = "local-dns")] DnsConfig::LocalDns(ns) => { - use crate::{config::Mode, local::dns::dns_resolver::DnsResolver as LocalDnsResolver}; + use crate::local::dns::dns_resolver::DnsResolver as LocalDnsResolver; use log::trace; + use shadowsocks::config::Mode; trace!("initializing direct DNS resolver for {}", ns); diff --git a/crates/shadowsocks-service/src/local/dns/dns_resolver.rs b/crates/shadowsocks-service/src/local/dns/dns_resolver.rs index 2da5b14f..d161d34f 100644 --- a/crates/shadowsocks-service/src/local/dns/dns_resolver.rs +++ b/crates/shadowsocks-service/src/local/dns/dns_resolver.rs @@ -8,14 +8,12 @@ use std::{ use async_trait::async_trait; use futures::future; use log::{debug, trace}; -use shadowsocks::{dns_resolver::DnsResolve, net::ConnectOpts}; +use shadowsocks::{config::Mode, dns_resolver::DnsResolve, net::ConnectOpts}; use trust_dns_resolver::proto::{ op::{Message, Query}, rr::{DNSClass, Name, RData, RecordType}, }; -use crate::config::Mode; - use super::{client_cache::DnsClientCache, config::NameServerAddr}; pub struct DnsResolver { diff --git a/crates/shadowsocks-service/src/local/dns/server.rs b/crates/shadowsocks-service/src/local/dns/server.rs index 9c91a6e0..efadf7f7 100644 --- a/crates/shadowsocks-service/src/local/dns/server.rs +++ b/crates/shadowsocks-service/src/local/dns/server.rs @@ -16,6 +16,7 @@ use futures::future::{self, Either}; use log::{debug, error, info, trace, warn}; use rand::{thread_rng, Rng}; use shadowsocks::{ + config::Mode, lookup_then, net::{TcpListener, UdpSocket as ShadowUdpSocket}, relay::{udprelay::MAXIMUM_UDP_PAYLOAD_SIZE, Address}, @@ -33,7 +34,6 @@ use trust_dns_resolver::proto::{ use crate::{ acl::AccessControl, - config::Mode, local::{context::ServiceContext, loadbalancing::PingBalancer}, }; diff --git a/crates/shadowsocks-service/src/local/loadbalancing/ping_balancer.rs b/crates/shadowsocks-service/src/local/loadbalancing/ping_balancer.rs index 4d0f3a3a..05e2e2ea 100644 --- a/crates/shadowsocks-service/src/local/loadbalancing/ping_balancer.rs +++ b/crates/shadowsocks-service/src/local/loadbalancing/ping_balancer.rs @@ -16,6 +16,7 @@ use byte_string::ByteStr; use futures::future::{self, AbortHandle}; use log::{debug, info, trace}; use shadowsocks::{ + config::Mode, relay::{ socks5::Address, tcprelay::proxy_stream::ProxyClientStream, @@ -28,7 +29,7 @@ use tokio::{ time, }; -use crate::{config::Mode, local::context::ServiceContext}; +use crate::local::context::ServiceContext; use super::{ server_data::ServerIdent, diff --git a/crates/shadowsocks-service/src/local/mod.rs b/crates/shadowsocks-service/src/local/mod.rs index 3af939e9..dcb595ab 100644 --- a/crates/shadowsocks-service/src/local/mod.rs +++ b/crates/shadowsocks-service/src/local/mod.rs @@ -11,6 +11,7 @@ use futures::{ }; use log::{error, trace, warn}; use shadowsocks::{ + config::Mode, net::{AcceptOpts, ConnectOpts}, plugin::{Plugin, PluginMode}, }; @@ -104,17 +105,16 @@ pub async fn run(mut config: Config) -> io::Result<()> { // Check if any of the local servers enable TCP connections - let mode = config.mode; let enable_tcp = config.local.iter().any(|local_config| match local_config.protocol { - ProtocolType::Socks => mode.enable_tcp(), + ProtocolType::Socks => local_config.mode.enable_tcp(), #[cfg(feature = "local-tunnel")] - ProtocolType::Tunnel => mode.enable_tcp(), + ProtocolType::Tunnel => local_config.mode.enable_tcp(), #[cfg(feature = "local-http")] ProtocolType::Http => true, #[cfg(feature = "local-redir")] - ProtocolType::Redir => mode.enable_tcp(), + ProtocolType::Redir => local_config.mode.enable_tcp(), #[cfg(feature = "local-dns")] - ProtocolType::Dns => mode.enable_tcp(), + ProtocolType::Dns => local_config.mode.enable_tcp(), }); if enable_tcp { @@ -169,7 +169,13 @@ pub async fn run(mut config: Config) -> io::Result<()> { // // XXX: This have to be called after allocating plugins' addresses let balancer = { - let mut balancer_builder = PingBalancerBuilder::new(context.clone(), config.mode); + let mut mode = Mode::TcpOnly; + + for local in &config.local { + mode = mode.merge(local.mode); + } + + let mut balancer_builder = PingBalancerBuilder::new(context.clone(), mode); for server in config.server { balancer_builder.add_server(ServerIdent::new(server)); } @@ -179,21 +185,6 @@ pub async fn run(mut config: Config) -> io::Result<()> { balancer }; - // #[cfg(feature = "local-dns")] - // if matches!(config.local_protocol, ProtocolType::Dns) || config.dns_bind_addr.is_some() { - // use self::dns::Dns; - - // let local_addr = config.local_dns_addr.expect("missing local_dns_addr"); - // let remote_addr = config.remote_dns_addr.expect("missing remote_dns_addr"); - - // let bind_addr = config.dns_bind_addr.as_ref().unwrap_or_else(|| &client_config); - - // let mut server = Dns::with_context(context.clone(), local_addr, remote_addr); - // server.set_mode(config.mode); - - // vfut.push(server.run(bind_addr, balancer.clone()).boxed()); - // } - #[cfg(feature = "local-flow-stat")] if let Some(stat_path) = config.stat_path { // For Android's flow statistic @@ -211,7 +202,7 @@ pub async fn run(mut config: Config) -> io::Result<()> { use self::socks::Socks; let mut server = Socks::with_context(context.clone()); - server.set_mode(config.mode); + server.set_mode(local_config.mode); if let Some(c) = config.udp_max_associations { server.set_udp_capacity(c); @@ -242,12 +233,13 @@ pub async fn run(mut config: Config) -> io::Result<()> { if let Some(d) = config.udp_timeout { server.set_udp_expiry_duration(d); } - server.set_mode(config.mode); + server.set_mode(local_config.mode); if config.no_delay { server.set_nodelay(true); } - vfut.push(async move { server.run(&client_addr, balancer).await }.boxed()); + let udp_addr = local_config.udp_addr.unwrap_or_else(|| client_addr.clone()); + vfut.push(async move { server.run(&client_addr, &udp_addr, balancer).await }.boxed()); } #[cfg(feature = "local-http")] ProtocolType::Http => { @@ -267,14 +259,15 @@ pub async fn run(mut config: Config) -> io::Result<()> { if let Some(d) = config.udp_timeout { server.set_udp_expiry_duration(d); } - server.set_mode(config.mode); + server.set_mode(local_config.mode); if config.no_delay { server.set_nodelay(true); } server.set_tcp_redir(local_config.tcp_redir); server.set_udp_redir(local_config.udp_redir); - vfut.push(async move { server.run(&client_addr, balancer).await }.boxed()); + let udp_addr = local_config.udp_addr.unwrap_or_else(|| client_addr.clone()); + vfut.push(async move { server.run(&client_addr, &udp_addr, balancer).await }.boxed()); } #[cfg(feature = "local-dns")] ProtocolType::Dns => { @@ -286,7 +279,7 @@ pub async fn run(mut config: Config) -> io::Result<()> { Dns::with_context(context.clone(), local_addr.clone(), remote_addr.clone()) }; - server.set_mode(config.mode); + server.set_mode(local_config.mode); vfut.push(async move { server.run(&client_addr, balancer).await }.boxed()); } diff --git a/crates/shadowsocks-service/src/local/redir/server.rs b/crates/shadowsocks-service/src/local/redir/server.rs index 1a000392..863b02aa 100644 --- a/crates/shadowsocks-service/src/local/redir/server.rs +++ b/crates/shadowsocks-service/src/local/redir/server.rs @@ -3,10 +3,10 @@ use std::{io, sync::Arc, time::Duration}; use futures::{future, FutureExt}; -use shadowsocks::ServerAddr; +use shadowsocks::{config::Mode, ServerAddr}; use crate::{ - config::{Mode, RedirType}, + config::RedirType, local::{context::ServiceContext, loadbalancing::PingBalancer}, }; @@ -74,15 +74,15 @@ impl Redir { } /// Start serving - pub async fn run(self, client_config: &ServerAddr, balancer: PingBalancer) -> io::Result<()> { + pub async fn run(self, tcp_addr: &ServerAddr, udp_addr: &ServerAddr, balancer: PingBalancer) -> io::Result<()> { let mut vfut = Vec::new(); if self.mode.enable_tcp() { - vfut.push(self.run_tcp_tunnel(client_config, balancer.clone()).boxed()); + vfut.push(self.run_tcp_tunnel(tcp_addr, balancer.clone()).boxed()); } if self.mode.enable_udp() { - vfut.push(self.run_udp_tunnel(client_config, balancer).boxed()); + vfut.push(self.run_udp_tunnel(udp_addr, balancer).boxed()); } let (res, ..) = future::select_all(vfut).await; diff --git a/crates/shadowsocks-service/src/local/socks/server/mod.rs b/crates/shadowsocks-service/src/local/socks/server/mod.rs index f9fd4f61..b2fd11e6 100644 --- a/crates/shadowsocks-service/src/local/socks/server/mod.rs +++ b/crates/shadowsocks-service/src/local/socks/server/mod.rs @@ -4,13 +4,10 @@ use std::{io, net::SocketAddr, sync::Arc, time::Duration}; use futures::{future, FutureExt}; use log::{error, info}; -use shadowsocks::{lookup_then, net::TcpListener as ShadowTcpListener, ServerAddr}; +use shadowsocks::{config::Mode, lookup_then, net::TcpListener as ShadowTcpListener, ServerAddr}; use tokio::{net::TcpStream, time}; -use crate::{ - config::Mode, - local::{context::ServiceContext, loadbalancing::PingBalancer}, -}; +use crate::local::{context::ServiceContext, loadbalancing::PingBalancer}; #[cfg(feature = "local-socks4")] use self::socks4::Socks4TcpHandler; diff --git a/crates/shadowsocks-service/src/local/socks/server/socks4/tcprelay.rs b/crates/shadowsocks-service/src/local/socks/server/socks4/tcprelay.rs index e3384920..ddd39601 100644 --- a/crates/shadowsocks-service/src/local/socks/server/socks4/tcprelay.rs +++ b/crates/shadowsocks-service/src/local/socks/server/socks4/tcprelay.rs @@ -7,19 +7,17 @@ use std::{ }; use log::{debug, trace, warn}; +use shadowsocks::config::Mode; use tokio::{ io::{AsyncWriteExt, BufReader}, net::TcpStream, }; -use crate::{ - config::Mode, - local::{ - context::ServiceContext, - loadbalancing::PingBalancer, - net::AutoProxyClientStream, - utils::establish_tcp_tunnel, - }, +use crate::local::{ + context::ServiceContext, + loadbalancing::PingBalancer, + net::AutoProxyClientStream, + utils::establish_tcp_tunnel, }; use crate::local::socks::socks4::{Address, Command, HandshakeRequest, HandshakeResponse, ResultCode}; diff --git a/crates/shadowsocks-service/src/local/socks/server/socks5/tcprelay.rs b/crates/shadowsocks-service/src/local/socks/server/socks5/tcprelay.rs index c0e9e948..9e0f3343 100644 --- a/crates/shadowsocks-service/src/local/socks/server/socks5/tcprelay.rs +++ b/crates/shadowsocks-service/src/local/socks/server/socks5/tcprelay.rs @@ -8,6 +8,7 @@ use std::{ use log::{debug, error, trace, warn}; use shadowsocks::{ + config::Mode, relay::socks5::{ self, Address, @@ -23,7 +24,6 @@ use shadowsocks::{ use tokio::net::TcpStream; use crate::{ - config::Mode, local::{ context::ServiceContext, loadbalancing::PingBalancer, diff --git a/crates/shadowsocks-service/src/local/tunnel/server.rs b/crates/shadowsocks-service/src/local/tunnel/server.rs index 7bf2c70d..91a03d5f 100644 --- a/crates/shadowsocks-service/src/local/tunnel/server.rs +++ b/crates/shadowsocks-service/src/local/tunnel/server.rs @@ -3,12 +3,9 @@ use std::{io, sync::Arc, time::Duration}; use futures::{future, FutureExt}; -use shadowsocks::{relay::socks5::Address, ServerAddr}; +use shadowsocks::{config::Mode, relay::socks5::Address, ServerAddr}; -use crate::{ - config::Mode, - local::{context::ServiceContext, loadbalancing::PingBalancer}, -}; +use crate::local::{context::ServiceContext, loadbalancing::PingBalancer}; use super::{tcprelay::run_tcp_tunnel, udprelay::UdpTunnel}; @@ -62,15 +59,15 @@ impl Tunnel { } /// Start serving - pub async fn run(self, client_config: &ServerAddr, balancer: PingBalancer) -> io::Result<()> { + pub async fn run(self, tcp_addr: &ServerAddr, udp_addr: &ServerAddr, balancer: PingBalancer) -> io::Result<()> { let mut vfut = Vec::new(); if self.mode.enable_tcp() { - vfut.push(self.run_tcp_tunnel(client_config, balancer.clone()).boxed()); + vfut.push(self.run_tcp_tunnel(tcp_addr, balancer.clone()).boxed()); } if self.mode.enable_udp() { - vfut.push(self.run_udp_tunnel(client_config, balancer).boxed()); + vfut.push(self.run_udp_tunnel(udp_addr, balancer).boxed()); } let (res, ..) = future::select_all(vfut).await; diff --git a/crates/shadowsocks-service/src/manager/mod.rs b/crates/shadowsocks-service/src/manager/mod.rs index ed9b6c35..cc6c3b4e 100644 --- a/crates/shadowsocks-service/src/manager/mod.rs +++ b/crates/shadowsocks-service/src/manager/mod.rs @@ -31,7 +31,6 @@ pub async fn run(config: Config) -> io::Result<()> { } let mut manager = Manager::new(config.manager.expect("missing manager config")); - manager.set_mode(config.mode); let mut connect_opts = ConnectOpts { #[cfg(any(target_os = "linux", target_os = "android"))] @@ -73,7 +72,7 @@ pub async fn run(config: Config) -> io::Result<()> { } for svr_cfg in config.server { - manager.add_server(svr_cfg, None).await; + manager.add_server(svr_cfg).await; } manager.run().await diff --git a/crates/shadowsocks-service/src/manager/server.rs b/crates/shadowsocks-service/src/manager/server.rs index ec0638e1..f18946c9 100644 --- a/crates/shadowsocks-service/src/manager/server.rs +++ b/crates/shadowsocks-service/src/manager/server.rs @@ -5,7 +5,7 @@ use std::{collections::HashMap, io, net::SocketAddr, sync::Arc, time::Duration}; use futures::future::{self, AbortHandle}; use log::{error, info}; use shadowsocks::{ - config::{ServerConfig, ServerType}, + config::{Mode, ServerConfig, ServerType}, context::{Context, SharedContext}, crypto::v1::CipherKind, dns_resolver::DnsResolver, @@ -30,7 +30,7 @@ use tokio::sync::Mutex; use crate::{ acl::AccessControl, - config::{ManagerConfig, ManagerServerHost, Mode}, + config::{ManagerConfig, ManagerServerHost}, net::FlowStat, server::Server, }; @@ -52,7 +52,6 @@ pub struct Manager { context: SharedContext, servers: Mutex>, svr_cfg: ManagerConfig, - mode: Mode, connect_opts: ConnectOpts, accept_opts: AcceptOpts, udp_expiry_duration: Option, @@ -72,7 +71,6 @@ impl Manager { context, servers: Mutex::new(HashMap::new()), svr_cfg, - mode: Mode::TcpOnly, connect_opts: ConnectOpts::default(), accept_opts: AcceptOpts::default(), udp_expiry_duration: None, @@ -101,11 +99,6 @@ impl Manager { self.udp_capacity = Some(c); } - /// Set server's default mode - pub fn set_mode(&mut self, mode: Mode) { - self.mode = mode; - } - /// Get the manager's configuration pub fn config(&self) -> &ManagerConfig { &self.svr_cfg @@ -166,7 +159,7 @@ impl Manager { } } - pub async fn add_server(&self, svr_cfg: ServerConfig, mode: Option) { + pub async fn add_server(&self, svr_cfg: ServerConfig) { // Each server should use a separate Context, but shares // // * AccessControlList @@ -185,8 +178,6 @@ impl Manager { server.set_udp_capacity(c); } - server.set_mode(mode.unwrap_or(self.mode)); - if let Some(ref acl) = self.acl { server.set_acl(acl.clone()); } @@ -261,7 +252,9 @@ impl Manager { }, }; - self.add_server(svr_cfg, mode).await; + svr_cfg.set_mode(mode.unwrap_or(self.svr_cfg.mode)); + + self.add_server(svr_cfg).await; Ok(AddResponse("ok".to_owned())) } diff --git a/crates/shadowsocks-service/src/server/mod.rs b/crates/shadowsocks-service/src/server/mod.rs index b0d921e2..652ecad5 100644 --- a/crates/shadowsocks-service/src/server/mod.rs +++ b/crates/shadowsocks-service/src/server/mod.rs @@ -91,7 +91,6 @@ pub async fn run(config: Config) -> io::Result<()> { if let Some(d) = config.udp_timeout { server.set_udp_expiry_duration(d); } - server.set_mode(config.mode); if let Some(ref m) = config.manager { server.set_manager_addr(m.addr.clone()); } diff --git a/crates/shadowsocks-service/src/server/server.rs b/crates/shadowsocks-service/src/server/server.rs index 58627840..b1a81b7f 100644 --- a/crates/shadowsocks-service/src/server/server.rs +++ b/crates/shadowsocks-service/src/server/server.rs @@ -18,7 +18,7 @@ use shadowsocks::{ }; use tokio::time; -use crate::{acl::AccessControl, config::Mode, net::FlowStat}; +use crate::{acl::AccessControl, net::FlowStat}; use super::{context::ServiceContext, tcprelay::TcpServer, udprelay::UdpServer}; @@ -26,7 +26,6 @@ use super::{context::ServiceContext, tcprelay::TcpServer, udprelay::UdpServer}; pub struct Server { context: Arc, svr_cfg: ServerConfig, - mode: Mode, udp_expiry_duration: Option, udp_capacity: Option, manager_addr: Option, @@ -44,7 +43,6 @@ impl Server { Server { context, svr_cfg, - mode: Mode::TcpOnly, udp_expiry_duration: None, udp_capacity: None, manager_addr: None, @@ -78,11 +76,6 @@ impl Server { self.udp_capacity = Some(c); } - /// Set server's mode - pub fn set_mode(&mut self, mode: Mode) { - self.mode = mode; - } - /// Set manager's address to report `stat` pub fn set_manager_addr(&mut self, manager_addr: ManagerAddr) { self.manager_addr = Some(manager_addr); @@ -114,7 +107,7 @@ impl Server { pub async fn run(mut self) -> io::Result<()> { let vfut = FuturesUnordered::new(); - if self.mode.enable_tcp() { + if self.svr_cfg.mode().enable_tcp() { if let Some(plugin_cfg) = self.svr_cfg.plugin() { let plugin = Plugin::start(plugin_cfg, self.svr_cfg.addr(), PluginMode::Server)?; self.svr_cfg.set_plugin_addr(plugin.local_addr().into()); @@ -139,7 +132,7 @@ impl Server { vfut.push(tcp_fut); } - if self.mode.enable_udp() { + if self.svr_cfg.mode().enable_udp() { let udp_fut = self.run_udp_server().boxed(); vfut.push(udp_fut); } diff --git a/crates/shadowsocks/src/config.rs b/crates/shadowsocks/src/config.rs index 00dcb548..735b842e 100644 --- a/crates/shadowsocks/src/config.rs +++ b/crates/shadowsocks/src/config.rs @@ -42,6 +42,61 @@ impl ServerType { } } +/// Server mode +#[derive(Clone, Copy, Debug)] +pub enum Mode { + TcpOnly = 0x01, + TcpAndUdp = 0x03, + UdpOnly = 0x02, +} + +impl Mode { + /// Check if UDP is enabled + pub fn enable_udp(self) -> bool { + matches!(self, Mode::UdpOnly | Mode::TcpAndUdp) + } + + /// Check if TCP is enabled + pub fn enable_tcp(self) -> bool { + matches!(self, Mode::TcpOnly | Mode::TcpAndUdp) + } + + /// Merge with another Mode + pub fn merge(&self, mode: Mode) -> Mode { + let me = *self as u8; + let fm = mode as u8; + match me | fm { + 0x01 => Mode::TcpOnly, + 0x02 => Mode::UdpOnly, + 0x03 => Mode::TcpAndUdp, + _ => unreachable!(), + } + } +} + +impl fmt::Display for Mode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Mode::TcpOnly => f.write_str("tcp_only"), + Mode::TcpAndUdp => f.write_str("tcp_and_udp"), + Mode::UdpOnly => f.write_str("udp_only"), + } + } +} + +impl FromStr for Mode { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "tcp_only" => Ok(Mode::TcpOnly), + "tcp_and_udp" => Ok(Mode::TcpAndUdp), + "udp_only" => Ok(Mode::UdpOnly), + _ => Err(()), + } + } +} + /// Configuration for a server #[derive(Clone, Debug)] pub struct ServerConfig { @@ -60,10 +115,14 @@ pub struct ServerConfig { plugin: Option, /// Plugin address plugin_addr: Option, + /// Remark (Profile Name), normally used as an identifier of this erver remarks: Option, /// ID (SIP008) is a random generated UUID id: Option, + + /// Mode + mode: Mode, } impl ServerConfig { @@ -88,6 +147,7 @@ impl ServerConfig { plugin_addr: None, remarks: None, id: None, + mode: Mode::TcpOnly, } } @@ -194,6 +254,16 @@ impl ServerConfig { self.id = Some(id.into()) } + /// Get server's `Mode` + pub fn mode(&self) -> Mode { + self.mode + } + + /// Set server's `Mode` + pub fn set_mode(&mut self, mode: Mode) { + self.mode = mode; + } + /// Get URL for QRCode /// ```plain /// ss:// + base64(method:password@host:port) diff --git a/tests/socks5.rs b/tests/socks5.rs index 6bb1b9d0..2b963827 100644 --- a/tests/socks5.rs +++ b/tests/socks5.rs @@ -11,12 +11,12 @@ use tokio::{ }; use shadowsocks_service::{ - config::{Config, ConfigType, LocalConfig, Mode, ProtocolType}, + config::{Config, ConfigType, LocalConfig, ProtocolType}, local::socks::client::socks5::Socks5TcpClient, run_local, run_server, shadowsocks::{ - config::{ServerAddr, ServerConfig}, + config::{Mode, ServerAddr, ServerConfig}, crypto::v1::CipherKind, relay::socks5::Address, }, @@ -42,14 +42,14 @@ impl Socks5TestServer { svr_config: { let mut cfg = Config::new(ConfigType::Server); cfg.server = vec![ServerConfig::new(svr_addr, pwd.to_owned(), method)]; - cfg.mode = if enable_udp { Mode::TcpAndUdp } else { Mode::TcpOnly }; + cfg.server[0].set_mode(if enable_udp { Mode::TcpAndUdp } else { Mode::TcpOnly }); cfg }, cli_config: { let mut cfg = Config::new(ConfigType::Local); cfg.local = vec![LocalConfig::new(ServerAddr::from(local_addr), ProtocolType::Socks)]; + cfg.local[0].mode = if enable_udp { Mode::TcpAndUdp } else { Mode::TcpOnly }; cfg.server = vec![ServerConfig::new(svr_addr, pwd.to_owned(), method)]; - cfg.mode = if enable_udp { Mode::TcpAndUdp } else { Mode::TcpOnly }; cfg }, } diff --git a/tests/udp.rs b/tests/udp.rs index e16ad3f8..cbfbb0b4 100644 --- a/tests/udp.rs +++ b/tests/udp.rs @@ -7,11 +7,11 @@ use log::debug; use tokio::time::{self, Duration}; use shadowsocks_service::{ - config::{Config, ConfigType, LocalConfig, Mode, ProtocolType}, + config::{Config, ConfigType, LocalConfig, ProtocolType}, local::socks::client::socks5::Socks5UdpClient, run_local, run_server, - shadowsocks::{crypto::v1::CipherKind, relay::socks5::Address, ServerConfig}, + shadowsocks::{config::Mode, crypto::v1::CipherKind, relay::socks5::Address, ServerConfig}, }; const SERVER_ADDR: &str = "127.0.0.1:8093"; @@ -29,19 +29,19 @@ fn get_svr_config() -> Config { PASSWORD.to_owned(), METHOD, )]; - cfg.mode = Mode::TcpAndUdp; + cfg.server[0].set_mode(Mode::TcpAndUdp); cfg } fn get_cli_config() -> Config { let mut cfg = Config::new(ConfigType::Local); cfg.local = vec![LocalConfig::new(LOCAL_ADDR.parse().unwrap(), ProtocolType::Socks)]; + cfg.local[0].mode = Mode::TcpAndUdp; cfg.server = vec![ServerConfig::new( SERVER_ADDR.parse::().unwrap(), PASSWORD.to_owned(), METHOD, )]; - cfg.mode = Mode::TcpAndUdp; cfg }