shadowsocks_rust/service/
server.rs

1//! Server launchers
2
3use 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
32/// Defines command line options
33pub 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                    // deprecated for removal
182                    .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
284/// Create `Runtime` and `main` entry
285pub 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                    // NOTE: svr_addr should have been checked by crate::vparser
336                    if method.is_none() {
337                        // If method doesn't need a key (none, plain), then we can leave it empty
338                        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            // For historical reason, servers that are created from command-line have to be tcp_only.
379            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        // DONE READING options
487
488        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            // Server future resolved without an error. This should never happen.
543            Either::Left((Ok(..), ..)) => Err(ShadowsocksError::ServerExitUnexpectedly(
544                "server exited unexpectedly".to_owned(),
545            )),
546            // Server future resolved with error, which are listener errors in most cases
547            Either::Left((Err(err), ..)) => Err(ShadowsocksError::ServerAborted(format!("server aborted with {err}"))),
548            // The abort signal future resolved. Means we should just exit.
549            Either::Right(_) => Ok(()),
550        }
551    };
552
553    Ok((runtime, main_fut))
554}
555
556/// Program entrance `main`
557#[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}