AlkantarClanX12
Current Path : /usr/share/perl5/vendor_perl/Net/SNMP/ |
Current File : //usr/share/perl5/vendor_perl/Net/SNMP/PDU.pm |
# -*- mode: perl -*- # ============================================================================ package Net::SNMP::PDU; # $Id: PDU.pm,v 3.1 2010/09/10 00:01:22 dtown Rel $ # Object used to represent a SNMP PDU. # Copyright (c) 2001-2010 David M. Town <dtown@cpan.org> # All rights reserved. # This program is free software; you may redistribute it and/or modify it # under the same terms as the Perl 5 programming language system itself. # ============================================================================ use strict; use Net::SNMP::Message qw( :types :versions asn1_itoa ENTERPRISE_SPECIFIC TRUE FALSE DEBUG_INFO ); use Net::SNMP::Transport qw( DOMAIN_UDPIPV4 DOMAIN_TCPIPV4 ); ## Version of the Net::SNMP::PDU module our $VERSION = v3.0.1; ## Handle importing/exporting of symbols use base qw( Net::SNMP::Message ); sub import { return Net::SNMP::Message->export_to_level(1, @_); } # [public methods] ----------------------------------------------------------- sub new { my $class = shift; # We play some games here to allow us to "convert" a Message into a PDU. my $this = ref($_[0]) ? bless shift(@_), $class : $class->SUPER::new(); # Override or initialize fields inherited from the base class $this->{_error_status} = 0; $this->{_error_index} = 0; $this->{_scoped} = FALSE; $this->{_var_bind_list} = undef; $this->{_var_bind_names} = []; $this->{_var_bind_types} = undef; my (%argv) = @_; # Validate the passed arguments for (keys %argv) { if (/^-?callback$/i) { $this->callback($argv{$_}); } elsif (/^-?contextengineid/i) { $this->context_engine_id($argv{$_}); } elsif (/^-?contextname/i) { $this->context_name($argv{$_}); } elsif (/^-?debug$/i) { $this->debug($argv{$_}); } elsif (/^-?leadingdot$/i) { $this->leading_dot($argv{$_}); } elsif (/^-?maxmsgsize$/i) { $this->max_msg_size($argv{$_}); } elsif (/^-?requestid$/i) { $this->request_id($argv{$_}); } elsif (/^-?security$/i) { $this->security($argv{$_}); } elsif (/^-?translate$/i) { $this->{_translate} = $argv{$_}; } elsif (/^-?transport$/i) { $this->transport($argv{$_}); } elsif (/^-?version$/i) { $this->version($argv{$_}); } else { $this->_error('The argument "%s" is unknown', $_); } if (defined $this->{_error}) { return wantarray ? (undef, $this->{_error}) : undef; } } if (!defined $this->{_transport}) { $this->_error('The Transport Domain object is not defined'); return wantarray ? (undef, $this->{_error}) : undef; } return wantarray ? ($this, q{}) : $this; } sub prepare_get_request { my ($this, $oids) = @_; $this->_error_clear(); return $this->prepare_pdu(GET_REQUEST, $this->_create_oid_null_pairs($oids)); } sub prepare_get_next_request { my ($this, $oids) = @_; $this->_error_clear(); return $this->prepare_pdu(GET_NEXT_REQUEST, $this->_create_oid_null_pairs($oids)); } sub prepare_get_response { my ($this, $trios) = @_; $this->_error_clear(); return $this->prepare_pdu(GET_RESPONSE, $this->_create_oid_value_pairs($trios)); } sub prepare_set_request { my ($this, $trios) = @_; $this->_error_clear(); return $this->prepare_pdu(SET_REQUEST, $this->_create_oid_value_pairs($trios)); } sub prepare_trap { my ($this, $enterprise, $addr, $generic, $specific, $time, $trios) = @_; $this->_error_clear(); return $this->_error('Insufficient arguments for a Trap-PDU') if (@_ < 6); # enterprise if (!defined $enterprise) { # Use iso(1).org(3).dod(6).internet(1).private(4).enterprises(1) # for the default enterprise. $this->{_enterprise} = '1.3.6.1.4.1'; } elsif ($enterprise !~ m/^\.?\d+(?:\.\d+)* *$/) { return $this->_error( 'The enterprise OBJECT IDENTIFIER "%s" is expected in dotted ' . 'decimal notation', $enterprise ); } else { $this->{_enterprise} = $enterprise; } # agent-addr if (!defined $addr) { # See if we can get the agent-addr from the Transport # Layer. If not, we return an error. if (defined $this->{_transport}) { if (($this->{_transport}->domain() ne DOMAIN_UDPIPV4) && ($this->{_transport}->domain() ne DOMAIN_TCPIPV4)) { $this->{_agent_addr} = '0.0.0.0'; } else { $this->{_agent_addr} = $this->{_transport}->agent_addr(); if ($this->{_agent_addr} eq '0.0.0.0') { delete $this->{_agent_addr}; } } } if (!exists $this->{_agent_addr}) { return $this->_error('Unable to resolve the local agent-addr'); } } elsif ($addr !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) { return $this->_error( 'The agent-addr "%s" is expected in dotted decimal notation', $addr ); } else { $this->{_agent_addr} = $addr; } # generic-trap if (!defined $generic) { # Use enterpriseSpecific(6) for the generic-trap type. $this->{_generic_trap} = ENTERPRISE_SPECIFIC; } elsif ($generic !~ /^\d+$/) { return $this->_error( 'The generic-trap value "%s" is expected in positive numeric format', $generic ); } else { $this->{_generic_trap} = $generic; } # specific-trap if (!defined $specific) { $this->{_specific_trap} = 0; } elsif ($specific !~ /^\d+$/) { return $this->_error( 'The specific-trap value "%s" is expected in positive numeric format', $specific ); } else { $this->{_specific_trap} = $specific; } # time-stamp if (!defined $time) { # Use the "uptime" of the script for the time-stamp. $this->{_time_stamp} = ((time() - $^T) * 100); } elsif ($time !~ /^\d+$/) { return $this->_error( 'The time-stamp value "%s" is expected in positive numeric format', $time ); } else { $this->{_time_stamp} = $time; } return $this->prepare_pdu(TRAP, $this->_create_oid_value_pairs($trios)); } sub prepare_get_bulk_request { my ($this, $repeaters, $repetitions, $oids) = @_; $this->_error_clear(); if (@_ < 3) { return $this->_error('Insufficient arguments for a GetBulkRequest-PDU'); } # non-repeaters if (!defined $repeaters) { $this->{_error_status} = 0; } elsif ($repeaters !~ /^\d+$/) { return $this->_error( 'The non-repeaters value "%s" is expected in positive numeric format', $repeaters ); } elsif ($repeaters > 2147483647) { return $this->_error( 'The non-repeaters value %s is out of range (0..2147483647)', $repeaters ); } else { $this->{_error_status} = $repeaters; } # max-repetitions if (!defined $repetitions) { $this->{_error_index} = 0; } elsif ($repetitions !~ /^\d+$/) { return $this->_error( 'The max-repetitions value "%s" is expected in positive numeric ' . 'format', $repetitions ); } elsif ($repetitions > 2147483647) { return $this->_error( 'The max-repetitions value %s is out of range (0..2147483647)', $repetitions ); } else { $this->{_error_index} = $repetitions; } # Some sanity checks if (defined($oids) && (ref($oids) eq 'ARRAY')) { if ($this->{_error_status} > @{$oids}) { return $this->_error( 'The non-repeaters value %d is greater than the number of ' . 'variable-bindings %d', $this->{_error_status}, scalar @{$oids} ); } if (($this->{_error_status} == @{$oids}) && ($this->{_error_index})) { return $this->_error( 'The non-repeaters value %d equals the number of variable-' . 'bindings and max-repetitions is not equal to zero', $this->{_error_status} ); } } return $this->prepare_pdu(GET_BULK_REQUEST, $this->_create_oid_null_pairs($oids)); } sub prepare_inform_request { my ($this, $trios) = @_; $this->_error_clear(); return $this->prepare_pdu(INFORM_REQUEST, $this->_create_oid_value_pairs($trios)); } sub prepare_snmpv2_trap { my ($this, $trios) = @_; $this->_error_clear(); return $this->prepare_pdu(SNMPV2_TRAP, $this->_create_oid_value_pairs($trios)); } sub prepare_report { my ($this, $trios) = @_; $this->_error_clear(); return $this->prepare_pdu(REPORT, $this->_create_oid_value_pairs($trios)); } sub prepare_pdu { my ($this, $type, $var_bind) = @_; # Clear the buffer $this->clear(); # Clear the "scoped" indication $this->{_scoped} = FALSE; # VarBindList::=SEQUENCE OF VarBind if (!defined $this->_prepare_var_bind_list($var_bind || [])) { return $this->_error(); } # PDU::=SEQUENCE if (!defined $this->_prepare_pdu_sequence($type)) { return $this->_error(); } return TRUE; } sub prepare_var_bind_list { my ($this, $var_bind) = @_; return $this->_prepare_var_bind_list($var_bind || []); } sub prepare_pdu_sequence { goto &_prepare_pdu_sequence; } sub prepare_pdu_scope { goto &_prepare_pdu_scope; } sub process_pdu { my ($this) = @_; # Clear any errors $this->_error_clear(); # PDU::=SEQUENCE return $this->_error() if !defined $this->_process_pdu_sequence(); # VarBindList::=SEQUENCE OF VarBind return $this->_process_var_bind_list(); } sub process_pdu_scope { goto &_process_pdu_scope; } sub process_pdu_sequence { goto &_process_pdu_sequence; } sub process_var_bind_list { goto &_process_var_bind_list; } sub expect_response { my ($this) = @_; if (($this->{_pdu_type} == GET_RESPONSE) || ($this->{_pdu_type} == TRAP) || ($this->{_pdu_type} == SNMPV2_TRAP) || ($this->{_pdu_type} == REPORT)) { return FALSE; } return TRUE; } sub pdu_type { return $_[0]->{_pdu_type}; } sub error_status { my ($this, $status) = @_; # error-status::=INTEGER { noError(0) .. inconsistentName(18) } if (@_ == 2) { if (!defined $status) { return $this->_error('The error-status value is not defined'); } if (($status < 0) || ($status > (($this->version > SNMP_VERSION_1) ? 18 : 5))) { return $this->_error( 'The error-status %s is out of range (0..%d)', $status, ($this->version > SNMP_VERSION_1) ? 18 : 5 ); } $this->{_error_status} = $status; } return $this->{_error_status} || 0; # noError(0) } sub error_index { my ($this, $index) = @_; # error-index::=INTEGER (0..max-bindings) if (@_ == 2) { if (!defined $index) { return $this->_error('The error-index value is not defined'); } if (($index < 0) || ($index > 2147483647)) { return $this->_error( 'The error-index value %s is out of range (0.. 2147483647)', $index ); } $this->{_error_index} = $index; } return $this->{_error_index} || 0; } sub non_repeaters { # non-repeaters::=INTEGER (0..max-bindings) return $_[0]->{_error_status} || 0; } sub max_repetitions { # max-repetitions::=INTEGER (0..max-bindings) return $_[0]->{_error_index} || 0; } sub enterprise { return $_[0]->{_enterprise}; } sub agent_addr { return $_[0]->{_agent_addr}; } sub generic_trap { return $_[0]->{_generic_trap}; } sub specific_trap { return $_[0]->{_specific_trap}; } sub time_stamp { return $_[0]->{_time_stamp}; } sub var_bind_list { my ($this, $vbl, $types) = @_; return if defined $this->{_error}; if (@_ > 1) { # The VarBindList HASH is being updated from an external # source. We need to update the VarBind names ARRAY to # correspond to the new keys of the HASH. If the updated # information is valid, we will use lexicographical ordering # for the ARRAY entries since we do not have a PDU to use # to determine the ordering. The ASN.1 types HASH is also # updated here if a cooresponding HASH is passed. We double # check the mapping by populating the hash with the keys of # the VarBindList HASH. if (!defined($vbl) || (ref($vbl) ne 'HASH')) { $this->{_var_bind_list} = undef; $this->{_var_bind_names} = []; $this->{_var_bind_types} = undef; } else { $this->{_var_bind_list} = $vbl; @{$this->{_var_bind_names}} = map { $_->[0] } sort { $a->[1] cmp $b->[1] } map { my $oid = $_; $oid =~ s/^\.//; $oid =~ s/ /\.0/g; [$_, pack 'N*', split m/\./, $oid] } keys %{$vbl}; if (!defined($types) || (ref($types) ne 'HASH')) { $types = {}; } for (keys %{$vbl}) { $this->{_var_bind_types}->{$_} = exists($types->{$_}) ? $types->{$_} : undef; } } } return $this->{_var_bind_list}; } sub var_bind_names { my ($this) = @_; return [] if defined($this->{_error}) || !defined $this->{_var_bind_names}; return $this->{_var_bind_names}; } sub var_bind_types { my ($this) = @_; return if defined $this->{_error}; return $this->{_var_bind_types}; } sub scoped { return $_[0]->{_scoped}; } # [private methods] ---------------------------------------------------------- sub _prepare_pdu_scope { my ($this) = @_; return TRUE if (($this->{_version} < SNMP_VERSION_3) || ($this->{_scoped})); # contextName::=OCTET STRING if (!defined $this->prepare(OCTET_STRING, $this->context_name())) { return $this->_error(); } # contextEngineID::=OCTET STRING if (!defined $this->prepare(OCTET_STRING, $this->context_engine_id())) { return $this->_error(); } # ScopedPDU::=SEQUENCE if (!defined $this->prepare(SEQUENCE)) { return $this->_error(); } # Indicate that this PDU has been scoped and return success. return $this->{_scoped} = TRUE; } sub _prepare_pdu_sequence { my ($this, $type) = @_; # Do not do anything if there has already been an error return $this->_error() if defined $this->{_error}; # Make sure the PDU type was passed return $this->_error('The SNMP PDU type is not defined') if (@_ != 2); # Set the PDU type $this->{_pdu_type} = $type; # Make sure the request-id has been set if (!exists $this->{_request_id}) { $this->{_request_id} = int rand 2147483648; } # We need to encode everything in reverse order so the # objects end up in the correct place. if ($this->{_pdu_type} != TRAP) { # PDU::=SEQUENCE # error-index/max-repetitions::=INTEGER if (!defined $this->prepare(INTEGER, $this->{_error_index})) { return $this->_error(); } # error-status/non-repeaters::=INTEGER if (!defined $this->prepare(INTEGER, $this->{_error_status})) { return $this->_error(); } # request-id::=INTEGER if (!defined $this->prepare(INTEGER, $this->{_request_id})) { return $this->_error(); } } else { # Trap-PDU::=IMPLICIT SEQUENCE # time-stamp::=TimeTicks if (!defined $this->prepare(TIMETICKS, $this->{_time_stamp})) { return $this->_error(); } # specific-trap::=INTEGER if (!defined $this->prepare(INTEGER, $this->{_specific_trap})) { return $this->_error(); } # generic-trap::=INTEGER if (!defined $this->prepare(INTEGER, $this->{_generic_trap})) { return $this->_error(); } # agent-addr::=NetworkAddress if (!defined $this->prepare(IPADDRESS, $this->{_agent_addr})) { return $this->_error(); } # enterprise::=OBJECT IDENTIFIER if (!defined $this->prepare(OBJECT_IDENTIFIER, $this->{_enterprise})) { return $this->_error(); } } # PDUs::=CHOICE if (!defined $this->prepare($this->{_pdu_type})) { return $this->_error(); } return TRUE; } sub _prepare_var_bind_list { my ($this, $var_bind) = @_; # The passed array is expected to consist of groups of four values # consisting of two sets of ASN.1 types and their values. if (@{$var_bind} % 4) { $this->var_bind_list(undef); return $this->_error( 'The VarBind list size of %d is not a factor of 4', scalar @{$var_bind} ); } # Initialize the "var_bind_*" data. $this->{_var_bind_list} = {}; $this->{_var_bind_names} = []; $this->{_var_bind_types} = {}; # Use the object's buffer to build each VarBind SEQUENCE and then append # it to a local buffer. The local buffer will then be used to create # the VarBindList SEQUENCE. my ($buffer, $name_type, $name_value, $syntax_type, $syntax_value) = (q{}); while (@{$var_bind}) { # Pull a quartet of ASN.1 types and values from the passed array. ($name_type, $name_value, $syntax_type, $syntax_value) = splice @{$var_bind}, 0, 4; # Reverse the order of the fields because prepare() does a prepend. # value::=ObjectSyntax if (!defined $this->prepare($syntax_type, $syntax_value)) { $this->var_bind_list(undef); return $this->_error(); } # name::=ObjectName if ($name_type != OBJECT_IDENTIFIER) { $this->var_bind_list(undef); return $this->_error( 'An ObjectName type of 0x%02x was expected, but 0x%02x was found', OBJECT_IDENTIFIER, $name_type ); } if (!defined $this->prepare($name_type, $name_value)) { $this->var_bind_list(undef); return $this->_error(); } # VarBind::=SEQUENCE if (!defined $this->prepare(SEQUENCE)) { $this->var_bind_list(undef); return $this->_error(); } # Append the VarBind to the local buffer and clear it. $buffer .= $this->clear(); # Populate the "var_bind_*" data so we can provide consistent # output for the methods regardless of whether we are a request # or a response PDU. Make sure the HASH key is unique if in # case duplicate OBJECT IDENTIFIERs are provided. while (exists $this->{_var_bind_list}->{$name_value}) { $name_value .= q{ }; # Pad with spaces } $this->{_var_bind_list}->{$name_value} = $syntax_value; $this->{_var_bind_types}->{$name_value} = $syntax_type; push @{$this->{_var_bind_names}}, $name_value; } # VarBindList::=SEQUENCE OF VarBind if (!defined $this->prepare(SEQUENCE, $buffer)) { $this->var_bind_list(undef); return $this->_error(); } return TRUE; } sub _create_oid_null_pairs { my ($this, $oids) = @_; return [] if !defined $oids; if (ref($oids) ne 'ARRAY') { return $this->_error( 'The OBJECT IDENTIFIER list is expected as an array reference' ); } my $pairs = []; for (@{$oids}) { push @{$pairs}, OBJECT_IDENTIFIER, $_, NULL, q{}; } return $pairs; } sub _create_oid_value_pairs { my ($this, $trios) = @_; return [] if !defined $trios; if (ref($trios) ne 'ARRAY') { return $this->_error('The trio list is expected as an array reference'); } if (@{$trios} % 3) { return $this->_error( 'The [OBJECT IDENTIFIER, ASN.1 type, object value] trio is expected' ); } my $pairs = []; for (my $i = 0; $i < $#{$trios}; $i += 3) { push @{$pairs}, OBJECT_IDENTIFIER, $trios->[$i], $trios->[$i+1], $trios->[$i+2]; } return $pairs; } sub _process_pdu_scope { my ($this) = @_; return TRUE if ($this->{_version} < SNMP_VERSION_3); # ScopedPDU::=SEQUENCE return $this->_error() if !defined $this->process(SEQUENCE); # contextEngineID::=OCTET STRING if (!defined $this->context_engine_id($this->process(OCTET_STRING))) { return $this->_error(); } # contextName::=OCTET STRING if (!defined $this->context_name($this->process(OCTET_STRING))) { return $this->_error(); } # Indicate that this PDU is scoped and return success. return $this->{_scoped} = TRUE; } sub _process_pdu_sequence { my ($this) = @_; # PDUs::=CHOICE if (!defined ($this->{_pdu_type} = $this->process())) { return $this->_error(); } if ($this->{_pdu_type} != TRAP) { # PDU::=SEQUENCE # request-id::=INTEGER if (!defined ($this->{_request_id} = $this->process(INTEGER))) { return $this->_error(); } # error-status::=INTEGER if (!defined ($this->{_error_status} = $this->process(INTEGER))) { return $this->_error(); } # error-index::=INTEGER if (!defined ($this->{_error_index} = $this->process(INTEGER))) { return $this->_error(); } # Indicate that we have an SNMP error, but do not return an error. if (($this->{_error_status}) && ($this->{_pdu_type} == GET_RESPONSE)) { $this->_error( 'Received %s error-status at error-index %d', _error_status_itoa($this->{_error_status}), $this->{_error_index} ); } } else { # Trap-PDU::=IMPLICIT SEQUENCE # enterprise::=OBJECT IDENTIFIER if (!defined ($this->{_enterprise} = $this->process(OBJECT_IDENTIFIER))) { return $this->_error(); } # agent-addr::=NetworkAddress if (!defined ($this->{_agent_addr} = $this->process(IPADDRESS))) { return $this->_error(); } # generic-trap::=INTEGER if (!defined ($this->{_generic_trap} = $this->process(INTEGER))) { return $this->_error(); } # specific-trap::=INTEGER if (!defined ($this->{_specific_trap} = $this->process(INTEGER))) { return $this->_error(); } # time-stamp::=TimeTicks if (!defined ($this->{_time_stamp} = $this->process(TIMETICKS))) { return $this->_error(); } } return TRUE; } sub _process_var_bind_list { my ($this) = @_; my $value; # VarBindList::=SEQUENCE if (!defined($value = $this->process(SEQUENCE))) { return $this->_error(); } # Using the length of the VarBindList SEQUENCE, # calculate the end index. my $end = $this->index() + $value; $this->{_var_bind_list} = {}; $this->{_var_bind_names} = []; $this->{_var_bind_types} = {}; my ($oid, $type); while ($this->index() < $end) { # VarBind::=SEQUENCE if (!defined $this->process(SEQUENCE)) { return $this->_error(); } # name::=ObjectName if (!defined ($oid = $this->process(OBJECT_IDENTIFIER))) { return $this->_error(); } # value::=ObjectSyntax if (!defined ($value = $this->process(undef, $type))) { return $this->_error(); } # Create a hash consisting of the OBJECT IDENTIFIER as a # key and the ObjectSyntax as the value. If there is a # duplicate OBJECT IDENTIFIER in the VarBindList, we pad # that OBJECT IDENTIFIER with spaces to make a unique # key in the hash. while (exists $this->{_var_bind_list}->{$oid}) { $oid .= q{ }; # Pad with spaces } DEBUG_INFO('{ %s => %s: %s }', $oid, asn1_itoa($type), $value); $this->{_var_bind_list}->{$oid} = $value; $this->{_var_bind_types}->{$oid} = $type; # Create an array with the ObjectName OBJECT IDENTIFIERs # so that the order in which the VarBinds where encoded # in the PDU can be retrieved later. push @{$this->{_var_bind_names}}, $oid; } # Return an error based on the contents of the VarBindList # if we received a Report-PDU. if ($this->{_pdu_type} == REPORT) { return $this->_report_pdu_error(); } # Return the var_bind_list hash return $this->{_var_bind_list}; } { my @error_status = qw( noError tooBig noSuchName badValue readOnly genError noAccess wrongType wrongLength wrongEncoding wrongValue noCreation inconsistentValue resourceUnavailable commitFailed undoFailed authorizationError notWritable inconsistentName ); sub _error_status_itoa { return '??' if (@_ != 1); if (($_[0] > $#error_status) || ($_[0] < 0)) { return sprintf '??(%d)', $_[0]; } return sprintf '%s(%d)', $error_status[$_[0]], $_[0]; } } { my %report_oids = ( '1.3.6.1.6.3.11.2.1.1' => 'snmpUnknownSecurityModels', '1.3.6.1.6.3.11.2.1.2' => 'snmpInvalidMsgs', '1.3.6.1.6.3.11.2.1.3' => 'snmpUnknownPDUHandlers', '1.3.6.1.6.3.12.1.4' => 'snmpUnavailableContexts', '1.3.6.1.6.3.12.1.5' => 'snmpUnknownContexts', '1.3.6.1.6.3.15.1.1.1' => 'usmStatsUnsupportedSecLevels', '1.3.6.1.6.3.15.1.1.2' => 'usmStatsNotInTimeWindows', '1.3.6.1.6.3.15.1.1.3' => 'usmStatsUnknownUserNames', '1.3.6.1.6.3.15.1.1.4' => 'usmStatsUnknownEngineIDs', '1.3.6.1.6.3.15.1.1.5' => 'usmStatsWrongDigests', '1.3.6.1.6.3.15.1.1.6' => 'usmStatsDecryptionErrors', ); sub _report_pdu_error { my ($this) = @_; # Remove the leading dot (if present) and replace the dotted notation # of the OBJECT IDENTIFIER with the text ObjectName based upon an # expected list of report OBJECT IDENTIFIERs. my %var_bind_list; for my $oid (@{$this->{_var_bind_names}}) { my $text = $oid; $text =~ s/^\.//; for (keys %report_oids) { if ($text =~ s/\Q$_/$report_oids{$_}/) { last; } } $var_bind_list{$text} = $this->{_var_bind_list}->{$oid}; } my $count = keys %var_bind_list; if ($count == 1) { # Return the OBJECT IDENTIFIER and value. my $text = (keys %var_bind_list)[0]; return $this->_error( 'Received %s Report-PDU with value %s', $text, $var_bind_list{$text} ); } elsif ($count > 1) { # Return a list of OBJECT IDENTIFIERs. return $this->_error( 'Received Report-PDU [%s]', join ', ', keys %var_bind_list ); } else { return $this->_error('Received empty Report-PDU'); } } } # ============================================================================ 1; # [end Net::SNMP::PDU]