default configuration file (config.json) for Windows, Linux, macOS

- $XDG_CONFIG_PATH/shadowsocks-rust/config.json
- $HOME/.config/shadowsocks-rust/config.json
- {FOLDERID_RoamingAppData}\shadowsocks\shadowsocks-rust\config\config.json
- $HOME/Library/Application Support/org.shadowsocks.shadowsocks-rust/config.json

fixes #688
This commit is contained in:
zonyitoo
2021-11-25 15:14:07 +08:00
parent 05d1b0715e
commit fa3a2a83d2
8 changed files with 138 additions and 18 deletions

58
Cargo.lock generated
View File

@@ -299,6 +299,35 @@ dependencies = [
"generic-array",
]
[[package]]
name = "directories"
version = "4.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "dtoa"
version = "0.4.8"
@@ -1291,6 +1320,16 @@ dependencies = [
"bitflags",
]
[[package]]
name = "redox_users"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
dependencies = [
"getrandom",
"redox_syscall 0.2.10",
]
[[package]]
name = "regex"
version = "1.5.4"
@@ -1524,9 +1563,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.71"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063bf466a64011ac24040a49009724ee60a57da1b437617ceb32e53ad61bfb19"
checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527"
dependencies = [
"itoa",
"ryu",
@@ -1624,6 +1663,7 @@ dependencies = [
"cfg-if",
"clap",
"daemonize",
"directories",
"env_logger",
"exitcode",
"futures",
@@ -1638,6 +1678,7 @@ dependencies = [
"snmalloc-rs",
"tcmalloc",
"tokio",
"xdg",
]
[[package]]
@@ -1774,9 +1815,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.81"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
dependencies = [
"proc-macro2",
"quote",
@@ -2438,6 +2479,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "xdg"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a23fe958c70412687039c86f578938b4a0bb50ec788e96bce4d6ab00ddd5803"
dependencies = [
"dirs",
]
[[package]]
name = "yaml-rust"
version = "0.4.5"

View File

@@ -128,6 +128,8 @@ cfg-if = "1"
qrcode = { version = "0.12", default-features = false }
exitcode = "1"
build-time = "0.1"
directories = "4.0"
xdg = "2.4"
futures = "0.3"
tokio = { version = "1", features = ["rt", "signal"] }

View File

@@ -49,6 +49,8 @@ Related Projects:
* `local-dns` - Allow using dns protocol for `sslocal`, serves as a DNS server proxying queries to local or remote DNS servers by ACL rules
* `local-tun` - [TUN](https://en.wikipedia.org/wiki/TUN/TAP) interface support for `sslocal`
* `stream-cipher` - Enable deprecated stream ciphers. WARN: stream ciphers are UNSAFE!
* `aead-cipher-extra` - Enable non-standard AEAD ciphers

41
bin/common/config.rs Normal file
View File

@@ -0,0 +1,41 @@
use directories::ProjectDirs;
use std::path::{Path, PathBuf};
/// Default configuration file path
pub fn get_default_config_path() -> Option<PathBuf> {
// System standard directories
if let Some(project_dirs) = ProjectDirs::from("org", "shadowsocks", "shadowsocks-rust") {
// Linux: $XDG_CONFIG_HOME/shadowsocks-rust/config.json
// $HOME/.config/shadowsocks-rust/config.json
// macOS: $HOME/Library/Application Support/org.shadowsocks.shadowsocks-rust/config.json
// Windows: {FOLDERID_RoamingAppData}/shadowsocks/shadowsocks-rust/config/config.json
let mut config_path = project_dirs.config_dir().to_path_buf();
config_path.push("config.json");
if config_path.exists() {
return Some(config_path);
}
}
// UNIX systems, XDG Base Directory
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
#[cfg(unix)]
if let Ok(base_directories) = xdg::BaseDirectories::with_prefix("shadowsocks-rust") {
// $XDG_CONFIG_HOME/shadowsocks-rust/config.json
// for dir in $XDG_CONFIG_DIRS; $dir/shadowsocks-rust/config.json
if let Some(config_path) = base_directories.find_config_file("config.json") {
return Some(config_path);
}
}
// UNIX global configuration file
if cfg!(unix) {
let global_config_path = Path::new("/etc/shadowsocks-rust/config.json");
if global_config_path.exists() {
return Some(global_config_path.to_path_buf());
}
}
None
}

View File

@@ -7,6 +7,7 @@ pub mod daemonize;
pub mod logging;
pub mod monitor;
pub mod validator;
pub mod config;
pub const EXIT_CODE_SERVER_EXIT_UNEXPECTEDLY: i32 = exitcode::SOFTWARE;
pub const EXIT_CODE_SERVER_ABORTED: i32 = exitcode::SOFTWARE;

View File

@@ -42,7 +42,7 @@ fn main() {
(version: VERSION)
(about: "A fast tunnel proxy that helps you bypass firewalls.")
(@arg CONFIG: -c --config +takes_value required_unless("SERVER_CONFIG") "Shadowsocks configuration file (https://shadowsocks.org/en/config/quick-guide.html)")
(@arg CONFIG: -c --config +takes_value "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")
@@ -207,11 +207,19 @@ fn main() {
}
}
let mut config = match matches.value_of("CONFIG") {
Some(cpath) => match Config::load_from_file(cpath, ConfigType::Local) {
let config_path_opt = matches.value_of("CONFIG").map(|c| PathBuf::from(c)).or_else(|| {
if !matches.is_present("SERVER_CONFIG") {
common::config::get_default_config_path()
} else {
None
}
});
let mut config = match config_path_opt {
Some(cpath) => match Config::load_from_file(&cpath, ConfigType::Local) {
Ok(cfg) => cfg,
Err(err) => {
eprintln!("loading config \"{}\", {}", cpath, err);
eprintln!("loading config \"{}\", {}", cpath.display(), err);
process::exit(common::EXIT_CODE_LOAD_CONFIG_FAILURE);
}
},

View File

@@ -7,7 +7,7 @@
//! *It should be notice that the extended configuration file is not suitable for the server
//! side.*
use std::{net::IpAddr, process, time::Duration};
use std::{net::IpAddr, path::PathBuf, process, time::Duration};
use clap::{clap_app, Arg};
use futures::future::{self, Either};
@@ -44,7 +44,7 @@ fn main() {
(@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_ADDR")
(@arg CONFIG: -c --config +takes_value
"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.")
@@ -136,11 +136,19 @@ fn main() {
}
}
let mut config = match matches.value_of("CONFIG") {
Some(cpath) => match Config::load_from_file(cpath, ConfigType::Manager) {
let config_path_opt = matches.value_of("CONFIG").map(|c| PathBuf::from(c)).or_else(|| {
if !matches.is_present("MANAGER_ADDR") {
common::config::get_default_config_path()
} else {
None
}
});
let mut config = match config_path_opt {
Some(cpath) => match Config::load_from_file(&cpath, ConfigType::Manager) {
Ok(cfg) => cfg,
Err(err) => {
eprintln!("loading config \"{}\", {}", cpath, err);
eprintln!("loading config \"{}\", {}", cpath.display(), err);
process::exit(common::EXIT_CODE_LOAD_CONFIG_FAILURE);
}
},

View File

@@ -7,7 +7,7 @@
//! *It should be notice that the extended configuration file is not suitable for the server
//! side.*
use std::{net::IpAddr, process, time::Duration};
use std::{net::IpAddr, path::PathBuf, process, time::Duration};
use clap::{clap_app, Arg};
use futures::future::{self, Either};
@@ -41,7 +41,7 @@ fn main() {
(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 "Shadowsocks configuration file (https://shadowsocks.org/en/config/quick-guide.html)")
(@arg OUTBOUND_BIND_ADDR: -b --("outbound-bind-addr") +takes_value alias("bind-addr") {validator::validate_ip_addr} "Bind address, outbound socket will bind this address")
(@arg OUTBOUND_BIND_INTERFACE: --("outbound-bind-interface") +takes_value "Set SO_BINDTODEVICE / IP_BOUND_IF / IP_UNICAST_IF option for outbound socket")
@@ -131,11 +131,19 @@ fn main() {
}
}
let mut config = match matches.value_of("CONFIG") {
Some(cpath) => match Config::load_from_file(cpath, ConfigType::Server) {
let config_path_opt = matches.value_of("CONFIG").map(|c| PathBuf::from(c)).or_else(|| {
if !matches.is_present("SERVER_ADDR") {
common::config::get_default_config_path()
} else {
None
}
});
let mut config = match config_path_opt {
Some(cpath) => match Config::load_from_file(&cpath, ConfigType::Server) {
Ok(cfg) => cfg,
Err(err) => {
eprintln!("loading config \"{}\", {}", cpath, err);
eprintln!("loading config \"{}\", {}", cpath.display(), err);
process::exit(common::EXIT_CODE_LOAD_CONFIG_FAILURE);
}
},