File Coverage

blib/lib/NSNMP.pm
Criterion Covered Total %
statement 169 176 96.0
branch 43 48 89.5
condition 9 12 75.0
subroutine 40 41 97.5
pod 5 5 100.0
total 266 282 94.3


line stmt bran cond sub pod time code
1 3     3   34106 use strict;
  3         6  
  3         170  
2             package NSNMP;
3             # Copyright (c) 2003-2004 AirWave Wireless, Inc.
4              
5             # Redistribution and use in source and binary forms, with or without
6             # modification, are permitted provided that the following conditions
7             # are met:
8              
9             # 1. Redistributions of source code must retain the above
10             # copyright notice, this list of conditions and the following
11             # disclaimer.
12             # 2. Redistributions in binary form must reproduce the above
13             # copyright notice, this list of conditions and the following
14             # disclaimer in the documentation and/or other materials provided
15             # with the distribution.
16             # 3. The name of the author may not be used to endorse or
17             # promote products derived from this software without specific
18             # prior written permission.
19              
20             # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
21             # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22             # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23             # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
24             # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25             # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
26             # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27             # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28             # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29             # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30             # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 3     3   18 use vars qw($error $VERSION);
  3         6  
  3         254  
32             $VERSION = '0.50';
33              
34             =head1 NAME
35              
36             NSNMP - fast, flexible, low-level, pure-Perl SNMP library
37              
38             =head1 SYNOPSIS
39              
40             $bytes = NSNMP->encode(type => $type, request_id => $request_id,
41             varbindlist => [
42             [$ber_encoded_oid, $vtype, $value],
43             ...
44             ],
45             # and optionally:
46             community => $com, error_status => $status,
47             error_index => $index);
48             $decoded = NSNMP->decode($bytes);
49             ($decoded->snmp_version, $decoded->community, $decoded->type,
50             $decoded->request_id, $decoded->error_status,
51             $decoded->error_index, $decoded->varbindlist);
52             $errname = NSNMP->error_description($decoded->error_status);
53             $comprehensible_oid =
54             NSNMP->decode_oid(($decoded->varbindlist)[0]->[0]);
55             $ber_encoded_oid = NSNMP->encode_oid('1.3.6.1.2.1.1.5.0');
56              
57             =head1 DESCRIPTION
58              
59             If you want something well-tested and production-quality, you probably
60             want L; if you just want to get and set some
61             values with SNMP, you probably want L.
62             This module is for you if you want something fast, something suitable
63             for dumping packet contents, or something suitable for writing an SNMP
64             agent.
65              
66             This is an SNMP message encoding and decoding library, providing very
67             low-level facilities; you pretty much need to read the SNMP RFCs to
68             use it. It is, however, very fast (it's more than an order of
69             magnitude faster than Net::SNMP 4.1.2, and it can send a request and
70             parse a response in only slightly more time than the snmpd from
71             net-snmp-5.0.6 takes to parse the request and send a response), and
72             it's relatively complete --- the interface is flexible enough that you
73             can use it to write SNMP management applications, SNMP agents, and
74             test suites for SNMP implementations.
75              
76             It doesn't export anything.
77              
78             =head1 MODULE CONTENTS
79              
80             =head2 Constants
81              
82             This module defines a number of constants for BER and SNMP type tags
83             and error names.
84              
85             =head3 BER and SNMP types
86              
87             These are one-byte strings:
88             INTEGER, OCTET_STRING, NULL, OBJECT_IDENTIFIER, SEQUENCE,
89             IpAddress, Counter32, Gauge32, TimeTicks,
90             GET_REQUEST, GET_NEXT_REQUEST, GET_RESPONSE, SET_REQUEST.
91              
92             =cut
93              
94 3     3   14 use constant INTEGER => "\x02";
  3         9  
  3         320  
95 3     3   15 use constant OCTET_STRING => "\x04";
  3         5  
  3         121  
96 3     3   12 use constant NULL => "\x05";
  3         5  
  3         113  
97 3     3   13 use constant OBJECT_IDENTIFIER => "\x06";
  3         6  
  3         121  
