modbus_core/codec/tcp/
server.rs

1// SPDX-FileCopyrightText: Copyright (c) 2018-2025 slowtec GmbH <post@slowtec.de>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Modbus TCP server (slave) specific functions.
5use super::*;
6
7/// Decode an TCP request.<'_>
8pub fn decode_request(buf: &[u8]) -> Result<Option<RequestAdu<'_>>> {
9    if buf.is_empty() {
10        return Ok(None);
11    }
12    let frame = decode(DecoderType::Request, buf)?;
13    let Some((decoded_frame, _frame_pos)) = frame else {
14        return Ok(None);
15    };
16    let DecodedFrame {
17        transaction_id,
18        unit_id,
19        pdu,
20    } = decoded_frame;
21    let hdr = Header {
22        transaction_id,
23        unit_id,
24    };
25    // Decoding of the PDU should are unlikely to fail due
26    // to transmission errors, because the frame's bytes
27    // have already been verified at the TCP level.
28    let request = Request::try_from(pdu)
29        .map(RequestPdu)
30        .map(|pdu| Some(RequestAdu { hdr, pdu }));
31    #[cfg(feature = "log")]
32    if let Err(error) = request {
33        // Unrecoverable error
34        log::error!("Failed to decode request PDU: {error}");
35    }
36    request
37}
38
39// Decode a TCP response
40pub fn decode_response(buf: &[u8]) -> Result<Option<ResponseAdu<'_>>> {
41    if buf.is_empty() {
42        return Err(Error::BufferSize);
43    }
44    decode(DecoderType::Response, buf)
45        .and_then(|frame| {
46            let Some((decoded_frame, _frame_pos)) = frame else {
47                return Ok(None);
48            };
49            let DecodedFrame {
50                transaction_id,
51                unit_id,
52                pdu,
53            } = decoded_frame;
54            let hdr = Header {
55                transaction_id,
56                unit_id,
57            };
58            // Decoding of the PDU should are unlikely to fail due
59            // to transmission errors, because the frame's bytes
60            // have already been verified at the TCP level.
61            let response = Response::try_from(pdu)
62                .map(Ok)
63                .or_else(|_| ExceptionResponse::try_from(pdu).map(Err))
64                .map(ResponsePdu)
65                .map(|pdu| Some(ResponseAdu { hdr, pdu }));
66            #[cfg(feature = "log")]
67            if let Err(error) = response {
68                // Unrecoverable error
69                log::error!("Failed to decode response PDU: {error}");
70            }
71            response
72        })
73        .map_err(|_| {
74            // Decoding the transport frame is non-destructive and must
75            // never fail!
76            unreachable!();
77        })
78}
79
80/// Encode an TCP response.
81pub fn encode_response(adu: ResponseAdu, buf: &mut [u8]) -> Result<usize> {
82    let ResponseAdu { hdr, pdu } = adu;
83    if buf.len() < 7 {
84        return Err(Error::BufferSize);
85    }
86    BigEndian::write_u16(&mut buf[0..2], hdr.transaction_id);
87    BigEndian::write_u16(&mut buf[2..4], 0); //MODBUS Protocol
88    buf[6] = hdr.unit_id;
89    let len = pdu.encode(&mut buf[7..])?;
90    if buf.len() < len + 7 {
91        return Err(Error::BufferSize);
92    }
93    BigEndian::write_u16(&mut buf[4..6], (len + 1) as u16);
94
95    Ok(len + 7)
96}
97
98pub fn encode_request(adu: RequestAdu, buf: &mut [u8]) -> Result<usize> {
99    let RequestAdu { hdr, pdu } = adu;
100    if buf.len() < 7 {
101        return Err(Error::BufferSize);
102    }
103    BigEndian::write_u16(&mut buf[0..2], hdr.transaction_id);
104    BigEndian::write_u16(&mut buf[2..4], 0); //MODBUS Protocol
105    buf[6] = hdr.unit_id;
106    let len = pdu.encode(&mut buf[7..])?;
107    if buf.len() < len + 7 {
108        return Err(Error::BufferSize);
109    }
110    BigEndian::write_u16(&mut buf[4..6], (len + 1) as u16);
111
112    Ok(len + 7)
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn decode_empty_request() {
121        let req = decode_request(&[]).unwrap();
122        assert!(req.is_none());
123    }
124
125    #[test]
126    fn decode_partly_received_request() {
127        let buf = &[
128            0x00, // garbage
129            0x01, //
130        ];
131        let req = decode_request(buf).unwrap();
132        assert!(req.is_none());
133    }
134
135    #[test]
136    fn decode_write_single_register_request() {
137        let buf = &[
138            0x00, // Transaction id
139            0x2a, // Transaction id
140            0x00, // Protocol id
141            0x00, // Protocol id
142            0x00, // length
143            0x06, // length
144            0x12, // unit id
145            0x06, // function code
146            0x22, // addr
147            0x22, // addr
148            0xAB, // value
149            0xCD, // value
150        ];
151        let adu = decode_request(buf).unwrap().unwrap();
152        let RequestAdu { hdr, pdu } = adu;
153        let RequestPdu(pdu) = pdu;
154        assert_eq!(hdr.transaction_id, 42);
155        assert_eq!(hdr.unit_id, 0x12);
156        assert_eq!(FunctionCode::from(pdu), FunctionCode::WriteSingleRegister);
157    }
158
159    #[test]
160    fn decode_wrong_protocol() {
161        let buf = &[
162            0x00, // Transaction id
163            0x2a, // Transaction id
164            0x00, // Protocol id
165            0x01, // Protocol id
166            0x00, // length
167            0x06, // length
168            0x12, // unit id
169            0x06, // function code
170            0x22, // addr
171            0x22, // addr
172            0xAB, // value
173            0xCD, // value
174        ];
175        assert!(decode_request(buf).unwrap().is_none());
176    }
177
178    #[test]
179    fn encode_write_single_register_response() {
180        let adu = ResponseAdu {
181            hdr: Header {
182                transaction_id: 42,
183                unit_id: 0x12,
184            },
185            pdu: ResponsePdu(Ok(Response::WriteSingleRegister(0x2222, 0xABCD))),
186        };
187        let buf = &mut [0; 100];
188        let len = encode_response(adu, buf).unwrap();
189        assert_eq!(len, 12);
190        assert_eq!(buf[0], 0x00);
191        assert_eq!(buf[1], 0x2a);
192        assert_eq!(buf[2], 0x00);
193        assert_eq!(buf[3], 0x00);
194        assert_eq!(buf[4], 0x00);
195        assert_eq!(buf[5], 0x06);
196        assert_eq!(buf[6], 0x12);
197        assert_eq!(buf[7], 0x06);
198        assert_eq!(buf[8], 0x22);
199        assert_eq!(buf[9], 0x22);
200        assert_eq!(buf[10], 0xAB);
201        assert_eq!(buf[11], 0xCD);
202    }
203
204    #[test]
205    fn response_buffer_too_small() {
206        let adu = ResponseAdu {
207            hdr: Header {
208                transaction_id: 42,
209                unit_id: 0x12,
210            },
211            pdu: ResponsePdu(Ok(Response::WriteSingleRegister(0x2222, 0xABCD))),
212        };
213        let buf = &mut [0; 11];
214        let res = encode_response(adu, buf).err().unwrap();
215        assert_eq!(res, Error::BufferSize);
216    }
217
218    #[test]
219    fn request_buffer_too_small() {
220        let adu = RequestAdu {
221            hdr: Header {
222                transaction_id: 42,
223                unit_id: 0x12,
224            },
225            pdu: RequestPdu(Request::WriteSingleRegister(0x2222, 0xABCD)),
226        };
227        let buf = &mut [0; 11];
228        let res = encode_request(adu, buf).err().unwrap();
229        assert_eq!(res, Error::BufferSize);
230    }
231}