Advertisement
Guest User

Untitled

a guest
Jun 5th, 2017
598
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 27.85 KB | None | 0 0
  1. # -*- mode: Perl -*-
  2. ######################################################################
  3. ### SNMP Request/Response Handling
  4. ######################################################################
  5. ### Copyright (c) 1995-2000, Simon Leinen.
  6. ###
  7. ### This program is free software; you can redistribute it under the
  8. ### "Artistic License" included in this distribution (file "Artistic").
  9. ######################################################################
  10. ### The abstract class SNMP_Session defines objects that can be used
  11. ### to communicate with SNMP entities.  It has methods to send
  12. ### requests to and receive responses from an agent.
  13. ###
  14. ### Two instantiable subclasses are defined:
  15. ### SNMPv1_Session implements SNMPv1 (RFC 1157) functionality
  16. ### SNMPv2c_Session implements community-based SNMPv2.
  17. ######################################################################
  18. ### Created by:  Simon Leinen  <simon@switch.ch>
  19. ###
  20. ### Contributions and fixes by:
  21. ###
  22. ### Matthew Trunnell <matter@media.mit.edu>
  23. ### Tobias Oetiker <oetiker@ee.ethz.ch>
  24. ### Heine Peters <peters@dkrz.de>
  25. ### Daniel L. Needles <dan_needles@INS.COM>
  26. ### Mike Mitchell <mcm@unx.sas.com>
  27. ### Clinton Wong <clintdw@netcom.com>
  28. ### Alan Nichols <Alan.Nichols@Ebay.Sun.COM>
  29. ### Mike McCauley <mikem@open.com.au>
  30. ### Andrew W. Elble <elble@icculus.nsg.nwu.edu>
  31. ### Brett T Warden <wardenb@eluminant.com>: pretty UInteger32
  32. ### Michael Deegan <michael@cnspc18.murdoch.edu.au>
  33. ### Sergio Macedo <macedo@tmp.com.br>
  34. ######################################################################
  35.  
  36. package SNMP_Session;      
  37.  
  38. require 5.002;
  39.  
  40. use strict qw(vars subs);   # cannot use strict subs here
  41.                 # because of the way we use
  42.                 # generated file handles
  43. use Exporter;
  44. use vars qw(@ISA $VERSION @EXPORT $errmsg $suppress_warnings);
  45. use Socket;
  46. use BER;
  47. use Carp;
  48.  
  49. sub map_table ($$$ );
  50. sub map_table_4 ($$$$);
  51. sub map_table_start_end ($$$$$$);
  52. sub index_compare ($$);
  53. sub oid_diff ($$);
  54.  
  55. $VERSION = '0.89';
  56.  
  57. @ISA = qw(Exporter);
  58.  
  59. @EXPORT = qw(errmsg suppress_warnings index_compare oid_diff recycle_socket);
  60.  
  61. my $default_debug = 0;
  62.  
  63. ### Default initial timeout (in seconds) waiting for a response PDU
  64. ### after a request is sent.  Note that when a request is retried, the
  65. ### timeout is increased by BACKOFF (see below).
  66. ###
  67. my $default_timeout = 2.0;
  68.  
  69. ### Default number of attempts to get a reply for an SNMP request.  If
  70. ### no response is received after TIMEOUT seconds, the request is
  71. ### resent and a new response awaited with a longer timeout (see the
  72. ### documentation on BACKOFF below).  The "retries" value should be at
  73. ### least 1, because the first attempt counts, too (the name "retries"
  74. ### is confusing, sorry for that).
  75. ###
  76. my $default_retries = 5;
  77.  
  78. ### Default backoff factor for SNMP_Session objects.  This factor is
  79. ### used to increase the TIMEOUT every time an SNMP request is
  80. ### retried.
  81. ###
  82. my $default_backoff = 1.0;
  83.  
  84. ### Default value for maxRepetitions.  This specifies how many table
  85. ### rows are requested in getBulk requests.  Used when walking tables
  86. ### using getBulk (only available in SNMPv2(c) and later).  If this is
  87. ### too small, then a table walk will need unnecessarily many
  88. ### request/response exchanges.  If it is too big, the agent may
  89. ### compute many variables after the end of the table.  It is
  90. ### recommended to set this explicitly for each table walk by using
  91. ### map_table_4().
  92. ###
  93. my $default_max_repetitions = 12;
  94.  
  95. ### Whether all SNMP_Session objects should share a single UDP socket.
  96. ###
  97. $SNMP_Session::recycle_socket = 0;
  98.  
  99. my $the_socket;
  100.  
  101. $SNMP_Session::errmsg = '';
  102. $SNMP_Session::suppress_warnings = 0;
  103.  
  104. sub get_request      { 0 | context_flag };
  105. sub getnext_request  { 1 | context_flag };
  106. sub get_response     { 2 | context_flag };
  107. sub set_request      { 3 | context_flag };
  108. sub trap_request     { 4 | context_flag };
  109. sub getbulk_request  { 5 | context_flag };
  110. sub inform_request   { 6 | context_flag };
  111. sub trap2_request    { 7 | context_flag };
  112.  
  113. sub standard_udp_port { 161 };
  114.  
  115. sub open
  116. {
  117.     return SNMPv1_Session::open (@_);
  118. }
  119.  
  120. sub timeout { $_[0]->{timeout} }
  121. sub retries { $_[0]->{retries} }
  122. sub backoff { $_[0]->{backoff} }
  123. sub set_timeout {
  124.     my ($session, $timeout) = @_;
  125.     croak ("timeout ($timeout) must be a positive number") unless $timeout > 0.0;
  126.     $session->{'timeout'} = $timeout;
  127. }
  128. sub set_retries {
  129.     my ($session, $retries) = @_;
  130.     croak ("retries ($retries) must be a non-negative integer")
  131.     unless $retries == int ($retries) && $retries >= 0;
  132.     $session->{'retries'} = $retries;
  133. }
  134. sub set_backoff {
  135.     my ($session, $backoff) = @_;
  136.     croak ("backoff ($backoff) must be a number >= 1.0")
  137.     unless $backoff == int ($backoff) && $backoff >= 1.0;
  138.     $session->{'backoff'} = $backoff;
  139. }
  140.  
  141. sub encode_request_3 ($$$@) {
  142.     my($this, $reqtype, $encoded_oids_or_pairs, $i1, $i2) = @_;
  143.     my($request);
  144.     local($_);
  145.  
  146.     $this->{request_id} = ($this->{request_id} == 0x7fffffff)
  147.     ? -0x80000000 : $this->{request_id}+1;
  148.     foreach $_ (@{$encoded_oids_or_pairs}) {
  149.       if (ref ($_) eq 'ARRAY') {
  150.     $_ = &encode_sequence ($_->[0], $_->[1])
  151.       || return $this->ber_error ("encoding pair");
  152.       } else {
  153.     $_ = &encode_sequence ($_, encode_null())
  154.       || return $this->ber_error ("encoding value/null pair");
  155.       }
  156.     }
  157.     $request = encode_tagged_sequence
  158.     ($reqtype,
  159.      encode_int ($this->{request_id}),
  160.      defined $i1 ? encode_int ($i1) : encode_int_0,
  161.      defined $i2 ? encode_int ($i2) : encode_int_0,
  162.      encode_sequence (@{$encoded_oids_or_pairs}))
  163.       || return $this->ber_error ("encoding request PDU");
  164.     return $this->wrap_request ($request);
  165. }
  166.  
  167. sub encode_get_request {
  168.     my($this, @oids) = @_;
  169.     return encode_request_3 ($this, get_request, \@oids);
  170. }
  171.  
  172. sub encode_getnext_request {
  173.     my($this, @oids) = @_;
  174.     return encode_request_3 ($this, getnext_request, \@oids);
  175. }
  176.  
  177. sub encode_getbulk_request {
  178.     my($this, $non_repeaters, $max_repetitions, @oids) = @_;
  179.     return encode_request_3 ($this, getbulk_request, \@oids,
  180.                  $non_repeaters, $max_repetitions);
  181. }
  182.  
  183. sub encode_set_request {
  184.     my($this, @encoded_pairs) = @_;
  185.     return encode_request_3 ($this, set_request, \@encoded_pairs);
  186. }
  187.  
  188. sub encode_trap_request ($$$$$$@) {
  189.     my($this, $ent, $agent, $gen, $spec, $dt, @pairs) = @_;
  190.     my($request);
  191.     local($_);
  192.  
  193.     foreach $_ (@pairs) {
  194.       if (ref ($_) eq 'ARRAY') {
  195.     $_ = &encode_sequence ($_->[0], $_->[1])
  196.       || return $this->ber_error ("encoding pair");
  197.       } else {
  198.     $_ = &encode_sequence ($_, encode_null())
  199.       || return $this->ber_error ("encoding value/null pair");
  200.       }
  201.     }
  202.     $request = encode_tagged_sequence
  203.     (trap_request, $ent, $agent, $gen, $spec, $dt, encode_sequence (@pairs))
  204.       || return $this->ber_error ("encoding trap PDU");
  205.     return $this->wrap_request ($request);
  206. }
  207.  
  208. sub encode_v2_trap_request ($@) {
  209.     my($this, @pairs) = @_;
  210.  
  211.     return encode_request_3($this, trap2_request, \@pairs);
  212. }
  213.  
  214. sub decode_get_response {
  215.     my($this, $response) = @_;
  216.     my @rest;
  217.     @{$this->{'unwrapped'}};
  218. }
  219.  
  220. sub decode_trap_request ($$) {
  221.     my ($this, $trap) = @_;
  222.     my ($snmp_version, $community, $ent, $agent, $gen, $spec, $dt,
  223.     $request_id, $error_status, $error_index,
  224.     $bindings);
  225.     ($snmp_version, $community,
  226.      $ent, $agent,
  227.      $gen, $spec, $dt,
  228.      $bindings)
  229.     = decode_by_template ($trap, "%{%i%s%*{%O%A%i%i%u%{%@",
  230.                 trap_request);
  231.     if (! defined ($snmp_version)) {
  232.     ($snmp_version, $community,
  233.      $request_id, $error_status, $error_index,
  234.      $bindings)
  235.         = decode_by_template ($trap, "%{%i%s%*{%i%i%i%{%@",
  236.                   trap2_request);
  237.     error_return ("v2 trap request contained errorStatus/errorIndex "
  238.               .$error_status."/".$error_index)
  239.         if $error_status != 0 || $error_index != 0;
  240.     }
  241.     if (!defined $snmp_version) {
  242.     error_return ("BER error decoding trap:\n  ".$BER::errmsg);
  243.     }
  244.     return ($community, $ent, $agent, $gen, $spec, $dt, $bindings);
  245. }
  246.  
  247. sub wait_for_response {
  248.     my($this) = shift;
  249.     my($timeout) = shift || 10.0;
  250.     my($rin,$win,$ein) = ('','','');
  251.     my($rout,$wout,$eout);
  252.     vec($rin,$this->sockfileno,1) = 1;
  253.     select($rout=$rin,$wout=$win,$eout=$ein,$timeout);
  254. }
  255.  
  256. sub get_request_response ($@) {
  257.     my($this, @oids) = @_;
  258.     return $this->request_response_5 ($this->encode_get_request (@oids),
  259.                       get_response, \@oids, 1);
  260. }
  261.  
  262. sub set_request_response ($@) {
  263.     my($this, @pairs) = @_;
  264.     return $this->request_response_5 ($this->encode_set_request (@pairs),
  265.                       get_response, \@pairs, 1);
  266. }
  267.  
  268. sub getnext_request_response ($@) {
  269.     my($this,@oids) = @_;
  270.     return $this->request_response_5 ($this->encode_getnext_request (@oids),
  271.                       get_response, \@oids, 1);
  272. }
  273.  
  274. sub getbulk_request_response ($$$@) {
  275.     my($this,$non_repeaters,$max_repetitions,@oids) = @_;
  276.     return $this->request_response_5
  277.     ($this->encode_getbulk_request ($non_repeaters,$max_repetitions,@oids),
  278.      get_response, \@oids, 1);
  279. }
  280.  
  281. sub trap_request_send ($$$$$$@) {
  282.     my($this, $ent, $agent, $gen, $spec, $dt, @pairs) = @_;
  283.     my($req);
  284.  
  285.     $req = $this->encode_trap_request ($ent, $agent, $gen, $spec, $dt, @pairs);
  286.     ## Encoding may have returned an error.
  287.     return undef unless defined $req;
  288.     $this->send_query($req)
  289.     || return $this->error ("send_trap: $!");
  290.     return 1;
  291. }
  292.  
  293. sub v2_trap_request_send ($$$@) {
  294.     my($this, $trap_oid, $dt, @pairs) = @_;
  295.     my @sysUptime_OID = ( 1,3,6,1,2,1,1,3 );
  296.     my @snmpTrapOID_OID = ( 1,3,6,1,6,3,1,1,4,1 );
  297.     my($req);
  298.  
  299.     unshift @pairs, [encode_oid (@snmpTrapOID_OID,0),
  300.              encode_oid (@{$trap_oid})];
  301.     unshift @pairs, [encode_oid (@sysUptime_OID,0),
  302.              encode_timeticks ($dt)];
  303.     $req = $this->encode_v2_trap_request (@pairs);
  304.     ## Encoding may have returned an error.
  305.     return undef unless defined $req;
  306.     $this->send_query($req)
  307.     || return $this->error ("send_trap: $!");
  308.     return 1;
  309. }
  310.  
  311. sub request_response_5 ($$$$$) {
  312.     my ($this, $req, $response_tag, $oids, $errorp) = @_;
  313.     my $retries = $this->retries;
  314.     my $timeout = $this->timeout;
  315.     my ($nfound, $timeleft);
  316.  
  317.     ## Encoding may have returned an error.
  318.     return undef unless defined $req;
  319.  
  320.     $timeleft = $timeout;
  321.     while ($retries > 0) {
  322.     $this->send_query ($req)
  323.         || return $this->error ("send_query: $!");
  324.       wait_for_response:
  325.     ($nfound, $timeleft) = $this->wait_for_response($timeleft);
  326.     if ($nfound > 0) {
  327.         my($response_length);
  328.  
  329.         $response_length
  330.         = $this->receive_response_3 ($response_tag, $oids, $errorp);
  331.         if ($response_length) {
  332.         return $response_length;
  333.         } elsif (defined ($response_length)) {
  334.         goto wait_for_response;
  335.         # A response has been received, but for a different
  336.         # request ID or from a different IP address.
  337.         } else {
  338.         return undef;
  339.         }
  340.     } else {
  341.         ## No response received - retry
  342.         --$retries;
  343.         $timeout *= $this->backoff;
  344.         $timeleft = $timeout;
  345.     }
  346.     }
  347.     $this->error ("no response received");
  348. }
  349.  
  350.  
  351. sub error_return ($$) {
  352.     my ($this,$message) = @_;
  353.     $SNMP_Session::errmsg = $message;
  354.     unless ($SNMP_Session::suppress_warnings) {
  355.     $message =~ s/^/  /mg;
  356.     carp ("Error:\n".$message."\n");
  357.     }
  358.     return undef;
  359. }
  360.  
  361. sub error ($$) {
  362.     my ($this,$message) = @_;
  363.     my $session = $this->to_string;
  364.     $SNMP_Session::errmsg = $message."\n".$session;
  365.     unless ($SNMP_Session::suppress_warnings) {
  366.     $session =~ s/^/  /mg;
  367.     $message =~ s/^/  /mg;
  368.     carp ("SNMP Error:\n".$SNMP_Session::errmsg."\n");
  369.     }
  370.     return undef;
  371. }
  372.  
  373. sub ber_error ($$) {
  374.   my ($this,$type) = @_;
  375.   my ($errmsg) = $BER::errmsg;
  376.  
  377.   $errmsg =~ s/^/  /mg;
  378.   return $this->error ("$type:\n$errmsg");
  379. }
  380.  
  381. sub map_table ($$$) {
  382.     my ($session, $columns, $mapfn) = @_;
  383.     return $session->map_table_4 ($columns, $mapfn,
  384.                   $session->default_max_repetitions ());
  385. }
  386.  
  387. sub map_table_4 ($$$$) {
  388.     my ($session, $columns, $mapfn, $max_repetitions) = @_;
  389.     return $session->map_table_start_end ($columns, $mapfn,
  390.                       "", undef,
  391.                       $max_repetitions);
  392. }
  393.  
  394. sub map_table_start_end ($$$$$$) {
  395.     my ($session, $columns, $mapfn, $start, $end, $max_repetitions) = @_;
  396.  
  397.     my @encoded_oids;
  398.     my $call_counter = 0;
  399.     my $base_index = $start;
  400.  
  401.     do {
  402.     foreach (@encoded_oids = @{$columns}) {
  403.         $_=encode_oid (@{$_},split '\.',$base_index)
  404.         || return $session->ber_error ("encoding OID $base_index");
  405.     }
  406.     if ($session->getnext_request_response (@encoded_oids)) {
  407.         my $response = $session->pdu_buffer;
  408.         my ($bindings) = $session->decode_get_response ($response);
  409.         my $smallest_index = undef;
  410.         my @collected_values = ();
  411.  
  412.         my @bases = @{$columns};
  413.         while ($bindings ne '') {
  414.         my ($binding, $oid, $value);
  415.         my $base = shift @bases;
  416.         ($binding, $bindings) = decode_sequence ($bindings);
  417.         ($oid, $value) = decode_by_template ($binding, "%O%@");
  418.  
  419.         my $out_index;
  420.  
  421.         $out_index = &oid_diff ($base, $oid);
  422.         my $cmp;
  423.         if (!defined $smallest_index
  424.             || ($cmp = index_compare ($out_index,$smallest_index)) == -1) {
  425.             $smallest_index = $out_index;
  426.             grep ($_=undef, @collected_values);
  427.             push @collected_values, $value;
  428.         } elsif ($cmp == 1) {
  429.             push @collected_values, undef;
  430.         } else {
  431.             push @collected_values, $value;
  432.         }
  433.         }
  434.         (++$call_counter,
  435.          &$mapfn ($smallest_index, @collected_values))
  436.         if defined $smallest_index;
  437.         $base_index = $smallest_index;
  438.     } else {
  439.         return undef;
  440.     }
  441.     }
  442.     while (defined $base_index
  443.        && (!defined $end || index_compare ($base_index, $end) < 0));
  444.     $call_counter;
  445. }
  446.  
  447. sub index_compare ($$) {
  448.   my ($i1, $i2) = @_;
  449.   $i1 = '' unless defined $i1;
  450.   $i2 = '' unless defined $i2;
  451.   if ($i1 eq '') {
  452.       return $i2 eq '' ? 0 : 1;
  453.   } elsif ($i2 eq '') {
  454.       return 1;
  455.   } elsif (!$i1) {
  456.       return $i2 eq '' ? 1 : !$i2 ? 0 : 1;
  457.   } elsif (!$i2) {
  458.       return -1;
  459.   } else {
  460.     my ($f1,$r1) = split('\.',$i1,2);
  461.     my ($f2,$r2) = split('\.',$i2,2);
  462.  
  463.     if ($f1 < $f2) {
  464.       return -1;
  465.     } elsif ($f1 > $f2) {
  466.       return 1;
  467.     } else {
  468.       return index_compare ($r1,$r2);
  469.     }
  470.   }
  471. }
  472.  
  473. sub oid_diff ($$) {
  474.   my($base, $full) = @_;
  475.   my $base_dotnot = join ('.',@{$base});
  476.   my $full_dotnot = BER::pretty_oid ($full);
  477.  
  478.   return undef unless substr ($full_dotnot, 0, length $base_dotnot)
  479.     eq $base_dotnot
  480.       && substr ($full_dotnot, length $base_dotnot, 1) eq '.';
  481.   substr ($full_dotnot, length ($base_dotnot)+1);
  482. }
  483.  
  484. sub pretty_address {
  485.     my($addr) = shift;
  486.     my($port,$ipaddr) = unpack_sockaddr_in($addr);
  487.     return sprintf ("[%s].%d",inet_ntoa($ipaddr),$port);
  488. }
  489.  
  490. sub version { $VERSION; }
  491.  
  492. package SNMPv1_Session;
  493.  
  494. use strict qw(vars subs);   # see above
  495. use vars qw(@ISA);
  496. use SNMP_Session;
  497. use Socket;
  498. use BER;
  499. use IO::Socket;
  500. use Carp;
  501.  
  502. @ISA = qw(SNMP_Session);
  503.  
  504. sub snmp_version { 0 }
  505.  
  506. sub open {
  507.     my($this,
  508.        $remote_hostname,$community,$port,
  509.        $max_pdu_len,$local_port,$max_repetitions,
  510.        $local_hostname) = @_;
  511.     my($remote_addr,$socket);
  512.  
  513.     $community = 'public' unless defined $community;
  514.     $port = SNMP_Session::standard_udp_port unless defined $port;
  515.     $max_pdu_len = 8000 unless defined $max_pdu_len;
  516.     $max_repetitions = $default_max_repetitions
  517.     unless defined $max_repetitions;
  518.     if (defined $remote_hostname) {
  519.     $remote_addr = inet_aton ($remote_hostname)
  520.         or return $this->error_return ("can't resolve \"$remote_hostname\" to IP address");
  521.     }
  522.     if ($SNMP_Session::recycle_socket && defined $the_socket) {
  523.     $socket = $the_socket;
  524.     } else {
  525.     $socket = IO::Socket::INET->new(Proto => 17,
  526.                     Type => SOCK_DGRAM,
  527.                     LocalAddr => $local_hostname,
  528.                     LocalPort => $local_port)
  529.         || return $this->error_return ("creating socket: $!");
  530.     $the_socket = $socket
  531.         if $SNMP_Session::recycle_socket;
  532.     }
  533.     $remote_addr = pack_sockaddr_in ($port, $remote_addr)
  534.     if defined $remote_addr;
  535.     bless {
  536.        'sock' => $socket,
  537.        'sockfileno' => fileno ($socket),
  538.        'community' => $community,
  539.        'remote_hostname' => $remote_hostname,
  540.        'remote_addr' => $remote_addr,
  541.        'max_pdu_len' => $max_pdu_len,
  542.        'pdu_buffer' => '\0' x $max_pdu_len,
  543.        'request_id' => ((int (rand 0x10000) << 16) + int (rand 0x10000))
  544.            - 0x80000000,
  545.        'timeout' => $default_timeout,
  546.        'retries' => $default_retries,
  547.        'backoff' => $default_backoff,
  548.        'debug' => $default_debug,
  549.        'error_status' => 0,
  550.        'error_index' => 0,
  551.        'default_max_repetitions' => $max_repetitions,
  552.        'use_getbulk' => 1,
  553.        'lenient_source_address_matching' => 1,
  554.        'lenient_source_port_matching' => 1,
  555.       };
  556. }
  557.  
  558. sub open_trap_session (@) {
  559.     my ($this, $port) = @_;
  560.     $port = 162 unless defined $port;
  561.     return $this->open (undef, "", 161, undef, $port);
  562. }
  563.  
  564. sub sock { $_[0]->{sock} }
  565. sub sockfileno { $_[0]->{sockfileno} }
  566. sub remote_addr { $_[0]->{remote_addr} }
  567. sub pdu_buffer { $_[0]->{pdu_buffer} }
  568. sub max_pdu_len { $_[0]->{max_pdu_len} }
  569. sub default_max_repetitions {
  570.     defined $_[1]
  571.     ? $_[0]->{default_max_repetitions} = $_[1]
  572.         : $_[0]->{default_max_repetitions} }
  573. sub debug { defined $_[1] ? $_[0]->{debug} = $_[1] : $_[0]->{debug} }
  574.  
  575. sub close {
  576.     my($this) = shift;
  577.     ## Avoid closing the socket if it may be shared with other session
  578.     ## objects.
  579.     if (! defined $the_socket || $this->sock ne $the_socket) {
  580.     close ($this->sock) || $this->error ("close: $!");
  581.     }
  582. }
  583.  
  584. sub wrap_request {
  585.     my($this) = shift;
  586.     my($request) = shift;
  587.  
  588.     encode_sequence (encode_int ($this->snmp_version),
  589.              encode_string ($this->{community}),
  590.              $request)
  591.       || return $this->ber_error ("wrapping up request PDU");
  592. }
  593.  
  594. my @error_status_code = qw(noError tooBig noSuchName badValue readOnly
  595.                genErr noAccess wrongType wrongLength
  596.                wrongEncoding wrongValue noCreation
  597.                inconsistentValue resourceUnavailable
  598.                commitFailed undoFailed authorizationError
  599.                notWritable inconsistentName);
  600.  
  601. sub unwrap_response_5b {
  602.     my ($this,$response,$tag,$oids,$errorp) = @_;
  603.     my ($community,$request_id,@rest,$snmpver);
  604.  
  605.     ($snmpver,$community,$request_id,
  606.      $this->{error_status},
  607.      $this->{error_index},
  608.      @rest)
  609.     = decode_by_template ($response, "%{%i%s%*{%i%i%i%{%@",
  610.                   $tag);
  611.     return $this->ber_error ("Error decoding response PDU")
  612.       unless defined $snmpver;
  613.     return $this->error ("Received SNMP response with unknown snmp-version field $snmpver")
  614.     unless $snmpver == $this->snmp_version;
  615.     if ($this->{error_status} != 0) {
  616.       if ($errorp) {
  617.     my ($oid, $errmsg);
  618.     $errmsg = $error_status_code[$this->{error_status}] || $this->{error_status};
  619.     $oid = $oids->[$this->{error_index}-1]
  620.       if $this->{error_index} > 0 && $this->{error_index}-1 <= $#{$oids};
  621.     $oid = $oid->[0]
  622.       if ref($oid) eq 'ARRAY';
  623.     return ($community, $request_id,
  624.         $this->error ("Received SNMP response with error code\n"
  625.                   ."  error status: $errmsg\n"
  626.                   ."  index ".$this->{error_index}
  627.                   .(defined $oid
  628.                 ? " (OID: ".&BER::pretty_oid($oid).")"
  629.                 : "")));
  630.       } else {
  631.     if ($this->{error_index} == 1) {
  632.       @rest[$this->{error_index}-1..$this->{error_index}] = ();
  633.     }
  634.       }
  635.     }
  636.     ($community, $request_id, @rest);
  637. }
  638.  
  639. sub send_query ($$) {
  640.     my ($this,$query) = @_;
  641.     send ($this->sock,$query,0,$this->remote_addr);
  642. }
  643.  
  644. ## Compare two sockaddr_in structures for equality.  This is used when
  645. ## matching incoming responses with outstanding requests.  Previous
  646. ## versions of the code simply did a bytewise comparison ("eq") of the
  647. ## two sockaddr_in structures, but this didn't work on some systems
  648. ## where sockaddr_in contains other elements than just the IP address
  649. ## and port number, notably FreeBSD.
  650. ##
  651. ## We allow for varying degrees of leniency when checking the source
  652. ## address.  By default we now ignore it altogether, because there are
  653. ## agents that don't respond from UDP port 161, and there are agents
  654. ## that don't respond from the IP address the query had been sent to.
  655. ##
  656. sub sa_equal_p ($$$) {
  657.     my ($this, $sa1, $sa2) = @_;
  658.     my ($p1, $a1) = sockaddr_in ($sa1);
  659.     my ($p2, $a2) = sockaddr_in ($sa2);
  660.     if (! $this->{'lenient_source_address_matching'}) {
  661.     return 0 if $a1 ne $a2;
  662.     }
  663.     if (! $this->{'lenient_source_port_matching'}) {
  664.     return 0 if $p1 != $p2;
  665.     }
  666.     return 1;
  667. }
  668.  
  669. sub receive_response_3 {
  670.     my ($this, $response_tag, $oids, $errorp) = @_;
  671.     my ($remote_addr);
  672.     $remote_addr = recv ($this->sock,$this->{'pdu_buffer'},$this->max_pdu_len,0);
  673.     return $this->error ("receiving response PDU: $!")
  674.     unless defined $remote_addr;
  675.     return $this->error ("short (".length $this->{'pdu_buffer'}
  676.              ." bytes) response PDU")
  677.     unless length $this->{'pdu_buffer'} > 2;
  678.     my $response = $this->{'pdu_buffer'};
  679.     ##
  680.     ## Check whether the response came from the address we've sent the
  681.     ## request to.  If this is not the case, we should probably ignore
  682.     ## it, as it may relate to another request.
  683.     ##
  684.     if (defined $this->{'remote_addr'}) {
  685.     if (! $this->sa_equal_p ($remote_addr, $this->{'remote_addr'})) {
  686.         if ($this->{'debug'} && !$SNMP_Session::recycle_socket) {
  687.         carp ("Response came from ".&SNMP_Session::pretty_address($remote_addr)
  688.               .", not ".&SNMP_Session::pretty_address($this->{'remote_addr'}))
  689.             unless $SNMP_Session::suppress_warnings;
  690.         }
  691.         return 0;
  692.     }
  693.     }
  694.     $this->{'last_sender_addr'} = $remote_addr;
  695.     my ($response_community, $response_id, @unwrapped)
  696.     = $this->unwrap_response_5b ($response, $response_tag,
  697.                      $oids, $errorp);
  698.     if ($response_community ne $this->{community}
  699.         || $response_id ne $this->{request_id}) {
  700.     if ($this->{'debug'}) {
  701.         carp ("$response_community != $this->{community}")
  702.         unless $SNMP_Session::suppress_warnings
  703.             || $response_community eq $this->{community};
  704.         carp ("$response_id != $this->{request_id}")
  705.         unless $SNMP_Session::suppress_warnings
  706.             || $response_id == $this->{request_id};
  707.     }
  708.     return 0;
  709.     }
  710.     if (!defined $unwrapped[0]) {
  711.     $this->{'unwrapped'} = undef;
  712.     return undef;
  713.     }
  714.     $this->{'unwrapped'} = \@unwrapped;
  715.     return length $this->pdu_buffer;
  716. }
  717.  
  718. sub receive_trap {
  719.     my ($this) = @_;
  720.     my ($remote_addr, $iaddr, $port, $trap);
  721.     $remote_addr = recv ($this->sock,$this->{'pdu_buffer'},$this->max_pdu_len,0);
  722.     return undef unless $remote_addr;
  723.     ($port, $iaddr) = sockaddr_in($remote_addr);
  724.     $trap = $this->{'pdu_buffer'};
  725.     return ($trap, $iaddr, $port);
  726. }
  727.  
  728. sub describe {
  729.     my($this) = shift;
  730.     print $this->to_string (),"\n";
  731. }
  732.  
  733. sub to_string {
  734.     my($this) = shift;
  735.     my ($class,$prefix);
  736.  
  737.     $class = ref($this);
  738.     $prefix = ' ' x (length ($class) + 2);
  739.     ($class
  740.      .(defined $this->{remote_hostname}
  741.        ? " (remote host: \"".$this->{remote_hostname}."\""
  742.        ." ".&SNMP_Session::pretty_address ($this->remote_addr).")"
  743.        : " (no remote host specified)")
  744.      ."\n"
  745.      .$prefix."  community: \"".$this->{'community'}."\"\n"
  746.      .$prefix." request ID: ".$this->{'request_id'}."\n"
  747.      .$prefix."PDU bufsize: ".$this->{'max_pdu_len'}." bytes\n"
  748.      .$prefix."    timeout: ".$this->{timeout}."s\n"
  749.      .$prefix."    retries: ".$this->{retries}."\n"
  750.      .$prefix."    backoff: ".$this->{backoff}.")");
  751. ##    sprintf ("SNMP_Session: %s (size %d timeout %g)",
  752. ##    &SNMP_Session::pretty_address ($this->remote_addr),$this->max_pdu_len,
  753. ##         $this->timeout);
  754. }
  755.  
  756. ### SNMP Agent support
  757. ### contributed by Mike McCauley <mikem@open.com.au>
  758. ###
  759. sub receive_request {
  760.     my ($this) = @_;
  761.     my ($remote_addr, $iaddr, $port, $request);
  762.  
  763.     $remote_addr = recv($this->sock, $this->{'pdu_buffer'},
  764.             $this->{'max_pdu_len'}, 0);
  765.     return undef unless $remote_addr;
  766.     ($port, $iaddr) = sockaddr_in($remote_addr);
  767.     $request = $this->{'pdu_buffer'};
  768.     return ($request, $iaddr, $port);
  769. }
  770.  
  771. sub decode_request {
  772.     my ($this, $request) = @_;
  773.     my ($snmp_version, $community, $requestid, $errorstatus, $errorindex, $bindings);
  774.  
  775.     ($snmp_version, $community, $requestid, $errorstatus, $errorindex, $bindings)
  776.     = decode_by_template ($request, "%{%i%s%*{%i%i%i%@", SNMP_Session::get_request);
  777.     if (defined $snmp_version)
  778.     {
  779.     # Its a valid get_request
  780.     return(SNMP_Session::get_request, $requestid, $bindings, $community);
  781.     }
  782.  
  783.     ($snmp_version, $community, $requestid, $errorstatus, $errorindex, $bindings)
  784.     = decode_by_template ($request, "%{%i%s%*{%i%i%i%@", SNMP_Session::getnext_request);
  785.     if (defined $snmp_version)
  786.     {
  787.     # Its a valid getnext_request
  788.     return(SNMP_Session::getnext_request, $requestid, $bindings, $community);
  789.     }
  790.  
  791.     ($snmp_version, $community, $requestid, $errorstatus, $errorindex, $bindings)
  792.     = decode_by_template ($request, "%{%i%s%*{%i%i%i%@", SNMP_Session::set_request);
  793.     if (defined $snmp_version)
  794.     {
  795.     # Its a valid set_request
  796.     return(SNMP_Session::set_request, $requestid, $bindings, $community);
  797.     }
  798.  
  799.     # Something wrong with this packet
  800.     # Decode failed
  801.     return undef;
  802. }
  803.  
  804. package SNMPv2c_Session;
  805. use strict qw(vars subs);   # see above
  806. use vars qw(@ISA);
  807. use SNMP_Session;
  808. use BER;
  809. use Carp;
  810.  
  811. @ISA = qw(SNMPv1_Session);
  812.  
  813. sub snmp_version { 1 }
  814.  
  815. sub open {
  816.     my $session = SNMPv1_Session::open (@_);
  817.     return bless $session;
  818. }
  819.  
  820. sub map_table_start_end ($$$$$$) {
  821.     my ($session, $columns, $mapfn, $start, $end, $max_repetitions) = @_;
  822.  
  823.     my @encoded_oids;
  824.     my $call_counter = 0;
  825.     my $base_index = $start;
  826.     my $ncols = @{$columns};
  827.     my @collected_values = ();
  828.  
  829.     if (! $session->{'use_getbulk'}) {
  830.     return SNMP_Session::map_table_start_end ($session, $columns, $mapfn, $start, $end, $max_repetitions);
  831.     }
  832.     $max_repetitions = $session->default_max_repetitions
  833.     unless defined $max_repetitions;
  834.  
  835.     for (;;) {
  836.     foreach (@encoded_oids = @{$columns}) {
  837.         $_=encode_oid (@{$_},split '\.',$base_index)
  838.         || return $session->ber_error ("encoding OID $base_index");
  839.     }
  840.     if ($session->getbulk_request_response (0, $max_repetitions,
  841.                         @encoded_oids)) {
  842.         my $response = $session->pdu_buffer;
  843.         my ($bindings) = $session->decode_get_response ($response);
  844.         my @colstack = ();
  845.         my $k = 0;
  846.         my $j;
  847.  
  848.         my $min_index = undef;
  849.  
  850.         my @bases = @{$columns};
  851.         my $n_bindings = 0;
  852.         my $binding;
  853.  
  854.         while ($bindings ne '') {
  855.         ($binding, $bindings) = decode_sequence ($bindings);
  856.         my ($oid, $value) = decode_by_template ($binding, "%O%@");
  857.  
  858.         push @{$colstack[$k]}, [$oid, $value];
  859.         ++$k; $k = 0 if $k >= $ncols;
  860.         }
  861.  
  862.         my $last_min_index = undef;
  863.       walk_rows_from_pdu:
  864.         for (;;) {
  865.         my $min_index = undef;
  866.  
  867.         for ($k = 0; $k < $ncols; ++$k) {
  868.             $collected_values[$k] = undef;
  869.             my $pair = $colstack[$k]->[0];
  870.             unless (defined $pair) {
  871.             $min_index = undef;
  872.             last walk_rows_from_pdu;
  873.             }
  874.             my $this_index
  875.             = SNMP_Session::oid_diff ($columns->[$k], $pair->[0]);
  876.             if (defined $this_index) {
  877.             my $cmp
  878.                 = !defined $min_index
  879.                 ? -1
  880.                     : SNMP_Session::index_compare
  881.                     ($this_index, $min_index);
  882.             if ($cmp == -1) {
  883.                 for ($j = 0; $j < $k; ++$j) {
  884.                 unshift (@{$colstack[$j]},
  885.                      [$min_index,
  886.                       $collected_values[$j]]);
  887.                 $collected_values[$j] = undef;
  888.                 }
  889.                 $min_index = $this_index;
  890.             }
  891.             if ($cmp <= 0) {
  892.                 $collected_values[$k] = $pair->[1];
  893.                 shift @{$colstack[$k]};
  894.             }
  895.             }
  896.         }
  897.         last unless defined $min_index;
  898.         last if defined $end && index_compare ($min_index, $end) >= 0;
  899.         &$mapfn ($min_index, @collected_values);
  900.         ++$call_counter;
  901.         $last_min_index = $min_index;
  902.         }
  903.         $base_index = $last_min_index;
  904.     } else {
  905.         return undef;
  906.     }
  907.     last unless (defined $base_index
  908.              && (!defined $end || index_compare ($base_index, $end) < 0));
  909.     }
  910.     $call_counter;
  911. }
  912.  
  913. 1;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement