tor_interface/
censorship_circumvention.rs1use std::net::SocketAddr;
3use std::path::PathBuf;
4use std::str::FromStr;
5use std::sync::OnceLock;
6
7use regex::Regex;
9
10#[derive(Clone, Debug)]
11pub struct PluggableTransportConfig {
13 transports: Vec<String>,
14 path_to_binary: PathBuf,
15 options: Vec<String>,
16}
17
18#[derive(thiserror::Error, Debug)]
19pub enum PluggableTransportConfigError {
21 #[error("pluggable transport name '{0}' is invalid")]
22 TransportNameInvalid(String),
24 #[error("unable to use '{0}' as pluggable transport binary path, {1}")]
25 BinaryPathInvalid(String, String),
27}
28
29static TRANSPORT_PATTERN: OnceLock<Regex> = OnceLock::new();
31fn init_transport_pattern() -> Regex {
32 Regex::new(r"(?m)^[a-zA-Z_][a-zA-Z0-9_]*$").unwrap()
33}
34
35impl PluggableTransportConfig {
37 pub fn new(
39 transports: Vec<String>,
40 path_to_binary: PathBuf,
41 ) -> Result<Self, PluggableTransportConfigError> {
42 let transport_pattern = TRANSPORT_PATTERN.get_or_init(init_transport_pattern);
43 for transport in &transports {
45 if !transport_pattern.is_match(transport) {
46 return Err(PluggableTransportConfigError::TransportNameInvalid(
47 transport.clone(),
48 ));
49 }
50 }
51
52 if !path_to_binary.is_absolute() {
55 return Err(PluggableTransportConfigError::BinaryPathInvalid(
56 format!("{:?}", path_to_binary.display()),
57 "must be an absolute path".to_string(),
58 ));
59 }
60
61 Ok(Self {
62 transports,
63 path_to_binary,
64 options: Default::default(),
65 })
66 }
67
68 pub fn transports(&self) -> &Vec<String> {
70 &self.transports
71 }
72
73 pub fn path_to_binary(&self) -> &PathBuf {
75 &self.path_to_binary
76 }
77
78 pub fn options(&self) -> &Vec<String> {
80 &self.options
81 }
82
83 pub fn add_option(&mut self, arg: String) {
85 self.options.push(arg);
86 }
87}
88
89#[derive(Clone, Debug)]
91pub struct BridgeLine {
92 transport: String,
93 address: SocketAddr,
94 fingerprint: Option<String>,
95 keyvalues: Vec<(String, String)>,
96}
97
98#[derive(thiserror::Error, Debug)]
99pub enum BridgeLineError {
101 #[error("bridge line '{0}' missing transport")]
102 TransportMissing(String),
104
105 #[error("bridge line '{0}' missing address")]
106 AddressMissing(String),
108
109 #[error("bridge line '{0}' missing fingerprint")]
110 FingerprintMissing(String),
112
113 #[error("transport name '{0}' is invalid")]
114 TransportNameInvalid(String),
116
117 #[error("address '{0}' cannot be parsed as IP:PORT")]
118 AddressParseFailed(String),
120
121 #[error("key=value '{0}' is invalid")]
122 KeyValueInvalid(String),
124
125 #[error("bridge address port must not be 0")]
126 AddressPortInvalid,
128
129 #[error("fingerprint '{0}' is invalid")]
130 FingerprintInvalid(String),
132}
133
134impl BridgeLine {
137 pub fn new(
141 transport: String,
142 address: SocketAddr,
143 fingerprint: Option<String>,
144 keyvalues: Vec<(String, String)>,
145 ) -> Result<BridgeLine, BridgeLineError> {
146 let transport_pattern = TRANSPORT_PATTERN.get_or_init(init_transport_pattern);
147
148 if !transport_pattern.is_match(&transport) {
150 return Err(BridgeLineError::TransportNameInvalid(transport));
151 }
152
153 if address.port() == 0 {
155 return Err(BridgeLineError::AddressPortInvalid);
156 }
157
158 let fingerprint = if let Some(fingerprint) = fingerprint {
159 if !Self::is_bridge_fingerprint_pattern(&fingerprint) {
161 return Err(BridgeLineError::FingerprintInvalid(fingerprint));
162 }
163 Some(fingerprint)
164 } else {
165 None
166 };
167
168 for (key, value) in &keyvalues {
170 if key.contains(' ') || key.contains('=') || key.is_empty() {
171 return Err(BridgeLineError::KeyValueInvalid(format!("{key}={value}")));
172 }
173 }
174
175 Ok(Self {
176 transport,
177 address,
178 fingerprint,
179 keyvalues,
180 })
181 }
182
183 fn is_bridge_fingerprint_pattern(fingerprint: &str) -> bool {
184 static BRIDGE_FINGERPRINT_PATTERN: OnceLock<Regex> = OnceLock::new();
185 let bridge_fingerprint_pattern = BRIDGE_FINGERPRINT_PATTERN
186 .get_or_init(|| Regex::new(r"(?m)^[0-9a-fA-F]{40}$").unwrap());
187
188 bridge_fingerprint_pattern.is_match(fingerprint)
189 }
190
191 pub fn transport(&self) -> &String {
193 &self.transport
194 }
195
196 pub fn address(&self) -> &SocketAddr {
198 &self.address
199 }
200
201 pub fn fingerprint(&self) -> &Option<String> {
203 &self.fingerprint
204 }
205
206 pub fn keyvalues(&self) -> &Vec<(String, String)> {
208 &self.keyvalues
209 }
210
211 #[cfg(feature = "legacy-tor-provider")]
212 pub fn as_legacy_tor_setconf_value(&self) -> String {
214 let transport = &self.transport;
215 let address = self.address.to_string();
216 let keyvalues: Vec<String> = self
217 .keyvalues
218 .iter()
219 .map(|(key, value)| format!("{key}={value}"))
220 .collect();
221 let keyvalues = keyvalues.join(" ");
222
223 if let Some(fingerprint) = &self.fingerprint {
224 format!("{transport} {address} {fingerprint} {keyvalues}")
225 } else {
226 format!("{transport} {address} {keyvalues}")
227 }
228 }
229}
230
231impl FromStr for BridgeLine {
232 type Err = BridgeLineError;
233 fn from_str(s: &str) -> Result<Self, Self::Err> {
234 let mut tokens = s.split(' ').peekable();
235 let transport = if let Some(transport) = tokens.next() {
237 transport
238 } else {
239 return Err(BridgeLineError::TransportMissing(s.to_string()));
240 };
241 let address = if let Some(address) = tokens.next() {
243 if let Ok(address) = SocketAddr::from_str(address) {
244 address
245 } else {
246 return Err(BridgeLineError::AddressParseFailed(address.to_string()));
247 }
248 } else {
249 return Err(BridgeLineError::AddressMissing(s.to_string()));
250 };
251 let fingerprint = if let Some(fingerprint) = tokens.peek() {
253 if Self::is_bridge_fingerprint_pattern(fingerprint) {
254 Some(tokens.next().unwrap().to_string())
255 } else {
256 None
257 }
258 } else {
259 None
260 };
261
262 static BRIDGE_OPTION_PATTERN: OnceLock<Regex> = OnceLock::new();
264 let bridge_option_pattern = BRIDGE_OPTION_PATTERN
265 .get_or_init(|| Regex::new(r"(?m)^(?<key>[^=]+)=(?<value>.*)$").unwrap());
266
267 let mut keyvalues: Vec<(String, String)> = Default::default();
268 for keyvalue in tokens {
269 if let Some(caps) = bridge_option_pattern.captures(keyvalue) {
270 let key = caps
271 .name("key")
272 .expect("missing key group")
273 .as_str()
274 .to_string();
275 let value = caps
276 .name("value")
277 .expect("missing value group")
278 .as_str()
279 .to_string();
280 keyvalues.push((key, value));
281 } else {
282 return Err(BridgeLineError::KeyValueInvalid(keyvalue.to_string()));
283 }
284 }
285
286 BridgeLine::new(transport.to_string(), address, fingerprint, keyvalues)
287 }
288}