98             # UNIVERSAL, constructed, tag 10000b (16 decimal):
99 3     3   13 use constant SEQUENCE => "\x30";
  3         4  
  3         134  
100 3     3   19 use constant IpAddress => "\x40";
  3         5  
  3         126  
101 3     3   12 use constant Counter32 => "\x41";
  3         4  
  3         127  
102 3     3   27 use constant Gauge32 => "\x42";
  3         5  
  3         121  
103 3     3   13 use constant TimeTicks => "\x43";
  3         4  
  3         123  
104 3     3   12 use constant GET_REQUEST => "\xa0"; # context-specific, constructed, zero tag
  3         6  
  3         145  
105 3     3   13 use constant GET_NEXT_REQUEST => "\xa1";
  3         4  
  3         116  
106 3     3   17 use constant GET_RESPONSE => "\xa2";
  3         5  
  3         154  
107 3     3   13 use constant SET_REQUEST => "\xa3";
  3         5  
  3         7522  
108              
109             =head3 SNMP error names
110              
111             These are small integers: noError, tooBig, noSuchName, badValue,
112             readOnly, genErr.
113              
114             =cut
115              
116             my @error_names = qw(noError tooBig noSuchName badValue readOnly genErr);
117             for my $index (0..$#error_names) {
118             constant->import($error_names[$index] => $index);
119             }
120              
121             =head2 NSNMP->error_description($error_status)
122              
123             Returns one of the strings 'noError', 'noSuchName', etc.
124              
125             =cut
126              
127             sub error_description {
128 13     13 1 104 my ($class, $error_status_number) = @_;
129 13         95 return $error_names[$error_status_number];
130             }
131              
132             # so far I have:
133             # - a debugging dumper for BER-encoded packets (subject to certain limitations)
134             # - an OID encoder that's twice as fast as Net::SNMP's, and knowledge that
135             # hashing is 25 times faster still
136             # - knowledge of a lot of "optimized" ways of sorting lists of OIDs that
137             # aren't faster than the obvious way, but also one way that's 3-16
138             # times as fast (packing the OIDs and memoizing that packing).
139             # - an SNMP PDU decoder that more or less works, at about 6800 PDUs per second
140             # to just get the metadata, or 3900 PDUs per second to get the
141             # contents. This is much faster than Net::SNMP, but it's around
142             # 10%-20% slower than my first attempt, because it correctly handles
143             # more encodings. (I hope it correctly handles everything, but I
144             # don't know.)
145             # - an SNMP PDU encoder that also more or less works and is even
146             # faster than the decoder. It doesn't quite work as well, though.
147             # - some speed. on my 500MHz notebook, a script to get the sysName
148             # 10 000 times takes up 6.7 user seconds, 0.57 system seconds, and
149             # 13.2 wallclock seconds, and the net-snmp snmpd (written in C)
150             # was using 40% of the CPU. (So if we were running on a machine of
151             # our own, we'd be doing 1300 requests per second.) By contrast,
152             # Net::SNMP can fetch localhost's sysName 1000 times in 9.160 user
153             # seconds, 0.050 system seconds, and 10.384 wallclock seconds, or
154             # 109 requests per second. So this SNMP implementation is 12 times
155             # as fast for this simple task. Even when I turned off OID
156             # translation caching, it only used an extra CPU second or so.
157              
158             # performance test results:
159             # [kragen@localhost snmp]$ ./decodetest.pl # now encode is slow too
160             # Benchmark: timing 10000 iterations of justbasics, varbindlist_too...
161             # justbasics: 2 wallclock secs ( 1.31 usr + 0.00 sys = 1.31 CPU) @ 7633.59/s (n=10000)
162             # varbindlist_too: 2 wallclock secs ( 2.43 usr + 0.00 sys = 2.43 CPU) @ 4115.23/s (n=10000)
163             # Benchmark: timing 10000 iterations of berdecode_encode, decode_encode, decode_encode_varbindlist, encode, slow_basicdecodes, unpackseq...
164             # berdecode_encode: 11 wallclock secs (11.20 usr + 0.00 sys = 11.20 CPU) @ 892.86/s (n=10000)
165             # decode_encode: 3 wallclock secs ( 3.00 usr + 0.00 sys = 3.00 CPU) @ 3333.33/s (n=10000)
166             # decode_encode_varbindlist: 4 wallclock secs ( 4.13 usr + 0.00 sys = 4.13 CPU) @ 2421.31/s (n=10000)
167             # encode: 2 wallclock secs ( 1.67 usr + 0.00 sys = 1.67 CPU) @ 5988.02/s (n=10000)
168             # (31 microseconds more. Ouch!)
169             # slow_basicdecodes: 6 wallclock secs ( 6.63 usr + 0.00 sys = 6.63 CPU) @ 1508.30/s (n=10000)
170             # unpackseq: 4 wallclock secs ( 3.83 usr + 0.00 sys = 3.83 CPU) @ 2610.97/s (n=10000)
171              
172              
173             =head2 NSNMP->decode($message)
174              
175             Given the bytes of a message (for example, received on a socket, or
176             returned from C), C returns an C object
177             on which you can call methods to retrieve various fields of the SNMP
178             message.
179              
180             If it can't parse the message, it returns C.
181              
182             See RFC 1157 (or a later SNMP RFC) for the meanings of each of these
183             fields.
184              
185             My 500MHz laptop can run about 1-1.5 million iterations of a Perl loop
186             per second, and it can decode almost 8000 small messages per second
187             with this method. It can decode a little over half as many if you
188             also need varbindlists.
189              
190             The available methods for retrieving message fields follow.
191              
192             =over
193              
194             =cut
195              
196             sub decode {
197 967     967 1 15616 my $class = shift;
198 967         1337 my $rv = eval { NSNMP::Message->new(@_) };
  967         2882  
199 967 100       3342 $error = $@ if $@;
200 967         2722 return $rv;
201             }
202              
203              
204             {
205             package NSNMP::Message;
206              
207             # This package holds decoded SNMP messages (and code for decoding
208             # them). The first couple of routines aren't usually used ---
209             # they're the "slow path". The fast path takes about 150
210             # microseconds to decode a message, excluding varbindlist, on my
211             # 500MHz laptop. The slow path takes 500 microseconds to do the
212             # same.
213              
214             # Given a string beginning with a BER item, split into type, length,
215             # value, and remainder
216             sub BERitem {
217 2341     2341   2557 my ($data) = @_;
218 2341         6671 my ($type, $len, $other) = unpack "aCa*", $data;
219 2341 100       4432 if ($len & 0x80) {
220 1015 100       2065 if ($len == 0x82) { ($len, $other) = unpack "na*", $other }
  389 50       1114  
221 626         1742 elsif ($len == 0x81) { ($len, $other) = unpack "Ca*", $other }
222             else {
223 0         0 (my $rawlen, $other) = unpack "a[$len]a*", $other;
224             # This would have a problem with values over 2^31.
225             # Fortunately, we're in an IP packet.
226 0         0 $len = unpack "N", "\0" x (4 - $len) . $rawlen;
227             }
228             }
229 2341         11288 return $type, $len, unpack "a[$len]a*", $other;
230             }
231              
232             sub unpack_integer {
233 651     651   636 my ($intstr) = @_;
234 651         3447 return unpack "N", "\0" x (4 - length($intstr)) . $intstr;
235             }
236              
237             # general BER sequence type unpacking
238             sub unpack_sequence {
239 225     225   680 my ($sequence) = @_;
240 225         324 my ($type, $len, $contents, $remainder) = BERitem($sequence);
241 225 100       631 return undef, "Unpacking non-sequence" unless ($type & "\x20") ne "\0";
242             # unpack individual items...
243 219         355 return _unpack_sequence_contents($contents);
244             }
245              
246             sub _unpack_sequence_contents {
247 830     830   1099 my ($contents) = @_;
248 830         732 my @rv;
249 830         778 my ($type, $len, $value);
250 830         1358 while ($contents) {
251 2116         2987 ($type, $len, $value, $contents) = BERitem($contents);
252 2116 100       5065 return undef, "Incomplete BER sequence" unless $len == length($value);
253 2115         5046 push @rv, $type, $value;
254             }
255 829         1776 return \@rv, undef;
256             }
257              
258             sub _basicdecodes_slow_but_robust {
259 222     222   522 my ($data) = @_;
260 222         331 my ($sequence, $error) = unpack_sequence($data);
261 222 100       435 die $error if $error;
262 217         410 my (undef, $version, undef, $community, $pdu_type, $pdu) = @$sequence;
263 217         289 ($sequence, $error) = _unpack_sequence_contents($pdu);
264 217 50       733 die $error if $error;
265 217         390 my (undef, $request_id, undef, $error_status,
266             undef, $error_index, undef, $varbindlist_str) = @$sequence;
267 217         356 return (version => unpack_integer($version) + 1, community => $community,
268             pdu_type => $pdu_type, request_id => $request_id,
269             error_status => unpack_integer($error_status),
270             error_index => unpack_integer($error_index),
271             varbindlist_str => $varbindlist_str);
272             }
273              
274             sub _basicdecodes {
275 967     967   1497 my ($data) = @_;
276             my ($packetlength, $verlen, $version, $community, $pdu_type, $pdulen,
277             $request_id, $eslen, $error_status, $eilen, $error_index, $vblen,
278 967         1036 $varbindlist_str) = eval {
279 967         9282 unpack "xC xCc xc/a aC xc/a xCC xCC xCa*", $data;
280             };
281 967 100 66     5618 if (not $@ and not (($packetlength | $verlen | $pdulen | $eslen |
282             $eilen | $vblen) & 0x80)) {
283 745         10190 return (version => $version + 1, community => $community,
284             pdu_type => $pdu_type, request_id => $request_id,
285             error_status => $error_status, error_index => $error_index,
286             varbindlist_str => $varbindlist_str);
287             }
288             # If we're here, it means that we probably have a multibyte length
289             # field on our hands --- either that, or a malformed packet.
290 222         353 return _basicdecodes_slow_but_robust($data);
291             }
292             sub new {
293 967     967   1558 my ($class, $data) = @_;
294 967         2286 return bless { data => $data, _basicdecodes($data) }, $class;
295             }
296              
297             =item ->version
298              
299             Returns the numeric SNMP version: 1, 2, or 3. (Note that 1 is encoded
300             as 0 in the packet, and 2 is encoded as 1, etc., but this method
301             returns the human-readable number, not the weird encoding in the
302             packet.)
303              
304             =cut
305              
306 4     4   26 sub version { $_[0]{version} }
307              
308             =item ->community
309              
310             Returns the community string.
311              
312             =cut
313              
314 28     28   194 sub community { $_[0]{community} }
315              
316             =item ->type
317              
318             Returns the type tag of the PDU, such as NSNMP::GET_REQUEST,
319             NSNMP::GET_RESPONSE, NSNMP::SET_REQUEST, etc. (See L.)
320              
321             =cut
322              
323 240     240   1164 sub type { $_[0]{pdu_type} } # 1-byte string
324              
325             =item ->request_id
326              
327             Returns the bytes representing the request ID in the SNMP message.
328             (This may seem perverse, but often, you don't have to decode them ---
329             you can simply reuse them in a reply packet, or look them up in a hash
330             of outstanding requests. Of course, in the latter case, you might
331             have to decode them anyway, if the agent was perverse and re-encoded
332             them in a different way than you sent them out.)
333              
334             =cut
335              
336 732     732   4705 sub request_id { $_[0]{request_id} } # string, not numeric
337              
338             =item ->error_status, ->error_index
339              
340             Return the numeric error-status and error-index from the SNMP packet.
341             In non-error cases, these will be 0.
342              
343             =cut
344              
345 725     725   2645 sub error_status { $_[0]{error_status} }
346 23     23   180 sub error_index { $_[0]{error_index} }
347             sub _decode_varbindlist {
348 921     921   1234 my ($str) = @_;
349 921         1093 my (@varbinds) = eval {
350             # the unpack issues warnings when failing sometimes
351 921     0   6226 local $SIG{__WARN__} = sub { };
  0         0  
352 921         7975 unpack "(xcxc/aac/a)*", $str;
353             };
354 921 100       2435 return _slow_decode_varbindlist($str) if $@;
355 777         861 my @rv;
356 777         1497 while (@varbinds) {
357 779         1676 my ($length, $oid, $type, $value) = splice @varbinds, 0, 4;
358 779 100       1687 return _slow_decode_varbindlist($str) if $length < 0;
359 726         3182 push @rv, [$oid, $type, $value];
360             }
361 724         5253 return \@rv;
362             }
363              
364             sub _slow_decode_varbindlist {
365 197     197   244 my ($str) = @_;
366 197         278 my ($varbinds, $error) = _unpack_sequence_contents($str);
367 197 50       370 die $error if $error;
368 197         184 my @rv;
369 197         320 while (@$varbinds) {
370 197         354 my (undef, $varbind) = splice @$varbinds, 0, 2;
371 197         357 my ($varbindary, undef) = _unpack_sequence_contents($varbind);
372 197         368 my (undef, $oid, $type, $value) = @$varbindary;
373 197         858 push @rv, [$oid, $type, $value];
374             }
375 197         1396 return \@rv;
376             }
377              
378             =item ->varbindlist
379              
380             Returns a list of C<[$oid, $type, $value]> triples. The type is a BER
381             type, normally equal to NSNMP::OCTET_STRING or one of the other
382             constants for BER types. (See L.) The OIDs are still
383             encoded in BER; you can use C<-Edecode_oid> to get human-readable
384             versions, as documented below.
385              
386             =back
387              
388             =cut
389              
390             sub varbindlist {
391 1143   66 1143   1659 @{$_[0]{varbindlist} ||= _decode_varbindlist($_[0]{varbindlist_str})}
  1143         4980  
392             }
393             }
394              
395             sub _encode_oid {
396 2145     2145   4712 my ($oid) = @_;
397 2145 100       8999 if ($oid =~ s/^1\.3\./43./) {
398 2141         12956 return pack 'w*', split /\./, $oid;
399             } else { # XXX need a test for this
400 4         22 my ($stupidity, $more_stupidity, @chunks) = split /\./, $oid;
401 4         44 return pack 'w*', $stupidity * 40 + $more_stupidity, @chunks;
402             }
403             }
404              
405             sub _decode_oid { # XXX need a test for this
406 96     96   123 my ($encoded) = @_;
407 96 50       470 if ($encoded =~ s/\A\x2b/\001\003/) {
408 96         626 return join '.', unpack 'w*', $encoded;
409             } else {
410 0         0 my ($stupidity, @chunks) = unpack 'w*', $encoded;
411 0         0 return join '.', int($stupidity/40), $stupidity % 40, @chunks;
412             }
413             }
414              
415             {
416             my %encode_oids;
417             my %decode_oids;
418              
419             =head2 NSNMP->encode_oid($oid)
420              
421             This method produces the BER-encoded version of the ASCII-represented
422             OID C<$oid>, which must be a sequence of decimal numbers separated by
423             periods. Leading periods are allowed.
424              
425             =cut
426              
427             sub encode_oid {
428 2476     2476 1 12701 my ($class, $oid) = @_;
429 2476 100       5049 if (keys %encode_oids > 1000) {
430 2         1034 %encode_oids = ();
431 2         717 %decode_oids = ();
432             }
433 2476 100       4417 return $encode_oids{$oid} if exists $encode_oids{$oid};
434 2474         9189 $oid =~ s/\A\.//;
435 2474 100       8583 return $encode_oids{$oid} if exists $encode_oids{$oid};
436 2145         8010 my $encoded = _encode_oid($oid);
437 2145         15414 $encode_oids{$oid} = $encoded;
438 2145         4613 $decode_oids{$encoded} = $oid;
439 2145         10763 return $encoded;
440             }
441              
442             =head2 NSNMP->decode_oid($bytestring)
443              
444             Given the BER encoding of an OID in C<$bytestring>, this method
445             produces the OID's ASCII representation, as a sequence of decimal
446             numbers separated by periods, without a leading period.
447              
448             =cut
449              
450             sub decode_oid {
451 649     649 1 1963 my ($class, $encoded) = @_;
452 649 50       1583 if (keys %encode_oids > 1000) {
453 0         0 %encode_oids = ();
454 0         0 %decode_oids = ();
455             }
456 649 100       2959 return $decode_oids{$encoded} if exists $decode_oids{$encoded};
457 96         181 my $oid = _decode_oid($encoded);
458 96         432 $encode_oids{$oid} = $encoded;
459 96         189 $decode_oids{$encoded} = $oid;
460 96         244 return $oid;
461             }
462             }
463              
464             {
465             sub _encode_length {
466 1238 100   1238   2238 if ($_[0] < 128) { return pack "c", $_[0] }
  231         639  
467 1007 100       1713 if ($_[0] < 256) { return "\201" . pack "C", $_[0] }
  625         2308  
468 382         1487 return "\202" . pack "n", $_[0];
469             }
470              
471             sub _encode_varbind {
472 965     965   973 my ($oid, $type, $value) = @{$_[0]};
  965         1887  
473             # 127 is max length to encode in 1 byte
474             # OID plus value + 2 length bytes + 2 tag bytes must <= 127
475             # to use short form
476 965 100       3063 if (length($oid) + length($value) < 123) {
477 770         6493 return pack "ac/a*", SEQUENCE,
478 770         938 pack "ac/a* ac/a*", OBJECT_IDENTIFIER, @{$_[0]};
479             } else {
480 195         323 my $oidlength = _encode_length(length($oid));
481 195         336 my $valuelength = _encode_length(length($value));
482 195         422 return join('', SEQUENCE, _encode_length(length($oid) + length($value)
483             + length($oidlength)
484             + length($valuelength) + 2),
485             OBJECT_IDENTIFIER, $oidlength, $oid,
486             $type, $valuelength, $value);
487             }
488             }
489              
490              
491             =head2 NSNMP->encode(%args)
492              
493             Returns a string containing an encoded SNMP message, according to the
494             args specified. Available args correspond one for one to the
495             C methods defined above under C; they include
496             the following:
497              
498             =over 4
499              
500             =item request_id => $req_id_str
501              
502             Request ID as a string (not an integer). Mandatory.
503              
504             =item varbindlist =E C<[[$oid, $type, $value], [$oid, $type, $value]...]>
505              
506             Varbindlist as an ARRAY ref containing (oid, type, value) tuples,
507             represented also as ARRAY refs. OIDs, types, and values are assumed
508             to already be BER-encoded. You can sensibly pass the results of the
509             C<-Evarbindlist> method from a decoded message in here, just wrap
510             it in an ARRAY ref: C [$msg-Evarbindlist]>.
511             Mandatory.
512              
513             =item type => $type
514              
515             PDU type --- normally NSNMP::GET_REQUEST, NSNMP::GET_RESPONSE,
516             etc. (See L.) Mandatory.
517              
518             =item community => $community
519              
520             Community string. Default is C.
521              
522             =item error_status => $error
523              
524             =item error_index => $index
525              
526             Error-status and error-index, as integers. Only meaningful on
527             response messages. Default 0.
528              
529             =item version => $ver
530              
531             Human-readable version of SNMP: 1, 2, or 3, default 1. Presently 2
532             and 3 have features this library doesn't support.
533              
534             =back
535              
536             =cut
537              
538             my $onebyteint = INTEGER . pack "c", 1;
539             sub encode {
540 964     964 1 6335 my ($class, %args) = @_;
541 964         1370 my $community = $args{community};
542 964 100       2181 $community = 'public' if not defined $community;
543 965         1914 my $encoded_varbinds = join '',
544 964         984 map { _encode_varbind $_ } @{$args{varbindlist}};
  964         2039  
545 964   100     13382 my $pdu_start = pack 'ac/a* a*C a*C', # XXX give error on long req IDs
      100        
      50        
546             INTEGER, $args{request_id},
547             $onebyteint, $args{error_status} || 0,
548             $onebyteint, $args{error_index} || 0,
549             my $message_start = pack 'aCC ac/a* a',
550             INTEGER, 1, ($args{version} || 1) - 1,
551             OCTET_STRING, $community, # XXX cope with long community strings
552             $args{type};
553 964 100       3357 if (length($encoded_varbinds) + length($pdu_start) + length($message_start)
554             < 123) { # 127 max - TL - L - TL = 122
555             # for a small GetRequestPDU with two varbinds, this path is 25
556             # microseconds shorter.
557 750         6670 return pack 'ac/a*', SEQUENCE, (pack 'a* c/a*', $message_start,
558             pack 'a* ac/a*', $pdu_start, SEQUENCE, $encoded_varbinds);
559             } else {
560 214         408 my $pdu_contents = join('', $pdu_start, SEQUENCE,
561             _encode_length(length($encoded_varbinds)), $encoded_varbinds);
562 214         417 my $message_contents = join('', $message_start,
563             _encode_length(length($pdu_contents)), $pdu_contents);
564 214         408 return join('', SEQUENCE, _encode_length(length($message_contents)),
565             $message_contents);
566             }
567             }
568             }
569              
570              
571             =head1 EXAMPLES
572              
573             Example usage of the main entry points, C, C,
574             C, and C, follows:
575              
576             my $bytes = NSNMP->encode(
577             type => NSNMP::GET_REQUEST,
578             request_id => (pack "N", 38202),
579             varbindlist => [
580             [NSNMP->encode_oid('.1.3.6.1.2.1.1.5.0'), NSNMP::NULL, ''],
581             ],
582             );
583             $socket->send($bytes);
584             my $decoded = NSNMP->decode($bytes);
585             # prints "111111\n"
586             print(
587             ($decoded->version==1),
588             ($decoded->community eq 'public'),
589             ($decoded->type eq NSNMP::GET_REQUEST),
590             ($decoded->request_id eq pack "N", 38202),
591             ($decoded->error_status == 0),
592             ($decoded->error_index == 0), "\n"
593             );
594             my @varbinds = $decoded->varbindlist;
595             # prints "111\n"
596             print(
597             (NSNMP->decode_oid($varbinds[0][0]) eq '1.3.6.1.2.1.1.5.0'),
598             ($varbinds[0][1] eq NSNMP::NULL),
599             ($varbinds[0][2] eq ''),
600             "\n",
601             );
602              
603             =head1 FILES
604              
605             None.
606              
607             =head1 AUTHOR
608              
609             Kragen Sitaker Ekragen@pobox.comE
610              
611             =head1 BUGS
612              
613             This documentation does not adequately express the stupidity and
614             rottenness of the SNMP protocol design.
615              
616             The ASN.1 BER, in which SNMP packets are encoded, allow the sender
617             lots of latitude in deciding how to encode things. This module
618             doesn't have to deal with that very often, but it does have to deal
619             with the version, error-status, and error-index fields of SNMP
620             messages, which are generally encoded in a single byte each. If the
621             sender of an SNMP packet encodes them in multiple bytes instead, this
622             module will fail to decode them, or worse, produce nonsense output.
623             It should instead handle these packets correctly.
624              
625             Malformed VarBindLists can cause the C<-Evarbindlist> method to
626             C with an unhelpful error message. It should instead return a
627             helpful error indication of some kind.
628              
629             It doesn't do much yet; in particular, it doesn't do SNMPv1 traps or
630             anything from SNMPv2 or v3.
631              
632             It doesn't even consider doing any of the following: decoding BER
633             values found in varbind values, understanding MIBs, or anything that
634             involves sending or receiving packets. These jobs belong to other
635             modules, most of which haven't been written yet.
636              
637             =cut
638              
639             1;