dnp3/link/
parser.rs

1use crate::link::constant;
2use crate::link::error::*;
3use crate::link::header::{AnyAddress, ControlField, Header};
4use crate::link::LinkErrorMode;
5use crate::util::slice_ext::*;
6
7use scursor::{ReadCursor, ReadError};
8
9#[derive(Copy, Clone)]
10enum ParseState {
11    FindSync1,
12    FindSync2,
13    ReadHeader,
14    ReadBody(Header, usize), // the header + calculated trailer length
15}
16
17pub(crate) struct FramePayload {
18    length: usize,
19    buffer: [u8; constant::MAX_FRAME_PAYLOAD_LENGTH],
20}
21
22impl FramePayload {
23    pub(crate) fn new() -> Self {
24        Self {
25            length: 0,
26            buffer: [0; constant::MAX_FRAME_PAYLOAD_LENGTH],
27        }
28    }
29
30    pub(crate) fn clear(&mut self) {
31        self.length = 0;
32    }
33
34    pub(crate) fn get(&self) -> &[u8] {
35        &self.buffer[0..self.length]
36    }
37
38    pub(crate) fn push(&mut self, data: &[u8]) -> Result<(), LogicError> {
39        let mut buff = self.buffer.as_mut();
40        let dest = buff.np_get_mut(self.length..self.length + data.len())?;
41        dest.copy_from_slice(data);
42        self.length += data.len();
43        Ok(())
44    }
45}
46
47impl Default for FramePayload {
48    fn default() -> Self {
49        Self::new()
50    }
51}
52
53pub(crate) struct Parser {
54    mode: LinkErrorMode,
55    state: ParseState,
56}
57
58impl From<ReadError> for ParseError {
59    fn from(_: ReadError) -> Self {
60        ParseError::BadLogic(LogicError::BadRead)
61    }
62}
63
64impl From<FrameError> for ParseError {
65    fn from(err: FrameError) -> Self {
66        ParseError::BadFrame(err)
67    }
68}
69
70impl From<LogicError> for ParseError {
71    fn from(err: LogicError) -> Self {
72        ParseError::BadLogic(err)
73    }
74}
75
76impl Parser {
77    pub(crate) fn new(mode: LinkErrorMode) -> Parser {
78        Parser {
79            mode,
80            state: ParseState::FindSync1,
81        }
82    }
83
84    pub(crate) fn reset(&mut self) {
85        self.state = ParseState::FindSync1;
86    }
87
88    pub(crate) fn parse(
89        &mut self,
90        cursor: &mut ReadCursor,
91        payload: &mut FramePayload,
92    ) -> Result<Option<Header>, ParseError> {
93        loop {
94            if self.mode == LinkErrorMode::Close {
95                return self.parse_impl(cursor, payload);
96            }
97
98            let res = cursor.transaction(|cur| self.parse_impl(cur, payload));
99
100            match res {
101                Ok(x) => return Ok(x),
102                Err(_) => {
103                    let _ = cursor.read_u8(); // advance one byte
104                    self.reset();
105                    // goto next iteration
106                }
107            }
108        }
109    }
110
111    fn parse_impl(
112        &mut self,
113        cursor: &mut ReadCursor,
114        payload: &mut FramePayload,
115    ) -> Result<Option<Header>, ParseError> {
116        loop {
117            let start = cursor.remaining();
118
119            match self.state {
120                ParseState::FindSync1 => self.parse_sync1(cursor)?,
121                ParseState::FindSync2 => self.parse_sync2(cursor)?,
122                ParseState::ReadHeader => self.parse_header(cursor)?,
123                ParseState::ReadBody(header, length) => {
124                    if let Some(()) = self.parse_body(length, cursor, payload)? {
125                        return Ok(Some(header));
126                    }
127                }
128            }
129
130            let end = cursor.remaining();
131
132            if start == end {
133                // no progress
134                return Ok(None);
135            }
136        }
137    }
138
139    fn calc_trailer_length(data_length: u8) -> usize {
140        let div16: usize = data_length as usize / constant::MAX_BLOCK_SIZE;
141        let mod16: usize = data_length as usize % constant::MAX_BLOCK_SIZE;
142
143        if mod16 == 0 {
144            div16 * constant::MAX_BLOCK_SIZE_WITH_CRC
145        } else {
146            (div16 * constant::MAX_BLOCK_SIZE_WITH_CRC) + mod16 + constant::CRC_LENGTH
147        }
148    }
149
150    fn parse_sync1(&mut self, cursor: &mut ReadCursor) -> Result<(), ParseError> {
151        if cursor.is_empty() {
152            return Ok(());
153        }
154
155        let x = cursor.read_u8()?;
156
157        if x != 0x05 {
158            return Err(FrameError::UnexpectedStart1(x).into());
159        }
160
161        self.state = ParseState::FindSync2;
162        Ok(())
163    }
164
165    fn parse_sync2(&mut self, cursor: &mut ReadCursor) -> Result<(), ParseError> {
166        if cursor.is_empty() {
167            return Ok(());
168        }
169
170        let x = cursor.read_u8()?;
171
172        if x != 0x64 {
173            return Err(FrameError::UnexpectedStart2(x).into());
174        }
175
176        self.state = ParseState::ReadHeader;
177        Ok(())
178    }
179
180    fn parse_header(&mut self, cursor: &mut ReadCursor) -> Result<(), ParseError> {
181        if cursor.remaining() < 8 {
182            return Ok(());
183        }
184
185        let crc_bytes = cursor.read_bytes(6)?;
186        let crc_value = cursor.read_u16_le()?;
187
188        let mut cursor = ReadCursor::new(crc_bytes);
189        let len = cursor.read_u8()?;
190
191        let header = Header::new(
192            ControlField::from(cursor.read_u8()?),
193            AnyAddress::from(cursor.read_u16_le()?),
194            AnyAddress::from(cursor.read_u16_le()?),
195        );
196
197        if len < 5 {
198            return Err(FrameError::BadLength(len).into());
199        }
200
201        let expected_crc = super::crc::calc_crc_with_0564(crc_bytes);
202        if crc_value != expected_crc {
203            return Err(FrameError::BadHeaderCrc.into());
204        }
205
206        let trailer_length = Self::calc_trailer_length(len - 5); // ok b/c len >= 5 above
207
208        self.state = ParseState::ReadBody(header, trailer_length);
209        Ok(())
210    }
211
212    fn parse_body(
213        &mut self,
214        trailer_length: usize,
215        cursor: &mut ReadCursor,
216        payload: &mut FramePayload,
217    ) -> Result<Option<()>, ParseError> {
218        if cursor.remaining() < trailer_length {
219            return Ok(None);
220        }
221
222        payload.clear();
223
224        let body = cursor.read_bytes(trailer_length)?;
225
226        for block in body.chunks(18) {
227            if block.len() < 3 {
228                // can't be a valid block
229                return Err(LogicError::BadSize.into());
230            }
231
232            let data_len = block.len() - 2;
233
234            let (data, crc) = block.np_split_at(data_len)?;
235            let crc_value = ReadCursor::new(crc).read_u16_le()?;
236            let calc_crc = super::crc::calc_crc(data);
237
238            if crc_value != calc_crc {
239                return Err(FrameError::BadBodyCrc.into());
240            }
241
242            // copy the data and advance the position
243            payload.push(data)?;
244        }
245
246        self.state = ParseState::FindSync1;
247        Ok(Some(()))
248    }
249}
250
251#[cfg(test)]
252mod test {
253    use super::super::test_data::*;
254    use super::*;
255
256    fn test_frame_parsing(parser: &mut Parser, frame: &TestFrame) {
257        let mut cursor = ReadCursor::new(frame.bytes);
258        let mut payload = FramePayload::new();
259        let header: Header = parser.parse(&mut cursor, &mut payload).unwrap().unwrap();
260        assert_eq!(cursor.remaining(), 0);
261        assert_eq!(header, frame.header);
262        assert_eq!(payload.get(), frame.payload)
263    }
264
265    #[test]
266    fn catches_bad_start1() {
267        let mut parser = Parser::new(LinkErrorMode::Close);
268        let mut cursor = ReadCursor::new(&[0x06]);
269        let mut payload = FramePayload::new();
270
271        assert_eq!(
272            parser.parse(&mut cursor, &mut payload),
273            Err(ParseError::BadFrame(FrameError::UnexpectedStart1(0x06)))
274        );
275
276        assert!(cursor.is_empty());
277    }
278
279    #[test]
280    fn catches_bad_start2() {
281        let mut parser = Parser::new(LinkErrorMode::Close);
282        let mut cursor = ReadCursor::new(&[0x05, 0x65]);
283        let mut payload = FramePayload::new();
284
285        assert_eq!(
286            parser.parse(&mut cursor, &mut payload),
287            Err(ParseError::BadFrame(FrameError::UnexpectedStart2(0x65)))
288        );
289
290        assert!(cursor.is_empty());
291    }
292
293    #[test]
294    fn catches_bad_length() {
295        let mut parser = Parser::new(LinkErrorMode::Close);
296        let mut cursor =
297            ReadCursor::new(&[0x05, 0x64, 0x04, 0xC0, 0x01, 0x00, 0x00, 0x04, 0xE9, 0x21]);
298        let mut payload = FramePayload::new();
299
300        assert_eq!(
301            parser.parse(&mut cursor, &mut payload),
302            Err(ParseError::BadFrame(FrameError::BadLength(4)))
303        );
304        assert_eq!(cursor.remaining(), 0);
305    }
306
307    #[test]
308    fn header_parse_catches_bad_crc() {
309        let mut parser = Parser::new(LinkErrorMode::Close);
310        let mut cursor =
311            ReadCursor::new(&[0x05, 0x64, 0x05, 0xC0, 0x01, 0x00, 0x00, 0x04, 0xE9, 0x20]);
312        let mut payload = FramePayload::new();
313
314        assert_eq!(
315            parser.parse(&mut cursor, &mut payload),
316            Err(ParseError::BadFrame(FrameError::BadHeaderCrc))
317        );
318        assert_eq!(cursor.remaining(), 0);
319    }
320
321    #[test]
322    fn catches_bad_crc_in_body() {
323        let data: [u8; 27] = [
324            // header
325            0x05, 0x64, 0x14, 0xF3, 0x01, 0x00, 0x00, 0x04, 0x0A, 0x3B, // body
326            0xC0, 0xC3, 0x01, 0x3C, 0x02, 0x06, 0x3C, 0x03, 0x06, 0x3C, 0x04, 0x06, 0x3C, 0x01,
327            0x06, 0x9A, 0xFF,
328        ];
329
330        let mut parser = Parser::new(LinkErrorMode::Close);
331        let mut cursor = ReadCursor::new(&data);
332        let mut payload = FramePayload::new();
333
334        assert_eq!(
335            parser.parse(&mut cursor, &mut payload),
336            Err(ParseError::BadFrame(FrameError::BadBodyCrc)),
337        );
338        assert_eq!(cursor.remaining(), 0);
339    }
340
341    #[test]
342    fn can_parse_multiple_different_frames_sequentially() {
343        let mut parser = Parser::new(LinkErrorMode::Close);
344        test_frame_parsing(&mut parser, &RESET_LINK);
345        test_frame_parsing(&mut parser, &ACK);
346        test_frame_parsing(&mut parser, &CONFIRM_USER_DATA);
347    }
348
349    #[test]
350    fn can_consume_leading_garbage_in_discard_mode() {
351        let mut parser = Parser::new(LinkErrorMode::Discard);
352        // -- ------------ leading garbage ------------- valid frame -----------------------------------------------------
353        let data = [
354            0x06, 0x05, 0x07, 0x05, 0x64, 0x05, 0x05, 0x64, 0x05, 0xC0, 0x01, 0x00, 0x00, 0x04,
355            0xE9, 0x21,
356        ];
357        let mut cursor = ReadCursor::new(&data);
358        let mut payload = FramePayload::new();
359
360        // consume leading garbage until we get to the valid frame
361        assert_eq!(
362            parser.parse(&mut cursor, &mut payload),
363            Ok(Some(RESET_LINK.header)),
364        );
365    }
366}