Skip to content

Commit 9da1c4d

Browse files
committed
feat(plugin): Support SIP003u, Plugin UDP mode
- ref shadowsocks/shadowsocks-org#180 - "plugin_mode" option in configuration file, and ssmanager API - "plugin_mode" is "tcp_only" by default - ref #1127
1 parent 2dff4ec commit 9da1c4d

File tree

13 files changed

+122
-15
lines changed

13 files changed

+122
-15
lines changed

crates/shadowsocks-service/src/config.rs

+60
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ struct SSConfig {
148148
plugin_opts: Option<String>,
149149
#[serde(skip_serializing_if = "Option::is_none")]
150150
plugin_args: Option<Vec<String>>,
151+
#[serde(skip_serializing_if = "Option::is_none")]
152+
plugin_mode: Option<String>,
151153

152154
#[serde(skip_serializing_if = "Option::is_none")]
153155
timeout: Option<u64>,
@@ -326,6 +328,8 @@ struct SSServerExtConfig {
326328
plugin_opts: Option<String>,
327329
#[serde(skip_serializing_if = "Option::is_none")]
328330
plugin_args: Option<Vec<String>>,
331+
#[serde(skip_serializing_if = "Option::is_none")]
332+
plugin_mode: Option<String>,
329333

330334
#[serde(skip_serializing_if = "Option::is_none")]
331335
timeout: Option<u64>,
@@ -1646,6 +1650,20 @@ impl Config {
16461650
plugin: p.clone(),
16471651
plugin_opts: config.plugin_opts.clone(),
16481652
plugin_args: config.plugin_args.clone().unwrap_or_default(),
1653+
plugin_mode: match config.plugin_mode {
1654+
None => Mode::TcpOnly,
1655+
Some(ref mode) => match mode.parse::<Mode>() {
1656+
Ok(m) => m,
1657+
Err(..) => {
1658+
let e = Error::new(
1659+
ErrorKind::Malformed,
1660+
"malformed `plugin_mode`, must be one of `tcp_only`, `udp_only` and `tcp_and_udp`",
1661+
None,
1662+
);
1663+
return Err(e);
1664+
}
1665+
},
1666+
},
16491667
};
16501668
nsvr.set_plugin(plugin);
16511669
}
@@ -1773,6 +1791,20 @@ impl Config {
17731791
plugin: p,
17741792
plugin_opts: svr.plugin_opts,
17751793
plugin_args: svr.plugin_args.unwrap_or_default(),
1794+
plugin_mode: match svr.plugin_mode {
1795+
None => Mode::TcpOnly,
1796+
Some(ref mode) => match mode.parse::<Mode>() {
1797+
Ok(m) => m,
1798+
Err(..) => {
1799+
let e = Error::new(
1800+
ErrorKind::Malformed,
1801+
"malformed `plugin_mode`, must be one of `tcp_only`, `udp_only` and `tcp_and_udp`",
1802+
None,
1803+
);
1804+
return Err(e);
1805+
}
1806+
},
1807+
},
17761808
};
17771809
nsvr.set_plugin(plugin);
17781810
}
@@ -1889,6 +1921,20 @@ impl Config {
18891921
plugin: p,
18901922
plugin_opts: config.plugin_opts,
18911923
plugin_args: config.plugin_args.unwrap_or_default(),
1924+
plugin_mode: match config.plugin_mode {
1925+
None => Mode::TcpOnly,
1926+
Some(ref mode) => match mode.parse::<Mode>() {
1927+
Ok(m) => m,
1928+
Err(..) => {
1929+
let e = Error::new(
1930+
ErrorKind::Malformed,
1931+
"malformed `plugin_mode`, must be one of `tcp_only`, `udp_only` and `tcp_and_udp`",
1932+
None,
1933+
);
1934+
return Err(e);
1935+
}
1936+
},
1937+
},
18921938
});
18931939
}
18941940
}
@@ -2461,6 +2507,13 @@ impl fmt::Display for Config {
24612507
Some(p.plugin_args.clone())
24622508
}
24632509
});
2510+
jconf.plugin_mode = match svr.plugin() {
2511+
None => None,
2512+
Some(p) => match p.plugin_mode {
2513+
Mode::TcpOnly => None,
2514+
_ => Some(p.plugin_mode.to_string()),
2515+
},
2516+
};
24642517
jconf.timeout = svr.timeout().map(|t| t.as_secs());
24652518
jconf.mode = Some(svr.mode().to_string());
24662519

@@ -2510,6 +2563,13 @@ impl fmt::Display for Config {
25102563
Some(p.plugin_args.clone())
25112564
}
25122565
}),
2566+
plugin_mode: match svr.plugin() {
2567+
None => None,
2568+
Some(p) => match p.plugin_mode {
2569+
Mode::TcpOnly => None,
2570+
_ => Some(p.plugin_mode.to_string()),
2571+
},
2572+
},
25132573
timeout: svr.timeout().map(|t| t.as_secs()),
25142574
remarks: svr.remarks().map(ToOwned::to_owned),
25152575
id: svr.id().map(ToOwned::to_owned),

