File Coverage

blib/lib/Net/SNMP/Message.pm
Criterion Covered Total %
statement 285 707 40.3
branch 127 446 28.4
condition 26 78 33.3
subroutine 80 171 46.7
pod 0 108 0.0
total 518 1510 34.3


line stmt bran cond sub pod time code
1             # -*- mode: perl -*-
2              
3             # ============================================================================
4              
5             package Net::SNMP::Message;
6              
7             # $Id: Message.pm,v 3.1 2010/09/10 00:01:22 dtown Rel $
8              
9             # Object used to represent a SNMP message.
10              
11             # Copyright (c) 2001-2010 David M. Town
12             # All rights reserved.
13              
14             # This program is free software; you may redistribute it and/or modify it
15             # under the same terms as the Perl 5 programming language system itself.
16              
17             # ============================================================================
18              
19 4     4   19474 use strict;
  4         12  
  4         357  
20 4     4   6399 use bytes;
  4         50  
  4         25  
21              
22 4     4   8067 use Math::BigInt();
  4         131471  
  4         272  
23              
24             ## Version of the Net::SNMP::Message module
25              
26             our $VERSION = v3.0.1;
27              
28             ## Handle importing/exporting of symbols
29              
30 4     4   56 use base qw( Exporter );
  4         12  
  4         57382  
