Skip to main content

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::future::{FutureExt, RemoteHandle};
5use futures::stream;
6use futures::task::{Spawn, SpawnError};
7use futures::{AsyncRead, AsyncWrite, Future};
8use std::fmt::Debug;
9use std::io::{self, Result as IoResult};
10use std::net;
11use std::time::{Duration, Instant, SystemTime};
12use tor_general_addr::unix;
13
14#[cfg(feature = "tls-server")]
15use tor_cert_x509::TlsKeyAndCert;
16
17/// A runtime for use by Tor client library code.
18///
19/// This trait comprises several other traits that we require all of our
20/// runtimes to provide:
21///
22/// * [`futures::task::Spawn`] or [`SpawnExt`] to launch new background tasks.
23/// * [`SleepProvider`] to pause a task for a given amount of time.
24/// * [`CoarseTimeProvider`] for a cheaper but less accurate notion of time.
25/// * [`NetStreamProvider`] to launch and accept network connections.
26/// * [`TlsProvider`] to launch TLS connections.
27/// * [`Blocking`] to be able to run synchronous (cpubound or IO) code,
28///   and *re*-enter the async context from synchronous thread
29///   (This may become optional in the future, if/when we add WASM
30///   support).
31///
32/// A value which is only `Runtime` cannot be used as an *entry point* to the runtime.
33/// For that, it must also implement [`ToplevelBlockOn`],
34/// making it a [`ToplevelRuntime`].
35/// Since you can only [enter a runtime](ToplevelBlockOn::block_on) once,
36/// typically you use a `ToplevelRuntime` to enter the runtime,
37/// and use it as only a `Runtime` afterwards.
38/// This means that library code should typically
39/// deal with `Runtime` rather than `ToplevelRuntime`.
40///
41/// We require that every `Runtime` has an efficient [`Clone`] implementation
42/// that gives a new opaque reference to the same underlying runtime.
43///
44/// Additionally, every `Runtime` is [`Send`] and [`Sync`], though these
45/// requirements may be somewhat relaxed in the future.
46///
47/// At some future point,
48/// Arti may require that the runtime `impl<S> TlsProvider<S>` (for suitable`S`),
49/// rather than just for their own `TcpStream`s.
50/// I.e., Arti may start to require that the runtime's TLS provider can wrap any streams,
51/// not only the runtime's own TCP streams.
52/// This might be expressed as an additional supertrait bound on `Runtime`,
53/// eg when Rust supports GATs,
54/// or as an additional bound on the Arti APIs that currently use `Runtime`.
55/// For API future compatibility, if you `impl Runtime for MyRuntime`,
56/// you should also ensure that you
57/// ```ignore
58/// impl<S> TlsProvider<S> for MyRuntime
59/// where S: futures::AsyncRead + futures::AsyncWrite + Unpin + Send + 'static
60/// ```
61//
62/// Perhaps we will need this if we make our own TLS connections *through* Tor,
63/// rather than just channels to guards.
64pub trait Runtime:
65    Sync
66    + Send
67    + Spawn
68    + Blocking
69    + Clone
70    + SleepProvider
71    + CoarseTimeProvider
72    + NetStreamProvider<net::SocketAddr>
73    + NetStreamProvider<unix::SocketAddr>
74    + TlsProvider<<Self as NetStreamProvider<net::SocketAddr>>::Stream>
75    + UdpProvider
76    + Debug
77    + 'static
78{
79}
80
81impl<T> Runtime for T where
82    T: Sync
83        + Send
84        + Spawn
85        + Blocking
86        + Clone
87        + SleepProvider
88        + CoarseTimeProvider
89        + NetStreamProvider<net::SocketAddr>
90        + NetStreamProvider<unix::SocketAddr>
91        + TlsProvider<<Self as NetStreamProvider<net::SocketAddr>>::Stream>
92        + UdpProvider
93        + Debug
94        + 'static
95{
96}
97
98/// A runtime that we can use to run Tor as a client.
99/// * [`ToplevelBlockOn`] to block on a top-level future and run it to completion
100///   (This may become optional in the future, if/when we add WASM
101///   support).
102///
103pub trait ToplevelRuntime: Runtime + ToplevelBlockOn {}
104impl<T: Runtime + ToplevelBlockOn> ToplevelRuntime for T {}
105
106/// Trait for a runtime that can wait until a timer has expired.
107///
108/// Every `SleepProvider` also implements
109/// [`SleepProviderExt`](crate::SleepProviderExt); see that trait
110/// for other useful functions.
111pub trait SleepProvider: Clone + Send + Sync + 'static {
112    /// A future returned by [`SleepProvider::sleep()`]
113    type SleepFuture: Future<Output = ()> + Send + 'static;
114    /// Return a future that will be ready after `duration` has
115    /// elapsed.
116    #[must_use = "sleep() returns a future, which does nothing unless used"]
117    fn sleep(&self, duration: Duration) -> Self::SleepFuture;
118
119    /// Return the SleepProvider's view of the current instant.
120    ///
121    /// (This is the same as `Instant::now`, if not running in test mode.)
122    fn now(&self) -> Instant {
123        Instant::now()
124    }
125
126    /// Return the SleepProvider's view of the current wall-clock time.
127    ///
128    /// (This is the same as `SystemTime::now`, if not running in test mode.)
129    fn wallclock(&self) -> SystemTime {
130        SystemTime::now()
131    }
132
133    /// Signify that a test running under mock time shouldn't advance time yet, with a given
134    /// unique reason string. This is useful for making sure (mock) time doesn't advance while
135    /// things that might require some (real-world) time to complete do so, such as spawning a task
136    /// on another thread.
137    ///
138    /// Call `release_advance` with the same reason string in order to unblock.
139    ///
140    /// This method is only for testing: it should never have any
141    /// effect when invoked on non-testing runtimes.
142    fn block_advance<T: Into<String>>(&self, _reason: T) {}
143
144    /// Signify that the reason to withhold time advancing provided in a call to `block_advance` no
145    /// longer exists, and it's fine to move time forward if nothing else is blocking advances.
146    ///
147    /// This method is only for testing: it should never have any
148    /// effect when invoked on non-testing runtimes.
149    fn release_advance<T: Into<String>>(&self, _reason: T) {}
150
151    /// Allow a test running under mock time to advance time by the provided duration, even if the
152    /// above `block_advance` API has been used.
153    ///
154    /// This method is only for testing: it should never have any
155    /// effect when invoked on non-testing runtimes.
156    fn allow_one_advance(&self, _dur: Duration) {}
157}
158
159/// A provider of reduced-precision timestamps
160///
161/// This doesn't provide any facility for sleeping.
162/// If you want to sleep based on reduced-precision timestamps,
163/// convert the desired sleep duration to `std::time::Duration`
164/// and use [`SleepProvider`].
165pub trait CoarseTimeProvider: Clone + Send + Sync + 'static {
166    /// Return the `CoarseTimeProvider`'s view of the current instant.
167    ///
168    /// This is supposed to be cheaper than `std::time::Instant::now`.
169    fn now_coarse(&self) -> crate::coarse_time::CoarseInstant;
170}
171
172/// Trait for a runtime that can be entered to block on a toplevel future.
173///
174/// This trait is *not* implied by `Runtime`, only by `ToplevelRuntime`.
175/// `ToplevelRuntime` is available at the toplevel of each program,
176/// typically, where a concrete async executor is selected.
177pub trait ToplevelBlockOn: Clone + Send + Sync + 'static {
178    /// Run `future` until it is ready, and return its output.
179    ///
180    /// # Not reentrant!
181    ///
182    /// There should be one call to `block_on` (for each fresh `Runtime`),
183    /// at the toplevel of the program (or test case).
184    /// (Sequential calls to `block_on` from the same thread are allowed.)
185    ///
186    /// `block_on` may not function correctly if is called
187    /// from multiple threads simultaneously,
188    /// or if calls involving different `Runtime`s are interleaved on the same thread.
189    /// (Specific runtimes may offer better guarantees.)
190    ///
191    /// (`tor_rtmock::MockExecutor`'s implementation will often detect violations.)
192    fn block_on<F: Future>(&self, future: F) -> F::Output;
193}
194
195/// Support for interacting with blocking (non-async) code
196///
197/// This supports two use cases: blocking IO and CPU-intensive activities.
198/// (In both of these cases, simply calling the functions within an `async` task
199/// is a bad idea, because that can block the whole async runtime.)
200///
201/// ### Blocking IO
202///
203/// `Blocking` can be used to interact with libraries or OS primitives
204/// that only offer a synchronous, blocking, interface.
205///
206/// Use [`spawn_blocking`](Blocking::spawn_blocking)
207/// when it is convenient to have a long-running thread,
208/// for these operations.
209///
210/// Use [`blocking_io`](Blocking::blocking_io)
211/// when the blocking code is usually expected to complete quickly,
212/// and/or you will be switching back and forth a lot
213/// between sync and async contexts.
214/// Note that you cannot call back to async code from within `blocking_io`.
215///
216/// ### CPU-intensive activities
217///
218/// Perform CPU-intensive work, that ought not to block the program's main loop,
219/// via [`Blocking::spawn_blocking`].
220///
221/// `spawn_blocking` does not apply any limiting or prioritisation;
222/// its threads simply compete for CPU with other threads in the program.
223/// That must be done by the caller; therefore:
224///
225/// **Limit the number of cpu threads** spawned
226/// in order to limit the total amount of CPU time consumed by any part of the program.
227/// For example, consider using one CPU thread per Tor Hidden Service.
228///
229/// It is most performant to spawn a long-running thread,
230/// rather than to repeatedly spawn short-lived threads for individual work items.
231/// This also makes it easier to limit the number of concurrente cpu threads.
232/// For the same reason, [`Blocking::blocking_io`] should be avoided
233/// for the CPU-intensive use case.
234///
235/// ### Mapping to concrete functions from underlying libraries
236///
237/// The semantics of `Blocking` are heavily influenced by Tokio
238/// and by the desire to be able to use tor-rtmock's `MockExecutor` to test Arti code.
239///
240/// | `tor-rtcompat`               | Tokio                 | `MockExecutor`                 |
241/// |------------------------------|-----------------------|--------------------------------|
242/// | `ToplevelBlockOn::block_on`  | `Runtime::block_on`   | `ToplevelBlockOn::block_on`    |
243/// | `Blocking::spawn_blocking`   | `task::spawn_blocking`  | `subthread_spawn`            |
244/// | `Blocking::reenter_block_on` | `Handle::block_on`    | `subthread_block_on_future`    |
245/// | `Blocking::blocking_io`      | `block_in_place`      | `subthread_spawn`              |
246/// | (not available)              | (not implemented)     | `progress_until_stalled` etc.  |
247///
248/// Re `block_on`, see also the docs for the underlying implementations in
249/// [tokio][tokio-threadpool] and
250/// [async-std][async-std-threadpool].
251///
252/// [tokio-threadpool]: https://docs.rs/tokio/latest/tokio/task/fn.spawn_blocking.html
253/// [async-std-threadpool]: https://docs.rs/async-std/latest/async_std/task/fn.spawn_blocking.html
254pub trait Blocking: Clone + Send + Sync + 'static {
255    /// Spawn a thread for blocking IO or CPU-bound work.
256    ///
257    /// This is used in two situations:
258    ///
259    ///  * To perform blocking IO
260    ///  * For cpu-intensive work
261    ///
262    /// See [`Blocking`]'s trait level docs for advice on choosing
263    /// between `spawn_blocking` and [`Blocking::blocking_io`].
264    ///
265    /// `Blocking::spawn_blocking` is similar to `std::thread::spawn`
266    /// but also makes any necessary arrangements so that `reenter_block_on`,
267    /// can be called on the spawned thread.
268    ///
269    /// However, `Blocking::spawn_blocking` *does not guarantee*
270    /// to use a completely fresh thread.
271    /// The implementation may have a thread pool, allowing it reuse an existing thread.
272    /// Correspondingly, if a very large number of `Blocking::spawn_blocking` calls,
273    /// are in progress at once, some of them may block.
274    /// (For example, the implementation for Tokio uses `tokio::task::spawn_blocking`,
275    /// which has both of these properties.)
276    ///
277    /// ### Typical use of `spawn_blocking`
278    ///
279    ///  * Spawn the thread with `SpawnThread::spawn_blocking`.
280    ///  * On that thread, receive work items from from the async environment
281    ///    using async inter-task facilities (eg `futures::channel::mpsc::channel`),
282    ///    called via [`reenter_block_on`](Blocking::reenter_block_on).
283    ///  * Return answers with async inter-task facilities, calling either
284    ///    a non-blocking immediate send (eg `[try_send`])
285    ///    or an async send call via `reneter_block_on`.
286    ///
287    /// ### CPU-intensive work
288    ///
289    /// Limit the number of CPU-intensive concurrent threads spawned with `spawn_blocking`.
290    /// See the [trait-level docs](Blocking) for more details.
291    ///
292    /// ### Panics
293    ///
294    /// `Blocking::spawn_blocking` may only be called from within either:
295    ///
296    ///  * A task or future being polled by this `Runtime`; or
297    ///  * A thread itself spawned with `Blocking::spawn_blocking` on the this runtime.
298    ///
299    /// Otherwise it may malfunction or panic.
300    /// (`tor_rtmock::MockExecutor`'s implementation will usually detect violations.)
301    ///
302    /// If `f` panics, `ThreadHandle` will also panic when polled
303    /// (perhaps using `resume_unwind`).
304    fn spawn_blocking<F, T>(&self, f: F) -> Self::ThreadHandle<T>
305    where
306        F: FnOnce() -> T + Send + 'static,
307        T: Send + 'static;
308
309    /// Future from [`spawn_blocking`](Self::spawn_blocking)
310    ///
311    /// The function callback (`f: F` in [`spawn_blocking`](Self::spawn_blocking)
312    /// will start to run regardless of whether this future is awaited.
313    ///
314    /// Dropping this future doesn't stop the callback; it detaches it:
315    /// the function will continue to run, but its output can no longer be collected.
316    type ThreadHandle<T: Send + 'static>: Future<Output = T>;
317
318    /// Block on a future, from within `Blocking::spawn_blocking`
319    ///
320    /// Reenters the executor, blocking this thread until `future` is `Ready`.
321    ///
322    /// See [`spawn_blocking`](Blocking::spawn_blocking) and
323    /// [`Blocking`]'s trait-level docs for more details.
324    ///
325    /// It is not guaranteed what thread the future will be polled on.
326    /// In production `Runtime`s, it will usually be the thread calling `reenter_block_on`.
327    // All existing runtimes other than MockExecutor accept a non-Send future, but
328    // MockExecutor::subthread_block_on_future does not.
329    // If this restriction turns out to be awkward, MockExecutor could be changed, with some work.
330    ///
331    /// ### Panics
332    ///
333    /// Must only be called on a thread made with `Blocking::spawn_blocking`.
334    /// **Not** allowed within [`blocking_io`](Blocking::blocking_io).
335    ///
336    /// Otherwise it may malfunction or panic.
337    /// (`tor_rtmock::MockExecutor`'s implementation will usually detect violations.)
338    fn reenter_block_on<F>(&self, future: F) -> F::Output
339    where
340        F: Future,
341        F::Output: Send + 'static;
342
343    /// Perform some blocking IO from an async future
344    ///
345    /// Call the blocking function `f`, informing the async executor
346    /// that we are going to perform blocking IO.
347    ///
348    /// This is a usually-faster, but simpler, alternative to [`Blocking::spawn_blocking`].
349    ///
350    /// Its API can be more convenient than `spawn_blocking`.
351    /// `blocking_io` is intended to be more performant than `spawn_blocking`
352    /// when called repeatedly (ie, when switching quickly between sync and async).
353    ///
354    /// See [`Blocking`]'s trait-level docs for more information about
355    /// the performance properties, and on choosing between `blocking_io`
356    /// and `spawn_blocking`.
357    /// (Avoid using `blocking_io` for CPU-intensive work.)
358    ///
359    /// ### Limitations
360    ///
361    ///  * `f` may **not** call [`Blocking::reenter_block_on`], so:
362    ///  * `f` cannot execute any futures.
363    ///    If this is needed, break up `f` into smaller pieces so that the
364    ///    futures can be awaited outside the call to `blocking_io`,
365    ///    or use `spawn_blocking` for the whole activity.
366    ///  * `f` *may* be called on the calling thread when `blocking_io` is called,
367    ///    on an executor thread when the returned future is polled,
368    ///    or a different thread.
369    ///  * Not suitable for CPU-intensive work
370    ///    (mostly because there is no practical way to ration or limit
371    ///    the amount of cpu time used).
372    ///    Use `spawn_blocking` for that.
373    ///  * Performance better than using `spawn_blocking` each time is not guaranteed.
374    ///
375    /// Otherwise the semantics are the same as
376    /// [`spawn_blocking`](Self::spawn_blocking).
377    ///
378    /// ### Panics
379    ///
380    /// `Blocking::block_in_place` may only be called from within
381    /// a task or future being polled by this `Runtime`.
382    ///
383    /// Otherwise it may malfunction or panic.
384    /// (`tor_rtmock::MockExecutor`'s implementation will usually detect violations.)
385    ///
386    /// ### Fallback (provided) implementation
387    ///
388    /// The fallback implementation is currently used with `async_std`.
389    /// It spawns a thread with `spawn_blocking`, once for each `blocking_io` call.
390    fn blocking_io<F, T>(&self, f: F) -> impl Future<Output = T>
391    where
392        F: FnOnce() -> T + Send + 'static,
393        T: Send + 'static,
394    {
395        self.spawn_blocking(f)
396    }
397}
398
399/// Extension trait for [`Spawn`].
400///
401/// This is very similar to, and preferred over, [`futures::task::SpawnExt`].
402/// Unlike `futures::task::SpawnExt`, it is compatible with tokio-console,
403/// and preserves span information for `tracing`.
404// If https://github.com/rust-lang/futures-rs/issues/2977 is ever addressed,
405// we can consider transitioning back to `futures::task::SpawnExt`.
406pub trait SpawnExt: Spawn {
407    /// Spawns a task that polls the given future with output `()` to completion.
408    ///
409    /// See [`futures::task::SpawnExt::spawn`].
410    #[track_caller]
411    fn spawn<Fut>(&self, future: Fut) -> Result<(), SpawnError>
412    where
413        Fut: Future<Output = ()> + Send + 'static,
414    {
415        use tracing::Instrument as _;
416        self.spawn_obj(Box::new(future.in_current_span()).into())
417    }
418
419    /// Spawns a task that polls the given future to completion and returns a future that resolves
420    /// to the spawned future’s output.
421    ///
422    /// See [`futures::task::SpawnExt::spawn_with_handle`].
423    #[track_caller]
424    fn spawn_with_handle<Fut>(
425        &self,
426        future: Fut,
427    ) -> Result<RemoteHandle<<Fut as Future>::Output>, SpawnError>
428    where
429        Fut: Future + Send + 'static,
430        <Fut as Future>::Output: Send,
431    {
432        let (future, handle) = future.remote_handle();
433        self.spawn(future)?;
434        Ok(handle)
435    }
436}
437
438impl<T: Spawn> SpawnExt for T {}
439
440/// Trait providing additional operations on network sockets.
441pub trait StreamOps {
442    /// Set the [`TCP_NOTSENT_LOWAT`] socket option, if this `Stream` is a TCP stream.
443    ///
444    /// Implementations should return an [`UnsupportedStreamOp`] IO error
445    /// if the stream is not a TCP stream,
446    /// and on platforms where the operation is not supported.
447    ///
448    /// [`TCP_NOTSENT_LOWAT`]: https://lwn.net/Articles/560082/
449    fn set_tcp_notsent_lowat(&self, _notsent_lowat: u32) -> IoResult<()> {
450        Err(UnsupportedStreamOp {
451            op: "set_tcp_notsent_lowat",
452            reason: "unsupported object type",
453        }
454        .into())
455    }
456
457    /// Return a new handle that implements [`StreamOps`],
458    /// and that can be used independently of `self`.
459    fn new_handle(&self) -> Box<dyn StreamOps + Send + Unpin> {
460        Box::new(NoOpStreamOpsHandle)
461    }
462}
463
464/// A [`StreamOps`] handle that always returns an error.
465///
466/// Returned from [`StreamOps::new_handle`] for types and platforms
467/// that do not support `StreamOps`.
468#[derive(Copy, Clone, Debug, Default)]
469#[non_exhaustive]
470pub struct NoOpStreamOpsHandle;
471
472impl StreamOps for NoOpStreamOpsHandle {
473    fn new_handle(&self) -> Box<dyn StreamOps + Send + Unpin> {
474        Box::new(*self)
475    }
476}
477
478impl<T: StreamOps, C> StreamOps for Framed<T, C> {
479    fn set_tcp_notsent_lowat(&self, notsent_lowat: u32) -> IoResult<()> {
480        let inner: &T = self;
481        inner.set_tcp_notsent_lowat(notsent_lowat)
482    }
483
484    fn new_handle(&self) -> Box<dyn StreamOps + Send + Unpin> {
485        let inner: &T = self;
486        inner.new_handle()
487    }
488}
489
490/// Error: Tried to perform a [`StreamOps`] operation on an unsupported stream type
491/// or on an unsupported platform.
492///
493/// (For example, you can't call [`StreamOps::set_tcp_notsent_lowat`] on Windows
494/// or on a stream type that is not backed by a TCP socket.)
495#[derive(Clone, Debug, thiserror::Error)]
496#[error("Operation {op} not supported: {reason}")]
497pub struct UnsupportedStreamOp {
498    /// The unsupported operation.
499    op: &'static str,
500    /// The reason the operation is unsupported.
501    reason: &'static str,
502}
503
504impl UnsupportedStreamOp {
505    /// Construct a new `UnsupportedStreamOp` error with the provided operation and reason.
506    pub fn new(op: &'static str, reason: &'static str) -> Self {
507        Self { op, reason }
508    }
509}
510
511impl From<UnsupportedStreamOp> for io::Error {
512    fn from(value: UnsupportedStreamOp) -> Self {
513        io::Error::new(io::ErrorKind::Unsupported, value)
514    }
515}
516
517/// Trait for a runtime that can create and accept connections
518/// over network sockets.
519///
520/// (In Arti we use the [`AsyncRead`] and [`AsyncWrite`] traits from
521/// [`futures::io`] as more standard, even though the ones from Tokio
522/// can be a bit more efficient.  Let's hope that they converge in the
523/// future.)
524// TODO: Use of async_trait is not ideal, since we have to box with every
525// call.  Still, async_io basically makes that necessary :/
526#[async_trait]
527pub trait NetStreamProvider<ADDR = net::SocketAddr>: Clone + Send + Sync + 'static {
528    /// The type for the connections returned by [`Self::connect()`].
529    type Stream: AsyncRead + AsyncWrite + StreamOps + Send + Sync + Unpin + 'static;
530    /// The type for the listeners returned by [`Self::listen()`].
531    type Listener: NetStreamListener<ADDR, Stream = Self::Stream> + Send + Sync + Unpin + 'static;
532
533    /// Launch a connection connection to a given socket address.
534    ///
535    /// Note that unlike `std::net:TcpStream::connect`, we do not accept
536    /// any types other than a single `ADDR`.  We do this because
537    /// we must be absolutely sure not to perform
538    /// unnecessary DNS lookups.
539    async fn connect(&self, addr: &ADDR) -> IoResult<Self::Stream>;
540
541    /// Open a listener on a given socket address.
542    async fn listen(&self, addr: &ADDR) -> IoResult<Self::Listener>;
543}
544
545/// Trait for a local socket that accepts incoming streams.
546///
547/// These objects are returned by instances of [`NetStreamProvider`].  To use
548/// one,
549/// use `incoming` to convert this object into a [`stream::Stream`].
550pub trait NetStreamListener<ADDR = net::SocketAddr> {
551    /// The type of connections returned by [`Self::incoming()`].
552    type Stream: AsyncRead + AsyncWrite + StreamOps + Send + Sync + Unpin + 'static;
553
554    /// The type of [`stream::Stream`] returned by [`Self::incoming()`].
555    type Incoming: stream::Stream<Item = IoResult<(Self::Stream, ADDR)>>
556        + Send
557        + Sync
558        + Unpin
559        + 'static;
560
561    /// Wrap this listener into a new [`stream::Stream`] that yields
562    /// streams and addresses.
563    fn incoming(self) -> Self::Incoming;
564
565    /// Return the local address that this listener is bound to.
566    fn local_addr(&self) -> IoResult<ADDR>;
567}
568
569/// Trait for a runtime that can send and receive UDP datagrams.
570#[async_trait]
571pub trait UdpProvider: Clone + Send + Sync + 'static {
572    /// The type of Udp Socket returned by [`Self::bind()`]
573    type UdpSocket: UdpSocket + Send + Sync + Unpin + 'static;
574
575    /// Bind a local port to send and receive packets from
576    async fn bind(&self, addr: &net::SocketAddr) -> IoResult<Self::UdpSocket>;
577}
578
579/// Trait for a locally bound Udp socket that can send and receive datagrams.
580///
581/// These objects are returned by instances of [`UdpProvider`].
582//
583// NOTE that UdpSocket objects are _necessarily_ un-connected.  If you need to
584// implement a connected Udp socket in the future, please make a new trait (and
585// a new type.)
586#[async_trait]
587pub trait UdpSocket {
588    /// Wait for an incoming datagram; return it along its address.
589    async fn recv(&self, buf: &mut [u8]) -> IoResult<(usize, net::SocketAddr)>;
590    /// Send a datagram to the provided address.
591    async fn send(&self, buf: &[u8], target: &net::SocketAddr) -> IoResult<usize>;
592    /// Return the local address that this socket is bound to.
593    fn local_addr(&self) -> IoResult<net::SocketAddr>;
594}
595
596/// An object with a peer certificate: typically a TLS connection.
597pub trait CertifiedConn {
598    /// Return the keying material (RFC 5705) given a label and an optional context.
599    fn export_keying_material(
600        &self,
601        len: usize,
602        label: &[u8],
603        context: Option<&[u8]>,
604    ) -> IoResult<Vec<u8>>;
605    /// Try to return the (DER-encoded) peer certificate for this
606    /// connection, if any.
607    fn peer_certificate(&self) -> IoResult<Option<Vec<u8>>>;
608
609    /// Try to return the (DER-encoded) link certificate (if any) containing
610    /// the key we used to authenticate this connection.
611    ///
612    /// Ordinarily, this will return a certificate for server connections,
613    /// and None for client connections.
614    //
615    // NOTE: (The correct return value in the _absence_ of a certificate is None.
616    // Later, if we support optional certificates for clients,
617    // the place to return an Unsupported error would be
618    // from whatever function tries to set such a certificate.)
619    fn own_certificate(&self) -> IoResult<Option<Vec<u8>>>;
620}
621
622/// An object that knows how to wrap a TCP connection (where the type of said TCP
623/// connection is `S`) with TLS.
624///
625/// # Usage notes
626///
627/// Note that because of Tor's peculiarities, this is not a
628/// general-purpose TLS type.  Unlike typical users, Tor does not want
629/// its TLS library to check whether the certificates used in TLS are signed
630/// within the web PKI hierarchy, or what their hostnames are, or even whether
631/// they are valid.  It *does*, however, check that the subject public key in the
632/// certificate is indeed correctly used to authenticate the TLS handshake.
633///
634/// If you are implementing something other than Tor, this is **not** the
635/// functionality you want.
636///
637/// How can this behavior be remotely safe, even in Tor?  It only works for Tor
638/// because the certificate that a Tor relay uses in TLS is not actually being
639/// used to certify that relay's public key.  Instead, the certificate only used
640/// as a container for the relay's public key.  The real certification happens
641/// later, inside the TLS session, when the relay presents a CERTS cell.
642///
643/// Such sneakiness was especially necessary before TLS 1.3, which encrypts more
644/// of the handshake, and before pluggable transports, which make
645/// "innocuous-looking TLS handshakes" less important than they once were.  Once
646/// TLS 1.3 is completely ubiquitous, we might be able to specify a simpler link
647/// handshake than Tor uses now.
648#[async_trait]
649pub trait TlsConnector<S> {
650    /// The type of connection returned by this connector
651    type Conn: AsyncRead + AsyncWrite + CertifiedConn + Unpin + Send + 'static;
652
653    /// Start a TLS session over the provided TCP stream `stream`.
654    ///
655    /// For a client connection,
656    /// declare `sni_hostname` as the desired hostname, but don't actually check
657    /// whether the hostname in the certificate matches it.  The connector may
658    /// send `sni_hostname` as part of its handshake, if it supports
659    /// [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication) or one of
660    /// the TLS 1.3 equivalents.
661    ///
662    /// (For a server connection, `sni_hostname` is ignored.)
663    async fn negotiate_unvalidated(&self, stream: S, sni_hostname: &str) -> IoResult<Self::Conn>;
664}
665
666/// Trait for a runtime that knows how to create TLS connections over
667/// TCP streams of type `S`.
668///
669/// This is separate from [`TlsConnector`] because eventually we may
670/// eventually want to support multiple `TlsConnector` implementations
671/// that use a single [`Runtime`].
672///
673/// See the [`TlsConnector`] documentation for a discussion of the Tor-specific
674/// limitations of this trait: If you are implementing something other than Tor,
675/// this is **not** the functionality you want.
676pub trait TlsProvider<S: StreamOps>: Clone + Send + Sync + 'static {
677    /// The Connector object that this provider can return.
678    type Connector: TlsConnector<S, Conn = Self::TlsStream> + Send + Sync + Unpin;
679
680    /// The type of the stream returned by that connector.
681    type TlsStream: AsyncRead + AsyncWrite + StreamOps + CertifiedConn + Unpin + Send + 'static;
682
683    /// The Acceptor object that this provider can return, for handling incoming connections.
684    type Acceptor: TlsConnector<S, Conn = Self::TlsServerStream> + Send + Sync + Unpin;
685
686    /// The type of stream returned by that Acceptor.
687    type TlsServerStream: AsyncRead
688        + AsyncWrite
689        + StreamOps
690        + CertifiedConn
691        + Unpin
692        + Send
693        + 'static;
694
695    /// Return a TLS connector for use with this runtime.
696    fn tls_connector(&self) -> Self::Connector;
697
698    /// Return a TLS acceptor for use with this runtime.
699    ///
700    /// Not every [`TlsProvider`] supports this method.
701    /// For those that do, this method is only supported
702    /// when `tor-rtcompat` is built with the `tls-server` feature.
703    /// When this method is unsupported, it returns an error.
704    fn tls_acceptor(&self, settings: TlsAcceptorSettings) -> IoResult<Self::Acceptor>;
705
706    /// Return true iff the keying material exporters (RFC 5705) is supported.
707    fn supports_keying_material_export(&self) -> bool;
708}
709
710/// Settings used for constructing a TlsAcceptor.
711#[derive(Debug, Clone)]
712#[non_exhaustive]
713pub struct TlsAcceptorSettings {
714    /// The certificates and keys for this acceptor.
715    #[cfg(feature = "tls-server")]
716    pub(crate) identity: TlsKeyAndCert,
717
718    #[cfg(not(feature = "tls-server"))]
719    unconstructable: void::Void,
720    //
721    // TODO: Add support for additional certificates in a chain.
722    // TODO: Possibly, add support for PEM.
723}
724
725impl TlsAcceptorSettings {
726    /// Create a new TlsAcceptorSettings from a certificate and its associated private key,
727    /// both in DER format.
728    ///
729    /// Does not perform full (or even, necessarily, any) validation.
730    //
731    // TODO: It would be great to take a tor_cert::x509::TlsKeyAndCert instead,
732    // but that would (apparently) introduce a dependency cycle.  It would be cool to figure out how
733    // to invert that.
734    #[allow(clippy::unnecessary_wraps)]
735    #[cfg(feature = "tls-server")]
736    pub fn new(identity: TlsKeyAndCert) -> std::io::Result<Self> {
737        Ok(Self { identity })
738    }
739
740    /// Return the primary certificate for this [`TlsAcceptorSettings`], in DER format.
741    pub fn cert_der(&self) -> &[u8] {
742        #[cfg(not(feature = "tls-server"))]
743        {
744            void::unreachable(self.unconstructable);
745        }
746        #[cfg(feature = "tls-server")]
747        self.identity.certificates_der()[0]
748    }
749}
750
751/// An error returned by TlsProvider::tls_acceptor when the TlsProvider does not have TLS server support.
752#[derive(Clone, Debug, thiserror::Error)]
753#[non_exhaustive]
754#[error("This TlsProvider does not support running as a server")]
755pub struct TlsServerUnsupported {}
756
757impl From<TlsServerUnsupported> for io::Error {
758    fn from(value: TlsServerUnsupported) -> Self {
759        io::Error::new(io::ErrorKind::Unsupported, value)
760    }
761}