Skip to main content

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}