tor_chanmgr/lib.rs
1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc = include_str!("../README.md")]
3// @@ begin lint list maintained by maint/add_warning @@
4#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
5#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
6#![warn(missing_docs)]
7#![warn(noop_method_call)]
8#![warn(unreachable_pub)]
9#![warn(clippy::all)]
10#![deny(clippy::await_holding_lock)]
11#![deny(clippy::cargo_common_metadata)]
12#![deny(clippy::cast_lossless)]
13#![deny(clippy::checked_conversions)]
14#![warn(clippy::cognitive_complexity)]
15#![deny(clippy::debug_assert_with_mut_call)]
16#![deny(clippy::exhaustive_enums)]
17#![deny(clippy::exhaustive_structs)]
18#![deny(clippy::expl_impl_clone_on_copy)]
19#![deny(clippy::fallible_impl_from)]
20#![deny(clippy::implicit_clone)]
21#![deny(clippy::large_stack_arrays)]
22#![warn(clippy::manual_ok_or)]
23#![deny(clippy::missing_docs_in_private_items)]
24#![warn(clippy::needless_borrow)]
25#![warn(clippy::needless_pass_by_value)]
26#![warn(clippy::option_option)]
27#![deny(clippy::print_stderr)]
28#![deny(clippy::print_stdout)]
29#![warn(clippy::rc_buffer)]
30#![deny(clippy::ref_option_ref)]
31#![warn(clippy::semicolon_if_nothing_returned)]
32#![warn(clippy::trait_duplication_in_bounds)]
33#![deny(clippy::unchecked_time_subtraction)]
34#![deny(clippy::unnecessary_wraps)]
35#![warn(clippy::unseparated_literal_suffix)]
36#![deny(clippy::unwrap_used)]
37#![deny(clippy::mod_module_files)]
38#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
39#![allow(clippy::uninlined_format_args)]
40#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
41#![allow(clippy::result_large_err)] // temporary workaround for arti#587
42#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
43#![allow(clippy::needless_lifetimes)] // See arti#1765
44#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
45#![allow(clippy::collapsible_if)] // See arti#2342
46#![deny(clippy::unused_async)]
47#![deny(clippy::string_slice)] // See arti#2571
48//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
49
50pub mod builder;
51mod config;
52mod err;
53mod event;
54pub mod factory;
55mod mgr;
56#[cfg(test)]
57mod testing;
58pub mod transport;
59
60use futures::StreamExt;
61use futures::select_biased;
62use std::result::Result as StdResult;
63use std::sync::{Arc, Weak};
64use std::time::Duration;
65use tor_config::ReconfigureError;
66use tor_error::error_report;
67use tor_linkspec::{ChanTarget, OwnedChanTarget};
68use tor_netdir::{NetDirProvider, params::NetParameters};
69use tor_proto::channel::Channel;
70#[cfg(feature = "experimental-api")]
71use tor_proto::memquota::ChannelAccount;
72use tor_proto::memquota::ToplevelAccount;
73use tor_rtcompat::SpawnExt;
74use tracing::debug;
75use tracing::instrument;
76use void::{ResultVoidErrExt, Void};
77
78#[cfg(feature = "relay")]
79use {
80 async_trait::async_trait, safelog::Sensitive, tor_proto::relay::CreateRequestHandler,
81 tor_proto::relay::channel_provider::ChannelProvider,
82};
83
84pub use err::Error;
85
86pub use config::{ChannelConfig, ChannelConfigBuilder, ProxyProtocol};
87pub use mgr::ChanMgrConfig;
88
89use tor_rtcompat::Runtime;
90
91/// A Result as returned by this crate.
92pub type Result<T> = std::result::Result<T, Error>;
93
94use crate::factory::BootstrapReporter;
95pub use event::{ConnBlockage, ConnStatus, ConnStatusEvents};
96use tor_rtcompat::scheduler::{TaskHandle, TaskSchedule};
97
98/// An object that remembers a set of live channels, and launches new ones on
99/// request.
100///
101/// Use the [`ChanMgr::get_or_launch`] function to create a new [`Channel`], or
102/// get one if it exists. (For a slightly lower-level API that does no caching,
103/// see [`ChannelFactory`](factory::ChannelFactory) and its implementors.
104///
105/// Each channel is kept open as long as there is a reference to it, or
106/// something else (such as the relay or a network error) kills the channel.
107///
108/// After a `ChanMgr` launches a channel, it keeps a reference to it until that
109/// channel has been unused (that is, had no circuits attached to it) for a
110/// certain amount of time. (Currently this interval is chosen randomly from
111/// between 180-270 seconds, but this is an implementation detail that may change
112/// in the future.)
113pub struct ChanMgr<R: Runtime> {
114 /// Internal channel manager object that does the actual work.
115 ///
116 /// ## How this is built
117 ///
118 /// This internal manager is parameterized over an
119 /// [`mgr::AbstractChannelFactory`], which here is instantiated with a [`factory::CompoundFactory`].
120 /// The `CompoundFactory` itself holds:
121 /// * A `dyn` [`factory::AbstractPtMgr`] that can provide a `dyn`
122 /// [`factory::ChannelFactory`] for each supported pluggable transport.
123 /// This starts out as `None`, but can be replaced with [`ChanMgr::set_pt_mgr`].
124 /// The `TorClient` code currently sets this using `tor_ptmgr::PtMgr`.
125 /// `PtMgr` currently returns `ChannelFactory` implementations that are
126 /// built using [`transport::proxied::ExternalProxyPlugin`], which implements
127 /// [`transport::TransportImplHelper`], which in turn is wrapped into a
128 /// `ChanBuilder` to implement `ChannelFactory`.
129 /// * A generic [`factory::ChannelFactory`] that it uses for everything else
130 /// We instantiate this with a
131 /// [`builder::ChanBuilder`] using a [`transport::default::DefaultTransport`].
132 // This type is a bit long, but I think it's better to just state it here explicitly rather than
133 // hiding parts of it behind a type alias to make it look nicer.
134 mgr: mgr::AbstractChanMgr<
135 factory::CompoundFactory<builder::ChanBuilder<R, transport::DefaultTransport<R>>>,
136 >,
137
138 /// Stream of [`ConnStatus`] events.
139 bootstrap_status: event::ConnStatusEvents,
140
141 /// The runtime. Needed to possibly spawn tasks.
142 #[allow(unused)] // Relay use this, not client yet. Keep it here instead of gating.
143 runtime: R,
144}
145
146/// Description of how we got a channel.
147#[non_exhaustive]
148#[derive(Debug, Copy, Clone, Eq, PartialEq)]
149pub enum ChanProvenance {
150 /// This channel was newly launched, or was in progress and finished while
151 /// we were waiting.
152 NewlyCreated,
153 /// This channel already existed when we asked for it.
154 Preexisting,
155}
156
157/// Dormancy state, as far as the channel manager is concerned
158///
159/// This is usually derived in higher layers from `arti_client::DormantMode`.
160#[non_exhaustive]
161#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
162pub enum Dormancy {
163 /// Not dormant
164 ///
165 /// Channels will operate normally.
166 #[default]
167 Active,
168 /// Totally dormant
169 ///
170 /// Channels will not perform any spontaneous activity (eg, netflow padding)
171 Dormant,
172}
173
174/// The usage that we have in mind when requesting a channel.
175///
176/// A channel may be used in multiple ways. Each time a channel is requested
177/// from `ChanMgr` a separate `ChannelUsage` is passed in to tell the `ChanMgr`
178/// how the channel will be used this time.
179///
180/// To be clear, the `ChannelUsage` is aspect of a _request_ for a channel, and
181/// is not an immutable property of the channel itself.
182///
183/// This type is obtained from a `tor_circmgr::usage::SupportedCircUsage` in
184/// `tor_circmgr::usage`, and it has roughly the same set of variants.
185#[derive(Clone, Debug, Copy, Eq, PartialEq)]
186#[non_exhaustive]
187pub enum ChannelUsage {
188 /// Requesting a channel to use for BEGINDIR-based non-anonymous directory
189 /// connections.
190 Dir,
191
192 /// Requesting a channel to transmit user traffic (including exit traffic)
193 /// over the network.
194 ///
195 /// This includes the case where we are constructing a circuit preemptively,
196 /// and _planning_ to use it for user traffic later on.
197 UserTraffic,
198
199 /// Requesting a channel that the caller does not plan to used at all, or
200 /// which it plans to use only for testing circuits.
201 UselessCircuit,
202}
203
204impl<R: Runtime> ChanMgr<R> {
205 /// Construct a new channel manager.
206 ///
207 /// A new `ChannelAccount` will be made from `memquota`, for each Channel.
208 ///
209 /// The `ChannelAccount` is used for data associated with this channel.
210 ///
211 /// This does *not* (currently) include downstream outbound data
212 /// (ie, data processed by the channel implementation here,
213 /// awaiting TLS processing and actual transmission).
214 /// In any case we try to keep those buffers small.
215 ///
216 /// The ChannelAccount *does* track upstream outbound data
217 /// (ie, data processed by a circuit, but not yet by the channel),
218 /// even though that data relates to a specific circuit.
219 /// TODO #1652 use `CircuitAccount` for circuit->channel queue.
220 ///
221 /// # Usage note
222 ///
223 /// For the manager to work properly, you will need to call `ChanMgr::launch_background_tasks`.
224 ///
225 /// The `keymgr` is only needed for a relay which is used for authenticating its channel to
226 /// other relays. Pass `None` for a client.
227 pub fn new(
228 runtime: R,
229 config: ChanMgrConfig,
230 dormancy: Dormancy,
231 netparams: &NetParameters,
232 memquota: ToplevelAccount,
233 ) -> Result<Self>
234 where
235 R: 'static,
236 {
237 let (sender, receiver) = event::channel();
238 let sender = Arc::new(std::sync::Mutex::new(sender));
239 let reporter = BootstrapReporter(sender);
240 let transport =
241 transport::DefaultTransport::new(runtime.clone(), config.cfg.outbound_proxy.clone());
242 cfg_if::cfg_if! {
243 if #[cfg(feature = "relay")] {
244 let builder = if let Some(auth_material) = &config.auth_material {
245 builder::ChanBuilder::new_relay(runtime.clone(), transport, auth_material.clone(), config.my_addrs, None)?
246 } else {
247 // Yes, clients can have the "relay" feature enabled (unit tests).
248 builder::ChanBuilder::new_client(runtime.clone(), transport)
249 };
250 } else {
251 let builder = builder::ChanBuilder::new_client(runtime.clone(), transport);
252 }
253 };
254
255 let factory = factory::CompoundFactory::new(
256 Arc::new(builder),
257 #[cfg(feature = "pt-client")]
258 None,
259 );
260
261 // Warn if outbound_proxy is configured to a non-loopback address
262 if let Some(ref proxy) = config.cfg.outbound_proxy {
263 if !proxy.is_loopback() {
264 tracing::warn!(
265 proxy_addr = %proxy,
266 "outbound_proxy is configured to a non-loopback address; \
267 this may expose Tor traffic to an untrusted intermediate"
268 );
269 }
270 }
271
272 let mgr =
273 mgr::AbstractChanMgr::new(factory, config.cfg, dormancy, netparams, reporter, memquota);
274
275 Ok(ChanMgr {
276 mgr,
277 bootstrap_status: receiver,
278 runtime,
279 })
280 }
281
282 /// Launch the periodic daemon tasks required by the manager to function properly.
283 ///
284 /// Returns a [`TaskHandle`] that can be used to manage
285 /// those daemon tasks that poll periodically.
286 #[instrument(level = "trace", skip_all)]
287 pub fn launch_background_tasks(
288 self: &Arc<Self>,
289 runtime: &R,
290 netdir: Arc<dyn NetDirProvider>,
291 ) -> Result<Vec<TaskHandle>> {
292 runtime
293 .spawn(Self::continually_update_channels_config(
294 Arc::downgrade(self),
295 netdir,
296 ))
297 .map_err(|e| Error::from_spawn("channels config task", e))?;
298
299 let (sched, handle) = TaskSchedule::new(runtime.clone());
300 runtime
301 .spawn(Self::continually_expire_channels(
302 sched,
303 Arc::downgrade(self),
304 ))
305 .map_err(|e| Error::from_spawn("channel expiration task", e))?;
306 Ok(vec![handle])
307 }
308
309 /// Build a channel for an incoming stream.
310 ///
311 /// The `my_addrs` are the IP address(es) that are advertised by the relay in the consensus. We
312 /// need to pass them so they can be sent in the NETINFO cell.
313 ///
314 /// The channel may or may not be authenticated. This method will wait until the channel is
315 /// usable, and may return an error if we already have an existing channel to this peer.
316 #[cfg(feature = "relay")]
317 pub async fn handle_incoming(
318 &self,
319 src: Sensitive<std::net::SocketAddr>,
320 stream: <R as tor_rtcompat::NetStreamProvider>::Stream,
321 ) -> Result<Arc<Channel>> {
322 let result = self.mgr.handle_incoming(src, stream).await;
323
324 #[cfg(feature = "metrics")]
325 self.mgr.metrics.increment_inbound_channels_built(&result);
326
327 result
328 }
329
330 /// Try to get a suitable channel to the provided `target`,
331 /// launching one if one does not exist.
332 ///
333 /// This function does not guarantee that the returned channel
334 /// satisfies all of the properties of `target`. For example if an
335 /// existing channel is returned, it might not be connected to any
336 /// of the addresses specified in `target`.
337 // ^ see https://gitlab.torproject.org/tpo/core/arti/-/issues/2344
338 ///
339 /// If there is already a channel launch attempt in progress, this
340 /// function will wait until that launch is complete, and succeed
341 /// or fail depending on its outcome.
342 #[instrument(level = "trace", skip_all)]
343 pub async fn get_or_launch<T: ChanTarget + ?Sized>(
344 &self,
345 target: &T,
346 usage: ChannelUsage,
347 ) -> Result<(Arc<Channel>, ChanProvenance)> {
348 let targetinfo = OwnedChanTarget::from_chan_target(target);
349
350 let (chan, provenance) = self.mgr.get_or_launch(targetinfo, usage).await?;
351 // Double-check the match to make sure that the RSA identity is
352 // what we wanted too.
353 chan.check_match(target)
354 .map_err(|e| Error::from_proto_no_skew(e, target))?;
355 Ok((chan, provenance))
356 }
357
358 /// Return a stream of [`ConnStatus`] events to tell us about changes
359 /// in our ability to connect to the internet.
360 ///
361 /// Note that this stream can be lossy: the caller will not necessarily
362 /// observe every event on the stream
363 pub fn bootstrap_events(&self) -> ConnStatusEvents {
364 self.bootstrap_status.clone()
365 }
366
367 /// Expire all channels that have been unused for too long.
368 ///
369 /// Return the duration from now until next channel expires.
370 pub fn expire_channels(&self) -> Duration {
371 self.mgr.expire_channels()
372 }
373
374 /// Notifies the chanmgr to be dormant like dormancy
375 pub fn set_dormancy(
376 &self,
377 dormancy: Dormancy,
378 netparams: Arc<dyn AsRef<NetParameters>>,
379 ) -> StdResult<(), tor_error::Bug> {
380 self.mgr.set_dormancy(dormancy, netparams)
381 }
382
383 /// Reconfigure all channels
384 pub fn reconfigure(
385 &self,
386 config: &ChannelConfig,
387 how: tor_config::Reconfigure,
388 netparams: Arc<dyn AsRef<NetParameters>>,
389 ) -> StdResult<(), ReconfigureError> {
390 if how == tor_config::Reconfigure::CheckAllOrNothing {
391 // Since `self.mgr.reconfigure` returns an error type of `Bug` and not
392 // `ReconfigureError` (see check below), the reconfigure should only fail due to bugs.
393 // This means we can return `Ok` here since there should never be an error with the
394 // provided `config` values.
395 return Ok(());
396 }
397
398 let r = self.mgr.reconfigure(config, netparams);
399
400 // Check that `self.mgr.reconfigure` returns an error type of `Bug` (see comment above).
401 let _: Option<&tor_error::Bug> = r.as_ref().err();
402
403 Ok(r?)
404 }
405
406 /// Replace the transport registry with one that may know about
407 /// more transports.
408 ///
409 /// Note that the [`ChannelFactory`](factory::ChannelFactory) instances returned by `ptmgr` are
410 /// required to time-out channels that take too long to build. You'll get
411 /// this behavior by default if the factories implement [`ChannelFactory`](factory::ChannelFactory) using
412 /// [`transport::proxied::ExternalProxyPlugin`], which `tor-ptmgr` does.
413 #[cfg(feature = "pt-client")]
414 pub fn set_pt_mgr(&self, ptmgr: Arc<dyn factory::AbstractPtMgr + 'static>) {
415 self.mgr.with_mut_builder(|f| f.replace_ptmgr(ptmgr));
416 }
417
418 /// Replace the relay auth material used for building new channels.
419 ///
420 /// This rebuilds the internal channel builder with the provided `auth_material`, which includes a
421 /// new TLS cert and key. Existing channels are not affected, only newly created channels will
422 /// use the new keys.
423 #[cfg(feature = "relay")]
424 pub fn set_relay_auth_material(
425 &self,
426 auth_material: Arc<tor_proto::RelayChannelAuthMaterial>,
427 ) -> Result<()> {
428 let mut result = Ok(());
429 self.mgr.with_mut_builder(|f| {
430 match f
431 .default_factory()
432 .rebuild_with_auth_material(auth_material)
433 {
434 Ok(b) => f.replace_default_factory(Arc::new(b)),
435 Err(e) => result = Err(e),
436 }
437 });
438 result
439 }
440
441 /// This will be used to handle CREATE* requests on channels.
442 ///
443 /// This handler will only be used for new channels, not existing channels.
444 ///
445 /// This will *not* be updated in any way by the channel manager,
446 /// for example by a netdir update or when any keys change.
447 /// The caller must handle this.
448 /// The idea is that the channel manager shouldn't need to deal with circuit-specific stuff.
449 ///
450 /// It's expected to only ever call this once.
451 /// Ideally it would be an `Option` in the constructor,
452 /// but we don't want conditionally-compiled constructor arguments,
453 /// and the [`CreateRequestHandler`] requires a [`dyn ChannelProvider`]
454 /// which is typically this [`ChanMgr`] itself.
455 #[cfg(feature = "relay")]
456 pub fn set_create_request_handler(&self, handler: Arc<CreateRequestHandler>) -> Result<()> {
457 let mut result = Ok(());
458 self.mgr.with_mut_builder(|f| {
459 match f
460 .default_factory()
461 .rebuild_with_create_request_handler(handler)
462 {
463 Ok(b) => f.replace_default_factory(Arc::new(b)),
464 Err(e) => result = Err(e),
465 }
466 });
467 result
468 }
469
470 /// Try to create a new, unmanaged channel to `target`.
471 ///
472 /// Unlike [`get_or_launch`](ChanMgr::get_or_launch), this function always
473 /// creates a new channel, never retries transient failure, and does not
474 /// register this channel with the `ChanMgr`.
475 ///
476 /// Generally you should not use this function; `get_or_launch` is usually a
477 /// better choice. This function is the right choice if, for whatever
478 /// reason, you need to manage the lifetime of the channel you create, and
479 /// make sure that no other code with access to this `ChanMgr` will be able
480 /// to use the channel.
481 #[cfg(feature = "experimental-api")]
482 #[instrument(level = "trace", skip_all)]
483 pub async fn build_unmanaged_channel(
484 &self,
485 target: impl tor_linkspec::IntoOwnedChanTarget,
486 memquota: ChannelAccount,
487 ) -> Result<Arc<Channel>> {
488 use factory::ChannelFactory as _;
489 let target = target.to_owned();
490
491 self.mgr
492 .channels
493 .builder()
494 .connect_via_transport(&target, self.mgr.reporter.clone(), memquota)
495 .await
496 }
497
498 /// Watch for things that ought to change the configuration of all channels in the client
499 ///
500 /// Currently this handles enabling and disabling channel padding.
501 ///
502 /// This is a daemon task that runs indefinitely in the background,
503 /// and exits when we find that `chanmgr` is dropped.
504 #[instrument(level = "trace", skip_all)]
505 async fn continually_update_channels_config(
506 self_: Weak<Self>,
507 netdir: Arc<dyn NetDirProvider>,
508 ) {
509 use tor_netdir::DirEvent as DE;
510 let mut netdir_stream = netdir.events().fuse();
511 let netdir = {
512 let weak = Arc::downgrade(&netdir);
513 drop(netdir);
514 weak
515 };
516 let termination_reason: std::result::Result<Void, &str> = async move {
517 loop {
518 select_biased! {
519 direvent = netdir_stream.next() => {
520 let direvent = direvent.ok_or("EOF on netdir provider event stream")?;
521 if ! matches!(direvent, DE::NewConsensus) { continue };
522 let self_ = self_.upgrade().ok_or("channel manager gone away")?;
523 let netdir = netdir.upgrade().ok_or("netdir gone away")?;
524 let netparams = netdir.params();
525 self_.mgr.update_netparams(netparams).map_err(|e| {
526 error_report!(e, "continually_update_channels_config: failed to process!");
527 "error processing netdir"
528 })?;
529 }
530 }
531 }
532 }
533 .await;
534 debug!(
535 "continually_update_channels_config: shutting down: {}",
536 termination_reason.void_unwrap_err()
537 );
538 }
539
540 /// Periodically expire any channels that have been unused beyond
541 /// the maximum duration allowed.
542 ///
543 /// Exist when we find that `chanmgr` is dropped
544 ///
545 /// This is a daemon task that runs indefinitely in the background
546 #[instrument(level = "trace", skip_all)]
547 async fn continually_expire_channels(mut sched: TaskSchedule<R>, chanmgr: Weak<Self>) {
548 while sched.next().await.is_some() {
549 let Some(cm) = Weak::upgrade(&chanmgr) else {
550 // channel manager is closed.
551 return;
552 };
553 let delay = cm.expire_channels();
554 // This will sometimes be an underestimate, but it's no big deal; we just sleep some more.
555 sched.fire_in(delay);
556 }
557 }
558}
559
560#[cfg(feature = "relay")]
561#[async_trait]
562impl<R: Runtime> ChannelProvider for ChanMgr<R> {
563 type BuildSpec = OwnedChanTarget;
564
565 fn get_or_launch(
566 self: Arc<Self>,
567 reactor_id: tor_proto::circuit::UniqId,
568 target: Self::BuildSpec,
569 tx: tor_proto::relay::channel_provider::OutboundChanSender,
570 ) -> tor_proto::Result<()> {
571 use tor_error::into_internal;
572
573 debug!("Get or launch channel to {target} for circuit reactor {reactor_id}");
574
575 let chanmgr = self.clone();
576 self.runtime
577 .spawn(async move {
578 let r = chanmgr
579 .mgr
580 .get_or_launch(target, ChannelUsage::UserTraffic)
581 .await
582 .map_err(|e| tor_proto::Error::ChanProto(e.to_string())); // Is it a ChanProto?
583 // Send back the channel.
584 tx.send(r.map(|(chan, _)| chan));
585 })
586 .map_err(into_internal!("Failed to launch channel provider task"))?;
587
588 Ok(())
589 }
590}