//! Traits and code to define different mechanisms for building Channels to //! different kinds of targets. use std::sync::{Arc, Mutex}; use crate::event::ChanMgrEventSender; use async_trait::async_trait; use tor_error::{HasKind, HasRetryTime, internal}; use tor_linkspec::{HasChanMethod, OwnedChanTarget, PtTransportName}; use tor_proto::channel::Channel; use tor_proto::memquota::ChannelAccount; use tracing::{debug, instrument}; /// An opaque type that lets a `ChannelFactory` update the `ChanMgr` about bootstrap progress. /// /// A future release of this crate might make this type less opaque. // FIXME(eta): Do that. #[derive(Clone)] pub struct BootstrapReporter(pub(crate) Arc>); impl BootstrapReporter { #[cfg(test)] /// Create a useless version of this type to satisfy some test. pub(crate) fn fake() -> Self { let (snd, _rcv) = crate::event::channel(); Self(Arc::new(Mutex::new(snd))) } } /// An object that knows how to build `Channels` to `ChanTarget`s. /// /// This trait must be object-safe. /// /// Every [`ChanMgr`](crate::ChanMgr) has a `ChannelFactory` that it uses to /// construct all of its channels. /// /// A `ChannelFactory` can be implemented in terms of a /// [`TransportImplHelper`](crate::transport::TransportImplHelper), by wrapping it in a /// `ChanBuilder`. /// // FIXME(eta): Rectify the below situation. /// (In fact, as of the time of writing, this is the *only* way to implement this trait /// outside of this crate while keeping bootstrap status reporting, since `BootstrapReporter` /// is an opaque type.) #[async_trait] pub trait ChannelFactory: Send + Sync { /// Open an authenticated channel to `target`. /// /// This method does does not necessarily handle retries or timeouts, /// although some of its implementations may. /// /// This method does not necessarily handle every kind of transport. If the /// caller provides a target with an unsupported /// [`TransportId`](tor_linkspec::TransportId), this method should return /// [`Error::NoSuchTransport`](crate::Error::NoSuchTransport). async fn connect_via_transport( &self, target: &OwnedChanTarget, reporter: BootstrapReporter, memquota: ChannelAccount, ) -> crate::Result>; } /// Similar to [`ChannelFactory`], but for building channels from incoming streams. // This is a separate trait since for some `ChannelFactory`s like the one returned from // `tor_ptmgr::PtMgr::factory_for_transport`, it doesn't make sense to deal with incoming streams // (all PT connections are outgoing). #[async_trait] pub trait IncomingChannelFactory: Send + Sync { /// The type of byte stream that's required to build channels for incoming connections. type Stream: Send + Sync + 'static; /// Open a channel from `peer` with the given `stream`. The channel may or may not be /// authenticated. #[cfg(feature = "relay")] async fn accept_from_transport( &self, peer: std::net::SocketAddr, stream: Self::Stream, memquota: ChannelAccount, ) -> crate::Result>; } #[async_trait] impl crate::mgr::AbstractChannelFactory for CF where CF: ChannelFactory + IncomingChannelFactory + Sync, { type Channel = tor_proto::channel::Channel; type BuildSpec = OwnedChanTarget; type Stream = CF::Stream; #[instrument(skip_all, level = "trace")] async fn build_channel( &self, target: &Self::BuildSpec, reporter: BootstrapReporter, memquota: ChannelAccount, ) -> crate::Result> { debug!("Attempting to open a new channel to {target}"); self.connect_via_transport(target, reporter, memquota).await } #[cfg(feature = "relay")] #[instrument(skip_all, level = "trace")] async fn build_channel_using_incoming( &self, peer: std::net::SocketAddr, stream: Self::Stream, memquota: ChannelAccount, ) -> crate::Result> { debug!("Attempting to open a new channel from {peer}"); self.accept_from_transport(peer, stream, memquota).await } } /// The error type returned by a pluggable transport manager. pub trait AbstractPtError: std::error::Error + HasKind + HasRetryTime + Send + Sync + std::fmt::Debug { } /// A pluggable transport manager. /// /// We can't directly reference the `PtMgr` type from `tor-ptmgr`, because of dependency resolution /// constraints, so this defines the interface for what one should look like. #[async_trait] pub trait AbstractPtMgr: Send + Sync { /// Get a `ChannelFactory` for the provided `PtTransportName`. async fn factory_for_transport( &self, transport: &PtTransportName, ) -> Result>, Arc>; } #[async_trait] impl

AbstractPtMgr for Option

where P: AbstractPtMgr, { async fn factory_for_transport( &self, transport: &PtTransportName, ) -> Result>, Arc> { match self { Some(mgr) => mgr.factory_for_transport(transport).await, None => Ok(None), } } } /// A ChannelFactory built from an optional PtMgr to use for pluggable transports, and a /// ChannelFactory to use for everything else. pub(crate) struct CompoundFactory { #[cfg(feature = "pt-client")] /// The PtMgr to use for pluggable transports ptmgr: Option>, /// The factory to use for everything else default_factory: Arc, } impl Clone for CompoundFactory { fn clone(&self) -> Self { Self { #[cfg(feature = "pt-client")] ptmgr: self.ptmgr.as_ref().map(Arc::clone), default_factory: Arc::clone(&self.default_factory), } } } #[async_trait] impl ChannelFactory for CompoundFactory { #[instrument(skip_all, level = "trace")] async fn connect_via_transport( &self, target: &OwnedChanTarget, reporter: BootstrapReporter, memquota: ChannelAccount, ) -> crate::Result> { use tor_linkspec::ChannelMethod::*; let factory = match target.chan_method() { Direct(_) => self.default_factory.clone(), #[cfg(feature = "pt-client")] Pluggable(a) => match self.ptmgr.as_ref() { Some(mgr) => mgr .factory_for_transport(a.transport()) .await .map_err(crate::Error::Pt)? .ok_or_else(|| crate::Error::NoSuchTransport(a.transport().clone().into()))?, None => return Err(crate::Error::NoSuchTransport(a.transport().clone().into())), }, #[allow(unreachable_patterns)] _ => { return Err(crate::Error::Internal(internal!( "No support for channel method" ))); } }; factory .connect_via_transport(target, reporter, memquota) .await } } #[async_trait] impl IncomingChannelFactory for CompoundFactory { type Stream = CF::Stream; #[cfg(feature = "relay")] async fn accept_from_transport( &self, peer: std::net::SocketAddr, stream: Self::Stream, memquota: ChannelAccount, ) -> crate::Result> { self.default_factory .accept_from_transport(peer, stream, memquota) .await } } impl CompoundFactory { /// Create a new `Factory` that will try to use `ptmgr` to handle pluggable /// transports requests, and `default_factory` to handle everything else. pub(crate) fn new( default_factory: Arc, #[cfg(feature = "pt-client")] ptmgr: Option>, ) -> Self { Self { default_factory, #[cfg(feature = "pt-client")] ptmgr, } } #[cfg(feature = "pt-client")] /// Replace the PtMgr in this object. pub(crate) fn replace_ptmgr(&mut self, ptmgr: Arc) { self.ptmgr = Some(ptmgr); } }