mirror of
https://github.com/shadowsocks/shadowsocks-rust.git
synced 2026-11-06 08:14:07 +00:00
free the memory of clap after parsing options
- --nofile is now only existed in *nix build except Android
This commit is contained in:
+362
-350
@@ -36,412 +36,424 @@ mod common;
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
fn main() {
|
||||
let mut app = clap_app!(shadowsocks =>
|
||||
(version: VERSION)
|
||||
(about: "A fast tunnel proxy that helps you bypass firewalls.")
|
||||
let (config, runtime) = {
|
||||
let mut app = clap_app!(shadowsocks =>
|
||||
(version: VERSION)
|
||||
(about: "A fast tunnel proxy that helps you bypass firewalls.")
|
||||
|
||||
(@arg CONFIG: -c --config +takes_value required_unless_all(&["LOCAL_ADDR", "SERVER_CONFIG"]) "Shadowsocks configuration file (https://shadowsocks.org/en/config/quick-guide.html)")
|
||||
(@arg CONFIG: -c --config +takes_value required_unless_all(&["LOCAL_ADDR", "SERVER_CONFIG"]) "Shadowsocks configuration file (https://shadowsocks.org/en/config/quick-guide.html)")
|
||||
|
||||
(@arg LOCAL_ADDR: -b --("local-addr") +takes_value {validator::validate_server_addr} "Local address, listen only to this address if specified")
|
||||
(@arg UDP_ONLY: -u conflicts_with[TCP_AND_UDP] requires[LOCAL_ADDR] "Server mode UDP_ONLY")
|
||||
(@arg TCP_AND_UDP: -U requires[LOCAL_ADDR] "Server mode TCP_AND_UDP")
|
||||
(@arg PROTOCOL: --protocol +takes_value requires[LOCAL_ADDR] possible_values(ProtocolType::available_protocols()) "Protocol for communicating with clients (SOCKS5 by default)")
|
||||
(@arg UDP_BIND_ADDR: --("udp-bind-addr") +takes_value requires[LOCAL_ADDR] {validator::validate_server_addr} "UDP relay's bind address, default is the same as local-addr")
|
||||
(@arg LOCAL_ADDR: -b --("local-addr") +takes_value {validator::validate_server_addr} "Local address, listen only to this address if specified")
|
||||
(@arg UDP_ONLY: -u conflicts_with[TCP_AND_UDP] requires[LOCAL_ADDR] "Server mode UDP_ONLY")
|
||||
(@arg TCP_AND_UDP: -U requires[LOCAL_ADDR] "Server mode TCP_AND_UDP")
|
||||
(@arg PROTOCOL: --protocol +takes_value requires[LOCAL_ADDR] possible_values(ProtocolType::available_protocols()) "Protocol for communicating with clients (SOCKS5 by default)")
|
||||
(@arg UDP_BIND_ADDR: --("udp-bind-addr") +takes_value requires[LOCAL_ADDR] {validator::validate_server_addr} "UDP relay's bind address, default is the same as local-addr")
|
||||
|
||||
(@arg SERVER_ADDR: -s --("server-addr") +takes_value {validator::validate_server_addr} requires[PASSWORD ENCRYPT_METHOD] "Server address")
|
||||
(@arg PASSWORD: -k --password +takes_value requires[SERVER_ADDR] "Server's password")
|
||||
(@arg ENCRYPT_METHOD: -m --("encrypt-method") +takes_value requires[SERVER_ADDR] possible_values(available_ciphers()) "Server's encryption method")
|
||||
(@arg TIMEOUT: --timeout +takes_value {validator::validate_u64} requires[SERVER_ADDR] "Server's timeout seconds for TCP relay")
|
||||
(@arg SERVER_ADDR: -s --("server-addr") +takes_value {validator::validate_server_addr} requires[PASSWORD ENCRYPT_METHOD] "Server address")
|
||||
(@arg PASSWORD: -k --password +takes_value requires[SERVER_ADDR] "Server's password")
|
||||
(@arg ENCRYPT_METHOD: -m --("encrypt-method") +takes_value requires[SERVER_ADDR] possible_values(available_ciphers()) "Server's encryption method")
|
||||
(@arg TIMEOUT: --timeout +takes_value {validator::validate_u64} requires[SERVER_ADDR] "Server's timeout seconds for TCP relay")
|
||||
|
||||
(@arg PLUGIN: --plugin +takes_value requires[SERVER_ADDR] "SIP003 (https://shadowsocks.org/en/spec/Plugin.html) plugin")
|
||||
(@arg PLUGIN_OPT: --("plugin-opts") +takes_value requires[PLUGIN] "Set SIP003 plugin options")
|
||||
(@arg PLUGIN: --plugin +takes_value requires[SERVER_ADDR] "SIP003 (https://shadowsocks.org/en/spec/Plugin.html) plugin")
|
||||
(@arg PLUGIN_OPT: --("plugin-opts") +takes_value requires[PLUGIN] "Set SIP003 plugin options")
|
||||
|
||||
(@arg URL: --("server-url") +takes_value {validator::validate_server_url} "Server address in SIP002 (https://shadowsocks.org/en/spec/SIP002-URI-Scheme.html) URL")
|
||||
(@arg URL: --("server-url") +takes_value {validator::validate_server_url} "Server address in SIP002 (https://shadowsocks.org/en/spec/SIP002-URI-Scheme.html) URL")
|
||||
|
||||
(@group SERVER_CONFIG =>
|
||||
(@attributes +multiple arg[SERVER_ADDR URL]))
|
||||
(@group SERVER_CONFIG =>
|
||||
(@attributes +multiple arg[SERVER_ADDR URL]))
|
||||
|
||||
(@arg NOFILE: -n --nofile +takes_value "Set RLIMIT_NOFILE with both soft and hard limit (only for *nix systems)")
|
||||
(@arg ACL: --acl +takes_value "Path to ACL (Access Control List)")
|
||||
(@arg DNS: --dns +takes_value "DNS nameservers, formatted like [(tcp|udp)://]host[:port][,host[:port]]..., or unix:///path/to/dns, or predefined keys like \"google\", \"cloudflare\"")
|
||||
(@arg ACL: --acl +takes_value "Path to ACL (Access Control List)")
|
||||
(@arg DNS: --dns +takes_value "DNS nameservers, formatted like [(tcp|udp)://]host[:port][,host[:port]]..., or unix:///path/to/dns, or predefined keys like \"google\", \"cloudflare\"")
|
||||
|
||||
(@arg TCP_NO_DELAY: --("tcp-no-delay") !takes_value alias("no-delay") "Set TCP_NODELAY option for socket")
|
||||
(@arg TCP_NO_DELAY: --("tcp-no-delay") !takes_value alias("no-delay") "Set TCP_NODELAY option for socket")
|
||||
|
||||
(@arg UDP_TIMEOUT: --("udp-timeout") +takes_value {validator::validate_u64} "Timeout seconds for UDP relay")
|
||||
(@arg UDP_MAX_ASSOCIATIONS: --("udp-max-associations") +takes_value {validator::validate_u64} "Maximum associations to be kept simultaneously for UDP relay")
|
||||
(@arg UDP_TIMEOUT: --("udp-timeout") +takes_value {validator::validate_u64} "Timeout seconds for UDP relay")
|
||||
(@arg UDP_MAX_ASSOCIATIONS: --("udp-max-associations") +takes_value {validator::validate_u64} "Maximum associations to be kept simultaneously for UDP relay")
|
||||
|
||||
|
||||
(@arg INBOUND_SEND_BUFFER_SIZE: --("inbound-send-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_SNDBUF option")
|
||||
(@arg INBOUND_RECV_BUFFER_SIZE: --("inbound-recv-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_RCVBUF option")
|
||||
(@arg OUTBOUND_SEND_BUFFER_SIZE: --("outbound-send-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_SNDBUF option")
|
||||
(@arg OUTBOUND_RECV_BUFFER_SIZE: --("outbound-recv-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_RCVBUF option")
|
||||
);
|
||||
|
||||
// FIXME: -6 is not a identifier, so we cannot build it with clap_app!
|
||||
app = app.arg(
|
||||
Arg::with_name("IPV6_FIRST")
|
||||
.short("6")
|
||||
.help("Resolve hostname to IPv6 address first"),
|
||||
);
|
||||
|
||||
#[cfg(feature = "logging")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg VERBOSE: -v ... "Set log level")
|
||||
(@arg LOG_WITHOUT_TIME: --("log-without-time") "Log without datetime prefix")
|
||||
(@arg LOG_CONFIG: --("log-config") +takes_value "log4rs configuration file")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-tunnel")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg FORWARD_ADDR: -f --("forward-addr") +takes_value requires[LOCAL_ADDR] {validator::validate_address} required_if("PROTOCOL", "tunnel") "Forwarding data directly to this address (for tunnel)")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg OUTBOUND_BIND_INTERFACE: --("outbound-bind-interface") +takes_value "Set SO_BINDTODEVICE option for outbound socket")
|
||||
(@arg OUTBOUND_FWMARK: --("outbound-fwmark") +takes_value {validator::validate_u32} "Set SO_MARK option for outbound socket")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg OUTBOUND_BIND_INTERFACE: --("outbound-bind-interface") +takes_value "Set IP_BOUND_IF option for outbound socket")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-redir")]
|
||||
{
|
||||
let available_redir_types = RedirType::available_types();
|
||||
|
||||
if RedirType::tcp_default() != RedirType::NotSupported {
|
||||
app = clap_app!(@app (app)
|
||||
(@arg TCP_REDIR: --("tcp-redir") +takes_value requires[LOCAL_ADDR] possible_values(&available_redir_types) "TCP redir (transparent proxy) type")
|
||||
);
|
||||
}
|
||||
|
||||
if RedirType::udp_default() != RedirType::NotSupported {
|
||||
app = clap_app!(@app (app)
|
||||
(@arg UDP_REDIR: --("udp-redir") +takes_value requires[LOCAL_ADDR] possible_values(&available_redir_types) "UDP redir (transparent proxy) type")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg VPN_MODE: --vpn "Enable VPN mode (only for Android)")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-flow-stat")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg STAT_PATH: --("stat-path") +takes_value "Specify socket path (unix domain socket) for sending traffic statistic")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-dns")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg LOCAL_DNS_ADDR: --("local-dns-addr") +takes_value required_if("PROTOCOL", "dns") requires[LOCAL_ADDR] {validator::validate_name_server_addr} "Specify the address of local DNS server, send queries directly")
|
||||
(@arg REMOTE_DNS_ADDR: --("remote-dns-addr") +takes_value required_if("PROTOCOL", "dns") requires[LOCAL_ADDR] {validator::validate_address} "Specify the address of remote DNS server, send queries through shadowsocks' tunnel")
|
||||
|
||||
(@arg INBOUND_SEND_BUFFER_SIZE: --("inbound-send-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_SNDBUF option")
|
||||
(@arg INBOUND_RECV_BUFFER_SIZE: --("inbound-recv-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_RCVBUF option")
|
||||
(@arg OUTBOUND_SEND_BUFFER_SIZE: --("outbound-send-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_SNDBUF option")
|
||||
(@arg OUTBOUND_RECV_BUFFER_SIZE: --("outbound-recv-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_RCVBUF option")
|
||||
);
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
// FIXME: -6 is not a identifier, so we cannot build it with clap_app!
|
||||
app = app.arg(
|
||||
Arg::with_name("IPV6_FIRST")
|
||||
.short("6")
|
||||
.help("Resolve hostname to IPv6 address first"),
|
||||
);
|
||||
|
||||
#[cfg(feature = "logging")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg DNS_LOCAL_ADDR: --("dns-addr") +takes_value requires_all(&["LOCAL_ADDR", "REMOTE_DNS_ADDR"]) {validator::validate_server_addr} "DNS address, listen to this address if specified")
|
||||
(@arg VERBOSE: -v ... "Set log level")
|
||||
(@arg LOG_WITHOUT_TIME: --("log-without-time") "Log without datetime prefix")
|
||||
(@arg LOG_CONFIG: --("log-config") +takes_value "log4rs configuration file")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg DAEMONIZE: -d --("daemonize") "Daemonize")
|
||||
(@arg DAEMONIZE_PID_PATH: --("daemonize-pid") +takes_value "File path to store daemonized process's PID")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "multi-threaded")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg SINGLE_THREADED: --("single-threaded") "Run the program all in one thread")
|
||||
(@arg WORKER_THREADS: --("worker-threads") +takes_value {validator::validate_usize} "Sets the number of worker threads the `Runtime` will use")
|
||||
);
|
||||
}
|
||||
|
||||
let matches = app.get_matches();
|
||||
|
||||
#[cfg(feature = "logging")]
|
||||
match matches.value_of("LOG_CONFIG") {
|
||||
Some(path) => {
|
||||
logging::init_with_file(path);
|
||||
}
|
||||
None => {
|
||||
logging::init_with_config("sslocal", &matches);
|
||||
}
|
||||
}
|
||||
|
||||
let mut config = match matches.value_of("CONFIG") {
|
||||
Some(cpath) => match Config::load_from_file(cpath, ConfigType::Local) {
|
||||
Ok(cfg) => cfg,
|
||||
Err(err) => {
|
||||
panic!("loading config \"{}\", {}", cpath, err);
|
||||
}
|
||||
},
|
||||
None => Config::new(ConfigType::Local),
|
||||
};
|
||||
|
||||
if let Some(svr_addr) = matches.value_of("SERVER_ADDR") {
|
||||
let password = matches.value_of("PASSWORD").expect("password");
|
||||
let method = matches
|
||||
.value_of("ENCRYPT_METHOD")
|
||||
.expect("encrypt-method")
|
||||
.parse::<CipherKind>()
|
||||
.expect("encryption method");
|
||||
let svr_addr = svr_addr.parse::<ServerAddr>().expect("server-addr");
|
||||
|
||||
let timeout = matches
|
||||
.value_of("TIMEOUT")
|
||||
.map(|t| t.parse::<u64>().expect("timeout"))
|
||||
.map(Duration::from_secs);
|
||||
|
||||
let mut sc = ServerConfig::new(svr_addr, password.to_owned(), method);
|
||||
if let Some(timeout) = timeout {
|
||||
sc.set_timeout(timeout);
|
||||
}
|
||||
|
||||
if let Some(p) = matches.value_of("PLUGIN") {
|
||||
let plugin = PluginConfig {
|
||||
plugin: p.to_owned(),
|
||||
plugin_opts: matches.value_of("PLUGIN_OPT").map(ToOwned::to_owned),
|
||||
plugin_args: Vec::new(),
|
||||
};
|
||||
|
||||
sc.set_plugin(plugin);
|
||||
}
|
||||
|
||||
config.server.push(sc);
|
||||
}
|
||||
|
||||
if let Some(url) = matches.value_of("URL") {
|
||||
let svr_addr = url.parse::<ServerConfig>().expect("server SIP002 url");
|
||||
config.server.push(svr_addr);
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-flow-stat")]
|
||||
{
|
||||
if let Some(stat_path) = matches.value_of("STAT_PATH") {
|
||||
config.stat_path = Some(From::from(stat_path));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
if matches.is_present("VPN_MODE") {
|
||||
// A socket `protect_path` in CWD
|
||||
// Same as shadowsocks-libev's android.c
|
||||
config.outbound_vpn_protect_path = Some(From::from("protect_path"));
|
||||
}
|
||||
|
||||
if let Some(local_addr) = matches.value_of("LOCAL_ADDR") {
|
||||
let local_addr = local_addr.parse::<ServerAddr>().expect("local bind addr");
|
||||
|
||||
let protocol = match matches.value_of("PROTOCOL") {
|
||||
Some("socks") => ProtocolType::Socks,
|
||||
#[cfg(feature = "local-http")]
|
||||
Some("http") => ProtocolType::Http,
|
||||
#[cfg(feature = "local-tunnel")]
|
||||
Some("tunnel") => ProtocolType::Tunnel,
|
||||
#[cfg(feature = "local-redir")]
|
||||
Some("redir") => ProtocolType::Redir,
|
||||
#[cfg(feature = "local-dns")]
|
||||
Some("dns") => ProtocolType::Dns,
|
||||
Some(p) => panic!("not supported `protocol` \"{}\"", p),
|
||||
None => ProtocolType::Socks,
|
||||
};
|
||||
|
||||
let mut local_config = LocalConfig::new(local_addr, protocol);
|
||||
|
||||
if let Some(udp_bind_addr) = matches.value_of("UDP_BIND_ADDR") {
|
||||
local_config.udp_addr = Some(udp_bind_addr.parse::<ServerAddr>().expect("udp-bind-addr"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-tunnel")]
|
||||
if let Some(faddr) = matches.value_of("FORWARD_ADDR") {
|
||||
let addr = faddr.parse::<Address>().expect("forward-addr");
|
||||
local_config.forward_addr = Some(addr);
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg FORWARD_ADDR: -f --("forward-addr") +takes_value requires[LOCAL_ADDR] {validator::validate_address} required_if("PROTOCOL", "tunnel") "Forwarding data directly to this address (for tunnel)")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg NOFILE: -n --nofile +takes_value "Set RLIMIT_NOFILE with both soft and hard limit")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg OUTBOUND_BIND_INTERFACE: --("outbound-bind-interface") +takes_value "Set SO_BINDTODEVICE option for outbound socket")
|
||||
(@arg OUTBOUND_FWMARK: --("outbound-fwmark") +takes_value {validator::validate_u32} "Set SO_MARK option for outbound socket")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg OUTBOUND_BIND_INTERFACE: --("outbound-bind-interface") +takes_value "Set IP_BOUND_IF option for outbound socket")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-redir")]
|
||||
{
|
||||
if let Some(tcp_redir) = matches.value_of("TCP_REDIR") {
|
||||
local_config.tcp_redir = tcp_redir.parse::<RedirType>().expect("TCP redir type");
|
||||
let available_redir_types = RedirType::available_types();
|
||||
|
||||
if RedirType::tcp_default() != RedirType::NotSupported {
|
||||
app = clap_app!(@app (app)
|
||||
(@arg TCP_REDIR: --("tcp-redir") +takes_value requires[LOCAL_ADDR] possible_values(&available_redir_types) "TCP redir (transparent proxy) type")
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(udp_redir) = matches.value_of("UDP_REDIR") {
|
||||
local_config.udp_redir = udp_redir.parse::<RedirType>().expect("UDP redir type");
|
||||
if RedirType::udp_default() != RedirType::NotSupported {
|
||||
app = clap_app!(@app (app)
|
||||
(@arg UDP_REDIR: --("udp-redir") +takes_value requires[LOCAL_ADDR] possible_values(&available_redir_types) "UDP redir (transparent proxy) type")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg VPN_MODE: --vpn "Enable VPN mode (only for Android)")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-flow-stat")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg STAT_PATH: --("stat-path") +takes_value "Specify socket path (unix domain socket) for sending traffic statistic")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-dns")]
|
||||
{
|
||||
use shadowsocks_service::local::dns::NameServerAddr;
|
||||
app = clap_app!(@app (app)
|
||||
(@arg LOCAL_DNS_ADDR: --("local-dns-addr") +takes_value required_if("PROTOCOL", "dns") requires[LOCAL_ADDR] {validator::validate_name_server_addr} "Specify the address of local DNS server, send queries directly")
|
||||
(@arg REMOTE_DNS_ADDR: --("remote-dns-addr") +takes_value required_if("PROTOCOL", "dns") requires[LOCAL_ADDR] {validator::validate_address} "Specify the address of remote DNS server, send queries through shadowsocks' tunnel")
|
||||
|
||||
if let Some(local_dns_addr) = matches.value_of("LOCAL_DNS_ADDR") {
|
||||
let addr = local_dns_addr.parse::<NameServerAddr>().expect("local dns address");
|
||||
local_config.local_dns_addr = Some(addr);
|
||||
}
|
||||
);
|
||||
|
||||
if let Some(remote_dns_addr) = matches.value_of("REMOTE_DNS_ADDR") {
|
||||
let addr = remote_dns_addr.parse::<Address>().expect("remote dns address");
|
||||
local_config.remote_dns_addr = Some(addr);
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg DNS_LOCAL_ADDR: --("dns-addr") +takes_value requires_all(&["LOCAL_ADDR", "REMOTE_DNS_ADDR"]) {validator::validate_server_addr} "DNS address, listen to this address if specified")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "local-dns", target_os = "android"))]
|
||||
if protocol != ProtocolType::Dns {
|
||||
// Start a DNS local server binding to DNS_LOCAL_ADDR
|
||||
//
|
||||
// This is a special route only for shadowsocks-android
|
||||
if let Some(dns_relay_addr) = matches.value_of("DNS_LOCAL_ADDR") {
|
||||
let addr = dns_relay_addr.parse::<ServerAddr>().expect("dns relay address");
|
||||
#[cfg(unix)]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg DAEMONIZE: -d --("daemonize") "Daemonize")
|
||||
(@arg DAEMONIZE_PID_PATH: --("daemonize-pid") +takes_value "File path to store daemonized process's PID")
|
||||
);
|
||||
}
|
||||
|
||||
let mut local_dns_config = LocalConfig::new(addr, ProtocolType::Dns);
|
||||
#[cfg(feature = "multi-threaded")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg SINGLE_THREADED: --("single-threaded") "Run the program all in one thread")
|
||||
(@arg WORKER_THREADS: --("worker-threads") +takes_value {validator::validate_usize} "Sets the number of worker threads the `Runtime` will use")
|
||||
);
|
||||
}
|
||||
|
||||
// The `local_dns_addr` and `remote_dns_addr` are for this DNS server (for compatibility)
|
||||
local_dns_config.local_dns_addr = local_config.local_dns_addr.take();
|
||||
local_dns_config.remote_dns_addr = local_config.remote_dns_addr.take();
|
||||
let matches = app.get_matches();
|
||||
|
||||
config.local.push(local_dns_config);
|
||||
#[cfg(feature = "logging")]
|
||||
match matches.value_of("LOG_CONFIG") {
|
||||
Some(path) => {
|
||||
logging::init_with_file(path);
|
||||
}
|
||||
None => {
|
||||
logging::init_with_config("sslocal", &matches);
|
||||
}
|
||||
}
|
||||
|
||||
if matches.is_present("UDP_ONLY") {
|
||||
local_config.mode = Mode::UdpOnly;
|
||||
}
|
||||
|
||||
if matches.is_present("TCP_AND_UDP") {
|
||||
local_config.mode = Mode::TcpAndUdp;
|
||||
}
|
||||
|
||||
config.local.push(local_config);
|
||||
}
|
||||
|
||||
if matches.is_present("TCP_NO_DELAY") {
|
||||
config.no_delay = true;
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
if let Some(mark) = matches.value_of("OUTBOUND_FWMARK") {
|
||||
config.outbound_fwmark = Some(mark.parse::<u32>().expect("an unsigned integer for `outbound-fwmark`"));
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos", target_os = "ios"))]
|
||||
if let Some(iface) = matches.value_of("OUTBOUND_BIND_INTERFACE") {
|
||||
config.outbound_bind_interface = Some(From::from(iface.to_owned()));
|
||||
}
|
||||
|
||||
if let Some(nofile) = matches.value_of("NOFILE") {
|
||||
config.nofile = Some(nofile.parse::<u64>().expect("an unsigned integer for `nofile`"));
|
||||
}
|
||||
|
||||
if let Some(acl_file) = matches.value_of("ACL") {
|
||||
let acl = match AccessControl::load_from_file(acl_file) {
|
||||
Ok(acl) => acl,
|
||||
Err(err) => {
|
||||
panic!("loading ACL \"{}\", {}", acl_file, err);
|
||||
}
|
||||
let mut config = match matches.value_of("CONFIG") {
|
||||
Some(cpath) => match Config::load_from_file(cpath, ConfigType::Local) {
|
||||
Ok(cfg) => cfg,
|
||||
Err(err) => {
|
||||
panic!("loading config \"{}\", {}", cpath, err);
|
||||
}
|
||||
},
|
||||
None => Config::new(ConfigType::Local),
|
||||
};
|
||||
config.acl = Some(acl);
|
||||
}
|
||||
|
||||
if let Some(dns) = matches.value_of("DNS") {
|
||||
config.set_dns_formatted(dns).expect("dns");
|
||||
}
|
||||
if let Some(svr_addr) = matches.value_of("SERVER_ADDR") {
|
||||
let password = matches.value_of("PASSWORD").expect("password");
|
||||
let method = matches
|
||||
.value_of("ENCRYPT_METHOD")
|
||||
.expect("encrypt-method")
|
||||
.parse::<CipherKind>()
|
||||
.expect("encryption method");
|
||||
let svr_addr = svr_addr.parse::<ServerAddr>().expect("server-addr");
|
||||
|
||||
if matches.is_present("IPV6_FIRST") {
|
||||
config.ipv6_first = true;
|
||||
}
|
||||
let timeout = matches
|
||||
.value_of("TIMEOUT")
|
||||
.map(|t| t.parse::<u64>().expect("timeout"))
|
||||
.map(Duration::from_secs);
|
||||
|
||||
if let Some(udp_timeout) = matches.value_of("UDP_TIMEOUT") {
|
||||
config.udp_timeout = Some(Duration::from_secs(udp_timeout.parse::<u64>().expect("udp-timeout")));
|
||||
}
|
||||
let mut sc = ServerConfig::new(svr_addr, password.to_owned(), method);
|
||||
if let Some(timeout) = timeout {
|
||||
sc.set_timeout(timeout);
|
||||
}
|
||||
|
||||
if let Some(udp_max_assoc) = matches.value_of("UDP_MAX_ASSOCIATIONS") {
|
||||
config.udp_max_associations = Some(udp_max_assoc.parse::<usize>().expect("udp-max-associations"));
|
||||
}
|
||||
if let Some(p) = matches.value_of("PLUGIN") {
|
||||
let plugin = PluginConfig {
|
||||
plugin: p.to_owned(),
|
||||
plugin_opts: matches.value_of("PLUGIN_OPT").map(ToOwned::to_owned),
|
||||
plugin_args: Vec::new(),
|
||||
};
|
||||
|
||||
if let Some(bs) = matches.value_of("INBOUND_SEND_BUFFER_SIZE") {
|
||||
config.inbound_send_buffer_size = Some(bs.parse::<u32>().expect("inbound-send-buffer-size"));
|
||||
}
|
||||
if let Some(bs) = matches.value_of("INBOUND_RECV_BUFFER_SIZE") {
|
||||
config.inbound_recv_buffer_size = Some(bs.parse::<u32>().expect("inbound-recv-buffer-size"));
|
||||
}
|
||||
if let Some(bs) = matches.value_of("OUTBOUND_SEND_BUFFER_SIZE") {
|
||||
config.outbound_send_buffer_size = Some(bs.parse::<u32>().expect("outbound-send-buffer-size"));
|
||||
}
|
||||
if let Some(bs) = matches.value_of("OUTBOUND_RECV_BUFFER_SIZE") {
|
||||
config.outbound_recv_buffer_size = Some(bs.parse::<u32>().expect("outbound-recv-buffer-size"));
|
||||
}
|
||||
sc.set_plugin(plugin);
|
||||
}
|
||||
|
||||
// DONE READING options
|
||||
config.server.push(sc);
|
||||
}
|
||||
|
||||
if config.local.is_empty() {
|
||||
eprintln!(
|
||||
"missing `local_address`, consider specifying it by --local-addr command line option, \
|
||||
if let Some(url) = matches.value_of("URL") {
|
||||
let svr_addr = url.parse::<ServerConfig>().expect("server SIP002 url");
|
||||
config.server.push(svr_addr);
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-flow-stat")]
|
||||
{
|
||||
if let Some(stat_path) = matches.value_of("STAT_PATH") {
|
||||
config.stat_path = Some(From::from(stat_path));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
if matches.is_present("VPN_MODE") {
|
||||
// A socket `protect_path` in CWD
|
||||
// Same as shadowsocks-libev's android.c
|
||||
config.outbound_vpn_protect_path = Some(From::from("protect_path"));
|
||||
}
|
||||
|
||||
if let Some(local_addr) = matches.value_of("LOCAL_ADDR") {
|
||||
let local_addr = local_addr.parse::<ServerAddr>().expect("local bind addr");
|
||||
|
||||
let protocol = match matches.value_of("PROTOCOL") {
|
||||
Some("socks") => ProtocolType::Socks,
|
||||
#[cfg(feature = "local-http")]
|
||||
Some("http") => ProtocolType::Http,
|
||||
#[cfg(feature = "local-tunnel")]
|
||||
Some("tunnel") => ProtocolType::Tunnel,
|
||||
#[cfg(feature = "local-redir")]
|
||||
Some("redir") => ProtocolType::Redir,
|
||||
#[cfg(feature = "local-dns")]
|
||||
Some("dns") => ProtocolType::Dns,
|
||||
Some(p) => panic!("not supported `protocol` \"{}\"", p),
|
||||
None => ProtocolType::Socks,
|
||||
};
|
||||
|
||||
let mut local_config = LocalConfig::new(local_addr, protocol);
|
||||
|
||||
if let Some(udp_bind_addr) = matches.value_of("UDP_BIND_ADDR") {
|
||||
local_config.udp_addr = Some(udp_bind_addr.parse::<ServerAddr>().expect("udp-bind-addr"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-tunnel")]
|
||||
if let Some(faddr) = matches.value_of("FORWARD_ADDR") {
|
||||
let addr = faddr.parse::<Address>().expect("forward-addr");
|
||||
local_config.forward_addr = Some(addr);
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-redir")]
|
||||
{
|
||||
if let Some(tcp_redir) = matches.value_of("TCP_REDIR") {
|
||||
local_config.tcp_redir = tcp_redir.parse::<RedirType>().expect("TCP redir type");
|
||||
}
|
||||
|
||||
if let Some(udp_redir) = matches.value_of("UDP_REDIR") {
|
||||
local_config.udp_redir = udp_redir.parse::<RedirType>().expect("UDP redir type");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "local-dns")]
|
||||
{
|
||||
use shadowsocks_service::local::dns::NameServerAddr;
|
||||
|
||||
if let Some(local_dns_addr) = matches.value_of("LOCAL_DNS_ADDR") {
|
||||
let addr = local_dns_addr.parse::<NameServerAddr>().expect("local dns address");
|
||||
local_config.local_dns_addr = Some(addr);
|
||||
}
|
||||
|
||||
if let Some(remote_dns_addr) = matches.value_of("REMOTE_DNS_ADDR") {
|
||||
let addr = remote_dns_addr.parse::<Address>().expect("remote dns address");
|
||||
local_config.remote_dns_addr = Some(addr);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "local-dns", target_os = "android"))]
|
||||
if protocol != ProtocolType::Dns {
|
||||
// Start a DNS local server binding to DNS_LOCAL_ADDR
|
||||
//
|
||||
// This is a special route only for shadowsocks-android
|
||||
if let Some(dns_relay_addr) = matches.value_of("DNS_LOCAL_ADDR") {
|
||||
let addr = dns_relay_addr.parse::<ServerAddr>().expect("dns relay address");
|
||||
|
||||
let mut local_dns_config = LocalConfig::new(addr, ProtocolType::Dns);
|
||||
|
||||
// The `local_dns_addr` and `remote_dns_addr` are for this DNS server (for compatibility)
|
||||
local_dns_config.local_dns_addr = local_config.local_dns_addr.take();
|
||||
local_dns_config.remote_dns_addr = local_config.remote_dns_addr.take();
|
||||
|
||||
config.local.push(local_dns_config);
|
||||
}
|
||||
}
|
||||
|
||||
if matches.is_present("UDP_ONLY") {
|
||||
local_config.mode = Mode::UdpOnly;
|
||||
}
|
||||
|
||||
if matches.is_present("TCP_AND_UDP") {
|
||||
local_config.mode = Mode::TcpAndUdp;
|
||||
}
|
||||
|
||||
config.local.push(local_config);
|
||||
}
|
||||
|
||||
if matches.is_present("TCP_NO_DELAY") {
|
||||
config.no_delay = true;
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
if let Some(mark) = matches.value_of("OUTBOUND_FWMARK") {
|
||||
config.outbound_fwmark = Some(mark.parse::<u32>().expect("an unsigned integer for `outbound-fwmark`"));
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos", target_os = "ios"))]
|
||||
if let Some(iface) = matches.value_of("OUTBOUND_BIND_INTERFACE") {
|
||||
config.outbound_bind_interface = Some(From::from(iface.to_owned()));
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
if let Some(nofile) = matches.value_of("NOFILE") {
|
||||
config.nofile = Some(nofile.parse::<u64>().expect("an unsigned integer for `nofile`"));
|
||||
}
|
||||
|
||||
if let Some(acl_file) = matches.value_of("ACL") {
|
||||
let acl = match AccessControl::load_from_file(acl_file) {
|
||||
Ok(acl) => acl,
|
||||
Err(err) => {
|
||||
panic!("loading ACL \"{}\", {}", acl_file, err);
|
||||
}
|
||||
};
|
||||
config.acl = Some(acl);
|
||||
}
|
||||
|
||||
if let Some(dns) = matches.value_of("DNS") {
|
||||
config.set_dns_formatted(dns).expect("dns");
|
||||
}
|
||||
|
||||
if matches.is_present("IPV6_FIRST") {
|
||||
config.ipv6_first = true;
|
||||
}
|
||||
|
||||
if let Some(udp_timeout) = matches.value_of("UDP_TIMEOUT") {
|
||||
config.udp_timeout = Some(Duration::from_secs(udp_timeout.parse::<u64>().expect("udp-timeout")));
|
||||
}
|
||||
|
||||
if let Some(udp_max_assoc) = matches.value_of("UDP_MAX_ASSOCIATIONS") {
|
||||
config.udp_max_associations = Some(udp_max_assoc.parse::<usize>().expect("udp-max-associations"));
|
||||
}
|
||||
|
||||
if let Some(bs) = matches.value_of("INBOUND_SEND_BUFFER_SIZE") {
|
||||
config.inbound_send_buffer_size = Some(bs.parse::<u32>().expect("inbound-send-buffer-size"));
|
||||
}
|
||||
if let Some(bs) = matches.value_of("INBOUND_RECV_BUFFER_SIZE") {
|
||||
config.inbound_recv_buffer_size = Some(bs.parse::<u32>().expect("inbound-recv-buffer-size"));
|
||||
}
|
||||
if let Some(bs) = matches.value_of("OUTBOUND_SEND_BUFFER_SIZE") {
|
||||
config.outbound_send_buffer_size = Some(bs.parse::<u32>().expect("outbound-send-buffer-size"));
|
||||
}
|
||||
if let Some(bs) = matches.value_of("OUTBOUND_RECV_BUFFER_SIZE") {
|
||||
config.outbound_recv_buffer_size = Some(bs.parse::<u32>().expect("outbound-recv-buffer-size"));
|
||||
}
|
||||
|
||||
// DONE READING options
|
||||
|
||||
if config.local.is_empty() {
|
||||
eprintln!(
|
||||
"missing `local_address`, consider specifying it by --local-addr command line option, \
|
||||
or \"local_address\" and \"local_port\" in configuration file"
|
||||
);
|
||||
println!("{}", matches.usage());
|
||||
return;
|
||||
}
|
||||
);
|
||||
println!("{}", matches.usage());
|
||||
return;
|
||||
}
|
||||
|
||||
if config.server.is_empty() {
|
||||
eprintln!(
|
||||
"missing proxy servers, consider specifying it by \
|
||||
if config.server.is_empty() {
|
||||
eprintln!(
|
||||
"missing proxy servers, consider specifying it by \
|
||||
--server-addr, --encrypt-method, --password command line option, \
|
||||
or --server-url command line option, \
|
||||
or configuration file, check more details in https://shadowsocks.org/en/config/quick-guide.html"
|
||||
);
|
||||
println!("{}", matches.usage());
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(err) = config.check_integrity() {
|
||||
eprintln!("config integrity check failed, {}", err);
|
||||
println!("{}", matches.usage());
|
||||
return;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
if matches.is_present("DAEMONIZE") {
|
||||
use self::common::daemonize;
|
||||
daemonize::daemonize(matches.value_of("DAEMONIZE_PID_PATH"));
|
||||
}
|
||||
|
||||
info!("shadowsocks {}", VERSION);
|
||||
|
||||
#[cfg(feature = "multi-threaded")]
|
||||
let mut builder = if matches.is_present("SINGLE_THREADED") {
|
||||
Builder::new_current_thread()
|
||||
} else {
|
||||
let mut builder = Builder::new_multi_thread();
|
||||
if let Some(worker_threads) = matches.value_of("WORKER_THREADS") {
|
||||
builder.worker_threads(worker_threads.parse::<usize>().expect("worker-threads"));
|
||||
);
|
||||
println!("{}", matches.usage());
|
||||
return;
|
||||
}
|
||||
builder
|
||||
};
|
||||
#[cfg(not(feature = "multi-threaded"))]
|
||||
let mut builder = Builder::new_current_thread();
|
||||
|
||||
let runtime = builder.enable_all().build().expect("create tokio Runtime");
|
||||
if let Err(err) = config.check_integrity() {
|
||||
eprintln!("config integrity check failed, {}", err);
|
||||
println!("{}", matches.usage());
|
||||
return;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
if matches.is_present("DAEMONIZE") {
|
||||
use self::common::daemonize;
|
||||
daemonize::daemonize(matches.value_of("DAEMONIZE_PID_PATH"));
|
||||
}
|
||||
|
||||
info!("shadowsocks {}", VERSION);
|
||||
|
||||
#[cfg(feature = "multi-threaded")]
|
||||
let mut builder = if matches.is_present("SINGLE_THREADED") {
|
||||
Builder::new_current_thread()
|
||||
} else {
|
||||
let mut builder = Builder::new_multi_thread();
|
||||
if let Some(worker_threads) = matches.value_of("WORKER_THREADS") {
|
||||
builder.worker_threads(worker_threads.parse::<usize>().expect("worker-threads"));
|
||||
}
|
||||
builder
|
||||
};
|
||||
#[cfg(not(feature = "multi-threaded"))]
|
||||
let mut builder = Builder::new_current_thread();
|
||||
|
||||
let runtime = builder.enable_all().build().expect("create tokio Runtime");
|
||||
|
||||
(config, runtime)
|
||||
};
|
||||
|
||||
runtime.block_on(async move {
|
||||
let abort_signal = monitor::create_signal_monitor();
|
||||
let server = run_local(config);
|
||||
|
||||
+233
-221
@@ -34,246 +34,258 @@ mod common;
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
fn main() {
|
||||
#[allow(unused_mut)]
|
||||
let mut app = clap_app!(shadowsocks =>
|
||||
(version: VERSION)
|
||||
(about: "A fast tunnel proxy that helps you bypass firewalls.")
|
||||
let (config, runtime) = {
|
||||
#[allow(unused_mut)]
|
||||
let mut app = clap_app!(shadowsocks =>
|
||||
(version: VERSION)
|
||||
(about: "A fast tunnel proxy that helps you bypass firewalls.")
|
||||
|
||||
(@arg UDP_ONLY: -u conflicts_with[TCP_AND_UDP] "Server mode UDP_ONLY")
|
||||
(@arg TCP_AND_UDP: -U conflicts_with[UDP_ONLY] "Server mode TCP_AND_UDP")
|
||||
(@arg UDP_ONLY: -u conflicts_with[TCP_AND_UDP] "Server mode UDP_ONLY")
|
||||
(@arg TCP_AND_UDP: -U conflicts_with[UDP_ONLY] "Server mode TCP_AND_UDP")
|
||||
|
||||
(@arg CONFIG: -c --config +takes_value required_unless("MANAGER_ADDRESS")
|
||||
"Shadowsocks configuration file (https://shadowsocks.org/en/config/quick-guide.html), \
|
||||
the only required fields are \"manager_address\" and \"manager_port\". \
|
||||
Servers defined will be created when process is started.")
|
||||
(@arg CONFIG: -c --config +takes_value required_unless("MANAGER_ADDRESS")
|
||||
"Shadowsocks configuration file (https://shadowsocks.org/en/config/quick-guide.html), \
|
||||
the only required fields are \"manager_address\" and \"manager_port\". \
|
||||
Servers defined will be created when process is started.")
|
||||
|
||||
(@arg BIND_ADDR: -b --("bind-addr") +takes_value {validator::validate_ip_addr} "Bind address, outbound socket will bind this address")
|
||||
(@arg SERVER_HOST: -s --("server-host") +takes_value "Host name or IP address of your remote server")
|
||||
(@arg BIND_ADDR: -b --("bind-addr") +takes_value {validator::validate_ip_addr} "Bind address, outbound socket will bind this address")
|
||||
(@arg SERVER_HOST: -s --("server-host") +takes_value "Host name or IP address of your remote server")
|
||||
|
||||
|
||||
(@arg MANAGER_ADDRESS: --("manager-address") +takes_value {validator::validate_manager_addr} "ShadowSocks Manager (ssmgr) address, could be ip:port, domain:port or /path/to/unix.sock")
|
||||
(@arg ENCRYPT_METHOD: -m --("encrypt-method") +takes_value possible_values(available_ciphers()) "Default encryption method")
|
||||
(@arg TIMEOUT: --timeout +takes_value {validator::validate_u64} "Default timeout seconds for TCP relay")
|
||||
(@arg MANAGER_ADDRESS: --("manager-address") +takes_value {validator::validate_manager_addr} "ShadowSocks Manager (ssmgr) address, could be ip:port, domain:port or /path/to/unix.sock")
|
||||
(@arg ENCRYPT_METHOD: -m --("encrypt-method") +takes_value possible_values(available_ciphers()) "Default encryption method")
|
||||
(@arg TIMEOUT: --timeout +takes_value {validator::validate_u64} "Default timeout seconds for TCP relay")
|
||||
|
||||
(@arg NOFILE: -n --nofile +takes_value "Set RLIMIT_NOFILE with both soft and hard limit (only for *nix systems)")
|
||||
(@arg ACL: --acl +takes_value "Path to ACL (Access Control List)")
|
||||
(@arg DNS: --dns +takes_value "DNS nameservers, formatted like [(tcp|udp)://]host[:port][,host[:port]]..., or unix:///path/to/dns, or predefined keys like \"google\", \"cloudflare\"")
|
||||
(@arg ACL: --acl +takes_value "Path to ACL (Access Control List)")
|
||||
(@arg DNS: --dns +takes_value "DNS nameservers, formatted like [(tcp|udp)://]host[:port][,host[:port]]..., or unix:///path/to/dns, or predefined keys like \"google\", \"cloudflare\"")
|
||||
|
||||
(@arg TCP_NO_DELAY: --("tcp-no-delay") !takes_value alias("no-delay") "Set TCP_NODELAY option for socket")
|
||||
(@arg TCP_NO_DELAY: --("tcp-no-delay") !takes_value alias("no-delay") "Set TCP_NODELAY option for socket")
|
||||
|
||||
(@arg UDP_TIMEOUT: --("udp-timeout") +takes_value {validator::validate_u64} "Timeout seconds for UDP relay")
|
||||
(@arg UDP_MAX_ASSOCIATIONS: --("udp-max-associations") +takes_value {validator::validate_u64} "Maximum associations to be kept simultaneously for UDP relay")
|
||||
(@arg UDP_TIMEOUT: --("udp-timeout") +takes_value {validator::validate_u64} "Timeout seconds for UDP relay")
|
||||
(@arg UDP_MAX_ASSOCIATIONS: --("udp-max-associations") +takes_value {validator::validate_u64} "Maximum associations to be kept simultaneously for UDP relay")
|
||||
|
||||
(@arg INBOUND_SEND_BUFFER_SIZE: --("inbound-send-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_SNDBUF option")
|
||||
(@arg INBOUND_RECV_BUFFER_SIZE: --("inbound-recv-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_RCVBUF option")
|
||||
(@arg OUTBOUND_SEND_BUFFER_SIZE: --("outbound-send-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_SNDBUF option")
|
||||
(@arg OUTBOUND_RECV_BUFFER_SIZE: --("outbound-recv-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_RCVBUF option")
|
||||
);
|
||||
|
||||
#[cfg(feature = "logging")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg VERBOSE: -v ... "Set log level")
|
||||
(@arg LOG_WITHOUT_TIME: --("log-without-time") "Log without datetime prefix")
|
||||
(@arg LOG_CONFIG: --("log-config") +takes_value "log4rs configuration file")
|
||||
(@arg INBOUND_SEND_BUFFER_SIZE: --("inbound-send-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_SNDBUF option")
|
||||
(@arg INBOUND_RECV_BUFFER_SIZE: --("inbound-recv-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_RCVBUF option")
|
||||
(@arg OUTBOUND_SEND_BUFFER_SIZE: --("outbound-send-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_SNDBUF option")
|
||||
(@arg OUTBOUND_RECV_BUFFER_SIZE: --("outbound-recv-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_RCVBUF option")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg DAEMONIZE: -d --("daemonize") "Daemonize")
|
||||
(@arg DAEMONIZE_PID_PATH: --("daemonize-pid") +takes_value "File path to store daemonized process's PID")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg OUTBOUND_BIND_INTERFACE: --("outbound-bind-interface") +takes_value "Set SO_BINDTODEVICE option for outbound socket")
|
||||
(@arg OUTBOUND_FWMARK: --("outbound-fwmark") +takes_value {validator::validate_u32} "Set SO_MARK option for outbound socket")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg OUTBOUND_BIND_INTERFACE: --("outbound-bind-interface") +takes_value "Set IP_BOUND_IF option for outbound socket")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "multi-threaded")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg SINGLE_THREADED: --("single-threaded") "Run the program all in one thread")
|
||||
(@arg WORKER_THREADS: --("worker-threads") +takes_value {validator::validate_usize} "Sets the number of worker threads the `Runtime` will use")
|
||||
);
|
||||
}
|
||||
|
||||
let matches = app
|
||||
.arg(
|
||||
Arg::with_name("IPV6_FIRST")
|
||||
.short("6")
|
||||
.help("Resolve hostname to IPv6 address first"),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
#[cfg(feature = "logging")]
|
||||
match matches.value_of("LOG_CONFIG") {
|
||||
Some(path) => {
|
||||
logging::init_with_file(path);
|
||||
#[cfg(feature = "logging")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg VERBOSE: -v ... "Set log level")
|
||||
(@arg LOG_WITHOUT_TIME: --("log-without-time") "Log without datetime prefix")
|
||||
(@arg LOG_CONFIG: --("log-config") +takes_value "log4rs configuration file")
|
||||
);
|
||||
}
|
||||
None => {
|
||||
logging::init_with_config("ssmanager", &matches);
|
||||
}
|
||||
}
|
||||
|
||||
let mut config = match matches.value_of("CONFIG") {
|
||||
Some(cpath) => match Config::load_from_file(cpath, ConfigType::Manager) {
|
||||
Ok(cfg) => cfg,
|
||||
Err(err) => {
|
||||
panic!("loading config \"{}\", {}", cpath, err);
|
||||
#[cfg(unix)]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg DAEMONIZE: -d --("daemonize") "Daemonize")
|
||||
(@arg DAEMONIZE_PID_PATH: --("daemonize-pid") +takes_value "File path to store daemonized process's PID")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg NOFILE: -n --nofile +takes_value "Set RLIMIT_NOFILE with both soft and hard limit")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg OUTBOUND_BIND_INTERFACE: --("outbound-bind-interface") +takes_value "Set SO_BINDTODEVICE option for outbound socket")
|
||||
(@arg OUTBOUND_FWMARK: --("outbound-fwmark") +takes_value {validator::validate_u32} "Set SO_MARK option for outbound socket")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg OUTBOUND_BIND_INTERFACE: --("outbound-bind-interface") +takes_value "Set IP_BOUND_IF option for outbound socket")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "multi-threaded")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg SINGLE_THREADED: --("single-threaded") "Run the program all in one thread")
|
||||
(@arg WORKER_THREADS: --("worker-threads") +takes_value {validator::validate_usize} "Sets the number of worker threads the `Runtime` will use")
|
||||
);
|
||||
}
|
||||
|
||||
let matches = app
|
||||
.arg(
|
||||
Arg::with_name("IPV6_FIRST")
|
||||
.short("6")
|
||||
.help("Resolve hostname to IPv6 address first"),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
#[cfg(feature = "logging")]
|
||||
match matches.value_of("LOG_CONFIG") {
|
||||
Some(path) => {
|
||||
logging::init_with_file(path);
|
||||
}
|
||||
},
|
||||
None => Config::new(ConfigType::Manager),
|
||||
};
|
||||
|
||||
if let Some(bind_addr) = matches.value_of("BIND_ADDR") {
|
||||
let bind_addr = bind_addr.parse::<IpAddr>().expect("bind-addr");
|
||||
config.local_addr = Some(bind_addr);
|
||||
}
|
||||
|
||||
if matches.is_present("TCP_NO_DELAY") {
|
||||
config.no_delay = true;
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
if let Some(mark) = matches.value_of("OUTBOUND_FWMARK") {
|
||||
config.outbound_fwmark = Some(mark.parse::<u32>().expect("an unsigned integer for `outbound-fwmark`"));
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
if let Some(iface) = matches.value_of("OUTBOUND_BIND_INTERFACE") {
|
||||
config.outbound_bind_interface = Some(From::from(iface.to_owned()));
|
||||
}
|
||||
|
||||
if let Some(m) = matches.value_of("MANAGER_ADDRESS") {
|
||||
if let Some(ref mut manager_config) = config.manager {
|
||||
manager_config.addr = m.parse::<ManagerAddr>().expect("manager-address");
|
||||
} else {
|
||||
config.manager = Some(ManagerConfig::new(m.parse::<ManagerAddr>().expect("manager-address")));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref mut manager_config) = config.manager {
|
||||
if let Some(m) = matches.value_of("ENCRYPT_METHOD") {
|
||||
manager_config.method = Some(m.parse::<CipherKind>().expect("encrypt-method"));
|
||||
}
|
||||
|
||||
if let Some(t) = matches.value_of("TIMEOUT") {
|
||||
manager_config.timeout = Some(Duration::from_secs(t.parse::<u64>().expect("timeout")));
|
||||
}
|
||||
|
||||
if let Some(sh) = matches.value_of("SERVER_HOST") {
|
||||
manager_config.server_host = sh.parse::<ManagerServerHost>().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Overrides
|
||||
if matches.is_present("UDP_ONLY") {
|
||||
if let Some(ref mut m) = config.manager {
|
||||
m.mode = m.mode.merge(Mode::UdpOnly);
|
||||
}
|
||||
}
|
||||
|
||||
if matches.is_present("TCP_AND_UDP") {
|
||||
if let Some(ref mut m) = config.manager {
|
||||
m.mode = Mode::TcpAndUdp;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(nofile) = matches.value_of("NOFILE") {
|
||||
config.nofile = Some(nofile.parse::<u64>().expect("an unsigned integer for `nofile`"));
|
||||
}
|
||||
|
||||
if let Some(acl_file) = matches.value_of("ACL") {
|
||||
let acl = match AccessControl::load_from_file(acl_file) {
|
||||
Ok(acl) => acl,
|
||||
Err(err) => {
|
||||
panic!("loading ACL \"{}\", {}", acl_file, err);
|
||||
None => {
|
||||
logging::init_with_config("ssmanager", &matches);
|
||||
}
|
||||
}
|
||||
|
||||
let mut config = match matches.value_of("CONFIG") {
|
||||
Some(cpath) => match Config::load_from_file(cpath, ConfigType::Manager) {
|
||||
Ok(cfg) => cfg,
|
||||
Err(err) => {
|
||||
panic!("loading config \"{}\", {}", cpath, err);
|
||||
}
|
||||
},
|
||||
None => Config::new(ConfigType::Manager),
|
||||
};
|
||||
config.acl = Some(acl);
|
||||
}
|
||||
|
||||
if let Some(dns) = matches.value_of("DNS") {
|
||||
config.set_dns_formatted(dns).expect("dns");
|
||||
}
|
||||
|
||||
if matches.is_present("IPV6_FIRST") {
|
||||
config.ipv6_first = true;
|
||||
}
|
||||
|
||||
if let Some(udp_timeout) = matches.value_of("UDP_TIMEOUT") {
|
||||
config.udp_timeout = Some(Duration::from_secs(udp_timeout.parse::<u64>().expect("udp-timeout")));
|
||||
}
|
||||
|
||||
if let Some(udp_max_assoc) = matches.value_of("UDP_MAX_ASSOCIATIONS") {
|
||||
config.udp_max_associations = Some(udp_max_assoc.parse::<usize>().expect("udp-max-associations"));
|
||||
}
|
||||
|
||||
if let Some(bs) = matches.value_of("INBOUND_SEND_BUFFER_SIZE") {
|
||||
config.inbound_send_buffer_size = Some(bs.parse::<u32>().expect("inbound-send-buffer-size"));
|
||||
}
|
||||
if let Some(bs) = matches.value_of("INBOUND_RECV_BUFFER_SIZE") {
|
||||
config.inbound_recv_buffer_size = Some(bs.parse::<u32>().expect("inbound-recv-buffer-size"));
|
||||
}
|
||||
if let Some(bs) = matches.value_of("OUTBOUND_SEND_BUFFER_SIZE") {
|
||||
config.outbound_send_buffer_size = Some(bs.parse::<u32>().expect("outbound-send-buffer-size"));
|
||||
}
|
||||
if let Some(bs) = matches.value_of("OUTBOUND_RECV_BUFFER_SIZE") {
|
||||
config.outbound_recv_buffer_size = Some(bs.parse::<u32>().expect("outbound-recv-buffer-size"));
|
||||
}
|
||||
|
||||
// DONE reading options
|
||||
|
||||
if config.manager.is_none() {
|
||||
eprintln!(
|
||||
"missing `manager_address`, consider specifying it by --manager-address command line option, \
|
||||
or \"manager_address\" and \"manager_port\" keys in configuration file"
|
||||
);
|
||||
println!("{}", matches.usage());
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(err) = config.check_integrity() {
|
||||
eprintln!("config integrity check failed, {}", err);
|
||||
println!("{}", matches.usage());
|
||||
return;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
if matches.is_present("DAEMONIZE") {
|
||||
use self::common::daemonize;
|
||||
daemonize::daemonize(matches.value_of("DAEMONIZE_PID_PATH"));
|
||||
}
|
||||
|
||||
info!("shadowsocks {}", VERSION);
|
||||
|
||||
#[cfg(feature = "multi-threaded")]
|
||||
let mut builder = if matches.is_present("SINGLE_THREADED") {
|
||||
Builder::new_current_thread()
|
||||
} else {
|
||||
let mut builder = Builder::new_multi_thread();
|
||||
if let Some(worker_threads) = matches.value_of("WORKER_THREADS") {
|
||||
builder.worker_threads(worker_threads.parse::<usize>().expect("worker-threads"));
|
||||
if let Some(bind_addr) = matches.value_of("BIND_ADDR") {
|
||||
let bind_addr = bind_addr.parse::<IpAddr>().expect("bind-addr");
|
||||
config.local_addr = Some(bind_addr);
|
||||
}
|
||||
builder
|
||||
};
|
||||
#[cfg(not(feature = "multi-threaded"))]
|
||||
let mut builder = Builder::new_current_thread();
|
||||
|
||||
let runtime = builder.enable_all().build().expect("create tokio Runtime");
|
||||
if matches.is_present("TCP_NO_DELAY") {
|
||||
config.no_delay = true;
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
if let Some(mark) = matches.value_of("OUTBOUND_FWMARK") {
|
||||
config.outbound_fwmark = Some(mark.parse::<u32>().expect("an unsigned integer for `outbound-fwmark`"));
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
if let Some(iface) = matches.value_of("OUTBOUND_BIND_INTERFACE") {
|
||||
config.outbound_bind_interface = Some(From::from(iface.to_owned()));
|
||||
}
|
||||
|
||||
if let Some(m) = matches.value_of("MANAGER_ADDRESS") {
|
||||
if let Some(ref mut manager_config) = config.manager {
|
||||
manager_config.addr = m.parse::<ManagerAddr>().expect("manager-address");
|
||||
} else {
|
||||
config.manager = Some(ManagerConfig::new(m.parse::<ManagerAddr>().expect("manager-address")));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref mut manager_config) = config.manager {
|
||||
if let Some(m) = matches.value_of("ENCRYPT_METHOD") {
|
||||
manager_config.method = Some(m.parse::<CipherKind>().expect("encrypt-method"));
|
||||
}
|
||||
|
||||
if let Some(t) = matches.value_of("TIMEOUT") {
|
||||
manager_config.timeout = Some(Duration::from_secs(t.parse::<u64>().expect("timeout")));
|
||||
}
|
||||
|
||||
if let Some(sh) = matches.value_of("SERVER_HOST") {
|
||||
manager_config.server_host = sh.parse::<ManagerServerHost>().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Overrides
|
||||
if matches.is_present("UDP_ONLY") {
|
||||
if let Some(ref mut m) = config.manager {
|
||||
m.mode = m.mode.merge(Mode::UdpOnly);
|
||||
}
|
||||
}
|
||||
|
||||
if matches.is_present("TCP_AND_UDP") {
|
||||
if let Some(ref mut m) = config.manager {
|
||||
m.mode = Mode::TcpAndUdp;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
if let Some(nofile) = matches.value_of("NOFILE") {
|
||||
config.nofile = Some(nofile.parse::<u64>().expect("an unsigned integer for `nofile`"));
|
||||
}
|
||||
|
||||
if let Some(acl_file) = matches.value_of("ACL") {
|
||||
let acl = match AccessControl::load_from_file(acl_file) {
|
||||
Ok(acl) => acl,
|
||||
Err(err) => {
|
||||
panic!("loading ACL \"{}\", {}", acl_file, err);
|
||||
}
|
||||
};
|
||||
config.acl = Some(acl);
|
||||
}
|
||||
|
||||
if let Some(dns) = matches.value_of("DNS") {
|
||||
config.set_dns_formatted(dns).expect("dns");
|
||||
}
|
||||
|
||||
if matches.is_present("IPV6_FIRST") {
|
||||
config.ipv6_first = true;
|
||||
}
|
||||
|
||||
if let Some(udp_timeout) = matches.value_of("UDP_TIMEOUT") {
|
||||
config.udp_timeout = Some(Duration::from_secs(udp_timeout.parse::<u64>().expect("udp-timeout")));
|
||||
}
|
||||
|
||||
if let Some(udp_max_assoc) = matches.value_of("UDP_MAX_ASSOCIATIONS") {
|
||||
config.udp_max_associations = Some(udp_max_assoc.parse::<usize>().expect("udp-max-associations"));
|
||||
}
|
||||
|
||||
if let Some(bs) = matches.value_of("INBOUND_SEND_BUFFER_SIZE") {
|
||||
config.inbound_send_buffer_size = Some(bs.parse::<u32>().expect("inbound-send-buffer-size"));
|
||||
}
|
||||
if let Some(bs) = matches.value_of("INBOUND_RECV_BUFFER_SIZE") {
|
||||
config.inbound_recv_buffer_size = Some(bs.parse::<u32>().expect("inbound-recv-buffer-size"));
|
||||
}
|
||||
if let Some(bs) = matches.value_of("OUTBOUND_SEND_BUFFER_SIZE") {
|
||||
config.outbound_send_buffer_size = Some(bs.parse::<u32>().expect("outbound-send-buffer-size"));
|
||||
}
|
||||
if let Some(bs) = matches.value_of("OUTBOUND_RECV_BUFFER_SIZE") {
|
||||
config.outbound_recv_buffer_size = Some(bs.parse::<u32>().expect("outbound-recv-buffer-size"));
|
||||
}
|
||||
|
||||
// DONE reading options
|
||||
|
||||
if config.manager.is_none() {
|
||||
eprintln!(
|
||||
"missing `manager_address`, consider specifying it by --manager-address command line option, \
|
||||
or \"manager_address\" and \"manager_port\" keys in configuration file"
|
||||
);
|
||||
println!("{}", matches.usage());
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(err) = config.check_integrity() {
|
||||
eprintln!("config integrity check failed, {}", err);
|
||||
println!("{}", matches.usage());
|
||||
return;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
if matches.is_present("DAEMONIZE") {
|
||||
use self::common::daemonize;
|
||||
daemonize::daemonize(matches.value_of("DAEMONIZE_PID_PATH"));
|
||||
}
|
||||
|
||||
info!("shadowsocks {}", VERSION);
|
||||
|
||||
#[cfg(feature = "multi-threaded")]
|
||||
let mut builder = if matches.is_present("SINGLE_THREADED") {
|
||||
Builder::new_current_thread()
|
||||
} else {
|
||||
let mut builder = Builder::new_multi_thread();
|
||||
if let Some(worker_threads) = matches.value_of("WORKER_THREADS") {
|
||||
builder.worker_threads(worker_threads.parse::<usize>().expect("worker-threads"));
|
||||
}
|
||||
builder
|
||||
};
|
||||
#[cfg(not(feature = "multi-threaded"))]
|
||||
let mut builder = Builder::new_current_thread();
|
||||
|
||||
let runtime = builder.enable_all().build().expect("create tokio Runtime");
|
||||
|
||||
(config, runtime)
|
||||
};
|
||||
|
||||
runtime.block_on(async move {
|
||||
let abort_signal = monitor::create_signal_monitor();
|
||||
let server = run_manager(config);
|
||||
|
||||
+234
-222
@@ -35,255 +35,267 @@ mod common;
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
fn main() {
|
||||
#[allow(unused_mut)]
|
||||
let mut app = clap_app!(shadowsocks =>
|
||||
(version: VERSION)
|
||||
(about: "A fast tunnel proxy that helps you bypass firewalls.")
|
||||
let (config, runtime) = {
|
||||
#[allow(unused_mut)]
|
||||
let mut app = clap_app!(shadowsocks =>
|
||||
(version: VERSION)
|
||||
(about: "A fast tunnel proxy that helps you bypass firewalls.")
|
||||
|
||||
(@arg CONFIG: -c --config +takes_value required_unless("SERVER_ADDR") "Shadowsocks configuration file (https://shadowsocks.org/en/config/quick-guide.html)")
|
||||
(@arg CONFIG: -c --config +takes_value required_unless("SERVER_ADDR") "Shadowsocks configuration file (https://shadowsocks.org/en/config/quick-guide.html)")
|
||||
|
||||
(@arg BIND_ADDR: -b --("bind-addr") +takes_value {validator::validate_ip_addr} "Bind address, outbound socket will bind this address")
|
||||
(@arg BIND_ADDR: -b --("bind-addr") +takes_value {validator::validate_ip_addr} "Bind address, outbound socket will bind this address")
|
||||
|
||||
(@arg SERVER_ADDR: -s --("server-addr") +takes_value {validator::validate_server_addr} requires[PASSWORD ENCRYPT_METHOD] "Server address")
|
||||
(@arg PASSWORD: -k --password +takes_value requires[SERVER_ADDR] "Server's password")
|
||||
(@arg ENCRYPT_METHOD: -m --("encrypt-method") +takes_value requires[SERVER_ADDR] possible_values(available_ciphers()) "Server's encryption method")
|
||||
(@arg TIMEOUT: --timeout +takes_value {validator::validate_u64} requires[SERVER_ADDR] "Server's timeout seconds for TCP relay")
|
||||
(@arg UDP_ONLY: -u conflicts_with[TCP_AND_UDP] requires[SERVER_ADDR] "Server mode UDP_ONLY")
|
||||
(@arg TCP_AND_UDP: -U requires[SERVER_ADDR] "Server mode TCP_AND_UDP")
|
||||
(@arg SERVER_ADDR: -s --("server-addr") +takes_value {validator::validate_server_addr} requires[PASSWORD ENCRYPT_METHOD] "Server address")
|
||||
(@arg PASSWORD: -k --password +takes_value requires[SERVER_ADDR] "Server's password")
|
||||
(@arg ENCRYPT_METHOD: -m --("encrypt-method") +takes_value requires[SERVER_ADDR] possible_values(available_ciphers()) "Server's encryption method")
|
||||
(@arg TIMEOUT: --timeout +takes_value {validator::validate_u64} requires[SERVER_ADDR] "Server's timeout seconds for TCP relay")
|
||||
(@arg UDP_ONLY: -u conflicts_with[TCP_AND_UDP] requires[SERVER_ADDR] "Server mode UDP_ONLY")
|
||||
(@arg TCP_AND_UDP: -U requires[SERVER_ADDR] "Server mode TCP_AND_UDP")
|
||||
|
||||
(@arg PLUGIN: --plugin +takes_value requires[SERVER_ADDR] "SIP003 (https://shadowsocks.org/en/spec/Plugin.html) plugin")
|
||||
(@arg PLUGIN_OPT: --("plugin-opts") +takes_value requires[PLUGIN] "Set SIP003 plugin options")
|
||||
(@arg PLUGIN: --plugin +takes_value requires[SERVER_ADDR] "SIP003 (https://shadowsocks.org/en/spec/Plugin.html) plugin")
|
||||
(@arg PLUGIN_OPT: --("plugin-opts") +takes_value requires[PLUGIN] "Set SIP003 plugin options")
|
||||
|
||||
(@arg MANAGER_ADDRESS: --("manager-address") +takes_value "ShadowSocks Manager (ssmgr) address, could be \"IP:Port\", \"Domain:Port\" or \"/path/to/unix.sock\"")
|
||||
(@arg MANAGER_ADDRESS: --("manager-address") +takes_value "ShadowSocks Manager (ssmgr) address, could be \"IP:Port\", \"Domain:Port\" or \"/path/to/unix.sock\"")
|
||||
|
||||
(@arg NOFILE: -n --nofile +takes_value "Set RLIMIT_NOFILE with both soft and hard limit (only for *nix systems)")
|
||||
(@arg ACL: --acl +takes_value "Path to ACL (Access Control List)")
|
||||
(@arg DNS: --dns +takes_value "DNS nameservers, formatted like [(tcp|udp)://]host[:port][,host[:port]]..., or unix:///path/to/dns, or predefined keys like \"google\", \"cloudflare\"")
|
||||
(@arg ACL: --acl +takes_value "Path to ACL (Access Control List)")
|
||||
(@arg DNS: --dns +takes_value "DNS nameservers, formatted like [(tcp|udp)://]host[:port][,host[:port]]..., or unix:///path/to/dns, or predefined keys like \"google\", \"cloudflare\"")
|
||||
|
||||
(@arg TCP_NO_DELAY: --("tcp-no-delay") !takes_value alias("no-delay") "Set TCP_NODELAY option for socket")
|
||||
(@arg TCP_NO_DELAY: --("tcp-no-delay") !takes_value alias("no-delay") "Set TCP_NODELAY option for socket")
|
||||
|
||||
(@arg UDP_TIMEOUT: --("udp-timeout") +takes_value {validator::validate_u64} "Timeout seconds for UDP relay")
|
||||
(@arg UDP_MAX_ASSOCIATIONS: --("udp-max-associations") +takes_value {validator::validate_u64} "Maximum associations to be kept simultaneously for UDP relay")
|
||||
(@arg UDP_TIMEOUT: --("udp-timeout") +takes_value {validator::validate_u64} "Timeout seconds for UDP relay")
|
||||
(@arg UDP_MAX_ASSOCIATIONS: --("udp-max-associations") +takes_value {validator::validate_u64} "Maximum associations to be kept simultaneously for UDP relay")
|
||||
|
||||
(@arg INBOUND_SEND_BUFFER_SIZE: --("inbound-send-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_SNDBUF option")
|
||||
(@arg INBOUND_RECV_BUFFER_SIZE: --("inbound-recv-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_RCVBUF option")
|
||||
(@arg OUTBOUND_SEND_BUFFER_SIZE: --("outbound-send-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_SNDBUF option")
|
||||
(@arg OUTBOUND_RECV_BUFFER_SIZE: --("outbound-recv-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_RCVBUF option")
|
||||
);
|
||||
|
||||
#[cfg(feature = "logging")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg VERBOSE: -v ... "Set log level")
|
||||
(@arg LOG_WITHOUT_TIME: --("log-without-time") "Log without datetime prefix")
|
||||
(@arg LOG_CONFIG: --("log-config") +takes_value "log4rs configuration file")
|
||||
(@arg INBOUND_SEND_BUFFER_SIZE: --("inbound-send-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_SNDBUF option")
|
||||
(@arg INBOUND_RECV_BUFFER_SIZE: --("inbound-recv-buffer-size") +takes_value {validator::validate_u32} "Set inbound sockets' SO_RCVBUF option")
|
||||
(@arg OUTBOUND_SEND_BUFFER_SIZE: --("outbound-send-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_SNDBUF option")
|
||||
(@arg OUTBOUND_RECV_BUFFER_SIZE: --("outbound-recv-buffer-size") +takes_value {validator::validate_u32} "Set outbound sockets' SO_RCVBUF option")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg DAEMONIZE: -d --("daemonize") "Daemonize")
|
||||
(@arg DAEMONIZE_PID_PATH: --("daemonize-pid") +takes_value "File path to store daemonized process's PID")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg OUTBOUND_BIND_INTERFACE: --("outbound-bind-interface") +takes_value "Set SO_BINDTODEVICE option for outbound socket")
|
||||
(@arg OUTBOUND_FWMARK: --("outbound-fwmark") +takes_value {validator::validate_u32} "Set SO_MARK option for outbound socket")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg OUTBOUND_BIND_INTERFACE: --("outbound-bind-interface") +takes_value "Set IP_BOUND_IF option for outbound socket")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "multi-threaded")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg SINGLE_THREADED: --("single-threaded") "Run the program all in one thread")
|
||||
(@arg WORKER_THREADS: --("worker-threads") +takes_value {validator::validate_usize} "Sets the number of worker threads the `Runtime` will use")
|
||||
);
|
||||
}
|
||||
|
||||
let matches = app
|
||||
.arg(
|
||||
Arg::with_name("IPV6_FIRST")
|
||||
.short("6")
|
||||
.help("Resolve hostname to IPv6 address first"),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
#[cfg(feature = "logging")]
|
||||
match matches.value_of("LOG_CONFIG") {
|
||||
Some(path) => {
|
||||
logging::init_with_file(path);
|
||||
#[cfg(feature = "logging")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg VERBOSE: -v ... "Set log level")
|
||||
(@arg LOG_WITHOUT_TIME: --("log-without-time") "Log without datetime prefix")
|
||||
(@arg LOG_CONFIG: --("log-config") +takes_value "log4rs configuration file")
|
||||
);
|
||||
}
|
||||
None => {
|
||||
logging::init_with_config("ssserver", &matches);
|
||||
}
|
||||
}
|
||||
|
||||
let mut config = match matches.value_of("CONFIG") {
|
||||
Some(cpath) => match Config::load_from_file(cpath, ConfigType::Server) {
|
||||
Ok(cfg) => cfg,
|
||||
Err(err) => {
|
||||
panic!("loading config \"{}\", {}", cpath, err);
|
||||
#[cfg(unix)]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg DAEMONIZE: -d --("daemonize") "Daemonize")
|
||||
(@arg DAEMONIZE_PID_PATH: --("daemonize-pid") +takes_value "File path to store daemonized process's PID")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg NOFILE: -n --nofile +takes_value "Set RLIMIT_NOFILE with both soft and hard limit")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg OUTBOUND_BIND_INTERFACE: --("outbound-bind-interface") +takes_value "Set SO_BINDTODEVICE option for outbound socket")
|
||||
(@arg OUTBOUND_FWMARK: --("outbound-fwmark") +takes_value {validator::validate_u32} "Set SO_MARK option for outbound socket")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg OUTBOUND_BIND_INTERFACE: --("outbound-bind-interface") +takes_value "Set IP_BOUND_IF option for outbound socket")
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "multi-threaded")]
|
||||
{
|
||||
app = clap_app!(@app (app)
|
||||
(@arg SINGLE_THREADED: --("single-threaded") "Run the program all in one thread")
|
||||
(@arg WORKER_THREADS: --("worker-threads") +takes_value {validator::validate_usize} "Sets the number of worker threads the `Runtime` will use")
|
||||
);
|
||||
}
|
||||
|
||||
let matches = app
|
||||
.arg(
|
||||
Arg::with_name("IPV6_FIRST")
|
||||
.short("6")
|
||||
.help("Resolve hostname to IPv6 address first"),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
#[cfg(feature = "logging")]
|
||||
match matches.value_of("LOG_CONFIG") {
|
||||
Some(path) => {
|
||||
logging::init_with_file(path);
|
||||
}
|
||||
},
|
||||
None => Config::new(ConfigType::Server),
|
||||
};
|
||||
|
||||
if let Some(svr_addr) = matches.value_of("SERVER_ADDR") {
|
||||
let password = matches.value_of("PASSWORD").expect("password");
|
||||
let method = matches
|
||||
.value_of("ENCRYPT_METHOD")
|
||||
.expect("encrypt-method")
|
||||
.parse::<CipherKind>()
|
||||
.expect("encryption method");
|
||||
let svr_addr = svr_addr.parse::<ServerAddr>().expect("server-addr");
|
||||
let timeout = matches
|
||||
.value_of("TIMEOUT")
|
||||
.map(|t| t.parse::<u64>().expect("timeout"))
|
||||
.map(Duration::from_secs);
|
||||
|
||||
let mut sc = ServerConfig::new(svr_addr, password.to_owned(), method);
|
||||
if let Some(timeout) = timeout {
|
||||
sc.set_timeout(timeout);
|
||||
}
|
||||
|
||||
if let Some(p) = matches.value_of("PLUGIN") {
|
||||
let plugin = PluginConfig {
|
||||
plugin: p.to_owned(),
|
||||
plugin_opts: matches.value_of("PLUGIN_OPT").map(ToOwned::to_owned),
|
||||
plugin_args: Vec::new(),
|
||||
};
|
||||
|
||||
sc.set_plugin(plugin);
|
||||
}
|
||||
|
||||
if matches.is_present("UDP_ONLY") {
|
||||
sc.set_mode(sc.mode().merge(Mode::UdpOnly));
|
||||
}
|
||||
|
||||
if matches.is_present("TCP_AND_UDP") {
|
||||
sc.set_mode(Mode::TcpAndUdp);
|
||||
}
|
||||
|
||||
config.server.push(sc);
|
||||
}
|
||||
|
||||
if let Some(bind_addr) = matches.value_of("BIND_ADDR") {
|
||||
let bind_addr = bind_addr.parse::<IpAddr>().expect("bind-addr");
|
||||
config.local_addr = Some(bind_addr);
|
||||
}
|
||||
|
||||
if matches.is_present("TCP_NO_DELAY") {
|
||||
config.no_delay = true;
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
if let Some(mark) = matches.value_of("OUTBOUND_FWMARK") {
|
||||
config.outbound_fwmark = Some(mark.parse::<u32>().expect("an unsigned integer for `outbound-fwmark`"));
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
if let Some(iface) = matches.value_of("OUTBOUND_BIND_INTERFACE") {
|
||||
config.outbound_bind_interface = Some(From::from(iface.to_owned()));
|
||||
}
|
||||
|
||||
if let Some(m) = matches.value_of("MANAGER_ADDRESS") {
|
||||
config.manager = Some(ManagerConfig::new(m.parse::<ManagerAddr>().expect("manager address")));
|
||||
}
|
||||
|
||||
if let Some(nofile) = matches.value_of("NOFILE") {
|
||||
config.nofile = Some(nofile.parse::<u64>().expect("an unsigned integer for `nofile`"));
|
||||
}
|
||||
|
||||
if let Some(acl_file) = matches.value_of("ACL") {
|
||||
let acl = match AccessControl::load_from_file(acl_file) {
|
||||
Ok(acl) => acl,
|
||||
Err(err) => {
|
||||
panic!("loading ACL \"{}\", {}", acl_file, err);
|
||||
None => {
|
||||
logging::init_with_config("ssserver", &matches);
|
||||
}
|
||||
}
|
||||
|
||||
let mut config = match matches.value_of("CONFIG") {
|
||||
Some(cpath) => match Config::load_from_file(cpath, ConfigType::Server) {
|
||||
Ok(cfg) => cfg,
|
||||
Err(err) => {
|
||||
panic!("loading config \"{}\", {}", cpath, err);
|
||||
}
|
||||
},
|
||||
None => Config::new(ConfigType::Server),
|
||||
};
|
||||
config.acl = Some(acl);
|
||||
}
|
||||
|
||||
if let Some(dns) = matches.value_of("DNS") {
|
||||
config.set_dns_formatted(dns).expect("dns");
|
||||
}
|
||||
if let Some(svr_addr) = matches.value_of("SERVER_ADDR") {
|
||||
let password = matches.value_of("PASSWORD").expect("password");
|
||||
let method = matches
|
||||
.value_of("ENCRYPT_METHOD")
|
||||
.expect("encrypt-method")
|
||||
.parse::<CipherKind>()
|
||||
.expect("encryption method");
|
||||
let svr_addr = svr_addr.parse::<ServerAddr>().expect("server-addr");
|
||||
let timeout = matches
|
||||
.value_of("TIMEOUT")
|
||||
.map(|t| t.parse::<u64>().expect("timeout"))
|
||||
.map(Duration::from_secs);
|
||||
|
||||
if matches.is_present("IPV6_FIRST") {
|
||||
config.ipv6_first = true;
|
||||
}
|
||||
let mut sc = ServerConfig::new(svr_addr, password.to_owned(), method);
|
||||
if let Some(timeout) = timeout {
|
||||
sc.set_timeout(timeout);
|
||||
}
|
||||
|
||||
if let Some(udp_timeout) = matches.value_of("UDP_TIMEOUT") {
|
||||
config.udp_timeout = Some(Duration::from_secs(udp_timeout.parse::<u64>().expect("udp-timeout")));
|
||||
}
|
||||
if let Some(p) = matches.value_of("PLUGIN") {
|
||||
let plugin = PluginConfig {
|
||||
plugin: p.to_owned(),
|
||||
plugin_opts: matches.value_of("PLUGIN_OPT").map(ToOwned::to_owned),
|
||||
plugin_args: Vec::new(),
|
||||
};
|
||||
|
||||
if let Some(udp_max_assoc) = matches.value_of("UDP_MAX_ASSOCIATIONS") {
|
||||
config.udp_max_associations = Some(udp_max_assoc.parse::<usize>().expect("udp-max-associations"));
|
||||
}
|
||||
sc.set_plugin(plugin);
|
||||
}
|
||||
|
||||
if let Some(bs) = matches.value_of("INBOUND_SEND_BUFFER_SIZE") {
|
||||
config.inbound_send_buffer_size = Some(bs.parse::<u32>().expect("inbound-send-buffer-size"));
|
||||
}
|
||||
if let Some(bs) = matches.value_of("INBOUND_RECV_BUFFER_SIZE") {
|
||||
config.inbound_recv_buffer_size = Some(bs.parse::<u32>().expect("inbound-recv-buffer-size"));
|
||||
}
|
||||
if let Some(bs) = matches.value_of("OUTBOUND_SEND_BUFFER_SIZE") {
|
||||
config.outbound_send_buffer_size = Some(bs.parse::<u32>().expect("outbound-send-buffer-size"));
|
||||
}
|
||||
if let Some(bs) = matches.value_of("OUTBOUND_RECV_BUFFER_SIZE") {
|
||||
config.outbound_recv_buffer_size = Some(bs.parse::<u32>().expect("outbound-recv-buffer-size"));
|
||||
}
|
||||
if matches.is_present("UDP_ONLY") {
|
||||
sc.set_mode(sc.mode().merge(Mode::UdpOnly));
|
||||
}
|
||||
|
||||
// DONE READING options
|
||||
if matches.is_present("TCP_AND_UDP") {
|
||||
sc.set_mode(Mode::TcpAndUdp);
|
||||
}
|
||||
|
||||
if config.server.is_empty() {
|
||||
eprintln!(
|
||||
"missing proxy servers, consider specifying it by \
|
||||
config.server.push(sc);
|
||||
}
|
||||
|
||||
if let Some(bind_addr) = matches.value_of("BIND_ADDR") {
|
||||
let bind_addr = bind_addr.parse::<IpAddr>().expect("bind-addr");
|
||||
config.local_addr = Some(bind_addr);
|
||||
}
|
||||
|
||||
if matches.is_present("TCP_NO_DELAY") {
|
||||
config.no_delay = true;
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
if let Some(mark) = matches.value_of("OUTBOUND_FWMARK") {
|
||||
config.outbound_fwmark = Some(mark.parse::<u32>().expect("an unsigned integer for `outbound-fwmark`"));
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
if let Some(iface) = matches.value_of("OUTBOUND_BIND_INTERFACE") {
|
||||
config.outbound_bind_interface = Some(From::from(iface.to_owned()));
|
||||
}
|
||||
|
||||
if let Some(m) = matches.value_of("MANAGER_ADDRESS") {
|
||||
config.manager = Some(ManagerConfig::new(m.parse::<ManagerAddr>().expect("manager address")));
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
if let Some(nofile) = matches.value_of("NOFILE") {
|
||||
config.nofile = Some(nofile.parse::<u64>().expect("an unsigned integer for `nofile`"));
|
||||
}
|
||||
|
||||
if let Some(acl_file) = matches.value_of("ACL") {
|
||||
let acl = match AccessControl::load_from_file(acl_file) {
|
||||
Ok(acl) => acl,
|
||||
Err(err) => {
|
||||
panic!("loading ACL \"{}\", {}", acl_file, err);
|
||||
}
|
||||
};
|
||||
config.acl = Some(acl);
|
||||
}
|
||||
|
||||
if let Some(dns) = matches.value_of("DNS") {
|
||||
config.set_dns_formatted(dns).expect("dns");
|
||||
}
|
||||
|
||||
if matches.is_present("IPV6_FIRST") {
|
||||
config.ipv6_first = true;
|
||||
}
|
||||
|
||||
if let Some(udp_timeout) = matches.value_of("UDP_TIMEOUT") {
|
||||
config.udp_timeout = Some(Duration::from_secs(udp_timeout.parse::<u64>().expect("udp-timeout")));
|
||||
}
|
||||
|
||||
if let Some(udp_max_assoc) = matches.value_of("UDP_MAX_ASSOCIATIONS") {
|
||||
config.udp_max_associations = Some(udp_max_assoc.parse::<usize>().expect("udp-max-associations"));
|
||||
}
|
||||
|
||||
if let Some(bs) = matches.value_of("INBOUND_SEND_BUFFER_SIZE") {
|
||||
config.inbound_send_buffer_size = Some(bs.parse::<u32>().expect("inbound-send-buffer-size"));
|
||||
}
|
||||
if let Some(bs) = matches.value_of("INBOUND_RECV_BUFFER_SIZE") {
|
||||
config.inbound_recv_buffer_size = Some(bs.parse::<u32>().expect("inbound-recv-buffer-size"));
|
||||
}
|
||||
if let Some(bs) = matches.value_of("OUTBOUND_SEND_BUFFER_SIZE") {
|
||||
config.outbound_send_buffer_size = Some(bs.parse::<u32>().expect("outbound-send-buffer-size"));
|
||||
}
|
||||
if let Some(bs) = matches.value_of("OUTBOUND_RECV_BUFFER_SIZE") {
|
||||
config.outbound_recv_buffer_size = Some(bs.parse::<u32>().expect("outbound-recv-buffer-size"));
|
||||
}
|
||||
|
||||
// DONE READING options
|
||||
|
||||
if config.server.is_empty() {
|
||||
eprintln!(
|
||||
"missing proxy servers, consider specifying it by \
|
||||
--server-addr, --encrypt-method, --password command line option, \
|
||||
or configuration file, check more details in https://shadowsocks.org/en/config/quick-guide.html"
|
||||
);
|
||||
println!("{}", matches.usage());
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(err) = config.check_integrity() {
|
||||
eprintln!("config integrity check failed, {}", err);
|
||||
println!("{}", matches.usage());
|
||||
return;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
if matches.is_present("DAEMONIZE") {
|
||||
use self::common::daemonize;
|
||||
daemonize::daemonize(matches.value_of("DAEMONIZE_PID_PATH"));
|
||||
}
|
||||
|
||||
info!("shadowsocks {}", VERSION);
|
||||
|
||||
#[cfg(feature = "multi-threaded")]
|
||||
let mut builder = if matches.is_present("SINGLE_THREADED") {
|
||||
Builder::new_current_thread()
|
||||
} else {
|
||||
let mut builder = Builder::new_multi_thread();
|
||||
if let Some(worker_threads) = matches.value_of("WORKER_THREADS") {
|
||||
builder.worker_threads(worker_threads.parse::<usize>().expect("worker-threads"));
|
||||
);
|
||||
println!("{}", matches.usage());
|
||||
return;
|
||||
}
|
||||
builder
|
||||
};
|
||||
#[cfg(not(feature = "multi-threaded"))]
|
||||
let mut builder = Builder::new_current_thread();
|
||||
|
||||
let runtime = builder.enable_all().build().expect("create tokio Runtime");
|
||||
if let Err(err) = config.check_integrity() {
|
||||
eprintln!("config integrity check failed, {}", err);
|
||||
println!("{}", matches.usage());
|
||||
return;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
if matches.is_present("DAEMONIZE") {
|
||||
use self::common::daemonize;
|
||||
daemonize::daemonize(matches.value_of("DAEMONIZE_PID_PATH"));
|
||||
}
|
||||
|
||||
info!("shadowsocks {}", VERSION);
|
||||
|
||||
#[cfg(feature = "multi-threaded")]
|
||||
let mut builder = if matches.is_present("SINGLE_THREADED") {
|
||||
Builder::new_current_thread()
|
||||
} else {
|
||||
let mut builder = Builder::new_multi_thread();
|
||||
if let Some(worker_threads) = matches.value_of("WORKER_THREADS") {
|
||||
builder.worker_threads(worker_threads.parse::<usize>().expect("worker-threads"));
|
||||
}
|
||||
builder
|
||||
};
|
||||
#[cfg(not(feature = "multi-threaded"))]
|
||||
let mut builder = Builder::new_current_thread();
|
||||
|
||||
let runtime = builder.enable_all().build().expect("create tokio Runtime");
|
||||
|
||||
(config, runtime)
|
||||
};
|
||||
|
||||
runtime.block_on(async move {
|
||||
let abort_signal = monitor::create_signal_monitor();
|
||||
let server = run_server(config);
|
||||
|
||||
@@ -123,6 +123,7 @@ struct SSConfig {
|
||||
mode: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
no_delay: Option<bool>,
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
nofile: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -718,6 +719,7 @@ pub struct Config {
|
||||
/// Set `TCP_NODELAY` socket option
|
||||
pub no_delay: bool,
|
||||
/// `RLIMIT_NOFILE` option for *nix systems
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
pub nofile: Option<u64>,
|
||||
|
||||
/// Set `SO_MARK` socket option for outbound sockets
|
||||
@@ -829,6 +831,7 @@ impl Config {
|
||||
ipv6_first: false,
|
||||
|
||||
no_delay: false,
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
nofile: None,
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
@@ -1282,7 +1285,10 @@ impl Config {
|
||||
nconfig.udp_max_associations = config.udp_max_associations;
|
||||
|
||||
// RLIMIT_NOFILE
|
||||
nconfig.nofile = config.nofile;
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
{
|
||||
nconfig.nofile = config.nofile;
|
||||
}
|
||||
|
||||
// Uses IPv6 first
|
||||
if let Some(f) = config.ipv6_first {
|
||||
@@ -1774,7 +1780,10 @@ impl fmt::Display for Config {
|
||||
|
||||
jconf.udp_max_associations = self.udp_max_associations;
|
||||
|
||||
jconf.nofile = self.nofile;
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
{
|
||||
jconf.nofile = self.nofile;
|
||||
}
|
||||
|
||||
if self.ipv6_first {
|
||||
jconf.ipv6_first = Some(self.ipv6_first);
|
||||
|
||||
@@ -58,7 +58,7 @@ pub async fn run(mut config: Config) -> io::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
if let Some(nofile) = config.nofile {
|
||||
use crate::sys::set_nofile;
|
||||
if let Err(err) = set_nofile(nofile) {
|
||||
|
||||
@@ -22,7 +22,7 @@ pub async fn run(config: Config) -> io::Result<()> {
|
||||
|
||||
trace!("{:?}", config);
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
if let Some(nofile) = config.nofile {
|
||||
use crate::sys::set_nofile;
|
||||
if let Err(err) = set_nofile(nofile) {
|
||||
|
||||
@@ -34,7 +34,7 @@ pub async fn run(config: Config) -> io::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
if let Some(nofile) = config.nofile {
|
||||
use crate::sys::set_nofile;
|
||||
if let Err(err) = set_nofile(nofile) {
|
||||
|
||||
Reference in New Issue
Block a user