Coverage Report

Created: 2025-12-31 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/snmp-parser-0.9.0/src/snmp.rs
Line
Count
Source
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 asn1_rs::{
13
    Any, BitString, Class, Error, FromBer, Header, Implicit, Integer, Oid, Sequence, Tag,
14
    TaggedValue,
15
};
16
use nom::combinator::map;
17
use nom::{Err, IResult};
18
use std::convert::TryFrom;
19
use std::net::Ipv4Addr;
20
use std::slice::Iter;
21
use std::{fmt, str};
22
23
// This will be merged in next release of asn1-rs
24
type Application<T, E, TagKind, const TAG: u32> = TaggedValue<T, E, TagKind, 0b01, TAG>;
25
26
#[derive(Clone, Copy, Eq, PartialEq)]
27
pub struct PduType(pub u32);
28
29
#[allow(non_upper_case_globals)]
30
impl PduType {
31
    pub const GetRequest: PduType = PduType(0);
32
    pub const GetNextRequest: PduType = PduType(1);
33
    pub const Response: PduType = PduType(2);
34
    pub const SetRequest: PduType = PduType(3);
35
    pub const TrapV1: PduType = PduType(4); // Obsolete, was the old Trap-PDU in SNMPv1
36
    pub const GetBulkRequest: PduType = PduType(5);
37
    pub const InformRequest: PduType = PduType(6);
38
    pub const TrapV2: PduType = PduType(7);
39
    pub const Report: PduType = PduType(8);
40
}
41
42
impl fmt::Debug for PduType {
43
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44
0
        match self.0 {
45
0
            0 => f.write_str("GetRequest"),
46
0
            1 => f.write_str("GetNextRequest"),
47
0
            2 => f.write_str("Response"),
48
0
            3 => f.write_str("SetRequest"),
49
0
            4 => f.write_str("TrapV1"),
50
0
            5 => f.write_str("GetBulkRequest"),
51
0
            6 => f.write_str("InformRequest"),
52
0
            7 => f.write_str("TrapV2"),
53
0
            8 => f.write_str("Report"),
54
0
            n => f.debug_tuple("PduType").field(&n).finish(),
55
        }
56
0
    }
57
}
58
59
#[derive(Clone, Copy, Eq, PartialEq)]
60
pub struct TrapType(pub u8);
61
62
impl TrapType {
63
    pub const COLD_START: TrapType = TrapType(0);
64
    pub const WARM_START: TrapType = TrapType(1);
65
    pub const LINK_DOWN: TrapType = TrapType(2);
66
    pub const LINK_UP: TrapType = TrapType(3);
67
    pub const AUTHENTICATION_FAILURE: TrapType = TrapType(4);
68
    pub const EGP_NEIGHBOR_LOSS: TrapType = TrapType(5);
69
    pub const ENTERPRISE_SPECIFIC: TrapType = TrapType(6);
70
}
71
72
impl fmt::Debug for TrapType {
73
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
74
0
        match self.0 {
75
0
            0 => f.write_str("coldStart"),
76
0
            1 => f.write_str("warmStart"),
77
0
            2 => f.write_str("linkDown"),
78
0
            3 => f.write_str("linkUp"),
79
0
            4 => f.write_str("authenticationFailure"),
80
0
            5 => f.write_str("egpNeighborLoss"),
81
0
            6 => f.write_str("enterpriseSpecific"),
82
0
            n => f.debug_tuple("TrapType").field(&n).finish(),
83
        }
84
0
    }
