| 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; |