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