async_snmp/ber/
decode.rs

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