85
}
86
87
/// This CHOICE represents an address from one of possibly several
88
/// protocol families.  Currently, only one protocol family, the Internet
89
/// family, is present in this CHOICE.
90
#[derive(Copy, Clone, Debug, PartialEq)]
91
pub enum NetworkAddress {
92
    IPv4(Ipv4Addr),
93
}
94
95
/// This application-wide type represents a non-negative integer which
96
/// monotonically increases until it reaches a maximum value, when it
97
/// wraps around and starts increasing again from zero.  This memo
98
/// specifies a maximum value of 2^32-1 (4294967295 decimal) for
99
/// counters.
100
pub type Counter = u32;
101
102
/// This application-wide type represents a non-negative integer, which
103
/// may increase or decrease, but which latches at a maximum value.  This
104
/// memo specifies a maximum value of 2^32-1 (4294967295 decimal) for
105
/// gauges.
106
pub type Gauge = u32;
107
108
/// This application-wide type represents a non-negative integer which
109
/// counts the time in hundredths of a second since some epoch.  When
110
/// object types are defined in the MIB which use this ASN.1 type, the
111
/// description of the object type identifies the reference epoch.
112
pub type TimeTicks = u32;
113
114
#[derive(Clone, Copy, Eq, PartialEq)]
115
pub struct ErrorStatus(pub u32);
116
117
#[allow(non_upper_case_globals)]
118
impl ErrorStatus {
119
    pub const NoError: ErrorStatus = ErrorStatus(0);
120
    pub const TooBig: ErrorStatus = ErrorStatus(1);
121
    pub const NoSuchName: ErrorStatus = ErrorStatus(2);
122
    pub const BadValue: ErrorStatus = ErrorStatus(3);
123
    pub const ReadOnly: ErrorStatus = ErrorStatus(4);
124
    pub const GenErr: ErrorStatus = ErrorStatus(5);
125
}
126
127
impl fmt::Debug for ErrorStatus {
128
42
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129
42
        match self.0 {
130
0
            0 => f.write_str("NoError"),
131
0
            1 => f.write_str("TooBig"),
132
36
            2 => f.write_str("NoSuchName"),
133
0
            3 => f.write_str("BadValue"),
134
3
            4 => f.write_str("ReadOnly"),
135
0
            5 => f.write_str("GenErr"),
136
3
            n => f.debug_tuple("ErrorStatus").field(&n).finish(),
137
        }
138
42
    }
139
}
140
141
#[derive(Debug, PartialEq)]
142
pub struct SnmpGenericPdu<'a> {
143
    pub pdu_type: PduType,
144
    pub req_id: u32,
145
    pub err: ErrorStatus,
146
    pub err_index: u32,
147
    pub var: Vec<SnmpVariable<'a>>,
148
}
149
150
#[derive(Debug, PartialEq)]
151
pub struct SnmpBulkPdu<'a> {
152
    pub req_id: u32,
153
    pub non_repeaters: u32,
154
    pub max_repetitions: u32,
155
    pub var: Vec<SnmpVariable<'a>>,
156
}
157
158
#[derive(Debug, PartialEq)]
159
pub struct SnmpTrapPdu<'a> {
160
    pub enterprise: Oid<'a>,
161
    pub agent_addr: NetworkAddress,
162
    pub generic_trap: TrapType,
163
    pub specific_trap: u32,
164
    pub timestamp: TimeTicks,
165
    pub var: Vec<SnmpVariable<'a>>,
