use crate::error::internal::DecodeErrorKind;
use crate::error::{Error, Result, UNKNOWN_TARGET};
use crate::oid::Oid;
use crate::pdu::Pdu;
use crate::value::Value;
use crate::varbind::VarBind;
use super::oids;
pub(crate) fn extract_notification_varbinds(pdu: &Pdu) -> Result<(u32, Oid, Vec<VarBind>)> {
extract_notification_varbinds_impl(pdu, false)
}
fn extract_notification_varbinds_impl(pdu: &Pdu, strict: bool) -> Result<(u32, Oid, Vec<VarBind>)> {
let target = UNKNOWN_TARGET;
if pdu.varbinds.len() < 2 {
tracing::debug!(target: "async_snmp::notification", { kind = %DecodeErrorKind::MissingPdu }, "notification has fewer than 2 varbinds");
return Err(Error::MalformedResponse { target }.boxed());
}
if strict && pdu.varbinds[0].oid != oids::sys_uptime() {
tracing::warn!(target: "async_snmp::notification", { expected = %oids::sys_uptime(), actual = %pdu.varbinds[0].oid }, "strict mode: first varbind OID is not sysUpTime.0");
tracing::debug!(target: "async_snmp::notification", { kind = %DecodeErrorKind::InvalidOid }, "invalid first varbind OID");
return Err(Error::MalformedResponse { target }.boxed());
}
let uptime = match &pdu.varbinds[0].value {
Value::TimeTicks(t) => *t,
_other => {
tracing::debug!(target: "async_snmp::notification", { kind = %DecodeErrorKind::MissingPdu }, "first varbind is not TimeTicks");
return Err(Error::MalformedResponse { target }.boxed());
}
};
if strict && pdu.varbinds[1].oid != oids::snmp_trap_oid() {
tracing::warn!(target: "async_snmp::notification", { expected = %oids::snmp_trap_oid(), actual = %pdu.varbinds[1].oid }, "strict mode: second varbind OID is not snmpTrapOID.0");
tracing::debug!(target: "async_snmp::notification", { kind = %DecodeErrorKind::InvalidOid }, "invalid second varbind OID");
return Err(Error::MalformedResponse { target }.boxed());
}
let trap_oid = match &pdu.varbinds[1].value {
Value::ObjectIdentifier(oid) => oid.clone(),
_other => {
tracing::debug!(target: "async_snmp::notification", { kind = %DecodeErrorKind::MissingPdu }, "second varbind is not OID");
return Err(Error::MalformedResponse { target }.boxed());
}
};
let varbinds = pdu.varbinds[2..].to_vec();
Ok((uptime, trap_oid, varbinds))
}
pub fn validate_notification_varbinds(pdu: &Pdu) -> bool {
if pdu.varbinds.len() < 2 {
return false;
}
if pdu.varbinds[0].oid != oids::sys_uptime() {
return false;
}
if !matches!(pdu.varbinds[0].value, Value::TimeTicks(_)) {
return false;
}
if pdu.varbinds[1].oid != oids::snmp_trap_oid() {
return false;
}
if !matches!(pdu.varbinds[1].value, Value::ObjectIdentifier(_)) {
return false;
}
true
}
#[cfg(test)]
mod tests {
use super::*;
use crate::oid;
use crate::pdu::PduType;
#[test]
fn test_extract_notification_varbinds() {
let pdu = Pdu {
pdu_type: PduType::TrapV2,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: vec![
VarBind::new(oids::sys_uptime(), Value::TimeTicks(12345)),
VarBind::new(
oids::snmp_trap_oid(),
Value::ObjectIdentifier(oids::link_down()),
),
VarBind::new(oid!(1, 3, 6, 1, 2, 1, 2, 2, 1, 1, 1), Value::Integer(1)),
],
};
let (uptime, trap_oid, varbinds) = extract_notification_varbinds(&pdu).unwrap();
assert_eq!(uptime, 12345);
assert_eq!(trap_oid, oids::link_down());
assert_eq!(varbinds.len(), 1);
}
#[test]
fn test_extract_notification_varbinds_too_few() {
let pdu = Pdu {
pdu_type: PduType::TrapV2,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: vec![VarBind::new(oids::sys_uptime(), Value::TimeTicks(12345))],
};
let result = extract_notification_varbinds(&pdu);
assert!(result.is_err());
}
#[test]
fn test_validate_notification_varbinds_valid() {
let pdu = Pdu {
pdu_type: PduType::TrapV2,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: vec![
VarBind::new(oids::sys_uptime(), Value::TimeTicks(12345)),
VarBind::new(
oids::snmp_trap_oid(),
Value::ObjectIdentifier(oids::link_down()),
),
],
};
assert!(validate_notification_varbinds(&pdu));
}
#[test]
fn test_validate_notification_varbinds_wrong_first_oid() {
let pdu = Pdu {
pdu_type: PduType::TrapV2,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: vec![
VarBind::new(oid!(1, 2, 3, 4), Value::TimeTicks(12345)),
VarBind::new(
oids::snmp_trap_oid(),
Value::ObjectIdentifier(oids::link_down()),
),
],
};
assert!(!validate_notification_varbinds(&pdu));
}
#[test]
fn test_validate_notification_varbinds_wrong_second_oid() {
let pdu = Pdu {
pdu_type: PduType::TrapV2,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: vec![
VarBind::new(oids::sys_uptime(), Value::TimeTicks(12345)),
VarBind::new(oid!(1, 2, 3, 4), Value::ObjectIdentifier(oids::link_down())),
],
};
assert!(!validate_notification_varbinds(&pdu));
}
#[test]
fn test_validate_notification_varbinds_wrong_first_type() {
let pdu = Pdu {
pdu_type: PduType::TrapV2,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: vec![
VarBind::new(oids::sys_uptime(), Value::Integer(12345)),
VarBind::new(
oids::snmp_trap_oid(),
Value::ObjectIdentifier(oids::link_down()),
),
],
};
assert!(!validate_notification_varbinds(&pdu));
}
#[test]
fn test_validate_notification_varbinds_wrong_second_type() {
let pdu = Pdu {
pdu_type: PduType::TrapV2,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: vec![
VarBind::new(oids::sys_uptime(), Value::TimeTicks(12345)),
VarBind::new(oids::snmp_trap_oid(), Value::Integer(1)),
],
};
assert!(!validate_notification_varbinds(&pdu));
}
#[test]
fn test_validate_notification_varbinds_too_few() {
let pdu = Pdu {
pdu_type: PduType::TrapV2,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: vec![
VarBind::new(oids::sys_uptime(), Value::TimeTicks(12345)),
],
};
assert!(!validate_notification_varbinds(&pdu));
}
}