diff --git a/src/config_models/mod.rs b/src/config_models/mod.rs index bea63b6..4488584 100644 --- a/src/config_models/mod.rs +++ b/src/config_models/mod.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] -pub struct VlessUser { +pub struct VnextUser { pub id: Option, pub encryption: Option, pub flow: Option, @@ -10,20 +10,33 @@ pub struct VlessUser { } #[derive(Serialize, Deserialize)] -pub struct VlessServerObject { +pub struct VnextServerObject { pub address: Option, pub port: Option, - pub users: Option>, + pub users: Option>, +} + +#[derive(Serialize, Deserialize)] +pub struct TrojanServerObject { + pub address: Option, + pub port: Option, + pub password: Option, + pub level: Option, } #[derive(Serialize, Deserialize)] pub struct VlessOutboundSettings { - pub vnext: Vec, + pub vnext: Vec, } #[derive(Serialize, Deserialize)] pub struct VmessOutboundSettings { - pub vnext: Vec, + pub vnext: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct TrojanOutboundSettings { + pub servers: Vec, } #[derive(Serialize, Deserialize)] @@ -31,6 +44,7 @@ pub struct VmessOutboundSettings { pub enum OutboundSettings { Vless(VlessOutboundSettings), Vmess(VmessOutboundSettings), + Trojan(TrojanOutboundSettings), } #[derive(Serialize, Deserialize)] diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 057206f..1f83c60 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -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"); } diff --git a/src/parser/trojan/data.rs b/src/parser/trojan/data.rs new file mode 100644 index 0000000..25ec883 --- /dev/null +++ b/src/parser/trojan/data.rs @@ -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: url_decode(Some(String::from(name))).unwrap_or(String::from("")), + 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::().unwrap(); + + return models::TrojanAddress { + uuid: url_decode(Some(uuid)).unwrap(), + address: parsed.host().unwrap().to_string(), + port: parsed.port().unwrap().as_u16(), + }; +} diff --git a/src/parser/trojan/mod.rs b/src/parser/trojan/mod.rs new file mode 100644 index 0000000..3361dc0 --- /dev/null +++ b/src/parser/trojan/mod.rs @@ -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), + }], + }); +} diff --git a/src/parser/trojan/models.rs b/src/parser/trojan/models.rs new file mode 100644 index 0000000..0d2b989 --- /dev/null +++ b/src/parser/trojan/models.rs @@ -0,0 +1,5 @@ +pub struct TrojanAddress { + pub uuid: String, + pub address: String, + pub port: u16, +} diff --git a/src/parser/vless/data.rs b/src/parser/vless/data.rs index a1c3fa3..e437e6b 100644 --- a/src/parser/vless/data.rs +++ b/src/parser/vless/data.rs @@ -13,7 +13,7 @@ pub fn get_data(uri: &str) -> RawData { let query: Vec<(&str, &str)> = querystring::querify(raw_query); return RawData { - remarks: String::from(name), + remarks: url_decode(Some(String::from(name))).unwrap_or(String::from("")), uuid: Some(parsed_address.uuid), port: Some(parsed_address.port), address: Some(parsed_address.address), diff --git a/src/parser/vless/mod.rs b/src/parser/vless/mod.rs index 0ec40a1..9a2d18d 100644 --- a/src/parser/vless/mod.rs +++ b/src/parser/vless/mod.rs @@ -4,10 +4,10 @@ use crate::{config_models::*, utils::parse_raw_json}; pub fn create_outbound_settings(data: &RawData) -> OutboundSettings { return OutboundSettings::Vless(VlessOutboundSettings { - vnext: vec![VlessServerObject { + vnext: vec![VnextServerObject { 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"))), diff --git a/src/parser/vmess/data.rs b/src/parser/vmess/data.rs index 4456297..f9795c9 100644 --- a/src/parser/vmess/data.rs +++ b/src/parser/vmess/data.rs @@ -19,7 +19,7 @@ fn get_raw_data_from_base64(decoded_base64: &Vec) -> RawData { let json = serde_json::from_str::(json_str).unwrap(); return RawData { - remarks: get_str_field(&json, "ps").unwrap_or(String::from("")), + remarks: url_decode(get_str_field(&json, "ps")).unwrap_or(String::from("")), uuid: get_str_field(&json, "id"), port: get_str_field(&json, "port") .and_then(|s| Some(s.parse::().expect("port is not a number"))), diff --git a/src/parser/vmess/mod.rs b/src/parser/vmess/mod.rs index 36b7497..63ae87e 100644 --- a/src/parser/vmess/mod.rs +++ b/src/parser/vmess/mod.rs @@ -4,10 +4,10 @@ use crate::{config_models::*, utils::parse_raw_json}; pub fn create_outbound_settings(data: &RawData) -> OutboundSettings { return OutboundSettings::Vmess(VmessOutboundSettings { - vnext: vec![VlessServerObject { + vnext: vec![VnextServerObject { 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"))),