1use std::collections::BTreeMap;
3use std::convert::From;
4use std::default::Default;
5use std::net::{IpAddr, SocketAddr, TcpListener};
6use std::option::Option;
7use std::path::PathBuf;
8use std::str::FromStr;
9use std::string::ToString;
10use std::sync::{atomic, Arc, Mutex};
11use std::time::Duration;
12
13use socks::Socks5Stream;
15use zeroize::ZeroizeOnDrop;
16
17use crate::censorship_circumvention::*;
19use crate::legacy_tor_control_stream::*;
20use crate::legacy_tor_controller::*;
21use crate::legacy_tor_process::*;
22use crate::legacy_tor_version::*;
23use crate::proxy::*;
24use crate::tor_crypto::*;
25use crate::tor_provider;
26use crate::tor_provider::*;
27
28#[derive(thiserror::Error, Debug)]
30pub enum Error {
31 #[error("failed to create LegacyTorProcess object")]
32 LegacyTorProcessCreationFailed(#[source] crate::legacy_tor_process::Error),
33
34 #[error("failed to create LegacyControlStream object")]
35 LegacyControlStreamCreationFailed(#[source] crate::legacy_tor_control_stream::Error),
36
37 #[error("failed to create LegacyTorController object")]
38 LegacyTorControllerCreationFailed(#[source] crate::legacy_tor_controller::Error),
39
40 #[error("failed to authenticate with the tor process")]
41 LegacyTorProcessAuthenticationFailed(#[source] crate::legacy_tor_controller::Error),
42
43 #[error("failed to determine the tor process version")]
44 GetInfoVersionFailed(#[source] crate::legacy_tor_controller::Error),
45
46 #[error("tor process version to old; found {0} but must be at least {1}")]
47 LegacyTorProcessTooOld(String, String),
48
49 #[error("failed to register for STATUS_CLIENT and HS_DESC events")]
50 SetEventsFailed(#[source] crate::legacy_tor_controller::Error),
51
52 #[error("failed to delete unused onion service")]
53 DelOnionFailed(#[source] crate::legacy_tor_controller::Error),
54
55 #[error("failed waiting for async events: {0}")]
56 WaitAsyncEventsFailed(#[source] crate::legacy_tor_controller::Error),
57
58 #[error("failed to begin bootstrap")]
59 SetConfDisableNetwork0Failed(#[source] crate::legacy_tor_controller::Error),
60
61 #[error("failed to setconf")]
62 SetConfFailed(#[source] crate::legacy_tor_controller::Error),
63
64 #[error("failed to add client auth for onion service")]
65 OnionClientAuthAddFailed(#[source] crate::legacy_tor_controller::Error),
66
67 #[error("failed to remove client auth from onion service")]
68 OnionClientAuthRemoveFailed(#[source] crate::legacy_tor_controller::Error),
69
70 #[error("failed to get socks listener")]
71 GetInfoNetListenersSocksFailed(#[source] crate::legacy_tor_controller::Error),
72
73 #[error("no socks listeners available to connect through")]
74 NoSocksListenersFound(),
75
76 #[error("invalid circuit token")]
77 CircuitTokenInvalid(),
78
79 #[error("unable to connect to socks listener")]
80 Socks5ConnectionFailed(#[source] std::io::Error),
81
82 #[error("failed to spawn connect_async thread")]
83 ConnectAsyncThreadSpawnFailed(#[source] std::io::Error),
84
85 #[error("unable to bind TCP listener")]
86 TcpListenerBindFailed(#[source] std::io::Error),
87
88 #[error("unable to get TCP listener's local address")]
89 TcpListenerLocalAddrFailed(#[source] std::io::Error),
90
91 #[error("failed to create onion service")]
92 AddOnionFailed(#[source] crate::legacy_tor_controller::Error),
93
94 #[error("tor not bootstrapped")]
95 LegacyTorNotBootstrapped(),
96
97 #[error("failed to create pluggable-transport directory: {0}")]
98 PluggableTransportDirectoryCreationFailed(#[source] std::io::Error),
99
100 #[error("unable to create pluggable-transport directory because file with same name already exists: {0:?}")]
101 PluggableTransportDirectoryNameCollision(PathBuf),
102
103 #[error("failed to remove old pluggable-transport symlink: {0}")]
104 PluggableTransportSymlinkRemovalFailed(#[source] std::io::Error),
105
106 #[cfg(not(target_os = "windows"))]
107 #[error("failed to create pluggable-transport symlink: {0}")]
108 PluggableTransportSymlinkCreationFailed(#[source] std::io::Error),
109
110 #[cfg(target_os = "windows")]
111 #[error("failed to copy pluggable-transport: {0}")]
112 PluggableTransportCopyFailed(#[source] std::io::Error),
113
114 #[error("pluggable transport binary name not representable as utf8: {0:?}")]
115 PluggableTransportBinaryNameNotUtf8Representnable(std::ffi::OsString),
116
117 #[error("{0}")]
118 PluggableTransportConfigError(
119 #[source] crate::censorship_circumvention::PluggableTransportConfigError,
120 ),
121
122 #[error("pluggable transport multiply defines '{0}' bridge transport type")]
123 BridgeTransportTypeMultiplyDefined(String),
124
125 #[error("bridge transport '{0}' not supported by pluggable transport configuration")]
126 BridgeTransportNotSupported(String),
127
128 #[error("invalid environment variable configuration: {0}")]
129 EnvironmentConfigurationInvalid(String),
130
131 #[error("not implemented")]
132 NotImplemented(),
133}
134
135impl From<Error> for crate::tor_provider::Error {
136 fn from(error: Error) -> Self {
137 crate::tor_provider::Error::Generic(error.to_string())
138 }
139}
140
141struct LegacyCircuitToken {
145 username: String,
146 password: String,
147}
148
149impl LegacyCircuitToken {
150 fn new() -> LegacyCircuitToken {
151 const CIRCUIT_TOKEN_USERNAME_LENGTH: usize = 32usize;
152 const CIRCUIT_TOKEN_PASSWORD_LENGTH: usize = 32usize;
153 let username = generate_password(CIRCUIT_TOKEN_USERNAME_LENGTH);
154 let password = generate_password(CIRCUIT_TOKEN_PASSWORD_LENGTH);
155
156 LegacyCircuitToken { username, password }
157 }
158}
159
160impl Default for LegacyCircuitToken {
161 fn default() -> Self {
162 Self::new()
163 }
164}
165
166#[derive(Clone, Debug)]
171pub enum LegacyTorClientConfig {
172 BundledTor {
173 tor_bin_path: PathBuf,
174 data_directory: PathBuf,
175 proxy_settings: Option<ProxyConfig>,
176 allowed_ports: Option<Vec<u16>>,
177 pluggable_transports: Option<Vec<PluggableTransportConfig>>,
178 bridge_lines: Option<Vec<BridgeLine>>,
179 },
180 SystemTor {
181 tor_socks_addr: SocketAddr,
182 tor_control_addr: SocketAddr,
183 tor_control_auth: TorAuth,
184 },
185}
186
187impl LegacyTorClientConfig {
188 pub fn try_from_environment() -> Result<Self, Error> {
191 use std::env::{var, var_os};
192
193 const TOR_SOCKS_HOST: &str = "TOR_SOCKS_HOST";
195 const TOR_SOCKS_PORT: &str = "TOR_SOCKS_PORT";
196 let tor_socks_addr = match (var(TOR_SOCKS_HOST), var(TOR_SOCKS_PORT)) {
197 (Ok(host), Ok(port)) => {
198 let ip = IpAddr::from_str(host.as_str()).map_err(|_| {
199 Error::EnvironmentConfigurationInvalid(format!(
200 "cannot parse TOR_SOCKS_HOST value '{host}' as ip address"
201 ))
202 })?;
203 let port = u16::from_str(port.as_str()).map_err(|_| {
204 Error::EnvironmentConfigurationInvalid(format!(
205 "cannot parse TOR_SOCKS_PORT value '{port}' as port"
206 ))
207 })?;
208 SocketAddr::new(ip, port)
209 }
210 _ => {
211 return Err(Error::EnvironmentConfigurationInvalid(
212 "environment variables TOR_SOCKS_HOST and TOR_SOCKS_PORT must be defined"
213 .to_string(),
214 ))
215 }
216 };
217
218 const TOR_CONTROL_HOST: &str = "TOR_CONTROL_HOST";
220 const TOR_CONTROL_PORT: &str = "TOR_CONTROL_PORT";
221 let tor_control_addr =
222 match (var(TOR_CONTROL_HOST), var(TOR_CONTROL_PORT)) {
223 (Ok(host), Ok(port)) => {
224 let ip = IpAddr::from_str(host.as_str()).map_err(|_| {
225 Error::EnvironmentConfigurationInvalid(format!(
226 "cannot parse TOR_CONTROL_HOST value '{host}' as ip address"
227 ))
228 })?;
229 let port = u16::from_str(port.as_str()).map_err(|_| {
230 Error::EnvironmentConfigurationInvalid(format!(
231 "cannot parse TOR_CONTROL_PORT value '{port}' as port"
232 ))
233 })?;
234 SocketAddr::new(ip, port)
235 }
236 _ => return Err(Error::EnvironmentConfigurationInvalid(
237 "environment variables TOR_CONTROL_HOST and TOR_CONTROL_PORT must be defined"
238 .to_string(),
239 )),
240 };
241
242 const TOR_CONTROL_COOKIE_AUTH_FILE: &str = "TOR_CONTROL_COOKIE_AUTH_FILE";
244 const TOR_CONTROL_PASSWD: &str = "TOR_CONTROL_PASSWD";
245 let tor_control_auth = if let Some(cookie_file) = var_os(TOR_CONTROL_COOKIE_AUTH_FILE) {
246 let cookie_file: PathBuf = cookie_file.clone().into();
247 TorAuth::CookieFile(cookie_file)
248 } else {
249 match var(TOR_CONTROL_PASSWD) {
250 Ok(control_password) => TorAuth::Password(control_password),
251 Err(std::env::VarError::NotPresent) => TorAuth::Null,
252 _ => {
253 return Err(Error::EnvironmentConfigurationInvalid(
254 "Failed to read TOR_CONTROL_PASSWD".to_string(),
255 ))
256 }
257 }
258 };
259
260 Ok(Self::SystemTor {
261 tor_socks_addr,
262 tor_control_addr,
263 tor_control_auth,
264 })
265 }
266}
267
268#[derive(Clone, Debug, Default, Eq, PartialEq, ZeroizeOnDrop)]
269pub enum TorAuth {
270 #[default]
271 #[zeroize(skip)]
272 Null,
273 Password(String),
274 #[zeroize(skip)]
275 CookieFile(PathBuf),
276}
277
278pub struct LegacyTorClient {
288 daemon: Option<LegacyTorProcess>,
289 version: LegacyTorVersion,
290 controller: LegacyTorController,
291 bootstrapped: bool,
292 socks_listener: Option<SocketAddr>,
293 async_events: Arc<Mutex<Vec<TorEvent>>>,
294 next_connect_handle: ConnectHandle,
295 onion_services: Vec<(V3OnionServiceId, Arc<atomic::AtomicBool>)>,
297 circuit_token_counter: usize,
299 circuit_tokens: BTreeMap<CircuitToken, LegacyCircuitToken>,
300}
301
302impl LegacyTorClient {
303 pub fn new(mut config: LegacyTorClientConfig) -> Result<LegacyTorClient, Error> {
305 let (daemon, mut controller, mut auth, socks_listener) = match &mut config {
306 LegacyTorClientConfig::BundledTor {
307 tor_bin_path,
308 data_directory,
309 ..
310 } => {
311 let daemon =
313 LegacyTorProcess::new(tor_bin_path.as_path(), data_directory.as_path())
314 .map_err(Error::LegacyTorProcessCreationFailed)?;
315 let control_stream =
317 LegacyControlStream::new(daemon.get_control_addr(), Duration::from_millis(16))
318 .map_err(Error::LegacyControlStreamCreationFailed)?;
319
320 let controller = LegacyTorController::new(control_stream)
322 .map_err(Error::LegacyTorControllerCreationFailed)?;
323
324 let password = daemon.get_password().to_string();
325 (Some(daemon), controller, TorAuth::Password(password), None)
326 }
327 LegacyTorClientConfig::SystemTor {
328 tor_socks_addr,
329 tor_control_addr,
330 tor_control_auth,
331 } => {
332 let control_stream =
334 LegacyControlStream::new(tor_control_addr, Duration::from_millis(16))
335 .map_err(Error::LegacyControlStreamCreationFailed)?;
336
337 let controller = LegacyTorController::new(control_stream)
339 .map_err(Error::LegacyTorControllerCreationFailed)?;
340
341 (
342 None,
343 controller,
344 std::mem::take(tor_control_auth),
345 Some(*tor_socks_addr),
346 )
347 }
348 };
349
350 match &mut auth {
352 TorAuth::Null => controller.authenticate(),
353 TorAuth::Password(pass) => controller.authenticate_password(std::mem::take(pass)),
354 TorAuth::CookieFile(file) => controller.authenticate_safecookie(std::mem::take(file)),
355 }
356 .map_err(Error::LegacyTorProcessAuthenticationFailed)?;
357
358 let min_required_version = LegacyTorVersion {
360 major: 0u32,
361 minor: 4u32,
362 micro: 6u32,
363 patch_level: 1u32,
364 status_tag: None,
365 };
366
367 let version = controller
369 .getinfo_version()
370 .map_err(Error::GetInfoVersionFailed)?;
371
372 if version < min_required_version {
373 return Err(Error::LegacyTorProcessTooOld(
374 version.to_string(),
375 min_required_version.to_string(),
376 ));
377 }
378
379 if let LegacyTorClientConfig::BundledTor {
381 data_directory,
382 proxy_settings,
383 allowed_ports,
384 pluggable_transports,
385 bridge_lines,
386 ..
387 } = config
388 {
389 match proxy_settings {
391 Some(ProxyConfig::Socks4(Socks4ProxyConfig { address })) => {
392 controller
393 .setconf(&[("Socks4Proxy", address.to_string())])
394 .map_err(Error::SetConfFailed)?;
395 }
396 Some(ProxyConfig::Socks5(Socks5ProxyConfig {
397 address,
398 username,
399 password,
400 })) => {
401 controller
402 .setconf(&[("Socks5Proxy", address.to_string())])
403 .map_err(Error::SetConfFailed)?;
404 let username = username.unwrap_or("".to_string());
405 if !username.is_empty() {
406 controller
407 .setconf(&[("Socks5ProxyUsername", username.to_string())])
408 .map_err(Error::SetConfFailed)?;
409 }
410 let password = password.unwrap_or("".to_string());
411 if !password.is_empty() {
412 controller
413 .setconf(&[("Socks5ProxyPassword", password.to_string())])
414 .map_err(Error::SetConfFailed)?;
415 }
416 }
417 Some(ProxyConfig::Https(HttpsProxyConfig {
418 address,
419 username,
420 password,
421 })) => {
422 controller
423 .setconf(&[("HTTPSProxy", address.to_string())])
424 .map_err(Error::SetConfFailed)?;
425 let username = username.unwrap_or("".to_string());
426 let password = password.unwrap_or("".to_string());
427 if !username.is_empty() || !password.is_empty() {
428 let authenticator = format!("{}:{}", username, password);
429 controller
430 .setconf(&[("HTTPSProxyAuthenticator", authenticator)])
431 .map_err(Error::SetConfFailed)?;
432 }
433 }
434 None => (),
435 }
436 if let Some(allowed_ports) = allowed_ports {
438 let allowed_addresses: Vec<String> = allowed_ports
439 .iter()
440 .map(|port| format!("*:{port}"))
441 .collect();
442 let allowed_addresses = allowed_addresses.join(", ");
443 controller
444 .setconf(&[("ReachableAddresses", allowed_addresses)])
445 .map_err(Error::SetConfFailed)?;
446 }
447 let mut supported_transports: std::collections::BTreeSet<String> = Default::default();
449 if let Some(pluggable_transports) = pluggable_transports {
450 let mut pt_directory = data_directory.clone();
458 pt_directory.push("pluggable-transports");
459 if !std::path::Path::exists(&pt_directory) {
460 std::fs::create_dir(&pt_directory)
462 .map_err(Error::PluggableTransportDirectoryCreationFailed)?;
463 } else if !std::path::Path::is_dir(&pt_directory) {
464 return Err(Error::PluggableTransportDirectoryNameCollision(
466 pt_directory,
467 ));
468 }
469
470 let mut conf: Vec<(&str, String)> = Default::default();
472 for pt_settings in &pluggable_transports {
473 let path_to_binary = pt_settings.path_to_binary();
476 let binary_name = path_to_binary
477 .file_name()
478 .expect("file_name should be absolute path");
479 let mut pt_symlink = pt_directory.clone();
480 pt_symlink.push(binary_name);
481 let binary_name = if let Some(binary_name) = binary_name.to_str() {
482 binary_name
483 } else {
484 return Err(Error::PluggableTransportBinaryNameNotUtf8Representnable(
485 binary_name.to_os_string(),
486 ));
487 };
488
489 #[cfg(windows)]
491 {
492 let pt_copy = &pt_symlink;
493 let _ = std::fs::remove_file(pt_copy);
495
496 std::fs::copy(path_to_binary, pt_copy).map_err(Error::PluggableTransportCopyFailed)?;
498 }
499 #[cfg(unix)]
501 {
502 if std::fs::read_link(&pt_symlink).is_ok() {
504 std::fs::remove_file(&pt_symlink)
505 .map_err(Error::PluggableTransportSymlinkRemovalFailed)?;
506 }
507
508 std::os::unix::fs::symlink(path_to_binary, &pt_symlink)
509 .map_err(Error::PluggableTransportSymlinkCreationFailed)?;
510 }
511
512 for transport in pt_settings.transports() {
514 if supported_transports.contains(transport) {
515 return Err(Error::BridgeTransportTypeMultiplyDefined(
516 transport.to_string(),
517 ));
518 }
519 supported_transports.insert(transport.to_string());
520 }
521
522 let transports = pt_settings.transports().join(",");
524 use std::path::MAIN_SEPARATOR;
525 let path_to_binary =
526 format!("pluggable-transports{MAIN_SEPARATOR}{binary_name}");
527 let options = pt_settings.options().join(" ");
528
529 let value = format!("{transports} exec {path_to_binary} {options}");
530 conf.push(("ClientTransportPlugin", value));
531 }
532 controller
533 .setconf(conf.as_slice())
534 .map_err(Error::SetConfFailed)?;
535 }
536 if let Some(bridge_lines) = bridge_lines {
538 let mut conf: Vec<(&str, String)> = Default::default();
539 for bridge_line in &bridge_lines {
540 if !supported_transports.contains(bridge_line.transport()) {
541 return Err(Error::BridgeTransportNotSupported(
542 bridge_line.transport().to_string(),
543 ));
544 }
545 let value = bridge_line.as_legacy_tor_setconf_value();
546 conf.push(("Bridge", value));
547 }
548 conf.push(("UseBridges", "1".to_string()));
549 controller
550 .setconf(conf.as_slice())
551 .map_err(Error::SetConfFailed)?;
552 }
553 }
554
555 controller
557 .setevents(&["STATUS_CLIENT", "HS_DESC"])
558 .map_err(Error::SetEventsFailed)?;
559
560 Ok(LegacyTorClient {
561 daemon,
562 version,
563 controller,
564 bootstrapped: false,
565 socks_listener,
566 onion_services: Default::default(),
567 async_events: Default::default(),
568 next_connect_handle: Default::default(),
569 circuit_token_counter: 0usize,
570 circuit_tokens: Default::default(),
571 })
572 }
573
574 pub fn version(&mut self) -> LegacyTorVersion {
576 self.version.clone()
577 }
578
579 fn socks_listener(&mut self) -> Result<SocketAddr, Error> {
580 match self.socks_listener {
581 Some(socks_listener) => Ok(socks_listener),
582 None => {
583 let mut listeners = self
584 .controller
585 .getinfo_net_listeners_socks()
586 .map_err(Error::GetInfoNetListenersSocksFailed)?;
587 if listeners.is_empty() {
588 return Err(Error::NoSocksListenersFound())?;
589 }
590 let socks_listener = listeners.swap_remove(0);
591 self.socks_listener = Some(socks_listener);
592 Ok(socks_listener)
593 }
594 }
595 }
596
597 fn connect_impl(
598 target_addr: TargetAddr,
599 socks_listener: SocketAddr,
600 socks_credentials: Option<(String, String)>,
601 ) -> Result<Socks5Stream, tor_provider::Error> {
602 let socks_target = match target_addr {
604 TargetAddr::Socket(socket_addr) => socks::TargetAddr::Ip(socket_addr),
605 TargetAddr::Domain(domain_addr) => {
606 socks::TargetAddr::Domain(domain_addr.domain().to_string(), domain_addr.port())
607 }
608 TargetAddr::OnionService(OnionAddr::V3(OnionAddrV3 {
609 service_id,
610 virt_port,
611 })) => socks::TargetAddr::Domain(format!("{}.onion", service_id), virt_port),
612 };
613
614 let stream = match socks_credentials {
616 None => Socks5Stream::connect(socks_listener, socks_target),
617 Some((username, password)) => Socks5Stream::connect_with_password(
618 socks_listener,
619 socks_target,
620 &username,
621 &password,
622 ),
623 }
624 .map_err(Error::Socks5ConnectionFailed)?;
625 Ok(stream)
626 }
627}
628
629impl TorProvider for LegacyTorClient {
630 fn update(&mut self) -> Result<Vec<TorEvent>, tor_provider::Error> {
631 let mut i = 0;
632 while i < self.onion_services.len() {
633 if !self.onion_services[i].1.load(atomic::Ordering::Relaxed) {
635 let entry = self.onion_services.swap_remove(i);
636 let service_id = entry.0;
637
638 self.controller
639 .del_onion(&service_id)
640 .map_err(Error::DelOnionFailed)?;
641 } else {
642 i += 1;
643 }
644 }
645
646 let mut events: Vec<TorEvent> = Default::default();
647 for async_event in self
648 .controller
649 .wait_async_events()
650 .map_err(Error::WaitAsyncEventsFailed)?
651 {
652 match async_event {
653 AsyncEvent::StatusClient {
654 severity,
655 action,
656 arguments,
657 } => {
658 if severity == "NOTICE" && action == "BOOTSTRAP" {
659 let mut progress: u32 = 0;
660 let mut tag: String = Default::default();
661 let mut summary: String = Default::default();
662 for (key, val) in arguments {
663 match key.as_str() {
664 "PROGRESS" => progress = val.parse().unwrap_or(0u32),
665 "TAG" => tag = val,
666 "SUMMARY" => summary = val,
667 _ => {} }
669 }
670 events.push(TorEvent::BootstrapStatus {
671 progress,
672 tag,
673 summary,
674 });
675 if progress == 100u32 {
676 events.push(TorEvent::BootstrapComplete);
677 self.bootstrapped = true;
678 }
679 }
680 }
681 AsyncEvent::HsDesc { action, hs_address } => {
682 if action == "UPLOADED" {
683 events.push(TorEvent::OnionServicePublished {
684 service_id: hs_address,
685 });
686 }
687 }
688 AsyncEvent::Unknown { lines } => {
689 println!("Received Unknown Event:");
690 for line in lines.iter() {
691 println!(" {}", line);
692 }
693 }
694 }
695 }
696
697 if let Some(daemon) = &mut self.daemon {
698 for log_line in daemon.wait_log_lines().iter_mut() {
700 events.push(TorEvent::LogReceived {
701 line: std::mem::take(log_line),
702 });
703 }
704 } else if !self.bootstrapped {
705 events.push(TorEvent::BootstrapComplete);
707 self.bootstrapped = true;
708 }
709
710 let mut async_events = self
712 .async_events
713 .lock()
714 .expect("async_events mutex poisoned");
715 if !async_events.is_empty() {
716 events.append(&mut std::mem::take(&mut *async_events));
717 }
718
719 Ok(events)
720 }
721
722 fn bootstrap(&mut self) -> Result<(), tor_provider::Error> {
723 if !self.bootstrapped {
724 self.controller
725 .setconf(&[("DisableNetwork", "0".to_string())])
726 .map_err(Error::SetConfDisableNetwork0Failed)?;
727 }
728 Ok(())
729 }
730
731 fn add_client_auth(
732 &mut self,
733 service_id: &V3OnionServiceId,
734 client_auth: &X25519PrivateKey,
735 ) -> Result<(), tor_provider::Error> {
736 Ok(self
737 .controller
738 .onion_client_auth_add(service_id, client_auth, None, &Default::default())
739 .map_err(Error::OnionClientAuthAddFailed)?)
740 }
741
742 fn remove_client_auth(
743 &mut self,
744 service_id: &V3OnionServiceId,
745 ) -> Result<(), tor_provider::Error> {
746 Ok(self
747 .controller
748 .onion_client_auth_remove(service_id)
749 .map_err(Error::OnionClientAuthRemoveFailed)?)
750 }
751
752 fn connect(
754 &mut self,
755 target: TargetAddr,
756 circuit: Option<CircuitToken>,
757 ) -> Result<OnionStream, tor_provider::Error> {
758 if !self.bootstrapped {
759 return Err(Error::LegacyTorNotBootstrapped().into());
760 }
761
762 let socks_listener = self.socks_listener()?;
763 let socks_credentials = match circuit {
764 Some(circuit) => {
765 if let Some(circuit) = self.circuit_tokens.get(&circuit) {
766 Some((circuit.username.clone(), circuit.password.clone()))
767 } else {
768 return Err(Error::CircuitTokenInvalid())?;
769 }
770 }
771 None => None,
772 };
773
774 let stream = Self::connect_impl(target.clone(), socks_listener, socks_credentials)?;
775
776 Ok(OnionStream {
777 stream: stream.into_inner(),
778 local_addr: None,
779 peer_addr: Some(target),
780 })
781 }
782
783 fn connect_async(
784 &mut self,
785 target: TargetAddr,
786 circuit: Option<CircuitToken>,
787 ) -> Result<ConnectHandle, tor_provider::Error> {
788 let socks_listener = self.socks_listener()?;
789 let socks_credentials = match circuit {
790 Some(circuit) => {
791 if let Some(circuit) = self.circuit_tokens.get(&circuit) {
792 Some((circuit.username.clone(), circuit.password.clone()))
793 } else {
794 return Err(Error::CircuitTokenInvalid())?;
795 }
796 }
797 None => None,
798 };
799
800 let handle = self.next_connect_handle;
801 self.next_connect_handle += 1usize;
802
803 let async_events = Arc::downgrade(&self.async_events);
804
805 std::thread::Builder::new()
807 .spawn(move || {
808 let stream = Self::connect_impl(target.clone(), socks_listener, socks_credentials);
809 if let Some(async_events) = async_events.upgrade() {
810 let event = match stream {
811 Ok(stream) => {
812 let stream = OnionStream {
813 stream: stream.into_inner(),
814 local_addr: None,
815 peer_addr: Some(target),
816 };
817 TorEvent::ConnectComplete { handle, stream }
818 }
819 Err(error) => TorEvent::ConnectFailed { handle, error },
820 };
821 let mut async_events =
822 async_events.lock().expect("async_events mutex poisoned");
823 async_events.push(event);
824 }
825 })
826 .map_err(Error::ConnectAsyncThreadSpawnFailed)?;
827
828 Ok(handle)
829 }
830
831 fn listener(
833 &mut self,
834 private_key: &Ed25519PrivateKey,
835 virt_port: u16,
836 authorized_clients: Option<&[X25519PublicKey]>,
837 ) -> Result<OnionListener, tor_provider::Error> {
838 if !self.bootstrapped {
839 return Err(Error::LegacyTorNotBootstrapped().into());
840 }
841
842 let socket_addr = SocketAddr::from(([127, 0, 0, 1], 0u16));
844 let listener = TcpListener::bind(socket_addr).map_err(Error::TcpListenerBindFailed)?;
845 let socket_addr = listener
846 .local_addr()
847 .map_err(Error::TcpListenerLocalAddrFailed)?;
848
849 let flags = AddOnionFlags {
850 discard_pk: true,
851 v3_auth: authorized_clients.is_some(),
852 ..Default::default()
853 };
854
855 let onion_addr = OnionAddr::V3(OnionAddrV3::new(
856 V3OnionServiceId::from_private_key(private_key),
857 virt_port,
858 ));
859
860 let (_, service_id) = self
862 .controller
863 .add_onion(
864 Some(private_key),
865 &flags,
866 None,
867 virt_port,
868 Some(socket_addr),
869 authorized_clients,
870 )
871 .map_err(Error::AddOnionFailed)?;
872
873 let is_active = Arc::new(atomic::AtomicBool::new(true));
874 self.onion_services
875 .push((service_id, Arc::clone(&is_active)));
876
877 Ok(OnionListener::new(
878 listener,
879 onion_addr,
880 is_active,
881 |is_active| {
882 is_active.store(false, atomic::Ordering::Relaxed);
883 },
884 ))
885 }
886
887 fn generate_token(&mut self) -> CircuitToken {
888 let new_token = self.circuit_token_counter;
889 self.circuit_token_counter += 1;
890 self.circuit_tokens
891 .insert(new_token, LegacyCircuitToken::new());
892 new_token
893 }
894
895 fn release_token(&mut self, circuit_token: CircuitToken) {
896 self.circuit_tokens.remove(&circuit_token);
897 }
898}