modbus_core/codec/tcp/
mod.rs

1//! Modbus RTU
2
3use super::*;
4use byteorder::{BigEndian, ByteOrder};
5
6pub mod server;
7pub use crate::frame::tcp::*;
8
9// [MODBUS MESSAGING ON TCP/IP IMPLEMENTATION GUIDE V1.0b](http://modbus.org/docs/Modbus_Messaging_Implementation_Guide_V1_0b.pdf), page 18
10// "a MODBUS request needs a maximum of 256 bytes + the MBAP header size"
11const MAX_FRAME_LEN: usize = 256;
12
13/// An extracted TCP PDU frame.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct DecodedFrame<'a> {
16    pub transaction_id: TransactionId,
17    pub unit_id: UnitId,
18    pub pdu: &'a [u8],
19}
20
21/// The location of all bytes that belong to the frame.
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub struct FrameLocation {
24    /// The index where the frame starts
25    pub start: usize,
26    /// Number of bytes that belong to the frame
27    pub size: usize,
28}
29
30/// Decode TCP PDU frames from a buffer.
31pub fn decode(
32    decoder_type: DecoderType,
33    buf: &[u8],
34) -> Result<Option<(DecodedFrame, FrameLocation)>> {
35    use DecoderType::{Request, Response};
36    let mut drop_cnt = 0;
37
38    if buf.is_empty() {
39        return Err(Error::BufferSize);
40    }
41
42    loop {
43        let mut retry = false;
44        if drop_cnt + 1 >= buf.len() {
45            return Ok(None);
46        }
47        let raw_frame = &buf[drop_cnt..];
48        let res = match decoder_type {
49            Request => request_pdu_len(raw_frame),
50            Response => response_pdu_len(raw_frame),
51        }
52        .and_then(|pdu_len| {
53            retry = false;
54            if let Some(pdu_len) = pdu_len {
55                extract_frame(raw_frame, pdu_len).map(|x| {
56                    x.map(|res| {
57                        (
58                            res,
59                            FrameLocation {
60                                start: drop_cnt,
61                                size: pdu_len + 7,
62                            },
63                        )
64                    })
65                })
66            } else {
67                // Incomplete frame
68                Ok(None)
69            }
70        })
71        .or_else(|err| {
72            let pdu_type = match decoder_type {
73                Request => "request",
74                Response => "response",
75            };
76            if drop_cnt + 1 >= MAX_FRAME_LEN {
77                log::error!(
78                    "Giving up to decode frame after dropping {drop_cnt} byte(s): {:X?}",
79                    &buf[0..drop_cnt]
80                );
81                return Err(err);
82            }
83            log::warn!("Failed to decode {pdu_type} frame: {err}");
84            drop_cnt += 1;
85            retry = true;
86            Ok(None)
87        });
88
89        if !retry {
90            return res;
91        }
92    }
93}
94
95/// Extract a PDU frame out of a buffer.
96pub fn extract_frame(buf: &[u8], pdu_len: usize) -> Result<Option<DecodedFrame>> {
97    if buf.is_empty() {
98        return Err(Error::BufferSize);
99    }
100    let adu_len = 7 + pdu_len;
101    if buf.len() >= adu_len {
102        let (adu_buf, _next_frame) = buf.split_at(adu_len);
103        let (adu_buf, pdu_data) = adu_buf.split_at(7);
104        let (transaction_buf, adu_buf) = adu_buf.split_at(2);
105        let (protocol_buf, adu_buf) = adu_buf.split_at(2);
106        let (length_buf, adu_buf) = adu_buf.split_at(2);
107        let protocol_id = BigEndian::read_u16(protocol_buf);
108        if protocol_id != 0 {
109            return Err(Error::ProtocolNotModbus(protocol_id));
110        }
111        let transaction = BigEndian::read_u16(transaction_buf);
112        let m_length = BigEndian::read_u16(length_buf) as usize;
113        let unit = adu_buf[0];
114        if m_length != pdu_len + 1 {
115            return Err(Error::LengthMismatch(m_length, pdu_len + 1));
116        }
117        return Ok(Some(DecodedFrame {
118            transaction_id: transaction,
119            unit_id: unit,
120            pdu: pdu_data,
121        }));
122    }
123    // Incomplete frame
124    Ok(None)
125}
126
127/// Extract the PDU length out of the ADU request buffer.
128pub const fn request_pdu_len(adu_buf: &[u8]) -> Result<Option<usize>> {
129    if adu_buf.len() < 8 {
130        return Ok(None);
131    }
132    let fn_code = adu_buf[7];
133    let len = match fn_code {
134        0x01..=0x06 => Some(5),
135        0x07 | 0x0B | 0x0C | 0x11 => Some(1),
136        0x0F | 0x10 => {
137            if adu_buf.len() > 10 {
138                Some(6 + adu_buf[12] as usize)
139            } else {
140                // incomplete frame
141                None
142            }
143        }
144        0x16 => Some(7),
145        0x18 => Some(3),
146        0x17 => {
147            if adu_buf.len() > 16 {
148                Some(10 + adu_buf[16] as usize)
149            } else {
150                // incomplete frame
151                None
152            }
153        }
154        _ => {
155            return Err(Error::FnCode(fn_code));
156        }
157    };
158    Ok(len)
159}
160
161/// Extract the PDU length out of the ADU response buffer.
162pub fn response_pdu_len(adu_buf: &[u8]) -> Result<Option<usize>> {
163    if adu_buf.len() < 8 {
164        return Ok(None);
165    }
166    let fn_code = adu_buf[7];
167    let len = match fn_code {
168        0x01..=0x04 | 0x0C | 0x17 => {
169            if adu_buf.len() > 8 {
170                Some(2 + adu_buf[8] as usize)
171            } else {
172                // incomplete frame
173                None
174            }
175        }
176        0x05 | 0x06 | 0x0B | 0x0F | 0x10 => Some(5),
177        0x07 | 0x81..=0xAB => Some(2),
178        0x16 => Some(7),
179        0x18 => {
180            if adu_buf.len() > 9 {
181                Some(3 + BigEndian::read_u16(&adu_buf[8..=9]) as usize)
182            } else {
183                // incomplete frame
184                None
185            }
186        }
187        _ => return Err(Error::FnCode(fn_code)),
188    };
189    Ok(len)
190}
191
192#[cfg(test)]
193mod tests {
194    use super::*;
195
196    #[test]
197    fn test_request_pdu_len() {
198        let buf = &mut [0x66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
199        assert!(request_pdu_len(buf).is_err());
200
201        buf[7] = 0x01;
202        assert_eq!(request_pdu_len(buf).unwrap(), Some(5));
203
204        buf[7] = 0x02;
205        assert_eq!(request_pdu_len(buf).unwrap(), Some(5));
206
207        buf[7] = 0x03;
208        assert_eq!(request_pdu_len(buf).unwrap(), Some(5));
209
210        buf[7] = 0x04;
211        assert_eq!(request_pdu_len(buf).unwrap(), Some(5));
212
213        buf[7] = 0x05;
214        assert_eq!(request_pdu_len(buf).unwrap(), Some(5));
215
216        buf[7] = 0x06;
217        assert_eq!(request_pdu_len(buf).unwrap(), Some(5));
218
219        buf[7] = 0x07;
220        assert_eq!(request_pdu_len(buf).unwrap(), Some(1));
221
222        // TODO: 0x08
223
224        buf[7] = 0x0B;
225        assert_eq!(request_pdu_len(buf).unwrap(), Some(1));
226
227        buf[7] = 0x0C;
228        assert_eq!(request_pdu_len(buf).unwrap(), Some(1));
229
230        buf[7] = 0x0F;
231        buf[12] = 99;
232        assert_eq!(request_pdu_len(buf).unwrap(), Some(105));
233
234        buf[7] = 0x10;
235        buf[12] = 99;
236        assert_eq!(request_pdu_len(buf).unwrap(), Some(105));
237
238        buf[7] = 0x11;
239        assert_eq!(request_pdu_len(buf).unwrap(), Some(1));
240
241        // TODO: 0x14
242
243        // TODO: 0x15
244
245        buf[7] = 0x16;
246        assert_eq!(request_pdu_len(buf).unwrap(), Some(7));
247
248        buf[7] = 0x17;
249        buf[16] = 99; // write byte count
250        assert_eq!(request_pdu_len(buf).unwrap(), Some(109));
251
252        buf[7] = 0x18;
253        assert_eq!(request_pdu_len(buf).unwrap(), Some(3));
254
255        // TODO: 0x2B
256    }
257
258    #[test]
259    fn test_get_response_pdu_len() {
260        let buf = &mut [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x01, 99];
261        assert_eq!(response_pdu_len(buf).unwrap(), Some(101));
262
263        let buf = &mut [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 99, 0x00];
264        assert_eq!(response_pdu_len(buf).err().unwrap(), Error::FnCode(0));
265
266        let buf = &mut [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0xee, 99, 0x00];
267        assert_eq!(response_pdu_len(buf).err().unwrap(), Error::FnCode(0xee));
268
269        buf[7] = 0x01;
270        assert_eq!(response_pdu_len(buf).unwrap(), Some(101));
271
272        buf[7] = 0x02;
273        assert_eq!(response_pdu_len(buf).unwrap(), Some(101));
274
275        buf[7] = 0x03;
276        assert_eq!(response_pdu_len(buf).unwrap(), Some(101));
277
278        buf[7] = 0x04;
279        assert_eq!(response_pdu_len(buf).unwrap(), Some(101));
280
281        buf[7] = 0x05;
282        assert_eq!(response_pdu_len(buf).unwrap(), Some(5));
283
284        buf[7] = 0x06;
285        assert_eq!(response_pdu_len(buf).unwrap(), Some(5));
286
287        buf[7] = 0x07;
288        assert_eq!(response_pdu_len(buf).unwrap(), Some(2));
289
290        // TODO: 0x08
291
292        buf[7] = 0x0B;
293        assert_eq!(response_pdu_len(buf).unwrap(), Some(5));
294
295        buf[7] = 0x0C;
296        assert_eq!(response_pdu_len(buf).unwrap(), Some(101));
297
298        buf[7] = 0x0F;
299        assert_eq!(response_pdu_len(buf).unwrap(), Some(5));
300
301        buf[7] = 0x10;
302        assert_eq!(response_pdu_len(buf).unwrap(), Some(5));
303
304        // TODO: 0x11
305
306        // TODO: 0x14
307
308        // TODO: 0x15
309
310        buf[7] = 0x16;
311        assert_eq!(response_pdu_len(buf).unwrap(), Some(7));
312
313        buf[7] = 0x17;
314        assert_eq!(response_pdu_len(buf).unwrap(), Some(101));
315
316        buf[7] = 0x18;
317        buf[8] = 0x01; // byte count Hi
318        buf[9] = 0x00; // byte count Lo
319        assert_eq!(response_pdu_len(buf).unwrap(), Some(259));
320
321        // TODO: 0x2B
322
323        for i in 0x81..0xAB {
324            buf[7] = i;
325            assert_eq!(response_pdu_len(buf).unwrap(), Some(2));
326        }
327    }
328
329    mod frame_decoder {
330
331        use super::*;
332
333        #[test]
334        fn extract_partly_received_tcp_frame() {
335            let buf = &[
336                0x01, // transaction id
337                0x02, // transaction id
338                0x00, // protocol id
339                0x00, // protocol id
340                0x00, // length
341                0x06, // length
342                0x01, // unit id
343                0x02, // function code
344                0x03, // byte count
345                0x00, // data
346                0x00, // data
347                      // missing final data byte
348            ];
349            let pdu_len = request_pdu_len(buf).unwrap().unwrap();
350            let res = extract_frame(buf, pdu_len).unwrap();
351            assert!(res.is_none());
352        }
353
354        #[test]
355        fn extract_usual_tcp_response_frame() {
356            let buf = &[
357                0x01, // transaction id
358                0x02, // transaction id
359                0x00, // protocol id
360                0x00, // protocol id
361                0x00, // length
362                0x07, // length
363                0x01, // unit id
364                0x03, // function code
365                0x04, // byte count
366                0x89, //
367                0x02, //
368                0x42, //
369                0xC7, //
370                0x03, // -- start of next frame
371            ];
372            let pdu_len = response_pdu_len(buf).unwrap().unwrap();
373            let DecodedFrame {
374                transaction_id,
375                unit_id,
376                pdu,
377            } = extract_frame(buf, pdu_len).unwrap().unwrap();
378            assert_eq!(transaction_id, 258);
379            assert_eq!(unit_id, 0x01);
380            assert_eq!(pdu.len(), 6);
381        }
382
383        #[test]
384        fn decode_tcp_response_drop_invalid_bytes() {
385            let buf = &[
386                0x42, // dropped byte
387                0x43, // dropped byte
388                0x01, // transaction id
389                0x02, // transaction id
390                0x00, // protocol id
391                0x00, // protocol id
392                0x00, // length
393                0x07, // length
394                0x01, // unit id
395                0x03, // function code
396                0x04, // byte count
397                0x89, //
398                0x02, //
399                0x42, //
400                0xC7, //
401                0x00, //next frame
402            ];
403            let (frame, location) = decode(DecoderType::Response, buf).unwrap().unwrap();
404            assert_eq!(frame.transaction_id, 258);
405            assert_eq!(frame.unit_id, 0x01);
406            assert_eq!(frame.pdu.len(), 6);
407            assert_eq!(location.start, 2);
408            assert_eq!(location.size, 13);
409        }
410
411        #[test]
412        fn decode_tcp_response_with_max_drops() {
413            let buf = &[0x42; 10];
414            assert!(decode(DecoderType::Response, buf).unwrap().is_none());
415
416            let buf = &mut [0x42; MAX_FRAME_LEN * 2];
417            buf[256] = 0x01; // slave address
418            buf[257] = 0x03; // function code
419            buf[258] = 0x04; // byte count
420            buf[259] = 0x89; //
421            buf[260] = 0x02; //
422            buf[261] = 0x42; //
423            buf[262] = 0xC7; //
424            assert!(decode(DecoderType::Response, buf).is_err());
425        }
426    }
427}