blob: 67bc9ca8b7c968d496c1002b259918f966aebc56 [file]
// 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 transport settings
//!
use core::mem::{MaybeUninit, transmute};
use crate::{
check_lib_error,
check_tls_error,
config::ConfigurationError,
connection::{
TlsConnection,
TlsConnectionBuilder,
methods::HasTlsConnectionMethod, //
},
context::HasBasicIo,
errors::Error,
io::{
AbstractReader,
AbstractSocket,
AbstractWriter,
RustBio, //
}, //
};
/// # Transport configurations
///
/// These are the methods to configure the underlying IO drivers and transport configurations.
impl<R, M> TlsConnection<R, M>
where
M: HasBasicIo + HasTlsConnectionMethod,
{
/// Set up underlying transport driver.
pub fn set_io<S: 'static + AbstractSocket>(&mut self, socket: S) -> Result<&mut Self, Error> {
let bio = RustBio::new_duplex(socket)?;
unsafe {
// Safety: the additional ref-count is to compensate for `SSL` taking ownership.
bssl_sys::BIO_up_ref(bio.ptr());
// Safety: the `bio` pointer has been sanitised and `self.0` is still valid.
bssl_sys::SSL_set_bio(self.ptr(), bio.ptr(), bio.ptr());
}
self.get_connection_methods().bio = Some(bio);
Ok(self)
}
/// Set up underlying transport driver, with a pair of read and write ends.
pub fn set_split_io<Reader, Writer>(
&mut self,
read: Reader,
write: Writer,
) -> Result<&mut Self, Error>
where
Reader: 'static + AbstractReader,
Writer: 'static + AbstractWriter,
{
let bio = RustBio::new_split(read, write)?;
unsafe {
// Safety: the additional ref-count is to compensate for `SSL` taking ownership.
bssl_sys::BIO_up_ref(bio.ptr());
// Safety: the `bio` pointer has been sanitised and `self.0` is still valid.
bssl_sys::SSL_set_bio(self.ptr(), bio.ptr(), bio.ptr());
}
self.get_connection_methods().bio = Some(bio);
Ok(self)
}
/// **For DTLS only**, trigger timeout handling on the connection.
///
/// On success, this method call returns `true` when a timeout is hit and successfully handled;
/// `false` when no timeout has expired yet.
pub fn dtlsv1_handle_timeout(&mut self) -> Result<bool, Error> {
let conn = self.ptr();
let rc = unsafe {
// Safety: the connection handle here is still valid.
bssl_sys::DTLSv1_handle_timeout(conn)
};
if rc == 0 {
return Ok(false);
}
let _ = check_tls_error!(conn, rc);
Ok(true)
}
/// **For DTLS only**, get connection's remaining timeout.
///
/// If a timeout is in effect, this method call returns the remaining seconds,
/// followed by the remaining microseconds.
pub fn dtlsv1_get_timeout(&self) -> Option<(i64, i64)> {
#[cfg(windows)]
#[repr(C)]
struct timeval {
tv_sec: core::ffi::c_long,
tv_usec: core::ffi::c_long,
}
#[cfg(not(windows))]
use bssl_sys::timeval;
let mut timeval = MaybeUninit::<timeval>::zeroed();
let rc = unsafe {
// Safety:
// - the validity of the handle `self.0` is witnessed by `self`;
// - the buffer for timeval is valid.
// - on Windows, the timeval structure should match the winsock2.h definition precisely.
bssl_sys::DTLSv1_get_timeout(self.ptr(), transmute(timeval.as_mut_ptr()))
};
match rc {
1 => {
let timeval = unsafe {
// Safety: timeval is now valid as per BoringSSL specification.
timeval.assume_init()
};
Some((timeval.tv_sec as i64, timeval.tv_usec as i64))
}
0 => None,
rc => {
unreachable!("BoringSSL should never return {rc} when calling dtlsv1_get_timeout")
}
}
}
/// Check if the underlying **transport** has closed its write end.
pub fn is_write_closed(&self) -> bool {
self.get_connection_methods_ref()
.bio
.as_ref()
.map_or(true, |bio| bio.as_ref().write_eos)
}
/// Check if the underlying **transport** has closed its read end.
pub fn is_read_closed(&self) -> bool {
self.get_connection_methods_ref()
.bio
.as_ref()
.map_or(true, |bio| bio.as_ref().read_eos)
}
/// Check if the underlying **transport** has closed either its read end or its write end.
pub fn is_one_side_closed(&self) -> bool {
self.get_connection_methods_ref()
.bio
.as_ref()
.map_or(true, |bio| bio.as_ref().read_eos || bio.as_ref().write_eos)
}
}
/// # Transport configurations
///
/// These are the methods to configure the underlying IO drivers and transport configurations.
impl<R, M> TlsConnectionBuilder<R, M>
where
M: HasTlsConnectionMethod,
{
/// **For DTLS only**, set connection's Maximum Transmission Unit.
pub fn with_mtu(&mut self, mtu: u32) -> Result<&mut Self, Error> {
let conn = self.ptr();
let mtu = mtu
.try_into()
.map_err(|_| Error::Configuration(ConfigurationError::InvalidParameters))?;
check_lib_error!(unsafe {
// Safety: `conn` is still valid.
bssl_sys::SSL_set_mtu(conn, mtu)
});
Ok(self)
}
/// **For DTLS only**, set connection's handshake timeout in milliseconds.
pub fn with_dtlsv1_initial_timeout(&mut self, milliseconds: u32) -> Result<&mut Self, Error> {
unsafe {
// Safety: the validity of the handle `self.0` is witnessed by `self`.
bssl_sys::DTLSv1_set_initial_timeout_duration(self.ptr(), milliseconds);
}
Ok(self)
}
}