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)]
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
38#[derive(Clone, Debug)]
39pub struct Socks5ProxyConfig {
41 pub(crate) address: TargetAddr,
42 pub(crate) username: Option<String>,
43 pub(crate) password: Option<String>,
44}
45
46impl Socks5ProxyConfig {
47 pub fn new(
49 address: TargetAddr,
50 username: Option<String>,
51 password: Option<String>,
52 ) -> Result<Self, ProxyConfigError> {
53 let port = match &address {
54 TargetAddr::Socket(addr) => addr.port(),
55 TargetAddr::Domain(addr) => addr.port(),
56 TargetAddr::OnionService(_) => {
57 return Err(ProxyConfigError::Generic(
58 "proxy address may not be onion service".to_string(),
59 ))
60 }
61 };
62 if port == 0 {
63 return Err(ProxyConfigError::Generic("proxy port not be 0".to_string()));
64 }
65
66 if let Some(username) = &username {
68 if username.len() > 255 {
69 return Err(ProxyConfigError::Generic(
70 "socks5 username must be <= 255 bytes".to_string(),
71 ));
72 }
73 }
74 if let Some(password) = &password {
76 if password.len() > 255 {
77 return Err(ProxyConfigError::Generic(
78 "socks5 password must be <= 255 bytes".to_string(),
79 ));
80 }
81 }
82
83 Ok(Self {
84 address,
85 username,
86 password,
87 })
88 }
89}
90
91#[derive(Clone, Debug)]
92pub struct HttpsProxyConfig {
94 pub(crate) address: TargetAddr,
95 pub(crate) username: Option<String>,
96 pub(crate) password: Option<String>,
97}
98
99impl HttpsProxyConfig {
100 pub fn new(
102 address: TargetAddr,
103 username: Option<String>,
104 password: Option<String>,
105 ) -> Result<Self, ProxyConfigError> {
106 let port = match &address {
107 TargetAddr::Socket(addr) => addr.port(),
108 TargetAddr::Domain(addr) => addr.port(),
109 TargetAddr::OnionService(_) => {
110 return Err(ProxyConfigError::Generic(
111 "proxy address may not be onion service".to_string(),
112 ))
113 }
114 };
115 if port == 0 {
116 return Err(ProxyConfigError::Generic("proxy port not be 0".to_string()));
117 }
118
119 if let Some(username) = &username {
121 if username.contains(':') {
122 return Err(ProxyConfigError::Generic(
123 "username may not contain ':' character".to_string(),
124 ));
125 }
126 }
127
128 Ok(Self {
129 address,
130 username,
131 password,
132 })
133 }
134}
135
136#[derive(Clone, Debug)]
137pub enum ProxyConfig {
139 Socks4(Socks4ProxyConfig),
141 Socks5(Socks5ProxyConfig),
143 Https(HttpsProxyConfig),
145}
146
147impl From<Socks4ProxyConfig> for ProxyConfig {
148 fn from(config: Socks4ProxyConfig) -> Self {
149 ProxyConfig::Socks4(config)
150 }
151}
152
153impl From<Socks5ProxyConfig> for ProxyConfig {
154 fn from(config: Socks5ProxyConfig) -> Self {
155 ProxyConfig::Socks5(config)
156 }
157}
158
159impl From<HttpsProxyConfig> for ProxyConfig {
160 fn from(config: HttpsProxyConfig) -> Self {
161 ProxyConfig::Https(config)
162 }
163}