Skip to main content

async_openssl/
lib.rs

1//! Async TLS streams backed by OpenSSL.
2//!
3//! This crate provides [`SslStream`], a wrapper around the [`openssl`] crate's
4//! [`SslStream`](ssl::SslStream) type that works with [`futures_io`]'s [`AsyncRead`] and
5//! [`AsyncWrite`] traits rather than std's blocking [`Read`] and [`Write`] traits. Its output is
6//! buffered: see [`SslStream`] for what that means for flushing, closing and errors.
7#![deny(missing_docs, missing_debug_implementations, unsafe_code)]
8#![warn(unreachable_pub, unused_qualifications, unused_lifetimes)]
9#![warn(
10    clippy::must_use_candidate,
11    clippy::unwrap_in_result,
12    clippy::panic_in_result_fn
13)]
14
15use futures_io::{AsyncRead, AsyncWrite};
16use openssl::{
17    error::ErrorStack,
18    ssl::{self, ErrorCode, ShutdownState, Ssl, SslRef},
19};
20use std::{
21    any::Any,
22    fmt, future,
23    io::{self, Read, Write},
24    mem,
25    panic::{self, AssertUnwindSafe},
26    pin::Pin,
27    sync::{
28        Arc, Mutex, MutexGuard, PoisonError,
29        atomic::{AtomicUsize, Ordering},
30    },
31    task::{Context, Poll, Wake, Waker},
32};
33
34#[cfg(test)]
35mod test;
36
37// Compiles the README's example as a doctest.
38#[cfg(doctest)]
39#[doc = include_str!("../README.md")]
40struct ReadmeDoctests;
41
42/// The most bytes that one write hands to OpenSSL, which bounds the output it can leave unsent.
43const WRITE_CHUNK_SIZE: usize = 16 * 1024;
44
45/// How much output may wait for the transport before reads wait for it too. A write leaves at
46/// most one chunk; the rest is what OpenSSL writes by itself while reading or handshaking.
47const MAX_UNSENT: usize = 4 * WRITE_CHUNK_SIZE;
48
49/// How many tasks a [`WakerSet`] keeps.
50const MAX_WAITERS: usize = 32;
51
52fn chunk(buf: &[u8]) -> &[u8] {
53    &buf[..buf.len().min(WRITE_CHUNK_SIZE)]
54}
55
56/// The tasks waiting on a stream.
57///
58/// The transport always gets this set's waker, which wakes every waiting task: OpenSSL may read
59/// while writing and write while reading, so any transport event may be what a task waits for. A
60/// stable waker keeps a transport that keeps one waker (tokio, per direction) from dropping a task,
61/// and one that wakes the waker it replaces (async-io) from making tasks wake each other. Every
62/// event thus wakes every task; a set per direction would lose tasks on a transport that keeps one
63/// waker for both.
64///
65/// A task joins when its operation returns `Pending` and leaves when woken; `generation` catches a
66/// transport wake that came before it joined. A full set is woken and emptied before it takes a
67/// new task, so stale wakers cost a wake each but cannot grow it. More than `MAX_WAITERS` tasks
68/// waiting at once keep waking each other.
69#[derive(Default)]
70struct WakerSet {
71    waiters: Mutex<Vec<Waiter>>,
72    /// How many times the transport woke the set. It only changes with `waiters` locked.
73    generation: AtomicUsize,
74}
75
76struct Waiter {
77    waker: Waker,
78    /// Whether the task waits for records, which another task's read may take, rather than for
79    /// the transport to take output.
80    records: bool,
81}
82
83impl WakerSet {
84    fn lock(&self) -> MutexGuard<'_, Vec<Waiter>> {
85        self.waiters.lock().unwrap_or_else(PoisonError::into_inner)
86    }
87
88    fn generation(&self) -> usize {
89        self.generation.load(Ordering::Acquire)
90    }
91
92    /// Adds a task, or wakes it at once if the transport woke the set since `generation`.
93    fn join(&self, waker: &Waker, records: bool, generation: usize) {
94        let mut waiters = self.lock();
95        if self.generation.load(Ordering::Relaxed) != generation {
96            drop(waiters);
97            waker.wake_by_ref();
98            return;
99        }
100        if let Some(waiter) = waiters.iter_mut().find(|w| w.waker.will_wake(waker)) {
101            waiter.records |= records;
102            return;
103        }
104        let waiter = Waiter {
105            waker: waker.clone(),
106            records,
107        };
108        if waiters.len() < MAX_WAITERS {
109            waiters.push(waiter);
110        } else {
111            let evicted = mem::replace(&mut *waiters, vec![waiter]);
112            drop(waiters);
113            self.wake_all(evicted);
114        }
115    }
116
117    /// Wakes the waiting tasks, or only those waiting for records.
118    fn wake_tasks(&self, by_transport: bool, records_only: bool) {
119        let woken = {
120            let mut waiters = self.lock();
121            if by_transport {
122                self.generation.fetch_add(1, Ordering::Release);
123            }
124            if waiters.is_empty() || (records_only && !waiters.iter().any(|w| w.records)) {
125                return;
126            }
127            let all = mem::take(&mut *waiters);
128            if records_only {
129                let (woken, kept) = all.into_iter().partition(|waiter| waiter.records);
130                *waiters = kept;
131                woken
132            } else {
133                all
134            }
135        };
136        self.wake_all(woken);
137    }
138
139    /// Wakes `wakers` and reuses their allocation for the set. If a waker panics, those not woken
140    /// yet go back into the set.
141    fn wake_all(&self, wakers: Vec<Waiter>) {
142        struct Requeue<'a> {
143            set: &'a WakerSet,
144            wakers: Vec<Waiter>,
145            next: usize,
146        }
147
148        impl Drop for Requeue<'_> {
149            fn drop(&mut self) {
150                if self.next < self.wakers.len() {
151                    let rest = self.wakers.drain(self.next..);
152                    self.set.lock().extend(rest);
153                }
154            }
155        }
156
157        let mut requeue = Requeue {
158            set: self,
159            wakers,
160            next: 0,
161        };
162        while let Some(waiter) = requeue.wakers.get(requeue.next) {
163            requeue.next += 1;
164            waiter.waker.wake_by_ref();
165        }
166        let mut wakers = mem::take(&mut requeue.wakers);
167        wakers.clear();
168        let mut waiters = self.lock();
169        if waiters.capacity() == 0 {
170            *waiters = wakers;
171        }
172    }
173}
174
175impl Wake for WakerSet {
176    fn wake(self: Arc<Self>) {
177        self.wake_by_ref();
178    }
179
180    fn wake_by_ref(self: &Arc<Self>) {
181        self.wake_tasks(true, false);
182    }
183}
184
185/// Runs `f`, which calls OpenSSL, on an empty error queue, then puts the thread's entries back.
186/// OpenSSL and LibreSSL take any queued entry as the reason a call failed, even a call that only
187/// has to be retried, and the entries belong to other code, such as an OpenSSL call that uses this
188/// stream as its transport.
189fn keeping_foreign_errors<T>(f: impl FnOnce() -> T) -> T {
190    let foreign = ErrorStack::get();
191    let result = f();
192    foreign.put();
193    result
194}
195
196/// Whether the handshake is finished. rust-openssl binds `SSL_is_init_finished` for OpenSSL only;
197/// both libraries describe that state the same way.
198fn handshake_finished(ssl: &SslRef) -> bool {
199    ssl.state_string_long() == "SSL negotiation finished successfully"
200}
201
202/// Writes `buf` from `*written` on until the transport took all of it, returned `Pending` or
203/// failed.
204fn poll_write_all<S: AsyncWrite + ?Sized>(
205    mut stream: Pin<&mut S>,
206    cx: &mut Context<'_>,
207    buf: &[u8],
208    written: &mut usize,
209) -> Poll<io::Result<()>> {
210    while *written < buf.len() {
211        let left = buf.len() - *written;
212        match std::task::ready!(stream.as_mut().poll_write(cx, &buf[*written..]))? {
213            0 => return Poll::Ready(Err(io::ErrorKind::WriteZero.into())),
214            n if n > left => {
215                let error = "the transport reported writing more than it was given";
216                return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidData, error)));
217            }
218            n => *written += n,
219        }
220    }
221    Poll::Ready(Ok(()))
222}
223
224/// Where a transport error goes when an OpenSSL call meets one: a read error, or a failure to send
225/// buffered output when the call would otherwise wait for the peer, who may be waiting for it.
226#[derive(Clone, Copy, Default)]
227enum ErrorPath {
228    /// To OpenSSL, as the error of a transport read: the only way rust-openssl puts an `io::Error`
229    /// in an `ssl::Error`. OpenSSL 4 then fails the session unless rust-openssl asks it to retry,
230    /// and LibreSSL reports an error of its own.
231    #[default]
232    ThroughOpenSsl,
233    /// To the caller, through `Call`: OpenSSL only sees a read that would wait, and the session
234    /// stays usable.
235    ToCaller,
236}
237
238/// What the last transport read of an OpenSSL call did.
239#[derive(Clone, Copy, Default)]
240enum LastRead {
241    /// The read got data, or the call made no read.
242    #[default]
243    Data,
244    Eof,
245    /// The BIO told OpenSSL to wait: the transport will wake the stream's tasks, or the call has
246    /// an error for the caller.
247    Waiting,
248    /// The BIO gave OpenSSL an error.
249    Failed,
250}
251
252/// What the transport did during one OpenSSL call.
253///
254/// The errors are `AssertUnwindSafe` to keep `SslStream` unwind safe: an `io::Error` never changes
255/// once made, so a panic cannot leave one broken.
256#[derive(Default)]
257struct Call {
258    path: ErrorPath,
259    read: LastRead,
260    /// The transport refused or failed to take output, so it is not asked again during this call.
261    send_blocked: bool,
262    /// Why sending the buffered output failed. That output stays buffered.
263    write_error: Option<AssertUnwindSafe<io::Error>>,
264    /// A transport read error kept from OpenSSL (`ErrorPath::ToCaller`).
265    read_error: Option<AssertUnwindSafe<io::Error>>,
266    /// A transport error kept from OpenSSL that the next read hands it, as the reason the call
267    /// fails.
268    inject: Option<AssertUnwindSafe<io::Error>>,
269}
270
271/// What the transport did during one operation of the stream, which can make several OpenSSL
272/// calls.
273#[derive(Default)]
274struct Op {
275    /// A read got records, which tasks waiting for records may have waited for.
276    read_progress: bool,
277    /// A send failed, which every waiting task has to retry or report.
278    send_failed: bool,
279    /// A read waits for the transport, rather than for the transport to take output.
280    read_wait: bool,
281}
282
283/// How [`StreamWrapper::poll_send`] flushes the transport after sending the buffered output.
284#[derive(Clone, Copy)]
285enum Flush {
286    /// If OpenSSL asked for a flush.
287    Requested,
288    /// Also if output was written since the transport's last flush.
289    Unflushed,
290    /// Always.
291    Always,
292}
293
294/// The transport, as OpenSSL sees it through rust-openssl's BIO.
295///
296/// Writes never wait: what the transport does not take at once is buffered and sent first. So
297/// OpenSSL never holds a record it must retry with the same bytes, and what it writes on its own,
298/// such as a session ticket or a key update during a read, never fails on a pending write.
299struct StreamWrapper<S: Unpin> {
300    stream: S,
301    wakers: Arc<WakerSet>,
302    /// The waker of `wakers`, which the transport always gets.
303    waker: Waker,
304    /// TLS output, of which the transport took the first `sent` bytes.
305    out: Vec<u8>,
306    sent: usize,
307    /// Whether OpenSSL asked for a transport flush that has not completed yet.
308    flush_needed: bool,
309    /// Whether output was written, to the transport or to `out`, since the transport's last flush.
310    unflushed: bool,
311    /// The generation of `wakers` at which the transport last refused output (`Pending`), if not
312    /// polled since: OpenSSL calls do not ask it again until it wakes the set. A transport that
313    /// failed is asked again.
314    refused: Option<usize>,
315    /// Whether a transport poll panicked: the transport may have taken part of a record, so
316    /// nothing else may go through it.
317    poisoned: bool,
318    /// The payload of that panic, resumed once the operation's bookkeeping is done.
319    panic: Mutex<Option<Box<dyn Any + Send>>>,
320    /// Whether the stream was closed. Nothing OpenSSL writes by itself afterwards, such as the
321    /// answer to a key update, may go out.
322    close: Close,
323    op: Op,
324    call: Call,
325}
326
327impl<S> fmt::Debug for StreamWrapper<S>
328where
329    S: fmt::Debug + Unpin,
330{
331    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
332        fmt.debug_struct("StreamWrapper")
333            .field("stream", &self.stream)
334            .field("unsent", &self.unsent())
335            .field("flush_needed", &self.flush_needed)
336            .field("unflushed", &self.unflushed)
337            .field("poisoned", &self.poisoned)
338            .field("close", &self.close)
339            .finish_non_exhaustive()
340    }
341}
342
343impl<S: Unpin> StreamWrapper<S> {
344    fn unsent(&self) -> usize {
345        self.out.len() - self.sent
346    }
347}
348
349impl<S: AsyncWrite + Unpin> StreamWrapper<S> {
350    fn new(stream: S) -> Self {
351        let wakers = Arc::<WakerSet>::default();
352        Self {
353            stream,
354            waker: Waker::from(wakers.clone()),
355            wakers,
356            out: Vec::new(),
357            sent: 0,
358            flush_needed: false,
359            unflushed: false,
360            refused: None,
361            poisoned: false,
362            panic: Mutex::new(None),
363            close: Close::Open,
364            op: Op::default(),
365            call: Call::default(),
366        }
367    }
368
369    fn check_poisoned(&self) -> io::Result<()> {
370        if self.poisoned {
371            let error = "the transport panicked during an earlier call";
372            return Err(io::Error::other(error));
373        }
374        Ok(())
375    }
376
377    /// Polls the transport with the stream's waker, unless an earlier poll panicked. A panic fails
378    /// the poll, poisons the stream, and is resumed at the end of the operation, even where
379    /// OpenSSL ignores the failure.
380    fn poll_transport<T>(
381        &mut self,
382        poll: impl FnOnce(Pin<&mut S>, &mut Context<'_>) -> Poll<io::Result<T>>,
383    ) -> Poll<io::Result<T>> {
384        self.check_poisoned()?;
385        let stream = Pin::new(&mut self.stream);
386        let mut cx = Context::from_waker(&self.waker);
387        match panic::catch_unwind(AssertUnwindSafe(|| poll(stream, &mut cx))) {
388            Ok(result) => result,
389            Err(payload) => {
390                self.poisoned = true;
391                *self.panic.get_mut().unwrap_or_else(PoisonError::into_inner) = Some(payload);
392                Poll::Ready(Err(io::Error::other("the transport panicked")))
393            }
394        }
395    }
396
397    /// Polls the transport to take output, noting a refusal (`refused`) or a failure
398    /// (`Op::send_failed`).
399    fn poll_output<T>(
400        &mut self,
401        poll: impl FnOnce(Pin<&mut S>, &mut Context<'_>) -> Poll<io::Result<T>>,
402    ) -> Poll<io::Result<T>> {
403        let generation = self.wakers.generation();
404        let result = self.poll_transport(poll);
405        self.refused = result.is_pending().then_some(generation);
406        if let Poll::Ready(Err(_)) = result {
407            self.op.send_failed = true;
408        }
409        result
410    }
411
412    /// Sends the buffered output, then flushes the transport as `flush` says.
413    fn poll_send(&mut self, flush: Flush) -> Poll<io::Result<()>> {
414        if let Err(error) = self.check_poisoned() {
415            self.op.send_failed = true;
416            return Poll::Ready(Err(error));
417        }
418        if self.unsent() > 0 {
419            let out = mem::take(&mut self.out);
420            let mut sent = self.sent;
421            let result = self.poll_output(|stream, cx| poll_write_all(stream, cx, &out, &mut sent));
422            (self.out, self.sent) = (out, sent);
423            std::task::ready!(result)?;
424            // Only a slow transport needs the buffer: keep enough memory for a full chunk's record.
425            self.out.clear();
426            self.out.shrink_to(2 * WRITE_CHUNK_SIZE);
427            self.sent = 0;
428        }
429        let needed = match flush {
430            Flush::Requested => self.flush_needed,
431            Flush::Unflushed => self.flush_needed || self.unflushed,
432            Flush::Always => true,
433        };
434        if needed {
435            std::task::ready!(self.poll_output(|stream, cx| stream.poll_flush(cx)))?;
436            self.flush_needed = false;
437            self.unflushed = false;
438        }
439        Poll::Ready(Ok(()))
440    }
441
442    fn poll_close(&mut self) -> Poll<io::Result<()>> {
443        self.poll_transport(|stream, cx| stream.poll_close(cx))
444    }
445
446    /// Whether an OpenSSL call may ask the transport to take output.
447    fn may_send(&self) -> bool {
448        !self.call.send_blocked && self.refused != Some(self.wakers.generation())
449    }
450
451    /// Prepares for an OpenSSL call.
452    fn start_call(&mut self, path: ErrorPath) {
453        self.call = Call {
454            path,
455            ..Call::default()
456        };
457    }
458
459    /// Sends the buffered output as far as the transport allows during an OpenSSL call.
460    fn send(&mut self, flush: Flush) {
461        if !self.may_send() {
462            return;
463        }
464        let result = self.poll_send(flush);
465        self.note_send(result);
466    }
467
468    /// Records what sending during an OpenSSL call did: a transport that refused or failed is not
469    /// asked again during the call, and its error is kept.
470    fn note_send(&mut self, result: Poll<io::Result<()>>) {
471        match result {
472            Poll::Ready(Ok(())) => {}
473            Poll::Ready(Err(error)) => {
474                self.call.send_blocked = true;
475                self.call.write_error = Some(AssertUnwindSafe(error));
476            }
477            Poll::Pending => self.call.send_blocked = true,
478        }
479    }
480
481    /// Tells OpenSSL to wait.
482    fn wait(&mut self) -> io::Result<usize> {
483        self.call.read = LastRead::Waiting;
484        Err(io::ErrorKind::WouldBlock.into())
485    }
486
487    fn read_failed(&mut self, error: io::Error) -> io::Result<usize> {
488        match self.call.path {
489            ErrorPath::ThroughOpenSsl => {
490                self.call.read = LastRead::Failed;
491                Err(error)
492            }
493            ErrorPath::ToCaller => {
494                self.call.read_error = Some(AssertUnwindSafe(error));
495                self.wait()
496            }
497        }
498    }
499
500    /// Fails with the error that kept output from being sent, if any, as the peer may never answer
501    /// output it did not get. Otherwise tells OpenSSL to wait.
502    fn fail_or_wait(&mut self) -> io::Result<usize> {
503        match self.call.write_error.take() {
504            Some(AssertUnwindSafe(error)) => self.read_failed(error),
505            None => self.wait(),
506        }
507    }
508
509    /// Takes the transport error that an `ErrorPath::ToCaller` call kept from OpenSSL.
510    fn kept_error(&mut self) -> Option<io::Error> {
511        (self.call.read_error.take())
512            .or_else(|| self.call.write_error.take())
513            .map(|AssertUnwindSafe(error)| error)
514    }
515}
516
517impl<S> Read for StreamWrapper<S>
518where
519    S: AsyncRead + AsyncWrite + Unpin,
520{
521    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
522        if let Some(AssertUnwindSafe(error)) = self.call.inject.take() {
523            self.call.read = LastRead::Failed;
524            return Err(error);
525        }
526        // What OpenSSL waits for may be the answer to the output, so send it first.
527        self.send(Flush::Requested);
528        // Bound what OpenSSL writes by itself while reading: past the limit, wait for the
529        // transport to take it, or report why it cannot.
530        if self.unsent() > MAX_UNSENT {
531            return self.fail_or_wait();
532        }
533        let len = buf.len();
534        match self.poll_transport(|stream, cx| stream.poll_read(cx, buf)) {
535            Poll::Ready(Ok(0)) => {
536                self.call.read = LastRead::Eof;
537                Ok(0)
538            }
539            Poll::Ready(Ok(n)) if n > len => {
540                let error = "the transport reported reading more than it was given";
541                self.read_failed(io::Error::new(io::ErrorKind::InvalidData, error))
542            }
543            Poll::Ready(Ok(n)) => {
544                self.call.read = LastRead::Data;
545                self.op.read_progress = true;
546                Ok(n)
547            }
548            Poll::Ready(Err(error)) => self.read_failed(error),
549            Poll::Pending => {
550                self.op.read_wait = true;
551                // Before waiting for the peer, make sure that it got what was written: some
552                // transports only send on flush, and some TLS libraries never ask for one.
553                self.send(Flush::Unflushed);
554                self.fail_or_wait()
555            }
556        }
557    }
558}
559
560impl<S> Write for StreamWrapper<S>
561where
562    S: AsyncRead + AsyncWrite + Unpin,
563{
564    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
565        // Nothing more may go out after a transport panic, and OpenSSL may as well know.
566        self.check_poisoned()?;
567        if !matches!(self.close, Close::Open) {
568            return Ok(buf.len());
569        }
570        let mut written = 0;
571        // Skip the copy when nothing is buffered, which is the common case.
572        if self.out.is_empty() && self.may_send() {
573            let result =
574                self.poll_output(|stream, cx| poll_write_all(stream, cx, buf, &mut written));
575            self.note_send(result);
576        } else if self.sent > 0 && self.sent >= self.unsent() {
577            // Drop what the transport took once that is at least what is left, so that a transport
578            // that keeps taking part of the output cannot make the buffer grow without bound.
579            self.out.drain(..self.sent);
580            self.sent = 0;
581        }
582        self.out.extend_from_slice(&buf[written..]);
583        self.unflushed = true;
584        Ok(buf.len())
585    }
586
587    fn flush(&mut self) -> io::Result<()> {
588        if !matches!(self.close, Close::Open) {
589            return Ok(());
590        }
591        // Never fails: a failed flush leaves `SSL_write_early_data` counting its next bytes as
592        // written without writing them. The next send finishes the flush.
593        self.flush_needed = true;
594        self.send(Flush::Requested);
595        Ok(())
596    }
597}
598
599/// Whether [`AsyncWrite::poll_close`] closed the stream.
600#[derive(Debug)]
601enum Close {
602    Open,
603    /// `close_notify` was written. Calling `SSL_shutdown` again would wait for the peer's.
604    Sent,
605    /// No session to close (no handshake, a handshake or post-handshake exchange in progress, or a
606    /// failed session): the transport is closed anyway, and the close that closes it returns
607    /// OpenSSL's reason, if any.
608    NoSession(Option<AssertUnwindSafe<io::Error>>),
609}
610
611/// An asynchronous version of [`openssl::ssl::SslStream`].
612///
613/// # Buffered output
614///
615/// OpenSSL writes into a buffer, from which the transport takes what it can. As with a
616/// [`BufWriter`](std::io::BufWriter), a write completes once OpenSSL has encrypted its bytes, and
617/// a handshake call can complete with its last flight still buffered. Buffered output goes out
618/// with later writes, flushes and closes, and with reads, peeks and handshake calls that read from
619/// the transport, which flush it before they wait for the peer. [`flush`](AsyncWrite::poll_flush)
620/// returns once everything was sent and the transport flushed. [`close`](AsyncWrite::poll_close)
621/// the stream rather than drop it, or close or flush its transport directly, or buffered output is
622/// lost.
623///
624/// A write first waits for buffered output, and for a transport flush that OpenSSL asked for:
625/// until then it returns `Pending` without writing, so it can be dropped or polled again with
626/// other bytes. Each write hands OpenSSL at most 16 KiB. Reads, peeks and handshake calls do not
627/// wait for output, so a peer that stops reading until it is read from cannot deadlock the
628/// stream, unless more than 64 KiB is waiting (what OpenSSL wrote by itself while reading or
629/// handshaking, on top of what writes left).
630///
631/// # Errors
632///
633/// A transport error while sending buffered output is returned by the write, flush or close that
634/// sends it, or by a call that would otherwise wait for the peer, who may be waiting for that
635/// output. A write whose own bytes the transport refuses still completes: OpenSSL wrote them. The
636/// output stays buffered and the error is not kept: a later call reports it if it recurs, so a
637/// transient error loses nothing.
638///
639/// Reads and writes return transport errors as they were, without passing them to OpenSSL.
640/// Peeks, handshake calls and [`read_early_data`](Self::read_early_data) return them through
641/// OpenSSL, inside an [`ssl::Error`]. OpenSSL 4 then fails the session, unless the error is
642/// [`WouldBlock`](io::ErrorKind::WouldBlock) or [`NotConnected`](io::ErrorKind::NotConnected),
643/// which rust-openssl asks it to retry. LibreSSL replaces the error with its own, and may report
644/// the transport error again later as the cause of an unrelated end of stream.
645/// [`write_early_data`](Self::write_early_data) never returns them.
646///
647/// A transport end without `close_notify` after the handshake reads as the end of the stream on
648/// OpenSSL 1.1.1, 3.x and LibreSSL, as with rust-openssl's own streams. On OpenSSL 4 it is an
649/// error, and a close then fails too, unless the session has `SslOptions::IGNORE_UNEXPECTED_EOF`.
650/// A transport end or a `close_notify` during a handshake that a read drives, renegotiation
651/// included, is always an error.
652///
653/// # Closing
654///
655/// A close sends buffered output, writes `close_notify` once, sends it and closes the transport.
656/// A send error is returned with the transport left open, so that closing again retries; dropping
657/// the stream releases the transport. After the peer's `close_notify`, failing to send ours is no
658/// error. Without a session to close (no handshake, a handshake or post-handshake exchange in
659/// progress, or a failed session), a close closes the transport and returns an error: the
660/// transport's if closing it failed, else OpenSSL's reason, if any. So does every later close. No
661/// write goes out after a close.
662///
663/// # Tasks
664///
665/// Waiting tasks are woken when the transport is ready or another task's call failed to send, and
666/// those waiting for the peer also when another task's call read from the transport. Keep one
667/// waker per task: each new one costs an extra wake, and more than 32 tasks waiting at once keep
668/// waking each other.
669///
670/// A panic in the transport poisons the stream: every later call that uses the transport fails.
671pub struct SslStream<S: Unpin> {
672    inner: ssl::SslStream<StreamWrapper<S>>,
673}
674
675impl<S> fmt::Debug for SslStream<S>
676where
677    S: fmt::Debug + Unpin,
678{
679    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
680        fmt.debug_struct("SslStream")
681            .field("stream", self.inner.get_ref())
682            .field("ssl", self.inner.ssl())
683            .finish()
684    }
685}
686
687impl<S> SslStream<S>
688where
689    S: AsyncRead + AsyncWrite + Unpin,
690{
691    /// Like [`SslStream::new`](ssl::SslStream::new).
692    pub fn new(ssl: Ssl, stream: S) -> Result<Self, ErrorStack> {
693        ssl::SslStream::new(ssl, StreamWrapper::new(stream)).map(|inner| SslStream { inner })
694    }
695
696    /// Like [`SslStream::connect`](ssl::SslStream::connect).
697    ///
698    /// It can complete while its last flight is still [buffered](Self#buffered-output), and
699    /// returns transport errors through OpenSSL, as [described](Self#errors).
700    pub fn poll_connect(
701        self: Pin<&mut Self>,
702        cx: &mut Context<'_>,
703    ) -> Poll<Result<(), ssl::Error>> {
704        self.poll_ssl_op(cx, |s| s.connect())
705    }
706
707    /// A convenience method wrapping [`poll_connect`](Self::poll_connect).
708    pub async fn connect(mut self: Pin<&mut Self>) -> Result<(), ssl::Error> {
709        future::poll_fn(|cx| self.as_mut().poll_connect(cx)).await
710    }
711
712    /// Like [`SslStream::accept`](ssl::SslStream::accept).
713    ///
714    /// It can complete while its last flight is still [buffered](Self#buffered-output), and
715    /// returns transport errors through OpenSSL, as [described](Self#errors).
716    pub fn poll_accept(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), ssl::Error>> {
717        self.poll_ssl_op(cx, |s| s.accept())
718    }
719
720    /// A convenience method wrapping [`poll_accept`](Self::poll_accept).
721    pub async fn accept(mut self: Pin<&mut Self>) -> Result<(), ssl::Error> {
722        future::poll_fn(|cx| self.as_mut().poll_accept(cx)).await
723    }
724
725    /// Like [`SslStream::do_handshake`](ssl::SslStream::do_handshake).
726    ///
727    /// It can complete while its last flight is still [buffered](Self#buffered-output), and
728    /// returns transport errors through OpenSSL, as [described](Self#errors).
729    pub fn poll_do_handshake(
730        self: Pin<&mut Self>,
731        cx: &mut Context<'_>,
732    ) -> Poll<Result<(), ssl::Error>> {
733        self.poll_ssl_op(cx, |s| s.do_handshake())
734    }
735
736    /// A convenience method wrapping [`poll_do_handshake`](Self::poll_do_handshake).
737    pub async fn do_handshake(mut self: Pin<&mut Self>) -> Result<(), ssl::Error> {
738        future::poll_fn(|cx| self.as_mut().poll_do_handshake(cx)).await
739    }
740
741    /// Like [`SslStream::ssl_peek`](ssl::SslStream::ssl_peek).
742    ///
743    /// Like a read, it sends [buffered output](Self#buffered-output) without waiting for it
744    /// below the limit, and it returns transport errors through OpenSSL, as
745    /// [described](Self#errors).
746    pub fn poll_peek(
747        self: Pin<&mut Self>,
748        cx: &mut Context<'_>,
749        buf: &mut [u8],
750    ) -> Poll<Result<usize, ssl::Error>> {
751        // A zero-length `SSL_peek_ex` waits for a record, then fails. There is nothing to peek
752        // into an empty buffer anyway.
753        if buf.is_empty() {
754            return Poll::Ready(Ok(0));
755        }
756        self.poll_ssl_op(cx, |s| s.ssl_peek(buf))
757    }
758
759    /// A convenience method wrapping [`poll_peek`](Self::poll_peek).
760    pub async fn peek(mut self: Pin<&mut Self>, buf: &mut [u8]) -> Result<usize, ssl::Error> {
761        future::poll_fn(|cx| self.as_mut().poll_peek(cx, buf)).await
762    }
763
764    /// Like [`SslStream::read_early_data`](ssl::SslStream::read_early_data), except that a
765    /// handshake failure, or a transport failure or end, right after the early data is an error,
766    /// not the end of early data (`Ok(0)`). After the end of early data, it advances the handshake
767    /// without waiting; [`accept`](Self::accept) finishes it.
768    ///
769    /// An empty buffer returns `Ok(0)` if no early data was accepted or it is over, and an error
770    /// once an early-data record arrives.
771    ///
772    /// Requires OpenSSL 1.1.1 or newer (LibreSSL only has stubs for early data).
773    #[cfg(ossl111)]
774    pub fn poll_read_early_data(
775        self: Pin<&mut Self>,
776        cx: &mut Context<'_>,
777        buf: &mut [u8],
778    ) -> Poll<Result<usize, ssl::Error>> {
779        self.poll_op(cx, |this| {
780            // Transport errors are kept from the first call: one right after the end of early data
781            // would make OpenSSL 4 fail the session without a reason. A second call gets it.
782            this.poll_ssl(ErrorPath::ToCaller, |s| {
783                let early = s.read_early_data(buf);
784                let over = matches!(early, Ok(0));
785                // Advance the handshake after the end of early data, or rejected early data. This
786                // also reports a handshake that failed in the same call: OpenSSL still returns the
787                // end of early data, and leaves the reason on the error queue for
788                // `SSL_do_handshake`.
789                let result = match early {
790                    Ok(0) => s.do_handshake().map(|()| 0),
791                    result => result,
792                };
793                let Err(error) = result else {
794                    return result;
795                };
796                // A kept error is why the call stopped only if it waited: then a second call hands
797                // it to OpenSSL. One kept from a call that OpenSSL failed by itself, such as
798                // sending its alert, is dropped.
799                let waited = matches!(s.get_ref().call.read, LastRead::Waiting);
800                match waited.then(|| s.get_mut().kept_error()).flatten() {
801                    Some(kept) => {
802                        s.get_mut().call.inject = Some(AssertUnwindSafe(kept));
803                        if over {
804                            s.do_handshake().map(|()| 0)
805                        } else {
806                            s.read_early_data(buf)
807                        }
808                    }
809                    // `accept` waits for the rest: `SSL_read_early_data` would fail now.
810                    None if over
811                        && matches!(error.code(), ErrorCode::WANT_READ | ErrorCode::WANT_WRITE) =>
812                    {
813                        Ok(0)
814                    }
815                    None => Err(error),
816                }
817            })
818        })
819    }
820
821    /// A convenience method wrapping [`poll_read_early_data`](Self::poll_read_early_data).
822    ///
823    /// Requires OpenSSL 1.1.1 or newer.
824    #[cfg(ossl111)]
825    pub async fn read_early_data(
826        mut self: Pin<&mut Self>,
827        buf: &mut [u8],
828    ) -> Result<usize, ssl::Error> {
829        future::poll_fn(|cx| self.as_mut().poll_read_early_data(cx, buf)).await
830    }
831
832    /// Like [`SslStream::write_early_data`](ssl::SslStream::write_early_data).
833    ///
834    /// Like a write, it first waits for [buffered output](Self#buffered-output), and writes at
835    /// most 16 KiB. Unlike a write, it goes on after a transport error, which it cannot return: an
836    /// [`ssl::Error`] only carries one that OpenSSL read, and `SSL_write_early_data` never reads.
837    /// The next call that sends reports it if it recurs. Until then, the output it leaves is
838    /// bounded by the session's maximum early data on a client, and only by the caller on a
839    /// server.
840    ///
841    /// Requires OpenSSL 1.1.1 or newer (LibreSSL only has stubs for early data).
842    #[cfg(ossl111)]
843    pub fn poll_write_early_data(
844        self: Pin<&mut Self>,
845        cx: &mut Context<'_>,
846        buf: &[u8],
847    ) -> Poll<Result<usize, ssl::Error>> {
848        self.poll_op(cx, |this| {
849            let _ = std::task::ready!(this.wrapper().poll_send(Flush::Requested));
850            let buf = chunk(buf);
851            this.poll_ssl(ErrorPath::ThroughOpenSsl, |s| s.write_early_data(buf))
852        })
853    }
854
855    /// A convenience method wrapping [`poll_write_early_data`](Self::poll_write_early_data).
856    ///
857    /// Requires OpenSSL 1.1.1 or newer.
858    #[cfg(ossl111)]
859    pub async fn write_early_data(
860        mut self: Pin<&mut Self>,
861        buf: &[u8],
862    ) -> Result<usize, ssl::Error> {
863        future::poll_fn(|cx| self.as_mut().poll_write_early_data(cx, buf)).await
864    }
865}
866
867impl<S: Unpin> SslStream<S> {
868    /// Returns a shared reference to the `Ssl` object associated with this stream.
869    #[must_use]
870    pub fn ssl(&self) -> &SslRef {
871        self.inner.ssl()
872    }
873
874    /// Returns a shared reference to the underlying stream.
875    #[must_use]
876    pub fn get_ref(&self) -> &S {
877        &self.inner.get_ref().stream
878    }
879
880    /// Returns a mutable reference to the underlying stream.
881    ///
882    /// # Warning
883    ///
884    /// Reading from or writing to the underlying stream directly will corrupt the TLS session,
885    /// and closing or flushing it directly misses the [buffered output](Self#buffered-output).
886    pub fn get_mut(&mut self) -> &mut S {
887        &mut self.inner.get_mut().stream
888    }
889
890    /// Returns a pinned mutable reference to the underlying stream.
891    ///
892    /// # Warning
893    ///
894    /// Reading from or writing to the underlying stream directly will corrupt the TLS session,
895    /// and closing or flushing it directly misses the [buffered output](Self#buffered-output).
896    #[must_use]
897    pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut S> {
898        Pin::new(Pin::into_inner(self).get_mut())
899    }
900
901    fn wrapper(&mut self) -> &mut StreamWrapper<S> {
902        self.inner.get_mut()
903    }
904}
905
906impl<S> SslStream<S>
907where
908    S: AsyncRead + AsyncWrite + Unpin,
909{
910    /// Runs one poll of an operation, and joins the waiting tasks if it returns `Pending`. A failed
911    /// send wakes every waiting task, to retry or report it. A read that got records wakes the
912    /// tasks waiting for records: no transport event will report those again.
913    fn poll_op<T>(
914        self: Pin<&mut Self>,
915        cx: &mut Context<'_>,
916        op: impl FnOnce(&mut Self) -> Poll<T>,
917    ) -> Poll<T> {
918        let this = self.get_mut();
919        let wrapper = this.wrapper();
920        let generation = wrapper.wakers.generation();
921        wrapper.op = Op::default();
922        let result = op(this);
923        // After a close, OpenSSL must refuse writes, which without a session would start a
924        // handshake. This also restores `SSL_SENT_SHUTDOWN` where OpenSSL 4 clears it (an unclean
925        // end under `SSL_OP_IGNORE_UNEXPECTED_EOF`), which keeps the session cached.
926        if !matches!(this.inner.get_ref().close, Close::Open) {
927            this.refuse_writes();
928        }
929        let wrapper = this.wrapper();
930        if wrapper.op.send_failed || wrapper.op.read_progress {
931            wrapper.wakers.wake_tasks(false, !wrapper.op.send_failed);
932        }
933        if result.is_pending() {
934            let records = wrapper.op.read_wait;
935            wrapper.wakers.join(cx.waker(), records, generation);
936        }
937        let panic = wrapper.panic.get_mut();
938        if let Some(panic) = panic.unwrap_or_else(PoisonError::into_inner).take() {
939            panic::resume_unwind(panic);
940        }
941        result
942    }
943
944    /// Runs an OpenSSL call until it completes or waits on the transport.
945    fn poll_ssl<T>(
946        &mut self,
947        path: ErrorPath,
948        mut f: impl FnMut(&mut ssl::SslStream<StreamWrapper<S>>) -> Result<T, ssl::Error>,
949    ) -> Poll<Result<T, ssl::Error>> {
950        keeping_foreign_errors(|| {
951            loop {
952                self.inner.get_mut().start_call(path);
953                let error = match f(&mut self.inner) {
954                    Ok(value) => break Poll::Ready(Ok(value)),
955                    Err(error) => error,
956                };
957                match (self.inner.get_ref().call.read, error.code()) {
958                    (LastRead::Waiting, _) => break Poll::Pending,
959                    (LastRead::Failed | LastRead::Eof, _) => break Poll::Ready(Err(error)),
960                    // Call again: OpenSSL handled a record without application data (without
961                    // `SSL_MODE_AUTO_RETRY`), or LibreSSL finished a handshake inside `SSL_write`
962                    // (`WANT_WRITE`, as the BIO never refuses a write).
963                    (LastRead::Data, ErrorCode::WANT_READ | ErrorCode::WANT_WRITE) => {}
964                    (LastRead::Data, _) => break Poll::Ready(Err(error)),
965                }
966            }
967        })
968    }
969
970    /// Sets `SSL_SENT_SHUTDOWN`, which makes OpenSSL refuse every later write.
971    fn refuse_writes(&mut self) {
972        let state = self.inner.get_shutdown();
973        self.inner.set_shutdown(state | ShutdownState::SENT);
974    }
975
976    /// Runs an OpenSSL call that returns transport errors through OpenSSL, as one operation.
977    fn poll_ssl_op<T>(
978        self: Pin<&mut Self>,
979        cx: &mut Context<'_>,
980        f: impl FnMut(&mut ssl::SslStream<StreamWrapper<S>>) -> Result<T, ssl::Error>,
981    ) -> Poll<Result<T, ssl::Error>> {
982        self.poll_op(cx, |this| this.poll_ssl(ErrorPath::ThroughOpenSsl, f))
983    }
984
985    /// Runs an OpenSSL call for `poll_read` or `poll_write`, returning transport errors as they
986    /// were without passing them to OpenSSL, so a transient one leaves the session usable.
987    fn poll_ssl_io<T>(
988        &mut self,
989        f: impl FnMut(&mut ssl::SslStream<StreamWrapper<S>>) -> Result<T, ssl::Error>,
990    ) -> Poll<io::Result<T>> {
991        match self.poll_ssl(ErrorPath::ToCaller, f) {
992            Poll::Ready(result) => Poll::Ready(result.map_err(io::Error::other)),
993            Poll::Pending => match self.wrapper().kept_error() {
994                Some(error) => Poll::Ready(Err(error)),
995                None => Poll::Pending,
996            },
997        }
998    }
999}
1000
1001impl<S> AsyncRead for SslStream<S>
1002where
1003    S: AsyncRead + AsyncWrite + Unpin,
1004{
1005    fn poll_read(
1006        self: Pin<&mut Self>,
1007        cx: &mut Context<'_>,
1008        buf: &mut [u8],
1009    ) -> Poll<io::Result<usize>> {
1010        if buf.is_empty() {
1011            return Poll::Ready(Ok(0));
1012        }
1013        self.poll_op(cx, |this| {
1014            this.poll_ssl_io(|s| match s.ssl_read(buf) {
1015                // Like `Read for ssl::SslStream`, end the stream at `close_notify` (also OpenSSL
1016                // 4's unclean end under `SSL_OP_IGNORE_UNEXPECTED_EOF`), and at the transport's end
1017                // where the library reports it as `SYSCALL` (all but OpenSSL 4, as rust-openssl's
1018                // BIO does not report its end). Not during a handshake, renegotiation included: no
1019                // session was made.
1020                Err(error)
1021                    if (error.code() == ErrorCode::ZERO_RETURN
1022                        || (error.code() == ErrorCode::SYSCALL
1023                            && matches!(s.get_ref().call.read, LastRead::Eof)))
1024                        && handshake_finished(s.ssl()) =>
1025                {
1026                    Ok(0)
1027                }
1028                result => result,
1029            })
1030        })
1031    }
1032}
1033
1034impl<S> AsyncWrite for SslStream<S>
1035where
1036    S: AsyncRead + AsyncWrite + Unpin,
1037{
1038    fn poll_write(
1039        self: Pin<&mut Self>,
1040        cx: &mut Context<'_>,
1041        buf: &[u8],
1042    ) -> Poll<io::Result<usize>> {
1043        if buf.is_empty() {
1044            return Poll::Ready(Ok(0));
1045        }
1046        self.poll_op(cx, |this| {
1047            std::task::ready!(this.wrapper().poll_send(Flush::Requested))?;
1048            let buf = chunk(buf);
1049            // A transport error while sending these bytes is not returned: OpenSSL wrote them,
1050            // and they stay buffered for the next call that sends.
1051            this.poll_ssl_io(|s| s.ssl_write(buf))
1052        })
1053    }
1054
1055    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
1056        self.poll_op(cx, |this| this.wrapper().poll_send(Flush::Always))
1057    }
1058
1059    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
1060        // Close the transport without waiting for the peer's close_notify, as RFC 8446 ยง6.1
1061        // permits, to avoid a half-close deadlock. The peer's data stays readable until the
1062        // transport goes away.
1063        self.poll_op(cx, |this| {
1064            if matches!(this.inner.get_ref().close, Close::Open) {
1065                // What earlier writes left goes first, and failing to send it is an error.
1066                std::task::ready!(this.wrapper().poll_send(Flush::Requested))?;
1067                // `SSL_shutdown` runs once, outside `poll_ssl` (see `Close::Sent`); the first call
1068                // never reads.
1069                this.inner.get_mut().start_call(ErrorPath::ThroughOpenSsl);
1070                let shutdown = keeping_foreign_errors(|| this.inner.shutdown());
1071                let sent = this.inner.get_shutdown().contains(ShutdownState::SENT);
1072                this.inner.get_mut().close = match shutdown {
1073                    Ok(_) if sent => Close::Sent,
1074                    // LibreSSL also reports success without writing close_notify while the
1075                    // handshake is unfinished.
1076                    Ok(_) => Close::NoSession(None),
1077                    Err(error) => {
1078                        let error = error.into_io_error().unwrap_or_else(io::Error::other);
1079                        Close::NoSession(Some(AssertUnwindSafe(error)))
1080                    }
1081                };
1082            }
1083            if matches!(this.inner.get_ref().close, Close::NoSession(_)) {
1084                // A transport that fails to close says so first, and keeps the reason for the
1085                // close that closes it.
1086                std::task::ready!(this.wrapper().poll_close())?;
1087                let reason = match &mut this.wrapper().close {
1088                    Close::NoSession(reason) => reason.take(),
1089                    Close::Open | Close::Sent => None,
1090                };
1091                let error = reason.map_or_else(
1092                    || io::Error::other("no TLS session to close"),
1093                    |AssertUnwindSafe(error)| error,
1094                );
1095                return Poll::Ready(Err(error));
1096            }
1097            match std::task::ready!(this.wrapper().poll_send(Flush::Unflushed)) {
1098                Ok(()) => {}
1099                // No error: ours only acknowledges the peer's close_notify, and the peer may be
1100                // gone.
1101                Err(_) if this.inner.get_shutdown().contains(ShutdownState::RECEIVED) => {}
1102                // The output stays buffered, so that closing again retries.
1103                Err(error) => return Poll::Ready(Err(error)),
1104            }
1105            this.wrapper().poll_close()
1106        })
1107    }
1108}