Skip to main content

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, 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
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("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                    // deprecated for removal
189                    .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
291/// Create `Runtime` and `main` entry
292pub 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                    // NOTE: svr_addr should have been checked by crate::vparser
343                    if method.is_none() {
344                        // If method doesn't need a key (none, plain), then we can leave it empty
345                        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            // For historical reason, servers that are created from command-line have to be tcp_only.
386            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        // DONE READING options
504
505        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            // Server future resolved without an error. This should never happen.
560            Either::Left((Ok(..), ..)) => Err(ShadowsocksError::ServerExitUnexpectedly(
561                "server exited unexpectedly".to_owned(),
562            )),
563            // Server future resolved with error, which are listener errors in most cases
564            Either::Left((Err(err), ..)) => Err(ShadowsocksError::ServerAborted(format!("server aborted with {err}"))),
565            // The abort signal future resolved. Means we should just exit.
566            Either::Right(_) => Ok(()),
567        }
568    };
569
570    Ok((runtime, main_fut))
571}
572
573/// Program entrance `main`
574#[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}