proxy_scraper/shadowsocks.rs
1use base64::{engine::general_purpose::URL_SAFE, Engine as _};
2use regex::Regex;
3
4/// Represents a Shadowsocks proxy.
5#[derive(Debug)]
6pub struct Shadowsocks {
7 /// The host address of the Shadowsocks proxy.
8 pub host: String,
9 /// The port number for the Shadowsocks proxy.
10 pub port: u32,
11 /// The password associated with the Shadowsocks proxy.
12 pub password: String,
13 /// The encryption method used by the Shadowsocks proxy.
14 pub method: String,
15}
16
17impl Shadowsocks {
18 /// Converts the Shadowsocks proxy information into a Shadowsocks URL.
19 ///
20 /// # Example
21 ///
22 /// ```
23 /// use proxy_scraper::shadowsocks::Shadowsocks;
24 /// let proxy = Shadowsocks {
25 /// host: "example.com".to_string(),
26 /// port: 443,
27 /// password: "password".to_string(),
28 /// method: "aes-256-gcm".to_string(),
29 /// };
30 /// let url = proxy.to_url();
31 /// assert_eq!(url, "ss://YWVzLTI1Ni1nY206cGFzc3dvcmQ=@example.com:443");
32 /// ```
33 pub fn to_url(&self) -> String {
34 let base64_part = URL_SAFE.encode(format!("{}:{}", self.method, self.password));
35 format!("ss://{}@{}:{}", base64_part, self.host, self.port)
36 }
37
38 /// Scrapes Shadowsocks proxy information from the provided source string and returns a vector of Shadowsocks instances.
39 ///
40 /// # Arguments
41 ///
42 /// * `source` - A string containing the source code or text from which to extract Shadowsocks proxy information.
43 ///
44 /// # Returns
45 ///
46 /// A vector of `Shadowsocks` instances parsed from the source string.
47 pub fn scrape(source: &str) -> Vec<Self> {
48 let source = crate::utils::seperate_links(source);
49 let mut proxy_list: Vec<Shadowsocks> = Vec::new();
50 let regex = Regex::new(
51 r#"ss://((?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?)@((.+):(\d+))#"#,
52 )
53 .unwrap();
54
55 for captures in regex.captures_iter(&source) {
56 let base64_part = captures.get(1).map(|b64| b64.as_str()).unwrap_or("");
57 let host = captures.get(3).map(|h| h.as_str()).unwrap_or("");
58 let port: u32 = captures
59 .get(4)
60 .map(|p| p.as_str())
61 .unwrap_or("0")
62 .parse::<u32>()
63 .unwrap();
64
65 if base64_part.is_empty() || host.is_empty() || port == 0 {
66 continue;
67 }
68
69 let decoded_base64_part =
70 String::from_utf8(URL_SAFE.decode(&base64_part).unwrap()).unwrap();
71 let parts: Vec<&str> = decoded_base64_part.split(":").collect();
72
73 let method = parts[0].to_string();
74 let password = parts[1].to_string();
75
76 proxy_list.push(Shadowsocks {
77 host: host.to_string(),
78 port,
79 password,
80 method,
81 });
82 }
83 proxy_list
84 }
85}