Skip to main content

socks_hub_core/
config.rs

1use serde_derive::{Deserialize, Serialize};
2use socks5_impl::protocol::{ProxyParameters, UserKey};
3
4/// Proxy tunnel from HTTP or SOCKS5 to SOCKS5
5#[derive(Debug, Clone, clap::Parser, Serialize, Deserialize)]
6#[command(author, version, about = "SOCKS5 hub for downstreams proxy of HTTP or SOCKS5.", long_about = None)]
7pub struct Config {
8    /// Source proxy role, URL in the form proto://[username[:password]@]host:port,
9    /// where proto is one of socks5, http.
10    /// Username and password are encoded in percent encoding. For example:
11    /// http://myname:pass%40word@127.0.0.1:1080
12    #[arg(short, long, value_parser = |s: &str| ProxyParameters::try_from(s), value_name = "URL")]
13    pub listen_proxy_role: ProxyParameters,
14
15    /// Optional middle SOCKS5 server, URL in form of socks5://[username[:password]@]host:port
16    #[arg(short, long, value_parser = |s: &str| ProxyParameters::try_from(s), value_name = "URL")]
17    #[serde(skip_serializing_if = "Option::is_none", default)]
18    pub middle_server: Option<ProxyParameters>,
19
20    /// Target SOCKS5 server, URL in form of socks5://[username[:password]@]host:port
21    #[arg(short, long, value_parser = |s: &str| ProxyParameters::try_from(s), value_name = "URL")]
22    pub remote_server: ProxyParameters,
23
24    /// ACL (Access Control List) file path, optional
25    #[arg(short, long, value_name = "path")]
26    #[serde(skip_serializing_if = "Option::is_none", default)]
27    pub acl_file: Option<std::path::PathBuf>,
28
29    /// Log verbosity level
30    #[arg(short, long, value_name = "level", default_value = "info")]
31    pub verbosity: ArgVerbosity,
32}
33
34impl Default for Config {
35    fn default() -> Self {
36        let remote_server: ProxyParameters = "socks5://127.0.0.1:1080".try_into().unwrap();
37        Config {
38            listen_proxy_role: ProxyParameters::default(),
39            middle_server: None,
40            remote_server,
41            acl_file: None,
42            verbosity: ArgVerbosity::Info,
43        }
44    }
45}
46
47impl Config {
48    pub fn parse_args() -> Self {
49        <Self as clap::Parser>::parse()
50    }
51
52    pub fn new(listen_proxy_role: &str, remote_server: &str) -> Self {
53        Config {
54            listen_proxy_role: listen_proxy_role.try_into().unwrap(),
55            middle_server: None,
56            remote_server: remote_server.try_into().unwrap(),
57            ..Config::default()
58        }
59    }
60
61    pub fn listen_proxy_role(&mut self, listen_proxy_role: &str) -> &mut Self {
62        self.listen_proxy_role = listen_proxy_role.try_into().unwrap();
63        self
64    }
65
66    pub fn remote_server(&mut self, remote_server: &str) -> &mut Self {
67        self.remote_server = remote_server.try_into().unwrap();
68        self
69    }
70
71    pub fn middle_server(&mut self, middle_server: &str) -> &mut Self {
72        self.middle_server = Some(middle_server.try_into().unwrap());
73        self
74    }
75
76    pub fn middle_server_opt(&mut self, middle_server: Option<&str>) -> &mut Self {
77        if let Some(middle_server) = middle_server {
78            self.middle_server(middle_server);
79        }
80        self
81    }
82
83    pub fn acl_file<P: Into<std::path::PathBuf>>(&mut self, acl_file: P) -> &mut Self {
84        self.acl_file = Some(acl_file.into());
85        self
86    }
87
88    pub fn verbosity(&mut self, verbosity: ArgVerbosity) -> &mut Self {
89        self.verbosity = verbosity;
90        self
91    }
92
93    pub fn get_listen_credentials(&self) -> UserKey {
94        self.listen_proxy_role.credentials.clone().unwrap_or_default()
95    }
96
97    pub fn get_s5_credentials(&self) -> UserKey {
98        self.remote_server.credentials.clone().unwrap_or_default()
99    }
100
101    pub fn get_middle_s5_credentials(&self) -> UserKey {
102        self.middle_server
103            .as_ref()
104            .and_then(|proxy| proxy.credentials.clone())
105            .unwrap_or_default()
106    }
107}
108
109#[repr(C)]
110#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, clap::ValueEnum, Serialize, Deserialize)]
111pub enum ArgVerbosity {
112    Off = 0,
113    Error,
114    Warn,
115    #[default]
116    Info,
117    Debug,
118    Trace,
119}
120
121impl From<ArgVerbosity> for log::LevelFilter {
122    fn from(verbosity: ArgVerbosity) -> Self {
123        match verbosity {
124            ArgVerbosity::Off => log::LevelFilter::Off,
125            ArgVerbosity::Error => log::LevelFilter::Error,
126            ArgVerbosity::Warn => log::LevelFilter::Warn,
127            ArgVerbosity::Info => log::LevelFilter::Info,
128            ArgVerbosity::Debug => log::LevelFilter::Debug,
129            ArgVerbosity::Trace => log::LevelFilter::Trace,
130        }
131    }
132}
133
134impl From<log::Level> for ArgVerbosity {
135    fn from(level: log::Level) -> Self {
136        match level {
137            log::Level::Error => ArgVerbosity::Error,
138            log::Level::Warn => ArgVerbosity::Warn,
139            log::Level::Info => ArgVerbosity::Info,
140            log::Level::Debug => ArgVerbosity::Debug,
141            log::Level::Trace => ArgVerbosity::Trace,
142        }
143    }
144}
145
146impl std::fmt::Display for ArgVerbosity {
147    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
148        match self {
149            ArgVerbosity::Off => write!(f, "off"),
150            ArgVerbosity::Error => write!(f, "error"),
151            ArgVerbosity::Warn => write!(f, "warn"),
152            ArgVerbosity::Info => write!(f, "info"),
153            ArgVerbosity::Debug => write!(f, "debug"),
154            ArgVerbosity::Trace => write!(f, "trace"),
155        }
156    }
157}