libsubconverter/parser/explodes/
socks.rs

1use crate::models::{Proxy, SOCKS_DEFAULT_GROUP};
2use crate::utils::base64::url_safe_base64_decode;
3use std::collections::HashMap;
4use url::Url;
5
6/// Parse a SOCKS link into a Proxy object
7pub fn explode_socks(link: &str, node: &mut Proxy) -> bool {
8    // Check if it's a v2rayN style socks link
9    if link.starts_with("socks://") {
10        return parse_v2rayn_socks(link, node);
11    }
12    // Check if it's a Telegram style socks link
13    else if link.starts_with("https://t.me/socks") || link.starts_with("tg://socks") {
14        return parse_telegram_socks(link, node);
15    }
16
17    false
18}
19
20/// Parse a v2rayN style socks link
21/// Format: socks://BASE64(username:password@server:port)#remarks
22fn parse_v2rayn_socks(link: &str, node: &mut Proxy) -> bool {
23    // Extract remarks if present
24    let mut remarks = String::new();
25    let mut trimmed_link = link.to_string();
26    if let Some(pos) = link.find('#') {
27        remarks = link[pos + 1..].to_string();
28        trimmed_link = link[..pos].to_string();
29    }
30
31    // Decode the base64 part
32    let decoded = url_safe_base64_decode(&trimmed_link[8..]);
33    if decoded.is_empty() {
34        return false;
35    }
36
37    // Parse the decoded content
38    let mut username = String::new();
39    let mut password = String::new();
40    let mut _server = String::new();
41    let mut _port = 0;
42
43    if decoded.contains('@') {
44        let parts: Vec<&str> = decoded.split('@').collect();
45        if parts.len() < 2 {
46            return false;
47        }
48
49        // Parse userinfo
50        let userinfo: Vec<&str> = parts[0].split(':').collect();
51        if userinfo.len() < 2 {
52            return false;
53        }
54        username = userinfo[0].to_string();
55        password = userinfo[1].to_string();
56
57        // Parse server and port
58        let server_port: Vec<&str> = parts[1].split(':').collect();
59        if server_port.len() < 2 {
60            return false;
61        }
62        _server = server_port[0].to_string();
63        _port = match server_port[1].parse::<u16>() {
64            Ok(p) => p,
65            Err(_) => return false,
66        };
67    } else {
68        // No authentication, just server and port
69        let server_port: Vec<&str> = decoded.split(':').collect();
70        if server_port.len() < 2 {
71            return false;
72        }
73        _server = server_port[0].to_string();
74        _port = match server_port[1].parse::<u16>() {
75            Ok(p) => p,
76            Err(_) => return false,
77        };
78    }
79
80    if _port == 0 {
81        return false;
82    }
83
84    // Use default remark if none provided
85    if remarks.is_empty() {
86        remarks = format!("{} ({})", _server, _port);
87    }
88
89    // Create the proxy object
90    *node = Proxy::socks_construct(
91        SOCKS_DEFAULT_GROUP,
92        &remarks,
93        &_server,
94        _port,
95        &username,
96        &password,
97        None,
98        None,
99        None,
100        "",
101    );
102
103    true
104}
105
106/// Parse a Telegram style socks link
107/// Format: tg://socks?server=x&port=x&user=x&pass=x&remarks=x&group=x
108/// or https://t.me/socks?server=x&port=x&user=x&pass=x&remarks=x&group=x
109fn parse_telegram_socks(link: &str, node: &mut Proxy) -> bool {
110    // Try to parse as URL
111    let url = match Url::parse(link) {
112        Ok(url) => url,
113        Err(_) => return false,
114    };
115
116    // Extract query parameters
117    let query_pairs: HashMap<String, String> = url
118        .query_pairs()
119        .map(|(k, v)| (k.to_string(), v.to_string()))
120        .collect();
121
122    // Get required parameters
123    let server = match query_pairs.get("server") {
124        Some(s) => s,
125        None => return false,
126    };
127
128    let port_str = match query_pairs.get("port") {
129        Some(p) => p,
130        None => return false,
131    };
132
133    let port = match port_str.parse::<u16>() {
134        Ok(p) => p,
135        Err(_) => return false,
136    };
137
138    if port == 0 {
139        return false;
140    }
141
142    // Get optional parameters
143    let username = query_pairs.get("user").map_or("", |s| s);
144    let password = query_pairs.get("pass").map_or("", |s| s);
145
146    let group = query_pairs.get("group").map_or(SOCKS_DEFAULT_GROUP, |s| s);
147
148    let remarks = if let Some(r) = query_pairs.get("remarks") {
149        if !r.is_empty() {
150            r.as_str()
151        } else {
152            &format!("{} ({})", server, port)
153        }
154    } else {
155        &format!("{} ({})", server, port)
156    };
157
158    // Create the proxy object
159    *node = Proxy::socks_construct(
160        group, remarks, server, port, username, password, None, None, None, "",
161    );
162
163    true
164}