use std::fmt;
use crate::info::ConnectionInfo;
pub trait TlsAcceptor: Send + Sync + 'static {
type Session: TlsSession;
fn session(&self, info: &ConnectionInfo) -> Result<Self::Session, TlsError>;
}
pub trait TlsSession: Send + 'static {
fn step<'i>(
&mut self,
input: &'i mut [u8],
output: &mut [u8],
) -> Result<TlsProgress<'i>, TlsError>;
fn encrypt(&mut self, plaintext: &[u8], output: &mut [u8]) -> Result<usize, TlsError>;
fn close(&mut self, output: &mut [u8]) -> Result<usize, TlsError>;
fn on_transport_closed(&mut self) -> TlsCloseKind;
fn info(&self) -> Option<TlsInfo>;
fn input_capacity_required(&self) -> usize;
}
#[must_use]
#[derive(Debug)]
pub struct TlsProgress<'i> {
pub consumed: usize,
pub written: usize,
pub event: TlsEvent<'i>,
}
#[non_exhaustive]
#[derive(Debug)]
pub enum TlsEvent<'i> {
NeedInput,
Transmit,
HandshakeComplete,
Data(&'i [u8]),
OutputFull,
PeerClosed,
Closed,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TlsCloseKind {
Clean,
Unclean,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TlsFailure {
Handshake,
HelloRefused,
Capacity,
Protocol,
OutputFull,
State,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TlsError {
pub kind: TlsFailure,
pub code: u16,
pub alert: Option<u8>,
}
impl TlsError {
#[must_use]
pub const fn new(kind: TlsFailure, code: u16, alert: Option<u8>) -> TlsError {
TlsError { kind, code, alert }
}
}
impl fmt::Display for TlsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?} (engine code {}", self.kind, self.code)?;
if let Some(alert) = self.alert {
write!(f, ", alert {alert}")?;
}
f.write_str(")")
}
}
impl std::error::Error for TlsError {}
#[non_exhaustive]
#[derive(Clone, Copy)]
pub struct TlsInfo {
sni: [u8; 255],
sni_len: u8,
alpn: [u8; 32],
alpn_len: u8,
version: u16,
cipher_suite: u16,
}
impl TlsInfo {
#[must_use]
pub fn new(
sni: Option<&str>,
alpn: Option<&[u8]>,
version: u16,
cipher_suite: u16,
) -> Option<TlsInfo> {
let sni = sni.unwrap_or("").as_bytes();
let alpn = alpn.unwrap_or(&[]);
if sni.len() > 255 || alpn.len() > 32 {
return None;
}
let mut info = TlsInfo {
sni: [0; 255],
sni_len: u8::try_from(sni.len()).ok()?,
alpn: [0; 32],
alpn_len: u8::try_from(alpn.len()).ok()?,
version,
cipher_suite,
};
info.sni[..sni.len()].copy_from_slice(sni);
info.alpn[..alpn.len()].copy_from_slice(alpn);
Some(info)
}
#[must_use]
pub fn sni(&self) -> Option<&str> {
if self.sni_len == 0 {
return None;
}
std::str::from_utf8(&self.sni[..usize::from(self.sni_len)]).ok()
}
#[must_use]
pub fn alpn(&self) -> Option<&[u8]> {
(self.alpn_len > 0).then(|| &self.alpn[..usize::from(self.alpn_len)])
}
#[must_use]
pub fn version(&self) -> u16 {
self.version
}
#[must_use]
pub fn cipher_suite(&self) -> u16 {
self.cipher_suite
}
}
impl fmt::Debug for TlsInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TlsInfo")
.field("sni", &self.sni())
.field("alpn", &self.alpn().map(String::from_utf8_lossy))
.field("version", &format_args!("{:#06x}", self.version))
.field("cipher_suite", &format_args!("{:#06x}", self.cipher_suite))
.finish()
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct NoTls;
#[cfg_attr(not(feature = "tls"), allow(dead_code))]
pub(crate) trait ErasedAcceptor: Send + Sync {
fn session_boxed(&self, info: &ConnectionInfo) -> Result<Box<dyn TlsSession>, TlsError>;
}
impl<A: TlsAcceptor> ErasedAcceptor for A {
fn session_boxed(&self, info: &ConnectionInfo) -> Result<Box<dyn TlsSession>, TlsError> {
self.session(info)
.map(|s| Box::new(s) as Box<dyn TlsSession>)
}
}
#[derive(Debug)]
pub enum NoTlsSession {}
impl TlsAcceptor for NoTls {
type Session = NoTlsSession;
fn session(&self, _info: &ConnectionInfo) -> Result<NoTlsSession, TlsError> {
Err(TlsError::new(TlsFailure::State, 0, None))
}
}
impl TlsSession for NoTlsSession {
fn step<'i>(
&mut self,
_input: &'i mut [u8],
_output: &mut [u8],
) -> Result<TlsProgress<'i>, TlsError> {
match *self {}
}
fn encrypt(&mut self, _plaintext: &[u8], _output: &mut [u8]) -> Result<usize, TlsError> {
match *self {}
}
fn close(&mut self, _output: &mut [u8]) -> Result<usize, TlsError> {
match *self {}
}
fn on_transport_closed(&mut self) -> TlsCloseKind {
match *self {}
}
fn info(&self) -> Option<TlsInfo> {
match *self {}
}
fn input_capacity_required(&self) -> usize {
match *self {}
}
}