mirror of
https://github.com/shadowsocks/shadowsocks-rust.git
synced 2026-11-06 16:14:11 +00:00
feat(local): basic support of macOS launchd activate socket (#1330)
supersede #1331
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -240,6 +240,14 @@ struct SSLocalExtConfig {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
protocol: Option<String>,
|
||||
|
||||
/// macOS launch activate socket
|
||||
#[cfg(target_os = "macos")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
launchd_udp_socket_name: Option<String>,
|
||||
#[cfg(target_os = "macos")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
launchd_tcp_socket_name: Option<String>,
|
||||
|
||||
/// 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<PathBuf>,
|
||||
|
||||
/// macOS launchd socket for TCP listener
|
||||
///
|
||||
/// <https://developer.apple.com/documentation/xpc/1505523-launch_activate_socket>
|
||||
/// <https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html>
|
||||
///
|
||||
/// ```plist
|
||||
/// <key>Sockets</key>
|
||||
/// <dict>
|
||||
/// <key>{launchd_tcp_socket_name}</key>
|
||||
/// <dict>
|
||||
/// <key>SockType</key>
|
||||
/// <string>stream</string>
|
||||
/// ... other keys ...
|
||||
/// </dict>
|
||||
/// </dict>
|
||||
/// ```
|
||||
#[cfg(target_os = "macos")]
|
||||
pub launchd_tcp_socket_name: Option<String>,
|
||||
/// macOS launchd socket for UDP listener
|
||||
///
|
||||
/// <https://developer.apple.com/documentation/xpc/1505523-launch_activate_socket>
|
||||
/// <https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html>
|
||||
///
|
||||
/// ```plist
|
||||
/// <key>Sockets</key>
|
||||
/// <dict>
|
||||
/// <key>{launchd_udp_socket_name}</key>
|
||||
/// <dict>
|
||||
/// <key>SockType</key>
|
||||
/// <string>dgram</string>
|
||||
/// ... other keys ...
|
||||
/// </dict>
|
||||
/// </dict>
|
||||
/// ```
|
||||
#[cfg(target_os = "macos")]
|
||||
pub launchd_udp_socket_name: Option<String>,
|
||||
|
||||
/// 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::<Mode>() {
|
||||
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())
|
||||
|
||||
@@ -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<String>,
|
||||
#[cfg(target_os = "macos")]
|
||||
launchd_udp_socket_name: Option<String>,
|
||||
}
|
||||
|
||||
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<Dns> {
|
||||
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<ServiceContext>,
|
||||
bind_addr: ServerAddr,
|
||||
local_addr: Arc<NameServerAddr>,
|
||||
remote_addr: Arc<Address>,
|
||||
client: Arc<DnsClient>,
|
||||
#[cfg(target_os = "macos")]
|
||||
launchd_socket_name: Option<String>,
|
||||
}
|
||||
|
||||
impl DnsTcpServerBuilder {
|
||||
fn new(
|
||||
context: Arc<ServiceContext>,
|
||||
bind_addr: ServerAddr,
|
||||
local_addr: Arc<NameServerAddr>,
|
||||
remote_addr: Arc<Address>,
|
||||
client: Arc<DnsClient>,
|
||||
) -> 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<DnsTcpServer> {
|
||||
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<ServiceContext>,
|
||||
bind_addr: &ServerAddr,
|
||||
local_addr: Arc<NameServerAddr>,
|
||||
remote_addr: Arc<Address>,
|
||||
client: Arc<DnsClient>,
|
||||
) -> io::Result<DnsTcpServer> {
|
||||
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<SocketAddr> {
|
||||
self.listener.local_addr()
|
||||
@@ -272,6 +347,67 @@ impl DnsTcpServer {
|
||||
}
|
||||
}
|
||||
|
||||
struct DnsUdpServerBuilder {
|
||||
context: Arc<ServiceContext>,
|
||||
bind_addr: ServerAddr,
|
||||
local_addr: Arc<NameServerAddr>,
|
||||
remote_addr: Arc<Address>,
|
||||
client: Arc<DnsClient>,
|
||||
#[cfg(target_os = "macos")]
|
||||
launchd_socket_name: Option<String>,
|
||||
}
|
||||
|
||||
impl DnsUdpServerBuilder {
|
||||
fn new(
|
||||
context: Arc<ServiceContext>,
|
||||
bind_addr: ServerAddr,
|
||||
local_addr: Arc<NameServerAddr>,
|
||||
remote_addr: Arc<Address>,
|
||||
client: Arc<DnsClient>,
|
||||
) -> 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<DnsUdpServer> {
|
||||
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<UdpSocket>,
|
||||
@@ -281,34 +417,6 @@ pub struct DnsUdpServer {
|
||||
}
|
||||
|
||||
impl DnsUdpServer {
|
||||
async fn new(
|
||||
context: Arc<ServiceContext>,
|
||||
bind_addr: &ServerAddr,
|
||||
local_addr: Arc<NameServerAddr>,
|
||||
remote_addr: Arc<Address>,
|
||||
client: Arc<DnsClient>,
|
||||
) -> io::Result<DnsUdpServer> {
|
||||
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<SocketAddr> {
|
||||
self.listener.local_addr()
|
||||
|
||||
@@ -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<ServiceContext>,
|
||||
client_config: ServerAddr,
|
||||
balancer: PingBalancer,
|
||||
#[cfg(target_os = "macos")]
|
||||
launchd_tcp_socket_name: Option<String>,
|
||||
}
|
||||
|
||||
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<Http> {
|
||||
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()));
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ pub use self::{
|
||||
udp::{UdpAssociationManager, UdpInboundWrite},
|
||||
};
|
||||
|
||||
mod tcp;
|
||||
pub(crate) mod tcp;
|
||||
pub(crate) mod udp;
|
||||
|
||||
@@ -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<TcpListener> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod auto_proxy_io;
|
||||
pub mod auto_proxy_stream;
|
||||
pub mod listener;
|
||||
|
||||
@@ -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<UdpSocket> {
|
||||
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),
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub use self::association::{UdpAssociationManager, UdpInboundWrite};
|
||||
|
||||
pub mod association;
|
||||
pub mod listener;
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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<String>,
|
||||
#[cfg(target_os = "macos")]
|
||||
launchd_udp_socket_name: Option<String>,
|
||||
}
|
||||
|
||||
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<Socks> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<ServiceContext>,
|
||||
client_config: ServerAddr,
|
||||
udp_bind_addr: ServerAddr,
|
||||
balancer: PingBalancer,
|
||||
mode: Mode,
|
||||
socks5_auth: Arc<Socks5AuthConfig>,
|
||||
#[cfg(target_os = "macos")]
|
||||
launchd_socket_name: Option<String>,
|
||||
}
|
||||
|
||||
impl SocksTcpServerBuilder {
|
||||
pub(crate) fn new(
|
||||
context: Arc<ServiceContext>,
|
||||
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<SocksTcpServer> {
|
||||
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<ServiceContext>,
|
||||
client_config: ServerAddr,
|
||||
udp_bind_addr: ServerAddr,
|
||||
balancer: PingBalancer,
|
||||
mode: Mode,
|
||||
socks5_auth: Socks5AuthConfig,
|
||||
) -> io::Result<SocksTcpServer> {
|
||||
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<SocketAddr> {
|
||||
self.listener.local_addr()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<ServiceContext>,
|
||||
client_config: ServerAddr,
|
||||
time_to_live: Option<Duration>,
|
||||
capacity: Option<usize>,
|
||||
balancer: PingBalancer,
|
||||
#[cfg(target_os = "macos")]
|
||||
launchd_socket_name: Option<String>,
|
||||
}
|
||||
|
||||
impl Socks5UdpServerBuilder {
|
||||
pub(crate) fn new(
|
||||
context: Arc<ServiceContext>,
|
||||
client_config: ServerAddr,
|
||||
time_to_live: Option<Duration>,
|
||||
capacity: Option<usize>,
|
||||
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<Socks5UdpServer> {
|
||||
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<UdpSocket>,
|
||||
@@ -77,36 +137,6 @@ pub struct Socks5UdpServer {
|
||||
}
|
||||
|
||||
impl Socks5UdpServer {
|
||||
pub(crate) async fn new(
|
||||
context: Arc<ServiceContext>,
|
||||
client_config: &ServerAddr,
|
||||
time_to_live: Option<Duration>,
|
||||
capacity: Option<usize>,
|
||||
balancer: PingBalancer,
|
||||
) -> io::Result<Socks5UdpServer> {
|
||||
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<SocketAddr> {
|
||||
self.listener.local_addr()
|
||||
|
||||
@@ -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<ServiceContext>,
|
||||
@@ -18,6 +21,10 @@ pub struct TunnelBuilder {
|
||||
client_addr: ServerAddr,
|
||||
udp_addr: Option<ServerAddr>,
|
||||
balancer: PingBalancer,
|
||||
#[cfg(target_os = "macos")]
|
||||
launchd_tcp_socket_name: Option<String>,
|
||||
#[cfg(target_os = "macos")]
|
||||
launchd_udp_socket_name: Option<String>,
|
||||
}
|
||||
|
||||
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<Tunnel> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<ServiceContext>,
|
||||
client_config: ServerAddr,
|
||||
balancer: PingBalancer,
|
||||
forward_addr: Address,
|
||||
#[cfg(target_os = "macos")]
|
||||
launchd_socket_name: Option<String>,
|
||||
}
|
||||
|
||||
impl TunnelTcpServerBuilder {
|
||||
pub(crate) fn new(
|
||||
context: Arc<ServiceContext>,
|
||||
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<TunnelTcpServer> {
|
||||
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<ServiceContext>,
|
||||
@@ -22,32 +81,6 @@ pub struct TunnelTcpServer {
|
||||
}
|
||||
|
||||
impl TunnelTcpServer {
|
||||
pub(crate) async fn new(
|
||||
context: Arc<ServiceContext>,
|
||||
client_config: &ServerAddr,
|
||||
balancer: PingBalancer,
|
||||
forward_addr: Address,
|
||||
) -> io::Result<TunnelTcpServer> {
|
||||
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<SocketAddr> {
|
||||
self.listener.local_addr()
|
||||
|
||||
@@ -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<ServiceContext>,
|
||||
client_config: ServerAddr,
|
||||
time_to_live: Option<Duration>,
|
||||
capacity: Option<usize>,
|
||||
balancer: PingBalancer,
|
||||
forward_addr: Address,
|
||||
#[cfg(target_os = "macos")]
|
||||
launchd_socket_name: Option<String>,
|
||||
}
|
||||
|
||||
impl TunnelUdpServerBuilder {
|
||||
pub(crate) fn new(
|
||||
context: Arc<ServiceContext>,
|
||||
client_config: ServerAddr,
|
||||
time_to_live: Option<Duration>,
|
||||
capacity: Option<usize>,
|
||||
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<TunnelUdpServer> {
|
||||
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<UdpSocket>,
|
||||
@@ -40,38 +104,6 @@ pub struct TunnelUdpServer {
|
||||
}
|
||||
|
||||
impl TunnelUdpServer {
|
||||
pub(crate) async fn new(
|
||||
context: Arc<ServiceContext>,
|
||||
client_config: &ServerAddr,
|
||||
time_to_live: Option<Duration>,
|
||||
capacity: Option<usize>,
|
||||
balancer: PingBalancer,
|
||||
forward_addr: Address,
|
||||
) -> io::Result<TunnelUdpServer> {
|
||||
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<SocketAddr> {
|
||||
self.listener.local_addr()
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
//! macOS launch activate socket
|
||||
//!
|
||||
//! <https://developer.apple.com/documentation/xpc/1505523-launch_activate_socket>
|
||||
//! <https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html>
|
||||
|
||||
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<TcpListener> {
|
||||
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<UdpSocket> {
|
||||
let fd = get_launch_activate_socket(name)?;
|
||||
debug!("created UDP socket from launch activate socket {}", fd);
|
||||
Ok(unsafe { UdpSocket::from_raw_fd(fd) })
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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<RawFd> {
|
||||
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
|
||||
}
|
||||
@@ -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<()> {
|
||||
|
||||
@@ -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<TcpListener> {
|
||||
// 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.
|
||||
|
||||
Reference in New Issue
Block a user