use rasn::{
types::{Integer, ObjectIdentifier, OctetString},
AsnType, Decode, Encode,
};
use smi::v1::{NetworkAddress, ObjectName, ObjectSyntax, TimeTicks};
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct Message<T> {
pub version: Integer,
pub community: OctetString,
pub data: T,
}
impl<T> Message<T> {
pub const VERSION_1: u64 = 0;
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[rasn(choice)]
pub enum Pdus {
GetRequest(GetRequest),
GetNextRequest(GetNextRequest),
GetResponse(GetResponse),
SetRequest(SetRequest),
Trap(Trap),
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[rasn(tag(0))]
#[rasn(delegate)]
pub struct GetRequest(pub Pdu);
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[rasn(tag(1))]
#[rasn(delegate)]
pub struct GetNextRequest(pub Pdu);
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[rasn(tag(2))]
#[rasn(delegate)]
pub struct GetResponse(pub Pdu);
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[rasn(tag(3))]
#[rasn(delegate)]
pub struct SetRequest(pub Pdu);
pub type VarBindList = alloc::vec::Vec<VarBind>;
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct Pdu {
pub request_id: Integer,
pub error_status: Integer,
pub error_index: Integer,
pub variable_bindings: VarBindList,
}
impl Pdu {
pub const ERROR_STATUS_NO_ERROR: u64 = 0;
pub const ERROR_STATUS_TOO_BIG: u64 = 1;
pub const ERROR_STATUS_NO_SUCH_NAME: u64 = 2;
pub const ERROR_STATUS_BAD_VALUE: u64 = 3;
pub const ERROR_STATUS_READ_ONLY: u64 = 4;
pub const ERROR_STATUS_GEN_ERR: u64 = 5;
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[rasn(tag(context, 4))]
pub struct Trap {
pub enterprise: ObjectIdentifier,
pub agent_addr: NetworkAddress,
pub generic_trap: Integer,
pub specific_trap: Integer,
pub time_stamp: TimeTicks,
pub variable_bindings: VarBindList,
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct VarBind {
pub name: ObjectName,
pub value: ObjectSyntax,
}
#[cfg(test)]
mod tests {
use super::{Message, Trap, VarBind};
use alloc::{string::String, string::ToString, vec, vec::Vec};
use pretty_assertions::assert_eq;
use rasn::types::ObjectIdentifier;
use smi::v1::{Gauge, IpAddress, NetworkAddress, TimeTicks};
fn string_oid(oid: impl AsRef<[u32]>) -> String {
oid.as_ref()
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(".")
}
#[test]
fn trap() {
#[rustfmt::skip]
let decode_data = [
0x30, 0x4f,
0x02, 0x01,
0x00,
0x04, 0x06,
0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
0xa4, 0x42,
0x06, 0x0c,
0x2b, 0x06, 0x01, 0x04, 0x01, 0xDC, 0x03, 0x01,
0x2a, 0x03, 0x07, 0x08,
0x40, 0x04,
0x0a, 0x0b, 0x0c, 0x0d,
0x02, 0x01,
0x06,
0x02, 0x01,
0x02,
0x43, 0x02,
0x2e, 0x9c,
0x30, 0x22,
0x30, 0x0d,
0x06, 0x07,
0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x03,
0x43, 0x02,
0x2e, 0x9c,
0x30, 0x11,
0x06, 0x0c,
0x2b, 0x06, 0x01, 0x04, 0x01, 0xDC, 0x03, 0x01,
0x2a, 0x02, 0x01, 0x07,
0x42, 0x01,
0x01,
];
let decode_msg: Message<Trap> = rasn::ber::decode(&decode_data).unwrap();
assert_eq!(decode_msg.version, 0.into());
assert_eq!(decode_msg.community, "public".as_bytes());
assert_eq!(
string_oid(decode_msg.data.enterprise),
"1.3.6.1.4.1.11779.1.42.3.7.8"
);
assert_eq!(
decode_msg.data.agent_addr,
NetworkAddress::Internet(IpAddress([10, 11, 12, 13].into()))
);
assert_eq!(decode_msg.data.generic_trap, 6.into());
assert_eq!(decode_msg.data.specific_trap, 2.into());
assert_eq!(decode_msg.data.time_stamp, TimeTicks(11_932));
assert_eq!(decode_msg.data.variable_bindings.len(), 2);
let encode_msg = Message {
version: 0.into(),
community: "public".into(),
data: Trap {
enterprise: ObjectIdentifier::new_unchecked(
vec![1, 3, 6, 1, 4, 1, 11779, 1, 42, 3, 7, 8].into(),
),
agent_addr: NetworkAddress::Internet(IpAddress([10, 11, 12, 13].into())),
generic_trap: 6.into(),
specific_trap: 2.into(),
time_stamp: TimeTicks(11_932),
variable_bindings: vec![
VarBind {
name: ObjectIdentifier::new_unchecked(vec![1, 3, 6, 1, 2, 1, 1, 3].into()),
value: TimeTicks(11_932).into(),
},
VarBind {
name: ObjectIdentifier::new_unchecked(
vec![1, 3, 6, 1, 4, 1, 11779, 1, 42, 2, 1, 7].into(),
),
value: Gauge(1).into(),
},
],
},
};
let encode_data = rasn::ber::encode(&encode_msg).unwrap();
assert_eq!(encode_data, decode_data);
let encode_msg_no_bindings = Message {
data: Trap {
variable_bindings: vec![],
..encode_msg.data
},
..encode_msg
};
assert!(rasn::ber::encode(&encode_msg_no_bindings).is_ok());
}
}