rust: bssl-tls: Introduce shutdown controls Bug: 479599893 Signed-off-by: Xiangfei Ding <xfding@google.com> Change-Id: I129f4aeaac80967ca3c56215c786d2456a6a6964 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/91408 Reviewed-by: Adam Langley <agl@google.com>
diff --git a/rust/bssl-tls/src/connection/lifecycle.rs b/rust/bssl-tls/src/connection/lifecycle.rs index a0ec5bd..1d96fa5 100644 --- a/rust/bssl-tls/src/connection/lifecycle.rs +++ b/rust/bssl-tls/src/connection/lifecycle.rs
@@ -14,6 +14,7 @@ //! TLS Connection lifecycle controls +use alloc::boxed::Box; use core::{ future::poll_fn, ops::{Deref, DerefMut}, @@ -24,9 +25,31 @@ check_tls_error, connection::{Client, Server, TlsConnectionRef, methods::HasTlsConnectionMethod}, context::{SupportedMode, TlsMode}, - errors::{Error, TlsRetryReason}, + errors::{Error, TlsErrorReason, TlsRetryReason}, + io::IoStatus, }; +/// # Connection shutdown +impl<R, M> TlsConnectionRef<R, M> { + /// Set whether shutting down this connection sends out a `close_notify` alert. + pub fn set_quiet_shutdown(&mut self, quiet: bool) -> &mut Self { + unsafe { + // Safety: the validity of the handle `self.0` is witnessed by `self`. + bssl_sys::SSL_set_quiet_shutdown(self.ptr(), if quiet { 1 } else { 0 }); + } + self + } + + /// Check whether shutting down this connection sends out a `close_notify` alert. + pub fn get_quiet_shutdown(&self) -> bool { + let rc = unsafe { + // Safety: the validity of the handle `self.0` is witnessed by `self`. + bssl_sys::SSL_get_quiet_shutdown(self.ptr()) + }; + rc == 1 + } +} + /// # Connection initialisation state /// /// There are methods and accessors that become available only when the connection is in the right @@ -182,6 +205,53 @@ } } +impl<R> EstablishedTlsConnection<'_, R, TlsMode> { + /// Perform synchronising shutdown. + /// + /// # Shutdown protocol + /// A live connection can be actively shut down by calling this method at most two times. + /// The first call will send `close_notify` down the transport. + /// On `Ok` the first call is considered successful with the following return value. + /// - [`ShutdownStatus::CloseNotifyReceived`] signifies that a `close_notify` is received from the peer, too. + /// - [`ShutdownStatus::CloseNotifyPosted`] signifies that a `close_notify` from our end is sent but that from the peer + /// has not arrived. + /// + /// In case of no reception of peer `close_notify`, it is necessary to call this method again. + /// There are two possible outcomes. + /// - [`ShutdownStatus::RemainingApplicationData`] signifies that there are pending application data. + /// Process it until the stream ends. + /// - [`ShutdownStatus::CloseNotifyReceived`] signifies that a `close_notify` is received from the peer, too. + /// The connection is then in terminal state. + /// To process the remaining application data, normal reading should continue until the end of + /// stream, at which [`Self::sync_shutdown`] can be called again to set the connection to the terminal state. + pub fn sync_shutdown(&mut self) -> Result<ShutdownStatus, Error> { + let rc = unsafe { + // Safety: we have exclusive access to the connection state. + bssl_sys::SSL_shutdown(self.ptr()) + }; + if self.is_write_closed() { + return Ok(ShutdownStatus::CloseNotifyReceived); + } + match rc { + 0 => Ok(ShutdownStatus::CloseNotifyPosted), + 1 => Ok(ShutdownStatus::CloseNotifyReceived), + _ => match self.categorise_error_for_io(rc) { + Ok(IoStatus::Ok(_)) => unreachable!(), + Ok(IoStatus::Empty | IoStatus::EndOfStream) => { + Err(Error::Io(crate::errors::IoError::EndOfStream)) + } + Ok(IoStatus::Retry(reason)) => Err(Error::TlsRetry(reason)), + Err(Error::TlsReason(TlsErrorReason::ApplicationDataOnShutdown)) => { + Ok(ShutdownStatus::RemainingApplicationData) + } + Err(Error::Library(0, _, _)) => Ok(ShutdownStatus::CloseNotifyReceived), + Ok(IoStatus::Err) => Err(Error::Unknown(Box::new("transport error".to_string()))), + Err(e) => Err(e), + }, + } + } +} + impl<R, M> TlsConnectionInHandshake<'_, R, M> where M: SupportedMode, @@ -206,3 +276,13 @@ }) } } + +/// Shutdown progress +pub enum ShutdownStatus { + /// `close_notify` has been sent. + CloseNotifyPosted, + /// Peer `close_notify` has been received. The connection is now in terminal state. + CloseNotifyReceived, + /// There are remaining application data. Consume them first before calling `shutdown` again. + RemainingApplicationData, +}