/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 | } |