| // Copyright 2026 The BoringSSL Authors | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| //! TLS Connection | |
| use alloc::boxed::Box; | |
| use core::{ | |
| marker::PhantomData, | |
| mem::{ | |
| forget, | |
| transmute, // | |
| }, | |
| ptr::NonNull, | |
| task::Waker, // | |
| }; | |
| use crate::{ | |
| config::{ | |
| ConnectionMode, | |
| ProtocolVersion, // | |
| }, | |
| connection::{ | |
| lifecycle::TlsConnectionInHandshake, | |
| methods::waker_data_ref_from_ssl, // | |
| }, | |
| context::TlsMode, | |
| sessions::TlsSession, // | |
| }; | |
| mod alpn; | |
| mod credentials; | |
| pub mod io; | |
| pub mod lifecycle; | |
| pub(crate) mod methods; | |
| pub mod transport; | |
| /// Server role - the connection runs as a server. | |
| pub enum Server {} | |
| /// Client role - the connection runs as a client. | |
| pub enum Client {} | |
| /// A TLS connection builder. | |
| pub struct TlsConnectionBuilder<Role, Mode = TlsMode> { | |
| ptr: NonNull<bssl_sys::SSL>, | |
| _p: PhantomData<fn() -> (Role, Mode)>, | |
| } | |
| unsafe impl<R, M> Send for TlsConnectionBuilder<R, M> {} | |
| impl<R, M> Drop for TlsConnectionBuilder<R, M> { | |
| fn drop(&mut self) { | |
| unsafe { | |
| // Safety: the validity of `self.ptr()` is witnessed by `self`. | |
| bssl_sys::SSL_free(self.ptr()); | |
| } | |
| } | |
| } | |
| impl<R, M> TlsConnectionBuilder<R, M> { | |
| fn ptr(&mut self) -> *mut bssl_sys::SSL { | |
| self.ptr.as_ptr() | |
| } | |
| } | |
| impl<R, M> TlsConnectionBuilder<R, M> | |
| where | |
| M: methods::HasTlsConnectionMethod, | |
| { | |
| /// Finalise the connection, so that a handshake can be run. | |
| pub fn build(self) -> TlsConnection<R, M> { | |
| let ptr = self.ptr; | |
| forget(self); | |
| TlsConnection { | |
| ptr, | |
| _p: PhantomData, | |
| } | |
| } | |
| pub(crate) fn from_ssl(ptr: NonNull<bssl_sys::SSL>) -> Self { | |
| let idx = M::registration(); | |
| let data = Box::into_raw(Box::new(methods::RustConnectionMethods::<M>::new())) as _; | |
| unsafe { | |
| // Safety: | |
| // - the validity of the handle `ptr` is witnessed by `self`. | |
| // - `M::registration` will return a valid ex-data index. | |
| // - `data` should be valid by non-null invariant. | |
| bssl_sys::SSL_set_ex_data(ptr.as_ptr(), idx, data); | |
| // Safety: the validity of the handle `ptr` is witnessed by `self`. | |
| bssl_sys::SSL_set_mode( | |
| ptr.as_ptr(), | |
| ConnectionMode::ACCEPT_MOVING_WRITE_BUFFER.bits(), | |
| ); | |
| } | |
| Self { | |
| ptr, | |
| _p: PhantomData, | |
| } | |
| } | |
| /// Disable session creation. | |
| pub fn disable_session(&mut self) -> &mut Self { | |
| unsafe { | |
| // Safety: the validity of the handle `ptr` is witnessed by `self`. | |
| bssl_sys::SSL_set_mode(self.ptr(), ConnectionMode::MODE_NO_SESSION_CREATION.bits()); | |
| } | |
| self | |
| } | |
| fn in_handshake(&mut self) -> TlsConnectionInHandshake<'_, R, M> { | |
| unsafe { | |
| // Safety: | |
| // - the connection is still technically in handshake phase, so it is safe for internal | |
| // use to configure the handshake through the associated methods. | |
| // - `TlsConnection` is a transparent wrapper around `NonNull<bssl_sys::SSL>`. | |
| // - the `Role` and `Mode` are matching. | |
| TlsConnectionInHandshake(transmute(&mut self.ptr)) | |
| } | |
| } | |
| /// Set the session for resumption. | |
| pub fn with_session(&mut self, session: &TlsSession) -> &mut Self { | |
| unsafe { | |
| // Safety: self.ptr and session.0 are valid. | |
| bssl_sys::SSL_set_session(self.ptr(), session.ptr()); | |
| } | |
| self | |
| } | |
| pub(crate) fn as_in_handshake(&mut self) -> lifecycle::TlsConnectionInHandshake<'_, R, M> { | |
| unsafe { | |
| // Safety: `TlsConnectionBuilder` and `TlsConnection` have identical memory layout. | |
| // Both are wrappers around `NonNull<bssl_sys::SSL>`. | |
| lifecycle::TlsConnectionInHandshake(core::mem::transmute(self)) | |
| } | |
| } | |
| } | |
| /// TLS Connection | |
| /// | |
| /// `Role` is expected to be either [`crate::context::Server`] or [`crate::context::Client`] and | |
| /// `Mode` is expected to be either [`crate::context::TlsMode`] or [`crate::context::QuicMode`]. | |
| /// These generics will govern the capabilities that respective TLS connection role can access, | |
| // NOTE: any method that involves I/O must require exclusive access, enforced by requiring `&mut`. | |
| #[repr(transparent)] | |
| pub struct TlsConnection<Role, Mode = TlsMode> { | |
| ptr: NonNull<bssl_sys::SSL>, | |
| _p: PhantomData<fn() -> (Role, Mode)>, | |
| } | |
| /// Safety: all internal states of `bssl_sys::SSL` that this connection are either exclusively owned | |
| /// or reference-counted but immutable, including `SSL_SESSION`s and `SSL_CTX`s. | |
| unsafe impl<R, M> Send for TlsConnection<R, M> {} | |
| impl<R, M> Drop for TlsConnection<R, M> { | |
| fn drop(&mut self) { | |
| unsafe { | |
| // Safety: the connection handle is still valid. | |
| bssl_sys::SSL_free(self.ptr()); | |
| } | |
| } | |
| } | |
| impl<R, M> TlsConnection<R, M> { | |
| /// Get a handle of the connection object. | |
| /// | |
| /// # Safety | |
| /// - `self` must outlive all uses of the returned handle; | |
| /// - this handle must be used with functions from the BoringSSL library this crate is linked | |
| /// to; otherwise, it is **undefined behaviour**. | |
| pub unsafe fn as_mut_ptr(&self) -> *mut bssl_sys::SSL { | |
| self.ptr.as_ptr() | |
| } | |
| /// Reconstitute a borrow of the connection object. | |
| /// | |
| /// # Safety | |
| /// - the handle `ptr` must be sourced originally from [`TlsConnection::as_mut_ptr`] | |
| /// of this crate. | |
| /// - the handle `*ptr` must not be aliased and accessed across threads until the borrow is | |
| /// expired; otherwise, it is **undefined behaviour**. | |
| pub unsafe fn from_mut_ptr(ptr: &mut *mut bssl_sys::SSL) -> &mut Self { | |
| unsafe { | |
| // Safety: `TlsConnection` is a thin wrapper around `*mut bssl_sys::SSL` | |
| core::mem::transmute(ptr) | |
| } | |
| } | |
| } | |
| // TODO(@xfding): there seems to be some type inference regression, drop the turbofish when it is | |
| // resolved. | |
| impl<R, M> TlsConnection<R, M> | |
| where | |
| M: methods::HasTlsConnectionMethod, | |
| { | |
| fn get_connection_methods(&mut self) -> &mut methods::RustConnectionMethods<M> { | |
| unsafe { | |
| // Safety: the validity of the handle `self.0` is witnessed by `self`. | |
| get_connection_methods::<M>(self.ptr()) | |
| } | |
| } | |
| fn get_connection_methods_ref(&self) -> &methods::RustConnectionMethods<M> { | |
| unsafe { | |
| // Safety: the validity of the handle `self.0` is witnessed by `self`. | |
| get_connection_methods_ref::<M>(self.ptr()) | |
| } | |
| } | |
| } | |
| impl<R, M> TlsConnection<R, M> | |
| where | |
| M: methods::HasTlsConnectionMethod, | |
| { | |
| /// This method sets up waker for BIO and various handlers. | |
| pub(crate) fn set_waker(&mut self, waker: &Waker) { | |
| let waker_data = unsafe { | |
| // Safety: | |
| // - `self.ptr()` is a valid `SSL` handle created from `from_ssl` in this crate. | |
| // - The data was previously initialized as `Box<Option<Waker>>`. | |
| waker_data_ref_from_ssl(self.ptr) | |
| }; | |
| if let Some(other_waker) = waker_data | |
| && waker.will_wake(other_waker) | |
| { | |
| // We do not have to put this waker in the mailbox. | |
| } else { | |
| *waker_data = Some(waker.clone()); | |
| } | |
| self.get_connection_methods().set_waker(waker); | |
| } | |
| } | |
| /// Safety: | |
| /// - caller must pass in the SSL handle constructed by this crate. | |
| /// - the handle must outlives `'a` for exclusive access. | |
| unsafe fn get_connection_methods<'a, M: methods::HasTlsConnectionMethod>( | |
| ssl: *mut bssl_sys::SSL, | |
| ) -> &'a mut methods::RustConnectionMethods<M> { | |
| let methods = unsafe { | |
| // Safety: this instance of SSL should be constructed by this crate. | |
| bssl_sys::SSL_get_ex_data(ssl, M::registration()) | |
| }; | |
| if methods.is_null() { | |
| panic!("context method is missing") | |
| } | |
| unsafe { | |
| // Safety: `methods` must be constructed by `new_inner` | |
| &mut *(methods as *mut _) | |
| } | |
| } | |
| /// Safety: | |
| /// - caller must pass in the SSL handle constructed by this crate. | |
| /// - the handle must outlives `'a` for shared access. | |
| unsafe fn get_connection_methods_ref<'a, M: methods::HasTlsConnectionMethod>( | |
| ssl: *mut bssl_sys::SSL, | |
| ) -> &'a methods::RustConnectionMethods<M> { | |
| let methods = unsafe { | |
| // Safety: this instance of SSL should be constructed by this crate. | |
| bssl_sys::SSL_get_ex_data(ssl, M::registration()) | |
| }; | |
| if methods.is_null() { | |
| panic!("connection method is missing") | |
| } | |
| unsafe { | |
| // Safety: `methods` must be constructed by `new_inner` | |
| &*(methods as *const _) | |
| } | |
| } | |
| impl<R, M> TlsConnection<R, M> { | |
| pub(crate) fn ptr(&self) -> *mut bssl_sys::SSL { | |
| self.ptr.as_ptr() | |
| } | |
| /// Check if the connection is DTLS. | |
| pub fn is_dtls(&self) -> bool { | |
| unsafe { | |
| // Safety: the SSL handle is still valid witnessed by self | |
| bssl_sys::SSL_is_dtls(self.ptr()) == 1 | |
| } | |
| } | |
| /// Check if the connection is server. | |
| /// | |
| /// This method returns `false` if it is a client connection. | |
| pub fn is_server(&self) -> bool { | |
| unsafe { | |
| // Safety: the SSL handle is still valid witnessed by self | |
| bssl_sys::SSL_is_server(self.ptr()) == 1 | |
| } | |
| } | |
| /// Check if a handshake is underway on this connection. | |
| pub fn is_in_handshake(&self) -> bool { | |
| unsafe { | |
| // Safety: the SSL handle is still valid witnessed by self | |
| bssl_sys::SSL_in_init(self.ptr()) == 1 | |
| } | |
| } | |
| /// Get the protocol version of this TLS connection. | |
| /// | |
| /// This method call returns `None` if the connection has not completed handshake. | |
| pub fn get_protocol_version(&self) -> Option<ProtocolVersion> { | |
| if self.is_in_handshake() { | |
| return None; | |
| } | |
| let ret = unsafe { | |
| // Safety: | |
| // - the handle is still valid witnessed by self; | |
| // - the connection has completed handshake. | |
| bssl_sys::SSL_version(self.ptr()) | |
| }; | |
| ret.try_into().ok() | |
| } | |
| } |