snmp_parser/
snmp.rs

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
11use 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
22// This will be merged in next release of asn1-rs
23type 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); // Obsolete, was the old Trap-PDU in SNMPv1
35    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/// This CHOICE represents an address from one of possibly several
87/// protocol families.  Currently, only one protocol family, the Internet
88/// family, is present in this CHOICE.
89#[derive(Copy, Clone, Debug, PartialEq)]
90pub enum NetworkAddress {
91    IPv4(Ipv4Addr),
92}
93
94/// This application-wide type represents a non-negative integer which
95/// monotonically increases until it reaches a maximum value, when it
96/// wraps around and starts increasing again from zero.  This memo
97/// specifies a maximum value of 2^32-1 (4294967295 decimal) for
98/// counters.
99pub type Counter = u32;
100
101/// This application-wide type represents a non-negative integer, which
102/// may increase or decrease, but which latches at a maximum value.  This
103/// memo specifies a maximum value of 2^32-1 (4294967295 decimal) for
104/// gauges.
105pub type Gauge = u32;
106
107/// This application-wide type represents a non-negative integer which
108/// counts the time in hundredths of a second since some epoch.  When
109/// object types are defined in the MIB which use this ASN.1 type, the
110/// description of the object type identifies the reference epoch.
111pub 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/// An SNMPv1 or SNMPv2c message
175///
176/// Use the [`FromBer`] trait to parse messages. The method returns the
177/// remaining (unparsed) bytes and the object, or an error.
178///
179/// Function [`parse_snmp_v1`] and [`parse_snmp_v2c`] are also provided, for convenience.
180///
181/// # Examples
182///
183/// ```rust
184/// use snmp_parser::SnmpMessage;
185/// use snmp_parser::asn1_rs::FromBer;
186///
187/// static SNMPV1_REQ: &[u8] = include_bytes!("../assets/snmpv1_req.bin");
188///
189/// # fn main() {
190/// match SnmpMessage::from_ber(&SNMPV1_REQ) {
191///   Ok((_, ref r)) => {
192///     assert!(r.version == 0);
193///     assert!(r.community == String::from("public"));
194///     assert!(r.vars_iter().count() == 1);
195///   },
196///   Err(e) => panic!("{}", e),
197/// }
198/// # }
199/// ```
200#[derive(Debug, PartialEq)]
201pub struct SnmpMessage<'a> {
202    /// Version, as raw-encoded: 0 for SNMPv1, 1 for SNMPv2c
203    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        // parse a SNMP v1 or V2 message
251        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        //.map_err(Err::convert)
270    }
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
288/// <pre>
289/// VarBind ::= SEQUENCE {
290///     name ObjectName,
291///
292///     CHOICE {
293///         value          ObjectSyntax,
294///         unSpecified    NULL,    -- in retrieval requests
295///
296///                                 -- exceptions in responses
297///         noSuchObject   [0] IMPLICIT NULL,
298///         noSuchInstance [1] IMPLICIT NULL,
299///         endOfMibView   [2] IMPLICIT NULL
300///     }
301/// }
302/// </pre>
303impl<'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
350/// <pre>
351/// ObjectSyntax ::= CHOICE {
352///     simple           SimpleSyntax,
353///     application-wide ApplicationSyntax }
354///
355/// SimpleSyntax ::= CHOICE {
356///     integer-value   INTEGER (-2147483648..2147483647),
357///     string-value    OCTET STRING (SIZE (0..65535)),
358///     objectID-value  OBJECT IDENTIFIER }
359///
360/// ApplicationSyntax ::= CHOICE {
361///     ipAddress-value        IpAddress,
362///     counter-value          Counter32,
363///     timeticks-value        TimeTicks,
364///     arbitrary-value        Opaque,
365///     big-counter-value      Counter64,
366///     unsigned-integer-value Unsigned32 }
367/// </pre>
368impl<'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            // ApplicationSyntax
374            match any.header.tag().0 {
375                0 => {
376                    // IpAddress ::=
377                    //     [APPLICATION 0]
378                    //         IMPLICIT OCTET STRING (SIZE (4))
379                    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                    // -- this wraps
389                    // Counter32 ::=
390                    //     [APPLICATION 1]
391                    //         IMPLICIT INTEGER (0..4294967295)
392                    //
393                    // -- this doesn't wrap
394                    // Gauge32 ::=
395                    //     [APPLICATION 2]
396                    //         IMPLICIT INTEGER (0..4294967295)
397                    //
398                    // -- an unsigned 32-bit quantity
399                    // -- indistinguishable from Gauge32
400                    // Unsigned32 ::=
401                    //     [APPLICATION 2]
402                    //         IMPLICIT INTEGER (0..4294967295)
403                    //
404                    // -- hundredths of seconds since an epoch
405                    // TimeTicks ::=
406                    //     [APPLICATION 3]
407                    //         IMPLICIT INTEGER (0..4294967295)
408                    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            // SimpleSyntax
431
432            // Some implementations do not send NULL, but empty objects
433            // Treat 0-length objects as ObjectSyntax::Empty
434            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    // parse_ber_sequence_of_v(parse_varbind)(i)
462    <Vec<SnmpVariable>>::from_ber(i)
463}
464
465/// <pre>
466///  NetworkAddress ::=
467///      CHOICE {
468///          internet
469///              IpAddress
470///      }
471/// IpAddress ::=
472///     [APPLICATION 0]          -- in network-byte order
473///         IMPLICIT OCTET STRING (SIZE (4))
474/// </pre>
475impl<'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
492/// <pre>
493/// TimeTicks ::=
494///     [APPLICATION 3]
495///         IMPLICIT INTEGER (0..4294967295)
496/// </pre>
497fn 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
549/// Parse a SNMP v1 message.
550///
551/// Top-level message
552///
553/// <pre>
554/// Message ::=
555///         SEQUENCE {
556///             version          -- version-1 for this RFC
557///                 INTEGER {
558///                     version-1(0)
559///                 },
560///
561///             community        -- community name
562///                 OCTET STRING,
563///
564///             data             -- e.g., PDUs if trivial
565///                 ANY          -- authentication is being used
566///         }
567/// </pre>
568///
569/// Example:
570///
571/// ```rust
572/// use snmp_parser::parse_snmp_v1;
573///
574/// static SNMPV1_REQ: &[u8] = include_bytes!("../assets/snmpv1_req.bin");
575///
576/// # fn main() {
577/// match parse_snmp_v1(&SNMPV1_REQ) {
578///   Ok((_, ref r)) => {
579///     assert!(r.version == 0);
580///     assert!(r.community == String::from("public"));
581///     assert!(r.vars_iter().count() == 1);
582///   },
583///   Err(e) => panic!("{}", e),
584/// }
585/// # }
586/// ```
587pub 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    //.map_err(Err::convert)
603}
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                // _                       => { return IResult::Error(error_code!(ErrorKind::Custom(SnmpError::InvalidPdu))); },
616            }
617        }
618        Err(e) => Err(Err::convert(e)),
619    }
620}
621
622/// Parse a SNMP v2c message.
623///
624/// Top-level message
625///
626/// <pre>
627/// Message ::=
628///         SEQUENCE {
629///             version
630///                 INTEGER {
631///                     version(1)  -- modified from RFC 1157
632///                 },
633///
634///             community           -- community name
635///                 OCTET STRING,
636///
637///             data                -- PDUs as defined in [4]
638///                 ANY
639///         }
640/// </pre>
641pub 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                // _                       => { return IResult::Error(error_code!(ErrorKind::Custom(SnmpError::InvalidPdu))); },
673            }
674        }
675        Err(e) => Err(Err::convert(e)),
676    }
677}