rustmod_core/frame/
tcp.rs1use crate::encoding::{Reader, Writer};
2use crate::{DecodeError, EncodeError};
3
4pub const MBAP_HEADER_LEN: usize = 7;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct MbapHeader {
8 pub transaction_id: u16,
9 pub protocol_id: u16,
10 pub length: u16,
12 pub unit_id: u8,
13}
14
15impl MbapHeader {
16 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
17 w.write_be_u16(self.transaction_id)?;
18 w.write_be_u16(self.protocol_id)?;
19 w.write_be_u16(self.length)?;
20 w.write_u8(self.unit_id)?;
21 Ok(())
22 }
23
24 pub fn decode(r: &mut Reader<'_>) -> Result<Self, DecodeError> {
25 let transaction_id = r.read_be_u16()?;
26 let protocol_id = r.read_be_u16()?;
27 let length = r.read_be_u16()?;
28 let unit_id = r.read_u8()?;
29
30 if protocol_id != 0 {
31 return Err(DecodeError::InvalidValue);
32 }
33 if length < 1 {
34 return Err(DecodeError::InvalidLength);
35 }
36
37 Ok(Self {
38 transaction_id,
39 protocol_id,
40 length,
41 unit_id,
42 })
43 }
44}
45
46pub fn encode_frame(
47 w: &mut Writer<'_>,
48 transaction_id: u16,
49 unit_id: u8,
50 pdu: &[u8],
51) -> Result<(), EncodeError> {
52 let pdu_len_u16: u16 = pdu
53 .len()
54 .try_into()
55 .map_err(|_| EncodeError::ValueOutOfRange)?;
56 let length = pdu_len_u16
57 .checked_add(1)
58 .ok_or(EncodeError::ValueOutOfRange)?;
59
60 let header = MbapHeader {
61 transaction_id,
62 protocol_id: 0,
63 length,
64 unit_id,
65 };
66 header.encode(w)?;
67 w.write_all(pdu)?;
68 Ok(())
69}
70
71pub fn decode_frame<'a>(r: &mut Reader<'a>) -> Result<(MbapHeader, &'a [u8]), DecodeError> {
72 let header = MbapHeader::decode(r)?;
73 let pdu_len = usize::from(header.length - 1);
74 let pdu = r.read_exact(pdu_len)?;
75 Ok((header, pdu))
76}
77
78#[cfg(test)]
79mod tests {
80 use super::{decode_frame, encode_frame, MbapHeader};
81 use crate::encoding::{Reader, Writer};
82 use crate::DecodeError;
83
84 #[test]
85 fn mbap_roundtrip() {
86 let mut buf = [0u8; 32];
87 let mut w = Writer::new(&mut buf);
88 encode_frame(&mut w, 1, 2, &[0x03, 0x00, 0x6B, 0x00, 0x03]).unwrap();
89
90 let mut r = Reader::new(w.as_written());
91 let (header, pdu) = decode_frame(&mut r).unwrap();
92 assert_eq!(
93 header,
94 MbapHeader {
95 transaction_id: 1,
96 protocol_id: 0,
97 length: 6,
98 unit_id: 2,
99 }
100 );
101 assert_eq!(pdu, &[0x03, 0x00, 0x6B, 0x00, 0x03]);
102 }
103
104 #[test]
105 fn rejects_non_zero_protocol_id() {
106 let bytes = [0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x01, 0x03];
107 let mut r = Reader::new(&bytes);
108 assert_eq!(decode_frame(&mut r).unwrap_err(), DecodeError::InvalidValue);
109 }
110}