Skip to main content

tor_interface/
proxy.rs

1// internal crates
2use crate::tor_provider::TargetAddr;
3
4#[derive(thiserror::Error, Debug)]
5/// Error type for the proxy module
6pub enum ProxyConfigError {
7    #[error("{0}")]
8    /// An error returned when constructing a proxy configuration with invalid parameters
9    Generic(String),
10}
11
12#[derive(Clone, Debug, PartialEq)]
13/// Configuration for a SOCKS4 proxy
14pub struct Socks4ProxyConfig {
15    pub(crate) address: TargetAddr,
16}
17
18impl Socks4ProxyConfig {
19    /// Construct a new `Socks4ProxyConfig`. The `address` argument must not be a [`crate::tor_provider::TargetAddr::OnionService`] and its port must not be 0.
20    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)]
43/// Configuration for a SOCKS5 proxy
44pub struct Socks5ProxyConfig {
45    pub(crate) address: TargetAddr,
46    pub(crate) username: Option<String>,
47    pub(crate) password: Option<String>,
48}
49
50impl Socks5ProxyConfig {
51    /// Construct a new `Socks5ProxyConfig`. The `address` argument must not be a  [`crate::tor_provider::TargetAddr::OnionService`] and its port must not be 0. The `username` and `password` arguments, if present, must each be less than 256 bytes long.
52    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        // username must be less than 255 bytes
71        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        // password must be less than 255 bytes
79        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)]
108/// Configuration for an HTTP CONNECT proxy (`HTTPSProxy` in c-tor torrc configuration)
109pub struct HttpsProxyConfig {
110    pub(crate) address: TargetAddr,
111    pub(crate) username: Option<String>,
112    pub(crate) password: Option<String>,
113}
114
115impl HttpsProxyConfig {
116    /// Construct a new `HttpsProxyConfig`. The `address` argument must not be a [`crate::tor_provider::TargetAddr::OnionService`] and its port must not be 0. The `username` argument, if present, must not contain the `:` (colon) character.
117    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        // username may not contain ':' character (per RFC 2617)
136        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)]
165/// An enum representing a possible proxy server configuration with address and possible credentials.
166pub enum ProxyConfig {
167    /// A SOCKS4 proxy
168    Socks4(Socks4ProxyConfig),
169    /// A SOCKS5 proxy
170    Socks5(Socks5ProxyConfig),
171    /// An HTTP CONNECT proxy
172    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}