1use crate::{
2 models::{
3 Proxy, HTTP_DEFAULT_GROUP, HYSTERIA2_DEFAULT_GROUP, HYSTERIA_DEFAULT_GROUP,
4 SNELL_DEFAULT_GROUP, SOCKS_DEFAULT_GROUP, SSR_DEFAULT_GROUP, SS_DEFAULT_GROUP,
5 TROJAN_DEFAULT_GROUP, V2RAY_DEFAULT_GROUP, WG_DEFAULT_GROUP,
6 },
7 parser::yaml::clash::parse_clash_yaml,
8};
9use serde_yaml::Value;
10
11pub fn explode_clash(content: &str, nodes: &mut Vec<Proxy>) -> bool {
13 match parse_clash_yaml(content) {
15 Ok(mut proxies) => {
16 if !proxies.is_empty() {
17 nodes.append(&mut proxies);
18 return true;
19 }
20 }
21 Err(e) => {
22 eprintln!("新YAML解析器失败: {}", e);
24 }
25 }
26
27 let yaml: Value = match serde_yaml::from_str(content) {
30 Ok(y) => y,
31 Err(_) => return false,
32 };
33
34 let proxies = match yaml.get("proxies") {
36 Some(Value::Sequence(seq)) => seq,
37 _ => match yaml.get("Proxy") {
38 Some(Value::Sequence(seq)) => seq,
39 _ => return false,
40 },
41 };
42
43 let mut success = false;
44
45 for proxy in proxies {
47 if let Some(node) = parse_clash_proxy(proxy) {
48 nodes.push(node);
49 success = true;
50 }
51 }
52
53 success
54}
55
56fn parse_clash_proxy(proxy: &Value) -> Option<Proxy> {
58 let proxy_type = match proxy.get("type") {
60 Some(Value::String(t)) => t.to_lowercase(),
61 _ => return None,
62 };
63
64 let name = proxy.get("name").and_then(|v| v.as_str()).unwrap_or("");
66 let server = proxy.get("server").and_then(|v| v.as_str()).unwrap_or("");
67 let port_value = proxy.get("port").and_then(|v| v.as_u64()).unwrap_or(0);
68 let port = port_value as u16;
69
70 if name.is_empty() || server.is_empty() || port == 0 {
72 return None;
73 }
74
75 let udp = proxy.get("udp").and_then(|v| v.as_bool());
77 let tfo = proxy.get("tfo").and_then(|v| v.as_bool());
78 let skip_cert_verify = proxy.get("skip-cert-verify").and_then(|v| v.as_bool());
79
80 match proxy_type.as_str() {
82 "ss" | "shadowsocks" => {
83 parse_clash_ss(proxy, name, server, port, udp, tfo, skip_cert_verify)
84 }
85 "ssr" | "shadowsocksr" => {
86 parse_clash_ssr(proxy, name, server, port, udp, tfo, skip_cert_verify)
87 }
88 "vmess" => parse_clash_vmess(proxy, name, server, port, udp, tfo, skip_cert_verify),
89 "socks" | "socks5" => {
90 parse_clash_socks(proxy, name, server, port, udp, tfo, skip_cert_verify)
91 }
92 "http" => parse_clash_http(proxy, name, server, port, false, tfo, skip_cert_verify),
93 "https" => parse_clash_http(proxy, name, server, port, true, tfo, skip_cert_verify),
94 "trojan" => parse_clash_trojan(proxy, name, server, port, udp, tfo, skip_cert_verify),
95 "snell" => parse_clash_snell(proxy, name, server, port, udp, tfo, skip_cert_verify),
96 "wireguard" => parse_clash_wireguard(proxy, name, server, port, udp),
97 "hysteria" => parse_clash_hysteria(proxy, name, server, port, tfo, skip_cert_verify),
98 "hysteria2" => parse_clash_hysteria2(proxy, name, server, port, tfo, skip_cert_verify),
99 _ => None,
100 }
101}
102
103fn parse_clash_ss(
105 proxy: &Value,
106 name: &str,
107 server: &str,
108 port: u16,
109 udp: Option<bool>,
110 tfo: Option<bool>,
111 skip_cert_verify: Option<bool>,
112) -> Option<Proxy> {
113 let password = proxy.get("password").and_then(|v| v.as_str()).unwrap_or("");
115 let method = proxy.get("cipher").and_then(|v| v.as_str()).unwrap_or("");
116
117 if password.is_empty() || method.is_empty() {
118 return None;
119 }
120
121 let underlying_proxy = proxy
123 .get("underlying-proxy")
124 .and_then(|v| v.as_str())
125 .unwrap_or("");
126
127 let mut plugin = "";
129 let mut pluginopts_mode = "";
130 let mut pluginopts_host = "";
131 let mut path = "";
132 let mut tls = "";
133 let mut pluginopts_mux = "";
134 let mut pluginopts = String::new();
135
136 if let Some(plugin_val) = proxy.get("plugin").and_then(|v| v.as_str()) {
138 match plugin_val {
139 "obfs" => {
140 plugin = "obfs-local";
141 if let Some(plugin_opts) = proxy.get("plugin-opts").and_then(|v| v.as_mapping()) {
142 if let Some(mode) = plugin_opts
143 .get(&Value::String("mode".to_string()))
144 .and_then(|v| v.as_str())
145 {
146 pluginopts_mode = mode;
147 }
148 if let Some(host) = plugin_opts
149 .get(&Value::String("host".to_string()))
150 .and_then(|v| v.as_str())
151 {
152 pluginopts_host = host;
153 }
154 }
155 }
156 "v2ray-plugin" => {
157 plugin = "v2ray-plugin";
158 if let Some(plugin_opts) = proxy.get("plugin-opts").and_then(|v| v.as_mapping()) {
159 if let Some(mode) = plugin_opts
160 .get(&Value::String("mode".to_string()))
161 .and_then(|v| v.as_str())
162 {
163 pluginopts_mode = mode;
164 }
165 if let Some(host) = plugin_opts
166 .get(&Value::String("host".to_string()))
167 .and_then(|v| v.as_str())
168 {
169 pluginopts_host = host;
170 }
171 if let Some(plugin_tls) = plugin_opts
172 .get(&Value::String("tls".to_string()))
173 .and_then(|v| v.as_bool())
174 {
175 tls = if plugin_tls { "tls;" } else { "" };
176 }
177 if let Some(plugin_path) = plugin_opts
178 .get(&Value::String("path".to_string()))
179 .and_then(|v| v.as_str())
180 {
181 path = plugin_path;
182 }
183 if let Some(mux) = plugin_opts
184 .get(&Value::String("mux".to_string()))
185 .and_then(|v| v.as_bool())
186 {
187 pluginopts_mux = if mux { "mux=4;" } else { "" };
188 }
189 }
190 }
191 _ => {}
192 }
193 } else if let Some(obfs) = proxy.get("obfs").and_then(|v| v.as_str()) {
194 plugin = "obfs-local";
196 pluginopts_mode = obfs;
197 if let Some(obfs_host) = proxy.get("obfs-host").and_then(|v| v.as_str()) {
198 pluginopts_host = obfs_host;
199 }
200 }
201
202 match plugin {
204 "simple-obfs" | "obfs-local" => {
205 pluginopts = format!("obfs={}", pluginopts_mode);
206 if !pluginopts_host.is_empty() {
207 pluginopts.push_str(&format!(";obfs-host={}", pluginopts_host));
208 }
209 }
210 "v2ray-plugin" => {
211 pluginopts = format!("mode={};{}{}", pluginopts_mode, tls, pluginopts_mux);
212 if !pluginopts_host.is_empty() {
213 pluginopts.push_str(&format!("host={};", pluginopts_host));
214 }
215 if !path.is_empty() {
216 pluginopts.push_str(&format!("path={};", path));
217 }
218 if !pluginopts_mux.is_empty() {
219 pluginopts.push_str(&format!("mux={};", pluginopts_mux));
220 }
221 }
222 _ => {}
223 }
224
225 let mut cipher = method;
227 if cipher == "AEAD_CHACHA20_POLY1305" {
228 cipher = "chacha20-ietf-poly1305";
229 } else if cipher.contains("AEAD") {
230 }
232
233 let pluginopts_str = Box::leak(pluginopts.into_boxed_str());
235
236 Some(Proxy::ss_construct(
237 SS_DEFAULT_GROUP,
238 name,
239 server,
240 port,
241 password,
242 cipher,
243 plugin,
244 pluginopts_str,
245 udp,
246 tfo,
247 skip_cert_verify,
248 None,
249 underlying_proxy,
250 ))
251}
252
253fn parse_clash_ssr(
255 proxy: &Value,
256 name: &str,
257 server: &str,
258 port: u16,
259 udp: Option<bool>,
260 tfo: Option<bool>,
261 skip_cert_verify: Option<bool>,
262) -> Option<Proxy> {
263 let password = proxy.get("password").and_then(|v| v.as_str()).unwrap_or("");
265 let method = proxy.get("cipher").and_then(|v| v.as_str()).unwrap_or("");
266 let protocol = proxy.get("protocol").and_then(|v| v.as_str()).unwrap_or("");
267 let protocol_param = proxy
268 .get("protocol-param")
269 .and_then(|v| v.as_str())
270 .unwrap_or("");
271 let obfs = proxy.get("obfs").and_then(|v| v.as_str()).unwrap_or("");
272 let obfs_param = proxy
273 .get("obfs-param")
274 .and_then(|v| v.as_str())
275 .unwrap_or("");
276
277 let underlying_proxy = proxy
279 .get("underlying-proxy")
280 .and_then(|v| v.as_str())
281 .unwrap_or("");
282
283 if password.is_empty() || method.is_empty() || protocol.is_empty() || obfs.is_empty() {
284 return None;
285 }
286
287 Some(Proxy::ssr_construct(
288 SSR_DEFAULT_GROUP,
289 name,
290 server,
291 port,
292 protocol,
293 method,
294 obfs,
295 password,
296 obfs_param,
297 protocol_param,
298 udp,
299 tfo,
300 skip_cert_verify,
301 underlying_proxy,
302 ))
303}
304
305fn parse_clash_vmess(
307 proxy: &Value,
308 name: &str,
309 server: &str,
310 port: u16,
311 udp: Option<bool>,
312 tfo: Option<bool>,
313 skip_cert_verify: Option<bool>,
314) -> Option<Proxy> {
315 let uuid = proxy.get("uuid").and_then(|v| v.as_str()).unwrap_or("");
317 let alter_id_val = proxy.get("alterId").and_then(|v| v.as_u64()).unwrap_or(0);
318 let alter_id = alter_id_val as u16;
319 let cipher = proxy
320 .get("cipher")
321 .and_then(|v| v.as_str())
322 .unwrap_or("auto");
323
324 let underlying_proxy = proxy
326 .get("underlying-proxy")
327 .and_then(|v| v.as_str())
328 .unwrap_or("");
329
330 if uuid.is_empty() {
331 return None;
332 }
333
334 let network = proxy
336 .get("network")
337 .and_then(|v| v.as_str())
338 .unwrap_or("tcp");
339
340 let tls = proxy.get("tls").and_then(|v| v.as_bool()).unwrap_or(false);
342 let sni = proxy
343 .get("servername")
344 .and_then(|v| v.as_str())
345 .unwrap_or("");
346
347 let mut host = String::new();
349 let mut path = String::new();
350
351 if let Some(ws_opts) = proxy.get("ws-opts").and_then(|v| v.as_mapping()) {
353 if let Some(path_val) = ws_opts
354 .get(&Value::String("path".to_string()))
355 .and_then(|v| v.as_str())
356 {
357 path = path_val.to_string();
358 }
359
360 if let Some(headers) = ws_opts
361 .get(&Value::String("headers".to_string()))
362 .and_then(|v| v.as_mapping())
363 {
364 if let Some(host_val) = headers
365 .get(&Value::String("Host".to_string()))
366 .and_then(|v| v.as_str())
367 {
368 host = host_val.to_string();
369 }
370 }
371 }
372 else if let Some(h2_opts) = proxy.get("h2-opts").and_then(|v| v.as_mapping()) {
374 if let Some(path_val) = h2_opts
375 .get(&Value::String("path".to_string()))
376 .and_then(|v| v.as_str())
377 {
378 path = path_val.to_string();
379 }
380
381 if let Some(hosts) = h2_opts
382 .get(&Value::String("host".to_string()))
383 .and_then(|v| v.as_sequence())
384 {
385 if !hosts.is_empty() {
386 if let Some(first_host) = hosts.get(0).and_then(|v| v.as_str()) {
387 host = first_host.to_string();
388 }
389 }
390 }
391 }
392 else if let Some(http_opts) = proxy.get("http-opts").and_then(|v| v.as_mapping()) {
394 if let Some(paths) = http_opts
395 .get(&Value::String("path".to_string()))
396 .and_then(|v| v.as_sequence())
397 {
398 if !paths.is_empty() {
399 if let Some(first_path) = paths.get(0).and_then(|v| v.as_str()) {
400 path = first_path.to_string();
401 }
402 }
403 }
404
405 if let Some(hosts) = http_opts
406 .get(&Value::String("host".to_string()))
407 .and_then(|v| v.as_sequence())
408 {
409 if !hosts.is_empty() {
410 if let Some(first_host) = hosts.get(0).and_then(|v| v.as_str()) {
411 host = first_host.to_string();
412 }
413 }
414 }
415 }
416 else if let Some(grpc_opts) = proxy.get("grpc-opts").and_then(|v| v.as_mapping()) {
418 if let Some(service_name) = grpc_opts
419 .get(&Value::String("grpc-service-name".to_string()))
420 .and_then(|v| v.as_str())
421 {
422 path = service_name.to_string();
423 }
424 }
425
426 let final_path = if path.is_empty() { "/" } else { &path };
428
429 let edge = "";
431
432 Some(Proxy::vmess_construct(
433 V2RAY_DEFAULT_GROUP,
434 name,
435 server,
436 port,
437 "", uuid,
439 alter_id,
440 network,
441 cipher,
442 final_path,
443 &host,
444 edge,
445 if tls { "tls" } else { "" },
446 sni,
447 udp,
448 tfo,
449 skip_cert_verify,
450 None,
451 underlying_proxy,
452 ))
453}
454
455fn parse_clash_socks(
457 proxy: &Value,
458 name: &str,
459 server: &str,
460 port: u16,
461 udp: Option<bool>,
462 tfo: Option<bool>,
463 skip_cert_verify: Option<bool>,
464) -> Option<Proxy> {
465 let username = proxy.get("username").and_then(|v| v.as_str()).unwrap_or("");
467 let password = proxy.get("password").and_then(|v| v.as_str()).unwrap_or("");
468
469 let underlying_proxy = proxy
471 .get("underlying-proxy")
472 .and_then(|v| v.as_str())
473 .unwrap_or("");
474
475 Some(Proxy::socks_construct(
476 SOCKS_DEFAULT_GROUP,
477 name,
478 server,
479 port,
480 username,
481 password,
482 udp,
483 tfo,
484 skip_cert_verify,
485 underlying_proxy,
486 ))
487}
488
489fn parse_clash_http(
491 proxy: &Value,
492 name: &str,
493 server: &str,
494 port: u16,
495 is_https: bool,
496 tfo: Option<bool>,
497 skip_cert_verify: Option<bool>,
498) -> Option<Proxy> {
499 let username = proxy.get("username").and_then(|v| v.as_str()).unwrap_or("");
501 let password = proxy.get("password").and_then(|v| v.as_str()).unwrap_or("");
502
503 let underlying_proxy = proxy
505 .get("underlying-proxy")
506 .and_then(|v| v.as_str())
507 .unwrap_or("");
508
509 Some(Proxy::http_construct(
510 HTTP_DEFAULT_GROUP,
511 name,
512 server,
513 port,
514 username,
515 password,
516 is_https,
517 tfo,
518 skip_cert_verify,
519 None,
520 underlying_proxy,
521 ))
522}
523
524fn parse_clash_trojan(
526 proxy: &Value,
527 name: &str,
528 server: &str,
529 port: u16,
530 udp: Option<bool>,
531 tfo: Option<bool>,
532 skip_cert_verify: Option<bool>,
533) -> Option<Proxy> {
534 let password = proxy.get("password").and_then(|v| v.as_str()).unwrap_or("");
536
537 if password.is_empty() {
538 return None;
539 }
540
541 let underlying_proxy = proxy
543 .get("underlying-proxy")
544 .and_then(|v| v.as_str())
545 .unwrap_or("");
546
547 let sni = proxy.get("sni").and_then(|v| v.as_str()).unwrap_or("");
549 let network = proxy.get("network").and_then(|v| v.as_str()).unwrap_or("");
550
551 let mut host = String::new();
553 let mut path = String::new();
554
555 if network == "ws" && proxy.get("ws-opts").is_some() {
557 if let Some(ws_opts) = proxy.get("ws-opts").and_then(|v| v.as_mapping()) {
558 if let Some(path_val) = ws_opts
559 .get(&Value::String("path".to_string()))
560 .and_then(|v| v.as_str())
561 {
562 path = path_val.to_string();
563 }
564
565 if let Some(headers) = ws_opts
566 .get(&Value::String("headers".to_string()))
567 .and_then(|v| v.as_mapping())
568 {
569 if let Some(host_val) = headers
570 .get(&Value::String("Host".to_string()))
571 .and_then(|v| v.as_str())
572 {
573 host = host_val.to_string();
574 }
575 }
576 }
577 }
578
579 Some(Proxy::trojan_construct(
580 TROJAN_DEFAULT_GROUP.to_string(),
581 name.to_string(),
582 server.to_string(),
583 port,
584 password.to_string(),
585 Some(network.to_string()),
586 Some(host),
587 Some(path),
588 Some(sni.to_owned()),
589 true, udp,
591 tfo,
592 skip_cert_verify,
593 None,
594 Some(underlying_proxy.to_string()),
595 ))
596}
597
598fn parse_clash_snell(
600 proxy: &Value,
601 name: &str,
602 server: &str,
603 port: u16,
604 udp: Option<bool>,
605 tfo: Option<bool>,
606 skip_cert_verify: Option<bool>,
607) -> Option<Proxy> {
608 let psk = proxy.get("psk").and_then(|v| v.as_str()).unwrap_or("");
610
611 if psk.is_empty() {
612 return None;
613 }
614
615 let underlying_proxy = proxy
617 .get("underlying-proxy")
618 .and_then(|v| v.as_str())
619 .unwrap_or("");
620
621 let version = proxy.get("version").and_then(|v| v.as_u64()).unwrap_or(1) as u16;
623 let obfs = proxy.get("obfs").and_then(|v| v.as_str()).unwrap_or("");
624 let obfs_host = proxy
625 .get("obfs-host")
626 .and_then(|v| v.as_str())
627 .unwrap_or("");
628
629 Some(Proxy::snell_construct(
630 SNELL_DEFAULT_GROUP.to_string(),
631 name.to_string(),
632 server.to_string(),
633 port,
634 psk.to_string(),
635 obfs.to_string(),
636 obfs_host.to_string(),
637 version,
638 udp,
639 tfo,
640 skip_cert_verify,
641 Some(underlying_proxy.to_string()),
642 ))
643}
644
645fn parse_clash_wireguard(
647 proxy: &Value,
648 name: &str,
649 server: &str,
650 port: u16,
651 udp: Option<bool>,
652) -> Option<Proxy> {
653 let private_key = proxy
655 .get("privateKey")
656 .and_then(|v| v.as_str())
657 .unwrap_or("");
658 let public_key = proxy
659 .get("publicKey")
660 .and_then(|v| v.as_str())
661 .unwrap_or("");
662 let preshared_key = proxy
663 .get("presharedKey")
664 .and_then(|v| v.as_str())
665 .unwrap_or("");
666
667 if private_key.is_empty() || public_key.is_empty() {
668 return None;
669 }
670
671 let underlying_proxy = proxy
673 .get("underlying-proxy")
674 .and_then(|v| v.as_str())
675 .unwrap_or("");
676
677 let self_ip = proxy.get("ip").and_then(|v| v.as_str()).unwrap_or("");
679 let self_ipv6 = proxy.get("ipv6").and_then(|v| v.as_str()).unwrap_or("");
680
681 let mtu_value = proxy.get("mtu").and_then(|v| v.as_u64()).unwrap_or(0);
683 let mtu = if mtu_value > 0 {
684 Some(mtu_value as u16)
685 } else {
686 None
687 };
688
689 let keepalive_value = proxy.get("keepalive").and_then(|v| v.as_u64()).unwrap_or(0);
690 let keepalive = if keepalive_value > 0 {
691 Some(keepalive_value as u16)
692 } else {
693 None
694 };
695
696 let mut dns_servers = Vec::new();
698 if let Some(Value::Sequence(dns_seq)) = proxy.get("dns") {
699 for dns in dns_seq {
700 if let Some(dns_str) = dns.as_str() {
701 dns_servers.push(dns_str.to_string());
702 }
703 }
704 }
705
706 let client_id = proxy.get("clientId").and_then(|v| v.as_str()).unwrap_or("");
708 let test_url = proxy.get("testUrl").and_then(|v| v.as_str()).unwrap_or("");
709
710 Some(Proxy::wireguard_construct(
711 WG_DEFAULT_GROUP.to_string(),
712 name.to_string(),
713 server.to_string(),
714 port,
715 self_ip.to_string(),
716 self_ipv6.to_string(),
717 private_key.to_string(),
718 public_key.to_string(),
719 preshared_key.to_string(),
720 dns_servers,
721 mtu,
722 keepalive,
723 test_url.to_string(),
724 client_id.to_string(),
725 udp,
726 Some(underlying_proxy.to_string()),
727 ))
728}
729
730fn parse_clash_hysteria(
732 proxy: &Value,
733 name: &str,
734 server: &str,
735 port: u16,
736 tfo: Option<bool>,
737 skip_cert_verify: Option<bool>,
738) -> Option<Proxy> {
739 let auth = proxy.get("auth").and_then(|v| v.as_str()).unwrap_or("");
741 let auth_str = proxy.get("auth-str").and_then(|v| v.as_str()).unwrap_or("");
742 let obfs = proxy.get("obfs").and_then(|v| v.as_str()).unwrap_or("");
743 let protocol = proxy
744 .get("protocol")
745 .and_then(|v| v.as_str())
746 .unwrap_or("udp");
747
748 let underlying_proxy = proxy
750 .get("underlying-proxy")
751 .and_then(|v| v.as_str())
752 .unwrap_or("");
753
754 let ports = proxy.get("ports").and_then(|v| v.as_str()).unwrap_or("");
756
757 let up_mbps = proxy.get("up").and_then(|v| v.as_u64()).unwrap_or(0);
759 let down_mbps = proxy.get("down").and_then(|v| v.as_u64()).unwrap_or(0);
760 let up_speed = if up_mbps > 0 {
761 Some(up_mbps as u32)
762 } else {
763 None
764 };
765 let down_speed = if down_mbps > 0 {
766 Some(down_mbps as u32)
767 } else {
768 None
769 };
770
771 let sni = proxy.get("sni").and_then(|v| v.as_str()).unwrap_or("");
773 let alpn_value = proxy.get("alpn").and_then(|v| v.as_str()).unwrap_or("");
774 let mut alpn = Vec::new();
775 if !alpn_value.is_empty() {
776 alpn.push(alpn_value.to_string());
777 }
778
779 let fingerprint = proxy
780 .get("fingerprint")
781 .and_then(|v| v.as_str())
782 .unwrap_or("");
783 let ca = proxy.get("ca").and_then(|v| v.as_str()).unwrap_or("");
784 let ca_str = proxy.get("ca-str").and_then(|v| v.as_str()).unwrap_or("");
785
786 let recv_window_conn_value = proxy
788 .get("recv-window-conn")
789 .and_then(|v| v.as_u64())
790 .unwrap_or(0);
791 let recv_window_value = proxy
792 .get("recv-window")
793 .and_then(|v| v.as_u64())
794 .unwrap_or(0);
795 let recv_window_conn = if recv_window_conn_value > 0 {
796 Some(recv_window_conn_value as u32)
797 } else {
798 None
799 };
800 let recv_window = if recv_window_value > 0 {
801 Some(recv_window_value as u32)
802 } else {
803 None
804 };
805
806 let disable_mtu_discovery = proxy.get("disable-mtu-discovery").and_then(|v| v.as_bool());
807
808 let hop_interval_value = proxy
809 .get("hop-interval")
810 .and_then(|v| v.as_u64())
811 .unwrap_or(0);
812 let hop_interval = if hop_interval_value > 0 {
813 Some(hop_interval_value as u32)
814 } else {
815 None
816 };
817
818 Some(Proxy::hysteria_construct(
819 HYSTERIA_DEFAULT_GROUP.to_string(),
820 name.to_string(),
821 server.to_string(),
822 port,
823 ports.to_string(),
824 protocol.to_string(),
825 "".to_string(), up_speed,
827 down_speed,
828 if !auth.is_empty() {
829 auth.to_string()
830 } else {
831 auth_str.to_string()
832 },
833 obfs.to_string(),
834 sni.to_string(),
835 fingerprint.to_string(),
836 ca.to_string(),
837 ca_str.to_string(),
838 recv_window_conn,
839 recv_window,
840 disable_mtu_discovery,
841 hop_interval,
842 alpn,
843 tfo,
844 skip_cert_verify,
845 Some(underlying_proxy.to_string()),
846 ))
847}
848
849fn parse_clash_hysteria2(
851 proxy: &Value,
852 name: &str,
853 server: &str,
854 port: u16,
855 tfo: Option<bool>,
856 skip_cert_verify: Option<bool>,
857) -> Option<Proxy> {
858 let password = proxy
860 .get("password")
861 .and_then(|v| v.as_str())
862 .unwrap_or("")
863 .to_owned();
864
865 let underlying_proxy = match proxy.get("underlying-proxy").and_then(|v| v.as_str()) {
867 Some(v) => Some(v.to_owned()),
868 None => None,
869 };
870
871 let obfs = match proxy.get("obfs").and_then(|v| v.as_str()) {
873 Some(v) => Some(v.to_owned()),
874 None => None,
875 };
876 let obfs_password = match proxy.get("obfs-password").and_then(|v| v.as_str()) {
877 Some(v) => Some(v.to_owned()),
878 None => None,
879 };
880
881 let ports = match proxy.get("ports").and_then(|v| v.as_str()) {
883 Some(v) => Some(v.to_owned()),
884 None => None,
885 };
886 let up_mbps = match proxy.get("up").and_then(|v| v.as_u64()) {
888 Some(v) => Some(v as u32),
889 None => None,
890 };
891 let down_mbps = match proxy.get("down").and_then(|v| v.as_u64()) {
892 Some(v) => Some(v as u32),
893 None => None,
894 };
895
896 let sni = match proxy.get("sni").and_then(|v| v.as_str()) {
898 Some(v) => Some(v.to_owned()),
899 None => None,
900 };
901 let alpn = proxy
902 .get("alpn")
903 .and_then(|v| v.as_sequence())
904 .map(|v| {
905 v.iter()
906 .map(|v| v.as_str().unwrap_or("").to_owned())
907 .collect()
908 })
909 .unwrap_or_default();
910
911 let fingerprint = match proxy.get("fingerprint").and_then(|v| v.as_str()) {
912 Some(v) => Some(v.to_owned()),
913 None => None,
914 };
915 let ca = match proxy.get("ca").and_then(|v| v.as_str()) {
916 Some(v) => Some(v.to_owned()),
917 None => None,
918 };
919 let ca_str = match proxy.get("ca-str").and_then(|v| v.as_str()) {
920 Some(v) => Some(v.to_owned()),
921 None => None,
922 };
923
924 let cwnd_value = proxy.get("cwnd").and_then(|v| v.as_u64()).unwrap_or(0);
926 let cwnd = if cwnd_value > 0 {
927 Some(cwnd_value as u32)
928 } else {
929 None
930 };
931
932 Some(Proxy::hysteria2_construct(
933 HYSTERIA2_DEFAULT_GROUP.to_string(),
934 name.to_string(),
935 server.to_string(),
936 port,
937 ports,
938 up_mbps,
939 down_mbps,
940 password,
941 obfs,
942 obfs_password,
943 sni,
944 fingerprint,
945 alpn,
946 ca,
947 ca_str,
948 cwnd,
949 tfo,
950 skip_cert_verify,
951 underlying_proxy,
952 ))
953}