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