use alloc::vec::Vec;
use nom::bytes::streaming::take;
use nom::combinator::{complete, map_parser};
use nom::error::{make_error, ErrorKind};
use nom::multi::many1;
use nom::{Err, IResult};
use nom_derive::{NomBE, Parse};
use rusticata_macros::newtype_enum;
use crate::tls_handshake::*;
use crate::tls_message::*;
use crate::TlsVersion;
pub const MAX_RECORD_LEN: u16 = (1 << 14) + 256;
#[derive(Clone, Copy, PartialEq, Eq, NomBE)]
pub struct TlsRecordType(pub u8);
newtype_enum! {
impl debug TlsRecordType {
ChangeCipherSpec = 0x14,
Alert = 0x15,
Handshake = 0x16,
ApplicationData = 0x17,
Heartbeat = 0x18,
}
}
impl From<TlsRecordType> for u8 {
fn from(v: TlsRecordType) -> u8 {
v.0
}
}
#[derive(Clone, Copy, PartialEq, NomBE)]
pub struct TlsRecordHeader {
pub record_type: TlsRecordType,
pub version: TlsVersion,
pub len: u16,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TlsPlaintext<'a> {
pub hdr: TlsRecordHeader,
pub msg: Vec<TlsMessage<'a>>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TlsEncryptedContent<'a> {
pub blob: &'a [u8],
}
#[derive(Clone, Debug, PartialEq)]
pub struct TlsEncrypted<'a> {
pub hdr: TlsRecordHeader,
pub msg: TlsEncryptedContent<'a>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TlsRawRecord<'a> {
pub hdr: TlsRecordHeader,
pub data: &'a [u8],
}
#[inline]
pub fn parse_tls_record_header(i: &[u8]) -> IResult<&[u8], TlsRecordHeader> {
TlsRecordHeader::parse(i)
}
#[rustfmt::skip]
#[allow(clippy::trivially_copy_pass_by_ref)] pub fn parse_tls_record_with_header<'i>(i:&'i [u8], hdr:&TlsRecordHeader ) -> IResult<&'i [u8], Vec<TlsMessage<'i>>> {
match hdr.record_type {
TlsRecordType::ChangeCipherSpec => many1(complete(parse_tls_message_changecipherspec))(i),
TlsRecordType::Alert => many1(complete(parse_tls_message_alert))(i),
TlsRecordType::Handshake => many1(complete(parse_tls_message_handshake))(i),
TlsRecordType::ApplicationData => many1(complete(parse_tls_message_applicationdata))(i),
TlsRecordType::Heartbeat => parse_tls_message_heartbeat(i, hdr.len),
_ => Err(Err::Error(make_error(i, ErrorKind::Switch)))
}
}
pub fn parse_tls_plaintext(i: &[u8]) -> IResult<&[u8], TlsPlaintext> {
let (i, hdr) = parse_tls_record_header(i)?;
if hdr.len > MAX_RECORD_LEN {
return Err(Err::Error(make_error(i, ErrorKind::TooLarge)));
}
let (i, msg) = map_parser(take(hdr.len as usize), |i| {
parse_tls_record_with_header(i, &hdr)
})(i)?;
Ok((i, TlsPlaintext { hdr, msg }))
}
pub fn parse_tls_encrypted(i: &[u8]) -> IResult<&[u8], TlsEncrypted> {
let (i, hdr) = parse_tls_record_header(i)?;
if hdr.len > MAX_RECORD_LEN {
return Err(Err::Error(make_error(i, ErrorKind::TooLarge)));
}
let (i, blob) = take(hdr.len as usize)(i)?;
let msg = TlsEncryptedContent { blob };
Ok((i, TlsEncrypted { hdr, msg }))
}
pub fn parse_tls_raw_record(i: &[u8]) -> IResult<&[u8], TlsRawRecord> {
let (i, hdr) = parse_tls_record_header(i)?;
if hdr.len > MAX_RECORD_LEN {
return Err(Err::Error(make_error(i, ErrorKind::TooLarge)));
}
let (i, data) = take(hdr.len as usize)(i)?;
Ok((i, TlsRawRecord { hdr, data }))
}
#[deprecated(since = "0.5.0", note = "Use parse_tls_plaintext")]
#[inline]
pub fn tls_parser(i: &[u8]) -> IResult<&[u8], TlsPlaintext> {
parse_tls_plaintext(i)
}
pub fn tls_parser_many(i: &[u8]) -> IResult<&[u8], Vec<TlsPlaintext>> {
many1(complete(parse_tls_plaintext))(i)
}