use std::error::Error;
use std::fmt;
use std::net::Ipv4Addr;
use derivative::Derivative;
use from_to_repr::FromToRepr;
use simple_asn1::{
ASN1Block, ASN1Class, ASN1DecodeErr, ASN1EncodeErr, BigInt, BigUint, FromASN1, from_der, OID,
ToASN1, to_der,
};
use crate::oid::{ObjectIdentifier, ObjectIdentifierConversionError};
pub const VERSION_VALUE: i64 = 1;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum ExpectedAsn1Type {
Integer,
OctetString,
Sequence,
Oid,
Null,
AnySnmpValueType,
}
#[derive(Clone, Debug, PartialEq)]
pub enum SnmpMessageError {
Asn1Decoding(ASN1DecodeErr),
Asn1Encoding(ASN1EncodeErr),
Length { expected: usize, obtained: usize },
TooShort { expected: usize, obtained: usize },
UnexpectedType { expected: ExpectedAsn1Type, obtained: ASN1Block },
IntegerPrimitiveRange { primitive_type: &'static str, obtained: ASN1Block },
IncorrectVersion { expected: i64, obtained: i64 },
UnexpectedTag { obtained: BigUint },
UnexpectedTagClass { expected: Vec<ASN1Class>, obtained: ASN1Class },
UntaggedValue { obtained: ASN1Block },
EnumRange { enum_name: &'static str, obtained: ASN1Block },
OidDecode { oid: OID, error: ObjectIdentifierConversionError },
OidEncode { oid: ObjectIdentifier, error: ObjectIdentifierConversionError },
}
impl SnmpMessageError {
pub fn check_min_length(blocks: &[ASN1Block], expected: usize) -> Result<(), SnmpMessageError> {
if blocks.len() < expected {
Err(SnmpMessageError::TooShort {
expected,
obtained: blocks.len(),
})
} else {
Ok(())
}
}
pub fn check_length(blocks: &[ASN1Block], expected: usize) -> Result<(), SnmpMessageError> {
if blocks.len() != expected {
Err(SnmpMessageError::Length {
expected,
obtained: blocks.len(),
})
} else {
Ok(())
}
}
pub fn check_tag_class(obtained: ASN1Class, expected: ASN1Class) -> Result<(), SnmpMessageError> {
if obtained != expected {
Err(SnmpMessageError::UnexpectedTagClass {
expected: vec![expected],
obtained,
})
} else {
Ok(())
}
}
}
impl fmt::Display for SnmpMessageError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Asn1Decoding(asn1)
=> write!(f, "ASN.1 decoding error: {}", asn1),
Self::Asn1Encoding(asn1)
=> write!(f, "ASN.1 encoding error: {}", asn1),
Self::Length { expected, obtained }
=> write!(f, "message has wrong length: expected {} ASN.1 blocks, obtained {}", expected, obtained),
Self::TooShort { expected, obtained }
=> write!(f, "message too short: expected {} ASN.1 blocks, obtained {}", expected, obtained),
Self::UnexpectedType { expected, obtained }
=> write!(f, "expected {:?} value, obtained {:?}", expected, obtained),
Self::IntegerPrimitiveRange { primitive_type, obtained }
=> write!(f, "integer value does not fit into {}, obtained {:?}", primitive_type, obtained),
Self::IncorrectVersion { expected, obtained }
=> write!(f, "incorrect SNMP message version: expected {}, obtained {}", expected, obtained),
Self::UnexpectedTag { obtained }
=> write!(f, "unexpected tag; obtained {:?}", obtained),
Self::UnexpectedTagClass { expected, obtained }
=> write!(f, "unexpected tag class: expected {:?}, obtained {:?}", expected, obtained),
Self::UntaggedValue { obtained }
=> write!(f, "untagged value; obtained {:?}", obtained),
Self::EnumRange { enum_name, obtained }
=> write!(f, "invalid value {:?} obtained for enumeration {:?}", obtained, enum_name),
Self::OidDecode { oid, error }
=> write!(f, "object identifier {:?} invalid for SNMP: {}", oid, error),
Self::OidEncode { oid, error }
=> write!(f, "object identifier {:?} invalid for ASN.1: {}", oid, error),
}
}
}
impl Error for SnmpMessageError {
}
impl From<ASN1DecodeErr> for SnmpMessageError {
fn from(e: ASN1DecodeErr) -> Self { Self::Asn1Decoding(e) }
}
impl From<ASN1EncodeErr> for SnmpMessageError {
fn from(e: ASN1EncodeErr) -> Self { Self::Asn1Encoding(e) }
}
macro_rules! asn1_number_to_primitive {
($name:ident, $type:ty) => {
fn $name(&self) -> Result<$type, SnmpMessageError> {
if let Self::Integer(_offset, value) = self {
value.try_into()
.map_err(|_| SnmpMessageError::IntegerPrimitiveRange {
primitive_type: stringify!($type),
obtained: self.clone(),
})
} else {
Err(SnmpMessageError::UnexpectedType {
expected: ExpectedAsn1Type::Integer,
obtained: self.clone(),
})
}
}
};
}
macro_rules! asn1_number_from_primitive {
($name:ident, $type:ty) => {
fn $name(i: $type) -> Self { Self::Integer(0, BigInt::from(i)) }
};
}
trait Asn1BlockExtensions: Sized {
fn as_i32(&self) -> Result<i32, SnmpMessageError>;
fn as_i64(&self) -> Result<i64, SnmpMessageError>;
fn as_u8(&self) -> Result<u8, SnmpMessageError>;
fn as_u32(&self) -> Result<u32, SnmpMessageError>;
fn as_u64(&self) -> Result<u64, SnmpMessageError>;
fn as_big_int(&self) -> Result<BigInt, SnmpMessageError>;
fn as_big_uint(&self) -> Result<BigUint, SnmpMessageError>;
fn as_bytes(&self) -> Result<&Vec<u8>, SnmpMessageError>;
fn as_sequence(&self) -> Result<&Vec<Self>, SnmpMessageError>;
fn as_oid(&self) -> Result<&OID, SnmpMessageError>;
fn is_null(&self) -> bool;
fn from_u8(i: u8) -> Self;
fn from_i32(i: i32) -> Self;
fn from_i64(i: i64) -> Self;
fn from_u32(i: u32) -> Self;
fn from_u64(i: u64) -> Self;
fn from_bytes(bs: &[u8]) -> Self;
fn implicit_tag(&self) -> Result<(ASN1Class, BigUint), SnmpMessageError>;
fn tag_of_class(&self, of_class: ASN1Class) -> Result<BigUint, SnmpMessageError> {
let (cls, tag) = self.implicit_tag()?;
if cls == of_class {
Ok(tag)
} else {
Err(SnmpMessageError::UnexpectedTagClass {
expected: vec![of_class],
obtained: cls,
})
}
}
fn untag_implicit(&self) -> Result<Self, SnmpMessageError>;
fn with_tag(self, of_class: ASN1Class, tag: BigUint) -> Self;
}
impl Asn1BlockExtensions for ASN1Block {
asn1_number_to_primitive!(as_i64, i64);
asn1_number_to_primitive!(as_i32, i32);
asn1_number_to_primitive!(as_u8, u8);
asn1_number_to_primitive!(as_u32, u32);
asn1_number_to_primitive!(as_u64, u64);
fn as_big_int(&self) -> Result<BigInt, SnmpMessageError> {
if let Self::Integer(_offset, value) = self {
Ok(value.clone())
} else {
Err(SnmpMessageError::UnexpectedType {
expected: ExpectedAsn1Type::Integer,
obtained: self.clone(),
})
}
}
fn as_big_uint(&self) -> Result<BigUint, SnmpMessageError> {
if let Self::Integer(_offset, value) = self {
value.try_into()
.map_err(|_| SnmpMessageError::IntegerPrimitiveRange {
primitive_type: "BigUint",
obtained: self.clone(),
})
} else {
Err(SnmpMessageError::UnexpectedType {
expected: ExpectedAsn1Type::Integer,
obtained: self.clone(),
})
}
}
fn as_bytes(&self) -> Result<&Vec<u8>, SnmpMessageError> {
if let Self::OctetString(_offset, bytes) = self {
Ok(bytes)
} else {
Err(SnmpMessageError::UnexpectedType {
expected: ExpectedAsn1Type::OctetString,
obtained: self.clone(),
})
}
}
fn as_sequence(&self) -> Result<&Vec<Self>, SnmpMessageError> {
if let Self::Sequence(_offset, blocks) = self {
Ok(blocks)
} else {
Err(SnmpMessageError::UnexpectedType {
expected: ExpectedAsn1Type::Sequence,
obtained: self.clone(),
})
}
}
fn as_oid(&self) -> Result<&OID, SnmpMessageError> {
if let Self::ObjectIdentifier(_offset, oid) = self {
Ok(oid)
} else {
Err(SnmpMessageError::UnexpectedType {
expected: ExpectedAsn1Type::Oid,
obtained: self.clone(),
})
}
}
fn is_null(&self) -> bool {
if let Self::Null(_offset) = self {
true
} else {
false
}
}
asn1_number_from_primitive!(from_i32, i32);
asn1_number_from_primitive!(from_i64, i64);
asn1_number_from_primitive!(from_u8, u8);
asn1_number_from_primitive!(from_u32, u32);
asn1_number_from_primitive!(from_u64, u64);
fn from_bytes(bs: &[u8]) -> Self {
Self::OctetString(0, Vec::from(bs))
}
fn implicit_tag(&self) -> Result<(ASN1Class, BigUint), SnmpMessageError> {
if let Self::Unknown(cls, _constructed, _offset, tag, _content) = self {
Ok((*cls, tag.clone()))
} else {
Err(SnmpMessageError::UntaggedValue {
obtained: self.clone(),
})
}
}
fn untag_implicit(&self) -> Result<Self, SnmpMessageError> {
if let Self::Unknown(_cls, _constructed, offset, _tag, content) = self {
let parsed_blocks = from_der(&content)?;
let sequence = ASN1Block::Sequence(*offset, parsed_blocks);
Ok(sequence)
} else {
Err(SnmpMessageError::UntaggedValue {
obtained: self.clone(),
})
}
}
fn with_tag(self, of_class: ASN1Class, tag: BigUint) -> Self {
Self::Explicit(of_class, self.offset(), tag, Box::new(self))
}
}
#[derive(Clone, Derivative, Eq, Hash, PartialEq)]
#[derivative(Debug)]
pub struct Snmp2cMessage {
pub version: i64,
#[derivative(Debug="ignore")]
pub community: Vec<u8>,
pub pdu: Snmp2cPdu,
}
impl Snmp2cMessage {
pub fn to_bytes(&self) -> Result<Vec<u8>, SnmpMessageError> {
simple_asn1::der_encode(self)
}
pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, SnmpMessageError> {
simple_asn1::der_decode(bytes)
}
}
impl FromASN1 for Snmp2cMessage {
type Error = SnmpMessageError;
fn from_asn1(v: &[ASN1Block]) -> Result<(Self, &[ASN1Block]), Self::Error> {
SnmpMessageError::check_min_length(&v, 1)?;
let seq = v[0].as_sequence()?;
SnmpMessageError::check_length(&seq, 3)?;
let version = seq[0].as_i64()?;
if version != VERSION_VALUE {
return Err(SnmpMessageError::IncorrectVersion {
expected: VERSION_VALUE,
obtained: version,
});
}
let community = seq[1].as_bytes()?.clone();
let (pdu, _rest) = Snmp2cPdu::from_asn1(&seq[2..3])?;
let message = Self {
version,
community,
pdu,
};
Ok((message, &v[1..]))
}
}
impl ToASN1 for Snmp2cMessage {
type Error = SnmpMessageError;
fn to_asn1_class(&self, _c: ASN1Class) -> Result<Vec<ASN1Block>, Self::Error> {
let mut pdu_asn1 = self.pdu.to_asn1()?;
let mut ret = Vec::with_capacity(2 + pdu_asn1.len());
ret.push(ASN1Block::from_i64(self.version));
ret.push(ASN1Block::from_bytes(&self.community));
ret.append(&mut pdu_asn1);
Ok(vec![ASN1Block::Sequence(0, ret)])
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Snmp2cPdu {
GetRequest(InnerPdu),
GetNextRequest(InnerPdu),
GetBulkRequest(BulkPdu),
Response(InnerPdu),
SetRequest(InnerPdu),
InformRequest(InnerPdu),
SnmpV2Trap(InnerPdu),
}
impl Snmp2cPdu {
pub fn request_id(&self) -> i32 {
match self {
Self::GetRequest(p) => p.request_id,
Self::GetNextRequest(p) => p.request_id,
Self::GetBulkRequest(p) => p.request_id,
Self::Response(p) => p.request_id,
Self::SetRequest(p) => p.request_id,
Self::InformRequest(p) => p.request_id,
Self::SnmpV2Trap(p) => p.request_id,
}
}
}
impl FromASN1 for Snmp2cPdu {
type Error = SnmpMessageError;
fn from_asn1(v: &[ASN1Block]) -> Result<(Self, &[ASN1Block]), Self::Error> {
SnmpMessageError::check_min_length(&v, 1)?;
let tag = v[0].tag_of_class(ASN1Class::ContextSpecific)?;
let untagged = v[0].untag_implicit()?;
let tag_0 = BigUint::from(0u8);
let tag_1 = BigUint::from(1u8);
let tag_2 = BigUint::from(2u8);
let tag_3 = BigUint::from(3u8);
let tag_5 = BigUint::from(5u8);
let tag_6 = BigUint::from(6u8);
let tag_7 = BigUint::from(7u8);
let outer_pdu = if (tag >= tag_0 && tag <= tag_3) || (tag >= tag_6 && tag <= tag_7) {
let (inner_pdu, _rest) = InnerPdu::from_asn1(&[untagged.clone()])?;
if tag == tag_0 {
Self::GetRequest(inner_pdu)
} else if tag == tag_1 {
Self::GetNextRequest(inner_pdu)
} else if tag == tag_2 {
Self::Response(inner_pdu)
} else if tag == tag_3 {
Self::SetRequest(inner_pdu)
} else if tag == tag_6 {
Self::InformRequest(inner_pdu)
} else if tag == tag_7 {
Self::SnmpV2Trap(inner_pdu)
} else {
unreachable!()
}
} else if tag == tag_5 {
let (bulk_pdu, _rest) = BulkPdu::from_asn1(&[untagged.clone()])?;
Self::GetBulkRequest(bulk_pdu)
} else {
return Err(SnmpMessageError::UnexpectedTag {
obtained: tag,
});
};
Ok((outer_pdu, &v[1..]))
}
}
impl ToASN1 for Snmp2cPdu {
type Error = SnmpMessageError;
fn to_asn1_class(&self, _c: ASN1Class) -> Result<Vec<ASN1Block>, Self::Error> {
let (tag, inner) = match self {
Self::GetRequest(pdu) => (0u8, pdu.to_asn1()?),
Self::GetNextRequest(pdu) => (1, pdu.to_asn1()?),
Self::Response(pdu) => (2, pdu.to_asn1()?),
Self::SetRequest(pdu) => (3, pdu.to_asn1()?),
Self::GetBulkRequest(pdu) => (5, pdu.to_asn1()?),
Self::InformRequest(pdu) => (6, pdu.to_asn1()?),
Self::SnmpV2Trap(pdu) => (7, pdu.to_asn1()?),
};
let mut all_inner = Vec::new();
for inner_block in &inner {
let mut inner_bytes = to_der(inner_block)?;
all_inner.append(&mut inner_bytes);
}
let outer_pdu = ASN1Block::Unknown(
ASN1Class::ContextSpecific,
true,
0,
BigUint::from(tag),
all_inner,
);
Ok(vec![outer_pdu])
}
}
#[derive(Clone, Copy, Debug, FromToRepr, Eq, Hash, PartialEq)]
#[repr(u8)]
pub enum ErrorStatus {
NoError = 0,
TooBig = 1,
NoSuchName = 2,
BadValue = 3,
ReadOnly = 4,
GenErr = 5,
NoAccess = 6,
WrongType = 7,
WrongLength = 8,
WrongEncoding = 9,
WrongValue = 10,
NoCreation = 11,
InconsistentValue = 12,
ResourceUnavailable = 13,
CommitFailed = 14,
UndoFailed = 15,
AuthorizationError = 16,
NotWritable = 17,
InconsistentName = 18,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct InnerPdu {
pub request_id: i32,
pub error_status: ErrorStatus,
pub error_index: u32,
pub variable_bindings: Vec<VariableBinding>,
}
impl FromASN1 for InnerPdu {
type Error = SnmpMessageError;
fn from_asn1(v: &[ASN1Block]) -> Result<(Self, &[ASN1Block]), Self::Error> {
SnmpMessageError::check_min_length(&v, 1)?;
let seq = v[0].as_sequence()?;
SnmpMessageError::check_length(&seq, 4)?;
let request_id = seq[0].as_i32()?;
let error_status = ErrorStatus::try_from(seq[1].as_u8()?)
.map_err(|_| SnmpMessageError::EnumRange { enum_name: "ErrorStatus", obtained: seq[1].clone() })?;
let error_index = seq[2].as_u32()?;
let bindings_sequence = seq[3].as_sequence()?;
let mut variable_bindings = Vec::with_capacity(bindings_sequence.len());
for block in bindings_sequence {
let (binding, _rest) = VariableBinding::from_asn1(&[block.clone()])?;
variable_bindings.push(binding);
}
let inner_pdu = Self {
request_id,
error_status,
error_index,
variable_bindings,
};
Ok((inner_pdu, &v[1..]))
}
}
impl ToASN1 for InnerPdu {
type Error = SnmpMessageError;
fn to_asn1_class(&self, _c: ASN1Class) -> Result<Vec<ASN1Block>, Self::Error> {
let mut ret = Vec::with_capacity(4);
ret.push(ASN1Block::from_i32(self.request_id));
ret.push(ASN1Block::from_u8(self.error_status.into()));
ret.push(ASN1Block::from_u32(self.error_index));
let mut bindings = Vec::with_capacity(self.variable_bindings.len());
for binding in &self.variable_bindings {
let mut binding_asn1 = binding.to_asn1()?;
bindings.append(&mut binding_asn1);
}
ret.push(ASN1Block::Sequence(0, bindings));
Ok(ret)
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct BulkPdu {
pub request_id: i32,
pub non_repeaters: u32,
pub max_repetitions: u32,
pub variable_bindings: Vec<VariableBinding>,
}
impl FromASN1 for BulkPdu {
type Error = SnmpMessageError;
fn from_asn1(v: &[ASN1Block]) -> Result<(Self, &[ASN1Block]), Self::Error> {
SnmpMessageError::check_min_length(&v, 1)?;
let seq = v[0].as_sequence()?;
SnmpMessageError::check_length(&seq, 4)?;
let request_id = seq[0].as_i32()?;
let non_repeaters = seq[1].as_u32()?;
let max_repetitions = seq[2].as_u32()?;
let bindings_sequence = seq[3].as_sequence()?;
let mut variable_bindings = Vec::with_capacity(bindings_sequence.len());
for block in bindings_sequence {
let (binding, _rest) = VariableBinding::from_asn1(&[block.clone()])?;
variable_bindings.push(binding);
}
let bulk_pdu = Self {
request_id,
non_repeaters,
max_repetitions,
variable_bindings,
};
Ok((bulk_pdu, &v[1..]))
}
}
impl ToASN1 for BulkPdu {
type Error = SnmpMessageError;
fn to_asn1_class(&self, _c: ASN1Class) -> Result<Vec<ASN1Block>, Self::Error> {
let mut ret = Vec::with_capacity(4);
ret.push(ASN1Block::from_i32(self.request_id));
ret.push(ASN1Block::from_u32(self.non_repeaters));
ret.push(ASN1Block::from_u32(self.max_repetitions));
let mut bindings = Vec::with_capacity(self.variable_bindings.len());
for binding in &self.variable_bindings {
let mut binding_asn1 = binding.to_asn1()?;
bindings.append(&mut binding_asn1);
}
ret.push(ASN1Block::Sequence(0, bindings));
Ok(ret)
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct VariableBinding {
pub name: ObjectIdentifier,
pub value: BindingValue,
}
impl FromASN1 for VariableBinding {
type Error = SnmpMessageError;
fn from_asn1(v: &[ASN1Block]) -> Result<(Self, &[ASN1Block]), Self::Error> {
SnmpMessageError::check_min_length(&v, 1)?;
let seq = v[0].as_sequence()?;
SnmpMessageError::check_length(&seq, 2)?;
let name_asn1 = seq[0].as_oid()?;
let name = ObjectIdentifier::try_from(name_asn1)
.map_err(|error| SnmpMessageError::OidDecode {
oid: name_asn1.clone(),
error,
})?;
let (value, _rest) = BindingValue::from_asn1(&[seq[1].clone()])?;
let binding = Self {
name,
value,
};
Ok((binding, &v[1..]))
}
}
impl ToASN1 for VariableBinding {
type Error = SnmpMessageError;
fn to_asn1_class(&self, _c: ASN1Class) -> Result<Vec<ASN1Block>, Self::Error> {
let mut val_vec = self.value.to_asn1()?;
let mut ret = Vec::with_capacity(1 + val_vec.len());
let name_asn1: OID = (&self.name).try_into()
.map_err(|error| SnmpMessageError::OidEncode {
oid: self.name.clone(),
error,
})?;
ret.push(ASN1Block::ObjectIdentifier(0, name_asn1));
ret.append(&mut val_vec);
let inner_pdu = ASN1Block::Sequence(0, ret);
Ok(vec![inner_pdu])
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum BindingValue {
Unspecified,
Value(ObjectValue),
NoSuchObject,
NoSuchInstance,
EndOfMibView,
}
impl FromASN1 for BindingValue {
type Error = SnmpMessageError;
fn from_asn1(v: &[ASN1Block]) -> Result<(Self, &[ASN1Block]), Self::Error> {
SnmpMessageError::check_min_length(&v, 1)?;
let binding_value = match &v[0] {
ASN1Block::Null(_offset) => Self::Unspecified,
ASN1Block::Unknown(ASN1Class::ContextSpecific, _constructed, _offset, tag, content_bytes) => {
if content_bytes.len() > 0 {
return Err(SnmpMessageError::UnexpectedType {
expected: ExpectedAsn1Type::Null,
obtained: v[0].clone(),
});
}
if tag == &BigUint::from(0u8) {
Self::NoSuchObject
} else if tag == &BigUint::from(1u8) {
Self::NoSuchInstance
} else if tag == &BigUint::from(2u8) {
Self::EndOfMibView
} else {
return Err(SnmpMessageError::EnumRange {
enum_name: "BindingValue",
obtained: v[0].clone(),
});
}
},
_other => {
let (val, _rest) = ObjectValue::from_asn1(v)?;
Self::Value(val)
},
};
Ok((binding_value, &v[1..]))
}
}
impl ToASN1 for BindingValue {
type Error = SnmpMessageError;
fn to_asn1_class(&self, _c: ASN1Class) -> Result<Vec<ASN1Block>, Self::Error> {
match self {
Self::Unspecified => Ok(vec![ASN1Block::Null(0)]),
Self::Value(val) => val.to_asn1(),
Self::NoSuchObject|Self::NoSuchInstance|Self::EndOfMibView => {
let tag: u8 = match self {
Self::NoSuchObject => 0,
Self::NoSuchInstance => 1,
Self::EndOfMibView => 2,
_ => unreachable!(),
};
Ok(vec![ASN1Block::Unknown(ASN1Class::ContextSpecific, true, 0, BigUint::from(tag), Vec::new())])
},
}
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum ObjectValue {
Integer(i32),
String(Vec<u8>),
ObjectId(ObjectIdentifier),
IpAddress(Ipv4Addr),
Counter32(u32),
Unsigned32(u32),
TimeTicks(u32),
Opaque(Vec<u8>),
Counter64(u64),
}
impl ObjectValue {
#[allow(dead_code)]
pub fn as_i32(&self) -> Option<i32> {
match self {
Self::Integer(i) => Some(*i),
_ => None,
}
}
#[allow(dead_code)]
pub fn as_u32(&self) -> Option<u32> {
match self {
Self::Counter32(i) => Some(*i),
Self::Unsigned32(i) => Some(*i),
Self::TimeTicks(i) => Some(*i),
_ => None,
}
}
#[allow(dead_code)]
pub fn as_u64(&self) -> Option<u64> {
match self {
Self::Counter32(i) => Some((*i).into()),
Self::Unsigned32(i) => Some((*i).into()),
Self::TimeTicks(i) => Some((*i).into()),
Self::Counter64(i) => Some(*i),
_ => None,
}
}
#[allow(dead_code)]
pub fn as_bytes(&self) -> Option<&Vec<u8>> {
match self {
Self::String(s) => Some(s),
Self::Opaque(o) => Some(o),
_ => None,
}
}
pub fn as_oid(&self) -> Option<ObjectIdentifier> {
match self {
Self::ObjectId(o) => Some(*o),
_ => None,
}
}
#[allow(dead_code)]
pub fn is_integer(&self) -> bool { matches!(self, Self::Integer(_)) }
#[allow(dead_code)]
pub fn is_string(&self) -> bool { matches!(self, Self::String(_)) }
#[allow(dead_code)]
pub fn is_object_id(&self) -> bool { matches!(self, Self::ObjectId(_)) }
#[allow(dead_code)]
pub fn is_ip_address(&self) -> bool { matches!(self, Self::IpAddress(_)) }
#[allow(dead_code)]
pub fn is_counter32(&self) -> bool { matches!(self, Self::Counter32(_)) }
#[allow(dead_code)]
pub fn is_unsigned32(&self) -> bool { matches!(self, Self::Unsigned32(_)) }
#[allow(dead_code)]
pub fn is_time_ticks(&self) -> bool { matches!(self, Self::TimeTicks(_)) }
#[allow(dead_code)]
pub fn is_opaque(&self) -> bool { matches!(self, Self::Opaque(_)) }
#[allow(dead_code)]
pub fn is_counter64(&self) -> bool { matches!(self, Self::Counter64(_)) }
#[allow(dead_code)]
pub fn as_type_str(&self) -> &'static str {
match self {
Self::Integer(_) => "Integer",
Self::String(_) => "String",
Self::ObjectId(_) => "ObjectId",
Self::IpAddress(_) => "IpAddress",
Self::Counter32(_) => "Counter32",
Self::Unsigned32(_) => "Unsigned32",
Self::TimeTicks(_) => "TimeTicks",
Self::Opaque(_) => "Opaque",
Self::Counter64(_) => "Counter64",
}
}
}
impl FromASN1 for ObjectValue {
type Error = SnmpMessageError;
fn from_asn1(v: &[ASN1Block]) -> Result<(Self, &[ASN1Block]), Self::Error> {
SnmpMessageError::check_min_length(&v, 1)?;
let obj_value = match &v[0] {
ASN1Block::Integer(_offset, num) => {
let int_val: i32 = num.try_into()
.map_err(|_| SnmpMessageError::IntegerPrimitiveRange {
primitive_type: "i32",
obtained: v[0].clone(),
})?;
Self::Integer(int_val)
},
ASN1Block::OctetString(_offset, bytes) => Self::String(bytes.clone()),
ASN1Block::ObjectIdentifier(_offset, oid) => {
let snmp_oid: ObjectIdentifier = oid.try_into()
.map_err(|error| SnmpMessageError::OidDecode {
oid: oid.clone(),
error,
})?;
Self::ObjectId(snmp_oid)
},
ASN1Block::Unknown(cls, _constructed, _offset, tag, content_bytes) => {
SnmpMessageError::check_tag_class(*cls, ASN1Class::Application)?;
if tag == &BigUint::from(0u8) {
if content_bytes.len() != 4 {
return Err(SnmpMessageError::Length {
expected: 4,
obtained: content_bytes.len(),
});
}
let bs_array: [u8; 4] = content_bytes.clone().try_into().unwrap();
let addr = Ipv4Addr::from(bs_array);
Self::IpAddress(addr)
} else if tag >= &BigUint::from(1u8) && tag <= &BigUint::from(3u8) {
let mut val_u32 = 0u32;
for b in content_bytes {
let b_u32: u32 = (*b).into();
val_u32 = (val_u32 << 8) | b_u32;
}
if tag == &BigUint::from(1u8) {
Self::Counter32(val_u32)
} else if tag == &BigUint::from(2u8) {
Self::Unsigned32(val_u32)
} else if tag == &BigUint::from(3u8) {
Self::TimeTicks(val_u32)
} else {
unreachable!();
}
} else if tag == &BigUint::from(4u8) {
Self::Opaque(content_bytes.clone())
} else if tag == &BigUint::from(6u8) {
let mut val_u64 = 0u64;
for b in content_bytes {
let b_u64: u64 = (*b).into();
val_u64 = (val_u64 << 8) | b_u64;
}
Self::Counter64(val_u64)
} else {
return Err(SnmpMessageError::EnumRange {
enum_name: "ObjectValue",
obtained: v[0].clone(),
});
}
},
other => {
return Err(SnmpMessageError::UnexpectedType {
expected: ExpectedAsn1Type::AnySnmpValueType,
obtained: other.clone(),
});
},
};
Ok((obj_value, &v[1..]))
}
}
impl ToASN1 for ObjectValue {
type Error = SnmpMessageError;
fn to_asn1_class(&self, _c: ASN1Class) -> Result<Vec<ASN1Block>, Self::Error> {
let mut ret = Vec::with_capacity(1);
match self {
Self::Integer(i) => ret.push(ASN1Block::from_i32(*i)),
Self::String(bs) => ret.push(ASN1Block::OctetString(0, bs.clone())),
Self::ObjectId(oid) => {
let oid_val: OID = oid.try_into()
.map_err(|error| SnmpMessageError::OidEncode {
oid: oid.clone(),
error,
})?;
ret.push(ASN1Block::ObjectIdentifier(0, oid_val));
},
Self::IpAddress(addr) => ret.push(ASN1Block::Unknown(ASN1Class::Application, false, 0,
BigUint::from(0u8),
Vec::from(&addr.octets()[..]),
)),
Self::Counter32(i) => ret.push(ASN1Block::Unknown(ASN1Class::Application, false, 0,
BigUint::from(1u8),
BigInt::from(*i).to_signed_bytes_be(),
)),
Self::Unsigned32(i) => ret.push(ASN1Block::Unknown(ASN1Class::Application, false, 0,
BigUint::from(2u8),
BigInt::from(BigUint::from(*i)).to_signed_bytes_be(),
)),
Self::TimeTicks(i) => ret.push(ASN1Block::Unknown(ASN1Class::Application, false, 0,
BigUint::from(3u8),
BigInt::from(BigUint::from(*i)).to_signed_bytes_be(),
)),
Self::Opaque(bs) => ret.push(ASN1Block::Unknown(ASN1Class::Application, false, 0,
BigUint::from(4u8),
bs.clone(),
)),
Self::Counter64(i) => ret.push(ASN1Block::Unknown(ASN1Class::Application, false, 0,
BigUint::from(6u8),
BigInt::from(BigUint::from(*i)).to_signed_bytes_be(),
)),
}
Ok(ret)
}
}
#[cfg(test)]
mod tests {
use super::*;
use simple_asn1::{der_decode, der_encode};
fn gimme_int(bind: &VariableBinding) -> i32 {
match &bind.value {
BindingValue::Value(ObjectValue::Integer(i)) => *i,
_ => panic!("want int, got {:?}", bind.value),
}
}
fn gimme_octets(bind: &VariableBinding) -> &Vec<u8> {
match &bind.value {
BindingValue::Value(ObjectValue::String(bs)) => bs,
_ => panic!("want octets, got {:?}", bind.value),
}
}
fn gimme_oid(bind: &VariableBinding) -> ObjectIdentifier {
match &bind.value {
BindingValue::Value(ObjectValue::ObjectId(oid)) => *oid,
_ => panic!("want oid, got {:?}", bind.value),
}
}
fn gimme_ip(bind: &VariableBinding) -> Ipv4Addr {
match &bind.value {
BindingValue::Value(ObjectValue::IpAddress(addr)) => *addr,
_ => panic!("want ip addr, got {:?}", bind.value),
}
}
fn gimme_counter(bind: &VariableBinding) -> u32 {
match &bind.value {
BindingValue::Value(ObjectValue::Counter32(counter)) => *counter,
_ => panic!("want counter, got {:?}", bind.value),
}
}
fn gimme_unsigned(bind: &VariableBinding) -> u32 {
match &bind.value {
BindingValue::Value(ObjectValue::Unsigned32(u)) => *u,
_ => panic!("want unsigned, got {:?}", bind.value),
}
}
fn gimme_time(bind: &VariableBinding) -> u32 {
match &bind.value {
BindingValue::Value(ObjectValue::TimeTicks(i)) => *i,
_ => panic!("want time, got {:?}", bind.value),
}
}
fn gimme_counter64(bind: &VariableBinding) -> u64 {
match &bind.value {
BindingValue::Value(ObjectValue::Counter64(c64)) => *c64,
_ => panic!("want counter64, got {:?}", bind.value),
}
}
#[test]
fn test_decode1() {
let bytes: Vec<u8> = vec![
48, 129, 238, 2, 1, 1, 4, 5, 70, 113, 97, 116, 101, 162, 129, 225,
2, 4, 38, 176, 163, 99, 2, 1, 0, 2, 1, 0, 48, 129, 210, 48,
19, 6, 14, 40, 196, 98, 1, 1, 2, 1, 4, 1, 1, 4, 0, 1,
95, 2, 1, 4, 48, 19, 6, 14, 40, 196, 98, 1, 1, 2, 1, 4,
1, 1, 4, 0, 2, 94, 2, 1, 4, 48, 19, 6, 14, 40, 196, 98,
1, 1, 2, 1, 4, 1, 1, 4, 0, 3, 96, 2, 1, 5, 48, 19,
6, 14, 40, 196, 98, 1, 1, 2, 1, 4, 1, 1, 4, 0, 4, 92,
2, 1, 4, 48, 19, 6, 14, 40, 196, 98, 1, 1, 2, 1, 4, 1,
1, 4, 0, 5, 93, 2, 1, 4, 48, 19, 6, 14, 40, 196, 98, 1,
1, 2, 1, 4, 1, 1, 4, 0, 19, 47, 2, 1, 5, 48, 19, 6,
14, 40, 196, 98, 1, 1, 2, 1, 4, 1, 1, 4, 0, 22, 54, 2,
1, 5, 48, 19, 6, 14, 40, 196, 98, 1, 1, 2, 1, 4, 1, 1,
4, 0, 23, 45, 2, 1, 5, 48, 19, 6, 14, 40, 196, 98, 1, 1,
2, 1, 4, 1, 1, 4, 0, 24, 52, 2, 1, 5, 48, 19, 6, 14,
40, 196, 98, 1, 1, 2, 1, 4, 1, 1, 4, 0, 25, 51, 2, 1,
5,
];
let asn1: Snmp2cMessage = der_decode(&bytes).unwrap();
assert_eq!(asn1.version, 1);
assert_eq!(asn1.community, b"Fqate");
let inner_pdu = match asn1.pdu {
Snmp2cPdu::Response(inner) => inner,
_ => panic!(),
};
assert_eq!(inner_pdu.request_id, 649110371);
assert_eq!(inner_pdu.error_status, ErrorStatus::NoError);
assert_eq!(inner_pdu.error_index, 0);
assert_eq!(inner_pdu.variable_bindings.len(), 10);
assert_eq!(inner_pdu.variable_bindings[0].name, "1.0.8802.1.1.2.1.4.1.1.4.0.1.95".parse().unwrap());
assert_eq!(gimme_int(&inner_pdu.variable_bindings[0]), 4);
assert_eq!(inner_pdu.variable_bindings[1].name, "1.0.8802.1.1.2.1.4.1.1.4.0.2.94".parse().unwrap());
assert_eq!(gimme_int(&inner_pdu.variable_bindings[1]), 4);
assert_eq!(inner_pdu.variable_bindings[2].name, "1.0.8802.1.1.2.1.4.1.1.4.0.3.96".parse().unwrap());
assert_eq!(gimme_int(&inner_pdu.variable_bindings[2]), 5);
assert_eq!(inner_pdu.variable_bindings[3].name, "1.0.8802.1.1.2.1.4.1.1.4.0.4.92".parse().unwrap());
assert_eq!(gimme_int(&inner_pdu.variable_bindings[3]), 4);
assert_eq!(inner_pdu.variable_bindings[4].name, "1.0.8802.1.1.2.1.4.1.1.4.0.5.93".parse().unwrap());
assert_eq!(gimme_int(&inner_pdu.variable_bindings[4]), 4);
assert_eq!(inner_pdu.variable_bindings[5].name, "1.0.8802.1.1.2.1.4.1.1.4.0.19.47".parse().unwrap());
assert_eq!(gimme_int(&inner_pdu.variable_bindings[5]), 5);
assert_eq!(inner_pdu.variable_bindings[6].name, "1.0.8802.1.1.2.1.4.1.1.4.0.22.54".parse().unwrap());
assert_eq!(gimme_int(&inner_pdu.variable_bindings[6]), 5);
assert_eq!(inner_pdu.variable_bindings[7].name, "1.0.8802.1.1.2.1.4.1.1.4.0.23.45".parse().unwrap());
assert_eq!(gimme_int(&inner_pdu.variable_bindings[7]), 5);
assert_eq!(inner_pdu.variable_bindings[8].name, "1.0.8802.1.1.2.1.4.1.1.4.0.24.52".parse().unwrap());
assert_eq!(gimme_int(&inner_pdu.variable_bindings[8]), 5);
assert_eq!(inner_pdu.variable_bindings[9].name, "1.0.8802.1.1.2.1.4.1.1.4.0.25.51".parse().unwrap());
assert_eq!(gimme_int(&inner_pdu.variable_bindings[9]), 5);
}
#[test]
fn test_encode1() {
let message = Snmp2cMessage {
version: 1,
community: b"Fqate".to_vec(),
pdu: Snmp2cPdu::Response(InnerPdu {
request_id: 649110371,
error_status: ErrorStatus::NoError,
error_index: 0,
variable_bindings: vec![
VariableBinding {
name: "1.0.8802.1.1.2.1.4.1.1.4.0.1.95".parse().unwrap(),
value: BindingValue::Value(ObjectValue::Integer(4)),
},
VariableBinding {
name: "1.0.8802.1.1.2.1.4.1.1.4.0.2.94".parse().unwrap(),
value: BindingValue::Value(ObjectValue::Integer(4)),
},
VariableBinding {
name: "1.0.8802.1.1.2.1.4.1.1.4.0.3.96".parse().unwrap(),
value: BindingValue::Value(ObjectValue::Integer(5)),
},
VariableBinding {
name: "1.0.8802.1.1.2.1.4.1.1.4.0.4.92".parse().unwrap(),
value: BindingValue::Value(ObjectValue::Integer(4)),
},
VariableBinding {
name: "1.0.8802.1.1.2.1.4.1.1.4.0.5.93".parse().unwrap(),
value: BindingValue::Value(ObjectValue::Integer(4)),
},
VariableBinding {
name: "1.0.8802.1.1.2.1.4.1.1.4.0.19.47".parse().unwrap(),
value: BindingValue::Value(ObjectValue::Integer(5)),
},
VariableBinding {
name: "1.0.8802.1.1.2.1.4.1.1.4.0.22.54".parse().unwrap(),
value: BindingValue::Value(ObjectValue::Integer(5)),
},
VariableBinding {
name: "1.0.8802.1.1.2.1.4.1.1.4.0.23.45".parse().unwrap(),
value: BindingValue::Value(ObjectValue::Integer(5)),
},
VariableBinding {
name: "1.0.8802.1.1.2.1.4.1.1.4.0.24.52".parse().unwrap(),
value: BindingValue::Value(ObjectValue::Integer(5)),
},
VariableBinding {
name: "1.0.8802.1.1.2.1.4.1.1.4.0.25.51".parse().unwrap(),
value: BindingValue::Value(ObjectValue::Integer(5)),
},
],
}),
};
let bytes = der_encode(&message).unwrap();
let expected_bytes: Vec<u8> = vec![
48, 129, 238, 2, 1, 1, 4, 5, 70, 113, 97, 116, 101, 162, 129, 225,
2, 4, 38, 176, 163, 99, 2, 1, 0, 2, 1, 0, 48, 129, 210, 48,
19, 6, 14, 40, 196, 98, 1, 1, 2, 1, 4, 1, 1, 4, 0, 1,
95, 2, 1, 4, 48, 19, 6, 14, 40, 196, 98, 1, 1, 2, 1, 4,
1, 1, 4, 0, 2, 94, 2, 1, 4, 48, 19, 6, 14, 40, 196, 98,
1, 1, 2, 1, 4, 1, 1, 4, 0, 3, 96, 2, 1, 5, 48, 19,
6, 14, 40, 196, 98, 1, 1, 2, 1, 4, 1, 1, 4, 0, 4, 92,
2, 1, 4, 48, 19, 6, 14, 40, 196, 98, 1, 1, 2, 1, 4, 1,
1, 4, 0, 5, 93, 2, 1, 4, 48, 19, 6, 14, 40, 196, 98, 1,
1, 2, 1, 4, 1, 1, 4, 0, 19, 47, 2, 1, 5, 48, 19, 6,
14, 40, 196, 98, 1, 1, 2, 1, 4, 1, 1, 4, 0, 22, 54, 2,
1, 5, 48, 19, 6, 14, 40, 196, 98, 1, 1, 2, 1, 4, 1, 1,
4, 0, 23, 45, 2, 1, 5, 48, 19, 6, 14, 40, 196, 98, 1, 1,
2, 1, 4, 1, 1, 4, 0, 24, 52, 2, 1, 5, 48, 19, 6, 14,
40, 196, 98, 1, 1, 2, 1, 4, 1, 1, 4, 0, 25, 51, 2, 1,
5,
];
assert_eq!(bytes, expected_bytes);
}
#[test]
fn test_decode2() {
let bytes: Vec<u8> = vec![
48, 130, 1, 162, 2, 1, 1, 4, 5, 57, 118, 55, 57, 73, 162, 130,
1, 148, 2, 1, 1, 2, 1, 0, 2, 1, 0, 48, 130, 1, 135, 48,
129, 202, 6, 8, 43, 6, 1, 2, 1, 1, 1, 0, 4, 129, 189, 67,
105, 115, 99, 111, 32, 78, 88, 45, 79, 83, 40, 116, 109, 41, 32, 110,
53, 48, 48, 48, 44, 32, 83, 111, 102, 116, 119, 97, 114, 101, 32, 40,
110, 53, 48, 48, 48, 45, 117, 107, 57, 41, 44, 32, 86, 101, 114, 115,
105, 111, 110, 32, 54, 46, 48, 40, 50, 41, 78, 50, 40, 52, 41, 44,
32, 82, 69, 76, 69, 65, 83, 69, 32, 83, 79, 70, 84, 87, 65, 82,
69, 32, 67, 111, 112, 121, 114, 105, 103, 104, 116, 32, 40, 99, 41, 32,
50, 48, 48, 50, 45, 50, 48, 49, 50, 32, 98, 121, 32, 67, 105, 115,
99, 111, 32, 83, 121, 115, 116, 101, 109, 115, 44, 32, 73, 110, 99, 46,
32, 68, 101, 118, 105, 99, 101, 32, 77, 97, 110, 97, 103, 101, 114, 32,
86, 101, 114, 115, 105, 111, 110, 32, 54, 46, 50, 40, 49, 41, 44, 32,
32, 67, 111, 109, 112, 105, 108, 101, 100, 32, 50, 47, 50, 52, 47, 50,
48, 49, 52, 32, 49, 52, 58, 48, 48, 58, 48, 48, 48, 24, 6, 8,
43, 6, 1, 2, 1, 1, 2, 0, 6, 12, 43, 6, 1, 4, 1, 9,
12, 3, 1, 3, 135, 112, 48, 17, 6, 8, 43, 6, 1, 2, 1, 1,
3, 0, 67, 5, 0, 153, 230, 155, 197, 48, 15, 6, 8, 43, 6, 1,
2, 1, 1, 4, 0, 4, 3, 75, 79, 77, 48, 20, 6, 8, 43, 6,
1, 2, 1, 1, 5, 0, 4, 8, 115, 119, 45, 100, 45, 115, 110, 49,
48, 19, 6, 8, 43, 6, 1, 2, 1, 1, 6, 0, 4, 7, 68, 67,
48, 50, 77, 48, 50, 48, 13, 6, 8, 43, 6, 1, 2, 1, 1, 7,
0, 2, 1, 70, 48, 17, 6, 8, 43, 6, 1, 2, 1, 1, 8, 0,
67, 5, 0, 255, 255, 255, 126, 48, 20, 6, 10, 43, 6, 1, 2, 1,
1, 9, 1, 2, 1, 6, 6, 43, 6, 1, 6, 3, 1, 48, 23, 6,
10, 43, 6, 1, 2, 1, 1, 9, 1, 2, 2, 6, 9, 43, 6, 1,
6, 3, 16, 2, 2, 1,
];
let asn1: Snmp2cMessage = der_decode(&bytes).unwrap();
assert_eq!(asn1.version, 1);
assert_eq!(asn1.community, b"9v79I");
let inner_pdu = match asn1.pdu {
Snmp2cPdu::Response(inner) => inner,
_ => panic!(),
};
assert_eq!(inner_pdu.request_id, 1);
assert_eq!(inner_pdu.error_status, ErrorStatus::NoError);
assert_eq!(inner_pdu.error_index, 0);
assert_eq!(inner_pdu.variable_bindings.len(), 10);
assert_eq!(inner_pdu.variable_bindings[0].name, "1.3.6.1.2.1.1.1.0".parse().unwrap());
assert_eq!(gimme_octets(&inner_pdu.variable_bindings[0]), b"Cisco NX-OS(tm) n5000, Software (n5000-uk9), Version 6.0(2)N2(4), RELEASE SOFTWARE Copyright (c) 2002-2012 by Cisco Systems, Inc. Device Manager Version 6.2(1), Compiled 2/24/2014 14:00:00");
assert_eq!(inner_pdu.variable_bindings[1].name, "1.3.6.1.2.1.1.2.0".parse().unwrap());
assert_eq!(gimme_oid(&inner_pdu.variable_bindings[1]), "1.3.6.1.4.1.9.12.3.1.3.1008".parse().unwrap());
assert_eq!(inner_pdu.variable_bindings[2].name, "1.3.6.1.2.1.1.3.0".parse().unwrap());
assert_eq!(gimme_time(&inner_pdu.variable_bindings[2]), 2582027205);
assert_eq!(inner_pdu.variable_bindings[3].name, "1.3.6.1.2.1.1.4.0".parse().unwrap());
assert_eq!(gimme_octets(&inner_pdu.variable_bindings[3]), b"KOM");
assert_eq!(inner_pdu.variable_bindings[4].name, "1.3.6.1.2.1.1.5.0".parse().unwrap());
assert_eq!(gimme_octets(&inner_pdu.variable_bindings[4]), b"sw-d-sn1");
assert_eq!(inner_pdu.variable_bindings[5].name, "1.3.6.1.2.1.1.6.0".parse().unwrap());
assert_eq!(gimme_octets(&inner_pdu.variable_bindings[5]), b"DC02M02");
assert_eq!(inner_pdu.variable_bindings[6].name, "1.3.6.1.2.1.1.7.0".parse().unwrap());
assert_eq!(gimme_int(&inner_pdu.variable_bindings[6]), 70);
assert_eq!(inner_pdu.variable_bindings[7].name, "1.3.6.1.2.1.1.8.0".parse().unwrap());
assert_eq!(gimme_time(&inner_pdu.variable_bindings[7]), 4294967166);
assert_eq!(inner_pdu.variable_bindings[8].name, "1.3.6.1.2.1.1.9.1.2.1".parse().unwrap());
assert_eq!(gimme_oid(&inner_pdu.variable_bindings[8]), "1.3.6.1.6.3.1".parse().unwrap());
assert_eq!(inner_pdu.variable_bindings[9].name, "1.3.6.1.2.1.1.9.1.2.2".parse().unwrap());
assert_eq!(gimme_oid(&inner_pdu.variable_bindings[9]), "1.3.6.1.6.3.16.2.2.1".parse().unwrap());
}
#[test]
fn test_encode2() {
let message = Snmp2cMessage {
version: 1,
community: b"9v79I".to_vec(),
pdu: Snmp2cPdu::Response(InnerPdu {
request_id: 1,
error_status: ErrorStatus::NoError,
error_index: 0,
variable_bindings: vec![
VariableBinding {
name: "1.3.6.1.2.1.1.1.0".parse().unwrap(),
value: BindingValue::Value(ObjectValue::String(b"Cisco NX-OS(tm) n5000, Software (n5000-uk9), Version 6.0(2)N2(4), RELEASE SOFTWARE Copyright (c) 2002-2012 by Cisco Systems, Inc. Device Manager Version 6.2(1), Compiled 2/24/2014 14:00:00".to_vec())),
},
VariableBinding {
name: "1.3.6.1.2.1.1.2.0".parse().unwrap(),
value: BindingValue::Value(ObjectValue::ObjectId("1.3.6.1.4.1.9.12.3.1.3.1008".parse().unwrap())),
},
VariableBinding {
name: "1.3.6.1.2.1.1.3.0".parse().unwrap(),
value: BindingValue::Value(ObjectValue::TimeTicks(2582027205)),
},
VariableBinding {
name: "1.3.6.1.2.1.1.4.0".parse().unwrap(),
value: BindingValue::Value(ObjectValue::String(b"KOM".to_vec())),
},
VariableBinding {
name: "1.3.6.1.2.1.1.5.0".parse().unwrap(),
value: BindingValue::Value(ObjectValue::String(b"sw-d-sn1".to_vec())),
},
VariableBinding {
name: "1.3.6.1.2.1.1.6.0".parse().unwrap(),
value: BindingValue::Value(ObjectValue::String(b"DC02M02".to_vec())),
},
VariableBinding {
name: "1.3.6.1.2.1.1.7.0".parse().unwrap(),
value: BindingValue::Value(ObjectValue::Integer(70)),
},
VariableBinding {
name: "1.3.6.1.2.1.1.8.0".parse().unwrap(),
value: BindingValue::Value(ObjectValue::TimeTicks(4294967166)),
},
VariableBinding {
name: "1.3.6.1.2.1.1.9.1.2.1".parse().unwrap(),
value: BindingValue::Value(ObjectValue::ObjectId("1.3.6.1.6.3.1".parse().unwrap())),
},
VariableBinding {
name: "1.3.6.1.2.1.1.9.1.2.2".parse().unwrap(),
value: BindingValue::Value(ObjectValue::ObjectId("1.3.6.1.6.3.16.2.2.1".parse().unwrap())),
},
],
}),
};
let bytes = der_encode(&message).unwrap();
let expected_bytes: Vec<u8> = vec![
48, 130, 1, 162, 2, 1, 1, 4, 5, 57, 118, 55, 57, 73, 162, 130,
1, 148, 2, 1, 1, 2, 1, 0, 2, 1, 0, 48, 130, 1, 135, 48,
129, 202, 6, 8, 43, 6, 1, 2, 1, 1, 1, 0, 4, 129, 189, 67,
105, 115, 99, 111, 32, 78, 88, 45, 79, 83, 40, 116, 109, 41, 32, 110,
53, 48, 48, 48, 44, 32, 83, 111, 102, 116, 119, 97, 114, 101, 32, 40,
110, 53, 48, 48, 48, 45, 117, 107, 57, 41, 44, 32, 86, 101, 114, 115,
105, 111, 110, 32, 54, 46, 48, 40, 50, 41, 78, 50, 40, 52, 41, 44,
32, 82, 69, 76, 69, 65, 83, 69, 32, 83, 79, 70, 84, 87, 65, 82,
69, 32, 67, 111, 112, 121, 114, 105, 103, 104, 116, 32, 40, 99, 41, 32,
50, 48, 48, 50, 45, 50, 48, 49, 50, 32, 98, 121, 32, 67, 105, 115,
99, 111, 32, 83, 121, 115, 116, 101, 109, 115, 44, 32, 73, 110, 99, 46,
32, 68, 101, 118, 105, 99, 101, 32, 77, 97, 110, 97, 103, 101, 114, 32,
86, 101, 114, 115, 105, 111, 110, 32, 54, 46, 50, 40, 49, 41, 44, 32,
32, 67, 111, 109, 112, 105, 108, 101, 100, 32, 50, 47, 50, 52, 47, 50,
48, 49, 52, 32, 49, 52, 58, 48, 48, 58, 48, 48, 48, 24, 6, 8,
43, 6, 1, 2, 1, 1, 2, 0, 6, 12, 43, 6, 1, 4, 1, 9,
12, 3, 1, 3, 135, 112, 48, 17, 6, 8, 43, 6, 1, 2, 1, 1,
3, 0, 67, 5, 0, 153, 230, 155, 197, 48, 15, 6, 8, 43, 6, 1,
2, 1, 1, 4, 0, 4, 3, 75, 79, 77, 48, 20, 6, 8, 43, 6,
1, 2, 1, 1, 5, 0, 4, 8, 115, 119, 45, 100, 45, 115, 110, 49,
48, 19, 6, 8, 43, 6, 1, 2, 1, 1, 6, 0, 4, 7, 68, 67,
48, 50, 77, 48, 50, 48, 13, 6, 8, 43, 6, 1, 2, 1, 1, 7,
0, 2, 1, 70, 48, 17, 6, 8, 43, 6, 1, 2, 1, 1, 8, 0,
67, 5, 0, 255, 255, 255, 126, 48, 20, 6, 10, 43, 6, 1, 2, 1,
1, 9, 1, 2, 1, 6, 6, 43, 6, 1, 6, 3, 1, 48, 23, 6,
10, 43, 6, 1, 2, 1, 1, 9, 1, 2, 2, 6, 9, 43, 6, 1,
6, 3, 16, 2, 2, 1,
];
assert_eq!(bytes, expected_bytes);
}
#[test]
fn test_decode3() {
let bytes: Vec<u8> = vec![
48, 130, 1, 53, 2, 1, 1, 4, 8, 114, 101, 97, 100, 111, 110, 108,
121, 162, 130, 1, 36, 2, 1, 1, 2, 1, 0, 2, 1, 0, 48, 130,
1, 23, 48, 20, 6, 15, 43, 6, 1, 4, 1, 130, 139, 19, 1, 134,
141, 31, 69, 1, 0, 2, 1, 214, 48, 43, 6, 15, 43, 6, 1, 4,
1, 130, 139, 19, 1, 134, 141, 31, 69, 2, 0, 4, 24, 65, 32, 99,
111, 99, 111, 110, 117, 116, 32, 105, 115, 32, 106, 117, 115, 116, 32, 97,
32, 110, 117, 116, 46, 48, 22, 6, 15, 43, 6, 1, 4, 1, 130, 139,
19, 1, 134, 141, 31, 69, 3, 0, 4, 3, 23, 42, 69, 48, 33, 6,
15, 43, 6, 1, 4, 1, 130, 139, 19, 1, 134, 141, 31, 69, 4, 0,
6, 14, 43, 6, 1, 4, 1, 130, 139, 19, 1, 134, 141, 31, 69, 9,
48, 23, 6, 15, 43, 6, 1, 4, 1, 130, 139, 19, 1, 134, 141, 31,
69, 5, 0, 64, 4, 128, 131, 34, 30, 48, 24, 6, 15, 43, 6, 1,
4, 1, 130, 139, 19, 1, 134, 141, 31, 69, 6, 0, 65, 5, 0, 254,
254, 254, 254, 48, 24, 6, 15, 43, 6, 1, 4, 1, 130, 139, 19, 1,
134, 141, 31, 69, 7, 0, 66, 5, 0, 222, 173, 190, 239, 48, 24, 6,
15, 43, 6, 1, 4, 1, 130, 139, 19, 1, 134, 141, 31, 69, 8, 0,
67, 5, 0, 165, 95, 172, 229, 48, 28, 6, 15, 43, 6, 1, 4, 1,
130, 139, 19, 1, 134, 141, 31, 69, 9, 0, 70, 9, 0, 222, 173, 190,
239, 165, 95, 172, 229, 48, 18, 6, 10, 43, 6, 1, 6, 3, 1, 1,
6, 1, 0, 2, 4, 36, 24, 7, 18,
];
let asn1: Snmp2cMessage = der_decode(&bytes).unwrap();
assert_eq!(asn1.version, 1);
assert_eq!(asn1.community, b"readonly");
let inner_pdu = match asn1.pdu {
Snmp2cPdu::Response(inner) => inner,
_ => panic!(),
};
assert_eq!(inner_pdu.request_id, 1);
assert_eq!(inner_pdu.error_status, ErrorStatus::NoError);
assert_eq!(inner_pdu.error_index, 0);
assert_eq!(inner_pdu.variable_bindings.len(), 10);
assert_eq!(inner_pdu.variable_bindings[0].name, "1.3.6.1.4.1.34195.1.99999.69.1.0".parse().unwrap());
assert_eq!(gimme_int(&inner_pdu.variable_bindings[0]), -42);
assert_eq!(inner_pdu.variable_bindings[1].name, "1.3.6.1.4.1.34195.1.99999.69.2.0".parse().unwrap());
assert_eq!(gimme_octets(&inner_pdu.variable_bindings[1]), b"A coconut is just a nut.");
assert_eq!(inner_pdu.variable_bindings[2].name, "1.3.6.1.4.1.34195.1.99999.69.3.0".parse().unwrap());
assert_eq!(gimme_octets(&inner_pdu.variable_bindings[2]), &[23, 42, 69]);
assert_eq!(inner_pdu.variable_bindings[3].name, "1.3.6.1.4.1.34195.1.99999.69.4.0".parse().unwrap());
assert_eq!(gimme_oid(&inner_pdu.variable_bindings[3]), "1.3.6.1.4.1.34195.1.99999.69.9".parse().unwrap());
assert_eq!(inner_pdu.variable_bindings[4].name, "1.3.6.1.4.1.34195.1.99999.69.5.0".parse().unwrap());
assert_eq!(gimme_ip(&inner_pdu.variable_bindings[4]), "128.131.34.30".parse::<Ipv4Addr>().unwrap());
assert_eq!(inner_pdu.variable_bindings[5].name, "1.3.6.1.4.1.34195.1.99999.69.6.0".parse().unwrap());
assert_eq!(gimme_counter(&inner_pdu.variable_bindings[5]), 0xFEFEFEFE);
assert_eq!(inner_pdu.variable_bindings[6].name, "1.3.6.1.4.1.34195.1.99999.69.7.0".parse().unwrap());
assert_eq!(gimme_unsigned(&inner_pdu.variable_bindings[6]), 0xDEADBEEF);
assert_eq!(inner_pdu.variable_bindings[7].name, "1.3.6.1.4.1.34195.1.99999.69.8.0".parse().unwrap());
assert_eq!(gimme_time(&inner_pdu.variable_bindings[7]), 0xA55FACE5);
assert_eq!(inner_pdu.variable_bindings[8].name, "1.3.6.1.4.1.34195.1.99999.69.9.0".parse().unwrap());
assert_eq!(gimme_counter64(&inner_pdu.variable_bindings[8]), 0xDEADBEEFA55FACE5);
assert_eq!(inner_pdu.variable_bindings[9].name, "1.3.6.1.6.3.1.1.6.1.0".parse().unwrap());
assert_eq!(gimme_int(&inner_pdu.variable_bindings[9]), 605554450);
}
#[test]
fn test_encode3() {
let message = Snmp2cMessage {
version: 1,
community: b"readonly".to_vec(),
pdu: Snmp2cPdu::Response(InnerPdu {
request_id: 1,
error_status: ErrorStatus::NoError,
error_index: 0,
variable_bindings: vec![
VariableBinding {
name: "1.3.6.1.4.1.34195.1.99999.69.1.0".parse().unwrap(),
value: BindingValue::Value(ObjectValue::Integer(-42)),
},
VariableBinding {
name: "1.3.6.1.4.1.34195.1.99999.69.2.0".parse().unwrap(),
value: BindingValue::Value(ObjectValue::String(b"A coconut is just a nut.".to_vec())),
},
VariableBinding {
name: "1.3.6.1.4.1.34195.1.99999.69.3.0".parse().unwrap(),
value: BindingValue::Value(ObjectValue::String([23, 42, 69].to_vec())),
},
VariableBinding {
name: "1.3.6.1.4.1.34195.1.99999.69.4.0".parse().unwrap(),
value: BindingValue::Value(ObjectValue::ObjectId("1.3.6.1.4.1.34195.1.99999.69.9".parse().unwrap())),
},
VariableBinding {
name: "1.3.6.1.4.1.34195.1.99999.69.5.0".parse().unwrap(),
value: BindingValue::Value(ObjectValue::IpAddress("128.131.34.30".parse().unwrap())),
},
VariableBinding {
name: "1.3.6.1.4.1.34195.1.99999.69.6.0".parse().unwrap(),
value: BindingValue::Value(ObjectValue::Counter32(0xFEFEFEFE)),
},
VariableBinding {
name: "1.3.6.1.4.1.34195.1.99999.69.7.0".parse().unwrap(),
value: BindingValue::Value(ObjectValue::Unsigned32(0xDEADBEEF)),
},
VariableBinding {
name: "1.3.6.1.4.1.34195.1.99999.69.8.0".parse().unwrap(),
value: BindingValue::Value(ObjectValue::TimeTicks(0xA55FACE5)),
},
VariableBinding {
name: "1.3.6.1.4.1.34195.1.99999.69.9.0".parse().unwrap(),
value: BindingValue::Value(ObjectValue::Counter64(0xDEADBEEFA55FACE5)),
},
VariableBinding {
name: "1.3.6.1.6.3.1.1.6.1.0".parse().unwrap(),
value: BindingValue::Value(ObjectValue::Integer(605554450)),
},
],
}),
};
let bytes = der_encode(&message).unwrap();
let expected_bytes: Vec<u8> = vec![
48, 130, 1, 53, 2, 1, 1, 4, 8, 114, 101, 97, 100, 111, 110, 108,
121, 162, 130, 1, 36, 2, 1, 1, 2, 1, 0, 2, 1, 0, 48, 130,
1, 23, 48, 20, 6, 15, 43, 6, 1, 4, 1, 130, 139, 19, 1, 134,
141, 31, 69, 1, 0, 2, 1, 214, 48, 43, 6, 15, 43, 6, 1, 4,
1, 130, 139, 19, 1, 134, 141, 31, 69, 2, 0, 4, 24, 65, 32, 99,
111, 99, 111, 110, 117, 116, 32, 105, 115, 32, 106, 117, 115, 116, 32, 97,
32, 110, 117, 116, 46, 48, 22, 6, 15, 43, 6, 1, 4, 1, 130, 139,
19, 1, 134, 141, 31, 69, 3, 0, 4, 3, 23, 42, 69, 48, 33, 6,
15, 43, 6, 1, 4, 1, 130, 139, 19, 1, 134, 141, 31, 69, 4, 0,
6, 14, 43, 6, 1, 4, 1, 130, 139, 19, 1, 134, 141, 31, 69, 9,
48, 23, 6, 15, 43, 6, 1, 4, 1, 130, 139, 19, 1, 134, 141, 31,
69, 5, 0, 64, 4, 128, 131, 34, 30, 48, 24, 6, 15, 43, 6, 1,
4, 1, 130, 139, 19, 1, 134, 141, 31, 69, 6, 0, 65, 5, 0, 254,
254, 254, 254, 48, 24, 6, 15, 43, 6, 1, 4, 1, 130, 139, 19, 1,
134, 141, 31, 69, 7, 0, 66, 5, 0, 222, 173, 190, 239, 48, 24, 6,
15, 43, 6, 1, 4, 1, 130, 139, 19, 1, 134, 141, 31, 69, 8, 0,
67, 5, 0, 165, 95, 172, 229, 48, 28, 6, 15, 43, 6, 1, 4, 1,
130, 139, 19, 1, 134, 141, 31, 69, 9, 0, 70, 9, 0, 222, 173, 190,
239, 165, 95, 172, 229, 48, 18, 6, 10, 43, 6, 1, 6, 3, 1, 1,
6, 1, 0, 2, 4, 36, 24, 7, 18,
];
assert_eq!(bytes, expected_bytes);
}
}