Skip to content

Commit

Permalink
feat(plugin): Support SIP003u, Plugin UDP mode
Browse files Browse the repository at this point in the history
- ref shadowsocks/shadowsocks-org#180
- "plugin_mode" option in configuration file, and ssmanager API
- "plugin_mode" is "tcp_only" by default
  • Loading branch information
zonyitoo committed Apr 15, 2023
1 parent 2dff4ec commit f0cdeb9
Show file tree
Hide file tree
Showing 13 changed files with 131 additions and 52 deletions.
69 changes: 61 additions & 8 deletions crates/shadowsocks-service/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,7 @@ use serde::{Deserialize, Serialize};
use shadowsocks::relay::socks5::Address;
use shadowsocks::{
config::{
ManagerAddr,
Mode,
ReplayAttackPolicy,
ServerAddr,
ServerConfig,
ServerUser,
ServerUserManager,
ServerWeight,
ManagerAddr, Mode, ReplayAttackPolicy, ServerAddr, ServerConfig, ServerUser, ServerUserManager, ServerWeight,
},
crypto::CipherKind,
plugin::PluginConfig,
Expand Down Expand Up @@ -148,6 +141,8 @@ struct SSConfig {
plugin_opts: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
plugin_args: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
plugin_mode: Option<String>,

#[serde(skip_serializing_if = "Option::is_none")]
timeout: Option<u64>,
Expand Down Expand Up @@ -326,6 +321,8 @@ struct SSServerExtConfig {
plugin_opts: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
plugin_args: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
plugin_mode: Option<String>,

#[serde(skip_serializing_if = "Option::is_none")]
timeout: Option<u64>,
Expand Down Expand Up @@ -1646,6 +1643,20 @@ impl Config {
plugin: p.clone(),
plugin_opts: config.plugin_opts.clone(),
plugin_args: config.plugin_args.clone().unwrap_or_default(),
plugin_mode: match config.plugin_mode {
None => Mode::TcpOnly,
Some(ref mode) => match mode.parse::<Mode>() {
Ok(m) => m,
Err(..) => {
let e = Error::new(
ErrorKind::Malformed,
"malformed `plugin_mode`, must be one of `tcp_only`, `udp_only` and `tcp_and_udp`",
None,
);
return Err(e);
}
},
},
};
nsvr.set_plugin(plugin);
}
Expand Down Expand Up @@ -1773,6 +1784,20 @@ impl Config {
plugin: p,
plugin_opts: svr.plugin_opts,
plugin_args: svr.plugin_args.unwrap_or_default(),
plugin_mode: match svr.plugin_mode {
None => Mode::TcpOnly,
Some(ref mode) => match mode.parse::<Mode>() {
Ok(m) => m,
Err(..) => {
let e = Error::new(
ErrorKind::Malformed,
"malformed `plugin_mode`, must be one of `tcp_only`, `udp_only` and `tcp_and_udp`",
None,
);
return Err(e);
}
},
},
};
nsvr.set_plugin(plugin);
}
Expand Down Expand Up @@ -1889,6 +1914,20 @@ impl Config {
plugin: p,
plugin_opts: config.plugin_opts,
plugin_args: config.plugin_args.unwrap_or_default(),
plugin_mode: match config.plugin_mode {
None => Mode::TcpOnly,
Some(ref mode) => match mode.parse::<Mode>() {
Ok(m) => m,
Err(..) => {
let e = Error::new(
ErrorKind::Malformed,
"malformed `plugin_mode`, must be one of `tcp_only`, `udp_only` and `tcp_and_udp`",
None,
);
return Err(e);
}
},
},
});
}
}
Expand Down Expand Up @@ -2461,6 +2500,13 @@ impl fmt::Display for Config {
Some(p.plugin_args.clone())
}
});
jconf.plugin_mode = match svr.plugin() {
None => None,
Some(p) => match p.plugin_mode {
Mode::TcpOnly => None,
_ => Some(p.plugin_mode.to_string()),
},
};
jconf.timeout = svr.timeout().map(|t| t.as_secs());
jconf.mode = Some(svr.mode().to_string());

Expand Down Expand Up @@ -2510,6 +2556,13 @@ impl fmt::Display for Config {
Some(p.plugin_args.clone())
}
}),
plugin_mode: match svr.plugin() {
None => None,
Some(p) => match p.plugin_mode {
Mode::TcpOnly => None,
_ => Some(p.plugin_mode.to_string()),
},
},
timeout: svr.timeout().map(|t| t.as_secs()),
remarks: svr.remarks().map(ToOwned::to_owned),
id: svr.id().map(ToOwned::to_owned),
Expand Down
2 changes: 1 addition & 1 deletion crates/shadowsocks-service/src/local/tunnel/tcprelay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ async fn handle_tcp_client(
"establishing tcp tunnel {} <-> {} through sever {} (outbound: {})",
peer_addr,
forward_addr,
svr_cfg.external_addr(),
svr_cfg.tcp_external_addr(),
svr_cfg.addr(),
);

