Parse base64 vmess uri

This commit is contained in:
Keivan-sf
2025-07-27 12:21:49 +03:30
parent cfe3c2ed53
commit c5ce2b56ee
7 changed files with 74 additions and 1 deletions

7
Cargo.lock generated
View File

@@ -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",

View File

@@ -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"

View File

@@ -6,6 +6,7 @@ pub struct VlessUser {
pub encryption: Option<String>,
pub flow: Option<String>,
pub level: Option<u8>,
pub security: Option<String>,
}
#[derive(Serialize, Deserialize)]
@@ -186,6 +187,7 @@ pub struct Config {
#[allow(non_snake_case)]
pub struct RawData {
pub security: Option<String>,
pub vnext_security: Option<String>,
pub sni: Option<String>,
pub fp: Option<String>,
pub pbk: Option<String>,

View File

@@ -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")),

View File

@@ -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,
}]),
}],
});

View File

@@ -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<u8>) -> RawData {
let json_str = std::str::from_utf8(decoded_base64).unwrap();
let json = serde_json::from_str::<Value>(json_str).unwrap();
return RawData {
uuid: get_str_field(&json, "id"),
port: get_str_field(&json, "port")
.and_then(|s| Some(s.parse::<u16>().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<String> {
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),
};

View File

@@ -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(),
}]),
}],
});