diff --git a/Cargo.lock b/Cargo.lock index 0c9cd84cd180..384c263f9a04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -674,6 +674,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + [[package]] name = "enum-as-inner" version = "0.6.0" @@ -1064,6 +1073,19 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.57" @@ -1399,6 +1421,12 @@ dependencies = [ "libmimalloc-sys", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -1883,6 +1911,43 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +[[package]] +name = "reqwest" +version = "0.11.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "resolv-conf" version = "0.7.0" @@ -2317,6 +2382,7 @@ dependencies = [ "num_cpus", "qrcode", "rand", + "reqwest", "rpassword", "rpmalloc", "serde", @@ -3025,6 +3091,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.87" diff --git a/Cargo.toml b/Cargo.toml index c59e2112ce89..272739253b0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -164,6 +164,7 @@ tokio = { version = "1", features = ["rt", "signal"] } num_cpus = "1.15" ipnet = { version = "2.7", optional = true } +reqwest = { version = "0.11", features = ["blocking"] } mimalloc = { version = "0.1", default-features = false, optional = true } tcmalloc = { version = "0.3", optional = true } diff --git a/bin/ssurl.rs b/bin/ssurl.rs index 2118a8744fcb..f5443018ba00 100644 --- a/bin/ssurl.rs +++ b/bin/ssurl.rs @@ -1,4 +1,4 @@ -//! SIP002 URL Scheme +//! SIP002 and SIP008 URL Schemes //! //! SS-URI = "ss://" userinfo "@" hostname ":" port [ "/" ] [ "?" plugin ] [ "#" tag ] //! userinfo = websafe-base64-encode-utf8(method ":" password) @@ -72,6 +72,26 @@ fn decode(encoded: &str, need_qrcode: bool) { } } +fn decode_outline(remote: &str, need_qrcode: bool) { + // Protect from using http and other non-ssconf links in reqwest call + if !remote.starts_with("ssconf") { + println!("Incorrect link format"); + return; + } + + let url = remote.replace("ssconf", "https"); + let svrconfig = ServerConfig::from_url(reqwest::blocking::get(url).unwrap().text().unwrap().as_str()).unwrap(); + + let mut config = Config::new(ConfigType::Server); + config.server.push(ServerInstanceConfig::with_server_config(svrconfig)); + + println!("{config}"); + + if need_qrcode { + print_qrcode(remote); + } +} + fn main() { let app = Command::new("ssurl") .version(VERSION) @@ -83,7 +103,7 @@ fn main() { .action(ArgAction::Set) .value_hint(ValueHint::FilePath) .conflicts_with("DECODE_CONFIG_PATH") - .required_unless_present("DECODE_CONFIG_PATH") + .required_unless_present_any(["DECODE_CONFIG_PATH", "OUTLINE_CONFIG_URL"]) .help("Encode the server configuration in the provided JSON file"), ) .arg( @@ -92,8 +112,16 @@ fn main() { .long("decode") .action(ArgAction::Set) .value_hint(ValueHint::FilePath) - .required_unless_present("ENCODE_CONFIG_PATH") - .help("Decode the server configuration from the provide ShadowSocks URL"), + .required_unless_present_any(["ENCODE_CONFIG_PATH", "OUTLINE_CONFIG_URL"]) + .help("Decode the server configuration from the provided ShadowSocks URL"), + ) + .arg( + Arg::new("OUTLINE_CONFIG_URL") + .short('o') + .long("outline") + .value_hint(ValueHint::Url) + .required_unless_present_any(["ENCODE_CONFIG_PATH", "DECODE_CONFIG_PATH"]) + .help("Fetch and decode config from ssconf URL used by Outline"), ) .arg( Arg::new("QRCODE") @@ -110,6 +138,8 @@ fn main() { encode(file, need_qrcode); } else if let Some(encoded) = matches.get_one::("DECODE_CONFIG_PATH") { decode(encoded, need_qrcode); + } else if let Some(remote) = matches.get_one::("OUTLINE_CONFIG_URL") { + decode_outline(remote, need_qrcode); } else { println!("Use -h for more detail"); }