pim_protocol/
transport_frame.rs1use bytes::{Buf, BufMut, BytesMut};
4
5use pim_core::{FrameCodec, PimError};
6
7use crate::frame_type::FrameType;
8
9pub const MAGIC: u16 = 0x504D;
11
12pub const VERSION: u8 = 1;
14
15pub const MAX_PAYLOAD_SIZE: u32 = 1_048_576;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct TransportFrame {
30 pub frame_type: FrameType,
32 pub nonce: [u8; 12],
34 pub payload: bytes::Bytes,
36 pub tag: [u8; 16],
38}
39
40const HEADER_SIZE: usize = 20;
42const TAG_SIZE: usize = 16;
44
45impl FrameCodec for TransportFrame {
46 fn encode(&self, buf: &mut BytesMut) {
47 buf.put_u16(MAGIC);
48 buf.put_u8(VERSION);
49 buf.put_u8(self.frame_type as u8);
50 buf.put_u32(self.payload.len() as u32);
51 buf.put_slice(&self.nonce);
52 buf.put_slice(&self.payload);
53 buf.put_slice(&self.tag);
54 }
55
56 fn decode(buf: &mut BytesMut) -> Result<Self, PimError> {
57 if buf.len() < HEADER_SIZE {
58 return Err(PimError::Protocol("frame too short for header".into()));
59 }
60
61 let magic = (&buf[0..2]).get_u16();
62 if magic != MAGIC {
63 return Err(PimError::Protocol(format!(
64 "invalid magic: 0x{magic:04X}, expected 0x{MAGIC:04X}"
65 )));
66 }
67
68 let version = buf[2];
69 if version != VERSION {
70 return Err(PimError::Protocol(format!(
71 "unsupported version: {version}, expected {VERSION}"
72 )));
73 }
74
75 let frame_type = FrameType::from_u8(buf[3])?;
76 let length = (&buf[4..8]).get_u32();
77
78 if length > MAX_PAYLOAD_SIZE {
79 return Err(PimError::Protocol(format!(
80 "payload too large: {length} bytes, max {MAX_PAYLOAD_SIZE}"
81 )));
82 }
83
84 let total_size = HEADER_SIZE + length as usize + TAG_SIZE;
85 if buf.len() < total_size {
86 return Err(PimError::Protocol(format!(
87 "frame truncated: need {total_size} bytes, have {}",
88 buf.len()
89 )));
90 }
91
92 let mut nonce = [0u8; 12];
93 nonce.copy_from_slice(&buf[8..20]);
94
95 buf.advance(HEADER_SIZE);
96 let payload = buf.split_to(length as usize).freeze();
97
98 let mut tag = [0u8; 16];
99 tag.copy_from_slice(&buf[0..TAG_SIZE]);
100
101 buf.advance(TAG_SIZE);
102
103 Ok(TransportFrame {
104 frame_type,
105 nonce,
106 payload,
107 tag,
108 })
109 }
110}
111
112#[cfg(test)]
113mod tests;