Convert-BER-XS

 view release on metacpan or  search on metacpan

XS.pm  view on Meta::CPAN

          and (ber_is_int $trap->[2], 6)
          and (ber_is_int $trap->[3], 1) # mac changed msg
       ) {
          ... and so on

 # finally, let's encode it again and hope it results in the same bit pattern

 my $buf = ber_encode $ber, $Convert::BER::XS::SNMP_PROFILE;

=head1 DESCRIPTION

WARNING: Before release 1.0, the API is not considered stable in any way.

This module implements a I<very> low level BER/DER en-/decoder.

It is tuned for low memory and high speed, while still maintaining some
level of user-friendlyness.

=head2 EXPORT TAGS AND CONSTANTS

By default this module doesn't export any symbols, but if you don't want
to break your keyboard, editor or eyesight with extremely long names, I
recommend importing the C<:all> tag. Still, you can selectively import
things.

=over

=item C<:all>

All of the below. Really. Recommended for at least first steps, or if you
don't care about a few kilobytes of wasted memory (and namespace).

=item C<:const>

All of the strictly ASN.1-related constants defined by this module, the
same as C<:const_asn :const_index>. Notably, this does not contain
C<:const_ber_type> and C<:const_snmp>.

A good set to get everything you need to decode and match BER data would be
C<:decode :const>.

=item C<:const_index>

The BER tuple array index constants:

        BER_CLASS BER_TAG BER_FLAGS BER_DATA

=item C<:const_asn>

ASN class values (these are C<0>, C<1>, C<2> and C<3>, respectively -
exactly the two topmost bits from the identifier octet shifted 6 bits to
the right):

      ASN_UNIVERSAL ASN_APPLICATION ASN_CONTEXT ASN_PRIVATE

ASN tag values (some of which are aliases, such as C<ASN_OID>). Their
numerical value corresponds exactly to the numbers used in BER/X.690.

      ASN_BOOLEAN ASN_INTEGER ASN_BIT_STRING ASN_OCTET_STRING ASN_NULL ASN_OID
      ASN_OBJECT_IDENTIFIER ASN_OBJECT_DESCRIPTOR ASN_EXTERNAL ASN_REAL ASN_SEQUENCE ASN_ENUMERATED
      ASN_EMBEDDED_PDV ASN_UTF8_STRING ASN_RELATIVE_OID ASN_SET ASN_NUMERIC_STRING
      ASN_PRINTABLE_STRING ASN_TELETEX_STRING ASN_T61_STRING ASN_VIDEOTEX_STRING ASN_IA5_STRING
      ASN_ASCII_STRING ASN_UTC_TIME ASN_GENERALIZED_TIME ASN_GRAPHIC_STRING ASN_VISIBLE_STRING
      ASN_ISO646_STRING ASN_GENERAL_STRING ASN_UNIVERSAL_STRING ASN_CHARACTER_STRING ASN_BMP_STRING

=item C<:const_ber_type>

The BER type constants, explained in the PROFILES section.

      BER_TYPE_BYTES BER_TYPE_UTF8 BER_TYPE_UCS2 BER_TYPE_UCS4 BER_TYPE_INT
      BER_TYPE_OID BER_TYPE_RELOID BER_TYPE_NULL BER_TYPE_BOOL BER_TYPE_REAL
      BER_TYPE_IPADDRESS BER_TYPE_CROAK

=item C<:const_snmp>

Constants only relevant to SNMP. These are the tag values used by SNMP in
the C<ASN_APPLICATION> namespace and have the exact numerical value as in
BER/RFC 2578.

      SNMP_IPADDRESS SNMP_COUNTER32 SNMP_UNSIGNED32 SNMP_GAUGE32
      SNMP_TIMETICKS SNMP_OPAQUE SNMP_COUNTER64

=item C<:decode>

C<ber_decode> and the match helper functions:

      ber_decode ber-decode_prefix
      ber_is ber_is_seq ber_is_int ber_is_oid
      ber_dump

=item C<:encode>

C<ber_encode> and the construction helper functions:

      ber_encode
      ber_int

=back

=head2 ASN.1/BER/DER/... BASICS

ASN.1 is a strange language that can be used to describe protocols and
data structures. It supports various mappings to JSON, XML, but most
importantly, to a various binary encodings such as BER, that is the topic
of this module, and is used in SNMP, LDAP or X.509 for example.

While ASN.1 defines a schema that is useful to interpret encoded data,
the BER encoding is actually somewhat self-describing: you might not know
whether something is a string or a number or a sequence or something else,
but you can nevertheless decode the overall structure, even if you end up
with just a binary blob for the actual value.

This works because BER values are tagged with a type and a namespace,
and also have a flag that says whether a value consists of subvalues (is
"constructed") or not (is "primitive").

Tags are simple integers, and ASN.1 defines a somewhat weird assortment
of those - for example, you have one integers and 16(!) different
string types, but there is no Unsigned32 type for example. Different
applications work around this in different ways, for example, SNMP defines
application-specific Gauge32, Counter32 and Unsigned32, which are mapped

XS.pm  view on Meta::CPAN

=item $bool = ber_is_oid $tuple, $oid_string

Returns true if the C<$tuple> represents an ASN_OBJECT_IDENTIFIER
that exactly matches C<$oid_string>. Example:

   ber_is_oid $tuple, "1.3.6.1.4"
      or die "oid must be 1.3.6.1.4";

=item $oid = ber_is_oid $tuple

Returns true (and extracts the OID string) if the C<$tuple> is an ASN
OBJECT IDENTIFIER. Otherwise, it returns C<undef>.

=back

=head3 CONSTRUCTION HELPERS

=over

=item $tuple = ber_int $value

Constructs a new C<ASN_INTEGER> tuple.

=back

=head2 RELATIONSHIP TO L<Convert::BER> and L<Convert::ASN1>

This module is I<not> the XS version of L<Convert::BER>, but a different
take at doing the same thing. I imagine this module would be a good base
for speeding up either of these, or write a similar module, or write your
own LDAP or SNMP module for example.

=cut

package Convert::BER::XS;

use common::sense;

use XSLoader ();
use Exporter qw(import);

use Carp ();

our $VERSION;

BEGIN {
   $VERSION = 1.21;
   XSLoader::load __PACKAGE__, $VERSION;
}

our %EXPORT_TAGS = (
   const_index => [qw(
      BER_CLASS BER_TAG BER_FLAGS BER_DATA
   )],
   const_asn_class => [qw(
      ASN_UNIVERSAL ASN_APPLICATION ASN_CONTEXT ASN_PRIVATE
   )],
   const_asn_tag => [qw(
      ASN_BOOLEAN ASN_INTEGER ASN_BIT_STRING ASN_OCTET_STRING ASN_NULL ASN_OID ASN_OBJECT_IDENTIFIER
      ASN_OBJECT_DESCRIPTOR ASN_EXTERNAL ASN_REAL ASN_SEQUENCE ASN_ENUMERATED
      ASN_EMBEDDED_PDV ASN_UTF8_STRING ASN_RELATIVE_OID ASN_SET ASN_NUMERIC_STRING
      ASN_PRINTABLE_STRING ASN_TELETEX_STRING ASN_T61_STRING ASN_VIDEOTEX_STRING ASN_IA5_STRING
      ASN_ASCII_STRING ASN_UTC_TIME ASN_GENERALIZED_TIME ASN_GRAPHIC_STRING ASN_VISIBLE_STRING
      ASN_ISO646_STRING ASN_GENERAL_STRING ASN_UNIVERSAL_STRING ASN_CHARACTER_STRING ASN_BMP_STRING
   )],
   const_ber_type => [qw(
      BER_TYPE_BYTES BER_TYPE_UTF8 BER_TYPE_UCS2 BER_TYPE_UCS4 BER_TYPE_INT
      BER_TYPE_OID BER_TYPE_RELOID BER_TYPE_NULL BER_TYPE_BOOL BER_TYPE_REAL
      BER_TYPE_IPADDRESS BER_TYPE_CROAK
   )],
   const_snmp => [qw(
      SNMP_IPADDRESS SNMP_COUNTER32 SNMP_GAUGE32 SNMP_UNSIGNED32
      SNMP_TIMETICKS SNMP_OPAQUE SNMP_COUNTER64
   )],
   decode => [qw(
      ber_decode ber_decode_prefix
      ber_is ber_is_seq ber_is_int ber_is_oid
      ber_dump
   )],
   encode => [qw(
      ber_encode
      ber_int
   )],
);

our @EXPORT_OK = map @$_, values %EXPORT_TAGS;

$EXPORT_TAGS{all}       = \@EXPORT_OK;
$EXPORT_TAGS{const_asn} = [map @{ $EXPORT_TAGS{$_} }, qw(const_asn_class const_asn_tag)];
$EXPORT_TAGS{const}     = [map @{ $EXPORT_TAGS{$_} }, qw(const_index const_asn)];

our $DEFAULT_PROFILE = new Convert::BER::XS::Profile;

$DEFAULT_PROFILE->_set_default;

# additional SNMP application types
our $SNMP_PROFILE = new Convert::BER::XS::Profile;

$SNMP_PROFILE->set (ASN_APPLICATION, SNMP_IPADDRESS , BER_TYPE_IPADDRESS);
$SNMP_PROFILE->set (ASN_APPLICATION, SNMP_COUNTER32 , BER_TYPE_INT);
$SNMP_PROFILE->set (ASN_APPLICATION, SNMP_UNSIGNED32, BER_TYPE_INT);
$SNMP_PROFILE->set (ASN_APPLICATION, SNMP_TIMETICKS , BER_TYPE_INT);

# decodes REAL values according to ECMA-63
# this is pretty strict, except it doesn't catch -0.
# I don't have access to ISO 6093 (or BS 6727, or ANSI X.3-42)), so this is all guesswork.
sub _decode_real_decimal {
   my ($format, $val) = @_;

   $val =~ y/,/./; # probably not in ISO-6093

   if ($format == 1) {
      $val =~ /^ \ * [+-]? [0-9]+ \z/x
         or Carp::croak "BER_TYPE_REAL NR1 value not in NR1 format ($val) (X.690 8.5.8)";
   } elsif ($format == 2) {
      $val =~ /^ \ * [+-]? (?: [0-9]+\.[0-9]* | [0-9]*\.[0-9]+ ) \z/x
         or Carp::croak "BER_TYPE_REAL NR2 value not in NR2 format ($val) (X.690 8.5.8)";
   } elsif ($format == 3) {
      $val =~ /^ \ * [+-] (?: [0-9]+\.[0-9]* | [0-9]*\.[0-9]+ ) [eE] [+-]? [0-9]+ \z/x
         or Carp::croak "BER_TYPE_REAL NR3 value not in NR3 format ($val) (X.690 8.5.8)";
   } else {



( run in 0.991 second using v1.01-cache-2.11-cpan-71847e10f99 )