webrtc_sniffer/dtls/
header.rs1use std::fmt;
2
3use nom::{
4 bytes::complete::take,
5 error::{Error, ErrorKind},
6 number::complete::{be_u16, be_u64, be_u8},
7 Err, IResult,
8};
9
10#[repr(u8)]
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum ContentType {
13 ChangeCipherSpec = 20,
14 Alert = 21,
15 Handshake = 22,
16 ApplicationData = 23,
17}
18
19impl ContentType {
20 pub fn from_u8(value: u8) -> Option<Self> {
21 match value {
22 20 => Some(ContentType::ChangeCipherSpec),
23 21 => Some(ContentType::Alert),
24 22 => Some(ContentType::Handshake),
25 23 => Some(ContentType::ApplicationData),
26 _ => None,
27 }
28 }
29}
30
31#[derive(Clone, Copy, Debug, PartialEq, Eq)]
32pub struct Chunk<'a> {
33 pub ty: ContentType,
34 pub epoch: u16,
35 pub sequence_number: u64,
36 pub length: u16,
37 pub body: &'a [u8],
38}
39
40impl fmt::Display for Chunk<'_> {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 let Chunk {
43 ty,
44 epoch,
45 sequence_number: seq,
46 length,
47 ..
48 } = self;
49 write!(
50 f,
51 "{ty:?}, epoch={epoch}, seq={seq:012x}, len={length}, data={}",
52 hex::encode(self.body)
53 )
54 }
55}
56
57impl<'a> Chunk<'a> {
58 pub fn parse(input: &'a [u8]) -> IResult<&'a [u8], Self> {
59 let (input, ty_byte) = be_u8(input)?;
60 let ty = ContentType::from_u8(ty_byte)
61 .ok_or_else(|| Err::Error(Error::new(input, ErrorKind::Alt)))?;
62
63 let (input, legacy_record_version) = be_u16(input)?;
64 if legacy_record_version != 0xFEFD {
65 return Err(Err::Error(Error::new(input, ErrorKind::Alt)));
66 }
67
68 let (input, t) = be_u64(input)?;
69 let epoch = (t >> 48) as u16;
70 let sequence_number = t & ((1 << 48) - 1);
71 let (input, length) = be_u16(input)?;
72 let (input, body) = take(length as usize)(input)?;
73
74 let header = Chunk {
75 ty,
76 epoch,
77 sequence_number,
78 length,
79 body,
80 };
81
82 Ok((input, header))
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn test_parse_header() {
92 let bytes = &[
93 22, 0xFE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00,
99 ];
100
101 let result = Chunk::parse(bytes);
102 let (_, chunk) = result.unwrap();
103
104 assert_eq!(chunk.ty, ContentType::Handshake);
105 assert_eq!(chunk.epoch, 1);
106 assert_eq!(chunk.sequence_number, 1);
107 assert_eq!(chunk.length, 3);
108 assert_eq!(chunk.body, [0; 3]);
109 }
110}