use std::net::SocketAddr;
use crate::error::internal::DecodeErrorKind;
use crate::error::{Error, Result, UNKNOWN_TARGET};
pub const MAX_LENGTH: usize = 0x200000;
#[inline]
pub(crate) const fn length_encoded_len(len: usize) -> usize {
if len <= 127 {
1
} else if len <= 0xFF {
2
} else if len <= 0xFFFF {
3
} else if len <= 0xFFFFFF {
4
} else {
5
}
}
#[inline]
pub(crate) const fn base128_len(value: u32) -> usize {
if value == 0 {
return 1;
}
if value < 0x80 {
1
} else if value < 0x4000 {
2
} else if value < 0x200000 {
3
} else if value < 0x10000000 {
4
} else {
5
}
}
#[inline]
pub(crate) fn integer_content_len(value: i32) -> usize {
super::encode::encode_integer_stack(value).1
}
#[inline]
pub(crate) const fn unsigned32_content_len(value: u32) -> usize {
if value == 0 {
return 1;
}
let bytes = value.to_be_bytes();
if bytes[0] != 0 {
if bytes[0] & 0x80 != 0 { 5 } else { 4 }
} else if bytes[1] != 0 {
if bytes[1] & 0x80 != 0 { 4 } else { 3 }
} else if bytes[2] != 0 {
if bytes[2] & 0x80 != 0 { 3 } else { 2 }
} else if bytes[3] & 0x80 != 0 {
2
} else {
1
}
}
#[inline]
pub(crate) const fn unsigned64_content_len(value: u64) -> usize {
if value == 0 {
return 1;
}
let bytes = value.to_be_bytes();
if bytes[0] != 0 {
if bytes[0] & 0x80 != 0 { 9 } else { 8 }
} else if bytes[1] != 0 {
if bytes[1] & 0x80 != 0 { 8 } else { 7 }
} else if bytes[2] != 0 {
if bytes[2] & 0x80 != 0 { 7 } else { 6 }
} else if bytes[3] != 0 {
if bytes[3] & 0x80 != 0 { 6 } else { 5 }
} else if bytes[4] != 0 {
if bytes[4] & 0x80 != 0 { 5 } else { 4 }
} else if bytes[5] != 0 {
if bytes[5] & 0x80 != 0 { 4 } else { 3 }
} else if bytes[6] != 0 {
if bytes[6] & 0x80 != 0 { 3 } else { 2 }
} else if bytes[7] & 0x80 != 0 {
2
} else {
1
}
}
pub fn encode_length(len: usize) -> ([u8; 5], usize) {
let mut buf = [0u8; 5];
if len <= 127 {
buf[0] = len as u8;
(buf, 1)
} else if len <= 0xFF {
buf[0] = len as u8;
buf[1] = 0x81;
(buf, 2)
} else if len <= 0xFFFF {
buf[0] = len as u8;
buf[1] = (len >> 8) as u8;
buf[2] = 0x82;
(buf, 3)
} else if len <= 0xFFFFFF {
buf[0] = len as u8;
buf[1] = (len >> 8) as u8;
buf[2] = (len >> 16) as u8;
buf[3] = 0x83;
(buf, 4)
} else {
buf[0] = len as u8;
buf[1] = (len >> 8) as u8;
buf[2] = (len >> 16) as u8;
buf[3] = (len >> 24) as u8;
buf[4] = 0x84;
(buf, 5)
}
}
pub(crate) fn parse_ber_length(data: &[u8]) -> Option<(usize, usize)> {
if data.is_empty() {
return None;
}
let first = data[0];
if first < 0x80 {
return Some((first as usize, 1));
}
if first == 0x80 {
return None;
}
let num_octets = (first & 0x7F) as usize;
if num_octets == 0 || num_octets > 8 || data.len() < 1 + num_octets {
return None;
}
let mut len: usize = 0;
for i in 0..num_octets {
len = (len << 8) | (data[1 + i] as usize);
}
Some((len, 1 + num_octets))
}
pub fn decode_length(
data: &[u8],
base_offset: usize,
target: Option<SocketAddr>,
) -> Result<(usize, usize)> {
let target = target.unwrap_or(UNKNOWN_TARGET);
if data.is_empty() {
tracing::debug!(target: "async_snmp::ber", { snmp.offset = %base_offset, kind = %DecodeErrorKind::TruncatedData }, "truncated data: unexpected end of input in length");
return Err(Error::MalformedResponse { target }.boxed());
}
let first = data[0];
if first == 0x80 {
tracing::debug!(target: "async_snmp::ber", { snmp.offset = %base_offset, kind = %DecodeErrorKind::IndefiniteLength }, "indefinite length encoding not supported");
return Err(Error::MalformedResponse { target }.boxed());
}
if first & 0x80 == 0 {
Ok((first as usize, 1))
} else {
let num_octets = (first & 0x7F) as usize;
if num_octets == 0 {
tracing::debug!(target: "async_snmp::ber", { snmp.offset = %base_offset, kind = %DecodeErrorKind::InvalidLength }, "invalid length encoding: zero octets in long form");
return Err(Error::MalformedResponse { target }.boxed());
}
if num_octets > 8 {
tracing::debug!(target: "async_snmp::ber", { snmp.offset = %base_offset, kind = %DecodeErrorKind::LengthTooLong { octets: num_octets } }, "length encoding too long");
return Err(Error::MalformedResponse { target }.boxed());
}
if data.len() < 1 + num_octets {
tracing::debug!(target: "async_snmp::ber", { snmp.offset = %base_offset, kind = %DecodeErrorKind::InsufficientData { needed: 1 + num_octets, available: data.len() } }, "truncated data in length field");
return Err(Error::MalformedResponse { target }.boxed());
}
let mut len: usize = 0;
for i in 0..num_octets {
len = (len << 8) | (data[1 + i] as usize);
}
if len > MAX_LENGTH {
tracing::debug!(target: "async_snmp::ber", { snmp.offset = %base_offset, kind = %DecodeErrorKind::LengthExceedsMax { length: len, max: MAX_LENGTH } }, "length exceeds maximum");
return Err(Error::MalformedResponse { target }.boxed());
}
Ok((len, 1 + num_octets))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_short_form() {
assert_eq!(decode_length(&[0], 0, None).unwrap(), (0, 1));
assert_eq!(decode_length(&[127], 0, None).unwrap(), (127, 1));
assert_eq!(decode_length(&[1], 0, None).unwrap(), (1, 1));
}
#[test]
fn test_long_form_1_byte() {
assert_eq!(decode_length(&[0x81, 128], 0, None).unwrap(), (128, 2));
assert_eq!(decode_length(&[0x81, 255], 0, None).unwrap(), (255, 2));
}
#[test]
fn test_long_form_2_bytes() {
assert_eq!(
decode_length(&[0x82, 0x01, 0x00], 0, None).unwrap(),
(256, 3)
);
assert_eq!(
decode_length(&[0x82, 0xFF, 0xFF], 0, None).unwrap(),
(65535, 3)
);
}
#[test]
fn test_indefinite_rejected() {
assert!(decode_length(&[0x80], 0, None).is_err());
}
#[test]
fn test_encode_short() {
let (buf, len) = encode_length(0);
assert_eq!(&buf[..len], &[0]);
let (buf, len) = encode_length(127);
assert_eq!(&buf[..len], &[127]);
}
#[test]
fn test_encode_long() {
let (buf, len) = encode_length(128);
assert_eq!(&buf[..len], &[128, 0x81]);
let (buf, len) = encode_length(256);
assert_eq!(&buf[..len], &[0, 1, 0x82]);
}
#[test]
fn test_accept_oversized_length_encoding() {
let result = decode_length(&[0x82, 0x00, 0x05], 0, None);
assert_eq!(result.unwrap(), (5, 3));
let result = decode_length(&[0x81, 0x01], 0, None);
assert_eq!(result.unwrap(), (1, 2));
let result = decode_length(&[0x82, 0x00, 0x7F], 0, None);
assert_eq!(result.unwrap(), (127, 3));
let result = decode_length(&[0x83, 0x00, 0x00, 0x80], 0, None);
assert_eq!(result.unwrap(), (128, 4));
}
#[test]
fn test_length_encoded_len() {
assert_eq!(length_encoded_len(0), 1);
assert_eq!(length_encoded_len(127), 1);
assert_eq!(length_encoded_len(128), 2);
assert_eq!(length_encoded_len(255), 2);
assert_eq!(length_encoded_len(256), 3);
assert_eq!(length_encoded_len(65535), 3);
assert_eq!(length_encoded_len(65536), 4);
}
#[test]
fn test_base128_len() {
assert_eq!(base128_len(0), 1);
assert_eq!(base128_len(127), 1);
assert_eq!(base128_len(128), 2);
assert_eq!(base128_len(16383), 2);
assert_eq!(base128_len(16384), 3);
assert_eq!(base128_len(2097151), 3);
assert_eq!(base128_len(2097152), 4);
assert_eq!(base128_len(268435455), 4);
assert_eq!(base128_len(268435456), 5);
assert_eq!(base128_len(u32::MAX), 5);
}
#[test]
fn test_integer_content_len() {
assert_eq!(integer_content_len(0), 1);
assert_eq!(integer_content_len(1), 1);
assert_eq!(integer_content_len(127), 1);
assert_eq!(integer_content_len(128), 2);
assert_eq!(integer_content_len(255), 2);
assert_eq!(integer_content_len(256), 2);
assert_eq!(integer_content_len(32767), 2);
assert_eq!(integer_content_len(32768), 3);
assert_eq!(integer_content_len(-1), 1);
assert_eq!(integer_content_len(-128), 1);
assert_eq!(integer_content_len(-129), 2);
assert_eq!(integer_content_len(i32::MAX), 4);
assert_eq!(integer_content_len(i32::MIN), 4);
}
#[test]
fn test_unsigned32_content_len() {
assert_eq!(unsigned32_content_len(0), 1);
assert_eq!(unsigned32_content_len(127), 1);
assert_eq!(unsigned32_content_len(128), 2); assert_eq!(unsigned32_content_len(255), 2); assert_eq!(unsigned32_content_len(256), 2);
assert_eq!(unsigned32_content_len(u32::MAX), 5); }
#[test]
fn test_unsigned64_content_len() {
assert_eq!(unsigned64_content_len(0), 1);
assert_eq!(unsigned64_content_len(127), 1);
assert_eq!(unsigned64_content_len(128), 2); assert_eq!(unsigned64_content_len(u64::MAX), 9); }
#[test]
fn test_long_form_5_to_8_byte_length_accepted() {
let result = decode_length(&[0x85, 0x00, 0x00, 0x00, 0x00, 0x05], 0, None);
assert_eq!(result.unwrap(), (5, 6));
let result = decode_length(
&[0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
0,
None,
);
assert_eq!(result.unwrap(), (1, 9));
let result = decode_length(
&[0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
0,
None,
);
assert!(result.is_err());
}
#[test]
fn test_max_length_enforced() {
let max = MAX_LENGTH;
let max_bytes = [
0x83,
((max >> 16) & 0xFF) as u8,
((max >> 8) & 0xFF) as u8,
(max & 0xFF) as u8,
];
let result = decode_length(&max_bytes, 0, None);
assert_eq!(result.unwrap(), (MAX_LENGTH, 4));
let over = MAX_LENGTH + 1;
let over_bytes = [
0x84, ((over >> 24) & 0xFF) as u8,
((over >> 16) & 0xFF) as u8,
((over >> 8) & 0xFF) as u8,
(over & 0xFF) as u8,
];
let result = decode_length(&over_bytes, 0, None);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(
matches!(*err, Error::MalformedResponse { .. }),
"Expected MalformedResponse error, got {:?}",
err
);
}
mod proptests {
use super::*;
use crate::ber::EncodeBuf;
use proptest::prelude::*;
proptest! {
#[test]
fn integer_content_len_matches_encoder(value: i32) {
let mut buf = EncodeBuf::new();
buf.push_integer(value);
let encoded_len = buf.len();
let actual_content_len = encoded_len - 2;
prop_assert_eq!(
integer_content_len(value),
actual_content_len,
"Mismatch for value {}: computed={}, actual={}",
value,
integer_content_len(value),
actual_content_len
);
}
#[test]
fn unsigned32_content_len_matches_encoder(value: u32) {
let mut buf = EncodeBuf::new();
buf.push_unsigned32(crate::ber::tag::application::COUNTER32, value);
let encoded_len = buf.len();
let actual_content_len = encoded_len - 2;
prop_assert_eq!(
unsigned32_content_len(value),
actual_content_len,
"Mismatch for value {}", value
);
}
#[test]
fn unsigned64_content_len_matches_encoder(value: u64) {
let mut buf = EncodeBuf::new();
buf.push_integer64(value);
let encoded_len = buf.len();
let actual_content_len = encoded_len - 2;
prop_assert_eq!(
unsigned64_content_len(value),
actual_content_len,
"Mismatch for value {}", value
);
}
}
}
}