internet 0.1.0

Network library for rust
Documentation
use crate::BufError;
use crate::Codec;
use crate::Cursor;
use crate::{Buf, BufMut, BufResult};

/// An alert level following [Section B.2].
///
/// [Section B.2]: https://datatracker.ietf.org/doc/html/rfc9846#appendix-B.2
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum AlertLevel {
    /// warning(1)
    Warning = 1,
    /// fatal(2)
    Fatal = 2,
}

impl Codec for AlertLevel {
    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
        (*self as u8).encode(writer, ())
    }

    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
        match u8::decode(reader, ())? {
            x if x == (Self::Warning as u8) => Ok(Self::Warning),
            x if x == (Self::Fatal as u8) => Ok(Self::Fatal),
            _ => Err(BufError::UnexpectedValue),
        }
    }
}

/// An alert description following [Section B.2].
///
/// [Section B.2]: https://datatracker.ietf.org/doc/html/rfc9846#appendix-B.2
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum AlertDescription {
    /// close_notify(0)
    CloseNotify = 0,
    /// unexpected_message(10)
    UnexpectedMessage = 10,
    /// bad_record_mac(20)
    BadRecordMac = 20,
    /// record_overflow(22)
    RecordOverflow = 22,
    /// handshake_failure(40)
    HandshakeFailure = 40,
    /// bad_certificate(42)
    BadCertificate = 42,
    /// unsupported_certificate(43)
    UnsupportedCertificate = 43,
    /// certificate_revoked(44)
    CertificateRevoked = 44,
    /// certificate_expired(45)
    CertificateExpired = 45,
    /// certificate_unknown(46)
    CertificateUnknown = 46,
    /// illegal_parameter(47)
    IllegalParameter = 47,
    /// unknown_ca(48)
    UnknownCa = 48,
    /// access_denied(49)
    AccessDenied = 49,
    /// decode_error(50)
    DecodeError = 50,
    /// decrypt_error(51)
    DecryptError = 51,
    /// export_restriction(60)
    ExportRestriction = 60,
    /// protocol_version(70)
    ProtocolVersion = 70,
    /// insufficient_security(71)
    InsufficientSecurity = 71,
    /// internal_error(80)
    InternalError = 80,
    /// inappropriate_fallback(86)
    InappropriateFallback = 86,
    /// user_canceled(90)
    UserCanceled = 90,
    /// missing_extension(109)
    MissingExtension = 109,
    /// unsupported_extension(110)
    UnsupportedExtension = 110,
    /// unrecognized_name(112)
    UnrecognizedName = 112,
    /// bad_certificate_status_response(113)
    BadCertificateStatusResponse = 113,
    /// unknown_psk_identity(115)
    UnknownPskIdentity = 115,
    /// certificate_required(116)
    CertificateRequired = 116,
    /// no_application_protocol(120)
    NoApplicationProtocol = 120,
}

impl Codec for AlertDescription {
    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
        (*self as u8).encode(writer, ())
    }

    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
        match u8::decode(reader, ())? {
            x if x == (Self::CloseNotify as u8) => Ok(Self::CloseNotify),
            x if x == (Self::UnexpectedMessage as u8) => Ok(Self::UnexpectedMessage),
            x if x == (Self::BadRecordMac as u8) => Ok(Self::BadRecordMac),
            x if x == (Self::RecordOverflow as u8) => Ok(Self::RecordOverflow),
            x if x == (Self::HandshakeFailure as u8) => Ok(Self::HandshakeFailure),
            x if x == (Self::BadCertificate as u8) => Ok(Self::BadCertificate),
            x if x == (Self::UnsupportedCertificate as u8) => Ok(Self::UnsupportedCertificate),
            x if x == (Self::CertificateRevoked as u8) => Ok(Self::CertificateRevoked),
            x if x == (Self::CertificateExpired as u8) => Ok(Self::CertificateExpired),
            x if x == (Self::CertificateUnknown as u8) => Ok(Self::CertificateUnknown),
            x if x == (Self::IllegalParameter as u8) => Ok(Self::IllegalParameter),
            x if x == (Self::UnknownCa as u8) => Ok(Self::UnknownCa),
            x if x == (Self::AccessDenied as u8) => Ok(Self::AccessDenied),
            x if x == (Self::DecodeError as u8) => Ok(Self::DecodeError),
            x if x == (Self::DecryptError as u8) => Ok(Self::DecryptError),
            x if x == (Self::ExportRestriction as u8) => Ok(Self::ExportRestriction),
            x if x == (Self::ProtocolVersion as u8) => Ok(Self::ProtocolVersion),
            x if x == (Self::InsufficientSecurity as u8) => Ok(Self::InsufficientSecurity),
            x if x == (Self::InternalError as u8) => Ok(Self::InternalError),
            x if x == (Self::UserCanceled as u8) => Ok(Self::UserCanceled),
            x if x == (Self::MissingExtension as u8) => Ok(Self::MissingExtension),
            x if x == (Self::UnsupportedExtension as u8) => Ok(Self::UnsupportedExtension),
            x if x == (Self::UnrecognizedName as u8) => Ok(Self::UnrecognizedName),
            x if x == (Self::BadCertificateStatusResponse as u8) => {
                Ok(Self::BadCertificateStatusResponse)
            }
            x if x == (Self::UnknownPskIdentity as u8) => Ok(Self::UnknownPskIdentity),
            x if x == (Self::CertificateRequired as u8) => Ok(Self::CertificateRequired),
            x if x == (Self::NoApplicationProtocol as u8) => Ok(Self::NoApplicationProtocol),
            _ => Err(BufError::UnexpectedValue),
        }
    }
}

/// An alert following [Section B.2].
///
/// [Section B.2]: https://datatracker.ietf.org/doc/html/rfc9846#appendix-B.2
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Alert {
    /// The level of the alert.
    pub level: AlertLevel,
    /// The description of the alert.
    pub description: AlertDescription,
}

impl Codec for Alert {
    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
        self.level.encode(writer, ())?;
        self.description.encode(writer, ())
    }

    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
        let level = AlertLevel::decode(reader, ())?;
        let description = AlertDescription::decode(reader, ())?;
        Ok(Self { level, description })
    }
}

#[cfg(test)]
mod tests {
    use core::fmt::Debug;

    use super::{Alert, AlertDescription, AlertLevel};
    use crate::{Codec, Cursor};

    fn codec_roundtrip<T: Codec<C> + Debug + Eq, C: Copy>(
        etalon_struct: T,
        etalon_bytes: &[u8],
        context: C,
    ) {
        let mut encoded_bytes = vec![];
        {
            let writer = &mut Cursor::new(&mut encoded_bytes);
            etalon_struct.encode(writer, context).unwrap();
        }
        assert_eq!(etalon_bytes, &encoded_bytes);

        let decoded_struct = {
            let reader = &mut Cursor::new(&mut encoded_bytes);
            T::decode(reader, context).unwrap()
        };
        assert_eq!(etalon_struct, decoded_struct);

        encoded_bytes.fill(0x00);
        {
            let writer = &mut Cursor::new(&mut encoded_bytes);
            decoded_struct.encode(writer, context).unwrap();
        }
        assert_eq!(etalon_bytes, &encoded_bytes);
    }

    #[test]
    fn alert() {
        let etalon_bytes = &[0x02, 0x14]; // fatal, bad_record_mac
        let etalon_struct = Alert {
            level: AlertLevel::Fatal,
            description: AlertDescription::BadRecordMac,
        };

        codec_roundtrip(etalon_struct, etalon_bytes, ());
    }
}