Parse trojan URIs

This commit is contained in:
Keivan-sf
2025-07-27 18:19:35 +03:30
parent b27f6e776f
commit 2a6c452c0d
7 changed files with 105 additions and 4 deletions

View File

@@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct VlessUser {
pub struct VnextUser {
pub id: Option<String>,
pub encryption: Option<String>,
pub flow: Option<String>,
@@ -13,7 +13,15 @@ pub struct VlessUser {
pub struct VlessServerObject {
pub address: Option<String>,
pub port: Option<u16>,
pub users: Option<Vec<VlessUser>>,
pub users: Option<Vec<VnextUser>>,
}
#[derive(Serialize, Deserialize)]
pub struct TrojanServerObject {
pub address: Option<String>,
pub port: Option<u16>,
pub password: Option<String>,
pub level: Option<u8>,
}
#[derive(Serialize, Deserialize)]
@@ -26,11 +34,17 @@ pub struct VmessOutboundSettings {
pub vnext: Vec<VlessServerObject>,
}
#[derive(Serialize, Deserialize)]
pub struct TrojanOutboundSettings {
pub servers: Vec<TrojanServerObject>,
}
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
pub enum OutboundSettings {
Vless(VlessOutboundSettings),
Vmess(VmessOutboundSettings),
Trojan(TrojanOutboundSettings),
}
#[derive(Serialize, Deserialize)]

View File

@@ -8,6 +8,7 @@ use crate::utils::{inbound_generator, parse_raw_json};
mod uri_identifier;
mod vless;
mod vmess;
mod trojan;
pub fn get_name(uri: &str) -> String {
let (_, data, _) = get_uri_data(uri);
@@ -162,6 +163,11 @@ fn get_uri_data(uri: &str) -> (String, RawData, OutboundSettings) {
let s = vmess::create_outbound_settings(&d);
(String::from("vmess"), d, s)
}
Some(uri_identifier::Protocols::Trojan) => {
let d = trojan::data::get_data(uri);
let s = trojan::create_outbound_settings(&d);
(String::from("trojan"), d, s)
}
Some(_) => {
panic!("The protocol was recognized but is not supported yet");
}

62
src/parser/trojan/data.rs Normal file
View File

@@ -0,0 +1,62 @@
use crate::config_models::RawData;
use crate::parser::trojan::models;
use crate::utils::{get_parameter_value, url_decode};
use http::Uri;
pub fn get_data(uri: &str) -> RawData {
let data = uri.split_once("trojan://").unwrap().1;
let query_and_name = uri.split_once("?").unwrap().1;
let (raw_query, name) = query_and_name
.split_once("#")
.unwrap_or((query_and_name, ""));
let parsed_address = parse_trojan_address(data.split_once("?").unwrap().0);
let query: Vec<(&str, &str)> = querystring::querify(raw_query);
return RawData {
remarks: String::from(name),
uuid: Some(parsed_address.uuid),
port: Some(parsed_address.port),
address: Some(parsed_address.address),
alpn: url_decode(get_parameter_value(&query, "alpn")),
path: url_decode(get_parameter_value(&query, "path")),
authority: url_decode(get_parameter_value(&query, "authority")),
pbk: url_decode(get_parameter_value(&query, "pbk")),
security: get_parameter_value(&query, "security"),
sid: url_decode(get_parameter_value(&query, "sid")),
flow: get_parameter_value(&query, "flow"),
sni: get_parameter_value(&query, "sni"),
fp: url_decode(get_parameter_value(&query, "fp")),
r#type: get_parameter_value(&query, "type"),
encryption: get_parameter_value(&query, "encryption"),
header_type: get_parameter_value(&query, "headerType"),
host: url_decode(get_parameter_value(&query, "host")),
seed: url_decode(get_parameter_value(&query, "seed")),
quic_security: get_parameter_value(&query, "quicSecurity"),
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")),
allowInsecure: get_parameter_value(&query, "allowInsecure"),
};
}
fn parse_trojan_address(raw_data: &str) -> models::TrojanAddress {
let (uuid, raw_address): (String, &str) = match raw_data.split_once("@") {
None => {
panic!("Wrong trojan format, no `@` found in the address");
}
Some(data) => (String::from(data.0), data.1),
};
let address_wo_slash = raw_address.strip_suffix("/").unwrap_or(raw_address);
let parsed = address_wo_slash.parse::<Uri>().unwrap();
return models::TrojanAddress{
uuid: url_decode(Some(uuid)).unwrap(),
address: parsed.host().unwrap().to_string(),
port: parsed.port().unwrap().as_u16(),
};
}

14
src/parser/trojan/mod.rs Normal file
View File

@@ -0,0 +1,14 @@
pub mod data;
mod models;
use crate::config_models::*;
pub fn create_outbound_settings(data: &RawData) -> OutboundSettings {
return OutboundSettings::Trojan(TrojanOutboundSettings {
servers: vec![TrojanServerObject {
address: data.address.clone(),
port: data.port,
password: data.uuid.clone(),
level: Some(0),
}],
});
}

View File

@@ -0,0 +1,5 @@
pub struct TrojanAddress {
pub uuid: String,
pub address: String,
pub port: u16,
}

View File

@@ -7,7 +7,7 @@ pub fn create_outbound_settings(data: &RawData) -> OutboundSettings {
vnext: vec![VlessServerObject {
port: data.port,
address: data.address.clone(),
users: Some(vec![VlessUser {
users: Some(vec![VnextUser {
id: data.uuid.clone(),
flow: data.flow.clone(),
encryption: Some(data.encryption.clone().unwrap_or(String::from("none"))),

View File

@@ -7,7 +7,7 @@ pub fn create_outbound_settings(data: &RawData) -> OutboundSettings {
vnext: vec![VlessServerObject {
port: data.port,
address: data.address.clone(),
users: Some(vec![VlessUser {
users: Some(vec![VnextUser {
id: data.uuid.clone(),
flow: data.flow.clone(),
encryption: Some(data.encryption.clone().unwrap_or(String::from("none"))),