diff --git a/Cargo.lock b/Cargo.lock index 334cadf..edfa879 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,6 +59,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bytes" version = "1.10.1" @@ -275,6 +281,7 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" name = "v2parser" version = "0.1.1" dependencies = [ + "base64", "clap", "http", "querystring", diff --git a/Cargo.toml b/Cargo.toml index b2a35bc..1247e7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +base64 = "0.22.1" clap = { version = "4.4.6", features = ["derive"] } http = "1.3.1" querystring = "1.1.0" diff --git a/src/config_models/mod.rs b/src/config_models/mod.rs index 62f4012..d277953 100644 --- a/src/config_models/mod.rs +++ b/src/config_models/mod.rs @@ -6,6 +6,7 @@ pub struct VlessUser { pub encryption: Option, pub flow: Option, pub level: Option, + pub security: Option, } #[derive(Serialize, Deserialize)] @@ -186,6 +187,7 @@ pub struct Config { #[allow(non_snake_case)] pub struct RawData { pub security: Option, + pub vnext_security: Option, pub sni: Option, pub fp: Option, pub pbk: Option, diff --git a/src/parser/vless/data.rs b/src/parser/vless/data.rs index 5c71ba1..42b01ee 100644 --- a/src/parser/vless/data.rs +++ b/src/parser/vless/data.rs @@ -35,6 +35,7 @@ pub fn get_data(uri: &str) -> RawData { key: get_parameter_value(&query, "key"), mode: url_decode(get_parameter_value(&query, "mode")), service_name: url_decode(get_parameter_value(&query, "serviceName")), + vnext_security: None, slpn: get_parameter_value(&query, "slpn"), spx: url_decode(get_parameter_value(&query, "spx")), extra: url_decode(get_parameter_value(&query, "extra")), diff --git a/src/parser/vless/mod.rs b/src/parser/vless/mod.rs index aee3620..0ec40a1 100644 --- a/src/parser/vless/mod.rs +++ b/src/parser/vless/mod.rs @@ -12,6 +12,7 @@ pub fn create_outbound_settings(data: &RawData) -> OutboundSettings { flow: data.flow.clone(), encryption: Some(data.encryption.clone().unwrap_or(String::from("none"))), level: Some(0), + security: None, }]), }], }); diff --git a/src/parser/vmess/data.rs b/src/parser/vmess/data.rs index 7314ca7..1877f14 100644 --- a/src/parser/vmess/data.rs +++ b/src/parser/vmess/data.rs @@ -1,11 +1,70 @@ use crate::config_models::RawData; use crate::parser::vmess::models::{self, VmessAddress}; use crate::utils::{get_parameter_value, url_decode}; +use base64::{engine::general_purpose, Engine}; use http::Uri; +use serde_json::Value; pub fn get_data(uri: &str) -> RawData { let data = uri.split_once("vmess://").unwrap().1; + + return match general_purpose::STANDARD.decode(data) { + Ok(decoded) => get_raw_data_from_base64(&decoded), + Err(_) => get_raw_data_from_uri(data), + }; +} + +fn get_raw_data_from_base64(decoded_base64: &Vec) -> RawData { + let json_str = std::str::from_utf8(decoded_base64).unwrap(); + let json = serde_json::from_str::(json_str).unwrap(); + + return RawData { + uuid: get_str_field(&json, "id"), + port: get_str_field(&json, "port") + .and_then(|s| Some(s.parse::().expect("port is not a number"))), + address: get_str_field(&json, "add"), + alpn: url_decode(get_str_field(&json, "alpn")), + path: url_decode(get_str_field(&json, "path")), + authority: url_decode(get_str_field(&json, "host")), + // this probably does not exist in vmess uri + pbk: url_decode(get_str_field(&json, "pbk")), + security: get_str_field(&json, "tls"), + vnext_security: get_str_field(&json, "scy"), + // this probably does not exist in vmess uri + sid: url_decode(get_str_field(&json, "pbk")), + // this probably does not exist in vmess uri + flow: url_decode(get_str_field(&json, "flow")), + sni: get_str_field(&json, "sni"), + fp: url_decode(get_str_field(&json, "fp")), + r#type: url_decode(get_str_field(&json, "net")), + encryption: None, + header_type: url_decode(get_str_field(&json, "type")), + host: url_decode(get_str_field(&json, "host")), + // this probably does not exist in vmess uri + seed: url_decode(get_str_field(&json, "seed")), + quic_security: None, + key: None, + mode: url_decode(get_str_field(&json, "type")), + service_name: url_decode(get_str_field(&json, "path")), + // this probably does not exist in vmess uri + slpn: url_decode(get_str_field(&json, "slpn")), + // this probably does not exist in vmess uri + spx: url_decode(get_str_field(&json, "spx")), + // this probably does not exist in vmess uri + extra: url_decode(get_str_field(&json, "extra")), + // this probably does not exist in vmess uri + allowInsecure: None, + }; +} + +fn get_str_field(json: &Value, field: &str) -> Option { + return json.get(field).and_then(|v| v.as_str()).map(String::from); +} + +fn get_raw_data_from_uri(uri: &str) -> RawData { + let data = uri.split_once("vmess://").unwrap().1; let query_and_name = uri.split_once("?").unwrap().1; + let raw_query = query_and_name .split_once("#") .unwrap_or((query_and_name, "")) @@ -35,6 +94,7 @@ pub fn get_data(uri: &str) -> RawData { key: get_parameter_value(&query, "key"), mode: url_decode(get_parameter_value(&query, "mode")), service_name: url_decode(get_parameter_value(&query, "serviceName")), + vnext_security: None, slpn: get_parameter_value(&query, "slpn"), spx: url_decode(get_parameter_value(&query, "spx")), extra: url_decode(get_parameter_value(&query, "extra")), @@ -45,7 +105,7 @@ pub fn get_data(uri: &str) -> RawData { fn parse_vmess_address(raw_data: &str) -> VmessAddress { let (uuid, raw_address): (String, &str) = match raw_data.split_once("@") { None => { - panic!("Wrong vmess format, no `@` found in the address"); + panic!("Wrong vmess format, no `@` found in the address and it was not a valid base64"); } Some(data) => (String::from(data.0), data.1), }; diff --git a/src/parser/vmess/mod.rs b/src/parser/vmess/mod.rs index 94f30fe..36b7497 100644 --- a/src/parser/vmess/mod.rs +++ b/src/parser/vmess/mod.rs @@ -12,6 +12,7 @@ pub fn create_outbound_settings(data: &RawData) -> OutboundSettings { flow: data.flow.clone(), encryption: Some(data.encryption.clone().unwrap_or(String::from("none"))), level: Some(0), + security: data.vnext_security.clone(), }]), }], });