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