diff --git a/README.md b/README.md index 4f27d2ee..9d676b0c 100644 --- a/README.md +++ b/README.md @@ -554,6 +554,9 @@ Example configuration: "socks5_auth_config_path": "/path/to/auth.json", // OPTIONAL. Instance specific ACL "acl": "/path/to/acl/file.acl", + // OPTIONAL. macOS launchd activate socket + "launchd_tcp_socket_name": "TCPListener", + "launchd_udp_socket_name": "UDPListener" }, { // SOCKS5, SOCKS4/4a local server @@ -567,7 +570,10 @@ Example configuration: // - TCP is enabled, then SOCKS5's UDP Association command will return this address // - UDP is enabled, then SOCKS5's UDP server will listen to this address. "local_udp_address": "127.0.0.1", - "local_udp_port": 2081 + "local_udp_port": 2081, + // OPTIONAL. macOS launchd activate socket + "launchd_tcp_socket_name": "TCPListener", + "launchd_udp_socket_name": "UDPListener" }, { // Tunnel local server (feature = "local-tunnel") @@ -580,14 +586,19 @@ Example configuration: "forward_address": "8.8.8.8", "forward_port": 53, // OPTIONAL. Customizing whether to start TCP and UDP tunnel - "mode": "tcp_only" + "mode": "tcp_only", + // OPTIONAL. macOS launchd activate socket + "launchd_tcp_socket_name": "TCPListener", + "launchd_udp_socket_name": "UDPListener" }, { // HTTP local server (feature = "local-http") "protocol": "http", // Listen address "local_address": "127.0.0.1", - "local_port": 3128 + "local_port": 3128, + // OPTIONAL. macOS launchd activate socket + "launchd_tcp_socket_name": "TCPListener" }, { // DNS local server (feature = "local-dns") @@ -607,7 +618,10 @@ Example configuration: // OPTIONAL. Remote DNS's port, 53 by default "remote_dns_port": 53, // OPTIONAL. dns client cache size for fetching dns queries. - "client_cache_size": 5 + "client_cache_size": 5, + // OPTIONAL. macOS launchd activate socket + "launchd_tcp_socket_name": "TCPListener", + "launchd_udp_socket_name": "UDPListener" }, { // Tun local server (feature = "local-tun") diff --git a/crates/shadowsocks-service/src/config.rs b/crates/shadowsocks-service/src/config.rs index 0bc026cf..7835cf4f 100644 --- a/crates/shadowsocks-service/src/config.rs +++ b/crates/shadowsocks-service/src/config.rs @@ -240,6 +240,14 @@ struct SSLocalExtConfig { #[serde(skip_serializing_if = "Option::is_none")] protocol: Option, + /// macOS launch activate socket + #[cfg(target_os = "macos")] + #[serde(skip_serializing_if = "Option::is_none")] + launchd_udp_socket_name: Option, + #[cfg(target_os = "macos")] + #[serde(skip_serializing_if = "Option::is_none")] + launchd_tcp_socket_name: Option, + /// TCP Transparent Proxy type #[cfg(feature = "local-redir")] #[serde(skip_serializing_if = "Option::is_none")] @@ -888,6 +896,43 @@ pub struct LocalConfig { #[cfg(all(feature = "local-tun", unix))] pub tun_device_fd_from_path: Option, + /// macOS launchd socket for TCP listener + /// + /// + /// + /// + /// ```plist + /// Sockets + /// + /// {launchd_tcp_socket_name} + /// + /// SockType + /// stream + /// ... other keys ... + /// + /// + /// ``` + #[cfg(target_os = "macos")] + pub launchd_tcp_socket_name: Option, + /// macOS launchd socket for UDP listener + /// + /// + /// + /// + /// ```plist + /// Sockets + /// + /// {launchd_udp_socket_name} + /// + /// SockType + /// dgram + /// ... other keys ... + /// + /// + /// ``` + #[cfg(target_os = "macos")] + pub launchd_udp_socket_name: Option, + /// Set `IPV6_V6ONLY` for listener socket pub ipv6_only: bool, @@ -941,6 +986,11 @@ impl LocalConfig { #[cfg(all(feature = "local-tun", unix))] tun_device_fd_from_path: None, + #[cfg(target_os = "macos")] + launchd_tcp_socket_name: None, + #[cfg(target_os = "macos")] + launchd_udp_socket_name: None, + ipv6_only: false, #[cfg(feature = "local")] @@ -1465,6 +1515,12 @@ impl Config { local_config.udp_addr = Some(local_udp_addr); } + #[cfg(target_os = "macos")] + { + local_config.launchd_tcp_socket_name = local.launchd_tcp_socket_name; + local_config.launchd_udp_socket_name = local.launchd_udp_socket_name; + } + match local.mode { Some(mode) => match mode.parse::() { Ok(mode) => local_config.mode = mode, @@ -2423,6 +2479,10 @@ impl fmt::Display for Config { #[allow(unreachable_patterns)] p => Some(p.as_str().to_owned()), }, + #[cfg(target_os = "macos")] + launchd_tcp_socket_name: local.launchd_tcp_socket_name.clone(), + #[cfg(target_os = "macos")] + launchd_udp_socket_name: local.launchd_udp_socket_name.clone(), #[cfg(feature = "local-redir")] tcp_redir: if local.tcp_redir != RedirType::tcp_default() { Some(local.tcp_redir.to_string()) diff --git a/crates/shadowsocks-service/src/local/dns/server.rs b/crates/shadowsocks-service/src/local/dns/server.rs index 0026ea55..567a5eed 100644 --- a/crates/shadowsocks-service/src/local/dns/server.rs +++ b/crates/shadowsocks-service/src/local/dns/server.rs @@ -32,15 +32,18 @@ use tokio::{ use shadowsocks::{ config::Mode, - lookup_then, - net::{TcpListener, UdpSocket as ShadowUdpSocket}, + net::TcpListener, relay::{udprelay::MAXIMUM_UDP_PAYLOAD_SIZE, Address}, ServerAddr, }; use crate::{ acl::AccessControl, - local::{context::ServiceContext, loadbalancing::PingBalancer}, + local::{ + context::ServiceContext, + loadbalancing::PingBalancer, + net::{tcp::listener::create_standard_tcp_listener, udp::listener::create_standard_udp_listener}, + }, }; use super::{client_cache::DnsClientCache, config::NameServerAddr}; @@ -54,6 +57,10 @@ pub struct DnsBuilder { bind_addr: ServerAddr, balancer: PingBalancer, client_cache_size: usize, + #[cfg(target_os = "macos")] + launchd_tcp_socket_name: Option, + #[cfg(target_os = "macos")] + launchd_udp_socket_name: Option, } impl DnsBuilder { @@ -93,6 +100,10 @@ impl DnsBuilder { bind_addr, balancer, client_cache_size, + #[cfg(target_os = "macos")] + launchd_tcp_socket_name: None, + #[cfg(target_os = "macos")] + launchd_udp_socket_name: None, } } @@ -101,6 +112,18 @@ impl DnsBuilder { self.mode = mode; } + /// macOS launchd activate socket + #[cfg(target_os = "macos")] + pub fn set_launchd_tcp_socket_name(&mut self, n: String) { + self.launchd_tcp_socket_name = Some(n); + } + + /// macOS launchd activate socket + #[cfg(target_os = "macos")] + pub fn set_launchd_udp_socket_name(&mut self, n: String) { + self.launchd_udp_socket_name = Some(n); + } + /// Build DNS server pub async fn build(self) -> io::Result { let client = Arc::new(DnsClient::new( @@ -115,20 +138,35 @@ impl DnsBuilder { let mut tcp_server = None; if self.mode.enable_tcp() { - let server = DnsTcpServer::new( + #[allow(unused_mut)] + let mut builder = DnsTcpServerBuilder::new( self.context.clone(), - &self.bind_addr, + self.bind_addr.clone(), local_addr.clone(), remote_addr.clone(), client.clone(), - ) - .await?; + ); + + #[cfg(target_os = "macos")] + if let Some(s) = self.launchd_tcp_socket_name { + builder.set_launchd_socket_name(s); + } + + let server = builder.build().await?; tcp_server = Some(server); } let mut udp_server = None; if self.mode.enable_udp() { - let server = DnsUdpServer::new(self.context, &self.bind_addr, local_addr, remote_addr, client).await?; + #[allow(unused_mut)] + let mut builder = DnsUdpServerBuilder::new(self.context, self.bind_addr, local_addr, remote_addr, client); + + #[cfg(target_os = "macos")] + if let Some(s) = self.launchd_udp_socket_name { + builder.set_launchd_socket_name(s); + } + + let server = builder.build().await?; udp_server = Some(server); } @@ -136,6 +174,68 @@ impl DnsBuilder { } } +struct DnsTcpServerBuilder { + context: Arc, + bind_addr: ServerAddr, + local_addr: Arc, + remote_addr: Arc
, + client: Arc, + #[cfg(target_os = "macos")] + launchd_socket_name: Option, +} + +impl DnsTcpServerBuilder { + fn new( + context: Arc, + bind_addr: ServerAddr, + local_addr: Arc, + remote_addr: Arc
, + client: Arc, + ) -> DnsTcpServerBuilder { + DnsTcpServerBuilder { + context, + bind_addr, + local_addr, + remote_addr, + client, + #[cfg(target_os = "macos")] + launchd_socket_name: None, + } + } + + /// macOS launchd activate socket + #[cfg(target_os = "macos")] + fn set_launchd_socket_name(&mut self, n: String) { + self.launchd_socket_name = Some(n); + } + + async fn build(self) -> io::Result { + cfg_if::cfg_if! { + if #[cfg(target_os = "macos")] { + let listener = if let Some(launchd_socket_name) = self.launchd_socket_name { + use tokio::net::TcpListener as TokioTcpListener; + use crate::net::launch_activate_socket::get_launch_activate_tcp_listener; + + let std_listener = get_launch_activate_tcp_listener(&launchd_socket_name)?; + let tokio_listener = TokioTcpListener::from_std(std_listener)?; + TcpListener::from_listener(tokio_listener, self.context.accept_opts())? + } else { + create_standard_tcp_listener(&self.context, &self.bind_addr).await? + }; + } else { + let listener = create_standard_tcp_listener(&self.context, &self.bind_addr).await?; + } + } + + Ok(DnsTcpServer { + listener, + local_addr: self.local_addr, + remote_addr: self.remote_addr, + client: self.client, + }) + } +} + /// DNS TCP server instance pub struct DnsTcpServer { listener: TcpListener, @@ -145,31 +245,6 @@ pub struct DnsTcpServer { } impl DnsTcpServer { - async fn new( - context: Arc, - bind_addr: &ServerAddr, - local_addr: Arc, - remote_addr: Arc
, - client: Arc, - ) -> io::Result { - let listener = match *bind_addr { - ServerAddr::SocketAddr(ref saddr) => TcpListener::bind_with_opts(saddr, context.accept_opts()).await?, - ServerAddr::DomainName(ref dname, port) => { - lookup_then!(context.context_ref(), dname, port, |addr| { - TcpListener::bind_with_opts(&addr, context.accept_opts()).await - })? - .1 - } - }; - - Ok(DnsTcpServer { - listener, - local_addr, - remote_addr, - client, - }) - } - /// Get server local address pub fn local_addr(&self) -> io::Result { self.listener.local_addr() @@ -272,6 +347,67 @@ impl DnsTcpServer { } } +struct DnsUdpServerBuilder { + context: Arc, + bind_addr: ServerAddr, + local_addr: Arc, + remote_addr: Arc
, + client: Arc, + #[cfg(target_os = "macos")] + launchd_socket_name: Option, +} + +impl DnsUdpServerBuilder { + fn new( + context: Arc, + bind_addr: ServerAddr, + local_addr: Arc, + remote_addr: Arc
, + client: Arc, + ) -> DnsUdpServerBuilder { + DnsUdpServerBuilder { + context, + bind_addr, + local_addr, + remote_addr, + client, + #[cfg(target_os = "macos")] + launchd_socket_name: None, + } + } + + /// macOS launchd activate socket + #[cfg(target_os = "macos")] + fn set_launchd_socket_name(&mut self, n: String) { + self.launchd_socket_name = Some(n); + } + + async fn build(self) -> io::Result { + cfg_if::cfg_if! { + if #[cfg(target_os = "macos")] { + let socket = if let Some(launchd_socket_name) = self.launchd_socket_name { + use tokio::net::UdpSocket as TokioUdpSocket; + use crate::net::launch_activate_socket::get_launch_activate_udp_socket; + + let std_socket = get_launch_activate_udp_socket(&launchd_socket_name)?; + TokioUdpSocket::from_std(std_socket)? + } else { + create_standard_udp_listener(&self.context, &self.bind_addr).await?.into() + }; + } else { + let socket = create_standard_udp_listener(&self.context, &self.bind_addr).await?.into(); + } + } + + Ok(DnsUdpServer { + listener: Arc::new(socket), + local_addr: self.local_addr, + remote_addr: self.remote_addr, + client: self.client, + }) + } +} + /// DNS UDP server instance pub struct DnsUdpServer { listener: Arc, @@ -281,34 +417,6 @@ pub struct DnsUdpServer { } impl DnsUdpServer { - async fn new( - context: Arc, - bind_addr: &ServerAddr, - local_addr: Arc, - remote_addr: Arc
, - client: Arc, - ) -> io::Result { - let socket = match *bind_addr { - ServerAddr::SocketAddr(ref saddr) => ShadowUdpSocket::listen(saddr).await?, - ServerAddr::DomainName(ref dname, port) => { - lookup_then!(context.context_ref(), dname, port, |addr| { - ShadowUdpSocket::listen(&addr).await - })? - .1 - } - }; - let socket: UdpSocket = socket.into(); - - let listener = Arc::new(socket); - - Ok(DnsUdpServer { - listener, - local_addr, - remote_addr, - client, - }) - } - /// Get server local address pub fn local_addr(&self) -> io::Result { self.listener.local_addr() diff --git a/crates/shadowsocks-service/src/local/http/server.rs b/crates/shadowsocks-service/src/local/http/server.rs index acca9724..bf975609 100644 --- a/crates/shadowsocks-service/src/local/http/server.rs +++ b/crates/shadowsocks-service/src/local/http/server.rs @@ -16,12 +16,13 @@ use hyper::{ Server, }; use log::{error, info}; -use shadowsocks::{config::ServerAddr, lookup_then, net::TcpListener}; +use shadowsocks::{config::ServerAddr, net::TcpListener}; use crate::local::{ context::ServiceContext, http::connector::Connector, loadbalancing::PingBalancer, + net::tcp::listener::create_standard_tcp_listener, LOCAL_DEFAULT_KEEPALIVE_TIMEOUT, }; @@ -32,6 +33,8 @@ pub struct HttpBuilder { context: Arc, client_config: ServerAddr, balancer: PingBalancer, + #[cfg(target_os = "macos")] + launchd_tcp_socket_name: Option, } impl HttpBuilder { @@ -51,18 +54,34 @@ impl HttpBuilder { context, client_config, balancer, + #[cfg(target_os = "macos")] + launchd_tcp_socket_name: None, } } + #[cfg(target_os = "macos")] + pub fn set_launchd_tcp_socket_name(&mut self, n: String) { + self.launchd_tcp_socket_name = Some(n); + } + /// Build HTTP server instance pub async fn build(self) -> io::Result { - let listener = match self.client_config { - ServerAddr::SocketAddr(sa) => TcpListener::bind_with_opts(&sa, self.context.accept_opts().clone()).await, - ServerAddr::DomainName(ref dname, port) => lookup_then!(self.context.context_ref(), dname, port, |addr| { - TcpListener::bind_with_opts(&addr, self.context.accept_opts().clone()).await - }) - .map(|(_, b)| b), - }?; + cfg_if::cfg_if! { + if #[cfg(target_os = "macos")] { + let listener = if let Some(launchd_socket_name) = self.launchd_tcp_socket_name { + use tokio::net::TcpListener as TokioTcpListener; + use crate::net::launch_activate_socket::get_launch_activate_tcp_listener; + + let std_listener = get_launch_activate_tcp_listener(&launchd_socket_name)?; + let tokio_listener = TokioTcpListener::from_std(std_listener)?; + TcpListener::from_listener(tokio_listener, self.context.accept_opts())? + } else { + create_standard_tcp_listener(&self.context, &self.client_config).await? + }; + } else { + let listener = create_standard_tcp_listener(&self.context, &self.client_config).await?; + } + } let proxy_client_cache = Arc::new(ProxyClientCache::new(self.context.clone())); diff --git a/crates/shadowsocks-service/src/local/mod.rs b/crates/shadowsocks-service/src/local/mod.rs index e098e471..f5b5c1e0 100644 --- a/crates/shadowsocks-service/src/local/mod.rs +++ b/crates/shadowsocks-service/src/local/mod.rs @@ -279,6 +279,15 @@ impl Server { server_builder.set_udp_bind_addr(b.clone()); } + #[cfg(target_os = "macos")] + if let Some(n) = local_config.launchd_tcp_socket_name { + server_builder.set_launchd_tcp_socket_name(n); + } + #[cfg(target_os = "macos")] + if let Some(n) = local_config.launchd_udp_socket_name { + server_builder.set_launchd_udp_socket_name(n); + } + let server = server_builder.build().await?; local_server.socks_servers.push(server); } @@ -305,6 +314,15 @@ impl Server { server_builder.set_udp_bind_addr(udp_addr); } + #[cfg(target_os = "macos")] + if let Some(n) = local_config.launchd_tcp_socket_name { + server_builder.set_launchd_tcp_socket_name(n); + } + #[cfg(target_os = "macos")] + if let Some(n) = local_config.launchd_udp_socket_name { + server_builder.set_launchd_udp_socket_name(n); + } + let server = server_builder.build().await?; local_server.tunnel_servers.push(server); } @@ -315,7 +333,14 @@ impl Server { None => return Err(io::Error::new(ErrorKind::Other, "http requires local address")), }; - let builder = HttpBuilder::with_context(context.clone(), client_addr, balancer); + #[allow(unused_mut)] + let mut builder = HttpBuilder::with_context(context.clone(), client_addr, balancer); + + #[cfg(target_os = "macos")] + if let Some(n) = local_config.launchd_tcp_socket_name { + builder.set_launchd_tcp_socket_name(n); + } + let server = builder.build().await?; local_server.http_servers.push(server); } @@ -366,6 +391,15 @@ impl Server { }; server_builder.set_mode(local_config.mode); + #[cfg(target_os = "macos")] + if let Some(n) = local_config.launchd_tcp_socket_name { + server_builder.set_launchd_tcp_socket_name(n); + } + #[cfg(target_os = "macos")] + if let Some(n) = local_config.launchd_udp_socket_name { + server_builder.set_launchd_udp_socket_name(n); + } + let server = server_builder.build().await?; local_server.dns_servers.push(server); } diff --git a/crates/shadowsocks-service/src/local/net/mod.rs b/crates/shadowsocks-service/src/local/net/mod.rs index ae2743b8..0426432b 100644 --- a/crates/shadowsocks-service/src/local/net/mod.rs +++ b/crates/shadowsocks-service/src/local/net/mod.rs @@ -5,5 +5,5 @@ pub use self::{ udp::{UdpAssociationManager, UdpInboundWrite}, }; -mod tcp; +pub(crate) mod tcp; pub(crate) mod udp; diff --git a/crates/shadowsocks-service/src/local/net/tcp/listener.rs b/crates/shadowsocks-service/src/local/net/tcp/listener.rs new file mode 100644 index 00000000..57c94ed6 --- /dev/null +++ b/crates/shadowsocks-service/src/local/net/tcp/listener.rs @@ -0,0 +1,21 @@ +//! Local instance listener helpers + +use std::io; + +use shadowsocks::{config::ServerAddr, lookup_then, net::TcpListener}; + +use crate::local::context::ServiceContext; + +/// Create a standard TCP listener listening on `client_config` +pub async fn create_standard_tcp_listener( + context: &ServiceContext, + client_config: &ServerAddr, +) -> io::Result { + match client_config { + ServerAddr::SocketAddr(saddr) => TcpListener::bind_with_opts(saddr, context.accept_opts()).await, + ServerAddr::DomainName(dname, port) => lookup_then!(context.context_ref(), dname, *port, |addr| { + TcpListener::bind_with_opts(&addr, context.accept_opts()).await + }) + .map(|(_, l)| l), + } +} diff --git a/crates/shadowsocks-service/src/local/net/tcp/mod.rs b/crates/shadowsocks-service/src/local/net/tcp/mod.rs index b2ecb9ec..54561e4f 100644 --- a/crates/shadowsocks-service/src/local/net/tcp/mod.rs +++ b/crates/shadowsocks-service/src/local/net/tcp/mod.rs @@ -1,2 +1,3 @@ pub mod auto_proxy_io; pub mod auto_proxy_stream; +pub mod listener; diff --git a/crates/shadowsocks-service/src/local/net/udp/listener.rs b/crates/shadowsocks-service/src/local/net/udp/listener.rs new file mode 100644 index 00000000..17f283c7 --- /dev/null +++ b/crates/shadowsocks-service/src/local/net/udp/listener.rs @@ -0,0 +1,21 @@ +//! Local instance listener helpers + +use std::io; + +use shadowsocks::{config::ServerAddr, lookup_then, net::UdpSocket}; + +use crate::local::context::ServiceContext; + +/// Create a standard UDP listener listening on `client_config` +pub async fn create_standard_udp_listener( + context: &ServiceContext, + client_config: &ServerAddr, +) -> io::Result { + match client_config { + ServerAddr::SocketAddr(saddr) => UdpSocket::listen_with_opts(saddr, context.accept_opts()).await, + ServerAddr::DomainName(dname, port) => lookup_then!(context.context_ref(), dname, *port, |addr| { + UdpSocket::listen_with_opts(&addr, context.accept_opts()).await + }) + .map(|(_, s)| s), + } +} diff --git a/crates/shadowsocks-service/src/local/net/udp/mod.rs b/crates/shadowsocks-service/src/local/net/udp/mod.rs index 1cd5d91e..20371dec 100644 --- a/crates/shadowsocks-service/src/local/net/udp/mod.rs +++ b/crates/shadowsocks-service/src/local/net/udp/mod.rs @@ -1,3 +1,4 @@ pub use self::association::{UdpAssociationManager, UdpInboundWrite}; pub mod association; +pub mod listener; diff --git a/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs b/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs index 29324c69..7c1f2aef 100644 --- a/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs +++ b/crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs @@ -112,7 +112,7 @@ impl RedirTcpServer { /// Start serving pub async fn run(self) -> io::Result<()> { - let listener = ShadowTcpListener::from_listener(self.listener, self.context.accept_opts()); + let listener = ShadowTcpListener::from_listener(self.listener, self.context.accept_opts())?; let actual_local_addr = listener.local_addr().expect("determine port bound to"); diff --git a/crates/shadowsocks-service/src/local/socks/server/mod.rs b/crates/shadowsocks-service/src/local/socks/server/mod.rs index 1ccfd3b0..edbb5cf8 100644 --- a/crates/shadowsocks-service/src/local/socks/server/mod.rs +++ b/crates/shadowsocks-service/src/local/socks/server/mod.rs @@ -7,8 +7,8 @@ use shadowsocks::{config::Mode, ServerAddr}; use crate::local::{context::ServiceContext, loadbalancing::PingBalancer}; -pub use self::server::{SocksTcpServer, SocksUdpServer}; -use self::socks5::Socks5UdpServer; +pub use self::server::{SocksTcpServer, SocksTcpServerBuilder, SocksUdpServer}; +use self::socks5::Socks5UdpServerBuilder; use super::config::Socks5AuthConfig; @@ -28,6 +28,10 @@ pub struct SocksBuilder { socks5_auth: Socks5AuthConfig, client_config: ServerAddr, balancer: PingBalancer, + #[cfg(target_os = "macos")] + launchd_tcp_socket_name: Option, + #[cfg(target_os = "macos")] + launchd_udp_socket_name: Option, } impl SocksBuilder { @@ -52,6 +56,10 @@ impl SocksBuilder { socks5_auth: Socks5AuthConfig::default(), client_config, balancer, + #[cfg(target_os = "macos")] + launchd_tcp_socket_name: None, + #[cfg(target_os = "macos")] + launchd_udp_socket_name: None, } } @@ -83,33 +91,59 @@ impl SocksBuilder { self.socks5_auth = p; } + /// macOS launchd activate socket + #[cfg(target_os = "macos")] + pub fn set_launchd_tcp_socket_name(&mut self, n: String) { + self.launchd_tcp_socket_name = Some(n); + } + + /// macOS launchd activate socket + #[cfg(target_os = "macos")] + pub fn set_launchd_udp_socket_name(&mut self, n: String) { + self.launchd_udp_socket_name = Some(n); + } + pub async fn build(self) -> io::Result { let udp_bind_addr = self.udp_bind_addr.unwrap_or_else(|| self.client_config.clone()); let mut udp_server = None; if self.mode.enable_udp() { - let server = Socks5UdpServer::new( + #[allow(unused_mut)] + let mut builder = Socks5UdpServerBuilder::new( self.context.clone(), - &udp_bind_addr, + udp_bind_addr.clone(), self.udp_expiry_duration, self.udp_capacity, self.balancer.clone(), - ) - .await?; + ); + + #[cfg(target_os = "macos")] + if let Some(s) = self.launchd_udp_socket_name { + builder.set_launchd_socket_name(s); + } + + let server = builder.build().await?; udp_server = Some(server); } let mut tcp_server = None; if self.mode.enable_tcp() { - let server = SocksTcpServer::new( + #[allow(unused_mut)] + let mut builder = SocksTcpServerBuilder::new( self.context.clone(), self.client_config, udp_bind_addr, self.balancer.clone(), self.mode, self.socks5_auth, - ) - .await?; + ); + + #[cfg(target_os = "macos")] + if let Some(s) = self.launchd_tcp_socket_name { + builder.set_launchd_socket_name(s); + } + + let server = builder.build().await?; tcp_server = Some(server); } diff --git a/crates/shadowsocks-service/src/local/socks/server/server.rs b/crates/shadowsocks-service/src/local/socks/server/server.rs index f7bbacea..70614fa8 100644 --- a/crates/shadowsocks-service/src/local/socks/server/server.rs +++ b/crates/shadowsocks-service/src/local/socks/server/server.rs @@ -1,16 +1,86 @@ use std::{io, net::SocketAddr, sync::Arc, time::Duration}; use log::{error, info}; -use shadowsocks::{config::Mode, lookup_then, net::TcpListener as ShadowTcpListener, ServerAddr}; +use shadowsocks::{config::Mode, net::TcpListener as ShadowTcpListener, ServerAddr}; use tokio::{net::TcpStream, time}; -use crate::local::{context::ServiceContext, loadbalancing::PingBalancer}; +use crate::local::{ + context::ServiceContext, + loadbalancing::PingBalancer, + net::tcp::listener::create_standard_tcp_listener, + socks::config::Socks5AuthConfig, +}; #[cfg(feature = "local-socks4")] use super::socks4::Socks4TcpHandler; use super::socks5::{Socks5TcpHandler, Socks5UdpServer}; -use crate::local::socks::config::Socks5AuthConfig; +pub struct SocksTcpServerBuilder { + context: Arc, + client_config: ServerAddr, + udp_bind_addr: ServerAddr, + balancer: PingBalancer, + mode: Mode, + socks5_auth: Arc, + #[cfg(target_os = "macos")] + launchd_socket_name: Option, +} + +impl SocksTcpServerBuilder { + pub(crate) fn new( + context: Arc, + client_config: ServerAddr, + udp_bind_addr: ServerAddr, + balancer: PingBalancer, + mode: Mode, + socks5_auth: Socks5AuthConfig, + ) -> SocksTcpServerBuilder { + SocksTcpServerBuilder { + context, + client_config, + udp_bind_addr, + balancer, + mode, + socks5_auth: Arc::new(socks5_auth), + #[cfg(target_os = "macos")] + launchd_socket_name: None, + } + } + + /// macOS launchd activate socket + #[cfg(target_os = "macos")] + pub fn set_launchd_socket_name(&mut self, n: String) { + self.launchd_socket_name = Some(n); + } + + pub async fn build(self) -> io::Result { + cfg_if::cfg_if! { + if #[cfg(target_os = "macos")] { + let listener = if let Some(launchd_socket_name) = self.launchd_socket_name { + use tokio::net::TcpListener as TokioTcpListener; + use crate::net::launch_activate_socket::get_launch_activate_tcp_listener; + + let std_listener = get_launch_activate_tcp_listener(&launchd_socket_name)?; + let tokio_listener = TokioTcpListener::from_std(std_listener)?; + ShadowTcpListener::from_listener(tokio_listener, self.context.accept_opts())? + } else { + create_standard_tcp_listener(&self.context, &self.client_config).await? + }; + } else { + let listener = create_standard_tcp_listener(&self.context, &self.client_config).await?; + } + } + + Ok(SocksTcpServer { + context: self.context, + listener, + udp_bind_addr: self.udp_bind_addr, + balancer: self.balancer, + mode: self.mode, + socks5_auth: self.socks5_auth, + }) + } +} /// SOCKS TCP server instance pub struct SocksTcpServer { @@ -23,35 +93,6 @@ pub struct SocksTcpServer { } impl SocksTcpServer { - pub(crate) async fn new( - context: Arc, - client_config: ServerAddr, - udp_bind_addr: ServerAddr, - balancer: PingBalancer, - mode: Mode, - socks5_auth: Socks5AuthConfig, - ) -> io::Result { - let listener = match client_config { - ServerAddr::SocketAddr(ref saddr) => { - ShadowTcpListener::bind_with_opts(saddr, context.accept_opts()).await? - } - ServerAddr::DomainName(ref dname, port) => { - lookup_then!(context.context_ref(), dname, port, |addr| { - ShadowTcpListener::bind_with_opts(&addr, context.accept_opts()).await - })? - .1 - } - }; - Ok(SocksTcpServer { - context, - listener, - udp_bind_addr, - balancer, - mode, - socks5_auth: Arc::new(socks5_auth), - }) - } - /// Get TCP server local addr pub fn local_addr(&self) -> io::Result { self.listener.local_addr() diff --git a/crates/shadowsocks-service/src/local/socks/server/socks5/mod.rs b/crates/shadowsocks-service/src/local/socks/server/socks5/mod.rs index c2138b5c..05525f18 100644 --- a/crates/shadowsocks-service/src/local/socks/server/socks5/mod.rs +++ b/crates/shadowsocks-service/src/local/socks/server/socks5/mod.rs @@ -1,6 +1,9 @@ //! SOCKS5 Local Server -pub use self::{tcprelay::Socks5TcpHandler, udprelay::Socks5UdpServer}; +pub use self::{ + tcprelay::Socks5TcpHandler, + udprelay::{Socks5UdpServer, Socks5UdpServerBuilder}, +}; mod tcprelay; mod udprelay; diff --git a/crates/shadowsocks-service/src/local/socks/server/socks5/udprelay.rs b/crates/shadowsocks-service/src/local/socks/server/socks5/udprelay.rs index 16948ebe..4c313d08 100644 --- a/crates/shadowsocks-service/src/local/socks/server/socks5/udprelay.rs +++ b/crates/shadowsocks-service/src/local/socks/server/socks5/udprelay.rs @@ -12,8 +12,6 @@ use byte_string::ByteStr; use bytes::{BufMut, BytesMut}; use log::{debug, error, info, trace}; use shadowsocks::{ - lookup_then, - net::UdpSocket as ShadowUdpSocket, relay::{ socks5::{Address, UdpAssociateHeader}, udprelay::MAXIMUM_UDP_PAYLOAD_SIZE, @@ -26,11 +24,73 @@ use crate::{ local::{ context::ServiceContext, loadbalancing::PingBalancer, - net::{UdpAssociationManager, UdpInboundWrite}, + net::{udp::listener::create_standard_udp_listener, UdpAssociationManager, UdpInboundWrite}, }, net::utils::to_ipv4_mapped, }; +pub struct Socks5UdpServerBuilder { + context: Arc, + client_config: ServerAddr, + time_to_live: Option, + capacity: Option, + balancer: PingBalancer, + #[cfg(target_os = "macos")] + launchd_socket_name: Option, +} + +impl Socks5UdpServerBuilder { + pub(crate) fn new( + context: Arc, + client_config: ServerAddr, + time_to_live: Option, + capacity: Option, + balancer: PingBalancer, + ) -> Socks5UdpServerBuilder { + Socks5UdpServerBuilder { + context, + client_config, + time_to_live, + capacity, + balancer, + #[cfg(target_os = "macos")] + launchd_socket_name: None, + } + } + + /// macOS launchd activate socket + #[cfg(target_os = "macos")] + pub fn set_launchd_socket_name(&mut self, n: String) { + self.launchd_socket_name = Some(n); + } + + pub async fn build(self) -> io::Result { + cfg_if::cfg_if! { + if #[cfg(target_os = "macos")] { + let socket = if let Some(launchd_socket_name) = self.launchd_socket_name { + use tokio::net::UdpSocket as TokioUdpSocket; + use crate::net::launch_activate_socket::get_launch_activate_udp_socket; + + let std_socket = get_launch_activate_udp_socket(&launchd_socket_name)?; + TokioUdpSocket::from_std(std_socket)? + } else { + create_standard_udp_listener(&self.context, &self.client_config).await?.into() + }; + } else { + let socket = create_standard_udp_listener(&self.context, &self.client_config).await?.into(); + } + } + + Ok(Socks5UdpServer { + context: self.context, + time_to_live: self.time_to_live, + capacity: self.capacity, + listener: Arc::new(socket), + balancer: self.balancer, + }) + } +} + #[derive(Clone)] struct Socks5UdpInboundWriter { inbound: Arc, @@ -77,36 +137,6 @@ pub struct Socks5UdpServer { } impl Socks5UdpServer { - pub(crate) async fn new( - context: Arc, - client_config: &ServerAddr, - time_to_live: Option, - capacity: Option, - balancer: PingBalancer, - ) -> io::Result { - let socket = match *client_config { - ServerAddr::SocketAddr(ref saddr) => { - ShadowUdpSocket::listen_with_opts(saddr, context.accept_opts()).await? - } - ServerAddr::DomainName(ref dname, port) => { - lookup_then!(context.context_ref(), dname, port, |addr| { - ShadowUdpSocket::listen_with_opts(&addr, context.accept_opts()).await - })? - .1 - } - }; - let socket: UdpSocket = socket.into(); - let listener = Arc::new(socket); - - Ok(Socks5UdpServer { - context, - time_to_live, - capacity, - listener, - balancer, - }) - } - /// Server's listen address pub fn local_addr(&self) -> io::Result { self.listener.local_addr() diff --git a/crates/shadowsocks-service/src/local/tunnel/server.rs b/crates/shadowsocks-service/src/local/tunnel/server.rs index a63baf44..a20487b8 100644 --- a/crates/shadowsocks-service/src/local/tunnel/server.rs +++ b/crates/shadowsocks-service/src/local/tunnel/server.rs @@ -7,7 +7,10 @@ use shadowsocks::{config::Mode, relay::socks5::Address, ServerAddr}; use crate::local::{context::ServiceContext, loadbalancing::PingBalancer}; -use super::{tcprelay::TunnelTcpServer, udprelay::TunnelUdpServer}; +use super::{ + tcprelay::{TunnelTcpServer, TunnelTcpServerBuilder}, + udprelay::{TunnelUdpServer, TunnelUdpServerBuilder}, +}; pub struct TunnelBuilder { context: Arc, @@ -18,6 +21,10 @@ pub struct TunnelBuilder { client_addr: ServerAddr, udp_addr: Option, balancer: PingBalancer, + #[cfg(target_os = "macos")] + launchd_tcp_socket_name: Option, + #[cfg(target_os = "macos")] + launchd_udp_socket_name: Option, } impl TunnelBuilder { @@ -43,6 +50,10 @@ impl TunnelBuilder { client_addr, udp_addr: None, balancer, + #[cfg(target_os = "macos")] + launchd_tcp_socket_name: None, + #[cfg(target_os = "macos")] + launchd_udp_socket_name: None, } } @@ -66,32 +77,58 @@ impl TunnelBuilder { self.udp_addr = Some(addr); } + /// macOS launchd activate socket + #[cfg(target_os = "macos")] + pub fn set_launchd_tcp_socket_name(&mut self, n: String) { + self.launchd_tcp_socket_name = Some(n); + } + + /// macOS launchd activate socket + #[cfg(target_os = "macos")] + pub fn set_launchd_udp_socket_name(&mut self, n: String) { + self.launchd_udp_socket_name = Some(n); + } + pub async fn build(self) -> io::Result { let mut tcp_server = None; if self.mode.enable_tcp() { - let server = TunnelTcpServer::new( + #[allow(unused_mut)] + let mut builder = TunnelTcpServerBuilder::new( self.context.clone(), - &self.client_addr, + self.client_addr.clone(), self.balancer.clone(), self.forward_addr.clone(), - ) - .await?; + ); + + #[cfg(target_os = "macos")] + if let Some(s) = self.launchd_tcp_socket_name { + builder.set_launchd_socket_name(s); + } + + let server = builder.build().await?; tcp_server = Some(server); } let mut udp_server = None; if self.mode.enable_udp() { - let udp_addr = self.udp_addr.as_ref().unwrap_or(&self.client_addr); + let udp_addr = self.udp_addr.unwrap_or(self.client_addr); - let server = TunnelUdpServer::new( + #[allow(unused_mut)] + let mut builder = TunnelUdpServerBuilder::new( self.context.clone(), udp_addr, self.udp_expiry_duration, self.udp_capacity, self.balancer, self.forward_addr, - ) - .await?; + ); + + #[cfg(target_os = "macos")] + if let Some(s) = self.launchd_udp_socket_name { + builder.set_launchd_socket_name(s); + } + + let server = builder.build().await?; udp_server = Some(server); } diff --git a/crates/shadowsocks-service/src/local/tunnel/tcprelay.rs b/crates/shadowsocks-service/src/local/tunnel/tcprelay.rs index 24168702..43bc8645 100644 --- a/crates/shadowsocks-service/src/local/tunnel/tcprelay.rs +++ b/crates/shadowsocks-service/src/local/tunnel/tcprelay.rs @@ -3,16 +3,75 @@ use std::{io, net::SocketAddr, sync::Arc, time::Duration}; use log::{error, info, trace}; -use shadowsocks::{lookup_then, net::TcpListener as ShadowTcpListener, relay::socks5::Address, ServerAddr}; +use shadowsocks::{net::TcpListener as ShadowTcpListener, relay::socks5::Address, ServerAddr}; use tokio::{net::TcpStream, time}; use crate::local::{ context::ServiceContext, loadbalancing::PingBalancer, - net::AutoProxyClientStream, + net::{tcp::listener::create_standard_tcp_listener, AutoProxyClientStream}, utils::{establish_tcp_tunnel, establish_tcp_tunnel_bypassed}, }; +pub struct TunnelTcpServerBuilder { + context: Arc, + client_config: ServerAddr, + balancer: PingBalancer, + forward_addr: Address, + #[cfg(target_os = "macos")] + launchd_socket_name: Option, +} + +impl TunnelTcpServerBuilder { + pub(crate) fn new( + context: Arc, + client_config: ServerAddr, + balancer: PingBalancer, + forward_addr: Address, + ) -> TunnelTcpServerBuilder { + TunnelTcpServerBuilder { + context, + client_config, + balancer, + forward_addr, + #[cfg(target_os = "macos")] + launchd_socket_name: None, + } + } + + /// macOS launchd activate socket + #[cfg(target_os = "macos")] + pub fn set_launchd_socket_name(&mut self, n: String) { + self.launchd_socket_name = Some(n); + } + + pub async fn build(self) -> io::Result { + cfg_if::cfg_if! { + if #[cfg(target_os = "macos")] { + let listener = if let Some(launchd_socket_name) = self.launchd_socket_name { + use tokio::net::TcpListener as TokioTcpListener; + use crate::net::launch_activate_socket::get_launch_activate_tcp_listener; + + let std_listener = get_launch_activate_tcp_listener(&launchd_socket_name)?; + let tokio_listener = TokioTcpListener::from_std(std_listener)?; + ShadowTcpListener::from_listener(tokio_listener, self.context.accept_opts())? + } else { + create_standard_tcp_listener(&self.context, &self.client_config).await? + }; + } else { + let listener = create_standard_tcp_listener(&self.context, &self.client_config).await?; + } + } + + Ok(TunnelTcpServer { + context: self.context, + listener, + balancer: self.balancer, + forward_addr: self.forward_addr, + }) + } +} + /// TCP Tunnel instance pub struct TunnelTcpServer { context: Arc, @@ -22,32 +81,6 @@ pub struct TunnelTcpServer { } impl TunnelTcpServer { - pub(crate) async fn new( - context: Arc, - client_config: &ServerAddr, - balancer: PingBalancer, - forward_addr: Address, - ) -> io::Result { - let listener = match *client_config { - ServerAddr::SocketAddr(ref saddr) => { - ShadowTcpListener::bind_with_opts(saddr, context.accept_opts()).await? - } - ServerAddr::DomainName(ref dname, port) => { - lookup_then!(context.context_ref(), dname, port, |addr| { - ShadowTcpListener::bind_with_opts(&addr, context.accept_opts()).await - })? - .1 - } - }; - - Ok(TunnelTcpServer { - context, - listener, - balancer, - forward_addr, - }) - } - /// Server's local address pub fn local_addr(&self) -> io::Result { self.listener.local_addr() diff --git a/crates/shadowsocks-service/src/local/tunnel/udprelay.rs b/crates/shadowsocks-service/src/local/tunnel/udprelay.rs index bdcd4d52..4bace85f 100644 --- a/crates/shadowsocks-service/src/local/tunnel/udprelay.rs +++ b/crates/shadowsocks-service/src/local/tunnel/udprelay.rs @@ -5,8 +5,6 @@ use std::{io, net::SocketAddr, sync::Arc, time::Duration}; use async_trait::async_trait; use log::{debug, error, info}; use shadowsocks::{ - lookup_then, - net::UdpSocket as ShadowUdpSocket, relay::{socks5::Address, udprelay::MAXIMUM_UDP_PAYLOAD_SIZE}, ServerAddr, }; @@ -15,9 +13,75 @@ use tokio::{net::UdpSocket, time}; use crate::local::{ context::ServiceContext, loadbalancing::PingBalancer, - net::{UdpAssociationManager, UdpInboundWrite}, + net::{udp::listener::create_standard_udp_listener, UdpAssociationManager, UdpInboundWrite}, }; +pub struct TunnelUdpServerBuilder { + context: Arc, + client_config: ServerAddr, + time_to_live: Option, + capacity: Option, + balancer: PingBalancer, + forward_addr: Address, + #[cfg(target_os = "macos")] + launchd_socket_name: Option, +} + +impl TunnelUdpServerBuilder { + pub(crate) fn new( + context: Arc, + client_config: ServerAddr, + time_to_live: Option, + capacity: Option, + balancer: PingBalancer, + forward_addr: Address, + ) -> TunnelUdpServerBuilder { + TunnelUdpServerBuilder { + context, + client_config, + time_to_live, + capacity, + balancer, + forward_addr, + #[cfg(target_os = "macos")] + launchd_socket_name: None, + } + } + + /// macOS launchd activate socket + #[cfg(target_os = "macos")] + pub fn set_launchd_socket_name(&mut self, n: String) { + self.launchd_socket_name = Some(n); + } + + pub async fn build(self) -> io::Result { + cfg_if::cfg_if! { + if #[cfg(target_os = "macos")] { + let socket = if let Some(launchd_socket_name) = self.launchd_socket_name { + use tokio::net::UdpSocket as TokioUdpSocket; + use crate::net::launch_activate_socket::get_launch_activate_udp_socket; + + let std_socket = get_launch_activate_udp_socket(&launchd_socket_name)?; + TokioUdpSocket::from_std(std_socket)? + } else { + create_standard_udp_listener(&self.context, &self.client_config).await?.into() + }; + } else { + let socket = create_standard_udp_listener(&self.context, &self.client_config).await?.into(); + } + } + + Ok(TunnelUdpServer { + context: self.context, + time_to_live: self.time_to_live, + capacity: self.capacity, + listener: Arc::new(socket), + balancer: self.balancer, + forward_addr: self.forward_addr, + }) + } +} + #[derive(Clone)] struct TunnelUdpInboundWriter { inbound: Arc, @@ -40,38 +104,6 @@ pub struct TunnelUdpServer { } impl TunnelUdpServer { - pub(crate) async fn new( - context: Arc, - client_config: &ServerAddr, - time_to_live: Option, - capacity: Option, - balancer: PingBalancer, - forward_addr: Address, - ) -> io::Result { - let socket = match client_config { - ServerAddr::SocketAddr(ref saddr) => { - ShadowUdpSocket::listen_with_opts(saddr, context.accept_opts()).await? - } - ServerAddr::DomainName(ref dname, port) => { - lookup_then!(context.context_ref(), dname, *port, |addr| { - ShadowUdpSocket::listen_with_opts(&addr, context.accept_opts()).await - })? - .1 - } - }; - let socket: UdpSocket = socket.into(); - let listener = Arc::new(socket); - - Ok(TunnelUdpServer { - context, - time_to_live, - capacity, - listener, - balancer, - forward_addr, - }) - } - /// Get server's local address pub fn local_addr(&self) -> io::Result { self.listener.local_addr() diff --git a/crates/shadowsocks-service/src/net/launch_activate_socket.rs b/crates/shadowsocks-service/src/net/launch_activate_socket.rs new file mode 100644 index 00000000..85cafc0b --- /dev/null +++ b/crates/shadowsocks-service/src/net/launch_activate_socket.rs @@ -0,0 +1,28 @@ +//! macOS launch activate socket +//! +//! +//! + +use std::{ + io, + net::{TcpListener, UdpSocket}, + os::unix::io::FromRawFd, +}; + +use log::debug; + +use crate::sys::get_launch_activate_socket; + +/// Get a macOS launch active socket as a `TcpListener` +pub fn get_launch_activate_tcp_listener(name: &str) -> io::Result { + let fd = get_launch_activate_socket(name)?; + debug!("created TCP listener from launch activate socket {}", fd); + Ok(unsafe { TcpListener::from_raw_fd(fd) }) +} + +/// Get a macOS launch activate socket as a `UdpSocket` +pub fn get_launch_activate_udp_socket(name: &str) -> io::Result { + let fd = get_launch_activate_socket(name)?; + debug!("created UDP socket from launch activate socket {}", fd); + Ok(unsafe { UdpSocket::from_raw_fd(fd) }) +} diff --git a/crates/shadowsocks-service/src/net/mod.rs b/crates/shadowsocks-service/src/net/mod.rs index 648aa56a..44ea46cc 100644 --- a/crates/shadowsocks-service/src/net/mod.rs +++ b/crates/shadowsocks-service/src/net/mod.rs @@ -3,6 +3,8 @@ pub use self::{flow::FlowStat, mon_socket::MonProxySocket, mon_stream::MonProxyStream}; pub mod flow; +#[cfg(target_os = "macos")] +pub mod launch_activate_socket; pub mod mon_socket; pub mod mon_stream; pub mod packet_window; diff --git a/crates/shadowsocks-service/src/sys/unix/macos.rs b/crates/shadowsocks-service/src/sys/unix/macos.rs new file mode 100644 index 00000000..0e887d65 --- /dev/null +++ b/crates/shadowsocks-service/src/sys/unix/macos.rs @@ -0,0 +1,85 @@ +//! macOS specific APIs + +use std::{ffi::CString, io, os::fd::RawFd, ptr}; + +use log::error; + +extern "C" { + /// https://developer.apple.com/documentation/xpc/1505523-launch_activate_socket + fn launch_activate_socket( + name: *const libc::c_char, + fds: *mut *mut libc::c_int, + cnt: *mut libc::size_t, + ) -> libc::c_int; +} + +pub fn get_launch_activate_socket(name: &str) -> io::Result { + let mut fds: *mut libc::c_int = ptr::null_mut(); + let mut cnt: libc::size_t = 0; + + let cname = match CString::new(name) { + Ok(n) => n, + Err(..) => { + return Err(io::Error::new( + io::ErrorKind::Other, + format!("activate socket name \"{}\" contains NUL bytes", name), + )); + } + }; + + unsafe { + let ret = launch_activate_socket(cname.as_ptr(), &mut fds as *mut _, &mut cnt as *mut _); + if ret != 0 { + let err = io::Error::last_os_error(); + match err.raw_os_error() { + Some(libc::ENOENT) => { + error!("activate socket name \"{}\" doesn't exist, error: {}", name, err); + } + Some(libc::ESRCH) => { + error!("current process is not managed by launchd, error: {}", err); + } + Some(libc::EALREADY) => { + error!( + "activate socket name \"{}\" has already been activated, error: {}", + name, err + ); + } + _ => {} + } + + return Err(err); + } + } + + let result = if cnt == 0 { + Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("launch socket with name \"{}\" doesn't exist", name), + )) + } else if cnt > 1 { + for idx in 0..cnt { + unsafe { + let fd = *(fds.offset(idx as isize)); + let _ = libc::close(fd); + } + } + + Err(io::Error::new( + io::ErrorKind::InvalidData, + format!( + "launch socket with name \"{}\" should be unique, but found {}", + name, cnt + ), + )) + } else { + // Take fds[0] as the result + let fd = unsafe { *fds }; + Ok(fd as RawFd) + }; + + if !fds.is_null() { + unsafe { libc::free(fds as *mut _) }; + } + + result +} diff --git a/crates/shadowsocks-service/src/sys/unix/mod.rs b/crates/shadowsocks-service/src/sys/unix/mod.rs index 2c85aeb8..78d1c831 100644 --- a/crates/shadowsocks-service/src/sys/unix/mod.rs +++ b/crates/shadowsocks-service/src/sys/unix/mod.rs @@ -1,5 +1,14 @@ use std::io; +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(target_os = "macos")] { + mod macos; + pub use self::macos::*; + } +} + #[allow(dead_code)] #[cfg(not(target_os = "android"))] pub fn set_nofile(nofile: u64) -> io::Result<()> { diff --git a/crates/shadowsocks/src/net/tcp.rs b/crates/shadowsocks/src/net/tcp.rs index b89e4dac..09fce110 100644 --- a/crates/shadowsocks/src/net/tcp.rs +++ b/crates/shadowsocks/src/net/tcp.rs @@ -166,11 +166,17 @@ impl TcpListener { } /// Create a `TcpListener` from tokio's `TcpListener` - pub fn from_listener(listener: TokioTcpListener, accept_opts: AcceptOpts) -> TcpListener { - TcpListener { + pub fn from_listener(listener: TokioTcpListener, accept_opts: AcceptOpts) -> io::Result { + // Enable TFO if supported + // macos requires TCP_FASTOPEN to be set after listen(), but other platform doesn't have this constraint + if accept_opts.tcp.fastopen { + set_tcp_fastopen(&listener)?; + } + + Ok(TcpListener { inner: listener, accept_opts, - } + }) } /// Polls to accept a new incoming connection to this listener.