libsubconverter/parser/explodes/
vless.rs

1use crate::models::proxy_node::combined::CombinedProxy;
2use crate::models::proxy_node::vless::VlessProxy;
3use crate::models::{Proxy, ProxyType};
4use crate::utils::url_decode;
5use std::collections::{HashMap, HashSet};
6use url::Url;
7
8/// Parse a VLESS link into a Proxy object
9pub fn explode_vless(vless: &str, node: &mut Proxy) -> bool {
10    // Check if the link starts with vless://
11    if !vless.starts_with("vless://") {
12        return false;
13    }
14
15    // Try to parse as URL
16    let url = match Url::parse(vless) {
17        Ok(url) => url,
18        Err(_) => return false,
19    };
20
21    // Extract parameters from the query string
22    let mut params = HashMap::new();
23    for (key, value) in url.query_pairs() {
24        params.insert(key.to_string(), url_decode(&value));
25    }
26
27    // Extract required fields
28    let uuid = match url.username() {
29        "" => return false,
30        username => username.to_string(),
31    };
32
33    // Extract host and port
34    let host = match url.host_str() {
35        Some(host) => host,
36        None => return false,
37    };
38    let port = url.port().unwrap_or(443);
39
40    // Extract optional fields
41    let tls = params
42        .get("security")
43        .map(|s| s.to_lowercase())
44        .map(|s| s.ends_with("tls") || s == "reality")
45        .unwrap_or(false);
46
47    let fingerprint = params
48        .get("fp")
49        .map(|s| s.to_string())
50        .unwrap_or_else(|| "chrome".to_string());
51
52    let alpn = params
53        .get("alpn")
54        .map(|s| {
55            s.split(',')
56                .map(|s| s.trim().to_string())
57                .collect::<HashSet<_>>()
58        })
59        .unwrap_or_default();
60
61    let sni = params.get("sni").map(|s| s.to_string());
62
63    let flow = params.get("flow").map(|s| s.to_string());
64
65    let packet_encoding = params.get("packetEncoding").map(|s| s.to_string());
66    let packet_addr = packet_encoding.as_deref() == Some("packet");
67
68    let network = params
69        .get("type")
70        .map(|s| s.to_lowercase())
71        .unwrap_or_else(|| "tcp".to_string());
72
73    let fake_type = params
74        .get("headerType")
75        .map(|s| s.to_lowercase())
76        .unwrap_or_default();
77
78    let mut vless_proxy = VlessProxy::default();
79    vless_proxy.uuid = uuid;
80    vless_proxy.tls = tls;
81    vless_proxy.alpn = alpn;
82    if let Some(packet_encoding) = packet_encoding {
83        let xudp = packet_encoding != "none";
84        vless_proxy.xudp = Some(xudp);
85        vless_proxy.packet_encoding = Some(packet_encoding);
86        vless_proxy.packet_addr = Some(packet_addr);
87    }
88    vless_proxy.network = Some(network.clone());
89    vless_proxy.servername = sni;
90    vless_proxy.client_fingerprint = Some(fingerprint);
91    vless_proxy.flow = flow;
92
93    // Handle Reality options
94    if let Some(public_key) = params.get("pbk") {
95        vless_proxy.reality_public_key = Some(public_key.to_string());
96        vless_proxy.reality_short_id = params.get("sid").map(|s| s.to_string());
97    }
98
99    // Handle network-specific options
100    match network.as_str() {
101        "tcp" => {
102            if fake_type != "none" {
103                let mut http_headers = HashMap::new();
104                let mut http_path = vec!["/".to_string()];
105
106                if let Some(host) = params.get("host") {
107                    http_headers.insert("Host".to_string(), vec![host.to_string()]);
108                }
109
110                if let Some(path) = params.get("path") {
111                    http_path = vec![path.to_string()];
112                }
113
114                vless_proxy.http_method = params.get("method").map(|s| s.to_string());
115                vless_proxy.http_path = Some(http_path[0].clone());
116                vless_proxy.http_headers = Some(http_headers);
117            }
118        }
119        "http" | "h2" => {
120            let mut h2_headers = HashMap::new();
121            let mut h2_path = vec!["/".to_string()];
122
123            if let Some(path) = params.get("path") {
124                h2_path = vec![path.to_string()];
125            }
126
127            if let Some(host) = params.get("host") {
128                h2_headers.insert("Host".to_string(), vec![host.to_string()]);
129            }
130
131            vless_proxy.h2_path = Some(h2_path[0].clone());
132            vless_proxy.h2_host = Some(h2_headers.get("Host").unwrap_or(&vec![]).clone());
133        }
134        "ws" | "httpupgrade" => {
135            let mut ws_headers = HashMap::new();
136            ws_headers.insert("User-Agent".to_string(), "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36".to_string());
137
138            if let Some(host) = params.get("host") {
139                ws_headers.insert("Host".to_string(), host.to_string());
140            }
141
142            vless_proxy.ws_path = params.get("path").map(|s| s.to_string());
143            vless_proxy.ws_headers = Some(ws_headers);
144
145            if let Some(early_data) = params.get("ed") {
146                if let Ok(_med) = early_data.parse::<i32>() {
147                    if network == "ws" {
148                        // Handle max early data
149                    } else if network == "httpupgrade" {
150                        // Handle v2ray-http-upgrade-fast-open
151                    }
152                }
153            }
154
155            if let Some(_early_data_header) = params.get("eh") {
156                // Handle early data header name
157            }
158        }
159        "grpc" => {
160            vless_proxy.grpc_service_name = params.get("serviceName").map(|s| s.to_string());
161        }
162        _ => {}
163    }
164
165    node.proxy_type = ProxyType::Vless;
166    node.combined_proxy = Some(CombinedProxy::Vless(vless_proxy));
167    node.remark = url_decode(url.fragment().unwrap_or(""));
168    node.hostname = host.to_string();
169    node.port = port;
170
171    true
172}