… | |
… | |
7 | use Convert::BER::XS ':all'; |
7 | use Convert::BER::XS ':all'; |
8 | |
8 | |
9 | my $ber = ber_decode $buf |
9 | my $ber = ber_decode $buf |
10 | or die "unable to decode SNMP message"; |
10 | or die "unable to decode SNMP message"; |
11 | |
11 | |
12 | # The above results in a data structure consisting of (class, tag, |
12 | # The above results in a data structure consisting of |
|
|
13 | # (class, tag, # constructed, data) |
13 | # constructed, data) tuples. Below is such a message, SNMPv1 trap |
14 | # tuples. Below is such a message, SNMPv1 trap |
14 | # with a Cisco mac change notification. |
15 | # with a Cisco mac change notification. |
15 | # Did you know that Cisco is in the news almost every week because |
16 | # Did you know that Cisco is in the news almost |
|
|
17 | # every week because # of some backdoor password |
16 | # of some backdoor password or other extremely stupid security bug? |
18 | # or other extremely stupid security bug? |
17 | |
19 | |
18 | [ ASN_UNIVERSAL, ASN_SEQUENCE, 1, |
20 | [ ASN_UNIVERSAL, ASN_SEQUENCE, 1, |
19 | [ |
21 | [ |
20 | [ ASN_UNIVERSAL, ASN_INTEGER32, 0, 0 ], # snmp version 1 |
22 | [ ASN_UNIVERSAL, ASN_INTEGER32, 0, 0 ], # snmp version 1 |
21 | [ ASN_UNIVERSAL, 4, 0, "public" ], # community |
23 | [ ASN_UNIVERSAL, 4, 0, "public" ], # community |
… | |
… | |
287 | use common::sense; |
289 | use common::sense; |
288 | |
290 | |
289 | use XSLoader (); |
291 | use XSLoader (); |
290 | use Exporter qw(import); |
292 | use Exporter qw(import); |
291 | |
293 | |
292 | our $VERSION = 0.2; |
294 | our $VERSION; |
293 | |
295 | |
|
|
296 | BEGIN { |
|
|
297 | $VERSION = 0.7; |
294 | XSLoader::load __PACKAGE__, $VERSION; |
298 | XSLoader::load __PACKAGE__, $VERSION; |
|
|
299 | } |
295 | |
300 | |
296 | our %EXPORT_TAGS = ( |
301 | our %EXPORT_TAGS = ( |
297 | const => [qw( |
302 | const => [qw( |
298 | BER_CLASS BER_TAG BER_CONSTRUCTED BER_DATA |
303 | BER_CLASS BER_TAG BER_CONSTRUCTED BER_DATA |
299 | |
304 | |
300 | ASN_BOOLEAN ASN_INTEGER32 ASN_BIT_STRING ASN_OCTET_STRING ASN_NULL ASN_OBJECT_IDENTIFIER ASN_TAG_BER ASN_TAG_MASK |
305 | ASN_BOOLEAN ASN_INTEGER32 ASN_BIT_STRING ASN_OCTET_STRING ASN_NULL ASN_OBJECT_IDENTIFIER |
301 | ASN_CONSTRUCTED ASN_UNIVERSAL ASN_APPLICATION ASN_CONTEXT ASN_PRIVATE ASN_CLASS_MASK ASN_CLASS_SHIFT |
306 | ASN_OBJECT_DESCRIPTOR ASN_OID ASN_EXTERNAL ASN_REAL ASN_SEQUENCE ASN_ENUMERATED |
302 | ASN_SEQUENCE |
307 | ASN_EMBEDDED_PDV ASN_UTF8_STRING ASN_RELATIVE_OID ASN_SET ASN_NUMERIC_STRING |
303 | |
308 | ASN_PRINTABLE_STRING ASN_TELETEX_STRING ASN_T61_STRING ASN_VIDEOTEX_STRING ASN_IA5_STRING |
|
|
309 | ASN_ASCII_STRING ASN_UTC_TIME ASN_GENERALIZED_TIME ASN_GRAPHIC_STRING ASN_VISIBLE_STRING |
|
|
310 | ASN_ISO646_STRING ASN_GENERAL_STRING ASN_UNIVERSAL_STRING ASN_CHARACTER_STRING ASN_BMP_STRING |
|
|
311 | |
|
|
312 | ASN_UNIVERSAL ASN_APPLICATION ASN_CONTEXT ASN_PRIVATE |
|
|
313 | |
|
|
314 | BER_TYPE_BYTES BER_TYPE_UTF8 BER_TYPE_UCS2 BER_TYPE_UCS4 BER_TYPE_INT |
|
|
315 | BER_TYPE_OID BER_TYPE_RELOID BER_TYPE_NULL BER_TYPE_BOOL BER_TYPE_REAL |
|
|
316 | BER_TYPE_IPADDRESS BER_TYPE_CROAK |
|
|
317 | )], |
|
|
318 | const_snmp => [qw( |
304 | SNMP_IPADDRESS SNMP_COUNTER32 SNMP_UNSIGNED32 SNMP_TIMETICKS SNMP_OPAQUE SNMP_COUNTER64 |
319 | SNMP_IPADDRESS SNMP_COUNTER32 SNMP_UNSIGNED32 SNMP_TIMETICKS SNMP_OPAQUE SNMP_COUNTER64 |
305 | )], |
320 | )], |
306 | encode => [qw( |
321 | encode => [qw( |
307 | ber_decode |
322 | ber_decode |
308 | ber_is ber_is_seq ber_is_i32 ber_is_oid |
323 | ber_is ber_is_seq ber_is_i32 ber_is_oid |
309 | )], |
324 | )], |
310 | decode => [qw( |
325 | decode => [qw( |
311 | ber_encode |
326 | ber_encode |
|
|
327 | ber_i32 |
312 | )], |
328 | )], |
313 | ); |
329 | ); |
314 | |
330 | |
315 | our @EXPORT_OK = map @$_, values %EXPORT_TAGS; |
331 | our @EXPORT_OK = map @$_, values %EXPORT_TAGS; |
316 | |
332 | |
317 | $EXPORT_TAGS{all} = \@EXPORT_OK; |
333 | $EXPORT_TAGS{all} = \@EXPORT_OK; |
318 | |
334 | |
|
|
335 | =head1 PROFILES |
|
|
336 | |
|
|
337 | While any BER data can be correctly encoded and decoded out of the box, it |
|
|
338 | can be inconvenient to have to manually decode some values into a "better" |
|
|
339 | format: for instance, SNMP TimeTicks values are decoded into the raw octet |
|
|
340 | strings of their BER representation, which is quite hard to decode. With |
|
|
341 | profiles, you can change which class/tag combinations map to which decoder |
|
|
342 | function inside C<ber_decode> (and of course also which encoder functions |
|
|
343 | are used in C<ber_encode>). |
|
|
344 | |
|
|
345 | This works by mapping specific class/tag combinations to an internal "ber |
|
|
346 | type". |
|
|
347 | |
|
|
348 | The default profile supports the standard ASN.1 types, but no |
|
|
349 | application-specific ones. This means that class/tag combinations not in |
|
|
350 | the base set of ASN.1 are decoded into their raw octet strings. |
|
|
351 | |
|
|
352 | C<Convert::BER::XS> defines two profile variables you cna use out of the box: |
|
|
353 | |
|
|
354 | =over |
|
|
355 | |
|
|
356 | =item C<$Convert::BER::XS::DEFAULT_PROFILE> |
|
|
357 | |
|
|
358 | This is the default profile, i.e. the profile that is used when no |
|
|
359 | profile is specified for de-/encoding. |
|
|
360 | |
|
|
361 | You cna modify it, but remember that this modifies the defaults for all |
|
|
362 | callers that rely on the defauit profile. |
|
|
363 | |
|
|
364 | =item C<$Convert::BER::XS::SNMP_PROFILE> |
|
|
365 | |
|
|
366 | A profile with mappings for SNMP-specific application tags added. This is |
|
|
367 | useful when de-/encoding SNMP data. |
|
|
368 | |
|
|
369 | Example: |
|
|
370 | $ber = ber_decode $data, $Convert::BER::XS::SNMP_PROFILE; |
|
|
371 | |
|
|
372 | =back |
|
|
373 | |
|
|
374 | =head2 The Convert::BER::XS::Profile class |
|
|
375 | |
|
|
376 | =over |
|
|
377 | |
|
|
378 | =item $profile = new Convert::BER::XS::Profile |
|
|
379 | |
|
|
380 | Create a new profile. The profile will be identical to the default |
|
|
381 | profile. |
|
|
382 | |
|
|
383 | =item $profile->set ($class, $tag, $type) |
|
|
384 | |
|
|
385 | Sets the mapping for the given C<$class>/C<$tag> combination to C<$type>, |
|
|
386 | which must be one of the C<BER_TYPE_*> constants. |
|
|
387 | |
|
|
388 | Note that currently, the mapping is stored in a flat array, so large |
|
|
389 | values of C<$tag> will consume large amounts of memory. |
|
|
390 | |
|
|
391 | Example: |
|
|
392 | $profile = new Convert::BER::XS::Profile; |
|
|
393 | $profile->set (ASN_APPLICATION, SNMP_COUNTER32, BER_TYPE_INT); |
|
|
394 | $ber = ber_decode $data, $profile; |
|
|
395 | |
|
|
396 | =item $type = $profile->get ($class, $tag) |
|
|
397 | |
|
|
398 | Returns the BER type mapped to the given C<$class>/C<$tag> combination. |
|
|
399 | |
|
|
400 | =back |
|
|
401 | |
|
|
402 | =head2 BER TYPES |
|
|
403 | |
|
|
404 | This lists the predefined BER types - you can map any C<CLASS>/C<TAG> |
|
|
405 | combination to any C<BER_TYPE_*>. |
|
|
406 | |
|
|
407 | =over |
|
|
408 | |
|
|
409 | =item C<BER_TYPE_BYTES> |
|
|
410 | |
|
|
411 | The raw octets of the value. This is the default type for unknown tags and |
|
|
412 | de-/encodes the value as if it were an octet string, i.e. by copying the |
|
|
413 | raw bytes. |
|
|
414 | |
|
|
415 | =item C<BER_TYPE_UTF8> |
|
|
416 | |
|
|
417 | Like C<BER_TYPE_BYTES>, but decodes the value as if it were a UTF-8 string |
|
|
418 | (without validation!) and encodes a perl unicode string into a UTF-8 BER |
|
|
419 | string. |
|
|
420 | |
|
|
421 | =item C<BER_TYPE_UCS2> |
|
|
422 | |
|
|
423 | Similar to C<BER_TYPE_UTF8>, but treats the BER value as UCS-2 encoded |
|
|
424 | string. NOT IMPLEMENTED. |
|
|
425 | |
|
|
426 | =item C<BER_TYPE_UCS4> |
|
|
427 | |
|
|
428 | Similar to C<BER_TYPE_UTF8>, but treats the BER value as UCS-4 encoded |
|
|
429 | string. NOT IMPLEMENTED. |
|
|
430 | |
|
|
431 | =item C<BER_TYPE_INT> |
|
|
432 | |
|
|
433 | Encodes and decodes a BER integer value to a perl integer scalar. This |
|
|
434 | should correctly handle 64 bit signed and unsigned values. |
|
|
435 | |
|
|
436 | =item C<BER_TYPE_OID> |
|
|
437 | |
|
|
438 | Encodes and decodes an OBJECT IDENTIFIER into dotted form without leading |
|
|
439 | dot, e.g. C<1.3.6.1.213>. |
|
|
440 | |
|
|
441 | =item C<BER_TYPE_RELOID> |
|
|
442 | |
|
|
443 | Same as C<BER_TYPE_OID> but uses relative OID encoding: ASN.1 has this |
|
|
444 | hack of encoding the first two OID components into a single integer in a |
|
|
445 | weird attempt to save an insignificant amount of space in an otherwise |
|
|
446 | wasteful encoding, and relative OIDs are basically OIDs without this |
|
|
447 | hack. The practical difference is that the second component of an OID |
|
|
448 | can only have the values 1..40, while relative OIDs do not have this |
|
|
449 | restriction. |
|
|
450 | |
|
|
451 | =item C<BER_TYPE_NULL> |
|
|
452 | |
|
|
453 | Decodes an C<ASN_NULL> value into C<undef>, and always encodes a |
|
|
454 | C<ASN_NULL> type, regardless of the perl value. |
|
|
455 | |
|
|
456 | =item C<BER_TYPE_BOOL> |
|
|
457 | |
|
|
458 | Decodes an C<ASN_BOOLEAN> value into C<0> or C<1>, and encodes a perl |
|
|
459 | boolean value into an C<ASN_BOOLEAN>. |
|
|
460 | |
|
|
461 | =item C<BER_TYPE_REAL> |
|
|
462 | |
|
|
463 | Decodes/encodes a BER real value. NOT IMPLEMENTED. |
|
|
464 | |
|
|
465 | =item C<BER_TYPE_IPADDRESS> |
|
|
466 | |
|
|
467 | Decodes/encodes a four byte string into an IOv4 dotted-quad address string |
|
|
468 | in perl. Given ther obsolete nature of this type, this is a low-effort |
|
|
469 | implementation that simply uses C<sprintf> and C<sscanf>-style conversion, |
|
|
470 | so it won't handle all string forms supported by C<inet_aton>. |
|
|
471 | |
|
|
472 | =item C<BER_TYPE_CROAK> |
|
|
473 | |
|
|
474 | Always croaks when encountered during encoding or decoding - the |
|
|
475 | default behaviour when encountering an unknown type is to treat it as |
|
|
476 | C<BER_TYPE_BYTES>. When you don't want that but instead prefer a hard |
|
|
477 | error for some types, then CyBER_TYPE_CROAK> is for you. |
|
|
478 | |
|
|
479 | =back |
|
|
480 | |
|
|
481 | =cut |
|
|
482 | |
|
|
483 | our $DEFAULT_PROFILE = new Convert::BER::XS::Profile; |
|
|
484 | our $SNMP_PROFILE = new Convert::BER::XS::Profile; |
|
|
485 | |
|
|
486 | $SNMP_PROFILE->set (ASN_APPLICATION, SNMP_IPADDRESS , BER_TYPE_IPADDRESS); |
|
|
487 | $SNMP_PROFILE->set (ASN_APPLICATION, SNMP_COUNTER32 , BER_TYPE_INT); |
|
|
488 | $SNMP_PROFILE->set (ASN_APPLICATION, SNMP_UNSIGNED32, BER_TYPE_INT); |
|
|
489 | $SNMP_PROFILE->set (ASN_APPLICATION, SNMP_TIMETICKS , BER_TYPE_INT); |
|
|
490 | $SNMP_PROFILE->set (ASN_APPLICATION, SNMP_OPAQUE , BER_TYPE_IPADDRESS); |
|
|
491 | $SNMP_PROFILE->set (ASN_APPLICATION, SNMP_COUNTER64 , BER_TYPE_INT); |
|
|
492 | |
|
|
493 | $DEFAULT_PROFILE->_set_default; |
|
|
494 | |
319 | 1; |
495 | 1; |
320 | |
496 | |
321 | =head2 BUGS / SHORTCOMINGs |
497 | =head2 LIMITATIONS |
322 | |
498 | |
323 | This module does have a number of SNMPisms hardcoded, such as the SNMP |
499 | This module can only en-/decode 64 bit signed and unsigned integers, and |
324 | tags for Unsigned32 and so on. More configurability is needed, and, if |
500 | only when your perl supports those. |
325 | ever implemented, will come in a form similar to how L<JSON::XS> and |
501 | |
326 | L<CBOR::XS> respresent things, namely with an object-oriented interface. |
502 | OBJECT IDENTIFIEERS cannot have unlimited length, although the limit is |
|
|
503 | much larger than e.g. the one imposed by SNMP or other protocols. |
327 | |
504 | |
328 | =head1 AUTHOR |
505 | =head1 AUTHOR |
329 | |
506 | |
330 | Marc Lehmann <schmorp@schmorp.de> |
507 | Marc Lehmann <schmorp@schmorp.de> |
331 | http://software.schmorp.de/pkg/Convert-BER-XS |
508 | http://software.schmorp.de/pkg/Convert-BER-XS |