Expand Down
2 changes: 1 addition & 1 deletion crates/shadowsocks-service/src/local/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ where
"established tcp tunnel {} <-> {} through sever {} (outbound: {})",
peer_addr,
target_addr,
svr_cfg.external_addr(),
svr_cfg.tcp_external_addr(),
svr_cfg.addr(),
);
} else {
Expand Down
31 changes: 17 additions & 14 deletions crates/shadowsocks-service/src/manager/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,12 @@ use shadowsocks::{
crypto::CipherKind,
dns_resolver::DnsResolver,
manager::protocol::{
self,
AddRequest,
AddResponse,
ErrorResponse,
ListResponse,
ManagerRequest,
PingResponse,
RemoveRequest,
RemoveResponse,
ServerUserConfig,
StatRequest,
self, AddRequest, AddResponse, ErrorResponse, ListResponse, ManagerRequest, PingResponse, RemoveRequest,
RemoveResponse, ServerUserConfig, StatRequest,
},
net::{AcceptOpts, ConnectOpts},
plugin::PluginConfig,
ManagerListener,
ServerAddr,
ManagerListener, ServerAddr,
};
use tokio::{sync::Mutex, task::JoinHandle};

Expand Down Expand Up @@ -256,7 +246,7 @@ impl Manager {
info!(
"closed managed server listening on {}, inbound address {}",
v.svr_cfg.addr(),
v.svr_cfg.external_addr()
v.svr_cfg.tcp_external_addr()
);
}

Expand Down Expand Up @@ -444,6 +434,18 @@ impl Manager {
plugin: plugin.clone(),
plugin_opts: req.plugin_opts.clone(),
plugin_args: Vec::new(),
plugin_mode: match req.plugin_mode {
None => Mode::TcpOnly,
Some(ref mode) => match mode.parse::<Mode>() {
Ok(m) => m,
Err(..) => {
error!("unrecognized plugin_mode \"{}\", req: {:?}", mode, req);

let err = format!("unrecognized plugin_mode \"{}\"", mode);
return Ok(AddResponse(err));
}
},
},
};
svr_cfg.set_plugin(p);
} else if let Some(ref plugin) = self.svr_cfg.plugin {
Expand Down Expand Up @@ -536,6 +538,7 @@ impl Manager {
no_delay: None,
plugin: None,
plugin_opts: None,
plugin_mode: None,
mode: None,
users,
};
Expand Down
22 changes: 19 additions & 3 deletions crates/shadowsocks/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,9 +541,24 @@ impl ServerConfig {
self.plugin_addr.as_ref()
}

/// Get server's external address
pub fn external_addr(&self) -> &ServerAddr {
self.plugin_addr.as_ref().unwrap_or(&self.addr)
/// Get server's TCP external address
pub fn tcp_external_addr(&self) -> &ServerAddr {
if let Some(plugin) = self.plugin() {
if plugin.plugin_mode.enable_tcp() {
return self.plugin_addr.as_ref().unwrap_or(&self.addr);
}
}
&self.addr
}

/// Get server's TCP external address
pub fn udp_external_addr(&self) -> &ServerAddr {
if let Some(plugin) = self.plugin() {
if plugin.plugin_mode.enable_udp() {
return self.plugin_addr.as_ref().unwrap_or(&self.addr);
}
}
&self.addr
}

/// Set timeout
Expand Down Expand Up @@ -790,6 +805,7 @@ impl ServerConfig {
plugin: p.to_owned(),
plugin_opts: vsp.next().map(ToOwned::to_owned),
plugin_args: Vec::new(), // SIP002 doesn't have arguments for plugins
plugin_mode: Mode::TcpOnly, // SIP002 doesn't support SIP003u
};
svrconfig.set_plugin(plugin);
}
Expand Down
2 changes: 2 additions & 0 deletions crates/shadowsocks/src/manager/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub struct ServerConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub plugin_opts: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub plugin_mode: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mode: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub users: Option<Vec<ServerUserConfig>>,
Expand Down
16 changes: 15 additions & 1 deletion crates/shadowsocks/src/plugin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use std::{
use log::{debug, error};
use tokio::{net::TcpStream, process::Child, time};

use crate::config::Mode;
use crate::config::ServerAddr;

mod obfs_proxy;
Expand All @@ -33,6 +34,7 @@ pub struct PluginConfig {
pub plugin: String,
pub plugin_opts: Option<String>,
pub plugin_args: Vec<String>,
pub plugin_mode: Mode,
}

/// Mode of Plugin
Expand Down Expand Up @@ -60,6 +62,7 @@ pub enum PluginMode {
pub struct Plugin {
process: Child,
local_addr: SocketAddr,
mode: Mode,
}

impl Plugin {
Expand Down Expand Up @@ -108,7 +111,11 @@ impl Plugin {
}
}

Ok(Plugin { process, local_addr })
Ok(Plugin {
process,
local_addr,
mode: c.plugin_mode,
})
}
}
}
Expand All @@ -120,6 +127,12 @@ impl Plugin {

/// Check if plugin have been started
pub async fn wait_started(&self, timeout: Duration) -> bool {
// Only test started with TCP connect()
// XXX: Is there an easy way to test if UDP port was listening? (no ICMP!)
if !self.mode.enable_tcp() {
return true;
}

let start_time = Instant::now();

loop {
Expand All @@ -130,6 +143,7 @@ impl Plugin {
}

let remain_time = timeout - elapsed_time;

match time::timeout(remain_time, TcpStream::connect(self.local_addr)).await {
Ok(Ok(..)) => {
return true;
Expand Down
2 changes: 1 addition & 1 deletion crates/shadowsocks/src/relay/tcprelay/proxy_listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl ProxyListener {
svr_cfg: &ServerConfig,
accept_opts: AcceptOpts,
) -> io::Result<ProxyListener> {
let listener = match svr_cfg.external_addr() {
let listener = match svr_cfg.tcp_external_addr() {
ServerAddr::SocketAddr(sa) => TcpListener::bind_with_opts(sa, accept_opts).await?,
ServerAddr::DomainName(domain, port) => {
lookup_then!(&context, domain, *port, |addr| {
Expand Down
6 changes: 3 additions & 3 deletions crates/shadowsocks/src/relay/tcprelay/proxy_stream/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ where
Some(d) => {
match time::timeout(
d,
OutboundTcpStream::connect_server_with_opts(&context, svr_cfg.external_addr(), opts),
OutboundTcpStream::connect_server_with_opts(&context, svr_cfg.tcp_external_addr(), opts),
)
.await
{
Expand All @@ -129,13 +129,13 @@ where
}
}
}
None => OutboundTcpStream::connect_server_with_opts(&context, svr_cfg.external_addr(), opts).await?,
None => OutboundTcpStream::connect_server_with_opts(&context, svr_cfg.tcp_external_addr(), opts).await?,
};

trace!(
"connected tcp remote {} (outbound: {}) with {:?}",
svr_cfg.addr(),
svr_cfg.external_addr(),
svr_cfg.tcp_external_addr(),
opts
);

Expand Down
12 changes: 4 additions & 8 deletions crates/shadowsocks/src/relay/udprelay/proxy_socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,7 @@ use crate::{
};

use super::crypto_io::{
decrypt_client_payload,
decrypt_server_payload,
encrypt_client_payload,
encrypt_server_payload,
ProtocolError,
decrypt_client_payload, decrypt_server_payload, encrypt_client_payload, encrypt_server_payload, ProtocolError,
ProtocolResult,
};

Expand Down Expand Up @@ -98,9 +94,9 @@ impl ProxySocket {
) -> ProxySocketResult<ProxySocket> {
// Note: Plugins doesn't support UDP relay

let socket = ShadowUdpSocket::connect_server_with_opts(&context, svr_cfg.addr(), opts).await?;
let socket = ShadowUdpSocket::connect_server_with_opts(&context, svr_cfg.udp_external_addr(), opts).await?;

trace!("connected udp remote {} with {:?}", svr_cfg.addr(), opts);
trace!("connected udp remote {} with {:?}", svr_cfg.udp_external_addr(), opts);

Ok(ProxySocket::from_socket(
UdpSocketType::Client,
Expand Down Expand Up @@ -158,7 +154,7 @@ impl ProxySocket {
opts: AcceptOpts,
) -> ProxySocketResult<ProxySocket> {
// Plugins doesn't support UDP
let socket = match svr_cfg.addr() {
let socket = match svr_cfg.udp_external_addr() {
ServerAddr::SocketAddr(sa) => ShadowUdpSocket::listen_with_opts(sa, opts).await?,
ServerAddr::DomainName(domain, port) => {
lookup_then!(&context, domain, *port, |addr| {
Expand Down
Loading

0 comments on commit f0cdeb9

Please sign in to comment.