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