async_snmp/ber/
length.rs

1//! BER length encoding and decoding.
2//!
3//! Length encoding follows X.690 Section 8.1.3:
4//! - Short form: Single byte, bit 8=0, value 0-127
5//! - Long form: Initial byte (bit 8=1, bits 7-1=count), followed by length bytes
6//! - Indefinite form (0x80): Rejected per net-snmp behavior
7
8use crate::error::{DecodeErrorKind, Error, Result};
9
10/// Maximum length we'll accept (to prevent DoS)
11pub const MAX_LENGTH: usize = 0xFFFFFF; // 16MB
12
13/// Encode a length value into the buffer (returns bytes in reverse order for prepending)
14///
15/// Uses short form for lengths <= 127, long form otherwise.
16pub fn encode_length(len: usize) -> ([u8; 5], usize) {
17    let mut buf = [0u8; 5];
18
19    if len <= 127 {
20        // Short form
21        buf[0] = len as u8;
22        (buf, 1)
23    } else if len <= 0xFF {
24        // Long form, 1 byte
25        buf[0] = len as u8;
26        buf[1] = 0x81;
27        (buf, 2)
28    } else if len <= 0xFFFF {
29        // Long form, 2 bytes
30        buf[0] = len as u8;
31        buf[1] = (len >> 8) as u8;
32        buf[2] = 0x82;
33        (buf, 3)
34    } else if len <= 0xFFFFFF {
35        // Long form, 3 bytes
36        buf[0] = len as u8;
37        buf[1] = (len >> 8) as u8;
38        buf[2] = (len >> 16) as u8;
39        buf[3] = 0x83;
40        (buf, 4)
41    } else {
42        // Long form, 4 bytes
43        buf[0] = len as u8;
44        buf[1] = (len >> 8) as u8;
45        buf[2] = (len >> 16) as u8;
46        buf[3] = (len >> 24) as u8;
47        buf[4] = 0x84;
48        (buf, 5)
49    }
50}
51
52/// Decode a length from bytes, returning (length, bytes_consumed)
53///
54/// The `base_offset` parameter is used to report error offsets correctly
55/// when this is called from within a decoder.
56pub fn decode_length(data: &[u8], base_offset: usize) -> Result<(usize, usize)> {
57    if data.is_empty() {
58        return Err(Error::decode(base_offset, DecodeErrorKind::TruncatedData));
59    }
60
61    let first = data[0];
62
63    if first == 0x80 {
64        // Indefinite length - rejected per net-snmp behavior
65        return Err(Error::decode(
66            base_offset,
67            DecodeErrorKind::IndefiniteLength,
68        ));
69    }
70
71    if first & 0x80 == 0 {
72        // Short form
73        Ok((first as usize, 1))
74    } else {
75        // Long form
76        let num_octets = (first & 0x7F) as usize;
77
78        if num_octets == 0 {
79            return Err(Error::decode(base_offset, DecodeErrorKind::InvalidLength));
80        }
81
82        if num_octets > 4 {
83            return Err(Error::decode(
84                base_offset,
85                DecodeErrorKind::LengthTooLong { octets: num_octets },
86            ));
87        }
88
89        if data.len() < 1 + num_octets {
90            return Err(Error::decode(base_offset, DecodeErrorKind::TruncatedData));
91        }
92
93        let mut len: usize = 0;
94        for i in 0..num_octets {
95            len = (len << 8) | (data[1 + i] as usize);
96        }
97
98        if len > MAX_LENGTH {
99            return Err(Error::decode(
100                base_offset,
101                DecodeErrorKind::LengthExceedsMax {
102                    length: len,
103                    max: MAX_LENGTH,
104                },
105            ));
106        }
107
108        Ok((len, 1 + num_octets))
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115
116    #[test]
117    fn test_short_form() {
118        assert_eq!(decode_length(&[0], 0).unwrap(), (0, 1));
119        assert_eq!(decode_length(&[127], 0).unwrap(), (127, 1));
120        assert_eq!(decode_length(&[1], 0).unwrap(), (1, 1));
121    }
122
123    #[test]
124    fn test_long_form_1_byte() {
125        assert_eq!(decode_length(&[0x81, 128], 0).unwrap(), (128, 2));
126        assert_eq!(decode_length(&[0x81, 255], 0).unwrap(), (255, 2));
127    }
128
129    #[test]
130    fn test_long_form_2_bytes() {
131        assert_eq!(decode_length(&[0x82, 0x01, 0x00], 0).unwrap(), (256, 3));
132        assert_eq!(decode_length(&[0x82, 0xFF, 0xFF], 0).unwrap(), (65535, 3));
133    }
134
135    #[test]
136    fn test_indefinite_rejected() {
137        assert!(decode_length(&[0x80], 0).is_err());
138    }
139
140    #[test]
141    fn test_encode_short() {
142        let (buf, len) = encode_length(0);
143        assert_eq!(&buf[..len], &[0]);
144
145        let (buf, len) = encode_length(127);
146        assert_eq!(&buf[..len], &[127]);
147    }
148
149    #[test]
150    fn test_encode_long() {
151        let (buf, len) = encode_length(128);
152        assert_eq!(&buf[..len], &[128, 0x81]);
153
154        let (buf, len) = encode_length(256);
155        assert_eq!(&buf[..len], &[0, 1, 0x82]);
156    }
157
158    #[test]
159    fn test_accept_oversized_length_encoding() {
160        // Non-minimal length encodings are valid per X.690 Section 8.1.3.5 Note 2
161        // 0x82 0x00 0x05 = length 5 using 2 bytes (minimal would be 0x05)
162        let result = decode_length(&[0x82, 0x00, 0x05], 0);
163        assert_eq!(result.unwrap(), (5, 3));
164
165        // 0x81 0x01 = length 1 using long form (non-minimal, minimal would be 0x01)
166        let result = decode_length(&[0x81, 0x01], 0);
167        assert_eq!(result.unwrap(), (1, 2));
168
169        // 0x82 0x00 0x7F = length 127 using 2 bytes (non-minimal, minimal would be 0x7F)
170        let result = decode_length(&[0x82, 0x00, 0x7F], 0);
171        assert_eq!(result.unwrap(), (127, 3));
172
173        // 0x83 0x00 0x00 0x80 = length 128 using 3 bytes (non-minimal, minimal would be 0x81 0x80)
174        let result = decode_length(&[0x83, 0x00, 0x00, 0x80], 0);
175        assert_eq!(result.unwrap(), (128, 4));
176    }
177}