crates/shadowsocks-service/src/local/tunnel/tcprelay.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ async fn handle_tcp_client(
7474
"establishing tcp tunnel {} <-> {} through sever {} (outbound: {})",
7575
peer_addr,
7676
forward_addr,
77-
svr_cfg.external_addr(),
77+
svr_cfg.tcp_external_addr(),
7878
svr_cfg.addr(),
7979
);
8080

crates/shadowsocks-service/src/local/utils.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ where
3030
"established tcp tunnel {} <-> {} through sever {} (outbound: {})",
3131
peer_addr,
3232
target_addr,
33-
svr_cfg.external_addr(),
33+
svr_cfg.tcp_external_addr(),
3434
svr_cfg.addr(),
3535
);
3636
} else {

crates/shadowsocks-service/src/manager/server.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ impl Manager {
256256
info!(
257257
"closed managed server listening on {}, inbound address {}",
258258
v.svr_cfg.addr(),
259-
v.svr_cfg.external_addr()
259+
v.svr_cfg.tcp_external_addr()
260260
);
261261
}
262262

@@ -444,6 +444,18 @@ impl Manager {
444444
plugin: plugin.clone(),
445445
plugin_opts: req.plugin_opts.clone(),
446446
plugin_args: Vec::new(),
447+
plugin_mode: match req.plugin_mode {
448+
None => Mode::TcpOnly,
449+
Some(ref mode) => match mode.parse::<Mode>() {
450+
Ok(m) => m,
451+
Err(..) => {
452+
error!("unrecognized plugin_mode \"{}\", req: {:?}", mode, req);
453+
454+
let err = format!("unrecognized plugin_mode \"{}\"", mode);
455+
return Ok(AddResponse(err));
456+
}
457+
},
458+
},
447459
};
448460
svr_cfg.set_plugin(p);
449461
} else if let Some(ref plugin) = self.svr_cfg.plugin {
@@ -536,6 +548,7 @@ impl Manager {
536548
no_delay: None,
537549
plugin: None,
538550
plugin_opts: None,
551+
plugin_mode: None,
539552
mode: None,
540553
users,
541554
};

crates/shadowsocks/src/config.rs

+19-3
Original file line numberDiff line numberDiff line change
@@ -541,9 +541,24 @@ impl ServerConfig {
541541
self.plugin_addr.as_ref()
542542
}
543543

544-
/// Get server's external address
545-
pub fn external_addr(&self) -> &ServerAddr {
546-
self.plugin_addr.as_ref().unwrap_or(&self.addr)
544+
/// Get server's TCP external address
545+
pub fn tcp_external_addr(&self) -> &ServerAddr {
546+
if let Some(plugin) = self.plugin() {
547+
if plugin.plugin_mode.enable_tcp() {
548+
return self.plugin_addr.as_ref().unwrap_or(&self.addr);
549+
}
550+
}
551+
&self.addr
552+
}
553+
554+
/// Get server's TCP external address
555+
pub fn udp_external_addr(&self) -> &ServerAddr {
556+
if let Some(plugin) = self.plugin() {
557+
if plugin.plugin_mode.enable_udp() {
558+
return self.plugin_addr.as_ref().unwrap_or(&self.addr);
559+
}
560+
}
561+
&self.addr
547562
}
548563

549564
/// Set timeout
@@ -790,6 +805,7 @@ impl ServerConfig {
790805
plugin: p.to_owned(),
791806
plugin_opts: vsp.next().map(ToOwned::to_owned),
792807
plugin_args: Vec::new(), // SIP002 doesn't have arguments for plugins
808+
plugin_mode: Mode::TcpOnly, // SIP002 doesn't support SIP003u
793809
};
794810
svrconfig.set_plugin(plugin);
795811
}

crates/shadowsocks/src/manager/protocol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ pub struct ServerConfig {
3737
#[serde(skip_serializing_if = "Option::is_none")]
3838
pub plugin_opts: Option<String>,
3939
#[serde(skip_serializing_if = "Option::is_none")]
40+
pub plugin_mode: Option<String>,
41+
#[serde(skip_serializing_if = "Option::is_none")]
4042
pub mode: Option<String>,
4143
#[serde(skip_serializing_if = "Option::is_none")]
4244
pub users: Option<Vec<ServerUserConfig>>,

crates/shadowsocks/src/plugin/mod.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use std::{
2222
use log::{debug, error};
2323
use tokio::{net::TcpStream, process::Child, time};
2424

25-
use crate::config::ServerAddr;
25+
use crate::config::{Mode, ServerAddr};
2626

2727
mod obfs_proxy;
2828
mod ss_plugin;
@@ -33,6 +33,7 @@ pub struct PluginConfig {
3333
pub plugin: String,
3434
pub plugin_opts: Option<String>,
3535
pub plugin_args: Vec<String>,
36+
pub plugin_mode: Mode,
3637
}
3738

3839
/// Mode of Plugin
@@ -60,6 +61,7 @@ pub enum PluginMode {
6061
pub struct Plugin {
6162
process: Child,
6263
local_addr: SocketAddr,
64+
mode: Mode,
6365
}
6466

6567
impl Plugin {
@@ -108,7 +110,11 @@ impl Plugin {
108110
}
109111
}
110112

111-
Ok(Plugin { process, local_addr })
113+
Ok(Plugin {
114+
process,
115+
local_addr,
116+
mode: c.plugin_mode,
117+
})
112118
}
113119
}
114120
}
@@ -120,6 +126,12 @@ impl Plugin {
120126

121127
/// Check if plugin have been started
122128
pub async fn wait_started(&self, timeout: Duration) -> bool {
129+
// Only test started with TCP connect()
130+
// XXX: Is there an easy way to test if UDP port was listening? (no ICMP!)
131+
if !self.mode.enable_tcp() {
132+
return true;
133+
}
134+
123135
let start_time = Instant::now();
124136

125137
loop {
@@ -130,6 +142,7 @@ impl Plugin {
130142
}
131143

132144
let remain_time = timeout - elapsed_time;
145+
133146
match time::timeout(remain_time, TcpStream::connect(self.local_addr)).await {
134147
Ok(Ok(..)) => {
135148
return true;

crates/shadowsocks/src/relay/tcprelay/proxy_listener.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ impl ProxyListener {
3939
svr_cfg: &ServerConfig,
4040
accept_opts: AcceptOpts,
4141
) -> io::Result<ProxyListener> {
42-
let listener = match svr_cfg.external_addr() {
42+
let listener = match svr_cfg.tcp_external_addr() {
4343
ServerAddr::SocketAddr(sa) => TcpListener::bind_with_opts(sa, accept_opts).await?,
4444
ServerAddr::DomainName(domain, port) => {
4545
lookup_then!(&context, domain, *port, |addr| {

crates/shadowsocks/src/relay/tcprelay/proxy_stream/client.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ where
115115
Some(d) => {
116116
match time::timeout(
117117
d,
118-
OutboundTcpStream::connect_server_with_opts(&context, svr_cfg.external_addr(), opts),
118+
OutboundTcpStream::connect_server_with_opts(&context, svr_cfg.tcp_external_addr(), opts),
119119
)
120120
.await
121121
{
@@ -129,13 +129,13 @@ where
129129
}
130130
}
131131
}
132-
None => OutboundTcpStream::connect_server_with_opts(&context, svr_cfg.external_addr(), opts).await?,
132+
None => OutboundTcpStream::connect_server_with_opts(&context, svr_cfg.tcp_external_addr(), opts).await?,
133133
};
134134

135135
trace!(
136136
"connected tcp remote {} (outbound: {}) with {:?}",
137137
svr_cfg.addr(),
138-
svr_cfg.external_addr(),
138+
svr_cfg.tcp_external_addr(),
139139
opts
140140
);
141141

crates/shadowsocks/src/relay/udprelay/proxy_socket.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ impl ProxySocket {
9898
) -> ProxySocketResult<ProxySocket> {
9999
// Note: Plugins doesn't support UDP relay
100100

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

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

105105
Ok(ProxySocket::from_socket(
106106
UdpSocketType::Client,
@@ -158,7 +158,7 @@ impl ProxySocket {
158158
opts: AcceptOpts,
159159
) -> ProxySocketResult<ProxySocket> {
160160
// Plugins doesn't support UDP
161-
let socket = match svr_cfg.addr() {
161+
let socket = match svr_cfg.udp_external_addr() {
162162
ServerAddr::SocketAddr(sa) => ShadowUdpSocket::listen_with_opts(sa, opts).await?,
163163
ServerAddr::DomainName(domain, port) => {
164164
lookup_then!(&context, domain, *port, |addr| {

src/service/local.rs

+1
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,7 @@ pub fn main(matches: &ArgMatches) -> ExitCode {
583583
plugin: p,
584584
plugin_opts: matches.get_one::<String>("PLUGIN_OPT").cloned(),
585585
plugin_args: Vec::new(),
586+
plugin_mode: Mode::TcpOnly,
586587
};
587588

588589
sc.set_plugin(plugin);

src/service/manager.rs

+1
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ pub fn main(matches: &ArgMatches) -> ExitCode {
373373
plugin: p,
374374
plugin_opts: matches.get_one::<String>("PLUGIN_OPT").cloned(),
375375
plugin_args: Vec::new(),
376+
plugin_mode: Mode::TcpOnly,
376377
});
377378
}
378379

src/service/server.rs

+1
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ pub fn main(matches: &ArgMatches) -> ExitCode {
354354
plugin: p,
355355
plugin_opts: matches.get_one::<String>("PLUGIN_OPT").cloned(),
356356
plugin_args: Vec::new(),
357+
plugin_mode: Mode::TcpOnly,
357358
};
358359

359360
sc.set_plugin(plugin);

0 commit comments

Comments
 (0)