tor_rtcompat/
traits.rs

1//! Declarations for traits that we need our runtimes to implement.
2use async_trait::async_trait;
3use asynchronous_codec::Framed;
4use futures::stream;
5use futures::task::Spawn;
6use futures::{AsyncRead, AsyncWrite, Future};
7use std::fmt::Debug;
8use std::io::{self, Result as IoResult};
9use std::net;
10use std::time::{Duration, Instant, SystemTime};
11use tor_general_addr::unix;
12
13/// A runtime that we can use to run Tor as a client.
14///
15/// This trait comprises several other traits that we require all of our
16/// runtimes to provide:
17///
18/// * [`futures::task::Spawn`] to launch new background tasks.
19/// * [`SleepProvider`] to pause a task for a given amount of time.
20/// * [`CoarseTimeProvider`] for a cheaper but less accurate notion of time.
21/// * [`NetStreamProvider`] to launch and accept network connections.
22/// * [`TlsProvider`] to launch TLS connections.
23/// * [`BlockOn`] to block on a future and run it to completion
24///   (This may become optional in the future, if/when we add WASM
25///   support).
26///
27/// We require that every `Runtime` has an efficient [`Clone`] implementation
28/// that gives a new opaque reference to the same underlying runtime.
29///
30/// Additionally, every `Runtime` is [`Send`] and [`Sync`], though these
31/// requirements may be somewhat relaxed in the future.
32///
33/// At some future point,
34/// Arti may require that the runtime `impl<S> TlsProvider<S>` (for suitable`S`),
35/// rather than just for their own `TcpStream`s.
36/// I.e., Arti may start to require that the runtime's TLS provider can wrap any streams,
37/// not only the runtime's own TCP streams.
38/// This might be expressed as an additional supertrait bound on `Runtime`,
39/// eg when Rust supports GATs,
40/// or as an additional bound on the Arti APIs that currently use `Runtime`.
41/// For API future compatibility, if you `impl Runtime for MyRuntime`,
42/// you should also ensure that you
43/// ```ignore
44/// impl<S> TlsProvider<S> for MyRuntime
45/// where S: futures::AsyncRead + futures::AsyncWrite + Unpin + Send + 'static
46/// ```
47//
48/// Perhaps we will need this if we make our own TLS connections *through* Tor,
49/// rather than just channels to guards.
50pub trait Runtime:
51    Sync
52    + Send
53    + Spawn
54    + SpawnBlocking
55    + BlockOn
56    + Clone
57    + SleepProvider
58    + CoarseTimeProvider
59    + NetStreamProvider<net::SocketAddr>
60    + NetStreamProvider<unix::SocketAddr>
61    + TlsProvider<<Self as NetStreamProvider<net::SocketAddr>>::Stream>
62    + UdpProvider
63    + Debug
64    + 'static
65{
66}
67
68impl<T> Runtime for T where
69    T: Sync
70        + Send
71        + Spawn
72        + SpawnBlocking
73        + BlockOn
74        + Clone
75        + SleepProvider
76        + CoarseTimeProvider
77        + NetStreamProvider<net::SocketAddr>
78        + NetStreamProvider<unix::SocketAddr>
79        + TlsProvider<<Self as NetStreamProvider<net::SocketAddr>>::Stream>
80        + UdpProvider
81        + Debug
82        + 'static
83{
84}
85
86/// Trait for a runtime that can wait until a timer has expired.
87///
88/// Every `SleepProvider` also implements
89/// [`SleepProviderExt`](crate::SleepProviderExt); see that trait
90/// for other useful functions.
91pub trait SleepProvider: Clone + Send + Sync + 'static {
92    /// A future returned by [`SleepProvider::sleep()`]
93    type SleepFuture: Future<Output = ()> + Send + 'static;
94    /// Return a future that will be ready after `duration` has
95    /// elapsed.
96    #[must_use = "sleep() returns a future, which does nothing unless used"]
97    fn sleep(&self, duration: Duration) -> Self::SleepFuture;
98
99    /// Return the SleepProvider's view of the current instant.
100    ///
101    /// (This is the same as `Instant::now`, if not running in test mode.)
102    fn now(&self) -> Instant {
103        Instant::now()
104    }
105
106    /// Return the SleepProvider's view of the current wall-clock time.
107    ///
108    /// (This is the same as `SystemTime::now`, if not running in test mode.)
109    fn wallclock(&self) -> SystemTime {
110        SystemTime::now()
111    }
112
113    /// Signify that a test running under mock time shouldn't advance time yet, with a given
114    /// unique reason string. This is useful for making sure (mock) time doesn't advance while
115    /// things that might require some (real-world) time to complete do so, such as spawning a task
116    /// on another thread.
117    ///
118    /// Call `release_advance` with the same reason string in order to unblock.
119    ///
120    /// This method is only for testing: it should never have any
121    /// effect when invoked on non-testing runtimes.
122    fn block_advance<T: Into<String>>(&self, _reason: T) {}
123
124    /// Signify that the reason to withhold time advancing provided in a call to `block_advance` no
125    /// longer exists, and it's fine to move time forward if nothing else is blocking advances.
126    ///
127    /// This method is only for testing: it should never have any
128    /// effect when invoked on non-testing runtimes.
129    fn release_advance<T: Into<String>>(&self, _reason: T) {}
130
131    /// Allow a test running under mock time to advance time by the provided duration, even if the
132    /// above `block_advance` API has been used.
133    ///
134    /// This method is only for testing: it should never have any
135    /// effect when invoked on non-testing runtimes.
136    fn allow_one_advance(&self, _dur: Duration) {}
137}
138
139/// A provider of reduced-precision timestamps
140///
141/// This doesn't provide any facility for sleeping.
142/// If you want to sleep based on reduced-precision timestamps,
143/// convert the desired sleep duration to `std::time::Duration`
144/// and use [`SleepProvider`].
145pub trait CoarseTimeProvider: Clone + Send + Sync + 'static {
146    /// Return the `CoarseTimeProvider`'s view of the current instant.
147    ///
148    /// This is supposed to be cheaper than `std::time::Instant::now`.
149    fn now_coarse(&self) -> crate::coarse_time::CoarseInstant;
150}
151
152/// Trait for a runtime that can block on a future.
153pub trait BlockOn: Clone + Send + Sync + 'static {
154    /// Run `future` until it is ready, and return its output.
155    fn block_on<F: Future>(&self, future: F) -> F::Output;
156}
157
158/// Trait to run a task on a threadpool for blocking tasks
159pub trait SpawnBlocking: Clone + Send + Sync + 'static {
160    /// The type of handle used to await the result of the task.
161    type Handle<T: Send + 'static>: Future<Output = T>;
162
163    /// Spawn a task on a threadpool specifically for blocking tasks.
164    ///
165    /// Note that this is not the best long-term solution for CPU bound tasks, and is better for
166    /// IO-bound tasks. However, until we complete #1784, this is probably a somewhat reasonable
167    /// place to put CPU-bound tasks.
168    ///
169    /// See the docs for the underlying implementations in [tokio][tokio-threadpool] and
170    /// [async-std][async-std-threadpool].
171    ///
172    /// [tokio-threadpool]: https://docs.rs/tokio/latest/tokio/task/fn.spawn_blocking.html
173    /// [async-std-threadpool]: https://docs.rs/async-std/latest/async_std/task/fn.spawn_blocking.html
174    fn spawn_blocking<F, T>(&self, f: F) -> Self::Handle<T>
175    where
176        F: FnOnce() -> T + Send + 'static,
177        T: Send + 'static;
178}
179
180/// Trait providing additional operations on network sockets.
181pub trait StreamOps {
182    /// Set the [`TCP_NOTSENT_LOWAT`] socket option, if this `Stream` is a TCP stream.
183    ///
184    /// Implementations should return an [`UnsupportedStreamOp`] IO error
185    /// if the stream is not a TCP stream,
186    /// and on platforms where the operation is not supported.
187    ///
188    /// [`TCP_NOTSENT_LOWAT`]: https://lwn.net/Articles/560082/
189    fn set_tcp_notsent_lowat(&self, _notsent_lowat: u32) -> IoResult<()> {
190        Err(UnsupportedStreamOp {
191            op: "set_tcp_notsent_lowat",
192            reason: "unsupported object type",
193        }
194        .into())
195    }
196
197    /// Return a new handle that implements [`StreamOps`],
198    /// and that can be used independently of `self`.
199    fn new_handle(&self) -> Box<dyn StreamOps + Send + Unpin> {
200        Box::new(NoOpStreamOpsHandle)
201    }
202}
203
204/// A [`StreamOps`] handle that always returns an error.
205///
206/// Returned from [`StreamOps::new_handle`] for types and platforms
207/// that do not support `StreamOps`.
208#[derive(Copy, Clone, Debug, Default)]
209#[non_exhaustive]
210pub struct NoOpStreamOpsHandle;
211
212impl StreamOps for NoOpStreamOpsHandle {
213    fn new_handle(&self) -> Box<dyn StreamOps + Send + Unpin> {
214        Box::new(*self)
215    }
216}
217
218impl<T: StreamOps, C> StreamOps for Framed<T, C> {
219    fn set_tcp_notsent_lowat(&self, notsent_lowat: u32) -> IoResult<()> {
220        let inner: &T = self;
221        inner.set_tcp_notsent_lowat(notsent_lowat)
222    }
223
224    fn new_handle(&self) -> Box<dyn StreamOps + Send + Unpin> {
225        let inner: &T = self;
226        inner.new_handle()
227    }
228}
229
230/// Error: Tried to perform a [`StreamOps`] operation on an unsupported stream type
231/// or on an unsupported platform.
232///
233/// (For example, you can't call [`StreamOps::set_tcp_notsent_lowat`] on Windows
234/// or on a stream type that is not backed by a TCP socket.)
235#[derive(Clone, Debug, thiserror::Error)]
236#[error("Operation {op} not supported: {reason}")]
237pub struct UnsupportedStreamOp {
238    /// The unsupported operation.
239    op: &'static str,
240    /// The reason the operation is unsupported.
241    reason: &'static str,
242}
243
244impl UnsupportedStreamOp {
245    /// Construct a new `UnsupportedStreamOp` error with the provided operation and reason.
246    pub fn new(op: &'static str, reason: &'static str) -> Self {
247        Self { op, reason }
248    }
249}
250
251impl From<UnsupportedStreamOp> for io::Error {
252    fn from(value: UnsupportedStreamOp) -> Self {
253        io::Error::new(io::ErrorKind::Unsupported, value)
254    }
255}
256
257/// Trait for a runtime that can create and accept connections
258/// over network sockets.
259///
260/// (In Arti we use the [`AsyncRead`] and [`AsyncWrite`] traits from
261/// [`futures::io`] as more standard, even though the ones from Tokio
262/// can be a bit more efficient.  Let's hope that they converge in the
263/// future.)
264// TODO: Use of async_trait is not ideal, since we have to box with every
265// call.  Still, async_io basically makes that necessary :/
266#[async_trait]
267pub trait NetStreamProvider<ADDR = net::SocketAddr>: Clone + Send + Sync + 'static {
268    /// The type for the connections returned by [`Self::connect()`].
269    type Stream: AsyncRead + AsyncWrite + StreamOps + Send + Sync + Unpin + 'static;
270    /// The type for the listeners returned by [`Self::listen()`].
271    type Listener: NetStreamListener<ADDR, Stream = Self::Stream> + Send + Sync + Unpin + 'static;
272
273    /// Launch a connection connection to a given socket address.
274    ///
275    /// Note that unlike `std::net:TcpStream::connect`, we do not accept
276    /// any types other than a single `ADDR`.  We do this because
277    /// we must be absolutely sure not to perform
278    /// unnecessary DNS lookups.
279    async fn connect(&self, addr: &ADDR) -> IoResult<Self::Stream>;
280
281    /// Open a listener on a given socket address.
282    async fn listen(&self, addr: &ADDR) -> IoResult<Self::Listener>;
283}
284
285/// Trait for a local socket that accepts incoming streams.
286///
287/// These objects are returned by instances of [`NetStreamProvider`].  To use
288/// one,
289/// use `incoming` to convert this object into a [`stream::Stream`].
290pub trait NetStreamListener<ADDR = net::SocketAddr> {
291    /// The type of connections returned by [`Self::incoming()`].
292    type Stream: AsyncRead + AsyncWrite + StreamOps + Send + Sync + Unpin + 'static;
293
294    /// The type of [`stream::Stream`] returned by [`Self::incoming()`].
295    type Incoming: stream::Stream<Item = IoResult<(Self::Stream, ADDR)>>
296        + Send
297        + Sync
298        + Unpin
299        + 'static;
300
301    /// Wrap this listener into a new [`stream::Stream`] that yields
302    /// streams and addresses.
303    fn incoming(self) -> Self::Incoming;
304
305    /// Return the local address that this listener is bound to.
306    fn local_addr(&self) -> IoResult<ADDR>;
307}
308
309/// Trait for a runtime that can send and receive UDP datagrams.
310#[async_trait]
311pub trait UdpProvider: Clone + Send + Sync + 'static {
312    /// The type of Udp Socket returned by [`Self::bind()`]
313    type UdpSocket: UdpSocket + Send + Sync + Unpin + 'static;
314
315    /// Bind a local port to send and receive packets from
316    async fn bind(&self, addr: &net::SocketAddr) -> IoResult<Self::UdpSocket>;
317}
318
319/// Trait for a locally bound Udp socket that can send and receive datagrams.
320///
321/// These objects are returned by instances of [`UdpProvider`].
322//
323// NOTE that UdpSocket objects are _necessarily_ un-connected.  If you need to
324// implement a connected Udp socket in the future, please make a new trait (and
325// a new type.)
326#[async_trait]
327pub trait UdpSocket {
328    /// Wait for an incoming datagram; return it along its address.
329    async fn recv(&self, buf: &mut [u8]) -> IoResult<(usize, net::SocketAddr)>;
330    /// Send a datagram to the provided address.
331    async fn send(&self, buf: &[u8], target: &net::SocketAddr) -> IoResult<usize>;
332    /// Return the local address that this socket is bound to.
333    fn local_addr(&self) -> IoResult<net::SocketAddr>;
334}
335
336/// An object with a peer certificate: typically a TLS connection.
337pub trait CertifiedConn {
338    /// Return the keying material (RFC 5705) given a label and an optional context.
339    fn export_keying_material(
340        &self,
341        len: usize,
342        label: &[u8],
343        context: Option<&[u8]>,
344    ) -> IoResult<Vec<u8>>;
345    /// Try to return the (DER-encoded) peer certificate for this
346    /// connection, if any.
347    fn peer_certificate(&self) -> IoResult<Option<Vec<u8>>>;
348}
349
350/// An object that knows how to wrap a TCP connection (where the type of said TCP
351/// connection is `S`) with TLS.
352///
353/// # Usage notes
354///
355/// Note that because of Tor's peculiarities, this is not a
356/// general-purpose TLS type.  Unlike typical users, Tor does not want
357/// its TLS library to check whether the certificates used in TLS are signed
358/// within the web PKI hierarchy, or what their hostnames are, or even whether
359/// they are valid.  It *does*, however, check that the subject public key in the
360/// certificate is indeed correctly used to authenticate the TLS handshake.
361///
362/// If you are implementing something other than Tor, this is **not** the
363/// functionality you want.
364///
365/// How can this behavior be remotely safe, even in Tor?  It only works for Tor
366/// because the certificate that a Tor relay uses in TLS is not actually being
367/// used to certify that relay's public key.  Instead, the certificate only used
368/// as a container for the relay's public key.  The real certification happens
369/// later, inside the TLS session, when the relay presents a CERTS cell.
370///
371/// Such sneakiness was especially necessary before TLS 1.3, which encrypts more
372/// of the handshake, and before pluggable transports, which make
373/// "innocuous-looking TLS handshakes" less important than they once were.  Once
374/// TLS 1.3 is completely ubiquitous, we might be able to specify a simpler link
375/// handshake than Tor uses now.
376#[async_trait]
377pub trait TlsConnector<S> {
378    /// The type of connection returned by this connector
379    type Conn: AsyncRead + AsyncWrite + CertifiedConn + Unpin + Send + 'static;
380
381    /// Start a TLS session over the provided TCP stream `stream`.
382    ///
383    /// Declare `sni_hostname` as the desired hostname, but don't actually check
384    /// whether the hostname in the certificate matches it.  The connector may
385    /// send `sni_hostname` as part of its handshake, if it supports
386    /// [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication) or one of
387    /// the TLS 1.3 equivalents.
388    async fn negotiate_unvalidated(&self, stream: S, sni_hostname: &str) -> IoResult<Self::Conn>;
389}
390
391/// Trait for a runtime that knows how to create TLS connections over
392/// TCP streams of type `S`.
393///
394/// This is separate from [`TlsConnector`] because eventually we may
395/// eventually want to support multiple `TlsConnector` implementations
396/// that use a single [`Runtime`].
397///
398/// See the [`TlsConnector`] documentation for a discussion of the Tor-specific
399/// limitations of this trait: If you are implementing something other than Tor,
400/// this is **not** the functionality you want.
401pub trait TlsProvider<S: StreamOps>: Clone + Send + Sync + 'static {
402    /// The Connector object that this provider can return.
403    type Connector: TlsConnector<S, Conn = Self::TlsStream> + Send + Sync + Unpin;
404
405    /// The type of the stream returned by that connector.
406    type TlsStream: AsyncRead + AsyncWrite + StreamOps + CertifiedConn + Unpin + Send + 'static;
407
408    /// Return a TLS connector for use with this runtime.
409    fn tls_connector(&self) -> Self::Connector;
410
411    /// Return true iff the keying material exporters (RFC 5705) is supported.
412    fn supports_keying_material_export(&self) -> bool;
413}