ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Convert-BER-XS/XS.pm
Revision: 1.4
Committed: Fri Apr 19 19:46:29 2019 UTC (5 years, 1 month ago) by root
Branch: MAIN
Changes since 1.3: +101 -9 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 =head1 NAME
2
3 Convert::BER::XS - I<very> low level BER en-/decoding
4
5 =head1 SYNOPSIS
6
7 use Convert::BER::XS ':all';
8
9 my $ber = ber_decode $buf
10 or die "unable to decode SNMP v1/v2c Message";
11
12 # the above results in a data structure consisting of (class, tag,
13 # constructed, data) tuples. here is such a message, SNMPv1 trap
14 # with a cisoc mac change notification
15
16 [ ASN_UNIVERSAL, ASN_SEQUENCE, 1,
17 [
18 [ ASN_UNIVERSAL, ASN_INTEGER32, 0, 0 ], # snmp version 1
19 [ ASN_UNIVERSAL, 4, 0, "public" ], # community
20 [ ASN_CONTEXT, 4, 1, # CHOICE, constructed
21 [
22 [ ASN_UNIVERSAL, ASN_OBJECT_IDENTIFIER, 0, "1.3.6.1.4.1.9.9.215.2" ], # enterprise oid
23 [ ASN_APPLICATION, 0, 0, "\x0a\x00\x00\x01" ], # SNMP IpAddress, 10.0.0.1
24 [ ASN_UNIVERSAL, ASN_INTEGER32, 0, 6 ], # generic trap
25 [ ASN_UNIVERSAL, ASN_INTEGER32, 0, 1 ], # specific trap
26 [ ASN_APPLICATION, ASN_TIMETICKS, 0, 1817903850 ], # SNMP TimeTicks
27 [ ASN_UNIVERSAL, ASN_SEQUENCE, 1, # the varbindlist
28 [
29 [ ASN_UNIVERSAL, ASN_SEQUENCE, 1, # a single varbind, "key value" pair
30 [
31 [ ASN_UNIVERSAL, ASN_OBJECT_IDENTIFIER, 0, "1.3.6.1.4.1.9.9.215.1.1.8.1.2.1" ], # the oid
32 [ ASN_UNIVERSAL, ASN_OCTET_STRING, 0, "...data..." # the value
33 ]
34 ]
35 ],
36 ...
37
38 # let's decode it a bit with some helper functions
39
40 my $msg = ber_is_seq $ber
41 or die "SNMP message does not start with a sequence";
42
43 ber_is $msg->[0], ASN_UNIVERSAL, ASN_INTEGER32, 0
44 or die "SNMP message does not start with snmp version\n";
45
46 # message is SNMP v1 or v2c?
47 if ($msg->[0][BER_DATA] == 0 || $msg->[0][BER_DATA] == 1) {
48
49 # message is v1 trap?
50 if (ber_is $msg->[2], ASN_CONTEXT, 4, 1) {
51 my $trap = $msg->[2][BER_DATA];
52
53 # check whether trap is a cisco mac notification mac changed message
54 if (
55 (ber_is_oid $trap->[0], "1.3.6.1.4.1.9.9.215.2") # cmnInterfaceObjects
56 and (ber_is_i32 $trap->[2], 6)
57 and (ber_is_i32 $trap->[3], 1) # mac changed msg
58 ) {
59 ... and so on
60
61 # finally, let's encode it again and hope it results in the same bit pattern
62
63 my $buf = ber_encode $ber;
64
65 =head1 DESCRIPTION
66
67 This module implements a I<very> low level BER/DER en-/decoder.
68
69 If is tuned for low memory and high speed, while still maintaining some
70 level of user-friendlyness.
71
72 Currently, not much is documented, as this is an initial release to
73 reserve CPAN namespace, stay tuned for a few days.
74
75 =head2 ASN.1/BER/DER/... BASICS
76
77 ASN.1 is a strange language that can be sed to describe protocols and
78 data structures. It supports various mappings to JSON, XML, but most
79 importantly, to a various binary encodings such as BER, that is the topic
80 of this module, and is used in SNMP or LDAP for example.
81
82 While ASN.1 defines a schema that is useful to interpret encoded data,
83 the BER encoding is actually somehat self-describing: you might not know
84 whether something is a string or a number or a sequence or something else,
85 but you can nevertheless decode the overall structure, even if you end up
86 with just a binary blob for the actual value.
87
88 This works because BER values are tagged with a type and a namespace,
89 and also have a flag that says whther a value consists of subvalues (is
90 "constructed") or not (is "primitive").
91
92 Tags are simple integers, and ASN.1 defines a somewhat weird assortment of
93 those - for example, you have 32 bit signed integers and 16(!) different
94 string types, but there is no unsigned32 type for example. Different
95 applications work around this in different ways, for example, SNMP defines
96 application-specific Gauge32, Counter32 and Unsigned32, which are mapped
97 to two different tags: you can distinguish between Counter32 and the
98 others, but not between Gause32 and Unsigned32, without the ASN.1 schema.
99
100 Ugh.
101
102 =head2 DECODED BER REPRESENTATION
103
104 This module represents every BER value as a 4-element tuple (actually an
105 array-reference):
106
107 [CLASS, TAG, CONSTRUCTED, DATA]
108
109 I<CLASS> is something like a namespace for I<TAG>s - there is the
110 C<ASN_UNIVERSAL> namespace which defines tags common to all ASN.1
111 implementations, the C<ASN_APPLICATION> namespace which defines tags for
112 specific applications (for example, the SNMP C<Unsigned32> type is in this
113 namespace), a special-purpose context namespace (C<ASN_CONTEXT>, used e.g.
114 for C<CHOICE>) and a private namespace (C<ASN_PRIVATE>).
115
116 The meaning of the I<TAG> depends on the namespace, and defines a
117 (partial) interpretation of the data value. For example, right now, SNMP
118 application namespace knowledge ix hardcoded into this module, so it
119 knows that SNMP C<Unsigned32> values need to be decoded into actual perl
120 integers.
121
122 The most common tags in the C<ASN_UNIVERSAL> namespace are
123 C<ASN_INTEGER32>, C<ASN_BIT_STRING>, C<ASN_NULL>, C<ASN_OCTET_STRING>,
124 C<ASN_OBJECT_IDENTIFIER>, C<ASN_SEQUENCE>, C<ASN_SET> and
125 C<ASN_IA5_STRING>.
126
127 The most common tags in SNMP's C<ASN_APPLICATION> namespace
128 are C<SNMP_IPADDRESS>, C<SNMP_COUNTER32>, C<SNMP_UNSIGNED32>,
129 C<SNMP_TIMETICKS>, C<SNMP_OPAQUE> and C<SNMP_COUNTER64>.
130
131 The I<CONSTRUCTED> flag is really just a boolean - if it is false, the
132 the value is "primitive" and contains no subvalues, kind of like a
133 non-reference perl scalar. IF it is true, then the value is "constructed"
134 which just means it contains a list of subvalues which this module will
135 en-/decode as BER tuples themselves.
136
137 The I<DATA> value is either a reference to an array of further tuples (if
138 the value is I<CONSTRUCTED>), some decoded representation of the value,
139 if this module knows how to decode it (e.g. for the integer types above)
140 or a binary string with the raw octets if this module doesn't know how to
141 interpret the namespace/tag.
142
143 Thus, you can always decode a BER data structure and at worst you get a
144 string in place of some nice decoded value.
145
146 See the SYNOPSIS for an example of such an encoded tuple representation.
147
148 =head2 RELATIONSHIP TO L<Convert::BER> and L<Convert::ASN1>
149
150 This module is I<not> the XS version of L<Convert::BER>, but a different
151 take at doing the same thing. I imagine this module would be a good base
152 for speeding up either of these, or write a similar module, or write your
153 own LDAP or SNMP module for example.
154
155 =cut
156
157 package Convert::BER::XS;
158
159 use common::sense;
160
161 use XSLoader ();
162 use Exporter qw(import);
163
164 our $VERSION = '0.0';
165
166 XSLoader::load __PACKAGE__, $VERSION;
167
168 our %EXPORT_TAGS = (
169 const => [qw(
170 BER_CLASS BER_TAG BER_CONSTRUCTED BER_DATA
171
172 ASN_BOOLEAN ASN_INTEGER32 ASN_BIT_STRING ASN_OCTET_STRING ASN_NULL ASN_OBJECT_IDENTIFIER ASN_TAG_BER ASN_TAG_MASK
173 ASN_CONSTRUCTED ASN_UNIVERSAL ASN_APPLICATION ASN_CONTEXT ASN_PRIVATE ASN_CLASS_MASK ASN_CLASS_SHIFT
174 ASN_SEQUENCE
175
176 SNMP_IPADDRESS SNMP_COUNTER32 SNMP_UNSIGNED32 SNMP_TIMETICKS SNMP_OPAQUE SNMP_COUNTER64
177 )],
178 encode => [qw(
179 ber_decode
180 ber_is ber_is_seq ber_is_i32 ber_is_oid
181 )],
182 decode => [qw(
183 ber_encode
184 )],
185 );
186
187 our @EXPORT_OK = map @$_, values %EXPORT_TAGS;
188
189 $EXPORT_TAGS{all} = \@EXPORT_OK;
190
191 1;
192
193 =head2 BUGS / SHORTCOMINGs
194
195 This module does have a number of SNMPisms hardcoded, such as the SNMP
196 tags for Unsigned32 and so on. More configurability is needed, and, if
197 ever implemented, will come in a form similar to how L<JSON::XS> and
198 L<CBOR::XS> respresent things, namely with an object-oriented interface.
199
200 =head1 AUTHOR
201
202 Marc Lehmann <schmorp@schmorp.de>
203 http://software.schmorp.de/pkg/Convert-BER-XS
204
205 =cut
206