1use crate::{
2 models::{Proxy, SOCKS_DEFAULT_GROUP, SS_DEFAULT_GROUP, V2RAY_DEFAULT_GROUP},
3 utils::url_decode,
4};
5use base64::{engine::general_purpose::STANDARD, Engine};
6use regex::Regex;
7use serde_json::Value;
8use std::collections::HashMap;
9use url::Url;
10
11pub fn explode_vmess(vmess: &str, node: &mut Proxy) -> bool {
13 if !vmess.starts_with("vmess://") {
15 return false;
16 }
17
18 let encoded = &vmess[8..];
20
21 let decoded = match STANDARD.decode(encoded) {
23 Ok(decoded) => match String::from_utf8(decoded) {
24 Ok(s) => s,
25 Err(_) => return false,
26 },
27 Err(_) => return false,
28 };
29
30 let json: Value = match serde_json::from_str(&decoded) {
32 Ok(json) => json,
33 Err(_) => return false,
34 };
35
36 let version = json["v"].as_u64().unwrap_or(1);
38
39 let add = json["add"].as_str().unwrap_or("").to_string();
41 let port = json["port"]
42 .as_str()
43 .map(|s| s.to_string())
44 .unwrap_or_else(|| {
45 json["port"]
46 .as_u64()
47 .map_or_else(|| "0".to_string(), |p| p.to_string())
48 });
49 let id = json["id"].as_str().unwrap_or("").to_string();
50 let aid = json["aid"]
51 .as_str()
52 .map(|s| s.to_string())
53 .unwrap_or_else(|| {
54 json["aid"]
55 .as_u64()
56 .map_or_else(|| "0".to_string(), |a| a.to_string())
57 });
58 let net = json["net"].as_str().unwrap_or("tcp").to_string();
59 let type_field = json["type"].as_str().unwrap_or("").to_string();
60 let mut host = json["host"].as_str().unwrap_or("").to_string();
61 let mut path = json["path"].as_str().unwrap_or("").to_string();
62 let tls = json["tls"].as_str().unwrap_or("").to_string();
63 let sni = json["sni"].as_str().unwrap_or("").to_string();
64
65 let remark = json["ps"].as_str().unwrap_or("").to_string();
67
68 let port = port.parse::<u16>().unwrap_or(0);
70 let aid = aid.parse::<u16>().unwrap_or(0);
71
72 if version == 2 {
74 if !host.is_empty() {
75 let host_str = host.clone();
76 let parts: Vec<&str> = host_str.split(';').collect();
77 if parts.len() == 2 {
78 host = parts[0].to_string();
79 path = parts[1].to_string();
80 }
81 }
82 }
83
84 *node = Proxy::vmess_construct(
86 "VMess",
87 &remark,
88 &add,
89 port,
90 &type_field,
91 &id,
92 aid,
93 &net,
94 "auto",
95 &path,
96 &host,
97 "",
98 &tls,
99 &sni,
100 None,
101 None,
102 None,
103 None,
104 "",
105 );
106
107 true
108}
109
110pub fn explode_std_vmess(vmess: &str, node: &mut Proxy) -> bool {
113 if !vmess.starts_with("vmess://") && !vmess.starts_with("vmess+") {
115 return false;
116 }
117
118 let protocol_end = match vmess.find("://") {
120 Some(pos) => pos,
121 None => return false,
122 };
123
124 let protocol = vmess[..protocol_end].to_string();
125 let tls = protocol.contains("+tls");
126
127 let url_part = &vmess[protocol_end + 3..];
129
130 let (url_without_fragment, remark) = match url_part.find('#') {
132 Some(pos) => (url_part[..pos].to_string(), url_part[pos + 1..].to_string()),
133 None => (url_part.to_string(), String::new()),
134 };
135
136 let re = Regex::new(
138 r"^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})-(\d+)@([^:]+):(\d+)(.*)$",
139 )
140 .unwrap();
141
142 let caps = match re.captures(&url_without_fragment) {
143 Some(c) => c,
144 None => return false,
145 };
146
147 let id = caps.get(1).map_or("", |m| m.as_str()).to_string();
148 let aid = caps
149 .get(2)
150 .map_or("0", |m| m.as_str())
151 .parse::<u16>()
152 .unwrap_or(0);
153 let host = caps.get(3).map_or("", |m| m.as_str()).to_string();
154 let port = caps
155 .get(4)
156 .map_or("0", |m| m.as_str())
157 .parse::<u16>()
158 .unwrap_or(0);
159 let query = caps.get(5).map_or("", |m| m.as_str()).to_string();
160
161 let mut net = "tcp".to_string();
163 let mut path = "/".to_string();
164 let mut host_header = host.clone();
165 let mut tls_str = if tls {
166 "tls".to_string()
167 } else {
168 String::new()
169 };
170 let mut sni = String::new();
171
172 if !query.is_empty() && query.starts_with("/?") {
174 for param in query[2..].split('&') {
175 let mut kv = param.split('=');
176 if let (Some(k), Some(v)) = (kv.next(), kv.next()) {
177 match k {
178 "network" => net = v.to_string(),
179 "host" => host_header = v.to_string(),
180 "path" => path = v.to_string(),
181 "tls" => tls_str = v.to_string(),
182 "sni" => sni = v.to_string(),
183 _ => {}
184 }
185 }
186 }
187 }
188
189 let formatted_remark = if remark.is_empty() {
191 format!("{} ({})", host, port)
192 } else {
193 remark
194 };
195
196 *node = Proxy::vmess_construct(
198 "VMess",
199 &formatted_remark,
200 &host,
201 port,
202 "",
203 &id,
204 aid,
205 &net,
206 "auto",
207 &path,
208 &host_header,
209 "",
210 &tls_str,
211 &sni,
212 None,
213 None,
214 None,
215 None,
216 "",
217 );
218
219 true
220}
221
222pub fn explode_shadowrocket(rocket: &str, node: &mut Proxy) -> bool {
224 if !rocket.starts_with("vmess://") {
226 return false;
227 }
228
229 let url = match Url::parse(rocket) {
231 Ok(url) => url,
232 Err(_) => return false,
233 };
234
235 let host = url.host_str().unwrap_or("").to_string();
237 let port = url.port().unwrap_or(0);
238 if port == 0 {
239 return false;
240 }
241
242 let username = url.username().to_string();
244 if username.is_empty() {
245 return false;
246 }
247
248 let decoded = match STANDARD.decode(username) {
250 Ok(decoded) => match String::from_utf8(decoded) {
251 Ok(s) => s,
252 Err(_) => return false,
253 },
254 Err(_) => return false,
255 };
256
257 let parts: Vec<&str> = decoded.split(':').collect();
259 if parts.len() < 6 {
260 return false;
261 }
262
263 let method = parts[0].to_string();
264 let id = parts[1].to_string();
265 let aid = parts[2].parse::<u16>().unwrap_or(0);
266
267 let mut net = "tcp".to_string();
269 let mut path = "/".to_string();
270 let mut host_header = host.clone();
271 let mut tls = String::new();
272 let mut sni = String::new();
273
274 for (key, value) in url.query_pairs() {
275 let value = url_decode(&value);
276 match key.as_ref() {
277 "obfs" => net = value,
278 "path" => path = value,
279 "obfsParam" => host_header = value,
280 "tls" => {
281 tls = if value == "1" {
282 "tls".to_string()
283 } else {
284 String::new()
285 }
286 }
287 "peer" => sni = value,
288 _ => {}
289 }
290 }
291
292 let remark = url_decode(url.fragment().unwrap_or(""));
294 let formatted_remark = if remark.is_empty() {
295 format!("{} ({})", host, port)
296 } else {
297 remark
298 };
299
300 *node = Proxy::vmess_construct(
302 "VMess",
303 &formatted_remark,
304 &host,
305 port,
306 "",
307 &id,
308 aid,
309 &net,
310 &method,
311 &path,
312 &host_header,
313 "",
314 &tls,
315 &sni,
316 None,
317 None,
318 None,
319 None,
320 "",
321 );
322
323 true
324}
325
326pub fn explode_kitsunebi(kit: &str, node: &mut Proxy) -> bool {
328 if !kit.starts_with("vmess://") {
330 return false;
331 }
332
333 let encoded = &kit[8..];
335
336 let decoded = match STANDARD.decode(encoded) {
338 Ok(decoded) => match String::from_utf8(decoded) {
339 Ok(s) => s,
340 Err(_) => return false,
341 },
342 Err(_) => return false,
343 };
344
345 let lines: Vec<&str> = decoded.lines().collect();
347 if lines.is_empty() {
348 return false;
349 }
350
351 let parts: Vec<&str> = lines[0].split(',').collect();
353 if parts.len() < 4 {
354 return false;
355 }
356
357 let add = parts[0].to_string();
358 let port = parts[1].parse::<u16>().unwrap_or(0);
359 let id = parts[2].to_string();
360 let aid = parts[3].parse::<u16>().unwrap_or(0);
361
362 let mut net = "tcp".to_string();
364 let mut path = "/".to_string();
365 let mut host = add.clone();
366 let mut tls = String::new();
367 let mut sni = String::new();
368 let mut remark = format!("{} ({})", add, port);
369
370 for i in 4..parts.len() {
372 let kv: Vec<&str> = parts[i].split('=').collect();
373 if kv.len() != 2 {
374 continue;
375 }
376
377 let value = kv[1].to_string();
378 match kv[0] {
379 "net" => net = value,
380 "path" => path = value,
381 "host" => host = value,
382 "tls" => tls = value,
383 "sni" => sni = value,
384 "remarks" | "remark" => remark = value,
385 _ => {}
386 }
387 }
388
389 *node = Proxy::vmess_construct(
391 "VMess", &remark, &add, port, "", &id, aid, &net, "auto", &path, &host, "", &tls, &sni,
392 None, None, None, None, "",
393 );
394
395 true
396}
397
398pub fn explode_vmess_conf(content: &str, nodes: &mut Vec<Proxy>) -> bool {
400 let json: Value = match serde_json::from_str(content) {
402 Ok(json) => json,
403 Err(_) => return false,
404 };
405
406 if json["outbounds"].is_array() {
408 let outbounds = json["outbounds"].as_array().unwrap();
410 let mut success = false;
411
412 for outbound in outbounds {
413 if outbound["protocol"].as_str().unwrap_or("") != "vmess" {
415 continue;
416 }
417
418 let settings = &outbound["settings"];
420 if !settings["vnext"].is_array() {
421 continue;
422 }
423
424 let vnext = settings["vnext"].as_array().unwrap();
426
427 for server in vnext {
428 let address = server["address"].as_str().unwrap_or("").to_string();
429 let port = server["port"].as_u64().unwrap_or(0) as u16;
430 if port == 0 {
431 continue;
432 }
433
434 if !server["users"].is_array() {
436 continue;
437 }
438
439 let users = server["users"].as_array().unwrap();
440
441 for user in users {
442 let id = user["id"].as_str().unwrap_or("").to_string();
443 let alter_id = user["alterId"].as_u64().unwrap_or(0) as u16;
444 let security = user["security"].as_str().unwrap_or("auto").to_string();
445
446 let stream_settings = &outbound["streamSettings"];
448 let network = stream_settings["network"]
449 .as_str()
450 .unwrap_or("tcp")
451 .to_string();
452 let security_type = stream_settings["security"]
453 .as_str()
454 .unwrap_or("")
455 .to_string();
456
457 let mut host = String::new();
459 let mut path = String::new();
460 let mut edge = String::new();
461 let mut tls = String::new();
462 let mut sni = String::new();
463 let mut type_field = String::new();
464
465 match network.as_str() {
466 "ws" => {
467 let ws_settings = &stream_settings["wsSettings"];
468 path = ws_settings["path"].as_str().unwrap_or("").to_string();
469
470 if let Some(headers) = ws_settings["headers"].as_object() {
471 if let Some(host_val) = headers.get("Host") {
472 host = host_val.as_str().unwrap_or("").to_string();
473 }
474 if let Some(edge_val) = headers.get("Edge") {
475 edge = edge_val.as_str().unwrap_or("").to_string();
476 }
477 }
478 }
479 "h2" => {
480 let h2_settings = &stream_settings["httpSettings"];
481 path = h2_settings["path"].as_str().unwrap_or("").to_string();
482
483 if let Some(hosts) = h2_settings["host"].as_array() {
484 if !hosts.is_empty() {
485 host = hosts[0].as_str().unwrap_or("").to_string();
486 }
487 }
488 }
489 "tcp" => {
490 let tcp_settings = &stream_settings["tcpSettings"];
491 if tcp_settings["header"]["type"].as_str().unwrap_or("") == "http" {
492 type_field = "http".to_string();
493
494 if let Some(request) = tcp_settings["header"]["request"].as_object()
495 {
496 if let Some(paths) = request.get("path") {
497 if let Some(paths_array) = paths.as_array() {
498 if !paths_array.is_empty() {
499 path = paths_array[0]
500 .as_str()
501 .unwrap_or("")
502 .to_string();
503 }
504 }
505 }
506
507 if let Some(headers) = request.get("headers") {
508 if let Some(headers_obj) = headers.as_object() {
509 if let Some(host_val) = headers_obj.get("Host") {
510 host = host_val.as_str().unwrap_or("").to_string();
511 }
512 if let Some(edge_val) = headers_obj.get("Edge") {
513 edge = edge_val.as_str().unwrap_or("").to_string();
514 }
515 }
516 }
517 }
518 }
519 }
520 _ => {}
521 }
522
523 if security_type == "tls" {
524 tls = "tls".to_string();
525 let tls_settings = &stream_settings["tlsSettings"];
526 sni = tls_settings["serverName"]
527 .as_str()
528 .unwrap_or("")
529 .to_string();
530 }
531
532 let formatted_remark = format!("{} ({})", address, port);
534
535 let node = Proxy::vmess_construct(
537 "VMess",
538 &formatted_remark,
539 &address,
540 port,
541 &type_field,
542 &id,
543 alter_id,
544 &network,
545 &security,
546 &path,
547 &host,
548 &edge,
549 &tls,
550 &sni,
551 None,
552 None,
553 None,
554 None,
555 "",
556 );
557
558 nodes.push(node);
559 success = true;
560 }
561 }
562 }
563
564 if success {
565 return true;
566 }
567 }
568
569 if json["vmess"].is_array() {
571 let mut group_map: HashMap<String, String> = HashMap::new();
572
573 if json["subItem"].is_array() {
575 let sub_items = json["subItem"].as_array().unwrap();
576 for sub_item in sub_items {
577 if let (Some(id), Some(remarks)) =
578 (sub_item["id"].as_str(), sub_item["remarks"].as_str())
579 {
580 group_map.insert(id.to_string(), remarks.to_string());
581 }
582 }
583 }
584
585 let vmess_entries = json["vmess"].as_array().unwrap();
587 let mut nodes_added = false;
588
589 for (_, entry) in vmess_entries.iter().enumerate() {
590 if entry["address"].is_null() || entry["port"].is_null() || entry["id"].is_null() {
592 continue;
593 }
594
595 let ps = entry["remarks"].as_str().unwrap_or("").to_string();
597 let add = entry["address"].as_str().unwrap_or("").to_string();
598 let port = entry["port"].as_u64().unwrap_or(0) as u16;
599 if port == 0 {
600 continue;
601 }
602
603 let sub_id = entry["subid"].as_str().unwrap_or("").to_string();
605
606 let mut group = V2RAY_DEFAULT_GROUP.to_string();
608 if !sub_id.is_empty() {
609 if let Some(sub_group) = group_map.get(&sub_id) {
610 group = sub_group.clone();
611 }
612 }
613
614 let remark = if ps.is_empty() {
616 format!("{} ({})", add, port)
617 } else {
618 ps
619 };
620
621 let config_type = entry["configType"].as_u64().unwrap_or(1);
623
624 match config_type {
626 1 => {
627 let type_field = entry["headerType"].as_str().unwrap_or("").to_string();
629 let id = entry["id"].as_str().unwrap_or("").to_string();
630 let aid = entry["alterId"].as_u64().unwrap_or(0) as u16;
631 let net = entry["network"].as_str().unwrap_or("tcp").to_string();
632 let path = entry["path"].as_str().unwrap_or("").to_string();
633 let host = entry["requestHost"].as_str().unwrap_or("").to_string();
634 let tls = entry["streamSecurity"].as_str().unwrap_or("").to_string();
635 let cipher = entry["security"].as_str().unwrap_or("auto").to_string();
636 let sni = entry["sni"].as_str().unwrap_or("").to_string();
637
638 let allow_insecure = entry["allowInsecure"].as_bool();
640
641 let node = Proxy::vmess_construct(
642 &group,
643 &remark,
644 &add,
645 port,
646 &type_field,
647 &id,
648 aid,
649 &net,
650 &cipher,
651 &path,
652 &host,
653 "",
654 &tls,
655 &sni,
656 None,
657 None,
658 allow_insecure,
659 None,
660 "",
661 );
662
663 nodes.push(node);
664 nodes_added = true;
665 }
666 3 => {
667 let id = entry["id"].as_str().unwrap_or("").to_string();
669 let cipher = entry["security"].as_str().unwrap_or("").to_string();
670
671 let allow_insecure = entry["allowInsecure"].as_bool();
672
673 let node = Proxy::ss_construct(
674 SS_DEFAULT_GROUP,
675 &remark,
676 &add,
677 port,
678 &id,
679 &cipher,
680 "",
681 "",
682 None,
683 None,
684 allow_insecure,
685 None,
686 "",
687 );
688
689 nodes.push(node);
690 nodes_added = true;
691 }
692 4 => {
693 let allow_insecure = entry["allowInsecure"].as_bool();
695
696 let node = Proxy::socks_construct(
697 SOCKS_DEFAULT_GROUP,
698 &remark,
699 &add,
700 port,
701 "",
702 "",
703 None,
704 None,
705 allow_insecure,
706 "",
707 );
708
709 nodes.push(node);
710 nodes_added = true;
711 }
712 _ => continue,
713 }
714 }
715
716 return nodes_added;
717 }
718
719 false
720}