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