use crate::event::metrics::aggregate;
use core::fmt;
use s2n_codec::DecoderError;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "thiserror", derive(thiserror::Error))]
pub struct Error {
pub reason: &'static str,
pub code: u8,
}
impl Error {
pub const fn new(code: u8) -> Self {
Self { code, reason: "" }
}
#[must_use]
pub const fn with_reason(mut self, reason: &'static str) -> Self {
self.reason = reason;
self
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if !self.reason.is_empty() {
self.reason.fmt(f)
} else if let Some(description) = self.description() {
description.fmt(f)
} else {
write!(f, "tls::Error({})", self.code)
}
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut d = f.debug_struct("tls::Error");
d.field("code", &self.code);
if let Some(description) = self.description() {
d.field("description", &description);
}
if !self.reason.is_empty() {
d.field("reason", &self.reason);
}
d.finish()
}
}
impl From<DecoderError> for Error {
fn from(_: DecoderError) -> Self {
Self::DECODE_ERROR
}
}
macro_rules! alert_descriptions {
($($name:ident = $value:expr),* $(,)?) => {
impl Error {
pub fn description(&self) -> Option<&'static str> {
match self.code {
$(
$value => Some(stringify!($name)),
)*
_ => None,
}
}
$(
pub const $name: Self = Self::new($value);
)*
}
impl aggregate::AsVariant for Error {
const VARIANTS: &'static [aggregate::info::Variant] = &{
use aggregate::info::{Variant, Str};
const fn count(_v: u64) -> usize {
1
}
const LEN: usize = 1 $(+ count($value))*;
let mut array = [Variant { name: Str::new("\0"), id: 0 }; LEN];
let mut id = 0;
$(
array[id] = Variant {
name: Str::new(concat!("TLS_", stringify!($name), "\0")),
id,
};
id += 1;
)*
array[id] = aggregate::info::Variant {
name: aggregate::info::Str::new("TLS_UNKNOWN_ERROR\0"),
id,
};
array
};
#[inline]
fn variant_idx(&self) -> usize {
let mut idx = 0;
$(
if self.code == $value {
return idx;
}
idx += 1;
)*
idx
}
}
#[test]
fn description_test() {
$(
assert_eq!(&Error::$name.to_string(), stringify!($name));
)*
}
#[test]
#[cfg_attr(miri, ignore)]
fn variants_test() {
use aggregate::AsVariant;
insta::assert_debug_snapshot!(Error::VARIANTS);
let mut seen = std::collections::HashSet::new();
for variant in Error::VARIANTS {
assert!(seen.insert(variant.id));
}
}
};
}
alert_descriptions!(
CLOSE_NOTIFY = 0,
UNEXPECTED_MESSAGE = 10,
BAD_RECORD_MAC = 20,
DECRYPTION_FAILED_RESERVED = 21,
RECORD_OVERFLOW = 22,
DECOMPRESSION_FAILURE_RESERVED = 30,
HANDSHAKE_FAILURE = 40,
NO_CERTIFICATE_RESERVED = 41,
BAD_CERTIFICATE = 42,
UNSUPPORTED_CERTIFICATE = 43,
CERTIFICATE_REVOKED = 44,
CERTIFICATE_EXPIRED = 45,
CERTIFICATE_UNKNOWN = 46,
ILLEGAL_PARAMETER = 47,
UNKNOWN_CA = 48,
ACCESS_DENIED = 49,
DECODE_ERROR = 50,
DECRYPT_ERROR = 51,
EXPORT_RESTRICTION_RESERVED = 60,
PROTOCOL_VERSION = 70,
INSUFFICIENT_SECURITY = 71,
INTERNAL_ERROR = 80,
INAPPROPRIATE_FALLBACK = 86,
USER_CANCELED = 90,
NO_RENEGOTIATION_RESERVED = 100,
MISSING_EXTENSION = 109,
UNSUPPORTED_EXTENSION = 110,
CERTIFICATE_UNOBTAINABLE_RESERVED = 111,
UNRECOGNIZED_NAME = 112,
BAD_CERTIFICATE_STATUS_RESPONSE = 113,
BAD_CERTIFICATE_HASH_VALUE_RESERVED = 114,
UNKNOWN_PSK_IDENTITY = 115,
CERTIFICATE_REQUIRED = 116,
NO_APPLICATION_PROTOCOL = 120,
);