1use crate::tor_provider::TargetAddr;
3
4#[derive(thiserror::Error, Debug)]
5pub enum ProxyConfigError {
7 #[error("{0}")]
8 Generic(String),
10}
11
12#[derive(Clone, Debug, PartialEq)]
13pub struct Socks4ProxyConfig {
15 pub(crate) address: TargetAddr,
16}
17
18impl Socks4ProxyConfig {
19 pub fn new(address: TargetAddr) -> Result<Self, ProxyConfigError> {
21 let port = match &address {
22 TargetAddr::Socket(addr) => addr.port(),
23 TargetAddr::Domain(addr) => addr.port(),
24 TargetAddr::OnionService(_) => {
25 return Err(ProxyConfigError::Generic(
26 "proxy address may not be onion service".to_string(),
27 ))
28 }
29 };
30 if port == 0 {
31 return Err(ProxyConfigError::Generic("proxy port not be 0".to_string()));
32 }
33
34 Ok(Self { address })
35 }
36
37 pub fn address(&self) -> &TargetAddr {
38 &self.address
39 }
40}
41
42#[derive(Clone, Debug, PartialEq)]
43pub struct Socks5ProxyConfig {
45 pub(crate) address: TargetAddr,
46 pub(crate) username: Option<String>,
47 pub(crate) password: Option<String>,
48}
49
50impl Socks5ProxyConfig {
51 pub fn new(
53 address: TargetAddr,
54 username: Option<String>,
55 password: Option<String>,
56 ) -> Result<Self, ProxyConfigError> {
57 let port = match &address {
58 TargetAddr::Socket(addr) => addr.port(),
59 TargetAddr::Domain(addr) => addr.port(),
60 TargetAddr::OnionService(_) => {
61 return Err(ProxyConfigError::Generic(
62 "proxy address may not be onion service".to_string(),
63 ))
64 }
65 };
66 if port == 0 {
67 return Err(ProxyConfigError::Generic("proxy port not be 0".to_string()));
68 }
69
70 if let Some(username) = &username {
72 if username.len() > 255 {
73 return Err(ProxyConfigError::Generic(
74 "socks5 username must be <= 255 bytes".to_string(),
75 ));
76 }
77 }
78 if let Some(password) = &password {
80 if password.len() > 255 {
81 return Err(ProxyConfigError::Generic(
82 "socks5 password must be <= 255 bytes".to_string(),
83 ));
84 }
85 }
86
87 Ok(Self {
88 address,
89 username,
90 password,
91 })
92 }
93
94 pub fn address(&self) -> &TargetAddr {
95 &self.address
96 }
97
98 pub fn username(&self) -> &Option<String> {
99 &self.username
100 }
101
102 pub fn password(&self) -> &Option<String> {
103 &self.password
104 }
105}
106
107#[derive(Clone, Debug, PartialEq)]
108pub struct HttpsProxyConfig {
110 pub(crate) address: TargetAddr,
111 pub(crate) username: Option<String>,
112 pub(crate) password: Option<String>,
113}
114
115impl HttpsProxyConfig {
116 pub fn new(
118 address: TargetAddr,
119 username: Option<String>,
120 password: Option<String>,
121 ) -> Result<Self, ProxyConfigError> {
122 let port = match &address {
123 TargetAddr::Socket(addr) => addr.port(),
124 TargetAddr::Domain(addr) => addr.port(),
125 TargetAddr::OnionService(_) => {
126 return Err(ProxyConfigError::Generic(
127 "proxy address may not be onion service".to_string(),
128 ))
129 }
130 };
131 if port == 0 {
132 return Err(ProxyConfigError::Generic("proxy port not be 0".to_string()));
133 }
134
135 if let Some(username) = &username {
137 if username.contains(':') {
138 return Err(ProxyConfigError::Generic(
139 "username may not contain ':' character".to_string(),
140 ));
141 }
142 }
143
144 Ok(Self {
145 address,
146 username,
147 password,
148 })
149 }
150
151 pub fn address(&self) -> &TargetAddr {
152 &self.address
153 }
154
155 pub fn username(&self) -> &Option<String> {
156 &self.username
157 }
158
159 pub fn password(&self) -> &Option<String> {
160 &self.password
161 }
162}
163
164#[derive(Clone, Debug, PartialEq)]
165pub enum ProxyConfig {
167 Socks4(Socks4ProxyConfig),
169 Socks5(Socks5ProxyConfig),
171 Https(HttpsProxyConfig),
173}
174
175impl From<Socks4ProxyConfig> for ProxyConfig {
176 fn from(config: Socks4ProxyConfig) -> Self {
177 ProxyConfig::Socks4(config)
178 }
179}
180
181impl From<Socks5ProxyConfig> for ProxyConfig {
182 fn from(config: Socks5ProxyConfig) -> Self {
183 ProxyConfig::Socks5(config)
184 }
185}
186
187impl From<HttpsProxyConfig> for ProxyConfig {
188 fn from(config: HttpsProxyConfig) -> Self {
189 ProxyConfig::Https(config)
190 }
191}