166
}
167
168
#[derive(Debug, PartialEq)]
169
pub enum SnmpPdu<'a> {
170
    Generic(SnmpGenericPdu<'a>),
171
    Bulk(SnmpBulkPdu<'a>),
172
    TrapV1(SnmpTrapPdu<'a>),
173
}
174
175
/// An SNMPv1 or SNMPv2c message
176
#[derive(Debug, PartialEq)]
177
pub struct SnmpMessage<'a> {
178
    /// Version, as raw-encoded: 0 for SNMPv1, 1 for SNMPv2c
179
    pub version: u32,
180
    pub community: String,
181
    pub pdu: SnmpPdu<'a>,
182
}
183
184
impl<'a> SnmpGenericPdu<'a> {
185
0
    pub fn vars_iter(&'a self) -> Iter<SnmpVariable> {
186
0
        self.var.iter()
187
0
    }
188
}
189
190
impl<'a> SnmpTrapPdu<'a> {
191
0
    pub fn vars_iter(&'a self) -> Iter<SnmpVariable> {
192
0
        self.var.iter()
193
0
    }
194
}
195
196
impl<'a> SnmpPdu<'a> {
197
155k
    pub fn pdu_type(&self) -> PduType {
198
155k
        match *self {
199
144k
            SnmpPdu::Generic(ref pdu) => pdu.pdu_type,
200
7.72k
            SnmpPdu::Bulk(_) => PduType::GetBulkRequest,
201
3.20k
            SnmpPdu::TrapV1(_) => PduType::TrapV1,
202
        }
203
155k
    }
204
205
155k
    pub fn vars_iter(&'a self) -> Iter<SnmpVariable> {
206
155k
        match *self {
207
144k
            SnmpPdu::Generic(ref pdu) => pdu.var.iter(),
208
7.72k
            SnmpPdu::Bulk(ref pdu) => pdu.var.iter(),
209
3.20k
            SnmpPdu::TrapV1(ref pdu) => pdu.var.iter(),
210
        }
211
155k
    }
212
}
213
214
impl<'a> SnmpMessage<'a> {
215
0
    pub fn pdu_type(&self) -> PduType {
216
0
        self.pdu.pdu_type()
217
0
    }
218
219
0
    pub fn vars_iter(&'a self) -> Iter<SnmpVariable> {
220
0
        self.pdu.vars_iter()
221
0
    }
222
}
223
224
#[derive(Debug, PartialEq)]
225
pub struct SnmpVariable<'a> {
226
    pub oid: Oid<'a>,
227
    pub val: VarBindValue<'a>,
228
}
229
230
#[derive(Debug, PartialEq)]
231
pub enum VarBindValue<'a> {
232
    Value(ObjectSyntax<'a>),
233
    Unspecified,
234
    NoSuchObject,
235
    NoSuchInstance,
236
    EndOfMibView,
237
}
238
239
/// <pre>
240
/// VarBind ::= SEQUENCE {
241
///     name ObjectName,
242
///
243
///     CHOICE {
244
///         value          ObjectSyntax,
245
///         unSpecified    NULL,    -- in retrieval requests
246
///
247
///                                 -- exceptions in responses
248
///         noSuchObject   [0] IMPLICIT NULL,
249
///         noSuchInstance [1] IMPLICIT NULL,
250
///         endOfMibView   [2] IMPLICIT NULL
251
///     }
252
/// }
253
/// </pre>
254
impl<'a> TryFrom<Any<'a>> for SnmpVariable<'a> {
255
    type Error = Error;
256
257
375k
    fn try_from(any: Any<'a>) -> Result<SnmpVariable<'a>, Self::Error> {
258
375k
        let (rem, oid) = Oid::from_ber(any.data)?;
259
375k
        let (_, choice) = Any::from_ber(rem)?;
260
374k
        let val = if choice.header.is_contextspecific() {
261
586
            match choice.tag().0 {
262
488
                0 => VarBindValue::NoSuchObject,
263
12
                1 => VarBindValue::NoSuchInstance,
264
52
                2 => VarBindValue::EndOfMibView,
265
                _ => {
266
34
                    return Err(Error::invalid_value(
267
34
                        choice.tag(),
268
34
                        "invalid VarBind tag".to_string(),
269
34
                    ))
270
                }
271
            }
272
374k
        } else if choice.tag() == Tag::Null {
273
6.69k
            VarBindValue::Unspecified
274
        } else {
275
367k
            VarBindValue::Value(ObjectSyntax::try_from(choice)?)
276
        };
277
374k
        let var_bind = SnmpVariable { oid, val };
278
374k
        Ok(var_bind)
279
375k
    }
280
}
281
282
#[derive(Debug, PartialEq)]
283
pub enum ObjectSyntax<'a> {
284
    Number(i32),
