1use crate::{error::SnmpError, Oid};
12use asn1_rs::{
13 Any, BitString, Class, Error, FromBer, Header, Implicit, Integer, Sequence, Tag, TaggedValue,
14};
15use nom::combinator::map;
16use nom::{Err, IResult};
17use std::convert::TryFrom;
18use std::net::Ipv4Addr;
19use std::slice::Iter;
20use std::{fmt, str};
21
22type Application<T, E, TagKind, const TAG: u32> = TaggedValue<T, E, TagKind, 0b01, TAG>;
24
25#[derive(Clone, Copy, Eq, PartialEq)]
26pub struct PduType(pub u32);
27
28#[allow(non_upper_case_globals)]
29impl PduType {
30 pub const GetRequest: PduType = PduType(0);
31 pub const GetNextRequest: PduType = PduType(1);
32 pub const Response: PduType = PduType(2);
33 pub const SetRequest: PduType = PduType(3);
34 pub const TrapV1: PduType = PduType(4); pub const GetBulkRequest: PduType = PduType(5);
36 pub const InformRequest: PduType = PduType(6);
37 pub const TrapV2: PduType = PduType(7);
38 pub const Report: PduType = PduType(8);
39}
40
41impl fmt::Debug for PduType {
42 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43 match self.0 {
44 0 => f.write_str("GetRequest"),
45 1 => f.write_str("GetNextRequest"),
46 2 => f.write_str("Response"),
47 3 => f.write_str("SetRequest"),
48 4 => f.write_str("TrapV1"),
49 5 => f.write_str("GetBulkRequest"),
50 6 => f.write_str("InformRequest"),
51 7 => f.write_str("TrapV2"),
52 8 => f.write_str("Report"),
53 n => f.debug_tuple("PduType").field(&n).finish(),
54 }
55 }
56}
57
58#[derive(Clone, Copy, Eq, PartialEq)]
59pub struct TrapType(pub u8);
60
61impl TrapType {
62 pub const COLD_START: TrapType = TrapType(0);
63 pub const WARM_START: TrapType = TrapType(1);
64 pub const LINK_DOWN: TrapType = TrapType(2);
65 pub const LINK_UP: TrapType = TrapType(3);
66 pub const AUTHENTICATION_FAILURE: TrapType = TrapType(4);
67 pub const EGP_NEIGHBOR_LOSS: TrapType = TrapType(5);
68 pub const ENTERPRISE_SPECIFIC: TrapType = TrapType(6);
69}
70
71impl fmt::Debug for TrapType {
72 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73 match self.0 {
74 0 => f.write_str("coldStart"),
75 1 => f.write_str("warmStart"),
76 2 => f.write_str("linkDown"),
77 3 => f.write_str("linkUp"),
78 4 => f.write_str("authenticationFailure"),
79 5 => f.write_str("egpNeighborLoss"),
80 6 => f.write_str("enterpriseSpecific"),
81 n => f.debug_tuple("TrapType").field(&n).finish(),
82 }
83 }
84}
85
86#[derive(Copy, Clone, Debug, PartialEq)]
90pub enum NetworkAddress {
91 IPv4(Ipv4Addr),
92}
93
94pub type Counter = u32;
100
101pub type Gauge = u32;
106
107pub type TimeTicks = u32;
112
113#[derive(Clone, Copy, Eq, PartialEq)]
114pub struct ErrorStatus(pub u32);
115
116#[allow(non_upper_case_globals)]
117impl ErrorStatus {
118 pub const NoError: ErrorStatus = ErrorStatus(0);
119 pub const TooBig: ErrorStatus = ErrorStatus(1);
120 pub const NoSuchName: ErrorStatus = ErrorStatus(2);
121 pub const BadValue: ErrorStatus = ErrorStatus(3);
122 pub const ReadOnly: ErrorStatus = ErrorStatus(4);
123 pub const GenErr: ErrorStatus = ErrorStatus(5);
124}
125
126impl fmt::Debug for ErrorStatus {
127 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
128 match self.0 {
129 0 => f.write_str("NoError"),
130 1 => f.write_str("TooBig"),
131 2 => f.write_str("NoSuchName"),
132 3 => f.write_str("BadValue"),
133 4 => f.write_str("ReadOnly"),
134 5 => f.write_str("GenErr"),
135 n => f.debug_tuple("ErrorStatus").field(&n).finish(),
136 }
137 }
138}
139
140#[derive(Debug, PartialEq)]
141pub struct SnmpGenericPdu<'a> {
142 pub pdu_type: PduType,
143 pub req_id: u32,
144 pub err: ErrorStatus,
145 pub err_index: u32,
146 pub var: Vec<SnmpVariable<'a>>,
147}
148
149#[derive(Debug, PartialEq)]
150pub struct SnmpBulkPdu<'a> {
151 pub req_id: u32,
152 pub non_repeaters: u32,
153 pub max_repetitions: u32,
154 pub var: Vec<SnmpVariable<'a>>,
155}
156
157#[derive(Debug, PartialEq)]
158pub struct SnmpTrapPdu<'a> {
159 pub enterprise: Oid<'a>,
160 pub agent_addr: NetworkAddress,
161 pub generic_trap: TrapType,
162 pub specific_trap: u32,
163 pub timestamp: TimeTicks,
164 pub var: Vec<SnmpVariable<'a>>,
165}
166
167#[derive(Debug, PartialEq)]
168pub enum SnmpPdu<'a> {
169 Generic(SnmpGenericPdu<'a>),
170 Bulk(SnmpBulkPdu<'a>),
171 TrapV1(SnmpTrapPdu<'a>),
172}
173
174#[derive(Debug, PartialEq)]
201pub struct SnmpMessage<'a> {
202 pub version: u32,
204 pub community: String,
205 pub pdu: SnmpPdu<'a>,
206}
207
208impl<'a> SnmpGenericPdu<'a> {
209 pub fn vars_iter(&'a self) -> Iter<'a, SnmpVariable<'a>> {
210 self.var.iter()
211 }
212}
213
214impl<'a> SnmpTrapPdu<'a> {
215 pub fn vars_iter(&'a self) -> Iter<'a, SnmpVariable<'a>> {
216 self.var.iter()
217 }
218}
219
220impl<'a> SnmpPdu<'a> {
221 pub fn pdu_type(&self) -> PduType {
222 match *self {
223 SnmpPdu::Generic(ref pdu) => pdu.pdu_type,
224 SnmpPdu::Bulk(_) => PduType::GetBulkRequest,
225 SnmpPdu::TrapV1(_) => PduType::TrapV1,
226 }
227 }
228
229 pub fn vars_iter(&'a self) -> Iter<'a, SnmpVariable<'a>> {
230 match *self {
231 SnmpPdu::Generic(ref pdu) => pdu.var.iter(),
232 SnmpPdu::Bulk(ref pdu) => pdu.var.iter(),
233 SnmpPdu::TrapV1(ref pdu) => pdu.var.iter(),
234 }
235 }
236}
237
238impl<'a> SnmpMessage<'a> {
239 pub fn pdu_type(&self) -> PduType {
240 self.pdu.pdu_type()
241 }
242
243 pub fn vars_iter(&'a self) -> Iter<'a, SnmpVariable<'a>> {
244 self.pdu.vars_iter()
245 }
246}
247
248impl<'a> FromBer<'a, SnmpError> for SnmpMessage<'a> {
249 fn from_ber(bytes: &'a [u8]) -> asn1_rs::ParseResult<'a, Self, SnmpError> {
250 Sequence::from_der_and_then(bytes, |i| {
252 let (i, version) = u32::from_ber(i).map_err(Err::convert)?;
253 if version > 1 {
254 return Err(Err::Error(SnmpError::InvalidVersion));
255 }
256 let (i, community) = parse_ber_octetstring_as_str(i).map_err(Err::convert)?;
257 let (i, pdu) = if version == 0 {
258 parse_snmp_v1_pdu(i)?
259 } else {
260 parse_snmp_v2c_pdu(i)?
261 };
262 let msg = SnmpMessage {
263 version,
264 community: community.to_string(),
265 pdu,
266 };
267 Ok((i, msg))
268 })
269 }
271}
272
273#[derive(Debug, PartialEq)]
274pub struct SnmpVariable<'a> {
275 pub oid: Oid<'a>,
276 pub val: VarBindValue<'a>,
277}
278
279#[derive(Debug, PartialEq)]
280pub enum VarBindValue<'a> {
281 Value(ObjectSyntax<'a>),
282 Unspecified,
283 NoSuchObject,
284 NoSuchInstance,
285 EndOfMibView,
286}
287
288impl<'a> TryFrom<Any<'a>> for SnmpVariable<'a> {
304 type Error = Error;
305
306 fn try_from(any: Any<'a>) -> Result<SnmpVariable<'a>, Self::Error> {
307 let (rem, oid) = Oid::from_ber(any.data)?;
308 let (_, choice) = Any::from_ber(rem)?;
309 let val = if choice.header.is_contextspecific() {
310 match choice.tag().0 {
311 0 => VarBindValue::NoSuchObject,
312 1 => VarBindValue::NoSuchInstance,
313 2 => VarBindValue::EndOfMibView,
314 _ => {
315 return Err(Error::invalid_value(
316 choice.tag(),
317 "invalid VarBind tag".to_string(),
318 ))
319 }
320 }
321 } else if choice.tag() == Tag::Null {
322 VarBindValue::Unspecified
323 } else {
324 VarBindValue::Value(ObjectSyntax::try_from(choice)?)
325 };
326 let var_bind = SnmpVariable { oid, val };
327 Ok(var_bind)
328 }
329}
330
331#[derive(Debug, PartialEq)]
332pub enum ObjectSyntax<'a> {
333 Number(i32),
334 String(&'a [u8]),
335 Object(Oid<'a>),
336 BitString(BitString<'a>),
337 Empty,
338 UnknownSimple(Any<'a>),
339 IpAddress(NetworkAddress),
340 Counter32(Counter),
341 Gauge32(Gauge),
342 TimeTicks(TimeTicks),
343 Opaque(&'a [u8]),
344 NsapAddress(&'a [u8]),
345 Counter64(u64),
346 UInteger32(u32),
347 UnknownApplication(Any<'a>),
348}
349
350impl<'a> TryFrom<Any<'a>> for ObjectSyntax<'a> {
369 type Error = Error;
370
371 fn try_from(any: Any<'a>) -> Result<ObjectSyntax<'a>, Self::Error> {
372 if any.header.is_application() {
373 match any.header.tag().0 {
375 0 => {
376 let s = any.data;
380 if s.len() == 4 {
381 let ipv4 = NetworkAddress::IPv4(Ipv4Addr::new(s[0], s[1], s[2], s[3]));
382 Ok(ObjectSyntax::IpAddress(ipv4))
383 } else {
384 Err(Error::InvalidTag)
385 }
386 }
387 tag @ 1..=3 => {
388 let x = Integer::new(any.data).as_u32()?;
409 let obj = match tag {
410 1 => ObjectSyntax::Counter32(x),
411 2 => ObjectSyntax::Gauge32(x),
412 3 => ObjectSyntax::TimeTicks(x),
413 _ => unreachable!(),
414 };
415 Ok(obj)
416 }
417 4 => Ok(ObjectSyntax::Opaque(any.data)),
418 5 => Ok(ObjectSyntax::NsapAddress(any.data)),
419 6 => {
420 let counter = Integer::new(any.data).as_u64()?;
421 Ok(ObjectSyntax::Counter64(counter))
422 }
423 7 => {
424 let number = Integer::new(any.data).as_u32()?;
425 Ok(ObjectSyntax::UInteger32(number))
426 }
427 _ => Ok(ObjectSyntax::UnknownApplication(any)),
428 }
429 } else {
430 if any.data.is_empty() {
435 return Ok(ObjectSyntax::Empty);
436 }
437 let obj = match any.header.tag() {
438 Tag::BitString => ObjectSyntax::BitString(any.bitstring()?),
439 Tag::Integer => {
440 let number = any.integer()?.as_i32()?;
441 ObjectSyntax::Number(number)
442 }
443 Tag::Null => ObjectSyntax::Empty,
444 Tag::Oid => ObjectSyntax::Object(any.oid()?),
445 Tag::OctetString => ObjectSyntax::String(any.data),
446 _ => ObjectSyntax::UnknownSimple(any),
447 };
448 Ok(obj)
449 }
450 }
451}
452
453#[inline]
454pub(crate) fn parse_ber_octetstring_as_str(i: &[u8]) -> IResult<&[u8], &str, Error> {
455 let (rem, b) = <&[u8]>::from_ber(i)?;
456 let s = core::str::from_utf8(b).map_err(|_| Error::StringInvalidCharset)?;
457 Ok((rem, s))
458}
459
460fn parse_varbind_list(i: &[u8]) -> IResult<&[u8], Vec<SnmpVariable>, Error> {
461 <Vec<SnmpVariable>>::from_ber(i)
463}
464
465impl<'a> TryFrom<Any<'a>> for NetworkAddress {
476 type Error = Error;
477
478 fn try_from(any: Any<'a>) -> Result<Self, Self::Error> {
479 any.class().assert_eq(Class::Application)?;
480 let s = any.data;
481 if s.len() == 4 {
482 Ok(NetworkAddress::IPv4(Ipv4Addr::new(s[0], s[1], s[2], s[3])))
483 } else {
484 Err(Error::invalid_value(
485 Tag::OctetString,
486 "NetworkAddress invalid length".to_string(),
487 ))
488 }
489 }
490}
491
492fn parse_timeticks(i: &[u8]) -> IResult<&[u8], TimeTicks, Error> {
498 let (rem, tagged) = Application::<u32, _, Implicit, 3>::from_ber(i)?;
499 Ok((rem, tagged.into_inner()))
500}
501
502fn parse_snmp_v1_generic_pdu(pdu: &[u8], tag: PduType) -> IResult<&[u8], SnmpPdu, SnmpError> {
503 let (i, req_id) = u32::from_ber(pdu).map_err(Err::convert)?;
504 let (i, err) = map(u32::from_ber, ErrorStatus)(i).map_err(Err::convert)?;
505 let (i, err_index) = u32::from_ber(i).map_err(Err::convert)?;
506 let (i, var) = parse_varbind_list(i).map_err(Err::convert)?;
507 let pdu = SnmpPdu::Generic(SnmpGenericPdu {
508 pdu_type: tag,
509 req_id,
510 err,
511 err_index,
512 var,
513 });
514 Ok((i, pdu))
515}
516
517fn parse_snmp_v1_bulk_pdu(i: &[u8]) -> IResult<&[u8], SnmpPdu, SnmpError> {
518 let (i, req_id) = u32::from_ber(i).map_err(Err::convert)?;
519 let (i, non_repeaters) = u32::from_ber(i).map_err(Err::convert)?;
520 let (i, max_repetitions) = u32::from_ber(i).map_err(Err::convert)?;
521 let (i, var) = parse_varbind_list(i).map_err(Err::convert)?;
522 let pdu = SnmpBulkPdu {
523 req_id,
524 non_repeaters,
525 max_repetitions,
526 var,
527 };
528 Ok((i, SnmpPdu::Bulk(pdu)))
529}
530
531fn parse_snmp_v1_trap_pdu(i: &[u8]) -> IResult<&[u8], SnmpPdu, SnmpError> {
532 let (i, enterprise) = Oid::from_ber(i).map_err(Err::convert)?;
533 let (i, agent_addr) = NetworkAddress::from_ber(i).map_err(Err::convert)?;
534 let (i, generic_trap) = u32::from_ber(i).map_err(Err::convert)?;
535 let (i, specific_trap) = u32::from_ber(i).map_err(Err::convert)?;
536 let (i, timestamp) = parse_timeticks(i).map_err(Err::convert)?;
537 let (i, var) = parse_varbind_list(i).map_err(Err::convert)?;
538 let pdu = SnmpTrapPdu {
539 enterprise,
540 agent_addr,
541 generic_trap: TrapType(generic_trap as u8),
542 specific_trap,
543 timestamp,
544 var,
545 };
546 Ok((i, SnmpPdu::TrapV1(pdu)))
547}
548
549pub fn parse_snmp_v1(bytes: &[u8]) -> IResult<&[u8], SnmpMessage, SnmpError> {
588 Sequence::from_der_and_then(bytes, |i| {
589 let (i, version) = u32::from_ber(i).map_err(Err::convert)?;
590 if version != 0 {
591 return Err(Err::Error(SnmpError::InvalidVersion));
592 }
593 let (i, community) = parse_ber_octetstring_as_str(i).map_err(Err::convert)?;
594 let (i, pdu) = parse_snmp_v1_pdu(i)?;
595 let msg = SnmpMessage {
596 version,
597 community: community.to_string(),
598 pdu,
599 };
600 Ok((i, msg))
601 })
602 }
604
605pub(crate) fn parse_snmp_v1_pdu(i: &[u8]) -> IResult<&[u8], SnmpPdu, SnmpError> {
606 match Header::from_ber(i) {
607 Ok((rem, hdr)) => {
608 match PduType(hdr.tag().0) {
609 PduType::GetRequest
610 | PduType::GetNextRequest
611 | PduType::Response
612 | PduType::SetRequest => parse_snmp_v1_generic_pdu(rem, PduType(hdr.tag().0)),
613 PduType::TrapV1 => parse_snmp_v1_trap_pdu(rem),
614 _ => Err(Err::Error(SnmpError::InvalidPduType)),
615 }
617 }
618 Err(e) => Err(Err::convert(e)),
619 }
620}
621
622pub fn parse_snmp_v2c(bytes: &[u8]) -> IResult<&[u8], SnmpMessage, SnmpError> {
642 Sequence::from_der_and_then(bytes, |i| {
643 let (i, version) = u32::from_ber(i).map_err(Err::convert)?;
644 if version != 1 {
645 return Err(Err::Error(SnmpError::InvalidVersion));
646 }
647 let (i, community) = parse_ber_octetstring_as_str(i).map_err(Err::convert)?;
648 let (i, pdu) = parse_snmp_v2c_pdu(i)?;
649 let msg = SnmpMessage {
650 version,
651 community: community.to_string(),
652 pdu,
653 };
654 Ok((i, msg))
655 })
656}
657
658pub(crate) fn parse_snmp_v2c_pdu(i: &[u8]) -> IResult<&[u8], SnmpPdu, SnmpError> {
659 match Header::from_ber(i) {
660 Ok((rem, hdr)) => {
661 match PduType(hdr.tag().0) {
662 PduType::GetRequest
663 | PduType::GetNextRequest
664 | PduType::Response
665 | PduType::SetRequest
666 | PduType::InformRequest
667 | PduType::TrapV2
668 | PduType::Report => parse_snmp_v1_generic_pdu(rem, PduType(hdr.tag().0)),
669 PduType::GetBulkRequest => parse_snmp_v1_bulk_pdu(rem),
670 PduType::TrapV1 => parse_snmp_v1_trap_pdu(rem),
671 _ => Err(Err::Error(SnmpError::InvalidPduType)),
672 }
674 }
675 Err(e) => Err(Err::convert(e)),
676 }
677}