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}