285
    String(&'a [u8]),
286
    Object(Oid<'a>),
287
    BitString(BitString<'a>),
288
    Empty,
289
    UnknownSimple(Any<'a>),
290
    IpAddress(NetworkAddress),
291
    Counter32(Counter),
292
    Gauge32(Gauge),
293
    TimeTicks(TimeTicks),
294
    Opaque(&'a [u8]),
295
    NsapAddress(&'a [u8]),
296
    Counter64(u64),
297
    UInteger32(u32),
298
    UnknownApplication(Any<'a>),
299
}
300
301
/// <pre>
302
/// ObjectSyntax ::= CHOICE {
303
///     simple           SimpleSyntax,
304
///     application-wide ApplicationSyntax }
305
///
306
/// SimpleSyntax ::= CHOICE {
307
///     integer-value   INTEGER (-2147483648..2147483647),
308
///     string-value    OCTET STRING (SIZE (0..65535)),
309
///     objectID-value  OBJECT IDENTIFIER }
310
///
311
/// ApplicationSyntax ::= CHOICE {
312
///     ipAddress-value        IpAddress,
313
///     counter-value          Counter32,
314
///     timeticks-value        TimeTicks,
315
///     arbitrary-value        Opaque,
316
///     big-counter-value      Counter64,
317
///     unsigned-integer-value Unsigned32 }
318
/// </pre>
319
impl<'a> TryFrom<Any<'a>> for ObjectSyntax<'a> {
320
    type Error = Error;
321
322
367k
    fn try_from(any: Any<'a>) -> Result<ObjectSyntax<'a>, Self::Error> {
323
367k
        if any.header.is_application() {
324
            // ApplicationSyntax
325
23.5k
            match any.header.tag().0 {
326
                0 => {
327
                    // IpAddress ::=
328
                    //     [APPLICATION 0]
329
                    //         IMPLICIT OCTET STRING (SIZE (4))
330
608
                    let s = any.data;
331
608
                    if s.len() == 4 {
332
602
                        let ipv4 = NetworkAddress::IPv4(Ipv4Addr::new(s[0], s[1], s[2], s[3]));
333
602
                        Ok(ObjectSyntax::IpAddress(ipv4))
334
                    } else {
335
6
                        Err(Error::InvalidTag)
336
                    }
337
                }
338
19.6k
                tag @ 1..=3 => {
339
                    // -- this wraps
340
                    // Counter32 ::=
341
                    //     [APPLICATION 1]
342
                    //         IMPLICIT INTEGER (0..4294967295)
343
                    //
344
                    // -- this doesn't wrap
345
                    // Gauge32 ::=
346
                    //     [APPLICATION 2]
347
                    //         IMPLICIT INTEGER (0..4294967295)
348
                    //
349
                    // -- an unsigned 32-bit quantity
350
                    // -- indistinguishable from Gauge32
351
                    // Unsigned32 ::=
352
                    //     [APPLICATION 2]
353
                    //         IMPLICIT INTEGER (0..4294967295)
354
                    //
355
                    // -- hundredths of seconds since an epoch
356
                    // TimeTicks ::=
357
                    //     [APPLICATION 3]
358
                    //         IMPLICIT INTEGER (0..4294967295)
359
4.46k
                    let x = Integer::new(any.data).as_u32()?;
360
4.44k
                    let obj = match tag {
361
3.51k
                        1 => ObjectSyntax::Counter32(x),
362
524
                        2 => ObjectSyntax::Gauge32(x),
363
402
                        3 => ObjectSyntax::TimeTicks(x),
364
0
                        _ => unreachable!(),
365
                    };
366
4.44k
                    Ok(obj)
367
                }
368
403
                4 => Ok(ObjectSyntax::Opaque(any.data)),
369
0
                5 => Ok(ObjectSyntax::NsapAddress(any.data)),
370
                6 => {
371
2.47k
                    let counter = Integer::new(any.data).as_u64()?;
372
2.45k
                    Ok(ObjectSyntax::Counter64(counter))
373
                }
374
                7 => {
375
473
                    let number = Integer::new(any.data).as_u32()?;
376
472
                    Ok(ObjectSyntax::UInteger32(number))
377
                }
378
15.1k
                _ => Ok(ObjectSyntax::UnknownApplication(any)),
379
            }
380
        } else {
381
            // SimpleSyntax
382
383
            // Some implementations do not send NULL, but empty objects
384
            // Treat 0-length objects as ObjectSyntax::Empty
385
343k
            if any.data.is_empty() {
386
336k
                return Ok(ObjectSyntax::Empty);
387
7.09k
            }
388
7.09k
            let obj = match any.header.tag() {
389
740
                Tag::BitString => ObjectSyntax::BitString(any.bitstring()?),
390
                Tag::Integer => {
391
3.39k
                    let number = any.integer()?.as_i32()?;
392
3.32k
                    ObjectSyntax::Number(number)
393
                }
394
0
                Tag::Null => ObjectSyntax::Empty,
395
757
                Tag::Oid => ObjectSyntax::Object(any.oid()?),
396
293
                Tag::OctetString => ObjectSyntax::String(any.data),
397
1.91k
                _ => ObjectSyntax::UnknownSimple(any),
398
            };
399
7.03k
            Ok(obj)
400
        }
401
367k
    }
402
}
403
404
#[inline]
405
161k
pub(crate) fn parse_ber_octetstring_as_str(i: &[u8]) -> IResult<&[u8], &str, Error> {
406
161k
    let (rem, b) = <&[u8]>::from_ber(i)?;
407
160k
    let s = core::str::from_utf8(b).map_err(|_| Error::StringInvalidCharset)?;
408
160k
    Ok((rem, s))
409
161k
}
410
411
157k
fn parse_varbind_list(i: &[u8]) -> IResult<&[u8], Vec<SnmpVariable>, Error> {
412
    // parse_ber_sequence_of_v(parse_varbind)(i)
413
157k
    <Vec<SnmpVariable>>::from_ber(i)
414
157k
}
415
416
/// <pre>
417
///  NetworkAddress ::=
418
///      CHOICE {
419
///          internet
420
///              IpAddress
421
///      }
422
/// IpAddress ::=
423
///     [APPLICATION 0]          -- in network-byte order
424
///         IMPLICIT OCTET STRING (SIZE (4))
425
/// </pre>
426
impl<'a> TryFrom<Any<'a>> for NetworkAddress {
427
    type Error = Error;
428
429
3.31k
    fn try_from(any: Any<'a>) -> Result<Self, Self::Error> {
430
3.31k
        any.class().assert_eq(Class::Application)?;
431
3.30k
        let s = any.data;
432
3.30k
        if s.len() == 4 {
433
3.28k
            Ok(NetworkAddress::IPv4(Ipv4Addr::new(s[0], s[1], s[2], s[3])))
434
        } else {
435
21
            Err(Error::invalid_value(
436
21
                Tag::OctetString,
437
21
                "NetworkAddress invalid length".to_string(),
438
21
            ))
439
        }
440
3.31k
    }
441
}
442
443
/// <pre>
444
/// TimeTicks ::=
445
///     [APPLICATION 3]
446
///         IMPLICIT INTEGER (0..4294967295)
447
/// </pre>
448
3.26k
fn parse_timeticks(i: &[u8]) -> IResult<&[u8], TimeTicks, Error> {
449
3.26k
    let (rem, tagged) = Application::<u32, _, Implicit, 3>::from_ber(i)?;
450
3.20k
    Ok((rem, tagged.into_inner()))
451
3.26k
}
452
453
147k
fn parse_snmp_v1_generic_pdu(pdu: &[u8], tag: PduType) -> IResult<&[u8], SnmpPdu, SnmpError> {
454
147k
    let (i, req_id) = u32::from_ber(pdu).map_err(Err::convert)?;
455
147k
    let (i, err) = map(u32::from_ber, ErrorStatus)(i).map_err(Err::convert)?;
456
146k
    let (i, err_index) = u32::from_ber(i).map_err(Err::convert)?;
457
146k
    let (i, var) = parse_varbind_list(i).map_err(Err::convert)?;
458
144k
    let pdu = SnmpPdu::Generic(SnmpGenericPdu {
459
144k
        pdu_type: tag,
460
144k
        req_id,
461
144k
        err,
462
144k
        err_index,
463
144k
        var,
464
144k
    });
465
144k
    Ok((i, pdu))
466
147k
}
467
468
8.18k
fn parse_snmp_v1_bulk_pdu(i: &[u8]) -> IResult<&[u8], SnmpPdu, SnmpError> {
469
8.18k
    let (i, req_id) = u32::from_ber(i).map_err(Err::convert)?;
470
8.17k
    let (i, non_repeaters) = u32::from_ber(i).map_err(Err::convert)?;
471
7.88k
    let (i, max_repetitions) = u32::from_ber(i).map_err(Err::convert)?;
472
7.86k
    let (i, var) = parse_varbind_list(i).map_err(Err::convert)?;
473
7.72k
    let pdu = SnmpBulkPdu {
474
7.72k
        req_id,
475
7.72k
        non_repeaters,
476
7.72k
        max_repetitions,
477
7.72k
        var,
478
7.72k
    };
479
7.72k
    Ok((i, SnmpPdu::Bulk(pdu)))
480
8.18k
}
481
482
3.60k
fn parse_snmp_v1_trap_pdu(i: &[u8]) -> IResult<&[u8], SnmpPdu, SnmpError> {
483
3.60k
    let (i, enterprise) = Oid::from_ber(i).map_err(Err::convert)?;
484
3.32k
    let (i, agent_addr) = NetworkAddress::from_ber(i).map_err(Err::convert)?;
485
3.28k
    let (i, generic_trap) = u32::from_ber(i).map_err(Err::convert)?;
486
3.27k
    let (i, specific_trap) = u32::from_ber(i).map_err(Err::convert)?;
487
3.26k
    let (i, timestamp) = parse_timeticks(i).map_err(Err::convert)?;
488
3.20k
    let (i, var) = parse_varbind_list(i).map_err(Err::convert)?;
489
3.20k
    let pdu = SnmpTrapPdu {
490
3.20k
        enterprise,
491
3.20k
        agent_addr,
492
3.20k
        generic_trap: TrapType(generic_trap as u8),
493
3.20k
        specific_trap,
494
3.20k
        timestamp,
495
3.20k
        var,
496
3.20k
    };
497
3.20k
    Ok((i, SnmpPdu::TrapV1(pdu)))
498
3.60k
}
499
500
/// Parse a SNMP v1 message.
501
///
502
/// Top-level message
503
///
504
/// <pre>
505
/// Message ::=
506
///         SEQUENCE {
507
///             version          -- version-1 for this RFC
508
///                 INTEGER {
509
///                     version-1(0)
510
///                 },
511
///
512
///             community        -- community name
513
///                 OCTET STRING,
514
///
515
///             data             -- e.g., PDUs if trivial
516
///                 ANY          -- authentication is being used
517
///         }
518
/// </pre>
519
///
520
/// Example:
521
///
522
/// ```rust
523
/// use snmp_parser::parse_snmp_v1;
524
///
525
/// static SNMPV1_REQ: &[u8] = include_bytes!("../assets/snmpv1_req.bin");
526
///
527
/// # fn main() {
528
/// match parse_snmp_v1(&SNMPV1_REQ) {
529
///   Ok((_, ref r)) => {
530
///     assert!(r.version == 0);
531
///     assert!(r.community == String::from("public"));
532
///     assert!(r.vars_iter().count() == 1);
533
///   },
534
///   Err(e) => panic!("{}", e),
535
/// }
536
/// # }
537
/// ```
538
0
pub fn parse_snmp_v1(bytes: &[u8]) -> IResult<&[u8], SnmpMessage, SnmpError> {
539
0
    Sequence::from_der_and_then(bytes, |i| {
540
0
        let (i, version) = u32::from_ber(i).map_err(Err::convert)?;
541
0
        if version != 0 {
542
0
            return Err(Err::Error(SnmpError::InvalidVersion));
543
0
        }
544
0
        let (i, community) = parse_ber_octetstring_as_str(i).map_err(Err::convert)?;
545
0
        let (i, pdu) = parse_snmp_v1_pdu(i)?;
546
0
        let msg = SnmpMessage {
547
0
            version,
548
0
            community: community.to_string(),
549
0
            pdu,
550
0
        };
551
0
        Ok((i, msg))
552
0
    })
553
    //.map_err(Err::convert)
554
0
}
555
556
130k
pub(crate) fn parse_snmp_v1_pdu(i: &[u8]) -> IResult<&[u8], SnmpPdu, SnmpError> {
557
130k
    match Header::from_ber(i) {
558
130k
        Ok((rem, hdr)) => {
559
130k
            match PduType(hdr.tag().0) {
560
                PduType::GetRequest |
561
                PduType::GetNextRequest |
562
                PduType::Response |
563
128k
                PduType::SetRequest     => parse_snmp_v1_generic_pdu(rem, PduType(hdr.tag().0)),
564
1.78k
                PduType::TrapV1         => parse_snmp_v1_trap_pdu(rem),
565
44
                _                       => Err(Err::Error(SnmpError::InvalidPduType)),
566
                // _                       => { return IResult::Error(error_code!(ErrorKind::Custom(SnmpError::InvalidPdu))); },
567
            }
568
        },
569
6
        Err(e)        => Err(Err::convert(e))
570
        // IResult::Incomplete(i) => IResult::Incomplete(i),
571
        // IResult::Error(_)      => IResult::Error(error_code!(ErrorKind::Custom(129))),
572
        // // IResult::Error(_)      => IResult::Error(error_code!(ErrorKind::Custom(SnmpError::InvalidScopedPduData))),
573
    }
574
130k
}
575
576
/// Parse a SNMP v2c message.
577
///
578
/// Top-level message
579
///
580
/// <pre>
581
/// Message ::=
582
///         SEQUENCE {
583
///             version
584
///                 INTEGER {
585
///                     version(1)  -- modified from RFC 1157
586
///                 },
587
///
588
///             community           -- community name
589
///                 OCTET STRING,
590
///
591
///             data                -- PDUs as defined in [4]
592
///                 ANY
593
///         }
594
/// </pre>
595
0
pub fn parse_snmp_v2c(bytes: &[u8]) -> IResult<&[u8], SnmpMessage, SnmpError> {
596
0
    Sequence::from_der_and_then(bytes, |i| {
597
0
        let (i, version) = u32::from_ber(i).map_err(Err::convert)?;
598
0
        if version != 1 {
599
0
            return Err(Err::Error(SnmpError::InvalidVersion));
600
0
        }
601
0
        let (i, community) = parse_ber_octetstring_as_str(i).map_err(Err::convert)?;
602
0
        let (i, pdu) = parse_snmp_v2c_pdu(i)?;
603
0
        let msg = SnmpMessage {
604
0
            version,
605
0
            community: community.to_string(),
606
0
            pdu,
607
0
        };
608
0
        Ok((i, msg))
609
0
    })
610
0
}
611
612
29.2k
pub(crate) fn parse_snmp_v2c_pdu(i: &[u8]) -> IResult<&[u8], SnmpPdu, SnmpError> {
613
29.2k
    match Header::from_ber(i) {
614
29.2k
        Ok((rem, hdr)) => {
615
29.2k
            match PduType(hdr.tag().0) {
616
                PduType::GetRequest |
617
                PduType::GetNextRequest |
618
                PduType::Response |
619
                PduType::SetRequest |
620
                PduType::InformRequest |
621
                PduType::TrapV2 |
622
19.0k
                PduType::Report         => parse_snmp_v1_generic_pdu(rem, PduType(hdr.tag().0)),
623
8.18k
                PduType::GetBulkRequest => parse_snmp_v1_bulk_pdu(rem),
624
1.82k
                PduType::TrapV1         => parse_snmp_v1_trap_pdu(rem),
625
159
                _                       => Err(Err::Error(SnmpError::InvalidPduType)),
626
                // _                       => { return IResult::Error(error_code!(ErrorKind::Custom(SnmpError::InvalidPdu))); },
627
            }
628
        },
629
28
        Err(e)        => Err(Err::convert(e))
630
        // IResult::Incomplete(i) => IResult::Incomplete(i),
631
        // IResult::Error(_)      => IResult::Error(error_code!(ErrorKind::Custom(129))),
632
        // // IResult::Error(_)      => IResult::Error(error_code!(ErrorKind::Custom(SnmpError::InvalidScopedPduData))),
633
    }
634
29.2k
}