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("{0}")]
98 PluggableTransportConfigDirectoryCreationFailed(#[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("{0}")]
104 PluggableTransportSymlinkRemovalFailed(#[source] std::io::Error),
105
106 #[error("{0}")]
107 PluggableTransportSymlinkCreationFailed(#[source] std::io::Error),
108
109 #[error("pluggable transport binary name not representable as utf8: {0:?}")]
110 PluggableTransportBinaryNameNotUtf8Representnable(std::ffi::OsString),
111
112 #[error("{0}")]
113 PluggableTransportConfigError(
114 #[source] crate::censorship_circumvention::PluggableTransportConfigError,
115 ),
116
117 #[error("pluggable transport multiply defines '{0}' bridge transport type")]
118 BridgeTransportTypeMultiplyDefined(String),
119
120 #[error("bridge transport '{0}' not supported by pluggable transport configuration")]
121 BridgeTransportNotSupported(String),
122
123 #[error("invalid environment variable configuration: {0}")]
124 EnvironmentConfigurationInvalid(String),
125
126 #[error("not implemented")]
127 NotImplemented(),
128}
129
130impl From<Error> for crate::tor_provider::Error {
131 fn from(error: Error) -> Self {
132 crate::tor_provider::Error::Generic(error.to_string())
133 }
134}
135
136struct LegacyCircuitToken {
140 username: String,
141 password: String,
142}
143
144impl LegacyCircuitToken {
145 fn new() -> LegacyCircuitToken {
146 const CIRCUIT_TOKEN_USERNAME_LENGTH: usize = 32usize;
147 const CIRCUIT_TOKEN_PASSWORD_LENGTH: usize = 32usize;
148 let username = generate_password(CIRCUIT_TOKEN_USERNAME_LENGTH);
149 let password = generate_password(CIRCUIT_TOKEN_PASSWORD_LENGTH);
150
151 LegacyCircuitToken { username, password }
152 }
153}
154
155impl Default for LegacyCircuitToken {
156 fn default() -> Self {
157 Self::new()
158 }
159}
160
161#[derive(Clone, Debug)]
166pub enum LegacyTorClientConfig {
167 BundledTor {
168 tor_bin_path: PathBuf,
169 data_directory: PathBuf,
170 proxy_settings: Option<ProxyConfig>,
171 allowed_ports: Option<Vec<u16>>,
172 pluggable_transports: Option<Vec<PluggableTransportConfig>>,
173 bridge_lines: Option<Vec<BridgeLine>>,
174 },
175 SystemTor {
176 tor_socks_addr: SocketAddr,
177 tor_control_addr: SocketAddr,
178 tor_control_auth: TorAuth,
179 },
180}
181
182impl LegacyTorClientConfig {
183 pub fn try_from_environment() -> Result<Self, Error> {
186 use std::env::{var, var_os};
187
188 const TOR_SOCKS_HOST: &str = "TOR_SOCKS_HOST";
190 const TOR_SOCKS_PORT: &str = "TOR_SOCKS_PORT";
191 let tor_socks_addr = match (var(TOR_SOCKS_HOST), var(TOR_SOCKS_PORT)) {
192 (Ok(host), Ok(port)) => {
193 let ip = IpAddr::from_str(host.as_str()).map_err(|_| {
194 Error::EnvironmentConfigurationInvalid(format!(
195 "cannot parse TOR_SOCKS_HOST value '{host}' as ip address"
196 ))
197 })?;
198 let port = u16::from_str(port.as_str()).map_err(|_| {
199 Error::EnvironmentConfigurationInvalid(format!(
200 "cannot parse TOR_SOCKS_PORT value '{port}' as port"
201 ))
202 })?;
203 SocketAddr::new(ip, port)
204 }
205 _ => {
206 return Err(Error::EnvironmentConfigurationInvalid(
207 "environment variables TOR_SOCKS_HOST and TOR_SOCKS_PORT must be defined"
208 .to_string(),
209 ))
210 }
211 };
212
213 const TOR_CONTROL_HOST: &str = "TOR_CONTROL_HOST";
215 const TOR_CONTROL_PORT: &str = "TOR_CONTROL_PORT";
216 let tor_control_addr =
217 match (var(TOR_CONTROL_HOST), var(TOR_CONTROL_PORT)) {
218 (Ok(host), Ok(port)) => {
219 let ip = IpAddr::from_str(host.as_str()).map_err(|_| {
220 Error::EnvironmentConfigurationInvalid(format!(
221 "cannot parse TOR_CONTROL_HOST value '{host}' as ip address"
222 ))
223 })?;
224 let port = u16::from_str(port.as_str()).map_err(|_| {
225 Error::EnvironmentConfigurationInvalid(format!(
226 "cannot parse TOR_CONTROL_PORT value '{port}' as port"
227 ))
228 })?;
229 SocketAddr::new(ip, port)
230 }
231 _ => return Err(Error::EnvironmentConfigurationInvalid(
232 "environment variables TOR_CONTROL_HOST and TOR_CONTROL_PORT must be defined"
233 .to_string(),
234 )),
235 };
236
237 const TOR_CONTROL_COOKIE_AUTH_FILE: &str = "TOR_CONTROL_COOKIE_AUTH_FILE";
239 const TOR_CONTROL_PASSWD: &str = "TOR_CONTROL_PASSWD";
240 let tor_control_auth = if let Some(cookie_file) = var_os(TOR_CONTROL_COOKIE_AUTH_FILE) {
241 let cookie_file: PathBuf = cookie_file.clone().into();
242 TorAuth::CookieFile(cookie_file)
243 } else {
244 match var(TOR_CONTROL_PASSWD) {
245 Ok(control_password) => TorAuth::Password(control_password),
246 Err(std::env::VarError::NotPresent) => TorAuth::Null,
247 _ => {
248 return Err(Error::EnvironmentConfigurationInvalid(
249 "Failed to read TOR_CONTROL_PASSWD".to_string(),
250 ))
251 }
252 }
253 };
254
255 Ok(Self::SystemTor {
256 tor_socks_addr,
257 tor_control_addr,
258 tor_control_auth,
259 })
260 }
261}
262
263#[derive(Clone, Debug, Default, Eq, PartialEq, ZeroizeOnDrop)]
264pub enum TorAuth {
265 #[default]
266 #[zeroize(skip)]
267 Null,
268 Password(String),
269 #[zeroize(skip)]
270 CookieFile(PathBuf),
271}
272
273pub struct LegacyTorClient {
283 daemon: Option<LegacyTorProcess>,
284 version: LegacyTorVersion,
285 controller: LegacyTorController,
286 bootstrapped: bool,
287 socks_listener: Option<SocketAddr>,
288 async_events: Arc<Mutex<Vec<TorEvent>>>,
289 next_connect_handle: ConnectHandle,
290 onion_services: Vec<(V3OnionServiceId, Arc<atomic::AtomicBool>)>,
292 circuit_token_counter: usize,
294 circuit_tokens: BTreeMap<CircuitToken, LegacyCircuitToken>,
295}
296
297impl LegacyTorClient {
298 pub fn new(mut config: LegacyTorClientConfig) -> Result<LegacyTorClient, Error> {
300 let (daemon, mut controller, mut auth, socks_listener) = match &mut config {
301 LegacyTorClientConfig::BundledTor {
302 tor_bin_path,
303 data_directory,
304 ..
305 } => {
306 let daemon =
308 LegacyTorProcess::new(tor_bin_path.as_path(), data_directory.as_path())
309 .map_err(Error::LegacyTorProcessCreationFailed)?;
310 let control_stream =
312 LegacyControlStream::new(daemon.get_control_addr(), Duration::from_millis(16))
313 .map_err(Error::LegacyControlStreamCreationFailed)?;
314
315 let controller = LegacyTorController::new(control_stream)
317 .map_err(Error::LegacyTorControllerCreationFailed)?;
318
319 let password = daemon.get_password().to_string();
320 (Some(daemon), controller, TorAuth::Password(password), None)
321 }
322 LegacyTorClientConfig::SystemTor {
323 tor_socks_addr,
324 tor_control_addr,
325 tor_control_auth,
326 } => {
327 let control_stream =
329 LegacyControlStream::new(tor_control_addr, Duration::from_millis(16))
330 .map_err(Error::LegacyControlStreamCreationFailed)?;
331
332 let controller = LegacyTorController::new(control_stream)
334 .map_err(Error::LegacyTorControllerCreationFailed)?;
335
336 (
337 None,
338 controller,
339 std::mem::take(tor_control_auth),
340 Some(*tor_socks_addr),
341 )
342 }
343 };
344
345 match &mut auth {
347 TorAuth::Null => controller.authenticate(),
348 TorAuth::Password(pass) => controller.authenticate_password(std::mem::take(pass)),
349 TorAuth::CookieFile(file) => controller.authenticate_safecookie(std::mem::take(file)),
350 }
351 .map_err(Error::LegacyTorProcessAuthenticationFailed)?;
352
353 let min_required_version = LegacyTorVersion {
355 major: 0u32,
356 minor: 4u32,
357 micro: 6u32,
358 patch_level: 1u32,
359 status_tag: None,
360 };
361
362 let version = controller
364 .getinfo_version()
365 .map_err(Error::GetInfoVersionFailed)?;
366
367 if version < min_required_version {
368 return Err(Error::LegacyTorProcessTooOld(
369 version.to_string(),
370 min_required_version.to_string(),
371 ));
372 }
373
374 if let LegacyTorClientConfig::BundledTor {
376 data_directory,
377 proxy_settings,
378 allowed_ports,
379 pluggable_transports,
380 bridge_lines,
381 ..
382 } = config
383 {
384 match proxy_settings {
386 Some(ProxyConfig::Socks4(Socks4ProxyConfig { address })) => {
387 controller
388 .setconf(&[("Socks4Proxy", address.to_string())])
389 .map_err(Error::SetConfFailed)?;
390 }
391 Some(ProxyConfig::Socks5(Socks5ProxyConfig {
392 address,
393 username,
394 password,
395 })) => {
396 controller
397 .setconf(&[("Socks5Proxy", address.to_string())])
398 .map_err(Error::SetConfFailed)?;
399 let username = username.unwrap_or("".to_string());
400 if !username.is_empty() {
401 controller
402 .setconf(&[("Socks5ProxyUsername", username.to_string())])
403 .map_err(Error::SetConfFailed)?;
404 }
405 let password = password.unwrap_or("".to_string());
406 if !password.is_empty() {
407 controller
408 .setconf(&[("Socks5ProxyPassword", password.to_string())])
409 .map_err(Error::SetConfFailed)?;
410 }
411 }
412 Some(ProxyConfig::Https(HttpsProxyConfig {
413 address,
414 username,
415 password,
416 })) => {
417 controller
418 .setconf(&[("HTTPSProxy", address.to_string())])
419 .map_err(Error::SetConfFailed)?;
420 let username = username.unwrap_or("".to_string());
421 let password = password.unwrap_or("".to_string());
422 if !username.is_empty() || !password.is_empty() {
423 let authenticator = format!("{}:{}", username, password);
424 controller
425 .setconf(&[("HTTPSProxyAuthenticator", authenticator)])
426 .map_err(Error::SetConfFailed)?;
427 }
428 }
429 None => (),
430 }
431 if let Some(allowed_ports) = allowed_ports {
433 let allowed_addresses: Vec<String> = allowed_ports
434 .iter()
435 .map(|port| format!("*{{}}:{port}"))
436 .collect();
437 let allowed_addresses = allowed_addresses.join(", ");
438 controller
439 .setconf(&[("ReachableAddresses", allowed_addresses)])
440 .map_err(Error::SetConfFailed)?;
441 }
442 let mut supported_transports: std::collections::BTreeSet<String> = Default::default();
444 if let Some(pluggable_transports) = pluggable_transports {
445 let mut pt_directory = data_directory.clone();
453 pt_directory.push("pluggable-transports");
454 if !std::path::Path::exists(&pt_directory) {
455 std::fs::create_dir(&pt_directory)
457 .map_err(Error::PluggableTransportConfigDirectoryCreationFailed)?;
458 } else if !std::path::Path::is_dir(&pt_directory) {
459 return Err(Error::PluggableTransportDirectoryNameCollision(
461 pt_directory,
462 ));
463 }
464
465 let mut conf: Vec<(&str, String)> = Default::default();
467 for pt_settings in &pluggable_transports {
468 let path_to_binary = pt_settings.path_to_binary();
471 let binary_name = path_to_binary
472 .file_name()
473 .expect("file_name should be absolute path");
474 let mut pt_symlink = pt_directory.clone();
475 pt_symlink.push(binary_name);
476 let binary_name = if let Some(binary_name) = binary_name.to_str() {
477 binary_name
478 } else {
479 return Err(Error::PluggableTransportBinaryNameNotUtf8Representnable(
480 binary_name.to_os_string(),
481 ));
482 };
483
484 if std::path::Path::exists(&pt_symlink) {
486 std::fs::remove_file(&pt_symlink)
487 .map_err(Error::PluggableTransportSymlinkRemovalFailed)?;
488 }
489
490 #[cfg(windows)]
492 std::os::windows::fs::symlink_file(path_to_binary, &pt_symlink)
493 .map_err(Error::PluggableTransportSymlinkCreationFailed)?;
494 #[cfg(unix)]
495 std::os::unix::fs::symlink(path_to_binary, &pt_symlink)
496 .map_err(Error::PluggableTransportSymlinkCreationFailed)?;
497
498 for transport in pt_settings.transports() {
500 if supported_transports.contains(transport) {
501 return Err(Error::BridgeTransportTypeMultiplyDefined(
502 transport.to_string(),
503 ));
504 }
505 supported_transports.insert(transport.to_string());
506 }
507
508 let transports = pt_settings.transports().join(",");
510 use std::path::MAIN_SEPARATOR;
511 let path_to_binary =
512 format!("pluggable-transports{MAIN_SEPARATOR}{binary_name}");
513 let options = pt_settings.options().join(" ");
514
515 let value = format!("{transports} exec {path_to_binary} {options}");
516 conf.push(("ClientTransportPlugin", value));
517 }
518 controller
519 .setconf(conf.as_slice())
520 .map_err(Error::SetConfFailed)?;
521 }
522 if let Some(bridge_lines) = bridge_lines {
524 let mut conf: Vec<(&str, String)> = Default::default();
525 for bridge_line in &bridge_lines {
526 if !supported_transports.contains(bridge_line.transport()) {
527 return Err(Error::BridgeTransportNotSupported(
528 bridge_line.transport().to_string(),
529 ));
530 }
531 let value = bridge_line.as_legacy_tor_setconf_value();
532 conf.push(("Bridge", value));
533 }
534 conf.push(("UseBridges", "1".to_string()));
535 controller
536 .setconf(conf.as_slice())
537 .map_err(Error::SetConfFailed)?;
538 }
539 }
540
541 controller
543 .setevents(&["STATUS_CLIENT", "HS_DESC"])
544 .map_err(Error::SetEventsFailed)?;
545
546 Ok(LegacyTorClient {
547 daemon,
548 version,
549 controller,
550 bootstrapped: false,
551 socks_listener,
552 onion_services: Default::default(),
553 async_events: Default::default(),
554 next_connect_handle: Default::default(),
555 circuit_token_counter: 0usize,
556 circuit_tokens: Default::default(),
557 })
558 }
559
560 pub fn version(&mut self) -> LegacyTorVersion {
562 self.version.clone()
563 }
564
565 fn socks_listener(&mut self) -> Result<SocketAddr, Error> {
566 match self.socks_listener {
567 Some(socks_listener) => Ok(socks_listener),
568 None => {
569 let mut listeners = self
570 .controller
571 .getinfo_net_listeners_socks()
572 .map_err(Error::GetInfoNetListenersSocksFailed)?;
573 if listeners.is_empty() {
574 return Err(Error::NoSocksListenersFound())?;
575 }
576 let socks_listener = listeners.swap_remove(0);
577 self.socks_listener = Some(socks_listener);
578 Ok(socks_listener)
579 }
580 }
581 }
582
583 fn connect_impl(
584 target_addr: TargetAddr,
585 socks_listener: SocketAddr,
586 socks_credentials: Option<(String, String)>,
587 ) -> Result<Socks5Stream, tor_provider::Error> {
588 let socks_target = match target_addr {
590 TargetAddr::Socket(socket_addr) => socks::TargetAddr::Ip(socket_addr),
591 TargetAddr::Domain(domain_addr) => {
592 socks::TargetAddr::Domain(domain_addr.domain().to_string(), domain_addr.port())
593 }
594 TargetAddr::OnionService(OnionAddr::V3(OnionAddrV3 {
595 service_id,
596 virt_port,
597 })) => socks::TargetAddr::Domain(format!("{}.onion", service_id), virt_port),
598 };
599
600 let stream = match socks_credentials {
602 None => Socks5Stream::connect(socks_listener, socks_target),
603 Some((username, password)) => Socks5Stream::connect_with_password(
604 socks_listener,
605 socks_target,
606 &username,
607 &password,
608 ),
609 }
610 .map_err(Error::Socks5ConnectionFailed)?;
611 Ok(stream)
612 }
613}
614
615impl TorProvider for LegacyTorClient {
616 fn update(&mut self) -> Result<Vec<TorEvent>, tor_provider::Error> {
617 let mut i = 0;
618 while i < self.onion_services.len() {
619 if !self.onion_services[i].1.load(atomic::Ordering::Relaxed) {
621 let entry = self.onion_services.swap_remove(i);
622 let service_id = entry.0;
623
624 self.controller
625 .del_onion(&service_id)
626 .map_err(Error::DelOnionFailed)?;
627 } else {
628 i += 1;
629 }
630 }
631
632 let mut events: Vec<TorEvent> = Default::default();
633 for async_event in self
634 .controller
635 .wait_async_events()
636 .map_err(Error::WaitAsyncEventsFailed)?
637 {
638 match async_event {
639 AsyncEvent::StatusClient {
640 severity,
641 action,
642 arguments,
643 } => {
644 if severity == "NOTICE" && action == "BOOTSTRAP" {
645 let mut progress: u32 = 0;
646 let mut tag: String = Default::default();
647 let mut summary: String = Default::default();
648 for (key, val) in arguments {
649 match key.as_str() {
650 "PROGRESS" => progress = val.parse().unwrap_or(0u32),
651 "TAG" => tag = val,
652 "SUMMARY" => summary = val,
653 _ => {} }
655 }
656 events.push(TorEvent::BootstrapStatus {
657 progress,
658 tag,
659 summary,
660 });
661 if progress == 100u32 {
662 events.push(TorEvent::BootstrapComplete);
663 self.bootstrapped = true;
664 }
665 }
666 }
667 AsyncEvent::HsDesc { action, hs_address } => {
668 if action == "UPLOADED" {
669 events.push(TorEvent::OnionServicePublished {
670 service_id: hs_address,
671 });
672 }
673 }
674 AsyncEvent::Unknown { lines } => {
675 println!("Received Unknown Event:");
676 for line in lines.iter() {
677 println!(" {}", line);
678 }
679 }
680 }
681 }
682
683 if let Some(daemon) = &mut self.daemon {
684 for log_line in daemon.wait_log_lines().iter_mut() {
686 events.push(TorEvent::LogReceived {
687 line: std::mem::take(log_line),
688 });
689 }
690 } else if !self.bootstrapped {
691 events.push(TorEvent::BootstrapComplete);
693 self.bootstrapped = true;
694 }
695
696 let mut async_events = self
698 .async_events
699 .lock()
700 .expect("async_events mutex poisoned");
701 if !async_events.is_empty() {
702 events.append(&mut std::mem::take(&mut *async_events));
703 }
704
705 Ok(events)
706 }
707
708 fn bootstrap(&mut self) -> Result<(), tor_provider::Error> {
709 if !self.bootstrapped {
710 self.controller
711 .setconf(&[("DisableNetwork", "0".to_string())])
712 .map_err(Error::SetConfDisableNetwork0Failed)?;
713 }
714 Ok(())
715 }
716
717 fn add_client_auth(
718 &mut self,
719 service_id: &V3OnionServiceId,
720 client_auth: &X25519PrivateKey,
721 ) -> Result<(), tor_provider::Error> {
722 Ok(self
723 .controller
724 .onion_client_auth_add(service_id, client_auth, None, &Default::default())
725 .map_err(Error::OnionClientAuthAddFailed)?)
726 }
727
728 fn remove_client_auth(
729 &mut self,
730 service_id: &V3OnionServiceId,
731 ) -> Result<(), tor_provider::Error> {
732 Ok(self
733 .controller
734 .onion_client_auth_remove(service_id)
735 .map_err(Error::OnionClientAuthRemoveFailed)?)
736 }
737
738 fn connect(
740 &mut self,
741 target: TargetAddr,
742 circuit: Option<CircuitToken>,
743 ) -> Result<OnionStream, tor_provider::Error> {
744 if !self.bootstrapped {
745 return Err(Error::LegacyTorNotBootstrapped().into());
746 }
747
748 let socks_listener = self.socks_listener()?;
749 let socks_credentials = match circuit {
750 Some(circuit) => {
751 if let Some(circuit) = self.circuit_tokens.get(&circuit) {
752 Some((circuit.username.clone(), circuit.password.clone()))
753 } else {
754 return Err(Error::CircuitTokenInvalid())?;
755 }
756 }
757 None => None,
758 };
759
760 let stream = Self::connect_impl(target.clone(), socks_listener, socks_credentials)?;
761
762 Ok(OnionStream {
763 stream: stream.into_inner(),
764 local_addr: None,
765 peer_addr: Some(target),
766 })
767 }
768
769 fn connect_async(
770 &mut self,
771 target: TargetAddr,
772 circuit: Option<CircuitToken>,
773 ) -> Result<ConnectHandle, tor_provider::Error> {
774 let socks_listener = self.socks_listener()?;
775 let socks_credentials = match circuit {
776 Some(circuit) => {
777 if let Some(circuit) = self.circuit_tokens.get(&circuit) {
778 Some((circuit.username.clone(), circuit.password.clone()))
779 } else {
780 return Err(Error::CircuitTokenInvalid())?;
781 }
782 }
783 None => None,
784 };
785
786 let handle = self.next_connect_handle;
787 self.next_connect_handle += 1usize;
788
789 let async_events = Arc::downgrade(&self.async_events);
790
791 std::thread::Builder::new()
793 .spawn(move || {
794 let stream = Self::connect_impl(target.clone(), socks_listener, socks_credentials);
795 if let Some(async_events) = async_events.upgrade() {
796 let event = match stream {
797 Ok(stream) => {
798 let stream = OnionStream {
799 stream: stream.into_inner(),
800 local_addr: None,
801 peer_addr: Some(target),
802 };
803 TorEvent::ConnectComplete { handle, stream }
804 }
805 Err(error) => TorEvent::ConnectFailed { handle, error },
806 };
807 let mut async_events =
808 async_events.lock().expect("async_events mutex poisoned");
809 async_events.push(event);
810 }
811 })
812 .map_err(Error::ConnectAsyncThreadSpawnFailed)?;
813
814 Ok(handle)
815 }
816
817 fn listener(
819 &mut self,
820 private_key: &Ed25519PrivateKey,
821 virt_port: u16,
822 authorized_clients: Option<&[X25519PublicKey]>,
823 ) -> Result<OnionListener, tor_provider::Error> {
824 if !self.bootstrapped {
825 return Err(Error::LegacyTorNotBootstrapped().into());
826 }
827
828 let socket_addr = SocketAddr::from(([127, 0, 0, 1], 0u16));
830 let listener = TcpListener::bind(socket_addr).map_err(Error::TcpListenerBindFailed)?;
831 let socket_addr = listener
832 .local_addr()
833 .map_err(Error::TcpListenerLocalAddrFailed)?;
834
835 let flags = AddOnionFlags {
836 discard_pk: true,
837 v3_auth: authorized_clients.is_some(),
838 ..Default::default()
839 };
840
841 let onion_addr = OnionAddr::V3(OnionAddrV3::new(
842 V3OnionServiceId::from_private_key(private_key),
843 virt_port,
844 ));
845
846 let (_, service_id) = self
848 .controller
849 .add_onion(
850 Some(private_key),
851 &flags,
852 None,
853 virt_port,
854 Some(socket_addr),
855 authorized_clients,
856 )
857 .map_err(Error::AddOnionFailed)?;
858
859 let is_active = Arc::new(atomic::AtomicBool::new(true));
860 self.onion_services
861 .push((service_id, Arc::clone(&is_active)));
862
863 Ok(OnionListener::new(
864 listener,
865 onion_addr,
866 is_active,
867 |is_active| {
868 is_active.store(false, atomic::Ordering::Relaxed);
869 },
870 ))
871 }
872
873 fn generate_token(&mut self) -> CircuitToken {
874 let new_token = self.circuit_token_counter;
875 self.circuit_token_counter += 1;
876 self.circuit_tokens
877 .insert(new_token, LegacyCircuitToken::new());
878 new_token
879 }
880
881 fn release_token(&mut self, circuit_token: CircuitToken) {
882 self.circuit_tokens.remove(&circuit_token);
883 }
884}