use std::convert::Infallible;
use std::fmt::{self, Debug};
use std::io;
use crate::tls::{AlertDescription, ProtocolVersion};
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
Tls(Box<dyn std::error::Error + Send + Sync>),
Ulp(io::Error),
UnsupportedProtocolVersion(ProtocolVersion),
CryptoMaterial(io::Error),
InvalidMessage(InvalidMessage),
PeerMisbehaved(PeerMisbehaved),
KeyUpdateFailed(io::Error),
HandleNewSessionTicketFailed(io::Error),
AlertReceived(AlertDescription),
General(io::Error),
}
impl Error {
#[must_use]
pub fn is_ktls_unsupported(&self) -> bool {
matches!(self, Self::Ulp(e) if e.raw_os_error() == Some(libc::ENOENT))
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Tls(e) => {
write!(f, "TLS error: {e}")
}
Self::Ulp(e) => {
write!(f, "Failed to set TLS ULP: {e}")
}
Self::UnsupportedProtocolVersion(v) => {
write!(f, "The given TLS protocol version is not supported: {v:?}")
}
Self::CryptoMaterial(e) => {
write!(
f,
"The given crypto material is not supported by the running kernel: {e}"
)
}
Self::InvalidMessage(e) => write!(f, "Invalid TLS message: {e:?}"),
Self::PeerMisbehaved(e) => write!(f, "The peer misbehaved: {e:?}"),
Self::KeyUpdateFailed(e) => write!(f, "Key update failed: {e}"),
Self::HandleNewSessionTicketFailed(e) => {
write!(f, "Handling NewSessionTicket failed: {e}")
}
Self::AlertReceived(a) => write!(f, "Received fatal alert from the peer: {a:?}"),
Self::General(e) => write!(f, "{e}"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
#[allow(clippy::match_same_arms)]
match self {
Self::Tls(e) => Some(&**e),
Self::Ulp(e) => Some(e),
Self::CryptoMaterial(e) => Some(e),
Self::KeyUpdateFailed(e) => Some(e),
Self::HandleNewSessionTicketFailed(e) => Some(e),
Self::General(e) => Some(e),
_ => None,
}
}
}
impl From<InvalidMessage> for Error {
fn from(error: InvalidMessage) -> Self {
Self::InvalidMessage(error)
}
}
impl From<PeerMisbehaved> for Error {
fn from(error: PeerMisbehaved) -> Self {
Self::PeerMisbehaved(error)
}
}
impl From<Infallible> for Error {
fn from(_: Infallible) -> Self {
unreachable!()
}
}
impl From<Error> for io::Error {
fn from(error: Error) -> Self {
match error {
Error::General(error) => error,
_ => Self::other(error),
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum InvalidMessage {
InvalidContentType,
InvalidKeyUpdate,
MessageTooLarge,
MessageTooShort,
UnexpectedMessage(&'static str),
}
impl InvalidMessage {
pub(crate) const fn description(&self) -> AlertDescription {
match self {
Self::InvalidContentType | Self::UnexpectedMessage(_) => {
AlertDescription::UnexpectedMessage
}
_ => AlertDescription::DecodeError,
}
}
}
#[allow(missing_docs)]
#[non_exhaustive]
#[derive(Debug, PartialEq, Clone)]
pub enum PeerMisbehaved {
IllegalMiddleboxChangeCipherSpec,
KeyEpochWithPendingFragment,
}
impl PeerMisbehaved {
pub(crate) const fn description(&self) -> AlertDescription {
match self {
Self::KeyEpochWithPendingFragment | Self::IllegalMiddleboxChangeCipherSpec => {
AlertDescription::UnexpectedMessage
}
#[allow(unreachable_patterns)]
_ => AlertDescription::InternalError,
}
}
}