shadowsocks_rust/service/
server.rs1use std::{future::Future, net::IpAddr, path::PathBuf, process::ExitCode, time::Duration};
4
5use clap::{Arg, ArgAction, ArgGroup, ArgMatches, Command, ValueHint, builder::PossibleValuesParser};
6use futures::future::{self, Either};
7use log::{info, trace};
8use tokio::{
9 self,
10 runtime::{Builder, Runtime},
11};
12
13use shadowsocks_service::{
14 acl::AccessControl,
15 config::{Config, ConfigType, ManagerConfig, OutboundProxy, ServerInstanceConfig, read_variable_field_value},
16 run_server,
17 shadowsocks::{
18 config::{ManagerAddr, Mode, ServerAddr, ServerConfig},
19 crypto::{CipherKind, available_ciphers},
20 plugin::PluginConfig,
21 },
22};
23
24#[cfg(feature = "logging")]
25use crate::logging;
26use crate::{
27 config::{Config as ServiceConfig, RuntimeMode},
28 error::{ShadowsocksError, ShadowsocksResult},
29 monitor, vparser,
30};
31
32pub fn define_command_line_options(mut app: Command) -> Command {
34 app = app
35 .arg(
36 Arg::new("CONFIG")
37 .short('c')
38 .long("config")
39 .num_args(1)
40 .action(ArgAction::Set)
41 .value_parser(clap::value_parser!(PathBuf))
42 .value_hint(ValueHint::FilePath)
43 .help("Shadowsocks configuration file (https://shadowsocks.org/doc/configs.html)"),
44 )
45 .arg(
46 Arg::new("OUTBOUND_BIND_ADDR")
47 .short('b')
48 .long("outbound-bind-addr")
49 .num_args(1)
50 .action(ArgAction::Set)
51 .alias("bind-addr")
52 .value_parser(vparser::parse_ip_addr)
53 .help("Bind address, outbound socket will bind this address"),
54 )
55 .arg(
56 Arg::new("OUTBOUND_BIND_INTERFACE")
57 .long("outbound-bind-interface")
58 .num_args(1)
59 .action(ArgAction::Set)
60 .help("Set SO_BINDTODEVICE / IP_BOUND_IF / IP_UNICAST_IF option for outbound socket"),
61 )
62 .arg(
63 Arg::new("OUTBOUND_PROXY")
64 .long("outbound-proxy")
65 .num_args(1)
66 .action(ArgAction::Append)
67 .help("Outbound proxy hop for ssserver TCP relay; supports socks5://, http:// and https:// with optional user:pass@, repeat in order for chaining"),
68 )
69 .arg(
70 Arg::new("SERVER_ADDR")
71 .short('s')
72 .long("server-addr")
73 .num_args(1)
74 .action(ArgAction::Set)
75 .requires("ENCRYPT_METHOD")
76 .help("Server address"),
77 )
78 .arg(
79 Arg::new("PASSWORD")
80 .short('k')
81 .long("password")
82 .num_args(1)
83 .action(ArgAction::Set)
84 .requires("SERVER_ADDR")
85 .help("Server's password"),
86 )
87 .arg(
88 Arg::new("ENCRYPT_METHOD")
89 .short('m')
90 .long("encrypt-method")
91 .num_args(1)
92 .action(ArgAction::Set)
93 .value_parser(PossibleValuesParser::new(available_ciphers()))
94 .requires("SERVER_ADDR")
95 .help("Server's encryption method"),
96 )
97 .arg(
98 Arg::new("TIMEOUT")
99 .long("timeout")
100 .num_args(1)
101 .action(ArgAction::Set)
102 .value_parser(clap::value_parser!(u64))
103 .requires("SERVER_ADDR")
104 .help("Server's timeout seconds for TCP relay"),
105 )
106 .group(
107 ArgGroup::new("SERVER_CONFIG").arg("SERVER_ADDR")
108 )
109 .arg(
110 Arg::new("UDP_ONLY")
111 .short('u')
112 .action(ArgAction::SetTrue)
113 .conflicts_with("TCP_AND_UDP")
114 .requires("SERVER_ADDR")
115 .help("Server mode UDP_ONLY"),
116 )
117 .arg(
118 Arg::new("TCP_AND_UDP")
119 .short('U')
120 .action(ArgAction::SetTrue)
121 .requires("SERVER_ADDR")
122 .help("Server mode TCP_AND_UDP"),
123 )
124 .arg(
125 Arg::new("PLUGIN")
126 .long("plugin")
127 .num_args(1)
128 .action(ArgAction::Set)
129 .value_hint(ValueHint::CommandName)
130 .requires("SERVER_ADDR")
131 .help("SIP003 (https://shadowsocks.org/doc/sip003.html) plugin"),
132 )
133 .arg(
134 Arg::new("PLUGIN_MODE")
135 .long("plugin-mode")
136 .num_args(1)
137 .action(ArgAction::Set)
138 .requires("PLUGIN")
139 .help("SIP003/SIP003u plugin mode, must be one of `tcp_only` (default), `udp_only` and `tcp_and_udp`"),
140 )
141 .arg(
142 Arg::new("PLUGIN_OPT")
143 .long("plugin-opts")
144 .num_args(1)
145 .action(ArgAction::Set)
146 .requires("PLUGIN")
147 .help("Set SIP003 plugin options"),
148 )
149 .arg(Arg::new("MANAGER_ADDR").long("manager-addr").num_args(1).action(ArgAction::Set).value_parser(vparser::parse_manager_addr).alias("manager-address").help("ShadowSocks Manager (ssmgr) address, could be \"IP:Port\", \"Domain:Port\" or \"/path/to/unix.sock\""))
150 .arg(Arg::new("ACL").long("acl").num_args(1).action(ArgAction::Set).value_hint(ValueHint::FilePath).help("Path to ACL (Access Control List)"))
151 .arg(Arg::new("DNS").long("dns").num_args(1).action(ArgAction::Set).help("DNS nameservers, formatted like [(tcp|udp)://]host[:port][,host[:port]]..., or unix:///path/to/dns, or predefined keys like \"google\", \"cloudflare\""))
152 .arg(Arg::new("DNS_CACHE_SIZE").long("dns-cache-size").num_args(1).action(ArgAction::Set).value_parser(clap::value_parser!(usize)).help("DNS cache size in number of records. Works when trust-dns DNS backend is enabled."))
153 .arg(Arg::new("TCP_NO_DELAY").long("tcp-no-delay").alias("no-delay").action(ArgAction::SetTrue).help("Set TCP_NODELAY option for sockets"))
154 .arg(Arg::new("TCP_FAST_OPEN").long("tcp-fast-open").alias("fast-open").action(ArgAction::SetTrue).help("Enable TCP Fast Open (TFO)"))
155 .arg(Arg::new("TCP_KEEP_ALIVE").long("tcp-keep-alive").num_args(1).action(ArgAction::Set).value_parser(clap::value_parser!(u64)).help("Set TCP keep alive timeout seconds"))
156 .arg(Arg::new("TCP_MULTIPATH").long("tcp-multipath").alias("mptcp").action(ArgAction::SetTrue).help("Enable Multipath-TCP (MPTCP)"))
157 .arg(Arg::new("UDP_TIMEOUT").long("udp-timeout").num_args(1).action(ArgAction::Set).value_parser(clap::value_parser!(u64)).help("Timeout seconds for UDP relay"))
158 .arg(Arg::new("UDP_MAX_ASSOCIATIONS").long("udp-max-associations").num_args(1).action(ArgAction::Set).value_parser(clap::value_parser!(usize)).help("Maximum associations to be kept simultaneously for UDP relay"))
159 .arg(Arg::new("INBOUND_SEND_BUFFER_SIZE").long("inbound-send-buffer-size").num_args(1).action(ArgAction::Set).value_parser(clap::value_parser!(u32)).help("Set inbound sockets' SO_SNDBUF option"))
160 .arg(Arg::new("INBOUND_RECV_BUFFER_SIZE").long("inbound-recv-buffer-size").num_args(1).action(ArgAction::Set).value_parser(clap::value_parser!(u32)).help("Set inbound sockets' SO_RCVBUF option"))
161 .arg(Arg::new("OUTBOUND_SEND_BUFFER_SIZE").long("outbound-send-buffer-size").num_args(1).action(ArgAction::Set).value_parser(clap::value_parser!(u32)).help("Set outbound sockets' SO_SNDBUF option"))
162 .arg(Arg::new("OUTBOUND_RECV_BUFFER_SIZE").long("outbound-recv-buffer-size").num_args(1).action(ArgAction::Set).value_parser(clap::value_parser!(u32)).help("Set outbound sockets' SO_RCVBUF option"))
163 .arg(
164 Arg::new("IPV6_FIRST")
165 .short('6')
166 .action(ArgAction::SetTrue)
167 .help("Resolve hostname to IPv6 address first"),
168 );
169
170 #[cfg(feature = "logging")]
171 {
172 app = app
173 .arg(
174 Arg::new("VERBOSE")
175 .short('v')
176 .action(ArgAction::Count)
177 .help("Set log level"),
178 )
179 .arg(
180 Arg::new("LOG_WITHOUT_TIME")
181 .long("log-without-time")
182 .action(ArgAction::SetTrue)
183 .help("Log without datetime prefix"),
184 )
185 .arg(
186 Arg::new("LOG_CONFIG")
187 .long("log-config")
188 .hide(true)
190 .num_args(1)
191 .action(ArgAction::Set)
192 .value_parser(clap::value_parser!(PathBuf))
193 .value_hint(ValueHint::FilePath)
194 .help("log4rs configuration file"),
195 );
196 }
197
198 #[cfg(unix)]
199 {
200 app = app
201 .arg(
202 Arg::new("DAEMONIZE")
203 .short('d')
204 .long("daemonize")
205 .action(ArgAction::SetTrue)
206 .help("Daemonize"),
207 )
208 .arg(
209 Arg::new("DAEMONIZE_PID_PATH")
210 .long("daemonize-pid")
211 .num_args(1)
212 .action(ArgAction::Set)
213 .value_parser(clap::value_parser!(PathBuf))
214 .value_hint(ValueHint::FilePath)
215 .help("File path to store daemonized process's PID"),
216 );
217 }
218
219 #[cfg(all(unix, not(target_os = "android")))]
220 {
221 app = app.arg(
222 Arg::new("NOFILE")
223 .short('n')
224 .long("nofile")
225 .num_args(1)
226 .action(ArgAction::Set)
227 .value_parser(clap::value_parser!(u64))
228 .help("Set RLIMIT_NOFILE with both soft and hard limit"),
229 );
230 }
231
232 #[cfg(any(target_os = "linux", target_os = "android"))]
233 {
234 app = app.arg(
235 Arg::new("OUTBOUND_FWMARK")
236 .long("outbound-fwmark")
237 .num_args(1)
238 .action(ArgAction::Set)
239 .value_parser(clap::value_parser!(u32))
240 .help("Set SO_MARK option for outbound sockets"),
241 );
242 }
243
244 #[cfg(target_os = "freebsd")]
245 {
246 app = app.arg(
247 Arg::new("OUTBOUND_USER_COOKIE")
248 .long("outbound-user-cookie")
249 .num_args(1)
250 .action(ArgAction::Set)
251 .value_parser(clap::value_parser!(u32))
252 .help("Set SO_USER_COOKIE option for outbound sockets"),
253 );
254 }
255
256 #[cfg(feature = "multi-threaded")]
257 {
258 app = app
259 .arg(
260 Arg::new("SINGLE_THREADED")
261 .long("single-threaded")
262 .action(ArgAction::SetTrue)
263 .help("Run the program all in one thread"),
264 )
265 .arg(
266 Arg::new("WORKER_THREADS")
267 .long("worker-threads")
268 .num_args(1)
269 .action(ArgAction::Set)
270 .value_parser(clap::value_parser!(usize))
271 .help("Sets the number of worker threads the `Runtime` will use"),
272 );
273 }
274
275 #[cfg(unix)]
276 {
277 app = app.arg(
278 Arg::new("USER")
279 .long("user")
280 .short('a')
281 .num_args(1)
282 .action(ArgAction::Set)
283 .value_hint(ValueHint::Username)
284 .help("Run as another user"),
285 );
286 }
287
288 app
289}
290
291pub fn create(matches: &ArgMatches) -> ShadowsocksResult<(Runtime, impl Future<Output = ShadowsocksResult> + use<>)> {
293 let (config, runtime) = {
294 let config_path_opt = matches.get_one::<PathBuf>("CONFIG").cloned().or_else(|| {
295 if !matches.contains_id("SERVER_CONFIG") {
296 match crate::config::get_default_config_path("server.json") {
297 None => None,
298 Some(p) => {
299 println!("loading default config {p:?}");
300 Some(p)
301 }
302 }
303 } else {
304 None
305 }
306 });
307
308 let mut service_config = match config_path_opt {
309 Some(ref config_path) => ServiceConfig::load_from_file(config_path)
310 .map_err(|err| ShadowsocksError::LoadConfigFailure(format!("loading config {config_path:?}, {err}")))?,
311 None => ServiceConfig::default(),
312 };
313 service_config.set_options(matches);
314
315 #[cfg(feature = "logging")]
316 match service_config.log.config_path {
317 Some(ref path) => {
318 logging::init_with_file(path);
319 }
320 None => {
321 logging::init_with_config("ssserver", &service_config.log);
322 }
323 }
324
325 trace!("{:?}", service_config);
326
327 let mut config = match config_path_opt {
328 Some(cpath) => Config::load_from_file(&cpath, ConfigType::Server)
329 .map_err(|err| ShadowsocksError::LoadConfigFailure(format!("loading config {cpath:?}, {err}")))?,
330 None => Config::new(ConfigType::Server),
331 };
332
333 if let Some(svr_addr) = matches.get_one::<String>("SERVER_ADDR") {
334 let method = matches
335 .get_one::<String>("ENCRYPT_METHOD")
336 .map(|x| x.parse::<CipherKind>().expect("method"))
337 .expect("`method` is required");
338
339 let password = match matches.get_one::<String>("PASSWORD") {
340 Some(pwd) => read_variable_field_value(pwd).into(),
341 None => {
342 if method.is_none() {
344 String::new()
346 } else {
347 match crate::password::read_server_password(svr_addr) {
348 Ok(pwd) => pwd,
349 Err(..) => panic!("`password` is required for server {svr_addr}"),
350 }
351 }
352 }
353 };
354
355 let svr_addr = svr_addr.parse::<ServerAddr>().expect("server-addr");
356 let timeout = matches.get_one::<u64>("TIMEOUT").map(|x| Duration::from_secs(*x));
357
358 let mut sc = match ServerConfig::new(svr_addr, password, method) {
359 Ok(sc) => sc,
360 Err(err) => {
361 panic!("failed to create ServerConfig, error: {}", err);
362 }
363 };
364 if let Some(timeout) = timeout {
365 sc.set_timeout(timeout);
366 }
367
368 if let Some(p) = matches.get_one::<String>("PLUGIN").cloned() {
369 let plugin = PluginConfig {
370 plugin: p,
371 plugin_opts: matches.get_one::<String>("PLUGIN_OPT").cloned(),
372 plugin_args: Vec::new(),
373 plugin_mode: matches
374 .get_one::<String>("PLUGIN_MODE")
375 .map(|x| {
376 x.parse::<Mode>()
377 .expect("plugin-mode must be one of `tcp_only` (default), `udp_only` and `tcp_and_udp`")
378 })
379 .unwrap_or(Mode::TcpOnly),
380 };
381
382 sc.set_plugin(plugin);
383 }
384
385 sc.set_mode(Mode::TcpOnly);
387
388 if matches.get_flag("UDP_ONLY") {
389 sc.set_mode(Mode::UdpOnly);
390 }
391
392 if matches.get_flag("TCP_AND_UDP") {
393 sc.set_mode(Mode::TcpAndUdp);
394 }
395
396 config.server.push(ServerInstanceConfig::with_server_config(sc));
397 }
398
399 if matches.get_flag("TCP_NO_DELAY") {
400 config.no_delay = true;
401 }
402
403 if matches.get_flag("TCP_FAST_OPEN") {
404 config.fast_open = true;
405 }
406
407 if let Some(keep_alive) = matches.get_one::<u64>("TCP_KEEP_ALIVE") {
408 config.keep_alive = Some(Duration::from_secs(*keep_alive));
409 }
410
411 if matches.get_flag("TCP_MULTIPATH") {
412 config.mptcp = true;
413 }
414
415 #[cfg(any(target_os = "linux", target_os = "android"))]
416 if let Some(mark) = matches.get_one::<u32>("OUTBOUND_FWMARK") {
417 config.outbound_fwmark = Some(*mark);
418 }
419
420 #[cfg(target_os = "freebsd")]
421 if let Some(user_cookie) = matches.get_one::<u32>("OUTBOUND_USER_COOKIE") {
422 config.outbound_user_cookie = Some(*user_cookie);
423 }
424
425 if let Some(iface) = matches.get_one::<String>("OUTBOUND_BIND_INTERFACE").cloned() {
426 config.outbound_bind_interface = Some(iface);
427 }
428
429 if let Some(proxy_urls) = matches.get_many::<String>("OUTBOUND_PROXY") {
430 let mut proxies = Vec::new();
431 for proxy_url in proxy_urls {
432 let proxy = OutboundProxy::from_url(proxy_url)
433 .map_err(|e| ShadowsocksError::LoadConfigFailure(format!("invalid --outbound-proxy: {e}")))?;
434 proxies.push(proxy);
435 }
436 config.outbound_proxy = proxies;
437 }
438
439 if let Some(addr) = matches.get_one::<ManagerAddr>("MANAGER_ADDR").cloned() {
440 match config.manager {
441 Some(ref mut manager_config) => {
442 manager_config.addr = addr;
443 }
444 _ => {
445 config.manager = Some(ManagerConfig::new(addr));
446 }
447 }
448 }
449
450 #[cfg(all(unix, not(target_os = "android")))]
451 match matches.get_one::<u64>("NOFILE") {
452 Some(nofile) => config.nofile = Some(*nofile),
453 None => {
454 if config.nofile.is_none() {
455 crate::sys::adjust_nofile();
456 }
457 }
458 }
459
460 if let Some(acl_file) = matches.get_one::<String>("ACL") {
461 let acl = AccessControl::load_from_file(acl_file)
462 .map_err(|err| ShadowsocksError::LoadAclFailure(format!("loading ACL \"{acl_file}\", {err}")))?;
463 config.acl = Some(acl);
464 }
465
466 if let Some(dns) = matches.get_one::<String>("DNS") {
467 config.set_dns_formatted(dns).expect("dns");
468 }
469
470 if let Some(dns_cache_size) = matches.get_one::<usize>("DNS_CACHE_SIZE") {
471 config.dns_cache_size = Some(*dns_cache_size);
472 }
473
474 if matches.get_flag("IPV6_FIRST") {
475 config.ipv6_first = true;
476 }
477
478 if let Some(udp_timeout) = matches.get_one::<u64>("UDP_TIMEOUT") {
479 config.udp_timeout = Some(Duration::from_secs(*udp_timeout));
480 }
481
482 if let Some(udp_max_assoc) = matches.get_one::<usize>("UDP_MAX_ASSOCIATIONS") {
483 config.udp_max_associations = Some(*udp_max_assoc);
484 }
485
486 if let Some(bs) = matches.get_one::<u32>("INBOUND_SEND_BUFFER_SIZE") {
487 config.inbound_send_buffer_size = Some(*bs);
488 }
489 if let Some(bs) = matches.get_one::<u32>("INBOUND_RECV_BUFFER_SIZE") {
490 config.inbound_recv_buffer_size = Some(*bs);
491 }
492 if let Some(bs) = matches.get_one::<u32>("OUTBOUND_SEND_BUFFER_SIZE") {
493 config.outbound_send_buffer_size = Some(*bs);
494 }
495 if let Some(bs) = matches.get_one::<u32>("OUTBOUND_RECV_BUFFER_SIZE") {
496 config.outbound_recv_buffer_size = Some(*bs);
497 }
498
499 if let Some(bind_addr) = matches.get_one::<IpAddr>("OUTBOUND_BIND_ADDR") {
500 config.outbound_bind_addr = Some(*bind_addr);
501 }
502
503 if config.server.is_empty() {
506 return Err(ShadowsocksError::InsufficientParams(
507 "missing proxy servers, consider specifying it by \
508 --server-addr, --encrypt-method, --password command line option, \
509 or configuration file, check more details in https://shadowsocks.org/doc/configs.html"
510 .to_string(),
511 ));
512 }
513
514 config
515 .check_integrity()
516 .map_err(|err| ShadowsocksError::LoadConfigFailure(format!("config integrity check failed, {err}")))?;
517
518 #[cfg(unix)]
519 if matches.get_flag("DAEMONIZE") || matches.get_raw("DAEMONIZE_PID_PATH").is_some() {
520 use crate::daemonize;
521 daemonize::daemonize(matches.get_one::<PathBuf>("DAEMONIZE_PID_PATH"));
522 }
523
524 #[cfg(unix)]
525 if let Some(uname) = matches.get_one::<String>("USER") {
526 crate::sys::run_as_user(uname).map_err(|err| {
527 ShadowsocksError::InsufficientParams(format!("failed to change as user, error: {err}"))
528 })?;
529 }
530
531 info!("shadowsocks server {} build {}", crate::VERSION, crate::BUILD_TIME);
532
533 let mut builder = match service_config.runtime.mode {
534 RuntimeMode::SingleThread => Builder::new_current_thread(),
535 #[cfg(feature = "multi-threaded")]
536 RuntimeMode::MultiThread => {
537 let mut builder = Builder::new_multi_thread();
538 if let Some(worker_threads) = service_config.runtime.worker_count {
539 builder.worker_threads(worker_threads);
540 }
541
542 builder
543 }
544 };
545
546 let runtime = builder.enable_all().build().expect("create tokio Runtime");
547
548 (config, runtime)
549 };
550
551 let main_fut = async move {
552 let abort_signal = monitor::create_signal_monitor();
553 let server = run_server(config);
554
555 tokio::pin!(abort_signal);
556 tokio::pin!(server);
557
558 match future::select(server, abort_signal).await {
559 Either::Left((Ok(..), ..)) => Err(ShadowsocksError::ServerExitUnexpectedly(
561 "server exited unexpectedly".to_owned(),
562 )),
563 Either::Left((Err(err), ..)) => Err(ShadowsocksError::ServerAborted(format!("server aborted with {err}"))),
565 Either::Right(_) => Ok(()),
567 }
568 };
569
570 Ok((runtime, main_fut))
571}
572
573#[inline]
575pub fn main(matches: &ArgMatches) -> ExitCode {
576 match create(matches).and_then(|(runtime, main_fut)| runtime.block_on(main_fut)) {
577 Ok(()) => ExitCode::SUCCESS,
578 Err(err) => {
579 eprintln!("{err}");
580 err.exit_code().into()
581 }
582 }
583}
584
585#[cfg(test)]
586mod test {
587 use clap::Command;
588
589 #[test]
590 fn verify_server_command() {
591 let mut app = Command::new("shadowsocks")
592 .version(crate::VERSION)
593 .about("A fast tunnel proxy that helps you bypass firewalls. (https://shadowsocks.org)");
594 app = super::define_command_line_options(app);
595 app.debug_assert();
596 }
597}