use super::{
error::{TlsBuildError, TlsParseError},
types::{TlsContentType, TlsProtocolVersion},
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TlsRecord<'a> {
bytes: &'a [u8],
}
impl<'a> TlsRecord<'a> {
pub fn parse(bytes: &'a [u8]) -> Result<Self, TlsParseError> {
let length = declared(bytes, 5)?;
Ok(Self {
bytes: &bytes[..length],
})
}
pub fn content_type(&self) -> TlsContentType {
TlsContentType::new(self.bytes[0])
}
pub fn version(&self) -> TlsProtocolVersion {
TlsProtocolVersion::new(u16::from_be_bytes([self.bytes[1], self.bytes[2]]))
}
pub fn fragment(&self) -> &'a [u8] {
&self.bytes[5..]
}
pub fn as_bytes(&self) -> &'a [u8] {
self.bytes
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct TlsRecordMut<'a> {
bytes: &'a mut [u8],
}
impl<'a> TlsRecordMut<'a> {
pub fn parse(bytes: &'a mut [u8]) -> Result<Self, TlsParseError> {
let length = declared(bytes, 5)?;
Ok(Self {
bytes: &mut bytes[..length],
})
}
pub(crate) fn from_validated(bytes: &'a mut [u8]) -> Self {
Self { bytes }
}
pub fn content_type(&self) -> TlsContentType {
TlsContentType::new(self.bytes[0])
}
pub fn set_content_type(&mut self, value: TlsContentType) {
self.bytes[0] = value.raw();
}
pub fn version(&self) -> TlsProtocolVersion {
TlsProtocolVersion::new(u16::from_be_bytes([self.bytes[1], self.bytes[2]]))
}
pub fn set_version(&mut self, value: TlsProtocolVersion) {
self.bytes[1..3].copy_from_slice(&value.raw().to_be_bytes());
}
pub fn fragment(&self) -> &[u8] {
&self.bytes[5..]
}
pub fn fragment_mut(&mut self) -> &mut [u8] {
&mut self.bytes[5..]
}
pub fn as_bytes(&self) -> &[u8] {
self.bytes
}
}
pub struct TlsRecordBuilder<'a, 'b> {
buffer: &'a mut [u8],
content_type: TlsContentType,
version: TlsProtocolVersion,
fragment: &'b [u8],
}
impl<'a, 'b> TlsRecordBuilder<'a, 'b> {
pub fn new(
buffer: &'a mut [u8],
content_type: TlsContentType,
version: TlsProtocolVersion,
fragment: &'b [u8],
) -> Self {
Self {
buffer,
content_type,
version,
fragment,
}
}
pub fn build(self) -> Result<TlsRecordMut<'a>, TlsBuildError> {
if self.fragment.len() > u16::MAX as usize {
return Err(TlsBuildError::LengthTooLarge);
}
let length = 5usize
.checked_add(self.fragment.len())
.ok_or(TlsBuildError::LengthTooLarge)?;
if self.buffer.len() < length {
return Err(TlsBuildError::BufferTooShort {
required: length,
available: self.buffer.len(),
});
}
let bytes = &mut self.buffer[..length];
bytes[0] = self.content_type.raw();
bytes[1..3].copy_from_slice(&self.version.raw().to_be_bytes());
bytes[3..5].copy_from_slice(&(self.fragment.len() as u16).to_be_bytes());
bytes[5..].copy_from_slice(self.fragment);
Ok(TlsRecordMut::from_validated(bytes))
}
}
fn declared(bytes: &[u8], header: usize) -> Result<usize, TlsParseError> {
if bytes.len() < header {
return Err(TlsParseError::Incomplete {
required: header,
available: bytes.len(),
});
}
let length = usize::from(u16::from_be_bytes([bytes[3], bytes[4]]));
let total = header + length;
if bytes.len() < total {
return Err(TlsParseError::Incomplete {
required: total,
available: bytes.len(),
});
}
Ok(total)
}