From 68d0715bf459cba1e94cdb8644d6f44036fe5694 Mon Sep 17 00:00:00 2001 From: Tobi Oetiker Date: Tue, 9 Sep 2008 05:30:26 +0000 Subject: updated snmp session to 1.12 --- lib/BER.pm | 158 +++++++++++------ lib/SNMP_Session.pm | 100 +++++++---- lib/SNMP_util.pm | 484 +++++++++++++++++++++++++++++----------------------- 3 files changed, 438 insertions(+), 304 deletions(-) (limited to 'lib') diff --git a/lib/BER.pm b/lib/BER.pm index 1a3ad89..1206feb 100644 --- a/lib/BER.pm +++ b/lib/BER.pm @@ -2,10 +2,11 @@ ###################################################################### ### BER (Basic Encoding Rules) encoding and decoding. ###################################################################### -### Copyright (c) 1995-2002, Simon Leinen. +### Copyright (c) 1995-2008, Simon Leinen. ### ### This program is free software; you can redistribute it under the -### "Artistic License" included in this distribution (file "Artistic"). +### "Artistic License 2.0" included in this distribution +### (file "Artistic"). ###################################################################### ### This module implements encoding and decoding of ASN.1-based data ### structures using the Basic Encoding Rules (BER). Only the subset @@ -20,13 +21,14 @@ ### Dave Rand : Added SysUpTime decode ### Philippe Simonet : Support larger subids ### Yufang HU : Support even larger subids -### Mike Mitchell : New generalized encode_int() +### Mike Mitchell : New generalized encode_int() ### Mike Diehn : encode_ip_address() ### Rik Hoorelbeke : encode_oid() fix ### Brett T Warden : pretty UInteger32 ### Bert Driehuis : Handle SNMPv2 exception codes ### Jakob Ilves (/IlvJa) : PDU decoding ### Jan Kasprzak : Fix for PDU syntax check +### Milen Pavlov : Recognize variant length for ints ###################################################################### package BER; @@ -34,10 +36,11 @@ package BER; require 5.002; use strict; -use vars qw(@ISA @EXPORT $VERSION $pretty_print_timeticks $errmsg); +use vars qw(@ISA @EXPORT $VERSION $pretty_print_timeticks + %pretty_printer %default_printer $errmsg); use Exporter; -$VERSION = '0.95'; +$VERSION = '1.05'; @ISA = qw(Exporter); @@ -50,7 +53,8 @@ $VERSION = '0.95'; decode_sequence decode_by_template pretty_print pretty_print_timeticks hex_string hex_string_of_type - encoded_oid_prefix_p errmsg); + encoded_oid_prefix_p errmsg + register_pretty_printer unregister_pretty_printer); ### Variables @@ -84,6 +88,8 @@ sub pretty_uptime ($); sub pretty_uptime_value ($); sub pretty_ip_address ($); sub pretty_generic_sequence ($); +sub register_pretty_printer ($); +sub unregister_pretty_printer ($); sub hex_string ($); sub hex_string_of_type ($$); sub decode_oid ($); @@ -95,7 +101,7 @@ sub decode_intlike ($); sub decode_unsignedlike ($); sub decode_intlike_s ($$); sub decode_string ($); -sub decode_length ($); +sub decode_length ($@); sub encoded_oid_prefix_p ($$); sub decode_subid ($$$); sub decode_generic_tlv ($); @@ -147,6 +153,22 @@ sub snmp_nosuchobject { context_flag () | 0x00 } sub snmp_nosuchinstance { context_flag () | 0x01 } sub snmp_endofmibview { context_flag () | 0x02 } +### pretty-printer initialization code. Create a hash with +### the most common types of pretty-printer routines. + +BEGIN { + $default_printer{int_tag()} = \&pretty_intlike; + $default_printer{snmp_counter32_tag()} = \&pretty_unsignedlike; + $default_printer{snmp_gauge32_tag()} = \&pretty_unsignedlike; + $default_printer{snmp_counter64_tag()} = \&pretty_unsignedlike; + $default_printer{snmp_uinteger32_tag()} = \&pretty_unsignedlike; + $default_printer{octet_string_tag()} = \&pretty_string; + $default_printer{object_id_tag()} = \&pretty_oid; + $default_printer{snmp_ip_address_tag()} = \&pretty_ip_address; + + %pretty_printer = %default_printer; +} + #### Encoding sub encode_header ($$) { @@ -187,12 +209,11 @@ sub encode_intlike ($$) { $sign = ($int >= 0) ? 0 : 0xff; if (ref $int && $int->isa ("Math::BigInt")) { for(;;) { - $val = $int->bmod (256); + $val = $int->copy()->bmod (256); unshift(@vals, $val); return encode_header ($tag, $#vals + 1).pack ("C*", @vals) if ($int >= -128 && $int < 128); - $int = $int - $sign; - $int = $int / 256; + $int->bsub ($sign)->bdiv (256); } } else { for(;;) { @@ -200,8 +221,7 @@ sub encode_intlike ($$) { unshift(@vals, $val); return encode_header ($tag, $#vals + 1).pack ("C*", @vals) if ($int >= -128 && $int < 128); - $int -= $sign; - $int = int($int / 256); + $int -= $sign, $int = int($int / 256); } } } @@ -304,23 +324,16 @@ sub encode_timeticks ($) { sub pretty_print ($) { my ($packet) = @_; - my ($type,$rest); return undef unless defined $packet; my $result = ord (substr ($packet, 0, 1)); - return pretty_intlike ($packet) - if $result == int_tag; - return pretty_unsignedlike ($packet) - if $result == snmp_counter32_tag - || $result == snmp_gauge32_tag - || $result == snmp_counter64_tag - || $result == snmp_uinteger32_tag; - return pretty_string ($packet) if $result == octet_string_tag; - return pretty_oid ($packet) if $result == object_id_tag; + if (exists ($pretty_printer{$result})) { + my $c_ref = $pretty_printer{$result}; + return &$c_ref ($packet); + } return ($pretty_print_timeticks ? pretty_uptime ($packet) : pretty_unsignedlike ($packet)) if $result == uptime_tag; - return pretty_ip_address ($packet) if $result == snmp_ip_address_tag; return "(null)" if $result == null_tag; return error ("Exception code: noSuchObject") if $result == snmp_nosuchobject; return error ("Exception code: noSuchInstance") if $result == snmp_nosuchinstance; @@ -334,7 +347,7 @@ sub pretty_print ($) { if($result == (&constructor_flag | &sequence_tag) # sequence || $result == (0 | $ctx_cons_flags) #get_request || $result == (1 | $ctx_cons_flags) #getnext_request - || $result == (2 | $ctx_cons_flags) #get_response + || $result == (2 | $ctx_cons_flags) #response || $result == (3 | $ctx_cons_flags) #set_request || $result == (4 | $ctx_cons_flags) #trap_request || $result == (5 | $ctx_cons_flags) #getbulk_request @@ -350,12 +363,13 @@ sub pretty_print ($) { (constructor_flag | sequence_tag) => "Sequence", (0 | $ctx_cons_flags) => "GetRequest", (1 | $ctx_cons_flags) => "GetNextRequest", - (2 | $ctx_cons_flags) => "GetResponse", + (2 | $ctx_cons_flags) => "Response", (3 | $ctx_cons_flags) => "SetRequest", - (4 | $ctx_cons_flags) => "TrapRequest", - (5 | $ctx_cons_flags) => "GetbulkRequest", + (4 | $ctx_cons_flags) => "Trap", + (5 | $ctx_cons_flags) => "GetBulkRequest", (6 | $ctx_cons_flags) => "InformRequest", - (7 | $ctx_cons_flags) => "Trap2Request", + (7 | $ctx_cons_flags) => "SNMPv2-Trap", + (8 | $ctx_cons_flags) => "Report", }->{($result)}; return $seq_type_desc . "{\n" . $pretty_result . "\n}"; @@ -391,7 +405,7 @@ sub pretty_oid ($) { my (@oid); $result = ord (substr ($oid, 0, 1)); return error ("Object ID expected") unless $result == object_id_tag; - ($result, $oid) = decode_length (substr ($oid, 1)); + ($result, $oid) = decode_length ($oid, 1); return error ("inconsistent length in OID") unless $result == length $oid; @oid = (); $subid = ord (substr ($oid, 0, 1)); @@ -460,8 +474,7 @@ sub pretty_ip_address ($) { my ($length, $rest); return error ("IP Address tag (".snmp_ip_address_tag.") expected") unless ord (substr ($pdu, 0, 1)) == snmp_ip_address_tag; - $pdu = substr ($pdu, 1); - ($length,$pdu) = decode_length ($pdu); + ($length,$pdu) = decode_length ($pdu, 1); return error ("Length of IP address should be four") unless $length == 4; sprintf "%d.%d.%d.%d", unpack ("CCCC", $pdu); @@ -482,7 +495,7 @@ sub pretty_generic_sequence ($) { unless ($type == (&constructor_flag | &sequence_tag) # sequence || $type == (0 | $flags) #get_request || $type == (1 | $flags) #getnext_request - || $type == (2 | $flags) #get_response + || $type == (2 | $flags) #response || $type == (3 | $flags) #set_request || $type == (4 | $flags) #trap_request || $type == (5 | $flags) #getbulk_request @@ -497,7 +510,7 @@ sub pretty_generic_sequence ($) { # Cut away the first Tag and Length from $packet and then # init $rest with that. - (undef, $rest) = decode_length(substr $pdu, 1); + (undef, $rest) = decode_length ($pdu, 1); while($rest) { ($curelem,$rest) = decode_generic_tlv($rest); @@ -522,8 +535,7 @@ sub hex_string_of_type ($$) { my ($length); return error ("BER tag ".$wanted_type." expected") unless ord (substr ($pdu, 0, 1)) == $wanted_type; - $pdu = substr ($pdu, 1); - ($length,$pdu) = decode_length ($pdu); + ($length,$pdu) = decode_length ($pdu, 1); hex_string_aux ($pdu); } @@ -543,7 +555,7 @@ sub decode_oid ($) { my (@result); $result = ord (substr ($pdu, 0, 1)); return error ("Object ID expected") unless $result == object_id_tag; - ($result, $pdu_rest) = decode_length (substr ($pdu, 1)); + ($result, $pdu_rest) = decode_length ($pdu, 1); return error ("Short PDU") if $result > length $pdu_rest; @result = (substr ($pdu, 0, $result + (length ($pdu) - length ($pdu_rest))), @@ -558,7 +570,7 @@ sub decode_oid ($) { sub decode_generic_tlv ($) { my ($pdu) = @_; my (@result); - my ($elemlength,$pdu_rest) = decode_length (substr($pdu,1)); + my ($elemlength,$pdu_rest) = decode_length ($pdu, 1); @result = (# Extract the first element. substr ($pdu, 0, $elemlength + (length ($pdu) - length ($pdu_rest) @@ -610,8 +622,7 @@ sub decode_by_template_2 { $template, $template_index) unless (ord (substr ($pdu, 0, 1)) == $expected); - $pdu = substr ($pdu,1); - (($length,$pdu) = decode_length ($pdu)) + (($length,$pdu) = decode_length ($pdu, 1)) || return template_error ("cannot read length", $template, $template_index); return template_error ("Expected length $length, got ".length $pdu , @@ -641,7 +652,7 @@ sub decode_by_template_2 { $tag = ord (substr ($pdu, 0, 1)); return error ("Expected IP address, got tag ".$tag) unless $tag == snmp_ip_address_tag; - ($length, $pdu) = decode_length (substr ($pdu, 1)); + ($length, $pdu) = decode_length ($pdu, 1); return error ("Inconsistent length of InetAddress encoding") if $length > length $pdu; return template_error ("IP address must be four bytes long", @@ -723,7 +734,7 @@ sub decode_sequence ($) { $result = ord (substr ($pdu, 0, 1)); return error ("Sequence expected") unless $result == (sequence_tag | constructor_flag); - ($result, $pdu) = decode_length (substr ($pdu, 1)); + ($result, $pdu) = decode_length ($pdu, 1); return error ("Short PDU") if $result > length $pdu; @result = (substr ($pdu, 0, $result), substr ($pdu, $result)); @@ -751,8 +762,8 @@ my $have_math_bigint_p = 0; sub decode_intlike_s ($$) { my ($pdu, $signedp) = @_; my ($length,$result); - $length = ord (substr ($pdu, 1, 1)); - my $ptr = 2; + ($length,$pdu) = decode_length ($pdu, 1); + my $ptr = 0; $result = unpack ($signedp ? "c" : "C", substr ($pdu, $ptr++, 1)); if ($length > 5 || ($length == 5 && $result > 0)) { require 'Math/BigInt.pm' unless $have_math_bigint_p++; @@ -771,32 +782,75 @@ sub decode_string ($) { $result = ord (substr ($pdu, 0, 1)); return error ("Expected octet string, got tag ".$result) unless $result == octet_string_tag; - ($result, $pdu) = decode_length (substr ($pdu, 1)); + ($result, $pdu) = decode_length ($pdu, 1); return error ("Short PDU") if $result > length $pdu; return (substr ($pdu, 0, $result), substr ($pdu, $result)); } -sub decode_length ($) { +sub decode_length ($@) { my ($pdu) = shift; + my $index = shift || 0; my ($result); my (@result); - $result = ord (substr ($pdu, 0, 1)); + $result = ord (substr ($pdu, $index, 1)); if ($result & long_length) { if ($result == (long_length | 1)) { - @result = (ord (substr ($pdu, 1, 1)), substr ($pdu, 2)); + @result = (ord (substr ($pdu, $index+1, 1)), substr ($pdu, $index+2)); } elsif ($result == (long_length | 2)) { - @result = ((ord (substr ($pdu, 1, 1)) << 8) - + ord (substr ($pdu, 2, 1)), substr ($pdu, 3)); + @result = ((ord (substr ($pdu, $index+1, 1)) << 8) + + ord (substr ($pdu, $index+2, 1)), substr ($pdu, $index+3)); } else { return error ("Unsupported length"); } } else { - @result = ($result, substr ($pdu, 1)); + @result = ($result, substr ($pdu, $index+1)); } @result; } +# This takes a hashref that specifies functions to call when +# the specified value type is being printed. It returns the +# number of functions that were registered. +sub register_pretty_printer($) +{ + my ($h_ref) = shift; + my ($type, $val, $cnt); + + $cnt = 0; + while(($type, $val) = each %$h_ref) { + if (ref $val eq "CODE") { + $pretty_printer{$type} = $val; + $cnt++; + } + } + return($cnt); +} + +# This takes a hashref that specifies functions to call when +# the specified value type is being printed. It removes the +# functions from the list for the types specified. +# It returns the number of functions that were unregistered. +sub unregister_pretty_printer($) +{ + my ($h_ref) = shift; + my ($type, $val, $cnt); + + $cnt = 0; + while(($type, $val) = each %$h_ref) { + if ((exists ($pretty_printer{$type})) + && ($pretty_printer{$type} == $val)) { + if (exists($default_printer{$type})) { + $pretty_printer{$type} = $default_printer{$type}; + } else { + delete $pretty_printer{$type}; + } + $cnt++; + } + } + return($cnt); +} + #### OID prefix check ### encoded_oid_prefix_p OID1 OID2 @@ -813,8 +867,8 @@ sub encoded_oid_prefix_p ($$) { my ($subid1, $subid2); return error ("OID tag expected") unless ord (substr ($oid1, 0, 1)) == object_id_tag; return error ("OID tag expected") unless ord (substr ($oid2, 0, 1)) == object_id_tag; - ($l1,$oid1) = decode_length (substr ($oid1, 1)); - ($l2,$oid2) = decode_length (substr ($oid2, 1)); + ($l1,$oid1) = decode_length ($oid1, 1); + ($l2,$oid2) = decode_length ($oid2, 1); for ($i1 = 0, $i2 = 0; $i1 < $l1 && $i2 < $l2; ++$i1, ++$i2) { diff --git a/lib/SNMP_Session.pm b/lib/SNMP_Session.pm index 34bde10..8521a52 100644 --- a/lib/SNMP_Session.pm +++ b/lib/SNMP_Session.pm @@ -2,10 +2,11 @@ ###################################################################### ### SNMP Request/Response Handling ###################################################################### -### Copyright (c) 1995-2002, Simon Leinen. +### Copyright (c) 1995-2008, Simon Leinen. ### ### This program is free software; you can redistribute it under the -### "Artistic License" included in this distribution (file "Artistic"). +### "Artistic License 2.0" included in this distribution +### (file "Artistic"). ###################################################################### ### The abstract class SNMP_Session defines objects that can be used ### to communicate with SNMP entities. It has methods to send @@ -20,7 +21,7 @@ ### Contributions and fixes by: ### ### Matthew Trunnell -### Tobias Oetiker +### Tobias Oetiker ### Heine Peters ### Daniel L. Needles ### Mike Mitchell @@ -35,6 +36,10 @@ ### Valerio Bontempi : IPv6 support ### Lorenzo Colitti : IPv6 support ### Philippe Simonet : Export avoid... +### Luc Pauwels : use_16bit_request_ids +### Andrew Cornford-Matheson : inform +### Gerry Dalton : strict subs bug +### Mike Fischer : pass MSG_DONTWAIT to recv() ###################################################################### package SNMP_Session; @@ -45,9 +50,10 @@ use strict; use Exporter; use vars qw(@ISA $VERSION @EXPORT $errmsg $suppress_warnings - $default_avoid_negative_request_ids); + $default_avoid_negative_request_ids + $default_use_16bit_request_ids); use Socket; -use BER '0.95'; +use BER '1.05'; use Carp; sub map_table ($$$ ); @@ -56,7 +62,7 @@ sub map_table_start_end ($$$$$$); sub index_compare ($$); sub oid_diff ($$); -$VERSION = '0.98'; +$VERSION = '1.12'; @ISA = qw(Exporter); @@ -107,6 +113,13 @@ my $default_max_repetitions = 12; ### $SNMP_Session::default_avoid_negative_request_ids = 0; +### Default value for "use_16bit_request_ids". +### +### Set this to non-zero if you have agents that use 16bit request IDs, +### and don't forget to complain to your agent vendor. +### +$SNMP_Session::default_use_16bit_request_ids = 0; + ### Whether all SNMP_Session objects should share a single UDP socket. ### $SNMP_Session::recycle_socket = 0; @@ -120,16 +133,24 @@ $SNMP_Session::recycle_socket = 0; ### but this function is only available in recent versions of Socket.pm. my $ipv6_addr_len; +### Flags to be passed to recv() when non-blocking behavior is +### desired. On most POSIX-like systems this will be set to +### MSG_DONTWAIT, on other systems we leave it at zero. +### +my $dont_wait_flags; + BEGIN { $ipv6_addr_len = undef; $SNMP_Session::ipv6available = 0; + $dont_wait_flags = 0; - if (eval {require Socket6;} && - eval {require IO::Socket::INET6; IO::Socket::INET6->VERSION("1.26");}) { + if (eval {local $SIG{__DIE__};require Socket6;} && + eval {local $SIG{__DIE__};require IO::Socket::INET6; IO::Socket::INET6->VERSION("1.26");}) { import Socket6; $ipv6_addr_len = length(pack_sockaddr_in6(161, inet_pton(AF_INET6(), "::1"))); $SNMP_Session::ipv6available = 1; } + eval 'local $SIG{__DIE__};local $SIG{__WARN__};$dont_wait_flags = MSG_DONTWAIT();'; } my $the_socket; @@ -137,14 +158,14 @@ my $the_socket; $SNMP_Session::errmsg = ''; $SNMP_Session::suppress_warnings = 0; -sub get_request { 0 | context_flag }; -sub getnext_request { 1 | context_flag }; -sub get_response { 2 | context_flag }; -sub set_request { 3 | context_flag }; -sub trap_request { 4 | context_flag }; -sub getbulk_request { 5 | context_flag }; -sub inform_request { 6 | context_flag }; -sub trap2_request { 7 | context_flag }; +sub get_request { 0 | context_flag () }; +sub getnext_request { 1 | context_flag () }; +sub get_response { 2 | context_flag () }; +sub set_request { 3 | context_flag () }; +sub trap_request { 4 | context_flag () }; +sub getbulk_request { 5 | context_flag () }; +sub inform_request { 6 | context_flag () }; +sub trap2_request { 7 | context_flag () }; sub standard_udp_port { 161 }; @@ -180,10 +201,11 @@ sub encode_request_3 ($$$@) { local($_); $this->{request_id} = ($this->{request_id} == 0x7fffffff) - ? ($this->{avoid_negative_request_ids} - ? 0x00000000 - : -0x80000000) - : $this->{request_id}+1; + ? -0x80000000 : $this->{request_id}+1; + $this->{request_id} += 0x80000000 + if ($this->{avoid_negative_request_ids} && $this->{request_id} < 0); + $this->{request_id} &= 0x0000ffff + if ($this->{use_16bit_request_ids}); foreach $_ (@{$encoded_oids_or_pairs}) { if (ref ($_) eq 'ARRAY') { $_ = &encode_sequence ($_->[0], $_->[1]) @@ -267,14 +289,18 @@ sub decode_trap_request ($$) { $bindings) = decode_by_template ($trap, "%{%i%s%*{%O%A%i%i%u%{%@", trap_request); - if (! defined ($snmp_version)) { + if (!defined $snmp_version) { ($snmp_version, $community, $request_id, $error_status, $error_index, $bindings) = decode_by_template ($trap, "%{%i%s%*{%i%i%i%{%@", trap2_request); - return $this->error_return ("v2 trap request contained errorStatus/errorIndex " - .$error_status."/".$error_index) + if (!defined $snmp_version) { + ($snmp_version, $community,$request_id, $error_status, $error_index, $bindings) + = decode_by_template ($trap, "%{%i%s%*{%i%i%i%{%@", inform_request); + } + return $this->error_return ("v2 trap/inform request contained errorStatus/errorIndex " + .$error_status."/".$error_index) if defined $error_status && defined $error_index && ($error_status != 0 || $error_index != 0); } @@ -367,14 +393,13 @@ sub request_response_5 ($$$$$) { if (defined $this->{'capture_buffer'} and ref $this->{'capture_buffer'} eq 'ARRAY'); # - wait_for_response: ($nfound, $timeleft) = $this->wait_for_response($timeleft); if ($nfound > 0) { my($response_length); $response_length - = $this->receive_response_3 ($response_tag, $oids, $errorp); + = $this->receive_response_3 ($response_tag, $oids, $errorp, 1); if ($response_length) { # IlvJa # Add response pdu to capture_buffer @@ -384,8 +409,6 @@ sub request_response_5 ($$$$$) { if (defined $this->{'capture_buffer'} and ref $this->{'capture_buffer'} eq 'ARRAY'); # - - return $response_length; } elsif (defined ($response_length)) { goto wait_for_response; @@ -407,7 +430,6 @@ sub request_response_5 ($$$$$) { if (defined $this->{'capture_buffer'} and ref $this->{'capture_buffer'} eq 'ARRAY'); # - $this->error ("no response received"); } @@ -667,11 +689,8 @@ sub open { 'sockfamily' => $sockfamily, 'max_pdu_len' => $max_pdu_len, 'pdu_buffer' => '\0' x $max_pdu_len, - 'request_id' => - $SNMP_Session::default_avoid_negative_request_ids - ? (int (rand 0x8000) << 16) + int (rand 0x10000) - : (int (rand 0x10000) << 16) + int (rand 0x10000) - - 0x80000000, + 'request_id' => (int (rand 0x10000) << 16) + + int (rand 0x10000) - 0x80000000, 'timeout' => $default_timeout, 'retries' => $default_retries, 'backoff' => $default_backoff, @@ -683,6 +702,7 @@ sub open { 'lenient_source_address_matching' => 1, 'lenient_source_port_matching' => 1, 'avoid_negative_request_ids' => $SNMP_Session::default_avoid_negative_request_ids, + 'use_16bit_request_ids' => $SNMP_Session::default_use_16bit_request_ids, 'capture_buffer' => undef, }; } @@ -817,9 +837,11 @@ sub sa_equal_p ($$$) { } sub receive_response_3 { - my ($this, $response_tag, $oids, $errorp) = @_; + my ($this, $response_tag, $oids, $errorp, $dont_block_p) = @_; my ($remote_addr); - $remote_addr = recv ($this->sock,$this->{'pdu_buffer'},$this->max_pdu_len,0); + my $flags = 0; + $flags = $dont_wait_flags if defined $dont_block_p and $dont_block_p; + $remote_addr = recv ($this->sock,$this->{'pdu_buffer'},$this->max_pdu_len,$flags); return $this->error ("receiving response PDU: $!") unless defined $remote_addr; return $this->error ("short (".length $this->{'pdu_buffer'} @@ -1075,7 +1097,9 @@ sub map_table_start_end ($$$$$$) { } ($base_index = undef), last if !defined $min_index; - last if defined $end && index_compare ($min_index, $end) >= 0; + last + if defined $end + and SNMP_Session::index_compare ($min_index, $end) >= 0; &$mapfn ($min_index, @collected_values); ++$call_counter; $base_index = $min_index; @@ -1084,7 +1108,9 @@ sub map_table_start_end ($$$$$$) { return undef; } last if !defined $base_index; - last if defined $end and index_compare ($base_index, $end) >= 0; + last + if defined $end + and SNMP_Session::index_compare ($base_index, $end) >= 0; } $call_counter; } diff --git a/lib/SNMP_util.pm b/lib/SNMP_util.pm index d103dbc..c3ea43e 100644 --- a/lib/SNMP_util.pm +++ b/lib/SNMP_util.pm @@ -2,30 +2,33 @@ ###################################################################### ### SNMP_util -- SNMP utilities using SNMP_Session.pm and BER.pm ###################################################################### -### Copyright (c) 1998-2002, Mike Mitchell. +### Copyright (c) 1998-2007, Mike Mitchell. ### ### This program is free software; you can redistribute it under the -### "Artistic License" included in this distribution (file "Artistic"). +### "Artistic License 2.0" included in this distribution +### (file "Artistic"). ###################################################################### ### Created by: Mike Mitchell ### ### Contributions and fixes by: ### -### Tobias Oetiker : Basic layout +### Tobias Oetiker : Basic layout ### Simon Leinen : SNMP_session.pm/BER.pm ### Jeff Allen : length() of undefined value ### Johannes Demel : MIB file parse problem ### Simon Leinen : more OIDs from Interface MIB ### Jacques Supcik : Specify local IP, port -### Tobias Oetiker : HASH as first OID to set SNMP options +### Tobias Oetiker : HASH as first OID to set SNMP options ### Simon Leinen : 'undefined port' bug ### Daniel McDonald : request for getbulk support -### Laurent Girod : code for snmpwalkhash +### Laurent Girod : code for snmpwalkhash ### Ian Duplisse : MIB parsing suggestions ### Jakob Ilves : return_array_refs for snmpwalk() ### Valerio Bontempi : IPv6 support ### Lorenzo Colitti : IPv6 support ### Joerg Kummer : TimeTicks support in snmpset() +### Christopher J. Tengi : Gauge32 support in snmpset() +### Nicolai Petri : hashref passing for snmpwalkhash() ###################################################################### package SNMP_util; @@ -37,11 +40,11 @@ use vars qw(@ISA @EXPORT $VERSION); use Exporter; use Carp; -use BER "0.95"; -use SNMP_Session "0.97"; +use BER "1.02"; +use SNMP_Session "1.00"; use Socket; -$VERSION = '0.98'; +$VERSION = '1.12'; @ISA = qw(Exporter); @@ -339,6 +342,7 @@ $SNMP_util::Debug = 0; $SNMP_util::CacheFile = "OID_cache.txt"; $SNMP_util::CacheLoaded = 0; $SNMP_util::Return_array_refs = 0; +$SNMP_util::Return_hash_refs = 0; srand(time + $$); @@ -361,6 +365,7 @@ sub encode_oid_with_errmsg ($); sub Check_OID ($); sub snmpLoad_OID_Cache ($); sub snmpQueue_MIB_File (@); +sub MIB_fill_OID ($); sub version () { $VERSION; } @@ -382,7 +387,7 @@ sub snmpopen ($$$) { # We can't split on the : character because a numeric IPv6 # address contains a variable number of :'s my $opts; - if( ($host =~ /^(\[.*\]):(.*)$/) || ($host =~ /^(\[.*\])$/) ) { + if( ($host =~ /^(\[.*\]):(.*)$/) or ($host =~ /^(\[.*\])$/) ) { # Numeric IPv6 address between [] ($host, $opts) = ($1, $2); } else { @@ -390,28 +395,28 @@ sub snmpopen ($$$) { ($host, $opts) = split(':', $host, 2); } ($port, $timeout, $retries, $backoff, $version, $v4onlystr) = split(':', $opts, 6) - if(defined($opts) && (length $opts > 0) ); + if(defined($opts) and (length $opts > 0) ); - undef($version) if (defined($version) && length($version) <= 0); + undef($version) if (defined($version) and length($version) <= 0); $v4onlystr = "" unless defined $v4onlystr; $version = '1' unless defined $version; - if (defined($port) && ($port =~ /^([^!]*)!(.*)$/)) { + if (defined($port) and ($port =~ /^([^!]*)!(.*)$/)) { ($port, $lhost) = ($1, $2); $nlhost = $lhost; ($lhost, $lport) = ($1, $2) if ($lhost =~ /^(.*)!(.*)$/); - undef($lhost) if (defined($lhost) && (length($lhost) <= 0)); - undef($lport) if (defined($lport) && (length($lport) <= 0)); + undef($lhost) if (defined($lhost) and (length($lhost) <= 0)); + undef($lport) if (defined($lport) and (length($lport) <= 0)); } - undef($port) if (defined($port) && length($port) <= 0); - $port = 162 if ($type == 1 && !defined($port)); + undef($port) if (defined($port) and length($port) <= 0); + $port = 162 if ($type == 1 and !defined($port)); $nhost = "$community\@$host"; $nhost .= ":" . $port if (defined($port)); if ((!defined($SNMP_util::Session)) - || ($SNMP_util::Host ne $nhost) - || ($SNMP_util::Version ne $version) - || ($SNMP_util::LHost ne $nlhost) - || ($SNMP_util::IPv4only ne $v4onlystr)) { + or ($SNMP_util::Host ne $nhost) + or ($SNMP_util::Version ne $version) + or ($SNMP_util::LHost ne $nlhost) + or ($SNMP_util::IPv4only ne $v4onlystr)) { if (defined($SNMP_util::Session)) { $SNMP_util::Session->close(); undef $SNMP_util::Session; @@ -435,8 +440,9 @@ sub snmpopen ($$$) { foreach $type (keys %$opts) { if ($type eq 'return_array_refs') { $SNMP_util::Return_array_refs = $opts->{$type}; - } - else { + } elsif ($type eq 'return_hash_refs') { + $SNMP_util::Return_hash_refs = $opts->{$type}; + } else { if (exists $SNMP_util::Session->{$type}) { if ($type eq 'timeout') { $SNMP_util::Session->set_timeout($opts->{$type}); @@ -455,11 +461,11 @@ sub snmpopen ($$$) { } } $SNMP_util::Session->set_timeout($timeout) - if (defined($timeout) && (length($timeout) > 0)); + if (defined($timeout) and (length($timeout) > 0)); $SNMP_util::Session->set_retries($retries) - if (defined($retries) && (length($retries) > 0)); + if (defined($retries) and (length($retries) > 0)); $SNMP_util::Session->set_backoff($backoff) - if (defined($backoff) && (length($backoff) > 0)); + if (defined($backoff) and (length($backoff) > 0)); } return $SNMP_util::Session; } @@ -492,7 +498,7 @@ sub snmpget ($@) { my $tempo = pretty_print($value); push @retvals, $tempo; } - return(@retvals); + return wantarray ? @retvals : $retvals[0]; } $var = join(' ', @vars); carp "SNMPGET Problem for $var on $host\n" @@ -536,7 +542,7 @@ sub snmpgetnext ($@) { my $tempv = pretty_print($value); push @retvals, "$tempo:$tempv"; } - return (@retvals); + return wantarray ? @retvals : $retvals[0]; } else { $var = join(' ', @vars); carp "SNMPGETNEXT Problem for $var on $host\n" @@ -568,7 +574,7 @@ sub snmpwalk_flg ($$@) { my($got, @nnoid, $noid, $ok, $ix, @avars); my $session; my(%soid); - my(%done, %rethash); + my(%done, %rethash, $h_ref); $session = &snmpopen($host, 0, \@vars); if (!defined($session)) { @@ -577,6 +583,8 @@ sub snmpwalk_flg ($$@) { return undef; } + $h_ref = (ref $vars[$#vars] eq "HASH") ? pop(@vars) : \%rethash; + @enoid = toOID(@vars); return undef unless defined $enoid[0]; @@ -584,9 +592,9 @@ sub snmpwalk_flg ($$@) { # # Create/Refresh a reversed hash with oid -> name # - if (defined($hash_sub) && $RevNeeded) { - %revOIDS = reverse %SNMP_util::OIDS; - $RevNeeded = 0; + if (defined($hash_sub) and ($RevNeeded)) { + %revOIDS = reverse %SNMP_util::OIDS; + $RevNeeded = 0; } $got = 0; @@ -618,7 +626,7 @@ sub snmpwalk_flg ($$@) { } - while(($SNMP_util::Version ne '1' && $session->{'use_getbulk'}) + while(($SNMP_util::Version ne '1' and $session->{'use_getbulk'}) ? $session->getbulk_request_response(0, $session->default_max_repetitions(), @nnoid) @@ -638,13 +646,13 @@ sub snmpwalk_flg ($$@) { $ok = 0; my $tempo = pretty_print($oid); $noid = $avars[$ix]; # IlvJa - if ($tempo =~ /^$noid\./ || $tempo eq $noid ) { + if ($tempo =~ /^$noid\./ or $tempo eq $noid ) { $ok = 1; $upoid = $noid; } else { # IlvJa # - # The walk for variable $var[$ix] has been finished as + # The walk for variable $vars[$ix] has been finished as # $nnoid[$ix] no longer is in the $avar[$ix] OID tree. # So we exclude this variable from further requests. @@ -655,7 +663,16 @@ sub snmpwalk_flg ($$@) { if ($ok) { my $tmp = encode_oid_with_errmsg ($tempo); return undef unless defined $tmp; - next if (exists($done{$tmp})); # GIL + if (exists($done{$tmp})) { # GIL, Ilvja + # + # We've detected a loop for $nnoid[$ix], so mark it as finished. + # Exclude this variable from further requests. + # + $avars[$ix] = ""; + $nnoid[$ix] = ""; + $retvaltmprefs[$ix] = undef if $SNMP_util::Return_array_refs; + next; + } $nnoid[$ix] = $tmp; # Keep on walking. (IlvJa) my $tempv = pretty_print($value); if (defined($hash_sub)) { @@ -664,40 +681,40 @@ sub snmpwalk_flg ($$@) { # my $inst = ""; my $upo = $upoid; - while (!exists($revOIDS{$upo}) && length($upo)) { + while (!exists($revOIDS{$upo}) and length($upo)) { $upo =~ s/(\.\d+?)$//; - if (defined($1) && length($1)) { + if (defined($1) and length($1)) { $inst = $1 . $inst; } else { $upo = ""; last; } } - if (length($upo) && exists($revOIDS{$upo})) { + if (length($upo) and exists($revOIDS{$upo})) { $upo = $revOIDS{$upo} . $inst; } else { $upo = $upoid; } $inst = ""; - while (!exists($revOIDS{$tempo}) && length($tempo)) { + while (!exists($revOIDS{$tempo}) and length($tempo)) { $tempo =~ s/(\.\d+?)$//; - if (defined($1) && length($1)) { + if (defined($1) and length($1)) { $inst = $1 . $inst; } else { $tempo = ""; last; } } - if (length($tempo) && exists($revOIDS{$tempo})) { - $tempo = $revOIDS{$tempo} . $inst; + if (length($tempo) and exists($revOIDS{$tempo})) { + $var = $revOIDS{$tempo}; } else { - $tempo = pretty_print($oid); + $var = pretty_print($oid); } # # call hash_sub # - &$hash_sub(\%rethash, $host, $revOIDS{$tempo}, $tempo, $inst, + &$hash_sub($h_ref, $host, $var, $tempo, $inst, $tempv, $upo); } else { if ($SNMP_util::Return_array_refs) { @@ -728,7 +745,8 @@ sub snmpwalk_flg ($$@) { } if ($got) { if (defined($hash_sub)) { - return (%rethash) + return ($h_ref) if ($SNMP_util::Return_hash_refs); + return (%$h_ref); } else { return (@retvals); } @@ -746,7 +764,7 @@ sub snmpwalk_flg ($$@) { sub snmpset($@) { my($host, @vars) = @_; my(@enoid, $response, $bindings, $binding); - my($oid, @retvals, $type, $value); + my($oid, @retvals, $type, $value, $val); my $session; $session = &snmpopen($host, 0, \@vars); @@ -760,27 +778,62 @@ sub snmpset($@) { ($oid) = toOID((shift @vars)); $type = shift @vars; $value = shift @vars; - if ($type =~ /string/i) { - $value = encode_string($value); - push @enoid, [$oid,$value]; - } elsif ($type =~ /ipaddr/i) { - $value = encode_ip_address($value); - push @enoid, [$oid,$value]; - } elsif ($type =~ /int/i) { - $value = encode_int($value); - push @enoid, [$oid,$value]; - } elsif ($type =~ /oid/i) { - my $tmp = encode_oid_with_errmsg($value); - return undef unless defined $tmp; - push @enoid, [$oid,$tmp]; - } elsif ($type =~ /timeticks/i) { - $value = encode_timeticks($value); - push @enoid, [$oid,$value]; + $type =~ tr/A-Z/a-z/; + if ($type eq "int") { + $val = encode_int($value); + } elsif ($type eq "integer") { + $val = encode_int($value); + } elsif ($type eq "string") { + $val = encode_string($value); + } elsif ($type eq "octetstring") { + $val = encode_string($value); + } elsif ($type eq "octet string") { + $val = encode_string($value); + } elsif ($type eq "oid") { + $val = encode_oid_with_errmsg($value); + } elsif ($type eq "object id") { + $val = encode_oid_with_errmsg($value); + } elsif ($type eq "object identifier") { + $val = encode_oid_with_errmsg($value); + } elsif ($type eq "ipaddr") { + $val = encode_ip_address($value); + } elsif ($type eq "ip address") { + $val = encode_ip_address($value); + } elsif ($type eq "timeticks") { + $val = encode_timeticks($value); + } elsif ($type eq "uint") { + $val = encode_uinteger32($value); + } elsif ($type eq "uinteger") { + $val = encode_uinteger32($value); + } elsif ($type eq "uinteger32") { + $val = encode_uinteger32($value); + } elsif ($type eq "unsigned int") { + $val = encode_uinteger32($value); + } elsif ($type eq "unsigned integer") { + $val = encode_uinteger32($value); + } elsif ($type eq "unsigned integer32") { + $val = encode_uinteger32($value); + } elsif ($type eq "counter") { + $val = encode_counter32($value); + } elsif ($type eq "counter32") { + $val = encode_counter32($value); + } elsif ($type eq "counter64") { + $val = encode_counter64($value); + } elsif ($type eq "gauge") { + $val = encode_gauge32($value); + } elsif ($type eq "gauge32") { + $val = encode_gauge32($value); } else { carp "unknown SNMP type: $type\n" unless ($SNMP_Session::suppress_warnings > 1); return undef; } + if (!defined($val)) { + carp "SNMP type $type value $value didn't encode properly\n" + unless ($SNMP_Session::suppress_warnings > 1); + return undef; + } + push @enoid, [$oid,$val]; } return undef unless defined $enoid[0]; if ($session->set_request_response(@enoid)) { @@ -792,7 +845,7 @@ sub snmpset($@) { my $tempo = pretty_print($value); push @retvals, $tempo; } - return (@retvals); + return wantarray ? @retvals : $retvals[0]; } return undef; } @@ -945,7 +998,7 @@ sub toOID(@) { undef @retvar; foreach $var (@vars) { ($oid, $tmp) = &Check_OID($var); - if (!$oid && $SNMP_util::CacheLoaded == 0) { + if (!$oid and $SNMP_util::CacheLoaded == 0) { $tmp = $SNMP_Session::suppress_warnings; $SNMP_Session::suppress_warnings = 1000; @@ -956,7 +1009,7 @@ sub toOID(@) { ($oid, $tmp) = &Check_OID($var); } - while (!$oid && $#SNMP_util::MIB_Files >= 0) { + while (!$oid and $#SNMP_util::MIB_Files >= 0) { $tmp = $SNMP_Session::suppress_warnings; $SNMP_Session::suppress_warnings = 1000; @@ -996,15 +1049,14 @@ sub toOID(@) { sub snmpmapOID(@) { my(@vars) = @_; - my($oid, $txt, $ind); + my($oid, $txt); - $ind = 0; - while($ind <= $#vars) { - $txt = $vars[$ind++]; - next unless($txt =~ /^(([a-zA-Z][a-zA-Z\d\-]*\.)*([a-zA-Z][a-zA-Z\d\-]*))$/); + while($#vars >= 0) { + $txt = shift @vars; + $oid = shift @vars; - $oid = $vars[$ind++]; - next unless($oid =~ /^((\d+.)*\d+)$/); + next unless($txt =~ /^[a-zA-Z][\w\-]*(\.[a-zA-Z][\w\-])*$/); + next unless($oid =~ /^\d+(\.\d+)*$/); $SNMP_util::OIDS{$txt} = $oid; $RevNeeded = 1; @@ -1033,13 +1085,23 @@ sub snmpLoad_OID_Cache ($) { } while() { - s/#.*//; - s/--.*--//g; - s/--.*//; + s/#.*//; # '#' starts a comment + s/--.*--//g; # comment delimited by '--', like MIBs + s/--.*//; # comment started by '--' next if (/^$/); - next unless (/\s/); - chop; + next unless (/\s/); # must have whitespace as separator + chomp; ($txt, $oid) = split(' ', $_, 2); + $txt = $1 if ($txt =~ /^[\'\"](.*)[\'\"]/); + $oid = $1 if ($oid =~ /^[\'\"](.*)[\'\"]/); + if (($txt =~ /^\.?\d+(\.\d+)*\.?$/) + and ($oid !~ /^\.?\d+(\.\d+)*\.?$/)) { + my($a) = $oid; + $oid = $txt; + $txt = $a; + } + $oid =~ s/^\.//; + $oid =~ s/\.$//; &snmpmapOID($txt, $oid); } close(CACHE); @@ -1055,12 +1117,12 @@ sub Check_OID ($) { my($var) = @_; my($tmp, $tmpv, $oid); - if ($var =~ /^(([a-zA-Z][a-zA-Z\d\-]*\.)*([a-zA-Z][a-zA-Z\d\-]*))/) + if ($var =~ /^[a-zA-Z][\w\-]*(\.[a-zA-Z][\w\-]*)*/) { $tmp = $&; $tmpv = $tmp; for (;;) { - last if defined($SNMP_util::OIDS{$tmpv}); + last if exists($SNMP_util::OIDS{$tmpv}); last if !($tmpv =~ s/^[^\.]*\.//); } $oid = $SNMP_util::OIDS{$tmpv}; @@ -1093,19 +1155,8 @@ sub snmpQueue_MIB_File (@) { # sub snmpMIB_to_OID ($) { my($arg) = @_; - my($quote, $buf, $var, $code, $val, $tmp, $tmpv, $strt); - my($ret, $pass, $pos, $need2pass, $cnt, %prev); - my(%Link) = ( - 'org' => 'iso', - 'dod' => 'org', - 'internet' => 'dod', - 'directory' => 'internet', - 'mgmt' => 'internet', - 'mib-2' => 'mgmt', - 'experimental' => 'internet', - 'private' => 'internet', - 'enterprises' => 'private', - ); + my($cnt, $quote, $buf, %tOIDs, $tgot); + my($var, @parts, $strt, $indx, $ind, $val); if (!open(MIB, $arg)) { carp "snmpMIB_to_OID: Can't open $arg: $!" @@ -1113,145 +1164,148 @@ sub snmpMIB_to_OID ($) { return -1; } print "snmpMIB_to_OID: loading $arg\n" if $SNMP_util::Debug; - $ret = 0; - $pass = 0; - $need2pass = 1; $cnt = 0; - $pos = tell(MIB); - while($need2pass) { - while() { - s/--.*--//g; # throw away comments (-- anything --) - s/--.*//; # throw away comments (-- anything EOL) - if ($quote) { - next unless /"/; - $quote = 0; - } - chop; -# -# $buf = "$buf $_"; -# Previous line removed (and following replacement) -# suggested by Brian Reichert, reichert@numachi.com -# - $buf .= ' ' . $_; - $buf =~ s/\s+/ /g; - - if ($buf =~ / DEFINITIONS ::= BEGIN/) { - if ($pass == 0 && $need2pass) { - seek(MIB, $pos, 0); - $buf = ""; - $pass = 1; - $need2pass = 0; - $cnt = 0; - next; - } - $need2pass = 0; - $pass = 0; - $pos = tell(MIB); - undef %Link; - undef %prev; - %Link = ( - 'org' => 'iso', - 'dod' => 'org', - 'internet' => 'dod', - 'directory' => 'internet', - 'mgmt' => 'internet', - 'mib-2' => 'mgmt', - 'experimental' => 'internet', - 'private' => 'internet', - 'enterprises' => 'private', - ); - $buf = ""; - next; - } + $quote = 0; + $tgot = 0; + $buf = ''; + while() { + if ($quote) { + next unless /"/; + $quote = 0; + } else { + s/--.*--//g; # throw away comments (-- anything --) + s/^\s*--.*//; # throw away comments at start of line + } + chomp; + + $buf .= ' ' . $_; - $buf =~ s/OBJECT-TYPE/OBJECT IDENTIFIER/; - $buf =~ s/OBJECT-IDENTITY/OBJECT IDENTIFIER/; - $buf =~ s/OBJECT-GROUP/OBJECT IDENTIFIER/; - $buf =~ s/MODULE-IDENTITY/OBJECT IDENTIFIER/; - $buf =~ s/ IMPORTS .*\;//; - $buf =~ s/ SEQUENCE {.*}//; - $buf =~ s/ SYNTAX .*//; - $buf =~ s/ [\w-]+ ::= OBJECT IDENTIFIER//; - $buf =~ s/ OBJECT IDENTIFIER .* ::= {/ OBJECT IDENTIFIER ::= {/; - $buf =~ s/".*"//; - if ($buf =~ /"/) { - $quote = 1; + $buf =~ s/"[^"]*"//g; + if ($buf =~ /"/) { + $quote = 1; + next; + } + $buf =~ s/--.*--//g; # throw away comments (-- anything --) + $buf =~ s/--.*//; # throw away comments (-- anything EOL) + $buf =~ s/\s+/ /g; + if ($buf =~ /DEFINITIONS *::= *BEGIN/) { + $cnt += MIB_fill_OID(\%tOIDs) if ($tgot); + $buf = ''; + %tOIDs = (); + $tgot = 0; + next; + } + $buf =~ s/OBJECT-TYPE/OBJECT IDENTIFIER/; + $buf =~ s/OBJECT-IDENTITY/OBJECT IDENTIFIER/; + $buf =~ s/OBJECT-GROUP/OBJECT IDENTIFIER/; + $buf =~ s/MODULE-IDENTITY/OBJECT IDENTIFIER/; + $buf =~ s/ IMPORTS .*\;//; + $buf =~ s/ SEQUENCE *{.*}//; + $buf =~ s/ SYNTAX .*//; + $buf =~ s/ [\w\-]+ *::= *OBJECT IDENTIFIER//; + $buf =~ s/ OBJECT IDENTIFIER.*::= *{/ OBJECT IDENTIFIER ::= {/; + + if ($buf =~ / ([\w\-]+) OBJECT IDENTIFIER *::= *{([^}]+)}/) { + $var = $1; + $buf = $2; + $buf =~ s/ +$//; + $buf =~ s/\s+\(/\(/g; # remove spacing around '(' + $buf =~ s/\(\s+/\(/g; + $buf =~ s/\s+\)/\)/g; # remove spacing before ')' + @parts = split(' ', $buf); + $strt = ''; + foreach $indx (@parts) { + if ($indx =~ /([\w\-]+)\((\d+)\)/) { + $ind = $1; + $val = $2; + if (exists($tOIDs{$strt})) { + $tOIDs{$ind} = $tOIDs{$strt} . '.' . $val; + } elsif ($strt ne '') { + $tOIDs{$ind} = "${strt}.${val}"; + } else { + $tOIDs{$ind} = $val; + } + $strt = $ind; + $tgot = 1; + } elsif ($indx =~ /^\d+$/) { + if (exists($tOIDs{$strt})) { + $tOIDs{$var} = $tOIDs{$strt} . '.' . $indx; + } else { + $tOIDs{$var} = "${strt}.${indx}"; + } + $tgot = 1; + } else { + $strt = $indx; + } } + $buf = ''; + } + } + $cnt += MIB_fill_OID(\%tOIDs) if ($tgot); + $RevNeeded = 1 if ($cnt > 0); + return $cnt; +} - if ($buf =~ / ([\w\-]+) OBJECT IDENTIFIER ::= {([^}]+)}/) { - $var = $1; - $buf = $2; - undef $val; - $buf =~ s/ +$//; - ($code, $val) = split(' ', $buf, 2); +# +# Fill the OIDS hash with results from the MIB parsing +# +sub MIB_fill_OID ($) +{ + my($href) = @_; + my($cnt, $changed, @del, $var, $val, @parts, $indx); + my(%seen); - if (!defined($val) || (length($val) <= 0)) { - $SNMP_util::OIDS{$var} = $code; - $cnt++; - print "'$var' => '$code'\n" if $SNMP_util::Debug; + $cnt = 0; + do { + $changed = 0; + @del = (); + foreach $var (keys %$href) { + $val = $href->{$var}; + @parts = split('\.', $val); + $val = ''; + foreach $indx (@parts) { + if ($indx =~ /^\d+$/) { + $val .= '.' . $indx; } else { - $strt = $code; - while($val =~ / /) { - ($tmp, $val) = split(' ', $val, 2); - if ($tmp =~ /([\w\-]+)\((\d+)\)/) { - $tmp = $1; - $tmpv = "$SNMP_util::OIDS{$strt}.$2"; - $Link{$tmp} = $strt; - if (!defined($prev{$tmp}) && defined($SNMP_util::OIDS{$tmp})) { - if ($tmpv ne $SNMP_util::OIDS{$tmp}) { - $strt = "$strt.$tmp"; - $SNMP_util::OIDS{$strt} = $tmpv; - $cnt++; - } - } else { - $prev{$tmp} = 1; - $SNMP_util::OIDS{$tmp} = $tmpv; - $cnt++; - $strt = $tmp; - } - } - } - - if (!defined($SNMP_util::OIDS{$strt})) { - if ($pass) { - carp "snmpMIB_to_OID: $arg: \"$strt\" prefix unknown, load the parent MIB first.\n" - unless ($SNMP_Session::suppress_warnings > 1); - } else { - $need2pass = 1; - } - } - $Link{$var} = $strt; - $val = "$SNMP_util::OIDS{$strt}.$val"; - if (!defined($prev{$var}) && defined($SNMP_util::OIDS{$var})) { - if ($val ne $SNMP_util::OIDS{$var}) { - $var = "$strt.$var"; - } + if (exists($SNMP_util::OIDS{$indx})) { + $val = $SNMP_util::OIDS{$indx}; + } else { + $val .= '.' . $indx; } - + } + } + if ($val =~ /^[\d\.]+$/) { + $val =~ s/^\.//; + if (!exists($SNMP_util::OIDS{$var}) + || (length($val) > length($SNMP_util::OIDS{$var}))) { $SNMP_util::OIDS{$var} = $val; - $prev{$var} = 1; - $cnt++; - print "'$var' => '$val'\n" if $SNMP_util::Debug; + $changed = 1; + $cnt++; } - undef $buf; + push @del, $var; } } - if ($pass == 0 && $need2pass) { - seek(MIB, $pos, 0); - $buf = ""; - $pass = 1; - $cnt = 0; - } else { - $ret += $cnt; - $need2pass = 0; + foreach $var (@del) { + delete $href->{$var}; } + } while($changed); + + $Carp::CarpLevel++; + foreach $var (sort keys %$href) { + $val = $href->{$var}; + $val =~ s/\..*//; + next if (exists($seen{$val})); + $seen{$val} = 1; + $seen{$var} = 1; + carp "snmpMIB_to_OID: prefix \"$val\" unknown, load the parent MIB first.\n" + unless ($SNMP_Session::suppress_warnings > 1); } - close(MIB); - $RevNeeded = 1; - return $ret; + $Carp::CarpLevel--; + return $cnt; } + sub encode_oid_with_errmsg ($) { my ($oid) = @_; my $tmp = encode_oid(split(/\./, $oid)); -- cgit v1.2.3-24-g4f1b