1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
//! Declarations for traits that we need our runtimes to implement.
use async_trait::async_trait;
use futures::stream;
use futures::task::Spawn;
use futures::{AsyncRead, AsyncWrite, Future};
use std::io::Result as IoResult;
use std::net::SocketAddr;
use std::time::{Duration, Instant, SystemTime};
/// A runtime that we can use to run Tor as a client.
///
/// This trait comprises several other traits that we require all of our
/// runtimes to provide:
///
/// * [`futures::task::Spawn`] to launch new background tasks.
/// * [`SleepProvider`] to pause a task for a given amount of time.
/// * [`TcpProvider`] to launch and accept TCP connections.
/// * [`TlsProvider`] to launch TLS connections.
/// * [`SpawnBlocking`] to block on a future and run it to completion
/// (This may become optional in the future, if/when we add WASM
/// support).
///
/// We require that every `Runtime` has an efficient [`Clone`] implementation
/// that gives a new opaque reference to the same underlying runtime.
///
/// Additionally, every `Runtime` is [`Send`] and [`Sync`], though these
/// requirements may be somewhat relaxed in the future.
pub trait Runtime:
Sync + Send + Spawn + SpawnBlocking + Clone + SleepProvider + TcpProvider + TlsProvider + 'static
{
}
impl<T> Runtime for T where
T: Sync
+ Send
+ Spawn
+ SpawnBlocking
+ Clone
+ SleepProvider
+ TcpProvider
+ TlsProvider
+ 'static
{
}
/// Trait for a runtime that can wait until a timer has expired.
///
/// Every `SleepProvider` also implements
/// [`SleepProviderExt`](crate::SleepProviderExt); see that trait
/// for other useful functions.
pub trait SleepProvider {
/// A future returned by [`SleepProvider::sleep()`]
type SleepFuture: Future<Output = ()> + Send + 'static;
/// Return a future that will be ready after `duration` has
/// elapsed.
#[must_use = "sleep() returns a future, which does nothing unless used"]
fn sleep(&self, duration: Duration) -> Self::SleepFuture;
/// Return the SleepProvider's view of the current instant.
///
/// (This is the same as `Instant::now`, if not running in test mode.)
fn now(&self) -> Instant {
Instant::now()
}
/// Return the SleepProvider's view of the current wall-clock time.
///
/// (This is the same as `SystemTime::now`, if not running in test mode.)
fn wallclock(&self) -> SystemTime {
SystemTime::now()
}
/// Signify that a test running under mock time shouldn't advance time yet, with a given
/// unique reason string. This is useful for making sure (mock) time doesn't advance while
/// things that might require some (real-world) time to complete do so, such as spawning a task
/// on another thread.
///
/// Call `release_advance` with the same reason string in order to unblock.
///
/// This method is only for testing: it should never have any
/// effect when invoked on non-testing runtimes.
fn block_advance<T: Into<String>>(&self, _reason: T) {}
/// Signify that the reason to withhold time advancing provided in a call to `block_advance` no
/// longer exists, and it's fine to move time forward if nothing else is blocking advances.
///
/// This method is only for testing: it should never have any
/// effect when invoked on non-testing runtimes.
fn release_advance<T: Into<String>>(&self, _reason: T) {}
/// Allow a test running under mock time to advance time by the provided duration, even if the
/// above `block_advance` API has been used.
///
/// This method is only for testing: it should never have any
/// effect when invoked on non-testing runtimes.
fn allow_one_advance(&self, _dur: Duration) {}
}
/// Trait for a runtime that can block on a future.
pub trait SpawnBlocking {
/// Run `future` until it is ready, and return its output.
fn block_on<F: Future>(&self, future: F) -> F::Output;
}
/// Trait for a runtime that can create and accept TCP connections.
///
/// (In Arti we use the [`AsyncRead`] and [`AsyncWrite`] traits from
/// [`futures::io`] as more standard, even though the ones from Tokio
/// can be a bit more efficient. Let's hope that they converge in the
/// future.)
// TODO: Use of async_trait is not ideal, since we have to box with every
// call. Still, async_io basically makes that necessary :/
#[async_trait]
pub trait TcpProvider {
/// The type for the TCP connections returned by [`Self::connect()`].
type TcpStream: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static;
/// The type for the TCP listeners returned by [`Self::listen()`].
type TcpListener: TcpListener<TcpStream = Self::TcpStream> + Send + Sync + Unpin + 'static;
/// Launch a TCP connection to a given socket address.
///
/// Note that unlike `std::net:TcpStream::connect`, we do not accept
/// any types other than a single [`SocketAddr`]. We do this because,
/// as a Tor implementation, we most be absolutely sure not to perform
/// unnecessary DNS lookups.
async fn connect(&self, addr: &SocketAddr) -> IoResult<Self::TcpStream>;
/// Open a TCP listener on a given socket address.
async fn listen(&self, addr: &SocketAddr) -> IoResult<Self::TcpListener>;
}
/// Trait for a local socket that accepts incoming TCP streams.
///
/// These objects are returned by instances of [`TcpProvider`]. To use
/// one, either call `accept` to accept a single connection, or
/// use `incoming` to wrap this object as a [`stream::Stream`].
// TODO: Use of async_trait is not ideal here either.
#[async_trait]
pub trait TcpListener {
/// The type of TCP connections returned by [`Self::accept()`].
type TcpStream: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static;
/// The type of [`stream::Stream`] returned by [`Self::incoming()`].
type Incoming: stream::Stream<Item = IoResult<(Self::TcpStream, SocketAddr)>> + Unpin;
/// Wait for an incoming stream; return it along with its address.
async fn accept(&self) -> IoResult<(Self::TcpStream, SocketAddr)>;
/// Wrap this listener into a new [`stream::Stream`] that yields
/// TCP streams and addresses.
fn incoming(self) -> Self::Incoming;
/// Return the local address that this listener is bound to.
fn local_addr(&self) -> IoResult<SocketAddr>;
}
/// An object with a peer certificate: typically a TLS connection.
pub trait CertifiedConn {
/// Try to return the (DER-encoded) peer certificate for this
/// connection, if any.
fn peer_certificate(&self) -> IoResult<Option<Vec<u8>>>;
}
/// An object that knows how to make a TLS-over-TCP connection we
/// can use in Tor.
///
/// (Note that because of Tor's peculiarities, this is not a
/// general-purpose TLS type. Unlike typical users, Tor does not want
/// its TLS library to check whether the certificates are signed
/// within the web PKI hierarchy, or what their hostnames are.
#[async_trait]
pub trait TlsConnector {
/// The type of connection returned by this connector
type Conn: AsyncRead + AsyncWrite + CertifiedConn + Unpin + Send + 'static;
/// Launch a TLS-over-TCP connection to a given address.
///
/// Declare `sni_hostname` as the desired hostname, but don't
/// actually check whether the hostname in the certificate matches
/// it.
async fn connect_unvalidated(
&self,
addr: &SocketAddr,
sni_hostname: &str,
) -> IoResult<Self::Conn>;
}
/// Trait for a runtime that knows how to create TLS connections.
///
/// This is separate from [`TlsConnector`] because eventually we may
/// eventually want to support multiple `TlsConnector` implementations
/// that use a single [`Runtime`].
pub trait TlsProvider {
/// The Connector object that this provider can return.
type Connector: TlsConnector<Conn = Self::TlsStream> + Send + Sync + Unpin;
/// The type of the stream returned by that connector.
type TlsStream: AsyncRead + AsyncWrite + CertifiedConn + Unpin + Send + 'static;
/// Return a TLS connector for use with this runtime.
fn tls_connector(&self) -> Self::Connector;
}