Skip to main content

tailtalk_packets/
ddp.rs

1use byteorder::{BigEndian, ByteOrder};
2use thiserror::Error;
3
4#[derive(Error, Debug)]
5pub enum DdpError {
6    #[error("invalid size - expected {expected:?} bytes but found {found:?}")]
7    InvalidSize { expected: usize, found: usize },
8    #[error("unknown header type - expected 1 or 2, but found {header:?}")]
9    UnknownHeader { header: u8 },
10    #[error("unknown protocol type {found:?}")]
11    UnknownProtocol { found: u8 },
12}
13
14const RTMP_RESPONSE: u8 = 1;
15const NBP: u8 = 2;
16const ATP: u8 = 3;
17const AEP: u8 = 4;
18const RTMP_REQUEST: u8 = 5;
19const ZIP: u8 = 6;
20const ADSP: u8 = 7;
21
22#[repr(u8)]
23#[derive(Debug, Copy, Clone, PartialEq, Eq)]
24pub enum DdpProtocolType {
25    RtmpResponse = RTMP_RESPONSE,
26    Nbp = NBP,
27    Atp = ATP,
28    Aep = AEP,
29    RtmpRequest = RTMP_REQUEST,
30    Zip = ZIP,
31    Adsp = ADSP,
32}
33
34impl TryFrom<u8> for DdpProtocolType {
35    type Error = DdpError;
36
37    fn try_from(data: u8) -> Result<Self, Self::Error> {
38        match data {
39            RTMP_RESPONSE => Ok(DdpProtocolType::RtmpResponse),
40            NBP => Ok(DdpProtocolType::Nbp),
41            ATP => Ok(DdpProtocolType::Atp),
42            AEP => Ok(DdpProtocolType::Aep),
43            RTMP_REQUEST => Ok(DdpProtocolType::RtmpRequest),
44            ZIP => Ok(DdpProtocolType::Zip),
45            ADSP => Ok(DdpProtocolType::Adsp),
46            _ => Err(DdpError::UnknownProtocol { found: data }),
47        }
48    }
49}
50
51#[derive(Debug, Clone)]
52pub struct DdpPacket {
53    pub hop_count: u8,
54    pub len: usize,
55    pub chksum: u16,
56    pub dest_network_num: u16,
57    pub src_network_num: u16,
58    pub dest_node_id: u8,
59    pub dest_sock_num: u8,
60    pub src_sock_num: u8,
61    pub src_node_id: u8,
62    pub protocol_typ: DdpProtocolType,
63}
64
65impl DdpPacket {
66    pub const LEN: usize = 13;
67
68    pub const fn calc_len(buf: &[u8]) -> usize {
69        Self::LEN + buf.len() - 1
70    }
71
72    pub fn compute_checksum(buf: &[u8]) -> u16 {
73        let mut csum: u16 = 0;
74
75        for &byte in buf {
76            csum = csum.wrapping_add(byte as u16);
77            csum = csum.rotate_left(1);
78        }
79        if csum == 0 { 0xFFFF } else { csum }
80    }
81
82    pub fn parse(bytes: &[u8]) -> Result<Self, DdpError> {
83        if bytes.len() < 13 {
84            return Err(DdpError::InvalidSize {
85                expected: 13,
86                found: bytes.len(),
87            });
88        }
89
90        let hop_count = (bytes[0] & 0x3C) >> 2;
91        let data_len = (BigEndian::read_u16(bytes) & 0x3FF) as usize;
92        let chksum = BigEndian::read_u16(&bytes[2..]);
93        let dest_network_num = BigEndian::read_u16(&bytes[4..]);
94        let src_network_num = BigEndian::read_u16(&bytes[6..]);
95        let dest_node_id = bytes[8];
96        let src_node_id = bytes[9];
97        let dest_sock_num = bytes[10];
98        let src_sock_num = bytes[11];
99        let protocol_typ = bytes[12].try_into()?;
100
101        Ok(Self {
102            dest_node_id,
103            hop_count,
104            len: data_len,
105            chksum,
106            dest_network_num,
107            src_network_num,
108            src_node_id,
109            dest_sock_num,
110            src_sock_num,
111            protocol_typ,
112        })
113    }
114
115    pub fn parse_short(bytes: &[u8], dst_node: u8, src_node: u8) -> Result<Self, DdpError> {
116        if bytes.len() < 5 {
117            return Err(DdpError::InvalidSize {
118                expected: 5,
119                found: bytes.len(),
120            });
121        }
122
123        let len = (BigEndian::read_u16(bytes) & 0x3FF) as usize;
124        let dest_sock_num = bytes[2];
125        let src_sock_num = bytes[3];
126        let protocol_typ = bytes[4].try_into()?;
127
128        Ok(Self {
129            dest_node_id: dst_node,
130            hop_count: 0,
131            len,
132            chksum: 0,
133            dest_network_num: 0,
134            src_network_num: 0,
135            src_node_id: src_node,
136            dest_sock_num,
137            src_sock_num,
138            protocol_typ,
139        })
140    }
141
142    pub fn to_bytes(&self, buf: &mut [u8]) -> Result<usize, DdpError> {
143        if buf.len() < 13 {
144            return Err(DdpError::InvalidSize {
145                expected: 13,
146                found: buf.len(),
147            });
148        }
149
150        BigEndian::write_u16(buf, ((self.hop_count as u16 & 0xF) << 2) | self.len as u16);
151        BigEndian::write_u16(&mut buf[2..], self.chksum);
152        BigEndian::write_u16(&mut buf[4..], self.dest_network_num);
153        BigEndian::write_u16(&mut buf[6..], self.src_network_num);
154        buf[8] = self.dest_node_id;
155        buf[9] = self.src_node_id;
156        buf[10] = self.dest_sock_num;
157        buf[11] = self.src_sock_num;
158        buf[12] = self.protocol_typ as u8;
159
160        Ok(13)
161    }
162
163    pub fn to_bytes_short(&self, buf: &mut [u8]) -> Result<usize, DdpError> {
164        if buf.len() < 5 {
165            return Err(DdpError::InvalidSize {
166                expected: 5,
167                found: buf.len(),
168            });
169        }
170
171        BigEndian::write_u16(buf, self.len as u16 & 0x3FF);
172        buf[2] = self.dest_sock_num;
173        buf[3] = self.src_sock_num;
174        buf[4] = self.protocol_typ as u8;
175
176        Ok(5)
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    #[test]
185    fn test_parse_ddp() {
186        let test_data: &[u8] = &[
187            0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0xff, 0x54, 0xff, 0x44, 0x06, 0x06, 0x06, 0x05,
188            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
189        ];
190
191        let packet: DdpPacket = DdpPacket::parse(test_data).expect("failed to parse");
192
193        assert_eq!(packet.hop_count, 0);
194        assert_eq!(packet.len, 20);
195        assert_eq!(packet.chksum, 0);
196        assert_eq!(packet.src_network_num, 65364);
197        assert_eq!(packet.dest_network_num, 0);
198        assert_eq!(packet.src_node_id, 68);
199        assert_eq!(packet.dest_node_id, 255);
200        assert_eq!(packet.src_sock_num, 6);
201        assert_eq!(packet.dest_sock_num, 6);
202        assert_eq!(packet.protocol_typ, DdpProtocolType::Zip);
203    }
204
205    #[test]
206    fn test_generate_ddp() {
207        let test_data: &[u8] = &[
208            0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0xff, 0x54, 0xff, 0x44, 0x06, 0x06, 0x06,
209        ];
210
211        let packet = DdpPacket {
212            hop_count: 0,
213            len: 20,
214            chksum: 0,
215            src_network_num: 65364,
216            dest_network_num: 0,
217            src_node_id: 68,
218            dest_node_id: 255,
219            src_sock_num: 6,
220            dest_sock_num: 6,
221            protocol_typ: DdpProtocolType::Zip,
222        };
223
224        let mut buffer: [u8; 13] = [0u8; 13];
225
226        packet
227            .to_bytes(&mut buffer)
228            .expect("failed to generate packet");
229
230        assert_eq!(test_data, &buffer);
231    }
232
233    #[test]
234    fn test_parse_ddp_short() {
235        // Short DDP packet: Length 5, DstSock 1, SrcSock 2, Type 6 (ZIP)
236        let test_data: &[u8] = &[0x00, 0x05, 0x01, 0x02, 0x06];
237
238        let dst_node = 10;
239        let src_node = 20;
240
241        let packet =
242            DdpPacket::parse_short(test_data, dst_node, src_node).expect("failed to parse");
243
244        assert_eq!(packet.len, 5);
245        assert_eq!(packet.dest_node_id, dst_node);
246        assert_eq!(packet.src_node_id, src_node);
247        assert_eq!(packet.dest_sock_num, 1);
248        assert_eq!(packet.src_sock_num, 2);
249        assert_eq!(packet.protocol_typ, DdpProtocolType::Zip);
250    }
251}