ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Convert-BER-XS/XS.pm
(Generate patch)

Comparing Convert-BER-XS/XS.pm (file contents):
Revision 1.40 by root, Sun Apr 21 00:42:15 2019 UTC vs.
Revision 1.62 by root, Thu Feb 6 23:15:44 2020 UTC

8 8
9 my $ber = ber_decode $buf, $Convert::BER::XS::SNMP_PROFILE 9 my $ber = ber_decode $buf, $Convert::BER::XS::SNMP_PROFILE
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 12 # The above results in a data structure consisting of
13 # (class, tag, flags, data) 13 # (class, tag, flags, data)
14 # tuples. Below is such a message, SNMPv1 trap 14 # tuples. Below is such a message, SNMPv1 trap
15 # with a Cisco mac change notification. 15 # with a Cisco mac change notification.
16 # Did you know that Cisco is in the news almost 16 # Did you know that Cisco is in the news almost
17 # every week because of some backdoor password 17 # every week because of some backdoor password
18 # or other extremely stupid security bug? 18 # or other extremely stupid security bug?
69 69
70 my $buf = ber_encode $ber, $Convert::BER::XS::SNMP_PROFILE; 70 my $buf = ber_encode $ber, $Convert::BER::XS::SNMP_PROFILE;
71 71
72=head1 DESCRIPTION 72=head1 DESCRIPTION
73 73
74WARNING: Before release 1.0, the API is not considered stable in any way.
75
76This module implements a I<very> low level BER/DER en-/decoder. 74This module implements a I<very> low level BER/DER en-/decoder.
77 75
78It is tuned for low memory and high speed, while still maintaining some 76It is tuned for low memory and high speed, while still maintaining some
79level of user-friendlyness. 77level of user-friendlyness.
80 78
108 BER_CLASS BER_TAG BER_FLAGS BER_DATA 106 BER_CLASS BER_TAG BER_FLAGS BER_DATA
109 107
110=item C<:const_asn> 108=item C<:const_asn>
111 109
112ASN class values (these are C<0>, C<1>, C<2> and C<3>, respectively - 110ASN class values (these are C<0>, C<1>, C<2> and C<3>, respectively -
113exactly thw two topmost bits from the identifier octet shifted 6 bits to 111exactly the two topmost bits from the identifier octet shifted 6 bits to
114the right): 112the right):
115 113
116 ASN_UNIVERSAL ASN_APPLICATION ASN_CONTEXT ASN_PRIVATE 114 ASN_UNIVERSAL ASN_APPLICATION ASN_CONTEXT ASN_PRIVATE
117 115
118ASN tag values (some of which are aliases, such as C<ASN_OID>). Their 116ASN tag values (some of which are aliases, such as C<ASN_OID>). Their
296Also, since BER is self-delimited, this can be used to decode multiple BER 294Also, since BER is self-delimited, this can be used to decode multiple BER
297values joined together. 295values joined together.
298 296
299=item $bindata = ber_encode $tuple[, $profile] 297=item $bindata = ber_encode $tuple[, $profile]
300 298
301Encodes the BER tuple into a BER/DER data structure. AS with 299Encodes the BER tuple into a BER/DER data structure. As with
302Cyber_decode>, an optional profile can be given. 300Cyber_decode>, an optional profile can be given.
303 301
304The encoded data should be both BER and DER ("shortest form") compliant 302The encoded data should be both BER and DER ("shortest form") compliant
305unless the input says otherwise (e.g. it uses constructed strings). 303unless the input says otherwise (e.g. it uses constructed strings).
306 304
409use common::sense; 407use common::sense;
410 408
411use XSLoader (); 409use XSLoader ();
412use Exporter qw(import); 410use Exporter qw(import);
413 411
412use Carp ();
413
414our $VERSION; 414our $VERSION;
415 415
416BEGIN { 416BEGIN {
417 $VERSION = '1.0'; 417 $VERSION = 1.21;
418 XSLoader::load __PACKAGE__, $VERSION; 418 XSLoader::load __PACKAGE__, $VERSION;
419} 419}
420 420
421our %EXPORT_TAGS = ( 421our %EXPORT_TAGS = (
422 const_index => [qw( 422 const_index => [qw(
468 468
469$SNMP_PROFILE->set (ASN_APPLICATION, SNMP_IPADDRESS , BER_TYPE_IPADDRESS); 469$SNMP_PROFILE->set (ASN_APPLICATION, SNMP_IPADDRESS , BER_TYPE_IPADDRESS);
470$SNMP_PROFILE->set (ASN_APPLICATION, SNMP_COUNTER32 , BER_TYPE_INT); 470$SNMP_PROFILE->set (ASN_APPLICATION, SNMP_COUNTER32 , BER_TYPE_INT);
471$SNMP_PROFILE->set (ASN_APPLICATION, SNMP_UNSIGNED32, BER_TYPE_INT); 471$SNMP_PROFILE->set (ASN_APPLICATION, SNMP_UNSIGNED32, BER_TYPE_INT);
472$SNMP_PROFILE->set (ASN_APPLICATION, SNMP_TIMETICKS , BER_TYPE_INT); 472$SNMP_PROFILE->set (ASN_APPLICATION, SNMP_TIMETICKS , BER_TYPE_INT);
473$SNMP_PROFILE->set (ASN_APPLICATION, SNMP_OPAQUE , BER_TYPE_IPADDRESS); 473
474# decodes REAL values according to ECMA-63
475# this is pretty strict, except it doesn't catch -0.
476# I don't have access to ISO 6093 (or BS 6727, or ANSI X.3-42)), so this is all guesswork.
477sub _decode_real_decimal {
478 my ($format, $val) = @_;
479
480 $val =~ y/,/./; # probably not in ISO-6093
481
482 if ($format == 1) {
483 $val =~ /^ \ * [+-]? [0-9]+ \z/x
484 or Carp::croak "BER_TYPE_REAL NR1 value not in NR1 format ($val) (X.690 8.5.8)";
485 } elsif ($format == 2) {
486 $val =~ /^ \ * [+-]? (?: [0-9]+\.[0-9]* | [0-9]*\.[0-9]+ ) \z/x
487 or Carp::croak "BER_TYPE_REAL NR2 value not in NR2 format ($val) (X.690 8.5.8)";
488 } elsif ($format == 3) {
489 $val =~ /^ \ * [+-] (?: [0-9]+\.[0-9]* | [0-9]*\.[0-9]+ ) [eE] [+-]? [0-9]+ \z/x
490 or Carp::croak "BER_TYPE_REAL NR3 value not in NR3 format ($val) (X.690 8.5.8)";
491 } else {
492 Carp::croak "BER_TYPE_REAL invalid decimal numerical representation format $format";
493 }
494
495 $val
496}
497
498# this is a mess, but perl's support for floating point formatting is nearly nonexistant
499sub _encode_real_decimal {
500 my ($val, $nvdig) = @_;
501
502 $val = sprintf "%.*G", $nvdig + 1, $val;
503
504 if ($val =~ /E/) {
505 $val =~ s/E(?=[^+-])/E+/;
506 $val =~ s/E/.E/ if $val !~ /\./;
507 $val =~ s/^/+/ unless $val =~ /^-/;
508
509 return "\x03$val" # NR3
510 }
511
512 $val =~ /\./
513 ? "\x02$val" # NR2
514 : "\x01$val" # NR1
515}
474 516
475=head2 DEBUGGING 517=head2 DEBUGGING
476 518
477To aid debugging, you cna call the C<ber_dump> function to print a "nice" 519To aid debugging, you can call the C<ber_dump> function to print a "nice"
478representation to STDOUT. 520representation to STDOUT.
479 521
480=over 522=over
481 523
482=item ber_dump $tuple[, $profile[, $prefix]] 524=item ber_dump $tuple[, $profile[, $prefix]]
483 525
484In addition to specifying the BER C<$tuple> to dump, youc an also specify 526In addition to specifying the BER C<$tuple> to dump, you can also specify
485a C<$profile> and a C<$prefix> string that is printed in front of each line. 527a C<$profile> and a C<$prefix> string that is printed in front of each line.
486 528
487If C<$profile> is C<$Convert::BER::XS::SNMP_PROFILE>, then C<ber_dump> 529If C<$profile> is C<$Convert::BER::XS::SNMP_PROFILE>, then C<ber_dump>
488will try to improve its output for SNMP data. 530will try to improve its output for SNMP data.
489 531
496Example output: 538Example output:
497 539
498 SEQUENCE 540 SEQUENCE
499 | OCTET_STRING bytes 800063784300454045045400000001 541 | OCTET_STRING bytes 800063784300454045045400000001
500 | OCTET_STRING bytes 542 | OCTET_STRING bytes
501 | CONTEXT (7) bytes CONSTRUCTED 543 | CONTEXT (7) CONSTRUCTED
502 | | INTEGER int 1058588941 544 | | INTEGER int 1058588941
503 | | INTEGER int 0 545 | | INTEGER int 0
504 | | INTEGER int 0 546 | | INTEGER int 0
505 | | SEQUENCE 547 | | SEQUENCE
506 | | | SEQUENCE 548 | | | SEQUENCE
507 | | | | OID oid 1.3.6.1.2.1.1.3.0 549 | | | | OID oid 1.3.6.1.2.1.1.3.0
508 | | | | TIMETICKS int 638085796 550 | | | | TIMETICKS int 638085796
509 551
552=back
553
510=cut 554=cut
511 555
512# reverse enum, very slow and ugly hack 556# reverse enum, very slow and ugly hack
513sub _re { 557sub _re {
514 my ($export_tag, $value) = @_; 558 my ($export_tag, $value) = @_;
547 $class =~ s/^ASN_//; 591 $class =~ s/^ASN_//;
548 $tag =~ s/^(ASN_|SNMP_)//; 592 $tag =~ s/^(ASN_|SNMP_)//;
549 $type =~ s/^BER_TYPE_//; 593 $type =~ s/^BER_TYPE_//;
550 594
551 if ($ber->[BER_FLAGS]) { 595 if ($ber->[BER_FLAGS]) {
552 printf "$indent%-16.16s %-6.6s CONSTRUCTED\n", $tag, lc $type; 596 printf "$indent%-16.16s\n", $tag;
553 &_ber_dump ($_, $profile, "$indent| ") 597 &_ber_dump ($_, $profile, "$indent| ")
554 for @$data; 598 for @$data;
555 } else { 599 } else {
556 if ($data =~ y/\x20-\x7e//c / (length $data || 1) > 0.2 or $data =~ /\x00./s) { 600 if ($data =~ y/\x20-\x7e//c / (length $data || 1) > 0.2 or $data =~ /\x00./s) {
557 # assume binary 601 # assume binary
558 $data = unpack "H*", $data; 602 $data = unpack "H*", $data;
559 substr $data, 40, 1e9, "..." if 40 < length $data;
560 } else { 603 } else {
561 $data =~ s/[^\x20-\x7e]/./g; 604 $data =~ s/[^\x20-\x7e]/./g;
562 $data = "\"$data\"" if $type =~ /string/i; 605 $data = "\"$data\"" if $tag =~ /string/i || !length $data;
563 substr $data, 40, 1e9, "..." if 40 < length $data;
564 } 606 }
607
608 substr $data, 40, 1e9, "..." if 40 < length $data;
565 609
566 printf "$indent%-16.16s %-6.6s %s\n", $tag, lc $type, $data; 610 printf "$indent%-16.16s %-6.6s %s\n", $tag, lc $type, $data;
567 } 611 }
568 } 612 }
569} 613}
639 683
640Returns the BER type mapped to the given C<$class>/C<$tag> combination. 684Returns the BER type mapped to the given C<$class>/C<$tag> combination.
641 685
642=back 686=back
643 687
644=head2 BER TYPES 688=head2 BER Types
645 689
646This lists the predefined BER types - you can map any C<CLASS>/C<TAG> 690This lists the predefined BER types. BER types are formatters used
647combination to any C<BER_TYPE_*>. 691internally to format and encode BER values. You can assign any C<BER_TYPE>
692to any C<CLASS>/C<TAG> combination tgo change how that tag is decoded or
693encoded.
648 694
649=over 695=over
650 696
651=item C<BER_TYPE_BYTES> 697=item C<BER_TYPE_BYTES>
652 698
681dot, e.g. C<1.3.6.1.213>. 727dot, e.g. C<1.3.6.1.213>.
682 728
683=item C<BER_TYPE_RELOID> 729=item C<BER_TYPE_RELOID>
684 730
685Same as C<BER_TYPE_OID> but uses relative object identifier 731Same as C<BER_TYPE_OID> but uses relative object identifier
686encoding: ASN.1 has this hack of encoding the first two OID components 732encoding: ASN.1 uses some hack encoding of the first two OID components
687into a single integer in a weird attempt to save an insignificant amount 733into a single integer in a weird attempt to save an insignificant amount
688of space in an otherwise wasteful encoding, and relative OIDs are 734of space in an otherwise wasteful encoding, and relative OIDs are
689basically OIDs without this hack. The practical difference is that the 735basically OIDs without this hack. The practical difference is that the
690second component of an OID can only have the values 1..40, while relative 736second component of an OID can only have the values 1..40, while relative
691OIDs do not have this restriction. 737OIDs do not have this restriction.
729 775
730 $SNMP_PROFILE->set (ASN_APPLICATION, SNMP_IPADDRESS , BER_TYPE_IPADDRESS); 776 $SNMP_PROFILE->set (ASN_APPLICATION, SNMP_IPADDRESS , BER_TYPE_IPADDRESS);
731 $SNMP_PROFILE->set (ASN_APPLICATION, SNMP_COUNTER32 , BER_TYPE_INT); 777 $SNMP_PROFILE->set (ASN_APPLICATION, SNMP_COUNTER32 , BER_TYPE_INT);
732 $SNMP_PROFILE->set (ASN_APPLICATION, SNMP_UNSIGNED32, BER_TYPE_INT); 778 $SNMP_PROFILE->set (ASN_APPLICATION, SNMP_UNSIGNED32, BER_TYPE_INT);
733 $SNMP_PROFILE->set (ASN_APPLICATION, SNMP_TIMETICKS , BER_TYPE_INT); 779 $SNMP_PROFILE->set (ASN_APPLICATION, SNMP_TIMETICKS , BER_TYPE_INT);
734 $SNMP_PROFILE->set (ASN_APPLICATION, SNMP_OPAQUE , BER_TYPE_IPADDRESS); 780 $SNMP_PROFILE->set (ASN_APPLICATION, SNMP_OPAQUE , BER_TYPE_BYTES);
735 $SNMP_PROFILE->set (ASN_APPLICATION, SNMP_COUNTER64 , BER_TYPE_INT); 781 $SNMP_PROFILE->set (ASN_APPLICATION, SNMP_COUNTER64 , BER_TYPE_INT);
736 782
737=head2 LIMITATIONS/NOTES 783=head2 LIMITATIONS/NOTES
738 784
739This module can only en-/decode 64 bit signed and unsigned integers, and 785This module can only en-/decode 64 bit signed and unsigned
740only when your perl supports those. 786integers/tags/lengths, and only when your perl supports those. So no UUID
787OIDs for now (unless you map the C<OBJECT IDENTIFIER> tag to something
788other than C<BER_TYPE_OID>).
741 789
742This module does not generally care about ranges, i.e. it will happily 790This module does not generally care about ranges, i.e. it will happily
743de-/encode 64 bit integers into an C<ASN_INTEGER> value, or a negative 791de-/encode 64 bit integers into an C<SNMP_UNSIGNED32> value, or a negative
744number into an C<SNMP_COUNTER64>. 792number into an C<SNMP_COUNTER64>.
745 793
746OBJECT IDENTIFIEERs cannot have unlimited length, although the limit is 794OBJECT IDENTIFIEERs cannot have unlimited length, although the limit is
747much larger than e.g. the one imposed by SNMP or other protocols,a nd is 795much larger than e.g. the one imposed by SNMP or other protocols, and is
748about 4kB. 796about 4kB.
749
750Indefinite length encoding is not supported.
751 797
752Constructed strings are decoded just fine, but there should be a way to 798Constructed strings are decoded just fine, but there should be a way to
753join them for convenience. 799join them for convenience.
754 800
755REAL values are not supported and will currently croak. 801REAL values will always be encoded in decimal form and ssometimes is
756 802forced into a perl "NV" type, potentially losing precision.
757The encoder and decoder tend to accept more formats than should be
758strictly supported.
759
760This module has undergone little to no testing so far.
761 803
762=head2 ITHREADS SUPPORT 804=head2 ITHREADS SUPPORT
763 805
764This module is unlikely to work when the (officially discouraged) ithreads 806This module is unlikely to work in any other than the loading thread when
765are in use. 807the (officially discouraged) ithreads are in use.
766 808
767=head1 AUTHOR 809=head1 AUTHOR
768 810
769 Marc Lehmann <schmorp@schmorp.de> 811 Marc Lehmann <schmorp@schmorp.de>
770 http://software.schmorp.de/pkg/Convert-BER-XS 812 http://software.schmorp.de/pkg/Convert-BER-XS

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines