use super::{put_u8, put_u16};
use crate::tls::{ContentType, Error, ProtocolVersion};
use alloc::vec::Vec;
pub(crate) const MAX_FRAGMENT: usize = (1 << 14) + 256;
pub(crate) struct ParsedRecord<'a> {
pub(crate) content_type: ContentType,
pub(crate) version: u16,
pub(crate) fragment: &'a [u8],
pub(crate) len: usize,
}
pub(crate) fn read_record(buf: &[u8]) -> Result<Option<ParsedRecord<'_>>, Error> {
if buf.len() < 5 {
return Ok(None);
}
let content_type = ContentType::from_u8(buf[0]);
let version = u16::from_be_bytes([buf[1], buf[2]]);
let len = u16::from_be_bytes([buf[3], buf[4]]) as usize;
if len > MAX_FRAGMENT {
return Err(Error::Decode);
}
let total = 5 + len;
if buf.len() < total {
return Ok(None);
}
Ok(Some(ParsedRecord {
content_type,
version,
fragment: &buf[5..total],
len: total,
}))
}
pub(crate) fn is_legal_record_version(version: u16) -> bool {
#[cfg(feature = "tls-legacy")]
{
matches!(version, 0x0300..=0x0303)
}
#[cfg(not(feature = "tls-legacy"))]
{
matches!(version, 0x0301..=0x0303)
}
}
pub(crate) fn write_record(
out: &mut Vec<u8>,
ct: ContentType,
version: ProtocolVersion,
fragment: &[u8],
) {
put_u8(out, ct.as_u8());
put_u16(out, version.as_u16());
put_u16(out, fragment.len() as u16);
out.extend_from_slice(fragment);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn record_roundtrip_and_partial() {
let mut out = Vec::new();
write_record(
&mut out,
ContentType::Handshake,
ProtocolVersion::TLSv1_2,
b"hello",
);
assert_eq!(out[0], 22); assert_eq!(&out[1..3], &[0x03, 0x03]); assert_eq!(&out[3..5], &[0x00, 0x05]);
let rec = read_record(&out).unwrap().unwrap();
assert_eq!(rec.content_type, ContentType::Handshake);
assert_eq!(rec.version, 0x0303);
assert_eq!(rec.fragment, b"hello");
assert_eq!(rec.len, out.len());
assert!(read_record(&out[..4]).unwrap().is_none());
assert!(read_record(&out[..7]).unwrap().is_none());
}
#[test]
fn record_version_filter() {
assert!(is_legal_record_version(0x0301));
assert!(is_legal_record_version(0x0302));
assert!(is_legal_record_version(0x0303));
#[cfg(feature = "tls-legacy")]
assert!(is_legal_record_version(0x0300));
#[cfg(not(feature = "tls-legacy"))]
assert!(!is_legal_record_version(0x0300));
assert!(!is_legal_record_version(0x0200));
assert!(!is_legal_record_version(0x0304));
assert!(!is_legal_record_version(0xFFFF));
}
}