1use std::collections::BTreeMap;
3use std::convert::From;
4use std::default::Default;
5use std::net::{SocketAddr, TcpListener};
6use std::option::Option;
7use std::path::PathBuf;
8use std::string::ToString;
9use std::sync::{atomic, Arc};
10use std::time::Duration;
11
12use socks::Socks5Stream;
14
15use crate::censorship_circumvention::*;
17use crate::legacy_tor_control_stream::*;
18use crate::legacy_tor_controller::*;
19use crate::legacy_tor_process::*;
20use crate::legacy_tor_version::*;
21use crate::proxy::*;
22use crate::tor_crypto::*;
23use crate::tor_provider;
24use crate::tor_provider::*;
25
26#[derive(thiserror::Error, Debug)]
28pub enum Error {
29 #[error("failed to create LegacyTorProcess object")]
30 LegacyTorProcessCreationFailed(#[source] crate::legacy_tor_process::Error),
31
32 #[error("failed to create LegacyControlStream object")]
33 LegacyControlStreamCreationFailed(#[source] crate::legacy_tor_control_stream::Error),
34
35 #[error("failed to create LegacyTorController object")]
36 LegacyTorControllerCreationFailed(#[source] crate::legacy_tor_controller::Error),
37
38 #[error("failed to authenticate with the tor process")]
39 LegacyTorProcessAuthenticationFailed(#[source] crate::legacy_tor_controller::Error),
40
41 #[error("failed to determine the tor process version")]
42 GetInfoVersionFailed(#[source] crate::legacy_tor_controller::Error),
43
44 #[error("tor process version to old; found {0} but must be at least {1}")]
45 LegacyTorProcessTooOld(String, String),
46
47 #[error("failed to register for STATUS_CLIENT and HS_DESC events")]
48 SetEventsFailed(#[source] crate::legacy_tor_controller::Error),
49
50 #[error("failed to delete unused onion service")]
51 DelOnionFailed(#[source] crate::legacy_tor_controller::Error),
52
53 #[error("failed waiting for async events: {0}")]
54 WaitAsyncEventsFailed(#[source] crate::legacy_tor_controller::Error),
55
56 #[error("failed to begin bootstrap")]
57 SetConfDisableNetwork0Failed(#[source] crate::legacy_tor_controller::Error),
58
59 #[error("failed to setconf")]
60 SetConfFailed(#[source] crate::legacy_tor_controller::Error),
61
62 #[error("failed to add client auth for onion service")]
63 OnionClientAuthAddFailed(#[source] crate::legacy_tor_controller::Error),
64
65 #[error("failed to remove client auth from onion service")]
66 OnionClientAuthRemoveFailed(#[source] crate::legacy_tor_controller::Error),
67
68 #[error("failed to get socks listener")]
69 GetInfoNetListenersSocksFailed(#[source] crate::legacy_tor_controller::Error),
70
71 #[error("no socks listeners available to connect through")]
72 NoSocksListenersFound(),
73
74 #[error("invalid circuit token")]
75 CircuitTokenInvalid(),
76
77 #[error("unable to connect to socks listener")]
78 Socks5ConnectionFailed(#[source] std::io::Error),
79
80 #[error("unable to bind TCP listener")]
81 TcpListenerBindFailed(#[source] std::io::Error),
82
83 #[error("unable to get TCP listener's local address")]
84 TcpListenerLocalAddrFailed(#[source] std::io::Error),
85
86 #[error("failed to create onion service")]
87 AddOnionFailed(#[source] crate::legacy_tor_controller::Error),
88
89 #[error("tor not bootstrapped")]
90 LegacyTorNotBootstrapped(),
91
92 #[error("{0}")]
93 PluggableTransportConfigDirectoryCreationFailed(#[source] std::io::Error),
94
95 #[error("unable to create pluggable-transport directory because file with same name already exists: {0:?}")]
96 PluggableTransportDirectoryNameCollision(PathBuf),
97
98 #[error("{0}")]
99 PluggableTransportSymlinkRemovalFailed(#[source] std::io::Error),
100
101 #[error("{0}")]
102 PluggableTransportSymlinkCreationFailed(#[source] std::io::Error),
103
104 #[error("pluggable transport binary name not representable as utf8: {0:?}")]
105 PluggableTransportBinaryNameNotUtf8Representnable(std::ffi::OsString),
106
107 #[error("{0}")]
108 PluggableTransportConfigError(#[source] crate::censorship_circumvention::PluggableTransportConfigError),
109
110 #[error("pluggable transport multiply defines '{0}' bridge transport type")]
111 BridgeTransportTypeMultiplyDefined(String),
112
113 #[error("bridge transport '{0}' not supported by pluggable transport configuration")]
114 BridgeTransportNotSupported(String),
115
116 #[error("not implemented")]
117 NotImplemented(),
118}
119
120impl From<Error> for crate::tor_provider::Error {
121 fn from(error: Error) -> Self {
122 crate::tor_provider::Error::Generic(error.to_string())
123 }
124}
125
126struct LegacyCircuitToken {
130 username: String,
131 password: String,
132}
133
134impl LegacyCircuitToken {
135 fn new() -> LegacyCircuitToken {
136 const CIRCUIT_TOKEN_USERNAME_LENGTH: usize = 32usize;
137 const CIRCUIT_TOKEN_PASSWORD_LENGTH: usize = 32usize;
138 let username = generate_password(CIRCUIT_TOKEN_USERNAME_LENGTH);
139 let password = generate_password(CIRCUIT_TOKEN_PASSWORD_LENGTH);
140
141 LegacyCircuitToken { username, password }
142 }
143}
144
145impl Default for LegacyCircuitToken {
146 fn default() -> Self {
147 Self::new()
148 }
149}
150
151#[derive(Clone, Debug)]
156pub enum LegacyTorClientConfig {
157 BundledTor {
158 tor_bin_path: PathBuf,
159 data_directory: PathBuf,
160 proxy_settings: Option<ProxyConfig>,
161 allowed_ports: Option<Vec<u16>>,
162 pluggable_transports: Option<Vec<PluggableTransportConfig>>,
163 bridge_lines: Option<Vec<BridgeLine>>,
164 },
165 SystemTor {
166 tor_socks_addr: SocketAddr,
167 tor_control_addr: SocketAddr,
168 tor_control_passwd: String,
169 },
170}
171
172pub struct LegacyTorClient {
182 daemon: Option<LegacyTorProcess>,
183 version: LegacyTorVersion,
184 controller: LegacyTorController,
185 bootstrapped: bool,
186 socks_listener: Option<SocketAddr>,
187 onion_services: Vec<(V3OnionServiceId, Arc<atomic::AtomicBool>)>,
189 circuit_token_counter: usize,
191 circuit_tokens: BTreeMap<CircuitToken, LegacyCircuitToken>,
192}
193
194impl LegacyTorClient {
195 pub fn new(config: LegacyTorClientConfig) -> Result<LegacyTorClient, Error> {
197 let (daemon, mut controller, password, socks_listener) = match &config {
198 LegacyTorClientConfig::BundledTor {
199 tor_bin_path,
200 data_directory,
201 ..
202 } => {
203 let daemon =
205 LegacyTorProcess::new(tor_bin_path.as_path(), data_directory.as_path())
206 .map_err(Error::LegacyTorProcessCreationFailed)?;
207 let control_stream =
209 LegacyControlStream::new(daemon.get_control_addr(), Duration::from_millis(16))
210 .map_err(Error::LegacyControlStreamCreationFailed)?;
211
212 let controller = LegacyTorController::new(control_stream)
214 .map_err(Error::LegacyTorControllerCreationFailed)?;
215
216 let password = daemon.get_password().to_string();
217 (Some(daemon), controller, password, None)
218 }
219 LegacyTorClientConfig::SystemTor {
220 tor_socks_addr,
221 tor_control_addr,
222 tor_control_passwd,
223 } => {
224 let control_stream =
226 LegacyControlStream::new(&tor_control_addr, Duration::from_millis(16))
227 .map_err(Error::LegacyControlStreamCreationFailed)?;
228
229 let controller = LegacyTorController::new(control_stream)
231 .map_err(Error::LegacyTorControllerCreationFailed)?;
232
233 (
234 None,
235 controller,
236 tor_control_passwd.clone(),
237 Some(tor_socks_addr.clone()),
238 )
239 }
240 };
241
242 controller
244 .authenticate(&password)
245 .map_err(Error::LegacyTorProcessAuthenticationFailed)?;
246
247 let min_required_version = LegacyTorVersion {
249 major: 0u32,
250 minor: 4u32,
251 micro: 6u32,
252 patch_level: 1u32,
253 status_tag: None,
254 };
255
256 let version = controller
258 .getinfo_version()
259 .map_err(Error::GetInfoVersionFailed)?;
260
261 if version < min_required_version {
262 return Err(Error::LegacyTorProcessTooOld(
263 version.to_string(),
264 min_required_version.to_string(),
265 ));
266 }
267
268 if let LegacyTorClientConfig::BundledTor {
270 data_directory,
271 proxy_settings,
272 allowed_ports,
273 pluggable_transports,
274 bridge_lines,
275 ..
276 } = config
277 {
278 match proxy_settings {
280 Some(ProxyConfig::Socks4(Socks4ProxyConfig { address })) => {
281 controller
282 .setconf(&[("Socks4Proxy", address.to_string())])
283 .map_err(Error::SetConfFailed)?;
284 }
285 Some(ProxyConfig::Socks5(Socks5ProxyConfig {
286 address,
287 username,
288 password,
289 })) => {
290 controller
291 .setconf(&[("Socks5Proxy", address.to_string())])
292 .map_err(Error::SetConfFailed)?;
293 let username = username.unwrap_or("".to_string());
294 if !username.is_empty() {
295 controller
296 .setconf(&[("Socks5ProxyUsername", username.to_string())])
297 .map_err(Error::SetConfFailed)?;
298 }
299 let password = password.unwrap_or("".to_string());
300 if !password.is_empty() {
301 controller
302 .setconf(&[("Socks5ProxyPassword", password.to_string())])
303 .map_err(Error::SetConfFailed)?;
304 }
305 }
306 Some(ProxyConfig::Https(HttpsProxyConfig {
307 address,
308 username,
309 password,
310 })) => {
311 controller
312 .setconf(&[("HTTPSProxy", address.to_string())])
313 .map_err(Error::SetConfFailed)?;
314 let username = username.unwrap_or("".to_string());
315 let password = password.unwrap_or("".to_string());
316 if !username.is_empty() || !password.is_empty() {
317 let authenticator = format!("{}:{}", username, password);
318 controller
319 .setconf(&[("HTTPSProxyAuthenticator", authenticator)])
320 .map_err(Error::SetConfFailed)?;
321 }
322 }
323 None => (),
324 }
325 if let Some(allowed_ports) = allowed_ports {
327 let allowed_addresses: Vec<String> = allowed_ports
328 .iter()
329 .map(|port| format!("*{{}}:{port}"))
330 .collect();
331 let allowed_addresses = allowed_addresses.join(", ");
332 controller
333 .setconf(&[("ReachableAddresses", allowed_addresses)])
334 .map_err(Error::SetConfFailed)?;
335 }
336 let mut supported_transports: std::collections::BTreeSet<String> = Default::default();
338 if let Some(pluggable_transports) = pluggable_transports {
339 let mut pt_directory = data_directory.clone();
347 pt_directory.push("pluggable-transports");
348 if !std::path::Path::exists(&pt_directory) {
349 std::fs::create_dir(&pt_directory)
351 .map_err(Error::PluggableTransportConfigDirectoryCreationFailed)?;
352 } else if !std::path::Path::is_dir(&pt_directory) {
353 return Err(Error::PluggableTransportDirectoryNameCollision(
355 pt_directory,
356 ));
357 }
358
359 let mut conf: Vec<(&str, String)> = Default::default();
361 for pt_settings in &pluggable_transports {
362 let path_to_binary = pt_settings.path_to_binary();
365 let binary_name = path_to_binary
366 .file_name()
367 .expect("file_name should be absolute path");
368 let mut pt_symlink = pt_directory.clone();
369 pt_symlink.push(binary_name);
370 let binary_name = if let Some(binary_name) = binary_name.to_str() {
371 binary_name
372 } else {
373 return Err(Error::PluggableTransportBinaryNameNotUtf8Representnable(
374 binary_name.to_os_string(),
375 ));
376 };
377
378 if std::path::Path::exists(&pt_symlink) {
380 std::fs::remove_file(&pt_symlink)
381 .map_err(Error::PluggableTransportSymlinkRemovalFailed)?;
382 }
383
384 #[cfg(windows)]
386 std::os::windows::fs::symlink_file(path_to_binary, &pt_symlink)
387 .map_err(Error::PluggableTransportSymlinkCreationFailed)?;
388 #[cfg(unix)]
389 std::os::unix::fs::symlink(path_to_binary, &pt_symlink)
390 .map_err(Error::PluggableTransportSymlinkCreationFailed)?;
391
392 for transport in pt_settings.transports() {
394 if supported_transports.contains(transport) {
395 return Err(Error::BridgeTransportTypeMultiplyDefined(
396 transport.to_string(),
397 ));
398 }
399 supported_transports.insert(transport.to_string());
400 }
401
402 let transports = pt_settings.transports().join(",");
404 use std::path::MAIN_SEPARATOR;
405 let path_to_binary =
406 format!("pluggable-transports{MAIN_SEPARATOR}{binary_name}");
407 let options = pt_settings.options().join(" ");
408
409 let value = format!("{transports} exec {path_to_binary} {options}");
410 conf.push(("ClientTransportPlugin", value));
411 }
412 controller
413 .setconf(conf.as_slice())
414 .map_err(Error::SetConfFailed)?;
415 }
416 if let Some(bridge_lines) = bridge_lines {
418 let mut conf: Vec<(&str, String)> = Default::default();
419 for bridge_line in &bridge_lines {
420 if !supported_transports.contains(bridge_line.transport()) {
421 return Err(Error::BridgeTransportNotSupported(
422 bridge_line.transport().to_string(),
423 ));
424 }
425 let value = bridge_line.as_legacy_tor_setconf_value();
426 conf.push(("Bridge", value));
427 }
428 conf.push(("UseBridges", "1".to_string()));
429 controller
430 .setconf(conf.as_slice())
431 .map_err(Error::SetConfFailed)?;
432 }
433 }
434
435 controller
437 .setevents(&["STATUS_CLIENT", "HS_DESC"])
438 .map_err(Error::SetEventsFailed)?;
439
440 Ok(LegacyTorClient {
441 daemon,
442 version,
443 controller,
444 bootstrapped: false,
445 socks_listener,
446 onion_services: Default::default(),
447 circuit_token_counter: 0usize,
448 circuit_tokens: Default::default(),
449 })
450 }
451
452 pub fn version(&mut self) -> LegacyTorVersion {
454 self.version.clone()
455 }
456}
457
458impl TorProvider for LegacyTorClient {
459 fn update(&mut self) -> Result<Vec<TorEvent>, tor_provider::Error> {
460 let mut i = 0;
461 while i < self.onion_services.len() {
462 if !self.onion_services[i].1.load(atomic::Ordering::Relaxed) {
464 let entry = self.onion_services.swap_remove(i);
465 let service_id = entry.0;
466
467 self.controller
468 .del_onion(&service_id)
469 .map_err(Error::DelOnionFailed)?;
470 } else {
471 i += 1;
472 }
473 }
474
475 let mut events: Vec<TorEvent> = Default::default();
476 for async_event in self
477 .controller
478 .wait_async_events()
479 .map_err(Error::WaitAsyncEventsFailed)?
480 .iter()
481 {
482 match async_event {
483 AsyncEvent::StatusClient {
484 severity,
485 action,
486 arguments,
487 } => {
488 if severity == "NOTICE" && action == "BOOTSTRAP" {
489 let mut progress: u32 = 0;
490 let mut tag: String = Default::default();
491 let mut summary: String = Default::default();
492 for (key, val) in arguments.iter() {
493 match key.as_str() {
494 "PROGRESS" => progress = val.parse().unwrap_or(0u32),
495 "TAG" => tag = val.to_string(),
496 "SUMMARY" => summary = val.to_string(),
497 _ => {} }
499 }
500 events.push(TorEvent::BootstrapStatus {
501 progress,
502 tag,
503 summary,
504 });
505 if progress == 100u32 {
506 events.push(TorEvent::BootstrapComplete);
507 self.bootstrapped = true;
508 }
509 }
510 }
511 AsyncEvent::HsDesc { action, hs_address } => {
512 if action == "UPLOADED" {
513 events.push(TorEvent::OnionServicePublished {
514 service_id: hs_address.clone(),
515 });
516 }
517 }
518 AsyncEvent::Unknown { lines } => {
519 println!("Received Unknown Event:");
520 for line in lines.iter() {
521 println!(" {}", line);
522 }
523 }
524 }
525 }
526
527 if let Some(daemon) = &mut self.daemon {
528 for log_line in daemon.wait_log_lines().iter_mut() {
530 events.push(TorEvent::LogReceived {
531 line: std::mem::take(log_line),
532 });
533 }
534 } else if !self.bootstrapped {
535 events.push(TorEvent::BootstrapComplete);
537 self.bootstrapped = true;
538 }
539
540 Ok(events)
541 }
542
543 fn bootstrap(&mut self) -> Result<(), tor_provider::Error> {
544 if !self.bootstrapped {
545 self.controller
546 .setconf(&[("DisableNetwork", "0".to_string())])
547 .map_err(Error::SetConfDisableNetwork0Failed)?;
548 }
549 Ok(())
550 }
551
552 fn add_client_auth(
553 &mut self,
554 service_id: &V3OnionServiceId,
555 client_auth: &X25519PrivateKey,
556 ) -> Result<(), tor_provider::Error> {
557 Ok(self
558 .controller
559 .onion_client_auth_add(service_id, client_auth, None, &Default::default())
560 .map_err(Error::OnionClientAuthAddFailed)?)
561 }
562
563 fn remove_client_auth(
564 &mut self,
565 service_id: &V3OnionServiceId,
566 ) -> Result<(), tor_provider::Error> {
567 Ok(self
568 .controller
569 .onion_client_auth_remove(service_id)
570 .map_err(Error::OnionClientAuthRemoveFailed)?)
571 }
572
573 fn connect(
575 &mut self,
576 target: TargetAddr,
577 circuit: Option<CircuitToken>,
578 ) -> Result<OnionStream, tor_provider::Error> {
579 if !self.bootstrapped {
580 return Err(Error::LegacyTorNotBootstrapped().into());
581 }
582
583 if self.socks_listener.is_none() {
584 let mut listeners = self
585 .controller
586 .getinfo_net_listeners_socks()
587 .map_err(Error::GetInfoNetListenersSocksFailed)?;
588 if listeners.is_empty() {
589 return Err(Error::NoSocksListenersFound())?;
590 }
591 self.socks_listener = Some(listeners.swap_remove(0));
592 }
593
594 let socks_listener = match self.socks_listener {
595 Some(socks_listener) => socks_listener,
596 None => unreachable!(),
597 };
598
599 let socks_target = match target.clone() {
601 TargetAddr::Socket(socket_addr) => socks::TargetAddr::Ip(socket_addr),
602 TargetAddr::Domain(domain_addr) => {
603 socks::TargetAddr::Domain(domain_addr.domain().to_string(), domain_addr.port())
604 }
605 TargetAddr::OnionService(OnionAddr::V3(OnionAddrV3 {
606 service_id,
607 virt_port,
608 })) => socks::TargetAddr::Domain(format!("{}.onion", service_id), virt_port),
609 };
610
611 let stream = match &circuit {
613 None => Socks5Stream::connect(socks_listener, socks_target),
614 Some(circuit) => {
615 if let Some(circuit) = self.circuit_tokens.get(circuit) {
616 Socks5Stream::connect_with_password(
617 socks_listener,
618 socks_target,
619 &circuit.username,
620 &circuit.password,
621 )
622 } else {
623 return Err(Error::CircuitTokenInvalid())?;
624 }
625 }
626 }
627 .map_err(Error::Socks5ConnectionFailed)?;
628
629 Ok(OnionStream {
630 stream: stream.into_inner(),
631 local_addr: None,
632 peer_addr: Some(target),
633 })
634 }
635
636 fn listener(
638 &mut self,
639 private_key: &Ed25519PrivateKey,
640 virt_port: u16,
641 authorized_clients: Option<&[X25519PublicKey]>,
642 ) -> Result<OnionListener, tor_provider::Error> {
643 if !self.bootstrapped {
644 return Err(Error::LegacyTorNotBootstrapped().into());
645 }
646
647 let socket_addr = SocketAddr::from(([127, 0, 0, 1], 0u16));
649 let listener = TcpListener::bind(socket_addr).map_err(Error::TcpListenerBindFailed)?;
650 let socket_addr = listener
651 .local_addr()
652 .map_err(Error::TcpListenerLocalAddrFailed)?;
653
654 let mut flags = AddOnionFlags {
655 discard_pk: true,
656 ..Default::default()
657 };
658 if authorized_clients.is_some() {
659 flags.v3_auth = true;
660 }
661
662 let onion_addr = OnionAddr::V3(OnionAddrV3::new(
663 V3OnionServiceId::from_private_key(private_key),
664 virt_port,
665 ));
666
667 let (_, service_id) = self
669 .controller
670 .add_onion(
671 Some(private_key),
672 &flags,
673 None,
674 virt_port,
675 Some(socket_addr),
676 authorized_clients,
677 )
678 .map_err(Error::AddOnionFailed)?;
679
680 let is_active = Arc::new(atomic::AtomicBool::new(true));
681 self.onion_services
682 .push((service_id, Arc::clone(&is_active)));
683
684 Ok(OnionListener::new(listener, onion_addr, is_active, |is_active| {
685 is_active.store(false, atomic::Ordering::Relaxed);
686 }))
687 }
688
689 fn generate_token(&mut self) -> CircuitToken {
690 let new_token = self.circuit_token_counter;
691 self.circuit_token_counter += 1;
692 self.circuit_tokens
693 .insert(new_token, LegacyCircuitToken::new());
694 new_token
695 }
696
697 fn release_token(&mut self, circuit_token: CircuitToken) {
698 self.circuit_tokens.remove(&circuit_token);
699 }
700}