use std::{fmt,str};
use std::net::Ipv4Addr;
use std::slice::Iter;
use nom::{IResult,Err,ErrorKind};
use der_parser::*;
use der_parser::oid::Oid;
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct PduType(pub u8);
#[allow(non_upper_case_globals)]
impl PduType {
pub const GetRequest : PduType = PduType(0);
pub const GetNextRequest : PduType = PduType(1);
pub const Response : PduType = PduType(2);
pub const SetRequest : PduType = PduType(3);
pub const TrapV1 : PduType = PduType(4);
pub const GetBulkRequest : PduType = PduType(5);
pub const InformRequest : PduType = PduType(6);
pub const TrapV2 : PduType = PduType(7);
pub const Report : PduType = PduType(8);
}
impl fmt::Debug for PduType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
0 => f.write_str("GetRequest"),
1 => f.write_str("GetNextRequest"),
2 => f.write_str("Response"),
3 => f.write_str("SetRequest"),
4 => f.write_str("TrapV1"),
5 => f.write_str("GetBulkRequest"),
6 => f.write_str("InformRequest"),
7 => f.write_str("TrapV2"),
8 => f.write_str("Report"),
n => f.debug_tuple("PduType").field(&n).finish(),
}
}
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct TrapType(pub u8);
impl TrapType {
pub const COLD_START : TrapType = TrapType(0);
pub const WARM_START : TrapType = TrapType(1);
pub const LINK_DOWN : TrapType = TrapType(2);
pub const LINK_UP : TrapType = TrapType(3);
pub const AUTHENTICATION_FAILURE : TrapType = TrapType(4);
pub const EGP_NEIGHBOR_LOSS : TrapType = TrapType(5);
pub const ENTERPRISE_SPECIFIC : TrapType = TrapType(6);
}
impl fmt::Debug for TrapType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
0 => f.write_str("coldStart"),
1 => f.write_str("warmStart"),
2 => f.write_str("linkDown"),
3 => f.write_str("linkUp"),
4 => f.write_str("authenticationFailure"),
5 => f.write_str("egpNeighborLoss"),
6 => f.write_str("enterpriseSpecific"),
n => f.debug_tuple("TrapType").field(&n).finish(),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum NetworkAddress {
IPv4(Ipv4Addr),
}
pub type Counter = u32;
pub type Gauge = u32;
pub type TimeTicks = u32;
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct ErrorStatus(pub u32);
#[allow(non_upper_case_globals)]
impl ErrorStatus {
pub const NoError : ErrorStatus = ErrorStatus(0);
pub const TooBig : ErrorStatus = ErrorStatus(1);
pub const NoSuchName : ErrorStatus = ErrorStatus(2);
pub const BadValue : ErrorStatus = ErrorStatus(3);
pub const ReadOnly : ErrorStatus = ErrorStatus(4);
pub const GenErr : ErrorStatus = ErrorStatus(5);
}
impl fmt::Debug for ErrorStatus {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
0 => f.write_str("NoError"),
1 => f.write_str("TooBig"),
2 => f.write_str("NoSuchName"),
3 => f.write_str("BadValue"),
4 => f.write_str("ReadOnly"),
5 => f.write_str("GenErr"),
n => f.debug_tuple("ErrorStatus").field(&n).finish(),
}
}
}
#[derive(Debug,PartialEq)]
pub struct SnmpGenericPdu<'a> {
pub pdu_type: PduType,
pub req_id: u32,
pub err: ErrorStatus,
pub err_index: u32,
pub var: Vec<SnmpVariable<'a>>,
}
#[derive(Debug,PartialEq)]
pub struct SnmpBulkPdu<'a> {
pub req_id: u32,
pub non_repeaters: u32,
pub max_repetitions: u32,
pub var: Vec<SnmpVariable<'a>>,
}
#[derive(Debug,PartialEq)]
pub struct SnmpTrapPdu<'a> {
pub enterprise: Oid,
pub agent_addr: NetworkAddress,
pub generic_trap: TrapType,
pub specific_trap: u32,
pub timestamp: TimeTicks,
pub var: Vec<SnmpVariable<'a>>,
}
#[derive(Debug,PartialEq)]
pub enum SnmpPdu<'a> {
Generic(SnmpGenericPdu<'a>),
Bulk(SnmpBulkPdu<'a>),
TrapV1(SnmpTrapPdu<'a>),
}
#[derive(Debug,PartialEq)]
pub struct SnmpMessage<'a> {
pub version: u32,
pub community: String,
pub pdu: SnmpPdu<'a>,
}
impl<'a> SnmpGenericPdu<'a> {
pub fn vars_iter(&'a self) -> Iter<SnmpVariable> {
self.var.iter()
}
}
impl<'a> SnmpTrapPdu<'a> {
pub fn vars_iter(&'a self) -> Iter<SnmpVariable> {
self.var.iter()
}
}
impl<'a> SnmpPdu<'a> {
pub fn pdu_type(&self) -> PduType {
match *self {
SnmpPdu::Generic(ref pdu) => pdu.pdu_type,
SnmpPdu::Bulk(_) => PduType::GetBulkRequest,
SnmpPdu::TrapV1(_) => PduType::TrapV1,
}
}
pub fn vars_iter(&'a self) -> Iter<SnmpVariable> {
match *self {
SnmpPdu::Generic(ref pdu) => pdu.var.iter(),
SnmpPdu::Bulk(ref pdu) => pdu.var.iter(),
SnmpPdu::TrapV1(ref pdu) => pdu.var.iter(),
}
}
}
impl<'a> SnmpMessage<'a> {
pub fn pdu_type(&self) -> PduType {
self.pdu.pdu_type()
}
pub fn vars_iter(&'a self) -> Iter<SnmpVariable> {
self.pdu.vars_iter()
}
}
#[derive(Debug,PartialEq)]
pub struct SnmpVariable<'a> {
pub oid: Oid,
pub val: ObjectSyntax<'a>
}
#[derive(Debug,PartialEq)]
pub enum ObjectSyntax<'a> {
Number(DerObject<'a>),
String(&'a[u8]),
Object(Oid),
BitString(u8, BitStringObject<'a>),
Empty,
UnknownSimple(DerObject<'a>),
IpAddress(NetworkAddress),
Counter32(Counter),
Gauge32(Gauge),
TimeTicks(TimeTicks),
Opaque(&'a[u8]),
NsapAddress(&'a[u8]),
Counter64(u64),
UInteger32(u32),
UnknownApplication(u8, &'a[u8]),
}
pub(crate) fn parse_der_octetstring_as_slice(i:&[u8]) -> IResult<&[u8],&[u8]> {
match parse_der_octetstring(i) {
Ok((rem,ref obj)) => {
match obj.content {
DerObjectContent::OctetString(s) => {
Ok((rem, s))
}
_ => Err(Err::Error(error_position!(i, ErrorKind::Custom(DER_TAG_ERROR)))),
}
}
Err(e) => Err(e)
}
}
fn parse_objectsyntax<'a>(i:&'a[u8]) -> IResult<&'a[u8],ObjectSyntax> {
match der_read_element_header(i) {
Ok((rem,hdr)) => {
if hdr.is_application() {
match hdr.tag {
0 => {
map_res!(
rem,
apply!(der_read_element_content_as,DerTag::OctetString as u8, hdr.len as usize),
|x:DerObjectContent| {
match x {
DerObjectContent::OctetString(s) if s.len() == 4 => {
Ok(ObjectSyntax::IpAddress(NetworkAddress::IPv4(Ipv4Addr::new(s[0],s[1],s[2],s[3]))))
},
_ => Err(DER_TAG_ERROR),
}
}
)
},
1 ... 3 => {
map_res!(
rem,
apply!(der_read_element_content_as, DerTag::Integer as u8, hdr.len as usize),
|x:DerObjectContent| {
x.as_u32().map(|x| {
match hdr.tag {
1 => ObjectSyntax::Counter32(x),
2 => ObjectSyntax::Gauge32(x),
3 => ObjectSyntax::TimeTicks(x),
_ => unreachable!(),
}
})
}
)
},
4 => {
map!(rem, take!(hdr.len as usize), ObjectSyntax::Opaque)
},
5 => {
map!(rem, take!(hdr.len as usize), ObjectSyntax::NsapAddress)
},
6 => {
map_res!(
rem,
apply!(der_read_element_content_as, DerTag::Integer as u8, hdr.len as usize),
|x:DerObjectContent| {
x.as_u64().map(ObjectSyntax::Counter64)
}
)
},
7 => {
map_res!(
rem,
apply!(der_read_element_content_as, DerTag::Integer as u8, hdr.len as usize),
|x:DerObjectContent| {
x.as_u32().map(ObjectSyntax::UInteger32)
}
)
},
_ => {
map!(rem, take!(hdr.len as usize), |x| ObjectSyntax::UnknownApplication(hdr.tag,x))
},
}
} else {
if hdr.len == 0 { return Ok((rem, ObjectSyntax::Empty)); }
map_res!(
rem,
apply!(der_read_element_content_as, hdr.tag, hdr.len as usize),
|x:DerObjectContent<'a>| {
match x {
DerObjectContent::Integer(_) => Ok(ObjectSyntax::Number(DerObject::from_obj(x))),
DerObjectContent::OctetString(s) => Ok(ObjectSyntax::String(s)),
DerObjectContent::OID(o) => Ok(ObjectSyntax::Object(o)),
DerObjectContent::BitString(a,s) => Ok(ObjectSyntax::BitString(a,s)),
DerObjectContent::Null => Ok(ObjectSyntax::Empty),
_ => Ok(ObjectSyntax::UnknownSimple(DerObject::from_obj(x))) as Result<_,u32>,
}
}
)
}
},
Err(e) => Err(e)
}
}
#[inline]
fn parse_varbind(i:&[u8]) -> IResult<&[u8],SnmpVariable> {
parse_der_struct!(
i,
TAG DerTag::Sequence,
oid: map_res!(parse_der_oid, |x:DerObject| x.as_oid_val()) >>
val: parse_objectsyntax >>
(
SnmpVariable{ oid, val }
)
).map(|(rem,x)| (rem,x.1))
}
#[inline]
fn parse_varbind_list(i:&[u8]) -> IResult<&[u8],Vec<SnmpVariable>> {
parse_der_struct!(
i,
TAG DerTag::Sequence,
l: many0!(complete!(parse_varbind)) >>
( l )
).map(|(rem,x)| (rem,x.1))
}
fn parse_networkaddress(i:&[u8]) -> IResult<&[u8],NetworkAddress> {
match parse_der(i) {
Ok((rem,obj)) => {
if obj.tag != 0 || obj.class != 0b01 {
return Err(Err::Error(error_position!(i, ErrorKind::Custom(DER_TAG_ERROR))));
}
match obj.content {
DerObjectContent::Unknown(s) if s.len() == 4 => {
Ok((rem, NetworkAddress::IPv4(Ipv4Addr::new(s[0],s[1],s[2],s[3]))))
},
_ => Err(Err::Error(error_position!(i, ErrorKind::Custom(DER_TAG_ERROR)))),
}
},
Err(e) => Err(e)
}
}
fn parse_timeticks(i:&[u8]) -> IResult<&[u8],TimeTicks> {
fn der_read_integer_content(i:&[u8], _tag:u8, len: usize) -> IResult<&[u8],DerObjectContent,u32> {
der_read_element_content_as(i, DerTag::Integer as u8, len)
}
map_res!(i, apply!(parse_der_implicit, 3, der_read_integer_content), |x: DerObject| {
match x.as_context_specific() {
Ok((_,Some(x))) => x.as_u32(),
_ => Err(DerError::DerTypeError),
}
})
}
fn parse_snmp_v1_generic_pdu(pdu: &[u8], tag:PduType) -> IResult<&[u8],SnmpPdu> {
do_parse!(pdu,
req_id: parse_der_u32 >>
err: parse_der_u32 >>
err_index: parse_der_u32 >>
var_bindings: parse_varbind_list >>
(
SnmpPdu::Generic(
SnmpGenericPdu {
pdu_type: tag,
req_id,
err: ErrorStatus(err),
err_index,
var: var_bindings
}
)
))
}
fn parse_snmp_v1_bulk_pdu(pdu: &[u8]) -> IResult<&[u8],SnmpPdu> {
do_parse!(pdu,
req_id: parse_der_u32 >>
non_repeaters: parse_der_u32 >>
max_repetitions: parse_der_u32 >>
var_bindings: parse_varbind_list >>
(
SnmpPdu::Bulk(
SnmpBulkPdu {
req_id,
non_repeaters,
max_repetitions,
var: var_bindings
}
)
))
}
fn parse_snmp_v1_trap_pdu(pdu: &[u8]) -> IResult<&[u8],SnmpPdu> {
do_parse!(
pdu,
enterprise: map_res!(parse_der_oid, |x: DerObject| x.as_oid_val()) >>
agent_addr: parse_networkaddress >>
generic_trap: parse_der_u32 >>
specific_trap: parse_der_u32 >>
timestamp: parse_timeticks >>
var_bindings: parse_varbind_list >>
(
SnmpPdu::TrapV1(
SnmpTrapPdu {
enterprise,
agent_addr,
generic_trap: TrapType(generic_trap as u8),
specific_trap,
timestamp,
var: var_bindings
}
)
)
)
}
pub fn parse_snmp_v1(i:&[u8]) -> IResult<&[u8],SnmpMessage> {
parse_der_struct!(
i,
TAG DerTag::Sequence,
version: parse_der_u32 >>
error_if!(version != 0, ErrorKind::Tag) >>
community: map_res!(
parse_der_octetstring_as_slice,
|s| str::from_utf8(s)
) >>
pdu: parse_snmp_v1_pdu >>
(
SnmpMessage{
version,
community: community.to_string(),
pdu
}
)
).map(|(rem,x)| (rem,x.1))
}
pub(crate) fn parse_snmp_v1_pdu(i:&[u8]) -> IResult<&[u8],SnmpPdu> {
match der_read_element_header(i) {
Ok((rem,hdr)) => {
match PduType(hdr.tag) {
PduType::GetRequest |
PduType::GetNextRequest |
PduType::Response |
PduType::SetRequest => parse_snmp_v1_generic_pdu(rem, PduType(hdr.tag)),
PduType::TrapV1 => parse_snmp_v1_trap_pdu(rem),
_ => Err(Err::Error(error_position!(i, ErrorKind::Custom(128)))),
}
},
Err(e) => Err(e)
}
}
pub fn parse_snmp_v2c(i:&[u8]) -> IResult<&[u8],SnmpMessage> {
parse_der_struct!(
i,
TAG DerTag::Sequence,
version: parse_der_u32 >>
error_if!(version != 1, ErrorKind::Tag) >>
community: map_res!(
parse_der_octetstring_as_slice,
|s| str::from_utf8(s)
) >>
pdu: parse_snmp_v2c_pdu >>
(
SnmpMessage{
version,
community: community.to_string(),
pdu
}
)
).map(|(rem,x)| (rem,x.1))
}
pub(crate) fn parse_snmp_v2c_pdu(i:&[u8]) -> IResult<&[u8],SnmpPdu> {
match der_read_element_header(i) {
Ok((rem,hdr)) => {
match PduType(hdr.tag) {
PduType::GetRequest |
PduType::GetNextRequest |
PduType::Response |
PduType::SetRequest |
PduType::InformRequest |
PduType::TrapV2 |
PduType::Report => parse_snmp_v1_generic_pdu(rem, PduType(hdr.tag)),
PduType::GetBulkRequest => parse_snmp_v1_bulk_pdu(rem),
PduType::TrapV1 => parse_snmp_v1_trap_pdu(rem),
_ => Err(Err::Error(error_position!(i, ErrorKind::Custom(128)))),
}
},
Err(e) => Err(e)
}
}