31              
32             our @EXPORT_OK = qw( TRUE FALSE DEBUG_INFO );
33              
34             our %EXPORT_TAGS = (
35             generictrap => [
36             qw( COLD_START WARM_START LINK_DOWN LINK_UP AUTHENTICATION_FAILURE
37             EGP_NEIGHBOR_LOSS ENTERPRISE_SPECIFIC )
38             ],
39             msgFlags => [
40             qw( MSG_FLAGS_NOAUTHNOPRIV MSG_FLAGS_AUTH MSG_FLAGS_PRIV
41             MSG_FLAGS_REPORTABLE MSG_FLAGS_MASK )
42             ],
43             securityLevels => [
44             qw( SECURITY_LEVEL_NOAUTHNOPRIV SECURITY_LEVEL_AUTHNOPRIV
45             SECURITY_LEVEL_AUTHPRIV )
46             ],
47             securityModels => [
48             qw( SECURITY_MODEL_ANY SECURITY_MODEL_SNMPV1 SECURITY_MODEL_SNMPV2C
49             SECURITY_MODEL_USM )
50             ],
51             translate => [
52             qw( TRANSLATE_NONE TRANSLATE_OCTET_STRING TRANSLATE_NULL
53             TRANSLATE_TIMETICKS TRANSLATE_OPAQUE TRANSLATE_NOSUCHOBJECT
54             TRANSLATE_NOSUCHINSTANCE TRANSLATE_ENDOFMIBVIEW TRANSLATE_UNSIGNED
55             TRANSLATE_ALL )
56             ],
57             types => [
58             qw( INTEGER INTEGER32 OCTET_STRING NULL OBJECT_IDENTIFIER SEQUENCE
59             IPADDRESS COUNTER COUNTER32 GAUGE GAUGE32 UNSIGNED32 TIMETICKS
60             OPAQUE COUNTER64 NOSUCHOBJECT NOSUCHINSTANCE ENDOFMIBVIEW
61             GET_REQUEST GET_NEXT_REQUEST GET_RESPONSE SET_REQUEST TRAP
62             GET_BULK_REQUEST INFORM_REQUEST SNMPV2_TRAP REPORT )
63             ],
64             utilities => [ qw( asn1_ticks_to_time asn1_itoa ) ],
65             versions => [ qw( SNMP_VERSION_1 SNMP_VERSION_2C SNMP_VERSION_3 ) ],
66             );
67              
68             Exporter::export_ok_tags(
69             qw( generictrap msgFlags securityLevels securityModels translate types
70             utilities versions )
71             );
72              
73             $EXPORT_TAGS{ALL} = [ @EXPORT_OK ];
74              
75             ## ASN.1 Basic Encoding Rules type definitions
76              
77 29     29 0 690 sub INTEGER { 0x02 } # INTEGER
78 0     0 0 0 sub INTEGER32 { 0x02 } # Integer32 - SNMPv2c
79 27     27 0 328 sub OCTET_STRING { 0x04 } # OCTET STRING
80 12     12 0 42 sub NULL { 0x05 } # NULL
81 17     17 0 73 sub OBJECT_IDENTIFIER { 0x06 } # OBJECT IDENTIFIER
82 25     25 0 125 sub SEQUENCE { 0x30 } # SEQUENCE
83              
84 12     12 0 37 sub IPADDRESS { 0x40 } # IpAddress
85 12     12 0 40 sub COUNTER { 0x41 } # Counter
86 0     0 0 0 sub COUNTER32 { 0x41 } # Counter32 - SNMPv2c
87 12     12 0 37 sub GAUGE { 0x42 } # Gauge
88 0     0 0 0 sub GAUGE32 { 0x42 } # Gauge32 - SNMPv2c
89 0     0 0 0 sub UNSIGNED32 { 0x42 } # Unsigned32 - SNMPv2c
90 12     12 0 32 sub TIMETICKS { 0x43 } # TimeTicks
91 17     17 0 77 sub OPAQUE { 0x44 } # Opaque
92 14     14 0 42 sub COUNTER64 { 0x46 } # Counter64 - SNMPv2c
93              
94 12     12 0 39 sub NOSUCHOBJECT { 0x80 } # noSuchObject - SNMPv2c
95 12     12 0 81 sub NOSUCHINSTANCE { 0x81 } # noSuchInstance - SNMPv2c
96 12     12 0 37 sub ENDOFMIBVIEW { 0x82 } # endOfMibView - SNMPv2c
97              
98 12     12 0 42 sub GET_REQUEST { 0xa0 } # GetRequest-PDU
99 12     12 0 37 sub GET_NEXT_REQUEST { 0xa1 } # GetNextRequest-PDU
100 15     15 0 67 sub GET_RESPONSE { 0xa2 } # GetResponse-PDU
101 14     14 0 50 sub SET_REQUEST { 0xa3 } # SetRequest-PDU
102 15     15 0 69 sub TRAP { 0xa4 } # Trap-PDU
103 12     12 0 36 sub GET_BULK_REQUEST { 0xa5 } # GetBulkRequest-PDU - SNMPv2c
104 12     12 0 38 sub INFORM_REQUEST { 0xa6 } # InformRequest-PDU - SNMPv2c
105 13     13 0 185 sub SNMPV2_TRAP { 0xa7 } # SNMPv2-Trap-PDU - SNMPv2c
106 13     13 0 259 sub REPORT { 0xa8 } # Report-PDU - SNMPv3
107              
108             ## SNMP RFC version definitions
109              
110 15     15 0 114 sub SNMP_VERSION_1 { 0x00 } # RFC 1157 SNMPv1
111 9     9 0 916 sub SNMP_VERSION_2C { 0x01 } # RFC 1901 Community-based SNMPv2
112 5     5 0 25 sub SNMP_VERSION_3 { 0x03 } # RFC 3411 SNMPv3
113              
114             ## RFC 1157 - generic-trap definitions
115              
116 0     0 0 0 sub COLD_START { 0 } # coldStart(0)
117 0     0 0 0 sub WARM_START { 1 } # warmStart(1)
118 0     0 0 0 sub LINK_DOWN { 2 } # linkDown(2)
119 0     0 0 0 sub LINK_UP { 3 } # linkUp(3)
120 0     0 0 0 sub AUTHENTICATION_FAILURE { 4 } # authenticationFailure(4)
121 0     0 0 0 sub EGP_NEIGHBOR_LOSS { 5 } # egpNeighborLoss(5)
122 0     0 0 0 sub ENTERPRISE_SPECIFIC { 6 } # enterpriseSpecific(6)
123              
124             ## RFC 3412 - msgFlags::=OCTET STRING
125              
126 0     0 0 0 sub MSG_FLAGS_NOAUTHNOPRIV { 0x00 } # Means noAuthNoPriv
127 0     0 0 0 sub MSG_FLAGS_AUTH { 0x01 } # authFlag
128 0     0 0 0 sub MSG_FLAGS_PRIV { 0x02 } # privFlag
129 0     0 0 0 sub MSG_FLAGS_REPORTABLE { 0x04 } # reportableFlag
130 0     0 0 0 sub MSG_FLAGS_MASK { 0x07 }
131              
132             ## RFC 3411 - SnmpSecurityLevel::=TEXTUAL-CONVENTION
133              
134 2     2 0 30 sub SECURITY_LEVEL_NOAUTHNOPRIV { 1 } # noAuthNoPriv
135 6     6 0 18 sub SECURITY_LEVEL_AUTHNOPRIV { 2 } # authNoPriv
136 4     4 0 9 sub SECURITY_LEVEL_AUTHPRIV { 3 } # authPriv
137              
138             ## RFC 3411 - SnmpSecurityModel::=TEXTUAL-CONVENTION
139              
140 0     0 0 0 sub SECURITY_MODEL_ANY { 0 } # Reserved for 'any'
141 0     0 0 0 sub SECURITY_MODEL_SNMPV1 { 1 } # Reserved for SNMPv1
142 0     0 0 0 sub SECURITY_MODEL_SNMPV2C { 2 } # Reserved for SNMPv2c
143 0     0 0 0 sub SECURITY_MODEL_USM { 3 } # User-Based Security Model (USM)
144              
145             ## Translation masks
146              
147 5     5 0 19 sub TRANSLATE_NONE { 0x00 } # Bit masks used to determine
148 6     6 0 15 sub TRANSLATE_OCTET_STRING { 0x01 } # if a specific ASN.1 type is
149 0     0 0 0 sub TRANSLATE_NULL { 0x02 } # translated into a "human
150 0     0 0 0 sub TRANSLATE_TIMETICKS { 0x04 } # readable" form.
151 0     0 0 0 sub TRANSLATE_OPAQUE { 0x08 }
152 0     0 0 0 sub TRANSLATE_NOSUCHOBJECT { 0x10 }
153 0     0 0 0 sub TRANSLATE_NOSUCHINSTANCE { 0x20 }
154 0     0 0 0 sub TRANSLATE_ENDOFMIBVIEW { 0x40 }
155 0     0 0 0 sub TRANSLATE_UNSIGNED { 0x80 }
156 0     0 0 0 sub TRANSLATE_ALL { 0xff }
157              
158             ## Truth values
159              
160 60     60 0 311 sub TRUE { 0x01 }
161 40     40 0 221 sub FALSE { 0x00 }
162              
163             ## Package variables
164              
165             our $DEBUG = FALSE; # Debug flag
166              
167             our $AUTOLOAD; # Used by the AUTOLOAD method
168              
169             ## Initialize the request-id/msgID.
170              
171             our $ID = int rand((2**16) - 1) + ($^T & 0xff);
172              
173             # [public methods] -----------------------------------------------------------
174              
175             sub new
176             {
177 5     5 0 114 my ($class, %argv) = @_;
178              
179             # Create a new data structure for the object
180 5         23 my $this = bless {
181             '_buffer' => q{}, # Serialized message buffer
182             '_error' => undef, # Error message
183             '_index' => 0, # Buffer index
184             '_leading_dot' => FALSE, # Prepend leading dot on OIDs
185             '_length' => 0, # Buffer length
186             '_security' => undef, # Security Model object
187             '_translate' => TRANSLATE_NONE, # Translation mode
188             '_transport' => undef, # Transport Layer object
189             '_version' => SNMP_VERSION_1, # SNMP version
190             }, $class;
191              
192             # Validate the passed arguments
193              
194 5         24 for (keys %argv) {
195              
196 8 100       103 if (/^-?callback$/i) {
    50          
    100          
    50          
    100          
    100          
    100          
    100          
    50          
197 1         5 $this->callback($argv{$_});
198             } elsif (/^-?debug$/i) {
199 0         0 $this->debug($argv{$_});
200             } elsif (/^-?leadingdot$/i) {
201 1         3 $this->leading_dot($argv{$_});
202             } elsif (/^-?msgid$/i) {
203 0         0 $this->msg_id($argv{$_});
204             } elsif (/^-?requestid$/i) {
205 1         3 $this->request_id($argv{$_});
206             } elsif (/^-?security$/i) {
207 1         3 $this->security($argv{$_});
208             } elsif (/^-?translate$/i) {
209 1         3 $this->translate($argv{$_});
210             } elsif (/^-?transport$/i) {
211 1         3 $this->transport($argv{$_});
212             } elsif (/^-?version$/i) {
213 2         8 $this->version($argv{$_});
214             } else {
215 0         0 $this->_error('The argument "%s" is unknown', $_);
216             }
217              
218 8 50       21 if (defined $this->{_error}) {
219 0 0       0 return wantarray ? (undef, $this->{_error}) : undef;
220             }
221              
222             }
223              
224 5 100       29 return wantarray ? ($this, q{}) : $this;
225             }
226              
227             {
228             my $prepare_methods = {
229             INTEGER, \&_prepare_integer,
230             OCTET_STRING, \&_prepare_octet_string,
231             NULL, \&_prepare_null,
232             OBJECT_IDENTIFIER, \&_prepare_object_identifier,
233             SEQUENCE, \&_prepare_sequence,
234             IPADDRESS, \&_prepare_ipaddress,
235             COUNTER, \&_prepare_counter,
236             GAUGE, \&_prepare_gauge,
237             TIMETICKS, \&_prepare_timeticks,
238             OPAQUE, \&_prepare_opaque,
239             COUNTER64, \&_prepare_counter64,
240             NOSUCHOBJECT, \&_prepare_nosuchobject,
241             NOSUCHINSTANCE, \&_prepare_nosuchinstance,
242             ENDOFMIBVIEW, \&_prepare_endofmibview,
243             GET_REQUEST, \&_prepare_get_request,
244             GET_NEXT_REQUEST, \&_prepare_get_next_request,
245             GET_RESPONSE, \&_prepare_get_response,
246             SET_REQUEST, \&_prepare_set_request,
247             TRAP, \&_prepare_trap,
248             GET_BULK_REQUEST, \&_prepare_get_bulk_request,
249             INFORM_REQUEST, \&_prepare_inform_request,
250             SNMPV2_TRAP, \&_prepare_v2_trap,
251             REPORT, \&_prepare_report
252             };
253              
254             sub prepare
255             {
256             # my ($this, $type, $value) = @_;
257              
258 21 50   21 0 140 return $_[0]->_error() if defined $_[0]->{_error};
259              
260 21 50       46 if (!defined $_[1]) {
261 0         0 return $_[0]->_error('The ASN.1 type is not defined');
262             }
263              
264 21 50       68 if (!exists $prepare_methods->{$_[1]}) {
265 0         0 return $_[0]->_error('The ASN.1 type "%s" is unknown', $_[1]);
266             }
267              
268 21         37 return $_[0]->${\$prepare_methods->{$_[1]}}($_[2]);
  21         91  
269             }
270             }
271              
272             {
273             my $process_methods = {
274             INTEGER, \&_process_integer32,
275             OCTET_STRING, \&_process_octet_string,
276             NULL, \&_process_null,
277             OBJECT_IDENTIFIER, \&_process_object_identifier,
278             SEQUENCE, \&_process_sequence,
279             IPADDRESS, \&_process_ipaddress,
280             COUNTER, \&_process_counter,
281             GAUGE, \&_process_gauge,
282             TIMETICKS, \&_process_timeticks,
283             OPAQUE, \&_process_opaque,
284             COUNTER64, \&_process_counter64,
285             NOSUCHOBJECT, \&_process_nosuchobject,
286             NOSUCHINSTANCE, \&_process_nosuchinstance,
287             ENDOFMIBVIEW, \&_process_endofmibview,
288             GET_REQUEST, \&_process_get_request,
289             GET_NEXT_REQUEST, \&_process_get_next_request,
290             GET_RESPONSE, \&_process_get_response,
291             SET_REQUEST, \&_process_set_request,
292             TRAP, \&_process_trap,
293             GET_BULK_REQUEST, \&_process_get_bulk_request,
294             INFORM_REQUEST, \&_process_inform_request,
295             SNMPV2_TRAP, \&_process_v2_trap,
296             REPORT, \&_process_report
297             };
298              
299             sub process
300             {
301             # my ($this, $expected, $found) = @_;
302              
303             # XXX: If present, $found is updated as a side effect.
304              
305 17 50   17 0 50 return $_[0]->_error() if defined $_[0]->{_error};
306              
307 17 50       47 return $_[0]->_error() if !defined (my $type = $_[0]->_buffer_get(1));
308              
309 17         42 $type = unpack 'C', $type;
310              
311 17 50       55 if (!exists $process_methods->{$type}) {
312 0         0 return $_[0]->_error('The ASN.1 type 0x%02x is unknown', $type);
313             }
314              
315             # Check to see if a specific ASN.1 type was expected.
316 17 50 66     108 if ((@_ > 1) && (defined $_[1]) && ($type != $_[1])) {
      66        
317 0         0 return $_[0]->_error(
318             'Expected %s, but found %s', asn1_itoa($_[1]), asn1_itoa($type)
319             );
320             }
321              
322             # Update the found ASN.1 type, if the argument is present.
323 17 50       44 if (@_ == 3) {
324 0         0 $_[2] = $type;
325             }
326              
327 17         32 return $_[0]->${\$process_methods->{$type}}($type);
  17         59  
328             }
329             }
330              
331             sub context_engine_id
332             {
333 0     0 0 0 my ($this, $engine_id) = @_;
334              
335             # RFC 3412 - contextEngineID::=OCTET STRING
336              
337 0 0       0 if (@_ == 2) {
338 0 0       0 if (!defined $engine_id) {
339 0         0 return $this->_error('The contextEngineID value is not defined');
340             }
341 0         0 $this->{_context_engine_id} = $engine_id;
342             }
343              
344 0 0       0 if (exists $this->{_context_engine_id}) {
    0          
345 0   0     0 return $this->{_context_engine_id} || q{};
346             } elsif (defined $this->{_security}) {
347 0   0     0 return $this->{_security}->engine_id() || q{};
348             }
349              
350 0         0 return q{};
351             }
352              
353             sub context_name
354             {
355 0     0 0 0 my ($this, $name) = @_;
356              
357             # RFC 3412 - contextName::=OCTET STRING
358              
359 0 0       0 if (@_ == 2) {
360 0 0       0 if (!defined $name) {
361 0         0 return $this->_error('The contextName value is not defined');
362             }
363 0         0 $this->{_context_name} = $name;
364             }
365              
366 0 0       0 return exists($this->{_context_name}) ? $this->{_context_name} : q{};
367             }
368              
369             sub msg_flags
370             {
371 0     0 0 0 my ($this, $flags) = @_;
372              
373             # RFC 3412 - msgFlags::=OCTET STRING (SIZE(1))
374              
375             # NOTE: The stored value is not an OCTET STRING.
376              
377 0 0       0 if (@_ == 2) {
378 0 0       0 if (!defined $flags) {
379 0         0 return $this->_error('The msgFlags value is not defined');
380             }
381 0         0 $this->{_msg_flags} = $flags;
382             }
383              
384 0 0       0 if (exists $this->{_msg_flags}) {
385 0         0 return $this->{_msg_flags};
386             }
387              
388 0         0 return MSG_FLAGS_NOAUTHNOPRIV;
389             }
390              
391             sub msg_id
392             {
393 1     1 0 2 my ($this, $msg_id) = @_;
394              
395             # RFC 3412 - msgID::=INTEGER (0..2147483647)
396              
397 1 50       4 if (@_ == 2) {
398 0 0       0 if (!defined $msg_id) {
399 0         0 return $this->_error('The msgID value is not defined');
400             }
401 0 0 0     0 if (($msg_id < 0) || ($msg_id > 2147483647)) {
402 0         0 return $this->_error(
403             'The msgId %d is out of range (0..2147483647)', $msg_id
404             );
405             }
406 0         0 $this->{_msg_id} = $msg_id;
407             }
408              
409 1 50       6 if (exists $this->{_msg_id}) {
    50          
410 0         0 return $this->{_msg_id};
411             } elsif (exists $this->{_request_id}) {
412 1         4 return $this->{_request_id};
413             }
414              
415 0         0 return 0;
416             }
417              
418             sub msg_max_size
419             {
420 0     0 0 0 my ($this, $size) = @_;
421              
422             # RFC 3412 - msgMaxSize::=INTEGER (484..2147483647)
423              
424 0 0       0 if (@_ == 2) {
425 0 0       0 if (!defined $size) {
426 0         0 return $this->_error('The msgMaxSize value is not defined');
427             }
428 0 0 0     0 if (($size < 484) || ($size > 2147483647)) {
429 0         0 return $this->_error(
430             'The msgMaxSize %d is out of range (484..2147483647)', $size
431             );
432             }
433 0         0 $this->{_msg_max_size} = $size;
434             }
435              
436 0   0     0 return $this->{_msg_max_size} || 484;
437             }
438              
439             sub msg_security_model
440             {
441 0     0 0 0 my ($this, $model) = @_;
442              
443             # RFC 3412 - msgSecurityModel::=INTEGER (1..2147483647)
444              
445 0 0       0 if (@_ == 2) {
446 0 0       0 if (!defined $model) {
447 0         0 return $this->_error('The msgSecurityModel value is not defined');
448             }
449 0 0 0     0 if (($model < 1) || ($model > 2147483647)) {
450 0         0 return $this->_error(
451             'The msgSecurityModel %d is out of range (1..2147483647)', $model
452             );
453             }
454 0         0 $this->{_security_model} = $model;
455             }
456              
457 0 0       0 if (exists $this->{_security_model}) {
    0          
458 0         0 return $this->{_security_model};
459             } elsif (defined $this->{_security}) {
460 0         0 return $this->{_security}->security_model();
461             } else {
462 0 0       0 if ($this->{_version} == SNMP_VERSION_1) {
    0          
    0          
463 0         0 return SECURITY_MODEL_SNMPV1;
464             } elsif ($this->{_version} == SNMP_VERSION_2C) {
465 0         0 return SECURITY_MODEL_SNMPV2C;
466             } elsif ($this->{_version} == SNMP_VERSION_3) {
467 0         0 return SECURITY_MODEL_USM;
468             }
469             }
470              
471 0         0 return SECURITY_MODEL_ANY;
472             }
473              
474             sub request_id
475             {
476 2     2 0 3 my ($this, $request_id) = @_;
477              
478             # request-id::=INTEGER
479              
480 2 100       13 if (@_ == 2) {
481 1 50       3 if (!defined $request_id) {
482 0         0 return $this->_error('The request-id value is not defined');
483             }
484 1         7 $this->{_request_id} = $request_id;
485             }
486              
487 2 50       11 return exists($this->{_request_id}) ? $this->{_request_id} : 0;
488             }
489              
490             sub security_level
491             {
492 0     0 0 0 my ($this, $level) = @_;
493              
494             # RFC 3411 - SnmpSecurityLevel::=INTEGER { noAuthNoPriv(1),
495             # authNoPriv(2),
496             # authPriv(3) }
497              
498 0 0       0 if (@_ == 2) {
499 0 0       0 if (!defined $level) {
500 0         0 return $this->_error('The securityLevel value is not defined');
501             }
502 0 0 0     0 if (($level < SECURITY_LEVEL_NOAUTHNOPRIV) ||
503             ($level > SECURITY_LEVEL_AUTHPRIV))
504             {
505 0         0 return $this->_error(
506             'The securityLevel %d is out of range (%d..%d)', $level,
507             SECURITY_LEVEL_NOAUTHNOPRIV, SECURITY_LEVEL_AUTHPRIV
508             );
509             }
510 0         0 $this->{_security_level} = $level;
511             }
512              
513 0 0       0 if (exists $this->{_security_level}) {
    0          
514 0         0 return $this->{_security_level};
515             } elsif (defined $this->{_security}) {
516 0         0 return $this->{_security}->security_level();
517             }
518              
519 0         0 return SECURITY_LEVEL_NOAUTHNOPRIV;
520             }
521              
522             sub security_name
523             {
524 1     1 0 2 my ($this, $name) = @_;
525              
526 1 50       3 if (@_ == 2) {
527 1 50       2 if (!defined $name) {
528 0         0 return $this->_error('The securityName value is not defined');
529             }
530             # No length checks due to no limits by RFC 1157 for community name.
531 1         3 $this->{_security_name} = $name;
532             }
533              
534 1 50       3 if (exists $this->{_security_name}) {
    0          
535 1         5 return $this->{_security_name};
536             } elsif (defined $this->{_security}) {
537 0         0 return $this->{_security}->security_name();
538             }
539              
540 0         0 return q{};
541             }
542              
543             sub version
544             {
545 8     8 0 13 my ($this, $version) = @_;
546              
547 8 100       19 if (@_ == 2) {
548 4 50 33     11 if (($version == SNMP_VERSION_1) ||
      33        
549             ($version == SNMP_VERSION_2C) ||
550             ($version == SNMP_VERSION_3))
551             {
552 4         15 $this->{_version} = $version;
553             } else {
554 0         0 return $this->_error('The SNMP version %d is not supported', $version);
555             }
556             }
557              
558 8         32 return $this->{_version};
559             }
560              
561             sub error_status
562             {
563 0     0 0 0 return 0; # noError(0)
564             }
565              
566             sub error_index
567             {
568 0     0 0 0 return 0;
569             }
570              
571             sub var_bind_list
572             {
573 0     0 0 0 return undef;
574             }
575              
576             sub var_bind_names
577             {
578 0     0 0 0 return [];
579             }
580              
581             sub var_bind_types
582             {
583 0     0 0 0 return undef;
584             }
585              
586             #
587             # Security Model accessor methods
588             #
589              
590             sub security
591             {
592 5     5 0 7 my ($this, $security) = @_;
593              
594 5 100       24 if (@_ == 2) {
595 2 50       5 if (defined $security) {
596 2         3 $this->{_security} = $security;
597             } else {
598 0         0 $this->_error_clear();
599 0         0 return $this->_error('The Security Model object is not defined');
600             }
601             }
602              
603 5         27 return $this->{_security};
604             }
605              
606             #
607             # Transport Domain accessor methods
608             #
609              
610             sub transport
611             {
612 3     3 0 5 my ($this, $transport) = @_;
613              
614 3 100       7 if (@_ == 2) {
615 2 50       14 if (defined $transport) {
616 2         4 $this->{_transport} = $transport;
617             } else {
618 0         0 $this->_error_clear();
619 0         0 return $this->_error('The Transport Domain object is not defined');
620             }
621             }
622              
623 3         9 return $this->{_transport};
624             }
625              
626             sub hostname
627             {
628 0     0 0 0 my ($this) = @_;
629              
630 0 0       0 if (defined $this->{_transport}) {
631 0         0 return $this->{_transport}->dest_hostname();
632             }
633              
634 0         0 return q{};
635             }
636              
637             sub dstname
638             {
639 0     0 0 0 require Carp;
640 0         0 Carp::croak(
641             sprintf '%s::dstname() is obsolete, use hostname() instead', ref $_[0]
642             );
643              
644             # Never get here.
645 0         0 return shift->hostname(@_);
646             }
647              
648             sub max_msg_size
649             {
650 0     0 0 0 my ($this, $size) = @_;
651              
652 0 0       0 if (!defined $this->{_transport}) {
653 0         0 return 0;
654             }
655              
656 0 0       0 if (@_ == 2) {
657 0         0 $this->_error_clear();
658 0 0       0 if (defined ($size = $this->{_transport}->max_msg_size($size))) {
659 0         0 return $size;
660             }
661 0         0 return $this->_error($this->{_transport}->error());
662             }
663              
664 0         0 return $this->{_transport}->max_msg_size();
665             }
666              
667             sub retries
668             {
669 0 0   0 0 0 return defined($_[0]->{_transport}) ? $_[0]->{_transport}->retries() : 0;
670             }
671              
672             sub timeout
673             {
674 0 0   0 0 0 return defined($_[0]->{_transport}) ? $_[0]->{_transport}->timeout() : 0;
675             }
676              
677             sub send
678             {
679 0     0 0 0 my ($this) = @_;
680              
681 0         0 $this->_error_clear();
682              
683 0 0       0 if (!defined $this->{_transport}) {
684 0         0 return $this->_error('The Transport Domain object is not defined');
685             }
686              
687 0         0 DEBUG_INFO('transport address %s', $this->{_transport}->dest_taddress());
688 0         0 $this->_buffer_dump();
689              
690 0 0       0 if (defined (my $bytes = $this->{_transport}->send($this->{_buffer}))) {
691 0         0 return $bytes;
692             }
693              
694 0         0 return $this->_error($this->{_transport}->error());
695             }
696              
697             sub recv
698             {
699 0     0 0 0 my ($this) = @_;
700              
701 0         0 $this->_error_clear();
702              
703 0 0       0 if (!defined $this->{_transport}) {
704 0         0 return $this->_error('The Transport Domain object is not defined');
705             }
706              
707 0         0 my $name = $this->{_transport}->recv($this->{_buffer});
708              
709 0 0       0 if (defined $name) {
710 0         0 $this->{_length} = CORE::length($this->{_buffer});
711 0         0 DEBUG_INFO('transport address %s', $this->{_transport}->peer_taddress());
712 0         0 $this->_buffer_dump();
713 0         0 return $name;
714             }
715              
716 0         0 return $this->_error($this->{_transport}->error());
717             }
718              
719             #
720             # Data representation methods
721             #
722              
723             sub translate
724             {
725 3 100   3 0 18 return (@_ == 2) ? $_[0]->{_translate} = $_[1] : $_[0]->{_translate};
726             }
727              
728             sub leading_dot
729             {
730 2 100   2 0 20 return (@_ == 2) ? $_[0]->{_leading_dot} = $_[1] : $_[0]->{_leading_dot};
731             }
732              
733             #
734             # Callback handler methods
735             #
736              
737             sub callback
738             {
739 2     2 0 3 my ($this, $callback) = @_;
740              
741 2 100       6 if (@_ == 2) {
742 1 50       5 if (ref($callback) eq 'CODE') {
    50          
743 0         0 $this->{_callback} = $callback;
744             } elsif (!defined $callback) {
745 1         3 $this->{_callback} = undef;
746             } else {
747 0         0 DEBUG_INFO('unexpected callback format');
748             }
749             }
750              
751 2         11 return $this->{_callback};
752             }
753              
754             sub callback_execute
755             {
756 0     0 0 0 my ($this) = @_;
757              
758 0 0       0 if (!defined $this->{_callback}) {
759 0         0 DEBUG_INFO('no callback');
760 0         0 return TRUE;
761             }
762              
763             # Protect ourselves from user error.
764 0         0 eval { $this->{_callback}->($this); };
  0         0  
765              
766             # We clear the callback in case it was a closure which might hold
767             # up the reference count of the calling object.
768              
769 0         0 $this->{_callback} = undef;
770              
771 0 0       0 return ($@) ? $this->_error($@) : TRUE;
772             }
773              
774             sub status_information
775             {
776 0     0 0 0 my $this = shift;
777              
778 0 0       0 if (@_) {
779 0 0       0 $this->{_error} = (@_ > 1) ? sprintf(shift(@_), @_) : $_[0];
780 0 0       0 if ($this->debug()) {
781 0         0 printf "error: [%d] %s(): %s\n",
782             (caller 0)[2], (caller 1)[3], $this->{_error};
783             }
784 0         0 $this->callback_execute();
785             }
786              
787 0   0     0 return $this->{_error} || q{};
788             }
789              
790             sub process_response_pdu
791             {
792 0     0 0 0 goto &callback_execute;
793             }
794              
795             sub timeout_id
796             {
797 0 0   0 0 0 return (@_ == 2) ? $_[0]->{_timeout_id} = $_[1] : $_[0]->{_timeout_id};
798             }
799              
800             #
801             # Buffer manipulation methods
802             #
803              
804             sub index
805             {
806 2     2 0 4 my ($this, $index) = @_;
807              
808 2 50 33     20 if ((@_ == 2) && ($index >= 0) && ($index <= $this->{_length})) {
      33        
809 2         3 $this->{_index} = $index;
810             }
811              
812 2         5 return $this->{_index};
813             }
814              
815             sub length
816             {
817 8     8 0 158 return $_[0]->{_length};
818             }
819              
820             sub prepend
821             {
822 2     2 0 5 goto &_buffer_put;
823             }
824              
825             sub append
826             {
827 3     3 0 16 goto &_buffer_append;
828             }
829              
830             sub copy
831             {
832 1     1 0 4 return $_[0]->{_buffer};
833             }
834              
835             sub reference
836             {
837 6     6 0 53 return \$_[0]->{_buffer};
838             }
839              
840             sub clear
841             {
842 13     13 0 30 my ($this) = @_;
843              
844 13         24 $this->{_index} = 0;
845 13         24 $this->{_length} = 0;
846              
847 13         50 return substr $this->{_buffer}, 0, CORE::length($this->{_buffer}), q{};
848             }
849              
850             sub dump
851             {
852 0     0 0 0 goto &_buffer_dump;
853             }
854              
855             #
856             # Debug/error handling methods
857             #
858              
859             sub error
860             {
861 3     3 0 6 my $this = shift;
862              
863 3 50       25 if (@_) {
864 0 0       0 if (defined $_[0]) {
865 0 0       0 $this->{_error} = (@_ > 1) ? sprintf(shift(@_), @_) : $_[0];
866 0 0       0 if ($this->debug()) {
867 0         0 printf "error: [%d] %s(): %s\n",
868             (caller 0)[2], (caller 1)[3], $this->{_error};
869             }
870             } else {
871 0         0 $this->{_error} = undef;
872             }
873             }
874              
875 3   50     26 return $this->{_error} || q{};
876             }
877              
878             sub debug
879             {
880 0 0   0 0 0 return (@_ == 2) ? $DEBUG = ($_[1]) ? TRUE : FALSE : $DEBUG;
    0          
881             }
882              
883             sub AUTOLOAD
884             {
885 0     0   0 my ($this) = @_;
886              
887 0 0       0 return if $AUTOLOAD =~ /::DESTROY$/;
888              
889 0         0 $AUTOLOAD =~ s/.*://;
890              
891 0 0       0 if (ref $this) {
892 0         0 $this->_error_clear();
893 0         0 return $this->_error('The method "%s" is not supported', $AUTOLOAD);
894             } else {
895 0         0 require Carp;
896 0         0 Carp::croak(sprintf 'The function "%s" is not supported', $AUTOLOAD);
897             }
898              
899             # Never get here.
900 0         0 return;
901             }
902              
903             # [private methods] ----------------------------------------------------------
904              
905             #
906             # Basic Encoding Rules (BER) prepare methods
907             #
908              
909             sub _prepare_type_length
910             {
911             # my ($this, $type, $value) = @_;
912              
913 18 50   18   145 if (!defined $_[1]) {
914 0         0 return $_[0]->_error('The ASN.1 type is not defined');
915             }
916              
917 18         25 my $length = CORE::length($_[2]);
918              
919 18 50       41 if ($length < 0x80) {
    0          
    0          
920 18         95 return $_[0]->_buffer_put(pack('C2', $_[1], $length) . $_[2]);
921             } elsif ($length <= 0xff) {
922 0         0 return $_[0]->_buffer_put(pack('C3', $_[1], 0x81, $length) . $_[2]);
923             } elsif ($length <= 0xffff) {
924 0         0 return $_[0]->_buffer_put(pack('CCn', $_[1], 0x82, $length) . $_[2]);
925             }
926              
927 0         0 return $_[0]->_error('Unable to prepare the ASN.1 length');
928             }
929              
930             sub _prepare_integer
931             {
932 6     6   11 my ($this, $value) = @_;
933              
934 6 50       17 if (!defined $value) {
935 0         0 return $this->_error('The INTEGER value is not defined');
936             }
937              
938 6 50       39 if ($value !~ /^-?\d+$/) {
939 0         0 return $this->_error(
940             'The INTEGER value "%s" is expected in numeric format', $value
941             );
942             }
943              
944 6 50 33     34 if ($value < -2147483648 || $value > 4294967295) {
945 0         0 return $this->_error(
946             'The INTEGER value "%s" is out of range (-2147483648..4294967295)',
947             $value
948             );
949             }
950              
951 6         15 return $this->_prepare_integer32(INTEGER, $value);
952             }
953              
954             sub _prepare_unsigned32
955             {
956 0     0   0 my ($this, $type, $value) = @_;
957              
958 0 0       0 if (!defined $value) {
959 0         0 return $this->_error('The %s value is not defined', asn1_itoa($type));
960             }
961              
962 0 0       0 if ($value !~ /^\d+$/) {
963 0         0 return $this->_error(
964             'The %s value "%s" is expected in positive numeric format',
965             asn1_itoa($type), $value
966             );
967             }
968              
969 0 0 0     0 if ($value < 0 || $value > 4294967295) {
970 0         0 return $this->_error(
971             'The %s value "%s" is out of range (0..4294967295)',
972             asn1_itoa($type), $value
973             );
974             }
975              
976 0         0 return $this->_prepare_integer32($type, $value);
977             }
978              
979             sub _prepare_integer32
980             {
981 6     6   12 my ($this, $type, $value) = @_;
982              
983             # Determine if the value is positive or negative
984 6         10 my $negative = ($value < 0);
985              
986             # Check to see if the most significant bit is set, if it is we
987             # need to prefix the encoding with a zero byte.
988              
989 6         8 my $size = 4; # Assuming 4 byte integers
990 6         11 my $prefix = FALSE;
991 6         9 my $bytes = q{};
992              
993 6 100 100     24 if ((($value & 0xff000000) & 0x80000000) && (!$negative)) {
994 1         2 $size++;
995 1         159 $prefix = TRUE;
996             }
997              
998             # Remove occurances of nine consecutive ones (if negative) or zeros
999             # from the most significant end of the two's complement integer.
1000              
1001 6   66     54 while ((((!($value & 0xff800000))) ||
      100        
1002             ((($value & 0xff800000) == 0xff800000) && ($negative))) &&
1003             ($size > 1))
1004             {
1005 12         13 $size--;
1006 12         111 $value <<= 8;
1007             }
1008              
1009             # Add a zero byte so the integer is decoded as a positive value
1010 6 100       19 if ($prefix) {
1011 1         2 $bytes = pack 'x';
1012 1         2 $size--;
1013             }
1014              
1015             # Build the integer
1016 6         16 while ($size-- > 0) {
1017 12         28 $bytes .= pack 'C*', (($value & 0xff000000) >> 24);
1018 12         29 $value <<= 8;
1019             }
1020              
1021             # Encode ASN.1 header
1022 6         18 return $this->_prepare_type_length($type, $bytes);
1023             }
1024              
1025             sub _prepare_octet_string
1026             {
1027 6     6   12 my ($this, $value) = @_;
1028              
1029 6 50       17 if (!defined $value) {
1030 0         0 return $this->_error('The OCTET STRING value is not defined');
1031             }
1032              
1033 6         24 return $this->_prepare_type_length(OCTET_STRING, $value);
1034             }
1035              
1036             sub _prepare_null
1037             {
1038 0     0   0 return $_[0]->_prepare_type_length(NULL, q{});
1039             }
1040              
1041             sub _prepare_object_identifier
1042             {
1043 2     2   5 my ($this, $value) = @_;
1044              
1045 2 50       7 if (!defined $value) {
1046 0         0 return $this->_error('The OBJECT IDENTIFIER value not defined');
1047             }
1048              
1049             # The OBJECT IDENTIFIER is expected in dotted notation.
1050 2 50       17 if ($value !~ m/^\.?\d+(?:\.\d+)* *$/) {
1051 0         0 return $this->_error(
1052             'The OBJECT IDENTIFIER value "%s" is expected in dotted decimal ' .
1053             'notation', $value
1054             );
1055             }
1056              
1057             # Break it up into sub-identifiers.
1058 2         13 my @subids = split /\./, $value;
1059              
1060             # If there was a leading dot on _any_ OBJECT IDENTIFIER passed to
1061             # a prepare method, return a leading dot on _all_ of the OBJECT
1062             # IDENTIFIERs in the process methods.
1063              
1064 2 100       15 if ($subids[0] eq q{}) {
1065 1         4 DEBUG_INFO('leading dot present');
1066 1         3 $this->{_leading_dot} = TRUE;
1067 1         2 shift @subids;
1068             }
1069              
1070             # RFC 2578 Section 3.5 - "...there are at most 128 sub-identifiers in
1071             # a value, and each sub-identifier has a maximum value of 2^32-1..."
1072              
1073 2 50       12 if (@subids > 128) {
1074 0         0 return $this->_error(
1075             'The OBJECT IDENTIFIER value "%s" contains more than the maximum ' .
1076             'of 128 sub-identifiers allowed', $value
1077             );
1078             }
1079              
1080 2 50       5 if (grep { $_ < 0 || $_ > 4294967295; } @subids) {
  18 50       79  
1081 0         0 return $this->_error(
1082             'The OBJECT IDENTIFIER value "%s" contains a sub-identifier which ' .
1083             'is out of range (0..4294967295)', $value
1084             );
1085             }
1086              
1087             # ISO/IEC 8825 - Specification of Basic Encoding Rules for Abstract
1088             # Syntax Notation One (ASN.1) dictates that the first two sub-identifiers
1089             # are encoded into the first identifier using the the equation:
1090             # subid = ((first * 40) + second). Pad the OBJECT IDENTIFIER to at
1091             # least two sub-identifiers.
1092              
1093 2         23 while (@subids < 2) {
1094 0         0 push @subids, 0;
1095             }
1096              
1097             # The first sub-identifiers are limited to ccitt(0), iso(1), and
1098             # joint-iso-ccitt(2) as defined by RFC 2578.
1099              
1100 2 50       49 if ($subids[0] > 2) {
1101 0         0 return $this->_error(
1102             'The OBJECT IDENTIFIER value "%s" must begin with either 0 ' .
1103             '(ccitt), 1 (iso), or 2 (joint-iso-ccitt)', $value
1104             );
1105             }
1106              
1107             # If the first sub-identifier is 0 or 1, the second is limited to 0 - 39.
1108              
1109 2 50 33     21 if (($subids[0] < 2) && ($subids[1] >= 40)) {
    50          
1110 0         0 return $this->_error(
1111             'The second sub-identifier in the OBJECT IDENTIFIER value "%s" ' .
1112             'must be less than 40', $value
1113             );
1114             } elsif ($subids[1] >= (4294967295 - 80)) {
1115 0         0 return $this->_error(
1116             'The second sub-identifier in the OBJECT IDENTIFIER value "%s" ' .
1117             'must be less than %u', $value, (4294967295 - 80)
1118             );
1119             }
1120              
1121             # Now apply: subid = ((first * 40) + second)
1122              
1123 2         7 $subids[1] += (shift(@subids) * 40);
1124              
1125             # Encode each sub-identifier in base 128, most significant digit first,
1126             # with as few digits as possible. Bit eight (the high bit) is set on
1127             # each byte except the last.
1128              
1129             # Encode the ASN.1 header
1130 2         7 return $this->_prepare_type_length(OBJECT_IDENTIFIER, pack 'w*', @subids);
1131             }
1132              
1133             sub _prepare_sequence
1134             {
1135 5     5   16 return $_[0]->_prepare_implicit_sequence(SEQUENCE, $_[1]);
1136             }
1137              
1138             sub _prepare_implicit_sequence
1139             {
1140 6     6   14 my ($this, $type, $value) = @_;
1141              
1142 6 100       26 if (defined $value) {
1143 3         17 return $this->_prepare_type_length($type, $value);
1144             }
1145              
1146             # If the passed value is undefined, we assume that the value of
1147             # the IMPLICIT SEQUENCE is the data currently in the serial buffer.
1148              
1149 3 50       7 if ($this->{_length} < 0x80) {
    0          
    0          
1150 3         10 return $this->_buffer_put(pack 'C2', $type, $this->{_length});
1151             } elsif ($this->{_length} <= 0xff) {
1152 0         0 return $this->_buffer_put(pack 'C3', $type, 0x81, $this->{_length});
1153             } elsif ($this->{_length} <= 0xffff) {
1154 0         0 return $this->_buffer_put(pack 'CCn', $type, 0x82, $this->{_length});
1155             }
1156              
1157 0         0 return $this->_error('Unable to prepare the ASN.1 SEQUENCE length');
1158             }
1159              
1160             sub _prepare_ipaddress
1161             {
1162 0     0   0 my ($this, $value) = @_;
1163              
1164 0 0       0 if (!defined $value) {
1165 0         0 return $this->_error('IpAddress is not defined');
1166             }
1167              
1168 0 0       0 if ($value !~ /^\d+\.\d+\.\d+\.\d+$/) {
1169 0         0 return $this->_error(
1170             'The IpAddress value "%s" is expected in dotted decimal notation',
1171             $value
1172             );
1173             }
1174              
1175 0         0 my @octets = split /\./, $value;
1176              
1177 0 0       0 if (grep { $_ > 255; } @octets) {
  0         0  
1178 0         0 return $this->_error('The IpAddress value "%s" is invalid', $value);
1179             }
1180              
1181 0         0 return $this->_prepare_type_length(IPADDRESS, pack 'C4', @octets);
1182             }
1183              
1184             sub _prepare_counter
1185             {
1186 0     0   0 return $_[0]->_prepare_unsigned32(COUNTER, $_[1]);
1187             }
1188              
1189             sub _prepare_gauge
1190             {
1191 0     0   0 return $_[0]->_prepare_unsigned32(GAUGE, $_[1]);
1192             }
1193              
1194             sub _prepare_timeticks
1195             {
1196 0     0   0 return $_[0]->_prepare_unsigned32(TIMETICKS, $_[1]);
1197             }
1198              
1199             sub _prepare_opaque
1200             {
1201 0     0   0 my ($this, $value) = @_;
1202              
1203 0 0       0 if (!defined $value) {
1204 0         0 return $this->_error('The Opaque value is not defined');
1205             }
1206              
1207 0         0 return $this->_prepare_type_length(OPAQUE, $value);
1208             }
1209              
1210             sub _prepare_counter64
1211             {
1212 1     1   3 my ($this, $value) = @_;
1213              
1214             # Validate the SNMP version
1215 1 50       3 if ($this->{_version} == SNMP_VERSION_1) {
1216 0         0 return $this->_error('The Counter64 type is not supported in SNMPv1');
1217             }
1218              
1219             # Validate the passed value
1220 1 50       10 if (!defined $value) {
1221 0         0 return $this->_error('The Counter64 value is not defined');
1222             }
1223              
1224 1 50       12 if ($value !~ /^\+?\d+$/) {
1225 0         0 return $this->_error(
1226             'The Counter64 value "%s" is expected in positive numeric format',
1227             $value
1228             );
1229             }
1230              
1231 1         9 $value = Math::BigInt->new($value);
1232              
1233 1 50       21627 if ($value eq 'NaN') {
1234 0         0 return $this->_error('The Counter64 value "%s" is invalid', $value);
1235             }
1236              
1237             # Make sure the value is no more than 8 bytes long
1238 1 50       52 if ($value->bcmp('18446744073709551615') > 0) {
1239 0         0 return $this->_error(
1240             'The Counter64 value "%s" is out of range (0..18446744073709551615)',
1241             $value
1242             );
1243             }
1244              
1245 1         129 my ($quotient, $remainder, @bytes);
1246              
1247             # Handle a value of zero
1248 1 50       4 if ($value == 0) {
1249 0         0 unshift @bytes, 0x00;
1250             }
1251              
1252 1         151 while ($value > 0) {
1253 8         1054 ($quotient, $remainder) = $value->bdiv(256);
1254 8         1585 $value = Math::BigInt->new($quotient);
1255 8         184 unshift @bytes, $remainder;
1256             }
1257              
1258             # Make sure that the value is encoded as a positive value
1259 1 50       125 if ($bytes[0] & 0x80) {
1260 1         304 unshift @bytes, 0x00;
1261             }
1262              
1263 1         7 return $this->_prepare_type_length(COUNTER64, pack 'C*', @bytes);
1264             }
1265              
1266             sub _prepare_nosuchobject
1267             {
1268 0     0   0 my ($this) = @_;
1269              
1270 0 0       0 if ($this->{_version} == SNMP_VERSION_1) {
1271 0         0 return $this->_error('The noSuchObject type is not supported in SNMPv1');
1272             }
1273              
1274 0         0 return $this->_prepare_type_length(NOSUCHOBJECT, q{});
1275             }
1276              
1277             sub _prepare_nosuchinstance
1278             {
1279 0     0   0 my ($this) = @_;
1280              
1281 0 0       0 if ($this->{_version} == SNMP_VERSION_1) {
1282 0         0 return $this->_error(
1283             'The noSuchInstance type is not supported in SNMPv1'
1284             );
1285             }
1286              
1287 0         0 return $this->_prepare_type_length(NOSUCHINSTANCE, q{});
1288             }
1289              
1290             sub _prepare_endofmibview
1291             {
1292 0     0   0 my ($this) = @_;
1293              
1294 0 0       0 if ($this->{_version} == SNMP_VERSION_1) {
1295 0         0 return $this->_error('The endOfMibView type is not supported in SNMPv1');
1296             }
1297              
1298 0         0 return $this->_prepare_type_length(ENDOFMIBVIEW, q{});
1299             }
1300              
1301             sub _prepare_get_request
1302             {
1303 0     0   0 return $_[0]->_prepare_implicit_sequence(GET_REQUEST, $_[1]);
1304             }
1305              
1306             sub _prepare_get_next_request
1307             {
1308 0     0   0 return $_[0]->_prepare_implicit_sequence(GET_NEXT_REQUEST, $_[1]);
1309             }
1310              
1311             sub _prepare_get_response
1312             {
1313 0     0   0 return $_[0]->_prepare_implicit_sequence(GET_RESPONSE, $_[1]);
1314             }
1315              
1316             sub _prepare_set_request
1317             {
1318 1     1   4 return $_[0]->_prepare_implicit_sequence(SET_REQUEST, $_[1]);
1319             }
1320              
1321             sub _prepare_trap
1322             {
1323 0     0   0 my ($this, $value) = @_;
1324              
1325 0 0       0 if ($this->{_version} != SNMP_VERSION_1) {
1326 0         0 return $this->_error('The Trap-PDU is only supported in SNMPv1');
1327             }
1328              
1329 0         0 return $this->_prepare_implicit_sequence(TRAP, $value);
1330             }
1331              
1332             sub _prepare_get_bulk_request
1333             {
1334 0     0   0 my ($this, $value) = @_;
1335              
1336 0 0       0 if ($this->{_version} == SNMP_VERSION_1) {
1337 0         0 return $this->_error(
1338             'The GetBulkRequest-PDU is not supported in SNMPv1'
1339             );
1340             }
1341              
1342 0         0 return $this->_prepare_implicit_sequence(GET_BULK_REQUEST, $value);
1343             }
1344              
1345             sub _prepare_inform_request
1346             {
1347 0     0   0 my ($this, $value) = @_;
1348              
1349 0 0       0 if ($this->{_version} == SNMP_VERSION_1) {
1350 0         0 return $this->_error('The InformRequest-PDU is not supported in SNMPv1');
1351             }
1352              
1353 0         0 return $this->_prepare_implicit_sequence(INFORM_REQUEST, $value);
1354             }
1355              
1356             sub _prepare_v2_trap
1357             {
1358 0     0   0 my ($this, $value) = @_;
1359              
1360 0 0       0 if ($this->{_version} == SNMP_VERSION_1) {
1361 0         0 return $this->_error('The SNMPv2-Trap-PDU is not supported in SNMPv1');
1362             }
1363              
1364 0         0 return $this->_prepare_implicit_sequence(SNMPV2_TRAP, $value);
1365             }
1366              
1367             sub _prepare_report
1368             {
1369 0     0   0 my ($this, $value) = @_;
1370              
1371 0 0       0 if ($this->{_version} == SNMP_VERSION_1) {
1372 0         0 return $this->_error('The Report-PDU is not supported in SNMPv1');
1373             }
1374              
1375 0         0 return $this->_prepare_implicit_sequence(REPORT, $value);
1376             }
1377              
1378             #
1379             # Basic Encoding Rules (BER) process methods
1380             #
1381              
1382             sub _process_length
1383             {
1384 17     17   24 my ($this) = @_;
1385              
1386 17 50       47 return $this->_error() if defined $this->{_error};
1387              
1388 17         33 my $length = $this->_buffer_get(1);
1389              
1390 17 50       39 if (!defined $length) {
1391 0         0 return $this->_error();
1392             }
1393              
1394 17         30 $length = unpack 'C', $length;
1395              
1396 17 50       38 if (!($length & 0x80)) { # "Short" length
1397 17         2181 return $length;
1398             }
1399              
1400 0         0 my $byte_cnt = $length & 0x7f;
1401              
1402 0 0       0 if ($byte_cnt == 0) {
    0          
1403 0         0 return $this->_error('Indefinite ASN.1 lengths are not supported');
1404             } elsif ($byte_cnt > 4) {
1405 0         0 return $this->_error(
1406             'The ASN.1 length is too long (%u bytes)', $byte_cnt
1407             );
1408             }
1409              
1410 0 0       0 if (!defined($length = $this->_buffer_get($byte_cnt))) {
1411 0         0 return $this->_error();
1412             }
1413              
1414 0         0 return unpack 'N', ("\000" x (4 - $byte_cnt) . $length);
1415             }
1416              
1417             sub _process_integer32
1418             {
1419 6     6   11 my ($this, $type) = @_;
1420              
1421             # Decode the length
1422 6 50       15 return $this->_error() if !defined(my $length = $this->_process_length());
1423              
1424             # Return an error if the object length is zero?
1425 6 50       14 if ($length < 1) {
1426 0         0 return $this->_error('The %s length is equal to zero', asn1_itoa($type));
1427             }
1428              
1429             # Retrieve the whole byte stream outside of the loop.
1430 6 50       18 return $this->_error() if !defined(my $bytes = $this->_buffer_get($length));
1431              
1432 6         15 my @bytes = unpack 'C*', $bytes;
1433 6         12 my $negative = FALSE;
1434 6         8 my $int32 = 0;
1435              
1436             # Validate the length of the Integer32
1437 6 50 66     38 if (($length > 5) || (($length > 4) && ($bytes[0] != 0x00))) {
      33        
1438 0         0 return $this->_error(
1439             'The %s length is too long (%u bytes)', asn1_itoa($type), $length
1440             );
1441             }
1442              
1443             # If the first bit is set, the Integer32 is negative
1444 6 100       18 if ($bytes[0] & 0x80) {
1445 1         2 $int32 = -1;
1446 1         3 $negative = TRUE;
1447             }
1448              
1449             # Build the Integer32
1450 6         8 map { $int32 = (($int32 << 8) | $_) } @bytes;
  13         24  
1451              
1452 6 100       14 if ($negative) {
1453 1 50 33     3 if (($type == INTEGER) || (!($this->{_translate} & TRANSLATE_UNSIGNED))) {
1454 1         7 return unpack 'l', pack 'l', $int32;
1455             } else {
1456 0         0 DEBUG_INFO('translating negative %s value', asn1_itoa($type));
1457 0         0 return unpack 'L', pack 'l', $int32;
1458             }
1459             }
1460              
1461 5         35 return unpack 'L', pack 'L', $int32;
1462             }
1463              
1464             sub _process_octet_string
1465             {
1466 5     5   12 my ($this, $type) = @_;
1467              
1468             # Decode the length
1469 5 50       16 return $this->_error() if !defined(my $length = $this->_process_length());
1470              
1471             # Get the string
1472 5 50       18 return $this->_error() if !defined(my $s = $this->_buffer_get($length));
1473              
1474             # Set the translation mask
1475 5 50       38 my $mask = ($type == OPAQUE) ? TRANSLATE_OPAQUE : TRANSLATE_OCTET_STRING;
1476              
1477             #
1478             # Translate based on the definition of a DisplayString in RFC 2579.
1479             #
1480             # DisplayString ::= TEXTUAL-CONVENTION
1481             #
1482             # - the graphics characters (32-126) are interpreted as
1483             # US ASCII
1484             # - NUL, LF, CR, BEL, BS, HT, VT and FF have the special
1485             # meanings specified in RFC 854
1486             # - the sequence 'CR x' for any x other than LF or NUL is
1487             # illegal.
1488             #
1489              
1490 5 100       24 if ($this->{_translate} & $mask) {
1491 1         5 $type = asn1_itoa($type);
1492 1 50       7 if ($s =~ m{
1493             # The values other than NUL, LF, CR, BEL, BS, HT, VT, FF,
1494             # and the graphic characters (32-126) trigger translation.
1495             [\x01-\x06\x0e-\x1f\x7f-\xff]|
1496             # The sequence 'CR x' for any x other than LF or NUL
1497             # also triggers translation.
1498             \x0d(?![\x00\x0a])
1499             }x)
1500             {
1501 1         5 DEBUG_INFO(
1502             'translating %s to hexadecimal formatted DisplayString', $type
1503             );
1504 1         11 return sprintf '0x%s', unpack 'H*', $s;
1505             } else {
1506 0         0 DEBUG_INFO(
1507             'not translating %s, all octets are allowed in a DisplayString',
1508             $type
1509             );
1510             }
1511             }
1512              
1513 4         28 return $s;
1514             }
1515              
1516             sub _process_null
1517             {
1518 0     0   0 my ($this) = @_;
1519              
1520             # Decode the length
1521 0 0       0 return $this->_error() if !defined(my $length = $this->_process_length());
1522              
1523 0 0       0 return $this->_error('NULL length is not equal to zero') if ($length != 0);
1524              
1525 0 0       0 if ($this->{_translate} & TRANSLATE_NULL) {
1526 0         0 DEBUG_INFO(q{translating NULL to 'NULL' string});
1527 0         0 return 'NULL';
1528             }
1529              
1530 0         0 return q{};
1531             }
1532              
1533             sub _process_object_identifier
1534             {
1535 1     1   3 my ($this) = @_;
1536              
1537             # Decode the length
1538 1 50       3 return $this->_error() if !defined(my $length = $this->_process_length());
1539              
1540             # Return an error if the length is equal to zero?
1541 1 50       3 if ($length < 1) {
1542 0         0 return $this->_error('The OBJECT IDENTIFIER length is equal to zero');
1543             }
1544              
1545             # Retrieve the whole byte stream (by Niilo Neuvo).
1546              
1547 1 50       4 return $this->_error() if !defined(my $bytes = $this->_buffer_get($length));
1548              
1549 1         3 my @oid = ( 0, eval { unpack 'w129', $bytes } );
  1         5  
1550              
1551             # RFC 2578 Section 3.5 - "...there are at most 128 sub-identifiers in
1552             # a value, and each sub-identifier has a maximum value of 2^32-1..."
1553              
1554 1 50 33     6 if ($@ || (grep { $_ > 4294967295; } @oid)) {
  9         16  
1555 0         0 return $this->_error(
1556             'The OBJECT IDENTIFIER contains a sub-identifier which is out of ' .
1557             'range (0..4294967295)'
1558             );
1559             }
1560              
1561 1 50       4 if (@oid > 128) {
1562 0         0 return $this->_error(
1563             'The OBJECT IDENTIFIER contains more than the maximum of 128 ' .
1564             'sub-identifiers allowed'
1565             );
1566             }
1567              
1568             # The first two sub-identifiers are encoded into the first identifier
1569             # using the the equation: subid = ((first * 40) + second).
1570              
1571 1 50       3 if ($oid[1] == 0x2b) { # Handle the most common case
    0          
    0          
1572 1         2 $oid[0] = 1; # first [iso(1).org(3)]
1573 1         2 $oid[1] = 3;
1574             } elsif ($oid[1] < 40) {
1575 0         0 $oid[0] = 0;
1576             } elsif ($oid[1] < 80) {
1577 0         0 $oid[0] = 1;
1578 0         0 $oid[1] -= 40;
1579             } else {
1580 0         0 $oid[0] = 2;
1581 0         0 $oid[1] -= 80;
1582             }
1583              
1584             # Return the OID in dotted notation (optionally with a
1585             # leading dot if one was passed to the prepare routine).
1586              
1587 1 50       4 if ($this->{_leading_dot}) {
1588 1         3 DEBUG_INFO('adding leading dot');
1589 1         8 unshift @oid, q{};
1590             }
1591              
1592 1         9 return join q{.}, @oid;
1593             }
1594              
1595             sub _process_sequence
1596             {
1597             # Return the length, instead of the value
1598 3     3   8 goto &_process_length;
1599             }
1600              
1601             sub _process_ipaddress
1602             {
1603 0     0   0 my ($this) = @_;
1604              
1605             # Decode the length
1606 0 0       0 return $this->_error() if !defined(my $length = $this->_process_length());
1607              
1608 0 0       0 if ($length != 4) {
1609 0         0 return $this->_error('The IpAddress length of %d is invalid', $length);
1610             }
1611              
1612 0 0       0 if (defined(my $ipaddress = $this->_buffer_get(4))) {
1613 0         0 return sprintf '%vd', $ipaddress;
1614             }
1615              
1616 0         0 return $this->_error();
1617             }
1618              
1619             sub _process_counter
1620             {
1621 0     0   0 goto &_process_integer32;
1622             }
1623              
1624             sub _process_gauge
1625             {
1626 0     0   0 goto &_process_integer32;
1627             }
1628              
1629             sub _process_timeticks
1630             {
1631 0     0   0 my ($this) = @_;
1632              
1633 0 0       0 if (defined(my $ticks = $this->_process_integer32(TIMETICKS))) {
1634 0 0       0 if ($this->{_translate} & TRANSLATE_TIMETICKS) {
1635 0         0 DEBUG_INFO('translating %u TimeTicks to time', $ticks);
1636 0         0 return asn1_ticks_to_time($ticks);
1637             } else {
1638 0         0 return $ticks;
1639             }
1640             }
1641              
1642 0         0 return $this->_error();
1643             }
1644              
1645             sub _process_opaque
1646             {
1647 0     0   0 goto &_process_octet_string;
1648             }
1649              
1650             sub _process_counter64
1651             {
1652 1     1   4 my ($this, $type) = @_;
1653              
1654             # Verify the SNMP version
1655 1 50       5 if ($this->{_version} == SNMP_VERSION_1) {
1656 0         0 return $this->_error('The Counter64 type is not supported in SNMPv1');
1657             }
1658              
1659             # Decode the length
1660 1 50       5 return $this->_error() if !defined(my $length = $this->_process_length());
1661              
1662             # Return an error if the object length is zero?
1663 1 50       5 if ($length < 1) {
1664 0         0 return $this->_error('The Counter64 length is equal to zero');
1665             }
1666              
1667             # Retrieve the whole byte stream outside of the loop.
1668 1 50       5 return $this->_error() if !defined(my $bytes = $this->_buffer_get($length));
1669              
1670 1         5 my @bytes = unpack 'C*', $bytes;
1671 1         4 my $negative = FALSE;
1672              
1673             # Validate the length of the Counter64
1674 1 50 33     13 if (($length > 9) || (($length > 8) && ($bytes[0] != 0x00))) {
      33        
1675 0         0 return $_[0]->_error(
1676             'The Counter64 length is too long (%u bytes)', $length
1677             );
1678             }
1679              
1680             # If the first bit is set, the integer is negative
1681 1 50       5 if ($bytes[0] & 0x80) {
1682 0         0 $bytes[0] ^= 0xff;
1683 0         0 $negative = TRUE;
1684             }
1685              
1686             # Build the Counter64
1687 1         5 my $int64 = Math::BigInt->new(shift @bytes);
1688             map {
1689 1 50       79 if ($negative) { $_ ^= 0xff; }
  8         1004  
  0         0  
1690 8         21 $int64 *= 256;
1691 8         1127 $int64 += $_;
1692             } @bytes;
1693              
1694             # If the value is negative the other end incorrectly encoded
1695             # the Counter64 since it should always be a positive value.
1696              
1697 1 50       113 if ($negative) {
1698 0         0 $int64 = Math::BigInt->new('-1') - $int64;
1699 0 0       0 if ($this->{_translate} & TRANSLATE_UNSIGNED) {
1700 0         0 DEBUG_INFO('translating negative Counter64 value');
1701 0         0 $int64 += Math::BigInt->new('18446744073709551616');
1702             }
1703             }
1704              
1705             # Perl 5.6.0 (force to string or substitution does not work).
1706 1         4 $int64 .= q{};
1707              
1708             # Remove the plus sign (or should we leave it to imply Math::BigInt?)
1709 1         1316 $int64 =~ s/^\+//;
1710              
1711 1         5 return $int64;
1712             }
1713              
1714             sub _process_nosuchobject
1715             {
1716 0     0   0 my ($this) = @_;
1717              
1718             # Verify the SNMP version
1719 0 0       0 if ($this->{_version} == SNMP_VERSION_1) {
1720 0         0 return $this->_error('The noSuchObject type is not supported in SNMPv1');
1721             }
1722              
1723             # Decode the length
1724 0 0       0 return $this->_error() if !defined(my $length = $this->_process_length());
1725              
1726 0 0       0 if ($length != 0) {
1727 0         0 return $this->_error('The noSuchObject length is not equal to zero');
1728             }
1729              
1730 0 0       0 if ($this->{_translate} & TRANSLATE_NOSUCHOBJECT) {
1731 0         0 DEBUG_INFO(q{translating noSuchObject to 'noSuchObject' string});
1732 0         0 return 'noSuchObject';
1733             }
1734              
1735             # XXX: Releases greater than v5.2.0 longer set the error-status.
1736             # $this->{_error_status} = NOSUCHOBJECT;
1737              
1738 0         0 return q{};
1739             }
1740              
1741             sub _process_nosuchinstance
1742             {
1743 0     0   0 my ($this) = @_;
1744              
1745             # Verify the SNMP version
1746 0 0       0 if ($this->{_version} == SNMP_VERSION_1) {
1747 0         0 return $this->_error(
1748             'The noSuchInstance type is not supported in SNMPv1'
1749             );
1750             }
1751              
1752             # Decode the length
1753 0 0       0 return $this->_error() if !defined(my $length = $this->_process_length());
1754              
1755 0 0       0 if ($length != 0) {
1756 0         0 return $this->_error('The noSuchInstance length is not equal to zero');
1757             }
1758              
1759 0 0       0 if ($this->{_translate} & TRANSLATE_NOSUCHINSTANCE) {
1760 0         0 DEBUG_INFO(q{translating noSuchInstance to 'noSuchInstance' string});
1761 0         0 return 'noSuchInstance';
1762             }
1763              
1764             # XXX: Releases greater than v5.2.0 longer set the error-status.
1765             # $this->{_error_status} = NOSUCHINSTANCE;
1766              
1767 0         0 return q{};
1768             }
1769              
1770             sub _process_endofmibview
1771             {
1772 0     0   0 my ($this) = @_;
1773              
1774             # Verify the SNMP version
1775 0 0       0 if ($this->{_version} == SNMP_VERSION_1) {
1776 0         0 return $this->_error('The endOfMibView type is not supported in SNMPv1');
1777             }
1778              
1779             # Decode the length
1780 0 0       0 return $this->_error() if !defined(my $length = $this->_process_length());
1781              
1782 0 0       0 if ($length != 0) {
1783 0         0 return $this->_error('The endOfMibView length is not equal to zero');
1784             }
1785              
1786 0 0       0 if ($this->{_translate} & TRANSLATE_ENDOFMIBVIEW) {
1787 0         0 DEBUG_INFO(q{translating endOfMibView to 'endOfMibView' string});
1788 0         0 return 'endOfMibView';
1789             }
1790              
1791             # XXX: Releases greater than v5.2.0 longer set the error-status.
1792             # $this->{_error_status} = ENDOFMIBVIEW;
1793              
1794 0         0 return q{};
1795             }
1796              
1797             sub _process_pdu_type
1798             {
1799 1     1   2 my ($this, $type) = @_;
1800              
1801             # Generic methods used to process the PDU type. The ASN.1 type is
1802             # returned by the method as passed by the generic process routine.
1803              
1804 1 50       6 return defined($this->_process_length()) ? $type : $this->_error();
1805             }
1806              
1807             sub _process_get_request
1808             {
1809 0     0   0 goto &_process_pdu_type;
1810             }
1811              
1812             sub _process_get_next_request
1813             {
1814 0     0   0 goto &_process_pdu_type;
1815             }
1816              
1817             sub _process_get_response
1818             {
1819 0     0   0 goto &_process_pdu_type;
1820             }
1821              
1822             sub _process_set_request
1823             {
1824 1     1   4 goto &_process_pdu_type;
1825             }
1826              
1827             sub _process_trap
1828             {
1829 0     0   0 my ($this) = @_;
1830              
1831 0 0       0 if ($this->{_version} != SNMP_VERSION_1) {
1832 0         0 return $this->_error('The Trap-PDU is only supported in SNMPv1');
1833             }
1834              
1835 0         0 goto &_process_pdu_type;
1836             }
1837              
1838             sub _process_get_bulk_request
1839             {
1840 0     0   0 my ($this) = @_;
1841              
1842 0 0       0 if ($this->{_version} == SNMP_VERSION_1) {
1843 0         0 return $this->_error('The GetBulkRequest-PDU is not supported in SNMPv1');
1844             }
1845              
1846 0         0 goto &_process_pdu_type;
1847             }
1848              
1849             sub _process_inform_request
1850             {
1851 0     0   0 my ($this) = @_;
1852              
1853 0 0       0 if ($this->{_version} == SNMP_VERSION_1) {
1854 0         0 return $this->_error('The InformRequest-PDU is not supported in SNMPv1');
1855             }
1856              
1857 0         0 goto &_process_pdu_type;
1858             }
1859              
1860             sub _process_v2_trap
1861             {
1862 0     0   0 my ($this) = @_;
1863              
1864 0 0       0 if ($this->{_version} == SNMP_VERSION_1) {
1865 0         0 return $this->_error('The SNMPv2-Trap-PDU is not supported in SNMPv1');
1866             }
1867              
1868 0         0 goto &_process_pdu_type;
1869             }
1870              
1871             sub _process_report
1872             {
1873 0     0   0 my ($this) = @_;
1874              
1875 0 0       0 if ($this->{_version} == SNMP_VERSION_1) {
1876 0         0 return $this->_error('The Report-PDU is not supported in SNMPv1');
1877             }
1878              
1879 0         0 goto &_process_pdu_type;
1880             }
1881              
1882             #
1883             # Abstract Syntax Notation One (ASN.1) utility functions
1884             #
1885              
1886             {
1887             my $types = {
1888             INTEGER, 'INTEGER',
1889             OCTET_STRING, 'OCTET STRING',
1890             NULL, 'NULL',
1891             OBJECT_IDENTIFIER, 'OBJECT IDENTIFIER',
1892             SEQUENCE, 'SEQUENCE',
1893             IPADDRESS, 'IpAddress',
1894             COUNTER, 'Counter',
1895             GAUGE, 'Gauge',
1896             TIMETICKS, 'TimeTicks',
1897             OPAQUE, 'Opaque',
1898             COUNTER64, 'Counter64',
1899             NOSUCHOBJECT, 'noSuchObject',
1900             NOSUCHINSTANCE, 'noSuchInstance',
1901             ENDOFMIBVIEW, 'endOfMibView',
1902             GET_REQUEST, 'GetRequest-PDU',
1903             GET_NEXT_REQUEST, 'GetNextRequest-PDU',
1904             GET_RESPONSE, 'GetResponse-PDU',
1905             SET_REQUEST, 'SetRequest-PDU',
1906             TRAP, 'Trap-PDU',
1907             GET_BULK_REQUEST, 'GetBulkRequest-PDU',
1908             INFORM_REQUEST, 'InformRequest-PDU',
1909             SNMPV2_TRAP, 'SNMPv2-Trap-PDU',
1910             REPORT, 'Report-PDU'
1911             };
1912              
1913             sub asn1_itoa
1914             {
1915 3     3 0 5 my ($type) = @_;
1916              
1917 3 50       18 return q{??} if (@_ != 1);
1918              
1919 3 50       14 if (!exists $types->{$type}) {
1920 0         0 return sprintf '?? [0x%02x]', $type;
1921             }
1922              
1923 3         13 return $types->{$type};
1924             }
1925             }
1926              
1927             sub asn1_ticks_to_time
1928             {
1929 0   0 0 0 0 my $ticks = shift || 0;
1930              
1931 0         0 my $days = int($ticks / (24 * 60 * 60 * 100));
1932 0         0 $ticks %= (24 * 60 * 60 * 100);
1933              
1934 0         0 my $hours = int($ticks / (60 * 60 * 100));
1935 0         0 $ticks %= (60 * 60 * 100);
1936              
1937 0         0 my $minutes = int($ticks / (60 * 100));
1938 0         0 $ticks %= (60 * 100);
1939              
1940 0         0 my $seconds = ($ticks / 100);
1941              
1942 0 0       0 if ($days != 0){
    0          
    0          
1943 0 0       0 return sprintf '%d day%s, %02d:%02d:%05.02f', $days,
1944             ($days == 1 ? q{} : 's'), $hours, $minutes, $seconds;
1945             } elsif ($hours != 0) {
1946 0 0       0 return sprintf '%d hour%s, %02d:%05.02f', $hours,
1947             ($hours == 1 ? q{} : 's'), $minutes, $seconds;
1948             } elsif ($minutes != 0) {
1949 0 0       0 return sprintf '%d minute%s, %05.02f', $minutes,
1950             ($minutes == 1 ? q{} : 's'), $seconds;
1951             } else {
1952 0 0       0 return sprintf '%04.02f second%s', $seconds, ($seconds == 1 ? q{} : 's');
1953             }
1954             }
1955              
1956             #
1957             # Error handlers
1958             #
1959              
1960             sub _error
1961             {
1962 0     0   0 my $this = shift;
1963              
1964 0 0       0 if (!defined $this->{_error}) {
1965 0 0       0 $this->{_error} = (@_ > 1) ? sprintf(shift(@_), @_) : $_[0];
1966 0 0       0 if ($this->debug()) {
1967 0         0 printf "error: [%d] %s(): %s\n",
1968             (caller 0)[2], (caller 1)[3], $this->{_error};
1969             }
1970             }
1971              
1972 0         0 return;
1973             }
1974              
1975             sub _error_clear
1976             {
1977 1     1   4 return $_[0]->{_error} = undef;
1978             }
1979              
1980             #
1981             # Buffer manipulation methods
1982             #
1983              
1984             sub _buffer_append
1985             {
1986             # my ($this, $value) = @_;
1987              
1988 3 50   3   12 return $_[0]->_error() if defined $_[0]->{_error};
1989              
1990             # Always reset the index when the buffer is modified
1991 3         7 $_[0]->{_index} = 0;
1992              
1993             # Update our length
1994 3         75 $_[0]->{_length} += CORE::length($_[1]);
1995              
1996             # Append to the current buffer
1997 3         14 return $_[0]->{_buffer} .= $_[1];
1998             }
1999              
2000             sub _buffer_get
2001             {
2002 47     47   67 my ($this, $requested) = @_;
2003              
2004 47 50       96 return $this->_error() if defined $this->{_error};
2005              
2006             # Return the number of bytes requested at the current index or
2007             # clear and return the whole buffer if no argument is passed.
2008              
2009 47 50       98 if (@_ == 2) {
2010              
2011 47 50       106 if (($this->{_index} += $requested) > $this->{_length}) {
2012 0         0 $this->{_index} -= $requested;
2013 0 0       0 if ($this->{_length} >= $this->max_msg_size()) {
2014 0         0 return $this->_error(
2015             'The message size exceeded the buffer maxMsgSize of %d',
2016             $this->max_msg_size()
2017             );
2018             }
2019 0         0 return $this->_error('Unexpected end of message buffer');
2020             }
2021              
2022 47         197 return substr $this->{_buffer}, $this->{_index} - $requested, $requested;
2023             }
2024              
2025             # Always reset the index when the buffer is modified
2026 0         0 $this->{_index} = 0;
2027              
2028             # Update our length to 0, the whole buffer is about to be cleared.
2029 0         0 $this->{_length} = 0;
2030              
2031 0         0 return substr $this->{_buffer}, 0, CORE::length($this->{_buffer}), q{};
2032             }
2033              
2034             sub _buffer_put
2035             {
2036             # my ($this, $value) = @_;
2037              
2038 23 50   23   65 return $_[0]->_error() if defined $_[0]->{_error};
2039              
2040             # Always reset the index when the buffer is modified
2041 23         38 $_[0]->{_index} = 0;
2042              
2043             # Update our length
2044 23         41 $_[0]->{_length} += CORE::length($_[1]);
2045              
2046             # Add the prefix to the current buffer
2047 23         161 substr $_[0]->{_buffer}, 0, 0, $_[1];
2048              
2049 23         122 return $_[0]->{_buffer};
2050             }
2051              
2052             sub _buffer_dump
2053             {
2054 0     0   0 my ($this) = @_;
2055              
2056 0 0       0 return $DEBUG if (!$DEBUG);
2057              
2058 0 0       0 DEBUG_INFO('%d byte%s', $this->{_length}, $this->{_length} != 1 ? 's' : q{});
2059              
2060 0         0 my ($offset, $hex, $text) = (0, q{}, q{});
2061              
2062 0         0 while ($this->{_buffer} =~ /(.{1,16})/gs) {
2063 0         0 $hex = unpack 'H*', ($text = $1);
2064 0         0 $hex .= q{ } x (32 - CORE::length($hex));
2065 0         0 $hex = sprintf '%s %s %s %s ' x 4, unpack 'a2' x 16, $hex;
2066 0         0 $text =~ s/[\x00-\x1f\x7f-\xff]/./g;
2067 0         0 printf "[%04d] %s %s\n", $offset, uc($hex), $text;
2068 0         0 $offset += 16;
2069             }
2070              
2071 0         0 return $DEBUG;
2072             }
2073              
2074             sub DEBUG_INFO
2075             {
2076 3 50   3 0 10 return $DEBUG if (!$DEBUG);
2077              
2078 0 0         return printf
2079             sprintf('debug: [%d] %s(): ', (caller 0)[2], (caller 1)[3]) .
2080             ((@_ > 1) ? shift(@_) : '%s') .
2081             "\n",
2082             @_;
2083             }
2084              
2085             # ============================================================================
2086             1; # [end Net::SNMP::Message]