libsubconverter/parser/explodes/
snell.rs1use crate::{
2 models::{Proxy, SNELL_DEFAULT_GROUP},
3 utils::url_decode,
4};
5use std::collections::HashMap;
6use url::Url;
7
8pub fn explode_snell(snell: &str, node: &mut Proxy) -> bool {
10 if !snell.starts_with("snell://") {
12 return false;
13 }
14
15 let url = match Url::parse(snell) {
17 Ok(url) => url,
18 Err(_) => return false,
19 };
20
21 let host = match url.host_str() {
23 Some(host) => host,
24 None => return false,
25 };
26 let port = url.port().unwrap_or(8388);
27 if port == 0 {
28 return false; }
30
31 let password = url.username();
33 if password.is_empty() {
34 return false;
35 }
36
37 let mut params = HashMap::new();
39 for (key, value) in url.query_pairs() {
40 params.insert(key.to_string(), url_decode(&value));
41 }
42
43 let obfs = params.get("obfs").map(|s| s.as_str()).unwrap_or("none");
45
46 let host_param = params.get("host").map(|s| s.as_str()).unwrap_or("");
48
49 let version = params
51 .get("version")
52 .map(|s| s.parse::<u16>().unwrap_or(1))
53 .unwrap_or(1);
54
55 let udp = params.get("udp").map(|s| s == "true" || s == "1");
57 let tfo = params.get("tfo").map(|s| s == "true" || s == "1");
58 let allow_insecure = params
59 .get("skip-cert-verify")
60 .map(|s| s == "true" || s == "1");
61
62 let remark = url_decode(url.fragment().unwrap_or(""));
64 let formatted_remark = if remark.is_empty() {
65 format!("{} ({})", host, port)
66 } else {
67 remark.to_string()
68 };
69
70 *node = Proxy::snell_construct(
72 SNELL_DEFAULT_GROUP.to_string(),
73 formatted_remark,
74 host.to_string(),
75 port,
76 password.to_string(),
77 obfs.to_string(),
78 host_param.to_string(),
79 version,
80 udp,
81 tfo,
82 allow_insecure,
83 None,
84 );
85
86 true
87}
88
89pub fn explode_snell_surge(surge: &str, node: &mut Proxy) -> bool {
92 if !surge.starts_with("snell") {
93 return false;
94 }
95
96 let parts: Vec<&str> = surge.split(',').map(|s| s.trim()).collect();
98 if parts.len() < 3 {
99 return false;
100 }
101
102 let server_part = parts[0];
104 let server = if server_part.contains('=') {
105 server_part
106 .split('=')
107 .nth(1)
108 .unwrap_or("")
109 .trim()
110 .to_string()
111 } else {
112 server_part.replace("snell", "").trim().to_string()
113 };
114 if server.is_empty() {
115 return false;
116 }
117
118 let port_str = parts[1];
119 let port = match port_str.parse::<u16>() {
120 Ok(p) => p,
121 Err(_) => return false,
122 };
123 if port == 0 {
124 return false;
125 }
126
127 let mut password = String::new();
129 let mut obfs = String::new();
130 let mut obfs_host = String::new();
131 let mut version = 1u16;
132 let mut udp = None;
133 let mut tfo = None;
134 let mut allow_insecure = None;
135
136 for i in 2..parts.len() {
138 let param_parts: Vec<&str> = parts[i].split('=').collect();
139 if param_parts.len() != 2 {
140 continue;
141 }
142 let key = param_parts[0].trim();
143 let value = param_parts[1].trim();
144
145 match key {
146 "psk" => password = value.to_string(),
147 "obfs" => obfs = value.to_string(),
148 "obfs-host" => obfs_host = value.to_string(),
149 "version" => version = value.parse::<u16>().unwrap_or(1),
150 "udp" | "udp-relay" => udp = Some(value == "true" || value == "1"),
151 "tfo" => tfo = Some(value == "true" || value == "1"),
152 "skip-cert-verify" => allow_insecure = Some(value == "true" || value == "1"),
153 _ => {}
154 }
155 }
156
157 if password.is_empty() {
158 return false;
159 }
160
161 let remark = format!("{} ({})", server, port);
163
164 *node = Proxy::snell_construct(
166 SNELL_DEFAULT_GROUP.to_string(),
167 remark,
168 server,
169 port,
170 password,
171 obfs,
172 obfs_host,
173 version,
174 udp,
175 tfo,
176 allow_insecure,
177 None,
178 );
179
180 true
181}