diff --git a/src/config_models/mod.rs b/src/config_models/mod.rs index c4d6d28..c6f7b49 100644 --- a/src/config_models/mod.rs +++ b/src/config_models/mod.rs @@ -2,17 +2,17 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] pub struct VlessUser { - pub id: String, - pub encryption: String, + pub id: Option, + pub encryption: Option, pub flow: Option, pub level: Option, } #[derive(Serialize, Deserialize)] pub struct VlessServerObject { - pub address: String, - pub port: u16, - pub users: Vec, + pub address: Option, + pub port: Option, + pub users: Option>, } #[derive(Serialize, Deserialize)] @@ -175,3 +175,33 @@ pub struct Config { pub outbounds: Vec, pub inbounds: Vec, } + +#[derive(Serialize, Deserialize)] +#[allow(non_snake_case)] +pub struct RawData { + pub security: Option, + pub sni: Option, + pub fp: Option, + pub pbk: Option, + pub sid: Option, + pub r#type: Option, + pub flow: Option, + pub path: Option, + pub encryption: Option, + pub header_type: Option, + pub host: Option, + pub seed: Option, + pub quic_security: Option, + pub r#key: Option, + pub mode: Option, + pub service_name: Option, + pub authority: Option, + pub slpn: Option, + pub spx: Option, + pub alpn: Option, + pub extra: Option, + pub allowInsecure: Option, + pub uuid: Option, + pub address: Option, + pub port: Option, +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index ad0ff87..a44ed2e 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,6 +1,9 @@ -use crate::config_models; -use crate::utils::inbound_generator; -use std::process::exit; +use crate::config_models::{ + self, GRPCSettings, KCPSettings, NonHeaderObject, Outbound, OutboundSettings, QuicSettings, + RawData, RealitySettings, StreamSettings, TCPHeader, TCPSettings, TlsSettings, WsSettings, + XHTTPSettings, +}; +use crate::utils::{inbound_generator, parse_raw_json}; mod uri_identifier; mod vless; @@ -31,19 +34,124 @@ pub fn create_config( pub fn create_outbound_object(uri: &str) -> config_models::Outbound { let protocol = uri_identifier::get_uri_protocol(uri); - match protocol { + let (name, data, outbound_settings): (String, RawData, OutboundSettings) = match protocol { Some(uri_identifier::Protocols::Vless) => { - let vless_data = vless::data::get_data(uri); - let outbound_object = vless::create_outbound_object(vless_data); - return outbound_object; + let d = vless::data::get_data(uri); + let s = vless::create_outbound_settings(&d); + (String::from("vless"), d, s) } Some(_) => { - println!("The protocol was recognized but is not supported yet"); - exit(0); + panic!("The protocol was recognized but is not supported yet"); } None => { - println!("The protcol is not supported"); - exit(0); + panic!("The protocol is not supported"); } - } + }; + + let network_type = data.r#type.clone().unwrap_or(String::from("")); + let allow_insecure = data.allowInsecure == Some(String::from("true")) + || data.allowInsecure == Some(String::from("1")); + + let outbound = Outbound { + protocol: name, + tag: String::from("proxy"), + streamSettings: StreamSettings { + network: data.r#type.clone(), + security: data.security.clone(), + tlsSettings: if data.security == Some(String::from("tls")) { + Some(TlsSettings { + alpn: data.alpn.map(|alpn| vec![alpn]), + rejectUnknownSni: None, + enableSessionResumption: None, + minVersion: None, + maxVersion: None, + cipherSuites: None, + disableSystemRoot: None, + preferServerCipherSuites: None, + fingerprint: data.fp.clone(), + serverName: data.sni.clone(), + allowInsecure: allow_insecure, + }) + } else { + None + }, + wsSettings: if network_type == String::from("ws") { + Some(WsSettings { + Host: data.host.clone(), + path: data.path.clone(), + acceptProxyProtocol: None, + }) + } else { + None + }, + tcpSettings: if network_type == String::from("tcp") { + Some(TCPSettings { + header: Some(TCPHeader { + r#type: Some(data.header_type.unwrap_or(String::from("none"))), + }), + acceptProxyProtocol: None, + }) + } else { + None + }, + realitySettings: if network_type == String::from("reality") { + Some(RealitySettings { + publicKey: data.pbk, + serverName: data.sni.clone(), + shortId: data.sid, + spiderX: Some(String::from("")), + fingerprint: data.fp.clone(), + }) + } else { + None + }, + grpcSettings: if network_type == String::from("grpc") { + Some(GRPCSettings { + authority: data.authority, + multiMode: Some(false), + serviceName: data.service_name, + }) + } else { + None + }, + quicSettings: if network_type == String::from("quic") { + Some(QuicSettings { + header: Some(NonHeaderObject { + r#type: Some(String::from("none")), + }), + security: Some(String::from("none")), + key: Some(String::from("")), + }) + } else { + None + }, + kcpSettings: if network_type == String::from("kcp") { + Some(KCPSettings { + mtu: None, + tti: None, + congestion: None, + uplinkCapacity: None, + readBufferSize: None, + writeBufferSize: None, + downlinkCapacity: None, + seed: data.seed, + }) + } else { + None + }, + xhttpSettings: if network_type == String::from("xhttp") { + Some(XHTTPSettings { + host: data.host.clone(), + path: data.path.clone(), + mode: data.mode, + extra: data.extra.and_then(|e| parse_raw_json(e.as_str())), + }) + } else { + None + }, + }, + settings: outbound_settings, + }; + + return outbound; } diff --git a/src/parser/vless/data.rs b/src/parser/vless/data.rs index 21916f2..b39c99d 100644 --- a/src/parser/vless/data.rs +++ b/src/parser/vless/data.rs @@ -1,19 +1,44 @@ +use crate::config_models::RawData; use crate::parser::vless::models; use crate::utils::{get_parameter_value, url_decode}; use http::Uri; -pub fn get_data(uri: &str) -> models::VlessData { +pub fn get_data(uri: &str) -> RawData { let data = uri.split_once("vless://").unwrap().1; let query_and_name = uri.split_once("?").unwrap().1; - let query = query_and_name + let raw_query = query_and_name .split_once("#") .unwrap_or((query_and_name, "")) .0; - let parsed_query = parse_vless_query(query); let parsed_address = parse_vless_address(data.split_once("?").unwrap().0); - return models::VlessData { - query: parsed_query, - address_data: parsed_address, + let query: Vec<(&str, &str)> = querystring::querify(raw_query); + + return RawData { + 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")), + 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"), }; } @@ -64,121 +89,3 @@ fn parse_vless_query(raw_query: &str) -> models::VlessQuery { }; return a; } - -#[cfg(test)] -mod tests { - use crate::parser::vless::create_outbound_object; - - use super::*; - - #[test] - fn vless_tcp_header_test() { - let v = "vless://1010501a-ca9a-479c-84d0-1308d97789b5@104.21.25.109:443?security=reality&alpn=http%2F1.1&encryption=none&pbk=testpub&host=mehr14-n.gowow31220.workers.dev&headerType=http&fp=chrome&spx=spideex&type=tcp&flow=xtls-rprx-vision&sni=mehr14-iran-mehr14-iran-mehr14-iran-mehr14-iran-mehr14-iran.gowow31220.workers.dev&sid=testshort#%E2%AD%90%EF%B8%8F%20Telegram%20%3D%20%40z_v2ray"; - let data = create_outbound_object(get_data(v)); - assert_eq!( - data.streamSettings - .tcpSettings - .unwrap() - .header - .unwrap() - .r#type, - Some(String::from("http")) - ) - } - - #[test] - fn vless_test_2() { - let v = "vless://2dc56709-sdfs-sdfs-2234-128904@nwarne.fast-ip.com:80/?type=ws&encryption=none&host=Shuposipet.com&path=%2Fde%3Fed%3D1048#[test]@test"; - let data = get_data(v); - assert_eq!(data.address_data.address, "nwarne.fast-ip.com"); - assert_eq!(data.address_data.uuid, "2dc56709-sdfs-sdfs-2234-128904"); - assert_eq!(data.address_data.port, 80); - assert_eq!(data.query.encryption.unwrap(), "none"); - assert_eq!(data.query.r#type.unwrap(), "ws"); - } - - #[test] - fn vless_test() { - let v = "vless://4d2c3e35-749d-52e3-bdb6-3f3f4950c183@tre.test.one:2053?security=reality&type=tcp&flow=xtls-rprx-vision#test-name"; - let data = get_data(v); - assert_eq!(data.address_data.address, "tre.test.one"); - assert_eq!( - data.address_data.uuid, - "4d2c3e35-749d-52e3-bdb6-3f3f4950c183" - ); - assert_eq!(data.address_data.port, 2053); - assert_eq!(data.query.flow.unwrap(), "xtls-rprx-vision"); - assert_eq!(data.query.security.unwrap(), "reality"); - assert_eq!(data.query.r#type.unwrap(), "tcp"); - } - - #[test] - fn parse_vless_query_data() { - let query = "security=reality&sni=bench.sh&fp=chrome&pbk=7xhH4b_VkliBxGulljcyPOH-bYUA2dl-XAdZAsfhk04&sid=6ba85179e30d4fc2&type=tcp&flow=xtls-rprx-vision&alpn=http%2F1.1&path=/"; - let parsed_query = parse_vless_query(query); - assert_eq!(parsed_query.sni.unwrap(), "bench.sh"); - assert_eq!(parsed_query.security.unwrap(), "reality"); - assert_eq!(parsed_query.fp.unwrap(), "chrome"); - assert_eq!( - parsed_query.pbk.unwrap(), - "7xhH4b_VkliBxGulljcyPOH-bYUA2dl-XAdZAsfhk04" - ); - assert_eq!(parsed_query.sid.unwrap(), "6ba85179e30d4fc2"); - assert_eq!(parsed_query.r#type.unwrap(), "tcp"); - assert_eq!(parsed_query.flow.unwrap(), "xtls-rprx-vision"); - assert_eq!(parsed_query.alpn.unwrap(), "http/1.1"); - assert_eq!(parsed_query.path.unwrap(), "/"); - assert_eq!(parsed_query.encryption, None); - assert_eq!(parsed_query.header_type, None); - assert_eq!(parsed_query.host, None); - assert_eq!(parsed_query.seed, None); - assert_eq!(parsed_query.quic_security, None); - assert_eq!(parsed_query.key, None); - assert_eq!(parsed_query.mode, None); - assert_eq!(parsed_query.service_name, None); - assert_eq!(parsed_query.slpn, None); - assert_eq!(parsed_query.spx, None); - } - - #[test] - fn parse_vless_query_with_defaults() { - let query = ""; - let parsed_query = parse_vless_query(query); - assert_eq!(parsed_query.sni, None); - assert_eq!(parsed_query.security, None); - assert_eq!(parsed_query.fp, None); - assert_eq!(parsed_query.pbk, None); - assert_eq!(parsed_query.sid, None); - assert_eq!(parsed_query.r#type, None); - assert_eq!(parsed_query.r#flow, None); - assert_eq!(parsed_query.path, None); - assert_eq!(parsed_query.encryption, None); - assert_eq!(parsed_query.header_type, None); - assert_eq!(parsed_query.host, None); - assert_eq!(parsed_query.seed, None); - assert_eq!(parsed_query.quic_security, None); - assert_eq!(parsed_query.key, None); - assert_eq!(parsed_query.mode, None); - assert_eq!(parsed_query.service_name, None); - assert_eq!(parsed_query.slpn, None); - assert_eq!(parsed_query.spx, None); - } - - #[test] - fn parse_vless_host() { - let raw_host = "uu0id@127.0.0.1:3012"; - let parsed = parse_vless_address(raw_host); - assert_eq!(parsed.address, "127.0.0.1"); - assert_eq!(parsed.port, 3012); - assert_eq!(parsed.uuid, "uu0id"); - } - - #[test] - fn parse_vless_ipv6_host() { - let raw_host = "uu0id@[2a06:98c1:3120::1]:443"; - let parsed = parse_vless_address(raw_host); - assert_eq!(parsed.port, 443); - assert_eq!(parsed.address, "[2a06:98c1:3120::1]"); - assert_eq!(parsed.uuid, "uu0id"); - } -} diff --git a/src/parser/vless/mod.rs b/src/parser/vless/mod.rs index 3dcb4ba..aee3620 100644 --- a/src/parser/vless/mod.rs +++ b/src/parser/vless/mod.rs @@ -2,121 +2,17 @@ pub mod data; mod models; use crate::{config_models::*, utils::parse_raw_json}; -pub fn create_outbound_object(data: models::VlessData) -> Outbound { - let network_type = data.query.r#type.clone().unwrap_or(String::from("")); - - let allow_insecure = data.query.allowInsecure == Some(String::from("true")) - || data.query.allowInsecure == Some(String::from("1")); - - return Outbound { - protocol: String::from("vless"), - tag: String::from("proxy"), - streamSettings: StreamSettings { - network: data.query.r#type.clone(), - security: data.query.security.clone(), - tlsSettings: if data.query.security == Some(String::from("tls")) { - Some(TlsSettings { - alpn: data.query.alpn.map(|alpn| vec![alpn]), - rejectUnknownSni: None, - enableSessionResumption: None, - minVersion: None, - maxVersion: None, - cipherSuites: None, - disableSystemRoot: None, - preferServerCipherSuites: None, - fingerprint: data.query.fp.clone(), - serverName: data.query.sni.clone(), - allowInsecure: allow_insecure, - }) - } else { - None - }, - wsSettings: if network_type == String::from("ws") { - Some(WsSettings { - Host: data.query.host.clone(), - path: data.query.path.clone(), - acceptProxyProtocol: None, - }) - } else { - None - }, - tcpSettings: if network_type == String::from("tcp") { - Some(TCPSettings { - header: Some(TCPHeader { - r#type: Some(data.query.header_type.unwrap_or(String::from("none"))), - }), - acceptProxyProtocol: None, - }) - } else { - None - }, - realitySettings: if network_type == String::from("reality") { - Some(RealitySettings { - publicKey: data.query.pbk, - serverName: data.query.sni.clone(), - shortId: data.query.sid, - spiderX: Some(String::from("")), - fingerprint: data.query.fp.clone(), - }) - } else { - None - }, - grpcSettings: if network_type == String::from("grpc") { - Some(GRPCSettings { - authority: data.query.authority, - multiMode: Some(false), - serviceName: data.query.service_name, - }) - } else { - None - }, - quicSettings: if network_type == String::from("quic") { - Some(QuicSettings { - header: Some(NonHeaderObject { - r#type: Some(String::from("none")), - }), - security: Some(String::from("none")), - key: Some(String::from("")), - }) - } else { - None - }, - kcpSettings: if network_type == String::from("kcp") { - Some(KCPSettings { - mtu: None, - tti: None, - congestion: None, - uplinkCapacity: None, - readBufferSize: None, - writeBufferSize: None, - downlinkCapacity: None, - seed: data.query.seed, - }) - } else { - None - }, - xhttpSettings: if network_type == String::from("xhttp") { - Some(XHTTPSettings { - host: data.query.host.clone(), - path: data.query.path.clone(), - mode: data.query.mode, - extra: data.query.extra.and_then(|e| parse_raw_json(e.as_str())), - }) - } else { - None - }, - }, - settings: OutboundSettings::Vless(VlessOutboundSettings { - vnext: vec![VlessServerObject { - port: data.address_data.port, - address: data.address_data.address, - users: vec![VlessUser { - id: data.address_data.uuid, - flow: data.query.flow, - encryption: data.query.encryption.unwrap_or(String::from("none")), - level: Some(0), - }], - }], - }), - }; +pub fn create_outbound_settings(data: &RawData) -> OutboundSettings { + return OutboundSettings::Vless(VlessOutboundSettings { + vnext: vec![VlessServerObject { + port: data.port, + address: data.address.clone(), + users: Some(vec![VlessUser { + id: data.uuid.clone(), + flow: data.flow.clone(), + encryption: Some(data.encryption.clone().unwrap_or(String::from("none"))), + level: Some(0), + }]), + }], + }); }