Skip to main content

opcua/types/
encoding.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2022 Adam Lock
4
5//! Contains the `BinaryEncoder` trait and helpers for reading and writing of scalar values and
6//! other primitives.
7
8use std::{
9    fmt::Debug,
10    io::{Cursor, Read, Result, Write},
11    sync::Arc,
12};
13
14use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
15use chrono::Duration;
16
17use crate::{
18    sync::Mutex,
19    types::{constants, status_codes::StatusCode},
20};
21
22pub type EncodingResult<T> = std::result::Result<T, StatusCode>;
23
24/// Depth lock holds a reference on the depth gauge. The drop ensures impl that the reference is
25/// decremented even if there is a panic unwind.
26#[derive(Debug)]
27pub struct DepthLock {
28    depth_gauge: Arc<Mutex<DepthGauge>>,
29}
30
31impl Drop for DepthLock {
32    fn drop(&mut self) {
33        let mut dg = trace_lock!(self.depth_gauge);
34        if dg.current_depth > 0 {
35            dg.current_depth -= 1;
36        }
37        // panic if current_depth == 0 is probably overkill and might have issues when drop
38        // is called from a panic.
39    }
40}
41
42impl DepthLock {
43    /// The depth lock tests if the depth can increment and then obtains a lock on it.
44    /// The lock will decrement the depth when it drops to ensure proper behaviour during unwinding.
45    pub fn obtain(
46        depth_gauge: Arc<Mutex<DepthGauge>>,
47    ) -> core::result::Result<DepthLock, StatusCode> {
48        let mut dg = trace_lock!(depth_gauge);
49        if dg.current_depth >= dg.max_depth {
50            warn!("Decoding in stream aborted due maximum recursion depth being reached");
51            Err(StatusCode::BadDecodingError)
52        } else {
53            dg.current_depth += 1;
54            drop(dg);
55            Ok(Self { depth_gauge })
56        }
57    }
58}
59
60/// Depth gauge is used on potentially recursive structures like Variant & ExtensionObject during
61/// decoding to limit the depth the decoder will go before giving up.
62#[derive(Debug)]
63pub struct DepthGauge {
64    /// Maximum decoding depth for recursive elements. Triggers when current depth equals max depth.
65    pub(crate) max_depth: usize,
66    /// Current decoding depth for recursive elements.
67    pub(crate) current_depth: usize,
68}
69
70impl Default for DepthGauge {
71    fn default() -> Self {
72        Self {
73            max_depth: constants::MAX_DECODING_DEPTH,
74            current_depth: 0,
75        }
76    }
77}
78
79impl DepthGauge {
80    pub fn minimal() -> Self {
81        Self {
82            max_depth: 1,
83            ..Default::default()
84        }
85    }
86    pub fn max_depth(&self) -> usize {
87        self.max_depth
88    }
89    pub fn current_depth(&self) -> usize {
90        self.current_depth
91    }
92}
93
94#[derive(Clone, Debug)]
95pub struct DecodingOptions {
96    /// Time offset between the client and the server, only used by the client when it's configured
97    /// to ignore time skew.
98    pub client_offset: Duration,
99    /// Maximum size of a message in bytes. 0 means no limit.
100    pub max_message_size: usize,
101    /// Maximum number of chunks. 0 means no limit.
102    pub max_chunk_count: usize,
103    /// Maximum length in bytes (not chars!) of a string. 0 actually means 0, i.e. no string permitted
104    pub max_string_length: usize,
105    /// Maximum length in bytes of a byte string. 0 actually means 0, i.e. no byte string permitted
106    pub max_byte_string_length: usize,
107    /// Maximum number of array elements. 0 actually means 0, i.e. no array permitted
108    pub max_array_length: usize,
109    /// Decoding depth gauge is used to check for recursion
110    pub decoding_depth_gauge: Arc<Mutex<DepthGauge>>,
111}
112
113impl Default for DecodingOptions {
114    fn default() -> Self {
115        DecodingOptions {
116            client_offset: Duration::zero(),
117            max_message_size: constants::MAX_MESSAGE_SIZE,
118            max_chunk_count: constants::MAX_CHUNK_COUNT,
119            max_string_length: constants::MAX_STRING_LENGTH,
120            max_byte_string_length: constants::MAX_BYTE_STRING_LENGTH,
121            max_array_length: constants::MAX_ARRAY_LENGTH,
122            decoding_depth_gauge: Arc::new(Mutex::new(DepthGauge::default())),
123        }
124    }
125}
126
127impl DecodingOptions {
128    /// This can be useful for decoding extension objects where the payload is not expected to contain
129    /// a large value.
130    pub fn minimal() -> Self {
131        DecodingOptions {
132            max_string_length: 8192,
133            max_byte_string_length: 8192,
134            max_array_length: 8192,
135            decoding_depth_gauge: Arc::new(Mutex::new(DepthGauge::minimal())),
136            ..Default::default()
137        }
138    }
139
140    /// For test only. Having a separate function makes it easier to control calls to DecodingOptions::default().
141    #[cfg(test)]
142    pub fn test() -> Self {
143        Self::default()
144    }
145
146    pub fn depth_lock(&self) -> core::result::Result<DepthLock, StatusCode> {
147        DepthLock::obtain(self.decoding_depth_gauge.clone())
148    }
149}
150
151/// OPC UA Binary Encoding interface. Anything that encodes to binary must implement this. It provides
152/// functions to calculate the size in bytes of the struct (for allocating memory), encoding to a stream
153/// and decoding from a stream.
154pub trait BinaryEncoder<T> {
155    /// Returns the exact byte length of the structure as it would be if `encode` were called.
156    /// This may be called prior to writing to ensure the correct amount of space is available.
157    fn byte_len(&self) -> usize;
158    /// Encodes the instance to the write stream.
159    fn encode<S: Write>(&self, stream: &mut S) -> EncodingResult<usize>;
160    /// Decodes an instance from the read stream. The decoding options contains restrictions set by
161    /// the server / client on the length of strings, arrays etc. If these limits are exceeded the
162    /// implementation should return with a `BadDecodingError` as soon as possible.
163    fn decode<S: Read>(stream: &mut S, decoding_options: &DecodingOptions) -> EncodingResult<T>;
164
165    // Convenience method for encoding a message straight into an array of bytes. It is preferable to reuse buffers than
166    // to call this so it should be reserved for tests and trivial code.
167    fn encode_to_vec(&self) -> Vec<u8> {
168        let mut buffer = Cursor::new(Vec::with_capacity(self.byte_len()));
169        let _ = self.encode(&mut buffer);
170        buffer.into_inner()
171    }
172}
173
174/// Converts an IO encoding error (and logs when in error) into an EncodingResult
175pub fn process_encode_io_result(result: Result<usize>) -> EncodingResult<usize> {
176    result.map_err(|err| {
177        trace!("Encoding error - {:?}", err);
178        StatusCode::BadEncodingError
179    })
180}
181
182/// Converts an IO encoding error (and logs when in error) into an EncodingResult
183pub fn process_decode_io_result<T>(result: Result<T>) -> EncodingResult<T>
184where
185    T: Debug,
186{
187    result.map_err(|err| {
188        trace!("Decoding error - {:?}", err);
189        StatusCode::BadDecodingError
190    })
191}
192
193/// Calculates the length in bytes of an array of encoded type
194pub fn byte_len_array<T: BinaryEncoder<T>>(values: &Option<Vec<T>>) -> usize {
195    let mut size = 4;
196    if let Some(ref values) = values {
197        size += values.iter().map(|v| v.byte_len()).sum::<usize>();
198    }
199    size
200}
201
202/// Write an array of the encoded type to stream, preserving distinction between null array and empty array
203pub fn write_array<S: Write, T: BinaryEncoder<T>>(
204    stream: &mut S,
205    values: &Option<Vec<T>>,
206) -> EncodingResult<usize> {
207    let mut size = 0;
208    if let Some(ref values) = values {
209        size += write_i32(stream, values.len() as i32)?;
210        for value in values.iter() {
211            size += value.encode(stream)?;
212        }
213    } else {
214        size += write_i32(stream, -1)?;
215    }
216    Ok(size)
217}
218
219/// Reads an array of the encoded type from a stream, preserving distinction between null array and empty array
220pub fn read_array<S: Read, T: BinaryEncoder<T>>(
221    stream: &mut S,
222    decoding_options: &DecodingOptions,
223) -> EncodingResult<Option<Vec<T>>> {
224    let len = read_i32(stream)?;
225    if len == -1 {
226        Ok(None)
227    } else if len < -1 {
228        error!("Array length is negative value and invalid");
229        Err(StatusCode::BadDecodingError)
230    } else if len as usize > decoding_options.max_array_length {
231        error!(
232            "Array length {} exceeds decoding limit {}",
233            len, decoding_options.max_array_length
234        );
235        Err(StatusCode::BadDecodingError)
236    } else {
237        let mut values: Vec<T> = Vec::with_capacity(len as usize);
238        for _ in 0..len {
239            values.push(T::decode(stream, decoding_options)?);
240        }
241        Ok(Some(values))
242    }
243}
244
245/// Writes a series of identical bytes to the stream
246pub fn write_bytes(stream: &mut dyn Write, value: u8, count: usize) -> EncodingResult<usize> {
247    for _ in 0..count {
248        let _ = stream
249            .write_u8(value)
250            .map_err(|_| StatusCode::BadEncodingError)?;
251    }
252    Ok(count)
253}
254
255/// Writes an unsigned byte to the stream
256pub fn write_u8<T>(stream: &mut dyn Write, value: T) -> EncodingResult<usize>
257where
258    T: Into<u8>,
259{
260    let buf: [u8; 1] = [value.into()];
261    process_encode_io_result(stream.write(&buf))
262}
263
264/// Writes a signed 16-bit value to the stream
265pub fn write_i16<T>(stream: &mut dyn Write, value: T) -> EncodingResult<usize>
266where
267    T: Into<i16>,
268{
269    let mut buf = [0u8; 2];
270    LittleEndian::write_i16(&mut buf, value.into());
271    process_encode_io_result(stream.write(&buf))
272}
273
274/// Writes an unsigned 16-bit value to the stream
275pub fn write_u16<T>(stream: &mut dyn Write, value: T) -> EncodingResult<usize>
276where
277    T: Into<u16>,
278{
279    let mut buf = [0u8; 2];
280    LittleEndian::write_u16(&mut buf, value.into());
281    process_encode_io_result(stream.write(&buf))
282}
283
284/// Writes a signed 32-bit value to the stream
285pub fn write_i32<T>(stream: &mut dyn Write, value: T) -> EncodingResult<usize>
286where
287    T: Into<i32>,
288{
289    let mut buf = [0u8; 4];
290    LittleEndian::write_i32(&mut buf, value.into());
291    process_encode_io_result(stream.write(&buf))
292}
293
294/// Writes an unsigned 32-bit value to the stream
295pub fn write_u32<T>(stream: &mut dyn Write, value: T) -> EncodingResult<usize>
296where
297    T: Into<u32>,
298{
299    let mut buf = [0u8; 4];
300    LittleEndian::write_u32(&mut buf, value.into());
301    process_encode_io_result(stream.write(&buf))
302}
303
304/// Writes a signed 64-bit value to the stream
305pub fn write_i64<T>(stream: &mut dyn Write, value: T) -> EncodingResult<usize>
306where
307    T: Into<i64>,
308{
309    let mut buf = [0u8; 8];
310    LittleEndian::write_i64(&mut buf, value.into());
311    process_encode_io_result(stream.write(&buf))
312}
313
314/// Writes an unsigned 64-bit value to the stream
315pub fn write_u64<T>(stream: &mut dyn Write, value: T) -> EncodingResult<usize>
316where
317    T: Into<u64>,
318{
319    let mut buf = [0u8; 8];
320    LittleEndian::write_u64(&mut buf, value.into());
321    process_encode_io_result(stream.write(&buf))
322}
323
324/// Writes a 32-bit precision value to the stream
325pub fn write_f32<T>(stream: &mut dyn Write, value: T) -> EncodingResult<usize>
326where
327    T: Into<f32>,
328{
329    let mut buf = [0u8; 4];
330    LittleEndian::write_f32(&mut buf, value.into());
331    process_encode_io_result(stream.write(&buf))
332}
333
334/// Writes a 64-bit precision value to the stream
335pub fn write_f64<T>(stream: &mut dyn Write, value: T) -> EncodingResult<usize>
336where
337    T: Into<f64>,
338{
339    let mut buf = [0u8; 8];
340    LittleEndian::write_f64(&mut buf, value.into());
341    process_encode_io_result(stream.write(&buf))
342}
343
344/// Reads an array of bytes from the stream
345pub fn read_bytes(stream: &mut dyn Read, buf: &mut [u8]) -> EncodingResult<usize> {
346    let result = stream.read_exact(buf);
347    process_decode_io_result(result)?;
348    Ok(buf.len())
349}
350
351/// Read an unsigned byte from the stream
352pub fn read_u8(stream: &mut dyn Read) -> EncodingResult<u8> {
353    let mut buf = [0u8];
354    let result = stream.read_exact(&mut buf);
355    process_decode_io_result(result)?;
356    Ok(buf[0])
357}
358
359/// Read an signed 16-bit value from the stream
360pub fn read_i16(stream: &mut dyn Read) -> EncodingResult<i16> {
361    let mut buf = [0u8; 2];
362    let result = stream.read_exact(&mut buf);
363    process_decode_io_result(result)?;
364    Ok(LittleEndian::read_i16(&buf))
365}
366
367/// Read an unsigned 16-bit value from the stream
368pub fn read_u16(stream: &mut dyn Read) -> EncodingResult<u16> {
369    let mut buf = [0u8; 2];
370    let result = stream.read_exact(&mut buf);
371    process_decode_io_result(result)?;
372    Ok(LittleEndian::read_u16(&buf))
373}
374
375/// Read a signed 32-bit value from the stream
376pub fn read_i32(stream: &mut dyn Read) -> EncodingResult<i32> {
377    let mut buf = [0u8; 4];
378    let result = stream.read_exact(&mut buf);
379    process_decode_io_result(result)?;
380    Ok(LittleEndian::read_i32(&buf))
381}
382
383/// Read an unsigned 32-bit value from the stream
384pub fn read_u32(stream: &mut dyn Read) -> EncodingResult<u32> {
385    let mut buf = [0u8; 4];
386    let result = stream.read_exact(&mut buf);
387    process_decode_io_result(result)?;
388    Ok(LittleEndian::read_u32(&buf))
389}
390
391/// Read a signed 64-bit value from the stream
392pub fn read_i64(stream: &mut dyn Read) -> EncodingResult<i64> {
393    let mut buf = [0u8; 8];
394    let result = stream.read_exact(&mut buf);
395    process_decode_io_result(result)?;
396    Ok(LittleEndian::read_i64(&buf))
397}
398
399/// Read an unsigned 64-bit value from the stream
400pub fn read_u64(stream: &mut dyn Read) -> EncodingResult<u64> {
401    let mut buf = [0u8; 8];
402    let result = stream.read_exact(&mut buf);
403    process_decode_io_result(result)?;
404    Ok(LittleEndian::read_u64(&buf))
405}
406
407/// Read a 32-bit precision value from the stream
408pub fn read_f32(stream: &mut dyn Read) -> EncodingResult<f32> {
409    let mut buf = [0u8; 4];
410    let result = stream.read_exact(&mut buf);
411    process_decode_io_result(result)?;
412    Ok(LittleEndian::read_f32(&buf))
413}
414
415/// Read a 64-bit precision from the stream
416pub fn read_f64(stream: &mut dyn Read) -> EncodingResult<f64> {
417    let mut buf = [0u8; 8];
418    let result = stream.read_exact(&mut buf);
419    process_decode_io_result(result)?;
420    Ok(LittleEndian::read_f64(&buf))
421}