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