libsubconverter/parser/explodes/
wireguard.rs

1use crate::{utils::url_decode, Proxy};
2use regex::Regex;
3use std::collections::HashMap;
4use url::Url;
5
6/// Parse a WireGuard link into a Proxy object
7pub fn explode_wireguard(wireguard: &str, node: &mut Proxy) -> bool {
8    // Check if the link starts with wireguard://
9    if !wireguard.starts_with("wireguard://") {
10        return false;
11    }
12
13    // Try to parse as URL
14    let url = match Url::parse(wireguard) {
15        Ok(url) => url,
16        Err(_) => return false,
17    };
18
19    // Extract parameters from the query string
20    let mut params = HashMap::new();
21    for (key, value) in url.query_pairs() {
22        params.insert(key.to_string(), url_decode(&value));
23    }
24
25    // Extract required fields
26    let private_key = match params.get("privateKey") {
27        Some(key) => key,
28        None => return false,
29    };
30
31    let public_key = match params.get("publicKey") {
32        Some(key) => key,
33        None => return false,
34    };
35
36    // Extract host and port
37    let host = match url.host_str() {
38        Some(host) => host,
39        None => return false,
40    };
41    let port = url.port().unwrap_or(51820);
42
43    // Extract optional fields
44    let preshared_key = params.get("presharedKey").map(|s| s.as_str()).unwrap_or("");
45    let self_ip = params
46        .get("selfIP")
47        .map(|s| s.as_str())
48        .unwrap_or("10.0.0.2");
49    let self_ipv6 = params.get("selfIPv6").map(|s| s.as_str()).unwrap_or("");
50    let mtu = params
51        .get("mtu")
52        .map(|s| s.parse::<u16>().unwrap_or(1420))
53        .unwrap_or(1420);
54    let keep_alive = params
55        .get("keepAlive")
56        .map(|s| s.parse::<u16>().unwrap_or(25))
57        .unwrap_or(25);
58
59    // Extract DNS servers
60    let dns_str = params.get("dns").map(|s| s.as_str()).unwrap_or("");
61    let dns_servers: Vec<String> = if dns_str.is_empty() {
62        vec!["1.1.1.1".to_string()]
63    } else {
64        dns_str.split(',').map(|s| s.trim().to_string()).collect()
65    };
66
67    // Extract remark from the fragment
68    let remark = url_decode(url.fragment().unwrap_or(""));
69    let formatted_remark = if remark.is_empty() {
70        format!("{} ({})", host, port)
71    } else {
72        remark.to_string()
73    };
74
75    // Create the proxy object
76    *node = Proxy::wireguard_construct(
77        "WireGuard".to_string(),
78        formatted_remark,
79        host.to_string(),
80        port,
81        self_ip.to_string(),
82        self_ipv6.to_string(),
83        private_key.to_string(),
84        public_key.to_string(),
85        preshared_key.to_string(),
86        dns_servers,
87        Some(mtu),
88        Some(keep_alive),
89        "https://www.gstatic.com/generate_204".to_string(),
90        "".to_string(),
91        None,
92        None,
93    );
94    parse_peers(wireguard, node);
95
96    true
97}
98
99/// Parse WireGuard peers from configuration text
100pub fn parse_peers(data: &str, node: &mut Proxy) -> bool {
101    // Find peers enclosed in parentheses
102    let peer_regex = Regex::new(r"\((.*?)\)").unwrap();
103    let peers: Vec<&str> = peer_regex
104        .captures_iter(data)
105        .filter_map(|cap| cap.get(1))
106        .map(|m| m.as_str())
107        .collect();
108
109    if peers.is_empty() {
110        return false;
111    }
112
113    // Take the first peer
114    let peer = peers[0];
115
116    // Extract key-value pairs
117    let pair_regex = Regex::new(r#"([a-z-]+) ?= ?([^" ),]+|".*?"),? ?"#).unwrap();
118    let pairs: Vec<(String, String)> = pair_regex
119        .captures_iter(peer)
120        .filter_map(|cap| {
121            if let (Some(key), Some(val)) = (cap.get(1), cap.get(2)) {
122                Some((key.as_str().to_string(), val.as_str().to_string()))
123            } else {
124                None
125            }
126        })
127        .collect();
128
129    if pairs.is_empty() {
130        return false;
131    }
132
133    // Process key-value pairs
134    for (key, val) in pairs {
135        match key.as_str() {
136            "public-key" => {
137                node.public_key = Some(val);
138            }
139            "endpoint" => {
140                if let Some(idx) = val.rfind(':') {
141                    node.hostname = val[..idx].to_string();
142                    if let Ok(port) = val[idx + 1..].parse::<u16>() {
143                        node.port = port;
144                    }
145                }
146            }
147            "client-id" => {
148                node.client_id = Some(val);
149            }
150            "allowed-ips" => {
151                node.allowed_ips = val.trim_matches('"').to_string();
152            }
153            _ => {}
154        }
155    }
156
157    true
158}