async_snmp/ber/
decode.rs

1//! BER decoding.
2//!
3//! Zero-copy decoding using `Bytes` to avoid allocations.
4
5use super::length::decode_length;
6use super::tag;
7use crate::error::{DecodeErrorKind, Error, Result};
8use crate::oid::Oid;
9use bytes::Bytes;
10
11/// BER decoder that reads from a byte buffer.
12pub struct Decoder {
13    data: Bytes,
14    offset: usize,
15}
16
17impl Decoder {
18    /// Create a new decoder from bytes.
19    pub fn new(data: Bytes) -> Self {
20        Self { data, offset: 0 }
21    }
22
23    /// Create a decoder from a byte slice (copies the data).
24    pub fn from_slice(data: &[u8]) -> Self {
25        Self::new(Bytes::copy_from_slice(data))
26    }
27
28    /// Get the current offset.
29    pub fn offset(&self) -> usize {
30        self.offset
31    }
32
33    /// Get remaining bytes.
34    pub fn remaining(&self) -> usize {
35        self.data.len() - self.offset
36    }
37
38    /// Check if we've reached the end.
39    pub fn is_empty(&self) -> bool {
40        self.offset >= self.data.len()
41    }
42
43    /// Peek at the next byte without consuming it.
44    pub fn peek_byte(&self) -> Option<u8> {
45        if self.offset < self.data.len() {
46            Some(self.data[self.offset])
47        } else {
48            None
49        }
50    }
51
52    /// Peek at the next tag without consuming it.
53    pub fn peek_tag(&self) -> Option<u8> {
54        self.peek_byte()
55    }
56
57    /// Read a single byte.
58    pub fn read_byte(&mut self) -> Result<u8> {
59        if self.offset >= self.data.len() {
60            return Err(Error::decode(self.offset, DecodeErrorKind::TruncatedData));
61        }
62        let byte = self.data[self.offset];
63        self.offset += 1;
64        Ok(byte)
65    }
66
67    /// Read a tag byte.
68    pub fn read_tag(&mut self) -> Result<u8> {
69        self.read_byte()
70    }
71
72    /// Read a length and return (length, bytes consumed).
73    pub fn read_length(&mut self) -> Result<usize> {
74        let (len, consumed) = decode_length(&self.data[self.offset..], self.offset)?;
75        self.offset += consumed;
76        Ok(len)
77    }
78
79    /// Read raw bytes without copying.
80    pub fn read_bytes(&mut self, len: usize) -> Result<Bytes> {
81        if self.offset + len > self.data.len() {
82            return Err(Error::decode(
83                self.offset,
84                DecodeErrorKind::InsufficientData {
85                    needed: len,
86                    available: self.remaining(),
87                },
88            ));
89        }
90        let bytes = self.data.slice(self.offset..self.offset + len);
91        self.offset += len;
92        Ok(bytes)
93    }
94
95    /// Read and expect a specific tag, returning the content length.
96    pub fn expect_tag(&mut self, expected: u8) -> Result<usize> {
97        let tag = self.read_tag()?;
98        if tag != expected {
99            return Err(Error::decode(
100                self.offset - 1,
101                DecodeErrorKind::UnexpectedTag {
102                    expected,
103                    actual: tag,
104                },
105            ));
106        }
107        self.read_length()
108    }
109
110    /// Read a BER integer (signed).
111    pub fn read_integer(&mut self) -> Result<i32> {
112        let len = self.expect_tag(tag::universal::INTEGER)?;
113        self.read_integer_value(len)
114    }
115
116    /// Read integer value given the length.
117    pub fn read_integer_value(&mut self, len: usize) -> Result<i32> {
118        if len == 0 {
119            return Err(Error::decode(
120                self.offset,
121                DecodeErrorKind::ZeroLengthInteger,
122            ));
123        }
124        if len > 4 {
125            // Permissive: truncate with warning (matches net-snmp)
126            tracing::warn!(
127                ber.offset = self.offset,
128                ber.length = len,
129                "integer too long, truncating to 4 bytes"
130            );
131        }
132
133        let bytes = self.read_bytes(len)?;
134
135        // Sign extend
136        let is_negative = bytes[0] & 0x80 != 0;
137        let mut value: i32 = if is_negative { -1 } else { 0 };
138
139        for &byte in bytes.iter().take(4) {
140            value = (value << 8) | (byte as i32);
141        }
142
143        Ok(value)
144    }
145
146    /// Read a 64-bit unsigned integer (Counter64).
147    pub fn read_integer64(&mut self, expected_tag: u8) -> Result<u64> {
148        let len = self.expect_tag(expected_tag)?;
149        self.read_integer64_value(len)
150    }
151
152    /// Read 64-bit unsigned integer value given the length.
153    pub fn read_integer64_value(&mut self, len: usize) -> Result<u64> {
154        if len == 0 {
155            return Err(Error::decode(
156                self.offset,
157                DecodeErrorKind::ZeroLengthInteger,
158            ));
159        }
160        if len > 9 {
161            // 9 bytes max: 1 leading zero + 8 bytes for u64
162            return Err(Error::decode(
163                self.offset,
164                DecodeErrorKind::Integer64TooLong { length: len },
165            ));
166        }
167
168        let bytes = self.read_bytes(len)?;
169        let mut value: u64 = 0;
170
171        for &byte in bytes.iter() {
172            value = (value << 8) | (byte as u64);
173        }
174
175        Ok(value)
176    }
177
178    /// Read an unsigned 32-bit integer with specific tag.
179    pub fn read_unsigned32(&mut self, expected_tag: u8) -> Result<u32> {
180        let len = self.expect_tag(expected_tag)?;
181        self.read_unsigned32_value(len)
182    }
183
184    /// Read unsigned 32-bit integer value given length.
185    pub fn read_unsigned32_value(&mut self, len: usize) -> Result<u32> {
186        if len == 0 {
187            return Err(Error::decode(
188                self.offset,
189                DecodeErrorKind::ZeroLengthInteger,
190            ));
191        }
192        if len > 5 {
193            // 5 bytes max: 1 leading zero + 4 bytes for u32
194            tracing::warn!(
195                ber.offset = self.offset,
196                ber.length = len,
197                "unsigned integer too long, truncating to 4 bytes"
198            );
199        }
200
201        let bytes = self.read_bytes(len)?;
202        let mut value: u32 = 0;
203
204        for &byte in bytes.iter().take(5) {
205            value = (value << 8) | (byte as u32);
206        }
207
208        Ok(value)
209    }
210
211    /// Read an OCTET STRING.
212    pub fn read_octet_string(&mut self) -> Result<Bytes> {
213        let len = self.expect_tag(tag::universal::OCTET_STRING)?;
214        self.read_bytes(len)
215    }
216
217    /// Read a NULL.
218    pub fn read_null(&mut self) -> Result<()> {
219        let len = self.expect_tag(tag::universal::NULL)?;
220        if len != 0 {
221            return Err(Error::decode(self.offset, DecodeErrorKind::InvalidNull));
222        }
223        Ok(())
224    }
225
226    /// Read an OBJECT IDENTIFIER.
227    pub fn read_oid(&mut self) -> Result<Oid> {
228        let len = self.expect_tag(tag::universal::OBJECT_IDENTIFIER)?;
229        let bytes = self.read_bytes(len)?;
230        Oid::from_ber(&bytes)
231    }
232
233    /// Read an OID given a pre-read length.
234    pub fn read_oid_value(&mut self, len: usize) -> Result<Oid> {
235        let bytes = self.read_bytes(len)?;
236        Oid::from_ber(&bytes)
237    }
238
239    /// Read a SEQUENCE, returning a decoder for its contents.
240    pub fn read_sequence(&mut self) -> Result<Decoder> {
241        let len = self.expect_tag(tag::universal::SEQUENCE)?;
242        let content = self.read_bytes(len)?;
243        Ok(Decoder::new(content))
244    }
245
246    /// Read a constructed type with a specific tag, returning a decoder for its contents.
247    pub fn read_constructed(&mut self, expected_tag: u8) -> Result<Decoder> {
248        let len = self.expect_tag(expected_tag)?;
249        let content = self.read_bytes(len)?;
250        Ok(Decoder::new(content))
251    }
252
253    /// Read an IP address.
254    pub fn read_ip_address(&mut self) -> Result<[u8; 4]> {
255        let len = self.expect_tag(tag::application::IP_ADDRESS)?;
256        if len != 4 {
257            return Err(Error::decode(
258                self.offset,
259                DecodeErrorKind::InvalidIpAddressLength { length: len },
260            ));
261        }
262        let bytes = self.read_bytes(4)?;
263        Ok([bytes[0], bytes[1], bytes[2], bytes[3]])
264    }
265
266    /// Skip a TLV (tag-length-value) without parsing.
267    pub fn skip_tlv(&mut self) -> Result<()> {
268        let _tag = self.read_tag()?;
269        let len = self.read_length()?;
270        self.offset += len;
271        if self.offset > self.data.len() {
272            return Err(Error::decode(self.offset, DecodeErrorKind::TlvOverflow));
273        }
274        Ok(())
275    }
276
277    /// Create a sub-decoder for a portion of the remaining data.
278    pub fn sub_decoder(&mut self, len: usize) -> Result<Decoder> {
279        let content = self.read_bytes(len)?;
280        Ok(Decoder::new(content))
281    }
282
283    /// Get the underlying bytes for the entire buffer.
284    pub fn as_bytes(&self) -> &Bytes {
285        &self.data
286    }
287
288    /// Get remaining data as a slice.
289    pub fn remaining_slice(&self) -> &[u8] {
290        &self.data[self.offset..]
291    }
292}
293
294#[cfg(test)]
295mod tests {
296    use super::*;
297
298    #[test]
299    fn test_decode_integer() {
300        let mut dec = Decoder::from_slice(&[0x02, 0x01, 0x00]);
301        assert_eq!(dec.read_integer().unwrap(), 0);
302
303        let mut dec = Decoder::from_slice(&[0x02, 0x01, 0x7F]);
304        assert_eq!(dec.read_integer().unwrap(), 127);
305
306        let mut dec = Decoder::from_slice(&[0x02, 0x02, 0x00, 0x80]);
307        assert_eq!(dec.read_integer().unwrap(), 128);
308
309        let mut dec = Decoder::from_slice(&[0x02, 0x01, 0xFF]);
310        assert_eq!(dec.read_integer().unwrap(), -1);
311
312        let mut dec = Decoder::from_slice(&[0x02, 0x01, 0x80]);
313        assert_eq!(dec.read_integer().unwrap(), -128);
314    }
315
316    #[test]
317    fn test_decode_null() {
318        let mut dec = Decoder::from_slice(&[0x05, 0x00]);
319        dec.read_null().unwrap();
320    }
321
322    #[test]
323    fn test_decode_octet_string() {
324        let mut dec = Decoder::from_slice(&[0x04, 0x05, b'h', b'e', b'l', b'l', b'o']);
325        let s = dec.read_octet_string().unwrap();
326        assert_eq!(&s[..], b"hello");
327    }
328
329    #[test]
330    fn test_decode_oid() {
331        // 1.3.6.1 = [0x2B, 0x06, 0x01]
332        let mut dec = Decoder::from_slice(&[0x06, 0x03, 0x2B, 0x06, 0x01]);
333        let oid = dec.read_oid().unwrap();
334        assert_eq!(oid.arcs(), &[1, 3, 6, 1]);
335    }
336
337    #[test]
338    fn test_decode_sequence() {
339        // SEQUENCE { INTEGER 1, INTEGER 2 }
340        let mut dec = Decoder::from_slice(&[0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02]);
341        let mut seq = dec.read_sequence().unwrap();
342        assert_eq!(seq.read_integer().unwrap(), 1);
343        assert_eq!(seq.read_integer().unwrap(), 2);
344    }
345
346    #[test]
347    fn test_accept_non_minimal_integer() {
348        // Non-minimal encodings are accepted per X.690 permissive parsing (matches net-snmp)
349        let mut dec = Decoder::from_slice(&[0x02, 0x02, 0x00, 0x01]);
350        assert_eq!(dec.read_integer().unwrap(), 1);
351
352        // 02 02 00 7F should decode as 127 (non-minimal: could be 02 01 7F)
353        let mut dec = Decoder::from_slice(&[0x02, 0x02, 0x00, 0x7F]);
354        assert_eq!(dec.read_integer().unwrap(), 127);
355
356        // 02 03 00 00 80 should decode as 128 (non-minimal: could be 02 02 00 80)
357        let mut dec = Decoder::from_slice(&[0x02, 0x03, 0x00, 0x00, 0x80]);
358        assert_eq!(dec.read_integer().unwrap(), 128);
359
360        // 02 02 FF FF should decode as -1 (non-minimal: could be 02 01 FF)
361        let mut dec = Decoder::from_slice(&[0x02, 0x02, 0xFF, 0xFF]);
362        assert_eq!(dec.read_integer().unwrap(), -1);
363    }
364
365    #[test]
366    fn test_integer_overflow_truncation() {
367        // 5-byte integer should truncate to 4 bytes (matches net-snmp CHECK_OVERFLOW)
368        // 02 05 01 02 03 04 05 = value that exceeds i32
369        let mut dec = Decoder::from_slice(&[0x02, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05]);
370        let result = dec.read_integer();
371        // Should succeed with truncated value, not error
372        assert!(result.is_ok());
373        // The value is truncated to first 4 bytes: 0x01020304
374        assert_eq!(result.unwrap(), 0x01020304);
375
376        // 6-byte integer also truncates
377        let mut dec = Decoder::from_slice(&[0x02, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
378        let result = dec.read_integer();
379        assert!(result.is_ok());
380        assert_eq!(result.unwrap(), 0x01020304);
381    }
382}