1 //! SNMP Parser (v1 and v2c)
2 //!
3 //! SNMP is defined in the following RFCs:
4 //!   - [RFC1157](https://tools.ietf.org/html/rfc1157): SNMP v1
5 //!   - [RFC1442](https://tools.ietf.org/html/rfc1442): Structure of Management Information for version 2 of the Simple Network Management Protocol (SNMPv2)
6 //!   - [RFC1902](https://tools.ietf.org/html/rfc1902): SNMP v2 SMI
7 //!   - [RFC2578](https://tools.ietf.org/html/rfc2578): Structure of Management Information Version 2 (SMIv2)
8 //!   - [RFC3416](https://tools.ietf.org/html/rfc3416): SNMP v2
9 //!   - [RFC2570](https://tools.ietf.org/html/rfc2570): Introduction to SNMP v3
10 
11 use crate::error::SnmpError;
12 use std::{fmt,str};
13 use std::net::Ipv4Addr;
14 use std::slice::Iter;
15 use nom::{Err, IResult};
16 use der_parser::ber::*;
17 use der_parser::error::*;
18 use der_parser::oid::Oid;
19 
20 #[derive(Clone, Copy, Eq, PartialEq)]
21 pub struct PduType(pub u32);
22 
23 #[allow(non_upper_case_globals)]
24 impl PduType {
25     pub const GetRequest     : PduType = PduType(0);
26     pub const GetNextRequest : PduType = PduType(1);
27     pub const Response       : PduType = PduType(2);
28     pub const SetRequest     : PduType = PduType(3);
29     pub const TrapV1         : PduType = PduType(4); // Obsolete, was the old Trap-PDU in SNMPv1
30     pub const GetBulkRequest : PduType = PduType(5);
31     pub const InformRequest  : PduType = PduType(6);
32     pub const TrapV2         : PduType = PduType(7);
33     pub const Report         : PduType = PduType(8);
34 }
35 
36 impl fmt::Debug for PduType {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result37     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38         match self.0 {
39            0 => f.write_str("GetRequest"),
40            1 => f.write_str("GetNextRequest"),
41            2 => f.write_str("Response"),
42            3 => f.write_str("SetRequest"),
43            4 => f.write_str("TrapV1"),
44            5 => f.write_str("GetBulkRequest"),
45            6 => f.write_str("InformRequest"),
46            7 => f.write_str("TrapV2"),
47            8 => f.write_str("Report"),
48            n => f.debug_tuple("PduType").field(&n).finish(),
49         }
50     }
51 }
52 
53 #[derive(Clone, Copy, Eq, PartialEq)]
54 pub struct TrapType(pub u8);
55 
56 impl TrapType {
57     pub const COLD_START             : TrapType = TrapType(0);
58     pub const WARM_START             : TrapType = TrapType(1);
59     pub const LINK_DOWN              : TrapType = TrapType(2);
60     pub const LINK_UP                : TrapType = TrapType(3);
61     pub const AUTHENTICATION_FAILURE : TrapType = TrapType(4);
62     pub const EGP_NEIGHBOR_LOSS      : TrapType = TrapType(5);
63     pub const ENTERPRISE_SPECIFIC    : TrapType = TrapType(6);
64 }
65 
66 impl fmt::Debug for TrapType {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result67     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68         match self.0 {
69            0 => f.write_str("coldStart"),
70            1 => f.write_str("warmStart"),
71            2 => f.write_str("linkDown"),
72            3 => f.write_str("linkUp"),
73            4 => f.write_str("authenticationFailure"),
74            5 => f.write_str("egpNeighborLoss"),
75            6 => f.write_str("enterpriseSpecific"),
76            n => f.debug_tuple("TrapType").field(&n).finish(),
77         }
78     }
79 }
80 
81 /// This CHOICE represents an address from one of possibly several
82 /// protocol families.  Currently, only one protocol family, the Internet
83 /// family, is present in this CHOICE.
84 #[derive(Copy, Clone, Debug, PartialEq)]
85 pub enum NetworkAddress {
86     IPv4(Ipv4Addr),
87 }
88 
89 /// This application-wide type represents a non-negative integer which
90 /// monotonically increases until it reaches a maximum value, when it
91 /// wraps around and starts increasing again from zero.  This memo
92 /// specifies a maximum value of 2^32-1 (4294967295 decimal) for
93 /// counters.
94 pub type Counter = u32;
95 
96 /// This application-wide type represents a non-negative integer, which
97 /// may increase or decrease, but which latches at a maximum value.  This
98 /// memo specifies a maximum value of 2^32-1 (4294967295 decimal) for
99 /// gauges.
100 pub type Gauge = u32;
101 
102 /// This application-wide type represents a non-negative integer which
103 /// counts the time in hundredths of a second since some epoch.  When
104 /// object types are defined in the MIB which use this ASN.1 type, the
105 /// description of the object type identifies the reference epoch.
106 pub type TimeTicks = u32;
107 
108 #[derive(Clone, Copy, Eq, PartialEq)]
109 pub struct ErrorStatus(pub u32);
110 
111 #[allow(non_upper_case_globals)]
112 impl ErrorStatus {
113     pub const NoError    : ErrorStatus = ErrorStatus(0);
114     pub const TooBig     : ErrorStatus = ErrorStatus(1);
115     pub const NoSuchName : ErrorStatus = ErrorStatus(2);
116     pub const BadValue   : ErrorStatus = ErrorStatus(3);
117     pub const ReadOnly   : ErrorStatus = ErrorStatus(4);
118     pub const GenErr     : ErrorStatus = ErrorStatus(5);
119 }
120 
121 impl fmt::Debug for ErrorStatus {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result122     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
123         match self.0 {
124            0 => f.write_str("NoError"),
125            1 => f.write_str("TooBig"),
126            2 => f.write_str("NoSuchName"),
127            3 => f.write_str("BadValue"),
128            4 => f.write_str("ReadOnly"),
129            5 => f.write_str("GenErr"),
130            n => f.debug_tuple("ErrorStatus").field(&n).finish(),
131         }
132     }
133 }
134 
135 #[derive(Debug,PartialEq)]
136 pub struct SnmpGenericPdu<'a> {
137     pub pdu_type: PduType,
138     pub req_id: u32,
139     pub err: ErrorStatus,
140     pub err_index: u32,
141     pub var: Vec<SnmpVariable<'a>>,
142 }
143 
144 #[derive(Debug,PartialEq)]
145 pub struct SnmpBulkPdu<'a> {
146     pub req_id: u32,
147     pub non_repeaters: u32,
148     pub max_repetitions: u32,
149     pub var: Vec<SnmpVariable<'a>>,
150 }
151 
152 #[derive(Debug,PartialEq)]
153 pub struct SnmpTrapPdu<'a> {
154     pub enterprise: Oid<'a>,
155     pub agent_addr: NetworkAddress,
156     pub generic_trap: TrapType,
157     pub specific_trap: u32,
158     pub timestamp: TimeTicks,
159     pub var: Vec<SnmpVariable<'a>>,
160 }
161 
162 #[derive(Debug,PartialEq)]
163 pub enum SnmpPdu<'a> {
164     Generic(SnmpGenericPdu<'a>),
165     Bulk(SnmpBulkPdu<'a>),
166     TrapV1(SnmpTrapPdu<'a>),
167 }
168 
169 /// An SNMPv1 or SNMPv2c message
170 #[derive(Debug,PartialEq)]
171 pub struct SnmpMessage<'a> {
172     /// Version, as raw-encoded: 0 for SNMPv1, 1 for SNMPv2c
173     pub version: u32,
174     pub community: String,
175     pub pdu: SnmpPdu<'a>,
176 }
177 
178 impl<'a> SnmpGenericPdu<'a> {
vars_iter(&'a self) -> Iter<SnmpVariable>179     pub fn vars_iter(&'a self) -> Iter<SnmpVariable> {
180         self.var.iter()
181     }
182 }
183 
184 impl<'a> SnmpTrapPdu<'a> {
vars_iter(&'a self) -> Iter<SnmpVariable>185     pub fn vars_iter(&'a self) -> Iter<SnmpVariable> {
186         self.var.iter()
187     }
188 }
189 
190 impl<'a> SnmpPdu<'a> {
pdu_type(&self) -> PduType191     pub fn pdu_type(&self) -> PduType {
192         match *self {
193             SnmpPdu::Generic(ref pdu) => pdu.pdu_type,
194             SnmpPdu::Bulk(_)          => PduType::GetBulkRequest,
195             SnmpPdu::TrapV1(_)        => PduType::TrapV1,
196         }
197     }
198 
vars_iter(&'a self) -> Iter<SnmpVariable>199     pub fn vars_iter(&'a self) -> Iter<SnmpVariable> {
200         match *self {
201             SnmpPdu::Generic(ref pdu) => pdu.var.iter(),
202             SnmpPdu::Bulk(ref pdu)    => pdu.var.iter(),
203             SnmpPdu::TrapV1(ref pdu)  => pdu.var.iter(),
204         }
205     }
206 }
207 
208 impl<'a> SnmpMessage<'a> {
pdu_type(&self) -> PduType209     pub fn pdu_type(&self) -> PduType {
210         self.pdu.pdu_type()
211     }
212 
vars_iter(&'a self) -> Iter<SnmpVariable>213     pub fn vars_iter(&'a self) -> Iter<SnmpVariable> {
214         self.pdu.vars_iter()
215     }
216 }
217 
218 #[derive(Debug,PartialEq)]
219 pub struct SnmpVariable<'a> {
220     pub oid: Oid<'a>,
221     pub val: ObjectSyntax<'a>
222 }
223 
224 #[derive(Debug,PartialEq)]
225 pub enum ObjectSyntax<'a> {
226     Number(BerObject<'a>),
227     String(&'a[u8]),
228     Object(Oid<'a>),
229     BitString(u8, BitStringObject<'a>),
230     Empty,
231     UnknownSimple(BerObject<'a>),
232     IpAddress(NetworkAddress),
233     Counter32(Counter),
234     Gauge32(Gauge),
235     TimeTicks(TimeTicks),
236     Opaque(&'a[u8]),
237     NsapAddress(&'a[u8]),
238     Counter64(u64),
239     UInteger32(u32),
240     UnknownApplication(u32, &'a[u8]),
241 }
242 
parse_ber_octetstring_as_slice(i:&[u8]) -> IResult<&[u8], &[u8], BerError>243 pub(crate) fn parse_ber_octetstring_as_slice(i:&[u8]) -> IResult<&[u8], &[u8], BerError> {
244     match parse_ber_octetstring(i) {
245         Ok((rem,ref obj)) => {
246             match obj.content {
247                 BerObjectContent::OctetString(s) => {
248                     Ok((rem, s))
249                 }
250                 _ => Err(Err::Error(BerError::InvalidTag)),
251             }
252         }
253         Err(e) => Err(e)
254     }
255 }
256 
257 // Defined in RFC1442 and RFC2578
parse_objectsyntax<'a>(i:&'a[u8]) -> IResult<&'a[u8], ObjectSyntax, BerError>258 fn parse_objectsyntax<'a>(i:&'a[u8]) -> IResult<&'a[u8], ObjectSyntax, BerError> {
259     match ber_read_element_header(i) {
260         Ok((rem,hdr)) => {
261             if hdr.is_application() {
262                 match hdr.tag.0 {
263                     0 => {
264                         map_res!(
265                             rem,
266                             call!(ber_read_element_content_as, BerTag::OctetString, hdr.len as usize, hdr.is_constructed(), 0),
267                             |x:BerObjectContent| {
268                                 match x {
269                                     BerObjectContent::OctetString(s) if s.len() == 4 => {
270                                         Ok(ObjectSyntax::IpAddress(NetworkAddress::IPv4(Ipv4Addr::new(s[0],s[1],s[2],s[3]))))
271                                     },
272                                     _ => Err(BerError::InvalidTag),
273                                 }
274                             }
275                         )
276                     },
277                     1 ..= 3 => {
278                         map_res!(
279                             rem,
280                             call!(ber_read_element_content_as, BerTag::Integer, hdr.len as usize, hdr.is_constructed(), 0),
281                             |x:BerObjectContent| {
282                                 x.as_u32().map(|x| {
283                                     match hdr.tag.0 {
284                                         1 => ObjectSyntax::Counter32(x),
285                                         2 => ObjectSyntax::Gauge32(x),
286                                         3 => ObjectSyntax::TimeTicks(x),
287                                         _ => unreachable!(),
288                                     }
289                                 })
290                             }
291                         )
292                     },
293                     4 => {
294                         map!(rem, take!(hdr.len as usize), ObjectSyntax::Opaque)
295                     },
296                     5 => {
297                         map!(rem, take!(hdr.len as usize), ObjectSyntax::NsapAddress)
298                     },
299                     6 => {
300                         map_res!(
301                             rem,
302                             call!(ber_read_element_content_as, BerTag::Integer, hdr.len as usize, hdr.is_constructed(), 0),
303                             |x:BerObjectContent| {
304                                 x.as_u64().map(ObjectSyntax::Counter64)
305                             }
306                         )
307                     },
308                     7 => {
309                         map_res!(
310                             rem,
311                             call!(ber_read_element_content_as, BerTag::Integer, hdr.len as usize, hdr.is_constructed(), 0),
312                             |x:BerObjectContent| {
313                                 x.as_u32().map(ObjectSyntax::UInteger32)
314                             }
315                         )
316                     },
317                     _ => {
318                         map!(rem, take!(hdr.len as usize), |x| ObjectSyntax::UnknownApplication(hdr.tag.0,x))
319                     },
320                 }
321             } else {
322                         if hdr.len == 0 { return Ok((rem, ObjectSyntax::Empty)); }
323                         map_res!(
324                             rem,
325                             call!(ber_read_element_content_as, hdr.tag, hdr.len as usize, hdr.is_constructed(), 0),
326                             |x:BerObjectContent<'a>| {
327                                 match x {
328                                     BerObjectContent::Integer(_)     => Ok(ObjectSyntax::Number(BerObject::from_obj(x))),
329                                     BerObjectContent::OctetString(s) => Ok(ObjectSyntax::String(s)),
330                                     BerObjectContent::OID(o)         => Ok(ObjectSyntax::Object(o)),
331                                     BerObjectContent::BitString(a,s) => Ok(ObjectSyntax::BitString(a,s)),
332                                     BerObjectContent::Null           => Ok(ObjectSyntax::Empty),
333                                     _                                => Ok(ObjectSyntax::UnknownSimple(BerObject::from_obj(x))) as Result<_,u32>,
334                                 }
335                             }
336                         )
337             }
338         },
339         Err(e)        => Err(e)
340     }
341 }
342 
343 #[inline]
parse_varbind<'a>(i:&'a [u8]) -> IResult<&'a [u8], SnmpVariable, BerError>344 fn parse_varbind<'a>(i:&'a [u8]) -> IResult<&'a [u8], SnmpVariable, BerError> {
345     parse_der_struct!(
346         i,
347         TAG BerTag::Sequence,
348         oid: map_res!(parse_ber_oid, |x:BerObject<'a>| x.as_oid_val()) >>
349         val: parse_objectsyntax >>
350              // eof!() >>
351         (
352             SnmpVariable{ oid, val }
353         )
354     ).map(|(rem,x)| (rem,x.1))
355 }
356 
357 #[inline]
parse_varbind_list(i:&[u8]) -> IResult<&[u8], Vec<SnmpVariable>, BerError>358 fn parse_varbind_list(i:&[u8]) -> IResult<&[u8], Vec<SnmpVariable>, BerError> {
359     parse_der_struct!(
360         i,
361         TAG BerTag::Sequence,
362         l: many0!(complete!(parse_varbind)) >>
363            // eof!() >>
364         ( l )
365     ).map(|(rem,x)| (rem,x.1))
366 }
367 
368 /// <pre>
369 ///  NetworkAddress ::=
370 ///      CHOICE {
371 ///          internet
372 ///              IpAddress
373 ///      }
374 /// IpAddress ::=
375 ///     [APPLICATION 0]          -- in network-byte order
376 ///         IMPLICIT OCTET STRING (SIZE (4))
377 /// </pre>
parse_networkaddress(i:&[u8]) -> IResult<&[u8], NetworkAddress, BerError>378 fn parse_networkaddress(i:&[u8]) -> IResult<&[u8], NetworkAddress, BerError> {
379     match parse_ber(i) {
380         Ok((rem,obj)) => {
381             if obj.header.tag != BerTag::EndOfContent || obj.header.class != BerClass::Application {
382                 return Err(Err::Error(BerError::InvalidTag));
383             }
384             match obj.content {
385                 BerObjectContent::Unknown(_,s) if s.len() == 4 => {
386                     Ok((rem, NetworkAddress::IPv4(Ipv4Addr::new(s[0],s[1],s[2],s[3]))))
387                 },
388                 _ => Err(Err::Error(BerError::InvalidTag)),
389             }
390         },
391         Err(e)        => Err(e)
392     }
393 }
394 
395 /// <pre>
396 /// TimeTicks ::=
397 ///     [APPLICATION 3]
398 ///         IMPLICIT INTEGER (0..4294967295)
399 /// </pre>
parse_timeticks(i:&[u8]) -> IResult<&[u8], TimeTicks, BerError>400 fn parse_timeticks(i:&[u8]) -> IResult<&[u8], TimeTicks, BerError> {
401     fn ber_read_integer_content(i:&[u8], _tag:BerTag, len: usize) -> IResult<&[u8], BerObjectContent, BerError> {
402         ber_read_element_content_as(i, BerTag::Integer, len, false, 0)
403     }
404     map_res!(i, call!(parse_ber_implicit, BerTag(3), ber_read_integer_content), |x: BerObject| {
405         match x.as_context_specific() {
406             Ok((_,Some(x))) => x.as_u32(),
407             _               => Err(BerError::BerTypeError),
408         }
409     })
410 }
411 
412 
413 
414 
parse_snmp_v1_generic_pdu(pdu: &[u8], tag:PduType) -> IResult<&[u8], SnmpPdu, BerError>415 fn parse_snmp_v1_generic_pdu(pdu: &[u8], tag:PduType) -> IResult<&[u8], SnmpPdu, BerError> {
416     do_parse! {
417         pdu,
418         req_id:       parse_ber_u32 >>
419         err:          parse_ber_u32 >>
420         err_index:    parse_ber_u32 >>
421         var_bindings: parse_varbind_list >>
422         (
423             SnmpPdu::Generic(
424                 SnmpGenericPdu {
425                     pdu_type:  tag,
426                     req_id,
427                     err:       ErrorStatus(err),
428                     err_index,
429                     var:       var_bindings
430                 }
431             )
432         )
433     }
434 }
435 
parse_snmp_v1_bulk_pdu(pdu: &[u8]) -> IResult<&[u8], SnmpPdu, BerError>436 fn parse_snmp_v1_bulk_pdu(pdu: &[u8]) -> IResult<&[u8], SnmpPdu, BerError> {
437     do_parse! {
438         pdu,
439         req_id:          parse_ber_u32 >>
440         non_repeaters:   parse_ber_u32 >>
441         max_repetitions: parse_ber_u32 >>
442         var_bindings:    parse_varbind_list >>
443         (
444             SnmpPdu::Bulk(
445                 SnmpBulkPdu {
446                     req_id,
447                     non_repeaters,
448                     max_repetitions,
449                     var:       var_bindings
450                 }
451             )
452         )
453     }
454 }
455 
parse_snmp_v1_trap_pdu<'a>(pdu: &'a [u8]) -> IResult<&'a [u8], SnmpPdu, BerError>456 fn parse_snmp_v1_trap_pdu<'a>(pdu: &'a [u8]) -> IResult<&'a [u8], SnmpPdu, BerError> {
457     do_parse! {
458         pdu,
459         enterprise:    map_res!(parse_ber_oid, |x: BerObject<'a>| x.as_oid_val()) >>
460         agent_addr:    parse_networkaddress >>
461         generic_trap:  parse_ber_u32 >>
462         specific_trap: parse_ber_u32 >>
463         timestamp:     parse_timeticks >>
464         var:           parse_varbind_list >>
465         (
466             SnmpPdu::TrapV1(
467                 SnmpTrapPdu {
468                     enterprise,
469                     agent_addr,
470                     generic_trap:  TrapType(generic_trap as u8),
471                     specific_trap,
472                     timestamp,
473                     var,
474                 }
475             )
476         )
477     }
478 }
479 
480 /// Parse a SNMP v1 message.
481 ///
482 /// Top-level message
483 ///
484 /// <pre>
485 /// Message ::=
486 ///         SEQUENCE {
487 ///             version          -- version-1 for this RFC
488 ///                 INTEGER {
489 ///                     version-1(0)
490 ///                 },
491 ///
492 ///             community        -- community name
493 ///                 OCTET STRING,
494 ///
495 ///             data             -- e.g., PDUs if trivial
496 ///                 ANY          -- authentication is being used
497 ///         }
498 /// </pre>
499 ///
500 /// Example:
501 ///
502 /// ```rust
503 /// # extern crate nom;
504 /// # #[macro_use] extern crate snmp_parser;
505 /// use snmp_parser::parse_snmp_v1;
506 ///
507 /// static SNMPV1_REQ: &'static [u8] = include_bytes!("../assets/snmpv1_req.bin");
508 ///
509 /// # fn main() {
510 /// match parse_snmp_v1(&SNMPV1_REQ) {
511 ///   Ok((_, ref r)) => {
512 ///     assert!(r.version == 0);
513 ///     assert!(r.community == String::from("public"));
514 ///     assert!(r.vars_iter().count() == 1);
515 ///   },
516 ///   Err(e) => panic!(e),
517 /// }
518 /// # }
519 /// ```
parse_snmp_v1(i:&[u8]) -> IResult<&[u8], SnmpMessage, SnmpError>520 pub fn parse_snmp_v1(i:&[u8]) -> IResult<&[u8], SnmpMessage, SnmpError> {
521     upgrade_error! {
522         parse_der_struct!(
523             i,
524             TAG BerTag::Sequence,
525             version:   map_res!(parse_ber_integer, |o:BerObject| o.as_u32()) >>
526                        custom_check!(version != 0, BerError::BerValueError) >>
527             community: map_res!(
528                 parse_ber_octetstring_as_slice,
529                 |s| str::from_utf8(s)
530             ) >>
531             pdu:       parse_snmp_v1_pdu >>
532             (
533                 SnmpMessage{
534                     version,
535                     community: community.to_string(),
536                     pdu
537                 }
538             )
539         ).map(|(rem,x)| (rem,x.1))
540     }
541 }
542 
parse_snmp_v1_pdu(i:&[u8]) -> IResult<&[u8], SnmpPdu, BerError>543 pub(crate) fn parse_snmp_v1_pdu(i:&[u8]) -> IResult<&[u8], SnmpPdu, BerError> {
544     match ber_read_element_header(i) {
545         Ok((rem,hdr)) => {
546             match PduType(hdr.tag.0) {
547                 PduType::GetRequest |
548                 PduType::GetNextRequest |
549                 PduType::Response |
550                 PduType::SetRequest     => parse_snmp_v1_generic_pdu(rem, PduType(hdr.tag.0)),
551                 PduType::TrapV1         => parse_snmp_v1_trap_pdu(rem),
552                 _                       => Err(Err::Error(BerError::BerValueError)),
553                 // _                       => { return IResult::Error(error_code!(ErrorKind::Custom(SnmpError::InvalidPdu))); },
554             }
555         },
556         Err(e)        => Err(e)
557         // IResult::Incomplete(i) => IResult::Incomplete(i),
558         // IResult::Error(_)      => IResult::Error(error_code!(ErrorKind::Custom(129))),
559         // // IResult::Error(_)      => IResult::Error(error_code!(ErrorKind::Custom(SnmpError::InvalidScopedPduData))),
560     }
561 }
562 
563 /// Parse a SNMP v2c message.
564 ///
565 /// Top-level message
566 ///
567 /// <pre>
568 /// Message ::=
569 ///         SEQUENCE {
570 ///             version
571 ///                 INTEGER {
572 ///                     version(1)  -- modified from RFC 1157
573 ///                 },
574 ///
575 ///             community           -- community name
576 ///                 OCTET STRING,
577 ///
578 ///             data                -- PDUs as defined in [4]
579 ///                 ANY
580 ///         }
581 /// </pre>
parse_snmp_v2c(i:&[u8]) -> IResult<&[u8], SnmpMessage, SnmpError>582 pub fn parse_snmp_v2c(i:&[u8]) -> IResult<&[u8], SnmpMessage, SnmpError> {
583     upgrade_error! {
584         parse_der_struct!(
585             i,
586             TAG BerTag::Sequence,
587             version:   parse_ber_u32 >>
588                        custom_check!(version != 1, BerError::BerValueError) >>
589             community: map_res!(
590                 parse_ber_octetstring_as_slice,
591                 |s| str::from_utf8(s)
592             ) >>
593             pdu:       parse_snmp_v2c_pdu >>
594             (
595                 SnmpMessage{
596                     version,
597                     community: community.to_string(),
598                     pdu
599                 }
600             )
601         ).map(|(rem,x)| (rem,x.1))
602     }
603 }
604 
parse_snmp_v2c_pdu(i:&[u8]) -> IResult<&[u8], SnmpPdu, BerError>605 pub(crate) fn parse_snmp_v2c_pdu(i:&[u8]) -> IResult<&[u8], SnmpPdu, BerError> {
606     match ber_read_element_header(i) {
607         Ok((rem,hdr)) => {
608             match PduType(hdr.tag.0) {
609                 PduType::GetRequest |
610                 PduType::GetNextRequest |
611                 PduType::Response |
612                 PduType::SetRequest |
613                 PduType::InformRequest |
614                 PduType::TrapV2 |
615                 PduType::Report         => parse_snmp_v1_generic_pdu(rem, PduType(hdr.tag.0)),
616                 PduType::GetBulkRequest => parse_snmp_v1_bulk_pdu(rem),
617                 PduType::TrapV1         => parse_snmp_v1_trap_pdu(rem),
618                 _                       => Err(Err::Error(BerError::BerValueError)),
619                 // _                       => { return IResult::Error(error_code!(ErrorKind::Custom(SnmpError::InvalidPdu))); },
620             }
621         },
622         Err(e)        => Err(e)
623         // IResult::Incomplete(i) => IResult::Incomplete(i),
624         // IResult::Error(_)      => IResult::Error(error_code!(ErrorKind::Custom(129))),
625         // // IResult::Error(_)      => IResult::Error(error_code!(ErrorKind::Custom(SnmpError::InvalidScopedPduData))),
626     }
627 }
628