1# SNMP.pm -- Perl 5 interface to the Net-SNMP toolkit
2#
3# written by G. S. Marzot (marz@users.sourceforge.net)
4#
5#     Copyright (c) 1995-2006 G. S. Marzot. All rights reserved.
6#     This program is free software; you can redistribute it and/or
7#     modify it under the same terms as Perl itself.
8
9package SNMP;
10$VERSION = '5.09';   # current release version number
11
12use strict;
13use warnings;
14
15require Exporter;
16require DynaLoader;
17require AutoLoader;
18
19use NetSNMP::default_store (':all');
20
21@SNMP::ISA = qw(Exporter AutoLoader DynaLoader);
22# Items to export into callers namespace by default. Note: do not export
23# names by default without a very good reason. Use EXPORT_OK instead.
24# Do not simply export all your public functions/methods/constants.
25@SNMP::EXPORT = qw(
26	RECEIVED_MESSAGE
27	SNMPERR_BAD_ADDRESS
28	SNMPERR_BAD_LOCPORT
29	SNMPERR_BAD_SESSION
30	SNMPERR_GENERR
31	SNMPERR_TOO_LONG
32	SNMP_DEFAULT_ADDRESS
33	SNMP_DEFAULT_COMMUNITY_LEN
34	SNMP_DEFAULT_ENTERPRISE_LENGTH
35	SNMP_DEFAULT_ERRINDEX
36	SNMP_DEFAULT_ERRSTAT
37	SNMP_DEFAULT_PEERNAME
38	SNMP_DEFAULT_REMPORT
39	SNMP_DEFAULT_REQID
40	SNMP_DEFAULT_RETRIES
41	SNMP_DEFAULT_TIME
42	SNMP_DEFAULT_TIMEOUT
43	SNMP_DEFAULT_VERSION
44	TIMED_OUT
45	snmp_get
46        snmp_getnext
47        snmp_set
48        snmp_trap
49	SNMP_API_TRADITIONAL
50	SNMP_API_SINGLE
51);
52
53sub AUTOLOAD {
54    no strict;
55    # This AUTOLOAD is used to 'autoload' constants from the constant()
56    # XS function.  If a constant is not found then control is passed
57    # to the AUTOLOAD in AutoLoader.
58    my($val,$pack,$file,$line);
59    my $constname;
60    ($constname = $AUTOLOAD) =~ s/.*:://;
61    # croak "&$module::constant not defined" if $constname eq 'constant';
62    ($!, $val) = constant($constname, @_ ? $_[0] : 0);
63    if ($! != 0) {
64	if ($! =~ /Invalid/) {
65	    $AutoLoader::AUTOLOAD = $AUTOLOAD;
66	    goto &AutoLoader::AUTOLOAD;
67	}
68	else {
69	    ($pack,$file,$line) = caller;
70	    die "Your vendor has not defined SNMP macro $constname, used at $file line $line.
71";
72	}
73    }
74    eval "sub $AUTOLOAD { $val }";
75    goto &$AUTOLOAD;
76}
77
78bootstrap SNMP;
79
80# Preloaded methods go here.
81
82# Package variables
83tie $SNMP::debugging,         'SNMP::DEBUGGING';
84tie $SNMP::debug_internals,   'SNMP::DEBUG_INTERNALS';
85tie $SNMP::dump_packet,       'SNMP::DUMP_PACKET';
86tie %SNMP::MIB,               'SNMP::MIB';
87tie $SNMP::save_descriptions, 'SNMP::MIB::SAVE_DESCR';
88tie $SNMP::replace_newer,     'SNMP::MIB::REPLACE_NEWER';
89tie $SNMP::mib_options,       'SNMP::MIB::MIB_OPTIONS';
90
91%SNMP::V3_SEC_LEVEL_MAP = (noAuthNoPriv => 1, authNoPriv => 2, authPriv =>3);
92
93use vars qw(
94  $auto_init_mib $use_long_names $use_sprint_value $use_enums
95  $use_numeric %MIB $verbose $debugging $dump_packet $save_descriptions
96  $best_guess $non_increasing $replace_newer %session_params
97  $debug_internals $mib_options
98);
99
100$auto_init_mib = 1; # enable automatic MIB loading at session creation time
101$use_long_names = 0; # non-zero to prefer longer mib textual identifiers rather
102                   # than just leaf indentifiers (see translateObj)
103                   # may also be set on a per session basis(see UseLongNames)
104$use_sprint_value = 0; # non-zero to enable formatting of response values
105                   # using the snmp libraries "snprint_value"
106                   # may also be set on a per session basis(see UseSprintValue)
107                   # note: returned values not suitable for 'set' operations
108$use_enums = 0; # non-zero to return integers as enums and allow sets
109                # using enums where appropriate - integer data will
110                # still be accepted for set operations
111                # may also be set on a per session basis (see UseEnums)
112$use_numeric = 0; # non-zero to return object tags as numeric OID's instead
113                  # of converting to textual representations.  use_long_names,
114                  # if non-zero, returns the entire OID, otherwise, return just
115                  # the label portion.  use_long_names is also set if the
116		  # use_numeric variable is set.
117%MIB = ();      # tied hash to access libraries internal mib tree structure
118                # parsed in from mib files
119$verbose = 0;   # controls warning/info output of SNMP module,
120                # 0 => no output, 1 => enables warning and info
121                # output from SNMP module itself (is also controlled
122                # by SNMP::debugging)
123$debugging = 0; # non-zero to globally enable libsnmp do_debugging output
124                # set to >= 2 to enabling packet dumping (see below)
125$dump_packet = 0; # non-zero to globally enable libsnmp dump_packet output.
126                  # is also enabled when $debugging >= 2
127$save_descriptions = 0; #tied scalar to control saving descriptions during
128               # mib parsing - must be set prior to mib loading
129$best_guess = 0;  # determine whether or not to enable best-guess regular
130                  # expression object name translation.  1 = Regex (-Ib),
131		  # 2 = random (-IR)
132$non_increasing = 0; # stop polling with an "OID not increasing"-error
133                     # when an OID does not increases in bulkwalk.
134$replace_newer = 0; # determine whether or not to tell the parser to replace
135                    # older MIB modules with newer ones when loading MIBs.
136                    # WARNING: This can cause an incorrect hierarchy.
137
138sub register_debug_tokens {
139    my $tokens = shift;
140
141    SNMP::_register_debug_tokens($tokens);
142}
143
144sub getenv {
145    my $name = shift;
146
147    return SNMP::_getenv($name);
148}
149
150sub setenv {
151    my $envname = shift;
152    my $envval = shift;
153    my $overwrite = shift;
154
155    return SNMP::_setenv($envname, $envval, $overwrite);
156}
157
158sub setMib {
159# loads mib from file name provided
160# setting second arg to true causes currently loaded mib to be replaced
161# otherwise mib file will be added to existing loaded mib database
162# NOTE: now deprecated in favor of addMibFiles and new module based funcs
163   my $file = shift;
164   my $force = shift || '0';
165   return 0 if $file and not (-r $file);
166   SNMP::_read_mib($file,$force);
167}
168
169sub initMib {
170# equivalent to calling the snmp library init_mib if Mib is NULL
171# if Mib is already loaded this function does nothing
172# Pass a zero valued argument to get minimal mib tree initialization
173# If non zero argument or no argument then full mib initialization
174
175  SNMP::init_snmp("perl");
176  return;
177
178
179  if (defined $_[0] and $_[0] == 0) {
180    SNMP::_init_mib_internals();
181  } else {
182    SNMP::_read_mib("");
183  }
184}
185
186sub addMibDirs {
187# adds directories to search path when a module is requested to be loaded
188  SNMP::init_snmp("perl");
189  foreach (@_) {
190    SNMP::_add_mib_dir($_) or return undef;
191  }
192  return 1;
193}
194
195sub addMibFiles {
196# adds mib definitions to currently loaded mib database from
197# file(s) supplied
198  SNMP::init_snmp("perl");
199  foreach (@_) {
200    SNMP::_read_mib($_) or return undef;
201  }
202  return 1;
203}
204
205sub loadModules {
206# adds mib module definitions to currently loaded mib database.
207# Modules will be searched from previously defined mib search dirs
208# Passing and arg of 'ALL' will cause all known modules to be loaded
209   SNMP::init_snmp("perl");
210   foreach (@_) {
211     SNMP::_read_module($_) or return undef;
212   }
213   return 1;
214}
215
216sub unloadModules {
217# causes modules to be unloaded from mib database
218# Passing and arg of 'ALL' will cause all known modules to be unloaded
219  warn("SNMP::unloadModules not implemented! (yet)");
220}
221
222sub translateObj {
223# Translate object identifier(tag or numeric) into alternate representation
224# (i.e., sysDescr => '.1.3.6.1.2.1.1.1' and '.1.3.6.1.2.1.1.1' => sysDescr)
225# when $SNMP::use_long_names or second arg is non-zero the translation will
226# return longer textual identifiers (e.g., system.sysDescr).  An optional
227# third argument of non-zero will cause the module name to be prepended
228# to the text name (e.g. 'SNMPv2-MIB::sysDescr').  If no Mib is loaded
229# when called and $SNMP::auto_init_mib is enabled then the Mib will be
230# loaded. Will return 'undef' upon failure.
231   SNMP::init_snmp("perl");
232   my $obj = shift;
233   my $temp = shift;
234   my $include_module_name = shift || "0";
235   my $long_names = $temp || $SNMP::use_long_names;
236
237   return undef if not defined $obj;
238   my $res;
239   if ($obj =~ /^\.?(\d+\.)*\d+$/) {
240      $res = SNMP::_translate_obj($obj,1,$long_names,$SNMP::auto_init_mib,0,$include_module_name);
241   } elsif ($obj =~ /(\.\d+)*$/ && $SNMP::best_guess == 0) {
242      $res = SNMP::_translate_obj($`,0,$long_names,$SNMP::auto_init_mib,0,$include_module_name);
243      $res .= $& if defined $res and defined $&;
244   } elsif ($SNMP::best_guess) {
245      $res = SNMP::_translate_obj($obj,0,$long_names,$SNMP::auto_init_mib,$SNMP::best_guess,$include_module_name);
246   }
247
248   return($res);
249}
250
251sub getType {
252# return SNMP data type for given textual identifier
253# OBJECTID, OCTETSTR, INTEGER, NETADDR, IPADDR, COUNTER
254# GAUGE, TIMETICKS, OPAQUE, or undef
255  my $tag = shift;
256  SNMP::_get_type($tag, $SNMP::best_guess);
257}
258
259sub mapEnum {
260# return the corresponding integer value *or* tag for a given MIB attribute
261# and value. The function will sense which direction to perform the conversion
262# various arg formats are supported
263#    $val = SNMP::mapEnum($varbind); # note: will update $varbind
264#    $val = SNMP::mapEnum('ipForwarding', 'forwarding');
265#    $val = SNMP::mapEnum('ipForwarding', 1);
266#
267  my $var = shift;
268  my ($tag, $val, $update);
269  if (ref($var) =~ /ARRAY/ or ref($var) =~ /Varbind/) {
270      $tag = SNMP::Varbind::tag($var);
271      $val = SNMP::Varbind::val($var);
272      $update = 1;
273  } else {
274      $tag = $var;
275      $val = shift;
276  }
277  my $iflag = $val =~ /^\d+$/;
278  my $res = SNMP::_map_enum($tag, $val, $iflag, $SNMP::best_guess);
279  if ($update and defined $res) { SNMP::Varbind::val($var) = $res; }
280  return($res);
281}
282
283%session_params = (DestHost => 1,
284		   Community => 1,
285		   Version => 1,
286		   Timeout => 1,
287		   Retries => 1,
288		   RemotePort => 1,
289                   LocalPort => 1);
290
291sub strip_session_params {
292    my @params;
293    my @args;
294    my $param;
295    while ($param = shift) {
296	push(@params,$param, shift), next
297	    if $session_params{$param};
298	push(@args,$param);
299    }
300    @_ = @args;
301    @params;
302}
303
304
305sub snmp_get {
306# procedural form of 'get' method. sometimes quicker to code
307# but is less efficient since the Session is created and destroyed
308# with each call. Takes all the parameters of both SNMP::Session::new and
309# SNMP::Session::get (*NOTE*: this api does not support async callbacks)
310
311    my @sess_params = &strip_session_params;
312    my $sess = new SNMP::Session(@sess_params);
313
314    $sess->get(@_);
315}
316
317sub snmp_getnext {
318# procedural form of 'getnext' method. sometimes quicker to code
319# but is less efficient since the Session is created and destroyed
320# with each call. Takes all the parameters of both SNMP::Session::new and
321# SNMP::Session::getnext (*NOTE*: this api does not support async callbacks)
322
323    my @sess_params = &strip_session_params;
324    my $sess = new SNMP::Session(@sess_params);
325
326    $sess->getnext(@_);
327}
328
329sub snmp_set {
330# procedural form of 'set' method. sometimes quicker to code
331# but is less efficient since the Session is created and destroyed
332# with each call. Takes all the parameters of both SNMP::Session::new and
333# SNMP::Session::set (*NOTE*: this api does not support async callbacks)
334
335    my @sess_params = &strip_session_params;
336    my $sess = new SNMP::Session(@sess_params);
337
338    $sess->set(@_);
339}
340
341sub snmp_trap {
342# procedural form of 'trap' method. sometimes quicker to code
343# but is less efficient since the Session is created and destroyed
344# with each call. Takes all the parameters of both SNMP::TrapSession::new and
345# SNMP::TrapSession::trap
346
347    my @sess_params = &strip_session_params;
348    my $sess = new SNMP::TrapSession(@sess_params);
349
350    $sess->trap(@_);
351}
352
353#---------------------------------------------------------------------
354# Preserves the ability to call MainLoop() with no args so we don't
355# break old code
356#
357# Alternately, MainLoop() could be called as an object method,
358# ( $sess->MainLoop() ) , so that $self winds up in @_.  Then it would
359# be more like :
360# my $self = shift;
361# ....
362# SNMP::_main_loop(......, $self->{SessPtr});
363#---------------------------------------------------------------------
364sub MainLoop {
365    my $ss = shift if(&SNMP::_api_mode() == SNMP::SNMP_API_SINGLE());
366    my $time = shift;
367    my $callback = shift;
368    my $time_sec = ($time ? int $time : 0);
369    my $time_usec = ($time ? int(($time-$time_sec)*1000000) : 0);
370    SNMP::_main_loop($time_sec,$time_usec,$callback,(defined($ss) ? $ss->{SessPtr} : ()));
371}
372
373sub finish {
374    SNMP::_mainloop_finish();
375}
376
377sub reply_cb {
378    # callback function for async snmp calls
379    # when triggered, will do a SNMP read on the
380    # given fd
381    my $fd = shift;
382  SNMP::_read_on_fd($fd);
383}
384
385sub select_info {
386    # retrieves SNMP used fd's and timeout info
387    # calculates timeout in fractional seconds
388    # ( easy to use with select statement )
389    my($block, $to_sec, $to_usec, @fd_set)=SNMP::_get_select_info();
390    my $time_sec_dec = ($block? 0 : $to_sec + $to_usec * 1e-6);
391    #print "fd's for snmp -> ", @fd_set, "\n";
392    #print "block		-> ", $block, "\n";
393    #print "timeout_sec	-> ", $to_sec, "\n";
394    #print "timeout_usec	-> ", $to_usec, "\n";
395    #print "timeout dec	-> ", $time_sec_dec, "\n";
396    return ($time_sec_dec,@fd_set);
397}
398
399sub check_timeout {
400  # check to see if a snmp session
401  # timed out, and if so triggers
402  # the callback function
403  SNMP::_check_timeout();
404  # check to see when have to check again
405  my($block, $to_sec, $to_usec, @fd_set)=SNMP::_get_select_info();
406  my $time_sec_dec = ($block? 0 : $to_sec + $to_usec * 1e-6);
407  #print "fd's for snmp -> ", @fd_set, "\n";
408  #print "block		-> ", $block, "\n";
409  #print "timeout_sec	-> ", $to_sec, "\n";
410  #print "timeout_usec	-> ", $to_usec, "\n";
411  #print "timeout dec	-> ", $time_sec_dec, "\n";
412  return ($time_sec_dec);
413}
414
415sub _tie {
416# this is a little implementation hack so ActiveState can access pp_tie
417# thru perl code. All other environments allow the calling of pp_tie from
418# XS code but AS was not exporting it when PERL_OBJECT was used.
419#
420# short term solution was call this perl func which calls 'tie'
421#
422# longterm fix is to supply a patch which allows AS to export pp_tie in
423# such a way that it can be called from XS code. gsarathy says:
424# a patch to util.c is needed to provide access to PL_paddr
425# so it is possible to call PL_paddr[OP_TIE] as the compiler does
426    tie($_[0],$_[1],$_[2],$_[3]);
427}
428
429sub split_vars {
430    # This sub holds the regex that is used throughout this module
431    #  to parse the base part of an OID from the IID.
432    #  eg: portName.9.30 -> ['portName','9.30']
433    my $vars = shift;
434
435    # The regex was changed to this simple form by patch 722075 for some reason.
436    # Testing shows now (2/05) that it is not needed, and that the long expression
437    # works fine.  AB
438    # my ($tag, $iid) = ($vars =~ /^(.*?)\.?(\d+)+$/);
439
440    # These following two are the same.  Broken down for easier maintenance
441    # my ($tag, $iid) = ($vars =~ /^((?:\.\d+)+|(?:\w+(?:\-*\w+)+))\.?(.*)$/);
442    my ($tag, $iid) =
443        ($vars =~ /^(               # Capture $1
444                    # 1. either this 5.5.5.5
445                     (?:\.\d+)+     # for grouping, won't increment $1
446                    |
447                    # 2. or asdf-asdf-asdf-asdf
448                     (?:            # grouping again
449                        \w+         # needs some letters followed by
450                        (?:\-*\w+)+ #  zero or more dashes, one or more letters
451                     )
452                    )
453                    \.?             # optionally match a dot
454                    (.*)            # whatever is left in the string is our iid ($2)
455                   $/x
456    );
457    return [$tag,$iid];
458}
459
460package SNMP::Session;
461
462sub new {
463   my $type = shift;
464   my $this = {};
465   my ($name, $aliases, $host_type, $len, $thisaddr);
466
467   SNMP::init_snmp("perl");
468
469   %$this = @_;
470
471   $this->{ErrorStr} = ''; # if methods return undef check for expln.
472   $this->{ErrorNum} = 0;  # contains SNMP error return
473
474   $this->{Version} ||=
475     NetSNMP::default_store::netsnmp_ds_get_int(NetSNMP::default_store::NETSNMP_DS_LIBRARY_ID,
476				      NetSNMP::default_store::NETSNMP_DS_LIB_SNMPVERSION) ||
477					SNMP::SNMP_DEFAULT_VERSION();
478
479   if ($this->{Version} eq 128) {
480       # special handling of the bogus v1 definition.
481       $this->{Version} = 1;
482   }
483
484   # allow override of local SNMP port
485   $this->{LocalPort} ||= 0;
486
487   # destination host defaults to localhost
488   $this->{DestHost} ||= 'localhost';
489
490   # community defaults to public
491   $this->{Community} ||= NetSNMP::default_store::netsnmp_ds_get_string(NetSNMP::default_store::NETSNMP_DS_LIBRARY_ID(),
492				        NetSNMP::default_store::NETSNMP_DS_LIB_COMMUNITY()) || 'public';
493
494   # number of retries before giving up, defaults to SNMP_DEFAULT_RETRIES
495   $this->{Retries} = SNMP::SNMP_DEFAULT_RETRIES() unless defined($this->{Retries});
496
497   # timeout before retry, defaults to SNMP_DEFAULT_TIMEOUT
498   $this->{Timeout} = SNMP::SNMP_DEFAULT_TIMEOUT() unless defined($this->{Timeout});
499   # flag to enable fixing pdu and retrying with a NoSuch error
500   $this->{RetryNoSuch} ||= 0;
501
502   # backwards compatibility.  Make host = host:port
503   if ($this->{RemotePort} && $this->{DestHost} !~ /:/) {
504       $this->{DestHost} = $this->{DestHost} . ":" . $this->{RemotePort};
505   }
506
507   if ($this->{DestHost} =~ /^(dtls|tls|ssh)/) {
508       # only works with version 3
509       $this->{Version} = 3;
510   }
511
512   if ($this->{Version} eq '1' or $this->{Version} eq '2'
513       or $this->{Version} eq '2c') {
514       $this->{SessPtr} = SNMP::_new_session($this->{Version},
515					     $this->{Community},
516					     $this->{DestHost},
517					     $this->{LocalPort},
518					     $this->{Retries},
519					     $this->{Timeout},
520					     );
521   } elsif ($this->{Version} eq '3' ) {
522       $this->{SecName} ||=
523	   NetSNMP::default_store::netsnmp_ds_get_string(NetSNMP::default_store::NETSNMP_DS_LIBRARY_ID(),
524		         NetSNMP::default_store::NETSNMP_DS_LIB_SECNAME()) ||
525			   'initial';
526       if (!$this->{SecLevel}) {
527	   $this->{SecLevel} =
528	       NetSNMP::default_store::netsnmp_ds_get_int(NetSNMP::default_store::NETSNMP_DS_LIBRARY_ID(),
529			  NetSNMP::default_store::NETSNMP_DS_LIB_SECLEVEL()) ||
530			      $SNMP::V3_SEC_LEVEL_MAP{'noAuthNoPriv'};
531       } elsif ($this->{SecLevel} !~ /^\d+$/) {
532	   $this->{SecLevel} = $SNMP::V3_SEC_LEVEL_MAP{$this->{SecLevel}};
533       }
534       $this->{SecEngineId} ||= '';
535       $this->{ContextEngineId} ||= $this->{SecEngineId};
536       $this->{Context} ||=
537	   NetSNMP::default_store::netsnmp_ds_get_string(NetSNMP::default_store::NETSNMP_DS_LIBRARY_ID(),
538		         NetSNMP::default_store::NETSNMP_DS_LIB_CONTEXT()) || '';
539
540       if ($this->{DestHost} =~ /^(dtls|tls|ssh)/) {
541	   # this is a tunneled protocol
542
543	   $this->{'OurIdentity'} ||= '';
544	   $this->{'TheirIdentity'} ||= '';
545	   $this->{'TheirHostname'} ||= '';
546	   $this->{'TrustCert'} ||= '';
547
548	   $this->{'SecLevel'} = $SNMP::V3_SEC_LEVEL_MAP{'authPriv'};
549
550	   $this->{SessPtr} =
551	     SNMP::_new_tunneled_session($this->{Version},
552					 $this->{DestHost},
553					 $this->{Retries},
554					 $this->{Timeout},
555					 $this->{SecName},
556					 $this->{SecLevel},
557					 $this->{ContextEngineId},
558					 $this->{Context},
559					 $this->{'OurIdentity'},
560					 $this->{'TheirIdentity'},
561					 $this->{'TheirHostname'},
562					 $this->{'TrustCert'},
563					);
564
565
566       } else {
567	   # USM or some other internal security protocol
568
569	   # USM specific parameters:
570	   $this->{AuthProto} ||= 'DEFAULT'; # use the library's default
571	   $this->{AuthPass} ||=
572	     NetSNMP::default_store::netsnmp_ds_get_string(NetSNMP::default_store::NETSNMP_DS_LIBRARY_ID(),
573							   NetSNMP::default_store::NETSNMP_DS_LIB_AUTHPASSPHRASE()) ||
574							       NetSNMP::default_store::netsnmp_ds_get_string(NetSNMP::default_store::NETSNMP_DS_LIBRARY_ID(),
575													     NetSNMP::default_store::NETSNMP_DS_LIB_PASSPHRASE()) || '';
576
577	   $this->{AuthMasterKey} ||= '';
578	   $this->{PrivMasterKey} ||= '';
579	   $this->{AuthLocalizedKey} ||= '';
580	   $this->{PrivLocalizedKey} ||= '';
581
582	   $this->{PrivProto} ||= 'DEFAULT'; # use the library's default
583	   $this->{PrivPass} ||=
584	     NetSNMP::default_store::netsnmp_ds_get_string(NetSNMP::default_store::NETSNMP_DS_LIBRARY_ID(),
585							   NetSNMP::default_store::NETSNMP_DS_LIB_PRIVPASSPHRASE()) ||
586							       NetSNMP::default_store::netsnmp_ds_get_string(NetSNMP::default_store::NETSNMP_DS_LIBRARY_ID(),
587													     NetSNMP::default_store::NETSNMP_DS_LIB_PASSPHRASE()) || '';
588	   $this->{EngineBoots} = 0 if not defined $this->{EngineBoots};
589	   $this->{EngineTime} = 0 if not defined $this->{EngineTime};
590
591	   $this->{SessPtr} =
592	     SNMP::_new_v3_session($this->{Version},
593				   $this->{DestHost},
594				   $this->{Retries},
595				   $this->{Timeout},
596				   $this->{SecName},
597				   $this->{SecLevel},
598				   $this->{SecEngineId},
599				   $this->{ContextEngineId},
600				   $this->{Context},
601				   $this->{AuthProto},
602				   $this->{AuthPass},
603				   $this->{PrivProto},
604				   $this->{PrivPass},
605				   $this->{EngineBoots},
606				   $this->{EngineTime},
607				   $this->{AuthMasterKey},
608				   length($this->{AuthMasterKey}),
609				   $this->{PrivMasterKey},
610				   length($this->{PrivMasterKey}),
611				   $this->{AuthLocalizedKey},
612				   length($this->{AuthLocalizedKey}),
613				   $this->{PrivLocalizedKey},
614				   length($this->{PrivLocalizedKey}),
615				  );
616       }
617   }
618   unless ($this->{SessPtr}) {
619       warn("unable to create session") if $SNMP::verbose;
620       return undef;
621   }
622
623   SNMP::initMib($SNMP::auto_init_mib); # ensures that *some* mib is loaded
624
625   $this->{UseLongNames} = $SNMP::use_long_names
626       unless exists $this->{UseLongNames};
627   $this->{UseSprintValue} = $SNMP::use_sprint_value
628       unless exists $this->{UseSprintValue};
629   $this->{BestGuess} = $SNMP::best_guess unless exists $this->{BestGuess};
630   $this->{NonIncreasing} ||= $SNMP::non_increasing;
631   $this->{UseEnums} = $SNMP::use_enums unless exists $this->{UseEnums};
632   $this->{UseNumeric} = $SNMP::use_numeric unless exists $this->{UseNumeric};
633
634   # Force UseLongNames if UseNumeric is in use.
635   $this->{UseLongNames}++  if $this->{UseNumeric};
636
637   bless $this, $type;
638}
639
640sub update {
641# *Not Implemented*
642# designed to update the fields of session to allow retargetting to different
643# host, community name change, timeout, retry changes etc. Unfortunately not
644# working yet because some updates (the address in particular) need to be
645# done on the internal session pointer which cannot be fetched w/o touching
646# globals at this point which breaks win32. A patch to the net-snmp toolkit
647# is needed
648   my $this = shift;
649   my ($name, $aliases, $host_type, $len, $thisaddr);
650   my %new_fields = @_;
651
652   @$this{keys %new_fields} = values %new_fields;
653
654   $this->{UseLongNames} = $SNMP::use_long_names
655       unless exists $this->{UseLongNames};
656   $this->{UseSprintValue} = $SNMP::use_sprint_value
657       unless exists $this->{UseSprintValue};
658   $this->{BestGuess} = $SNMP::best_guess unless exists $this->{BestGuess};
659   $this->{NonIncreasing} ||= $SNMP::non_increasing;
660   $this->{UseEnums} = $SNMP::use_enums unless exists $this->{UseEnums};
661   $this->{UseNumeric} = $SNMP::use_numeric unless exists $this->{UseNumeric};
662
663   # Force UseLongNames if UseNumeric is in use.
664   $this->{UseLongNames}++  if $this->{UseNumeric};
665
666   SNMP::_update_session($this->{Version},
667		 $this->{Community},
668		 $this->{DestHost},
669		 $this->{RemotePort},
670		 $this->{LocalPort},
671		 $this->{Retries},
672		 $this->{Timeout},
673		);
674
675
676}
677
678sub set {
679   my $this = shift;
680   my $vars = shift;
681   my $varbind_list_ref;
682   my $res = 0;
683
684   if (ref($vars) =~ /SNMP::VarList/) {
685     $varbind_list_ref = $vars;
686   } elsif (ref($vars) =~ /SNMP::Varbind/) {
687     $varbind_list_ref = [$vars];
688   } elsif (ref($vars) =~ /ARRAY/) {
689     $varbind_list_ref = [$vars];
690     $varbind_list_ref = $vars if ref($$vars[0]) =~ /ARRAY/;
691   } else {
692     #$varbind_list_ref = [[$tag, $iid, $val]];
693     my $split_vars = SNMP::split_vars($vars);
694     my $val = shift;
695     push @$split_vars,$val;
696     $varbind_list_ref = [$split_vars];
697   }
698   my $cb = shift;
699
700   $res = SNMP::_set($this, $varbind_list_ref, $cb);
701}
702
703sub get {
704   my $this = shift;
705   my $vars = shift;
706   my ($varbind_list_ref, @res);
707
708   if (ref($vars) =~ /SNMP::VarList/) {
709     $varbind_list_ref = $vars;
710   } elsif (ref($vars) =~ /SNMP::Varbind/) {
711     $varbind_list_ref = [$vars];
712   } elsif (ref($vars) =~ /ARRAY/) {
713     $varbind_list_ref = [$vars];
714     $varbind_list_ref = $vars if ref($$vars[0]) =~ /ARRAY/;
715   } else {
716     $varbind_list_ref = [SNMP::split_vars($vars)];
717   }
718
719   my $cb = shift;
720
721   @res = SNMP::_get($this, $this->{RetryNoSuch}, $varbind_list_ref, $cb);
722
723   return(wantarray() ? @res : $res[0]);
724}
725
726
727my $have_netsnmp_oid = eval { require NetSNMP::OID; };
728sub gettable {
729
730    #
731    # getTable
732    # --------
733    #
734    # Get OIDs starting at $table_oid, and continue down the tree
735    # until we get to an OID which does not start with $table_oid,
736    # i.e. we have reached the end of this table.
737    #
738
739    my $state;
740
741    my ($this, $root_oid, @options) = @_;
742    $state->{'options'} = {@options};
743    my ($textnode, $varbinds, $vbl, $res, $repeat);
744
745    # translate the OID into numeric form if its not
746    if ($root_oid !~ /^[\.0-9]+$/) {
747	$textnode = $root_oid;
748	$root_oid = SNMP::translateObj($root_oid);
749    } else {
750	$textnode = SNMP::translateObj($root_oid);
751    }
752
753    # bail if we don't have a valid oid.
754    return if (!$root_oid);
755
756    # deficed if we're going to parse indexes
757    my $parse_indexes = (defined($state->{'options'}{'noindexes'})) ?
758      0 : $have_netsnmp_oid;
759
760    # get the list of columns we should look at.
761    my @columns;
762    if (!$state->{'options'}{'columns'}) {
763	if ($textnode) {
764	    my %indexes;
765
766	    if ($parse_indexes) {
767		# get indexes
768		my @indexes =
769		  @{$SNMP::MIB{$textnode}{'children'}[0]{'indexes'} || []};
770		# quick translate into a hash
771		map { $indexes{$_} = 1; } @indexes;
772	    }
773
774	    # calculate the list of accessible columns that aren't indexes
775	    my $children = $SNMP::MIB{$textnode}{'children'}[0]{'children'};
776	    foreach my $c (@$children) {
777		push @{$state->{'columns'}},
778		  $root_oid . ".1." . $c->{'subID'}
779		    if (!$indexes{$c->{'label'}});
780	    }
781	    if ($#{$state->{'columns'}} == -1) {
782		# some tables are only indexes, and we need to walk at
783		# least one column.  We pick the last.
784		push @{$state->{'columns'}}, $root_oid . ".1." .
785		  $children->[$#$children]{'subID'}
786		  if ref($state) eq 'HASH' and ref($children) eq 'ARRAY';
787	    }
788	}
789    } else {
790	# XXX: requires specification in numeric OID...  ack.!
791	@{$state->{'columns'}} = @{$state->{'options'}{'columns'}};
792
793	# if the columns aren't numeric, we need to turn them into
794	# numeric columns...
795	map {
796	    if ($_ !~ /\.1\.3/) {
797		$_ = $SNMP::MIB{$_}{'objectID'};
798	    }
799	} @{$state->{'columns'}};
800    }
801
802    # create the initial walking info.
803    foreach my $c (@{$state->{'columns'}}) {
804	push @{$state->{'varbinds'}}, [$c];
805	push @{$state->{'stopconds'}}, $c;
806    }
807
808    if ($#{$state->{'varbinds'}} == -1) {
809	print STDERR "ack: gettable failed to find any columns to look for.\n";
810	return;
811    }
812
813    $vbl = $state->{'varbinds'};
814
815    my $repeatcount;
816    if ($this->{Version} eq '1' || $state->{'options'}{nogetbulk}) {
817	$state->{'repeatcount'} = 1;
818    } elsif ($state->{'options'}{'repeat'}) {
819	$state->{'repeatcount'} = $state->{'options'}{'repeat'};
820    } elsif ($#{$state->{'varbinds'}} == -1) {
821	$state->{'repeatcount'} = 1;
822    } else {
823	# experimentally determined maybe guess at a best repeat value
824	# 1000 bytes max (safe), 30 bytes average for encoding of the
825	# varbind (experimentally determined to be closer to
826	# 26.  Again, being safe.  Then devide by the number of
827	# varbinds.
828	$state->{'repeatcount'} = int(1000 / 36 / ($#{$state->{'varbinds'}} + 1));
829    }
830    # Make sure we run at least once
831    if ($state->{'repeatcount'} < 1) {
832	$state->{'repeatcount'} = 1;
833    }
834
835    #
836    # if we've been configured with a callback, then call the
837    # sub-functions with a callback to our own "next" processing
838    # function (_gettable_do_it).  or else call the blocking method and
839    # call the next processing function ourself.
840    #
841    if ($state->{'options'}{'callback'}) {
842	if ($this->{Version} ne '1' && !$state->{'options'}{'nogetbulk'}) {
843	    $res = $this->getbulk(0, $state->{'repeatcount'}, $vbl,
844				  [\&_gettable_do_it, $this, $vbl,
845				   $parse_indexes, $textnode, $state]);
846	} else {
847	    $res = $this->getnext($vbl,
848				  [\&_gettable_do_it, $this, $vbl,
849				   $parse_indexes, $textnode, $state]);
850	}
851    } else {
852	if ($this->{Version} ne '1' && !$state->{'options'}{'nogetbulk'}) {
853	    $res = $this->getbulk(0, $state->{'repeatcount'}, $vbl);
854	} else {
855	    $res = $this->getnext($vbl);
856	}
857	return $this->_gettable_do_it($vbl, $parse_indexes, $textnode, $state);
858    }
859    return 0;
860}
861
862sub _gettable_do_it() {
863    my ($this, $vbl, $parse_indexes, $textnode, $state) = @_;
864
865    my ($res);
866
867    $vbl = $_[$#_] if ($state->{'options'}{'callback'});
868
869    while ($#$vbl > -1 && !$this->{ErrorNum}) {
870	if (($#$vbl + 1) % ($#{$state->{'stopconds'}} + 1) != 0) {
871	    if ($vbl->[$#$vbl][2] ne 'ENDOFMIBVIEW') {
872		# unless it's an end of mib view we didn't get the
873		# proper number of results back.
874		print STDERR "ack: gettable results not appropriate\n";
875	    }
876	    my @k = keys(%{$state->{'result_hash'}});
877	    last if ($#k > -1);  # bail with what we have
878	    return;
879	}
880
881	$state->{'varbinds'} = [];
882	my $newstopconds;
883
884	my $lastsetstart = ($state->{'repeatcount'}-1) * ($#{$state->{'stopconds'}}+1);
885
886	for (my $i = 0; $i <= $#$vbl; $i++) {
887	    my $row_oid = SNMP::translateObj($vbl->[$i][0]);
888	    my $row_text = $vbl->[$i][0];
889	    my $row_index = $vbl->[$i][1];
890	    my $row_value = $vbl->[$i][2];
891	    my $row_type = $vbl->[$i][3];
892
893	    if ($row_oid =~
894		/^$state->{'stopconds'}[$i % ($#{$state->{'stopconds'}}+1)]/ &&
895		$row_value ne 'ENDOFMIBVIEW' ){
896
897		if ($row_type eq "OBJECTID") {
898
899		    # If the value returned is an OID, translate this
900		    # back in to a textual OID
901
902		    $row_value = SNMP::translateObj($row_value);
903
904		}
905
906		# Place the results in a hash
907
908		$state->{'result_hash'}{$row_index}{$row_text} = $row_value;
909
910		# continue past this next time
911		if ($i >= $lastsetstart) {
912		    push @$newstopconds,
913		      $state->{'stopconds'}->[$i%($#{$state->{'stopconds'}}+1)];
914		    push @{$state->{'varbinds'}},[$vbl->[$i][0],$vbl->[$i][1]];
915		}
916	    }
917	}
918	if ($#$newstopconds == -1) {
919	    last;
920	}
921	if ($#{$state->{'varbinds'}} == -1) {
922	    print "gettable ack.  shouldn't get here\n";
923	}
924	$vbl = $state->{'varbinds'};
925	$state->{'stopconds'} = $newstopconds;
926
927        #
928        # if we've been configured with a callback, then call the
929        # sub-functions with a callback to our own "next" processing
930        # function (_gettable_do_it).  or else call the blocking method and
931        # call the next processing function ourself.
932        #
933	if ($state->{'options'}{'callback'}) {
934	    if ($this->{Version} ne '1' && !$state->{'options'}{'nogetbulk'}) {
935		$res = $this->getbulk(0, $state->{'repeatcount'}, $vbl,
936				      [\&_gettable_do_it, $this, $vbl,
937				       $parse_indexes, $textnode, $state]);
938	    } else {
939		$res = $this->getnext($vbl,
940				      [\&_gettable_do_it, $this, $vbl,
941				       $parse_indexes, $textnode, $state]);
942	    }
943	    return;
944	} else {
945	    if ($this->{Version} ne '1' && !$state->{'options'}{'nogetbulk'}) {
946		$res = $this->getbulk(0, $state->{'repeatcount'}, $vbl);
947	    } else {
948		$res = $this->getnext($vbl);
949	    }
950	}
951    }
952
953    # finish up
954    _gettable_end_routine($state, $parse_indexes, $textnode);
955
956    # return the hash if no callback was specified
957    if (!$state->{'options'}{'callback'}) {
958	return($state->{'result_hash'});
959    }
960
961    #
962    # if they provided a callback, call it
963    #   (if an array pass the args as well)
964    #
965    if (ref($state->{'options'}{'callback'}) eq 'ARRAY') {
966	my $code = shift @{$state->{'options'}{'callback'}};
967	$code->(@{$state->{'options'}{'callback'}}, $state->{'result_hash'});
968    } else {
969	$state->{'options'}{'callback'}->($state->{'result_hash'});
970    }
971}
972
973sub _gettable_end_routine {
974    my ($state, $parse_indexes, $textnode) = @_;
975    if ($parse_indexes) {
976	my @indexes = @{$SNMP::MIB{$textnode}{'children'}[0]{'indexes'}};
977	my $i;
978	foreach my $trow (keys(%{$state->{'result_hash'}})) {
979	    my $noid = new NetSNMP::OID($state->{'columns'}[0] . "." . $trow);
980	    if (!$noid) {
981		print STDERR "***** ERROR parsing $state->{'columns'}[0].$trow MIB OID\n";
982		next;
983	    }
984	    my $nindexes = $noid->get_indexes();
985	    if (!$nindexes || ref($nindexes) ne 'ARRAY' ||
986		$#indexes != $#$nindexes) {
987		print STDERR "***** ERROR parsing $state->{'columns'}[0].$trow MIB indexes:\n  $noid => " . ref($nindexes) . "\n   [should be an ARRAY]\n  expended # indexes = $#indexes\n";
988		if (ref($nindexes) eq 'ARRAY') {
989		    print STDERR "***** ERROR parsing $state->{'columns'}[0].$trow MIB indexes: " . ref($nindexes) . " $#indexes $#$nindexes\n";
990		}
991		next;
992	    }
993
994	    for ($i = 0; $i <= $#indexes; $i++) {
995		$state->{'result_hash'}{$trow}{$indexes[$i]} = $nindexes->[$i];
996	    }
997	}
998    }
999}
1000
1001
1002sub fget {
1003   my $this = shift;
1004   my $vars = shift;
1005   my ($varbind_list_ref, @res);
1006
1007   if (ref($vars) =~ /SNMP::VarList/) {
1008     $varbind_list_ref = $vars;
1009   } elsif (ref($vars) =~ /SNMP::Varbind/) {
1010     $varbind_list_ref = [$vars];
1011   } elsif (ref($vars) =~ /ARRAY/) {
1012     $varbind_list_ref = [$vars];
1013     $varbind_list_ref = $vars if ref($$vars[0]) =~ /ARRAY/;
1014   } else {
1015     $varbind_list_ref = [SNMP::split_vars($vars)];
1016   }
1017
1018   my $cb = shift;
1019
1020   SNMP::_get($this, $this->{RetryNoSuch}, $varbind_list_ref, $cb);
1021
1022   foreach my $varbind (@$varbind_list_ref) {
1023     my $sub = $this->{VarFormats}{SNMP::Varbind::tag($varbind)} ||
1024	      $this->{TypeFormats}{SNMP::Varbind::type($varbind)};
1025     &$sub($varbind) if defined $sub;
1026     push(@res, SNMP::Varbind::val($varbind));
1027   }
1028
1029   return(wantarray() ? @res : $res[0]);
1030}
1031
1032sub getnext {
1033   my $this = shift;
1034   my $vars = shift;
1035   my ($varbind_list_ref, @res);
1036
1037   if (ref($vars) =~ /SNMP::VarList/) {
1038     $varbind_list_ref = $vars;
1039   } elsif (ref($vars) =~ /SNMP::Varbind/) {
1040     $varbind_list_ref = [$vars];
1041   } elsif (ref($vars) =~ /ARRAY/) {
1042     $varbind_list_ref = [$vars];
1043     $varbind_list_ref = $vars if ref($$vars[0]) =~ /ARRAY/;
1044   } else {
1045     $varbind_list_ref = [SNMP::split_vars($vars)];
1046   }
1047
1048   my $cb = shift;
1049
1050   @res = SNMP::_getnext($this, $varbind_list_ref, $cb);
1051
1052   return(wantarray() ? @res : $res[0]);
1053}
1054
1055sub fgetnext {
1056   my $this = shift;
1057   my $vars = shift;
1058   my ($varbind_list_ref, @res);
1059
1060   if (ref($vars) =~ /SNMP::VarList/) {
1061     $varbind_list_ref = $vars;
1062   } elsif (ref($vars) =~ /SNMP::Varbind/) {
1063     $varbind_list_ref = [$vars];
1064   } elsif (ref($vars) =~ /ARRAY/) {
1065     $varbind_list_ref = [$vars];
1066     $varbind_list_ref = $vars if ref($$vars[0]) =~ /ARRAY/;
1067   } else {
1068     $varbind_list_ref = [SNMP::split_vars($vars)];
1069   }
1070
1071   my $cb = shift;
1072
1073   SNMP::_getnext($this, $varbind_list_ref, $cb);
1074
1075   foreach my $varbind (@$varbind_list_ref) {
1076     my $sub = $this->{VarFormats}{SNMP::Varbind::tag($varbind)} ||
1077	      $this->{TypeFormats}{SNMP::Varbind::type($varbind)};
1078     &$sub($varbind) if defined $sub;
1079     push(@res, SNMP::Varbind::val($varbind));
1080   }
1081
1082   return(wantarray() ? @res : $res[0]);
1083}
1084
1085sub getbulk {
1086   my $this = shift;
1087   my $nonrepeaters = shift;
1088   my $maxrepetitions = shift;
1089   my $vars = shift;
1090   my ($varbind_list_ref, @res);
1091
1092   if (ref($vars) =~ /SNMP::VarList/) {
1093     $varbind_list_ref = $vars;
1094   } elsif (ref($vars) =~ /SNMP::Varbind/) {
1095     $varbind_list_ref = [$vars];
1096   } elsif (ref($vars) =~ /ARRAY/) {
1097     $varbind_list_ref = [$vars];
1098     $varbind_list_ref = $vars if ref($$vars[0]) =~ /ARRAY/;
1099   } else {
1100     $varbind_list_ref = [SNMP::split_vars($vars)];
1101   }
1102
1103   my $cb = shift;
1104
1105   @res = SNMP::_getbulk($this, $nonrepeaters, $maxrepetitions, $varbind_list_ref, $cb);
1106
1107   return(wantarray() ? @res : $res[0]);
1108}
1109
1110sub bulkwalk {
1111   my $this = shift;
1112   my $nonrepeaters = shift;
1113   my $maxrepetitions = shift;
1114   my $vars = shift;
1115   my ($varbind_list_ref, @res);
1116
1117   if (ref($vars) =~ /SNMP::VarList/) {
1118      $varbind_list_ref = $vars;
1119   } elsif (ref($vars) =~ /SNMP::Varbind/) {
1120      $varbind_list_ref = [$vars];
1121   } elsif (ref($vars) =~ /ARRAY/) {
1122      $varbind_list_ref = [$vars];
1123      $varbind_list_ref = $vars if ref($$vars[0]) =~ /ARRAY/;
1124   } else {
1125      # my ($tag, $iid) = ($vars =~ /^((?:\.\d+)+|\w+)\.?(.*)$/);
1126      my ($tag, $iid) = ($vars =~ /^(.*?)\.?(\d+)+$/);
1127      $varbind_list_ref = [[$tag, $iid]];
1128   }
1129
1130   if (scalar @$varbind_list_ref == 0) {
1131      $this->{ErrorNum} = SNMP::constant("SNMPERR_GENERR", 0);
1132      $this->{ErrorStr} = "cannot bulkwalk() empty variable list";
1133      return undef;
1134   }
1135   if (scalar @$varbind_list_ref < $nonrepeaters) {
1136      $this->{ErrorNum} = SNMP::constant("SNMPERR_GENERR", 0);
1137      $this->{ErrorStr} = "bulkwalk() needs at least $nonrepeaters varbinds";
1138      return undef;
1139   }
1140
1141   my $cb = shift;
1142   @res = SNMP::_bulkwalk($this, $nonrepeaters, $maxrepetitions,
1143						$varbind_list_ref, $cb);
1144
1145   # Return, in list context, a copy of the array of arrays of Varbind refs.
1146   # In scalar context, return either a reference to the array of arrays of
1147   # Varbind refs, or the request ID for an asynchronous bulkwalk.  This is
1148   # a compromise between the getbulk()-ish return, and the more useful array
1149   # of arrays of Varbinds return from the synchronous bulkwalk().
1150   #
1151   return @res if (wantarray());
1152   return defined($cb) ? $res[0] : \@res;
1153}
1154
1155my %trap_type = (coldStart => 0, warmStart => 1, linkDown => 2, linkUp => 3,
1156	      authFailure => 4, egpNeighborLoss => 5, specific => 6 );
1157sub trap {
1158# (v1) enterprise, agent, generic, specific, uptime, <vars>
1159# $sess->trap(enterprise=>'.1.3.6.1.4.1.2021', # or 'ucdavis' [default]
1160#             agent => '127.0.0.1', # or 'localhost',[default 1st intf on host]
1161#             generic => specific,  # can be omitted if 'specific' supplied
1162#             specific => 5,        # can be omitted if 'generic' supplied
1163#             uptime => 1234,       # default to localhost uptime (0 on win32)
1164#             [[ifIndex, 1, 1],[sysLocation, 0, "here"]]); # optional vars
1165#                                                          # always last
1166# (v2) oid, uptime, <vars>
1167# $sess->trap(uptime => 1234,
1168#             oid => 'snmpRisingAlarm',
1169#             [[ifIndex, 1, 1],[sysLocation, 0, "here"]]); # optional vars
1170#                                                          # always last
1171#                                                          # always last
1172
1173
1174   my $this = shift;
1175   my $vars = pop if ref($_[$#_]); # last arg may be varbind or varlist
1176   my %param = @_;
1177   my ($varbind_list_ref, @res);
1178
1179   if (ref($vars) =~ /SNMP::VarList/) {
1180     $varbind_list_ref = $vars;
1181   } elsif (ref($vars) =~ /SNMP::Varbind/) {
1182     $varbind_list_ref = [$vars];
1183   } elsif (ref($vars) =~ /ARRAY/) {
1184     $varbind_list_ref = [$vars];
1185     $varbind_list_ref = $vars if ref($$vars[0]) =~ /ARRAY/;
1186   }
1187
1188   if ($this->{Version} =~ '^1') {
1189       my $enterprise = $param{enterprise} || 'ucdavis';
1190       $enterprise = SNMP::translateObj($enterprise)
1191	   unless $enterprise =~ /^[\.\d]+$/;
1192       my $agent = $param{agent} || '';
1193       my $generic = $param{generic} || 'specific';
1194       $generic = $trap_type{$generic} || $generic;
1195       my $uptime = $param{uptime} || SNMP::_sys_uptime();
1196       my $specific = $param{specific} || 0;
1197       @res = SNMP::_trapV1($this, $enterprise, $agent, $generic, $specific,
1198			  $uptime, $varbind_list_ref);
1199   } elsif ($this->{Version} =~ '^[23]') {
1200       my $trap_oid = $param{oid} || $param{trapoid} || '.0.0';
1201       my $uptime = $param{uptime} || SNMP::_sys_uptime();
1202       @res = SNMP::_trapV2($this, $uptime, $trap_oid, $varbind_list_ref);
1203   } else {
1204     warn("error:trap: Unsupported SNMP version " . $this->{Version} . "\n");
1205   }
1206
1207   return(wantarray() ? @res : $res[0]);
1208}
1209
1210sub inform {
1211# (v3) oid, uptime, <vars>
1212# $sess->inform(uptime => 1234,
1213#             oid => 'coldStart',
1214#             [[ifIndex, 1, 1],[sysLocation, 0, "here"]]); # optional vars
1215#                                                          # then callback
1216                                                           # always last
1217
1218
1219   my $this = shift;
1220   my $vars;
1221   my $cb;
1222   $cb = pop if ref($_[$#_]) eq 'CODE'; # last arg may be code
1223   $vars = pop if ref($_[$#_]); # varbind or varlist
1224   my %param = @_;
1225   my ($varbind_list_ref, @res);
1226
1227   if (ref($vars) =~ /SNMP::VarList/) {
1228     $varbind_list_ref = $vars;
1229   } elsif (ref($vars) =~ /SNMP::Varbind/) {
1230     $varbind_list_ref = [$vars];
1231   } elsif (ref($vars) =~ /ARRAY/) {
1232     $varbind_list_ref = [$vars];
1233     $varbind_list_ref = $vars if ref($$vars[0]) =~ /ARRAY/;
1234   }
1235
1236   my $trap_oid = $param{oid} || $param{trapoid};
1237   my $uptime = $param{uptime} || SNMP::_sys_uptime();
1238
1239   if ($this->{Version} =~ '^[23]') {
1240     @res = SNMP::_inform($this, $uptime, $trap_oid, $varbind_list_ref, $cb);
1241   } else {
1242     warn("error:inform: This version doesn't support the command\n");
1243   }
1244
1245   return(wantarray() ? @res : $res[0]);
1246}
1247
1248package SNMP::TrapSession;
1249@SNMP::TrapSession::ISA = ('SNMP::Session');
1250
1251sub new {
1252   my $type = shift;
1253
1254   # allow override of remote SNMP trap port
1255   unless (grep(/RemotePort/, @_)) {
1256       push(@_, 'RemotePort', 162); # push on new default for trap session
1257   }
1258
1259   SNMP::Session::new($type, @_);
1260}
1261
1262package SNMP::Varbind;
1263
1264$SNMP::Varbind::tag_f = 0;
1265$SNMP::Varbind::iid_f = 1;
1266$SNMP::Varbind::val_f = 2;
1267$SNMP::Varbind::type_f = 3;
1268$SNMP::Varbind::time_f = 4;
1269
1270sub new {
1271   my $type = shift;
1272   my $this = shift;
1273   $this ||= [];
1274   bless $this;
1275}
1276
1277sub tag {
1278  $_[0]->[$SNMP::Varbind::tag_f];
1279}
1280
1281sub iid {
1282  $_[0]->[$SNMP::Varbind::iid_f];
1283}
1284
1285sub val {
1286  $_[0]->[$SNMP::Varbind::val_f];
1287}
1288
1289sub type {
1290  $_[0]->[$SNMP::Varbind::type_f];
1291}
1292
1293sub name {
1294   if (defined($_[0]->[$SNMP::Varbind::iid_f]) && ($_[0]->[$SNMP::Varbind::iid_f] =~ m/^[0-9]+$/)) {
1295      return $_[0]->[$SNMP::Varbind::tag_f] . "." . $_[0]->[$SNMP::Varbind::iid_f];
1296   }
1297
1298   return $_[0]->[$SNMP::Varbind::tag_f];
1299}
1300
1301sub fmt {
1302    my $self = shift;
1303    return $self->name . " = \"" . $self->val . "\" (" . $self->type . ")";
1304}
1305
1306
1307#sub DESTROY {
1308#    print "SNMP::Varbind::DESTROY($_[0])\n";
1309#}
1310
1311package SNMP::VarList;
1312
1313sub new {
1314   my $type = shift;
1315   my $this = [];
1316   my $varb;
1317   foreach $varb (@_) {
1318     $varb = new SNMP::Varbind($varb) unless ref($varb) =~ /SNMP::Varbind/;
1319     push(@{$this}, $varb);
1320   }
1321
1322   bless $this;
1323}
1324
1325#sub DESTROY {
1326#    print "SNMP::VarList::DESTROY($_[0])\n";
1327#}
1328
1329package SNMP::DEBUGGING;
1330# controls info/debugging output from SNMP module and libsnmp
1331# $SNMP::debugging == 1    =>   enables general info and warning output
1332#                                (eqiv. to setting $SNMP::verbose)
1333# $SNMP::debugging == 2    =>   enables do_debugging from libsnmp as well
1334# $SNMP::debugging == 3    =>   enables packet_dump from libsnmp as well
1335sub TIESCALAR { my $class = shift; my $val; bless \$val, $class; }
1336
1337sub FETCH { ${$_[0]}; }
1338
1339sub STORE {
1340    $SNMP::verbose = $_[1];
1341    SNMP::_set_debugging($_[1]>1);
1342    $SNMP::dump_packet = ($_[1]>2);
1343    ${$_[0]} = $_[1];
1344}
1345
1346sub DELETE {
1347    $SNMP::verbose = 0;
1348    SNMP::_set_debugging(0);
1349    $SNMP::dump_packet = 0;
1350    ${$_[0]} = undef;
1351}
1352
1353package SNMP::DEBUG_INTERNALS;		# Controls SNMP.xs debugging.
1354sub TIESCALAR { my $class = shift; my $val; bless \$val, $class; }
1355
1356sub FETCH { ${$_[0]}; }
1357
1358sub STORE {
1359    SNMP::_debug_internals($_[1]);
1360    ${$_[0]} = $_[1];
1361}
1362
1363sub DELETE {
1364    SNMP::_debug_internals(0);
1365    ${$_[0]} = undef;
1366}
1367
1368package SNMP::DUMP_PACKET;
1369# controls packet dump output from libsnmp
1370
1371sub TIESCALAR { my $class = shift; my $val; bless \$val, $class; }
1372
1373sub FETCH { ${$_[0]}; }
1374
1375sub STORE { SNMP::_dump_packet($_[1]); ${$_[0]} = $_[1]; }
1376
1377sub DELETE { SNMP::_dump_packet(0); ${$_[0]} = 0; }
1378
1379package SNMP::MIB;
1380
1381sub TIEHASH {
1382    bless {};
1383}
1384
1385sub FETCH {
1386    my $this = shift;
1387    my $key = shift;
1388
1389    if (!defined $this->{$key}) {
1390	tie(%{$this->{$key}}, 'SNMP::MIB::NODE', $key) or return undef;
1391    }
1392    $this->{$key};
1393}
1394
1395sub STORE {
1396    warn "STORE(@_) : write access to the MIB not implemented\n";
1397}
1398
1399sub DELETE {
1400    delete $_[0]->{$_[1]}; # just delete cache entry
1401}
1402
1403sub FIRSTKEY { return '.1'; } # this should actually start at .0 but
1404                              # because nodes are not stored in lexico
1405                              # order in ucd-snmp node tree walk will
1406                              # miss most of the tree
1407sub NEXTKEY { # this could be sped up by using an XS __get_next_oid maybe
1408   my $node = $_[0]->FETCH($_[1])->{nextNode};
1409   $node->{objectID};
1410}
1411sub EXISTS { exists $_[0]->{$_[1]} || $_[0]->FETCH($_[1]); }
1412sub CLEAR { undef %{$_[0]}; } # clear the cache
1413
1414package SNMP::MIB::NODE;
1415my %node_elements =
1416    (
1417     objectID => 0, # dotted decimal fully qualified OID
1418     label => 0,    # leaf textual identifier (e.g., 'sysDescr')
1419     subID => 0,    # leaf numeric OID component of objectID (e.g., '1')
1420     moduleID => 0, # textual identifier for module (e.g., 'RFC1213-MIB')
1421     parent => 0,   # parent node
1422     children => 0, # array reference of children nodes
1423     indexes => 0,  # returns array of column labels
1424     implied => 0,  # boolean: is the last index IMPLIED
1425     varbinds => 0, # returns array of trap/notification varbinds
1426     nextNode => 0, # next lexico node (BUG! does not return in lexico order)
1427     type => 0,     # returns simple type (see getType for values)
1428     access => 0,   # returns ACCESS (ReadOnly, ReadWrite, WriteOnly,
1429                    # NoAccess, Notify, Create)
1430     status => 0,   # returns STATUS (Mandatory, Optional, Obsolete,
1431                    # Deprecated)
1432     syntax => 0,   # returns 'textualConvention' if defined else 'type'
1433     textualConvention => 0, # returns TEXTUAL-CONVENTION
1434     units => 0,    # returns UNITS
1435     hint => 0,     # returns HINT
1436     enums => 0,    # returns hash ref {tag => num, ...}
1437     ranges => 0,   # returns array ref of hash ref [{low => num, high => num}]
1438     defaultValue => 0, # returns default value
1439     description => 0, # returns DESCRIPTION ($SNMP::save_descriptions must
1440                    # be set prior to MIB initialization/parsing
1441     augments => 0, # textual identifier of augmented object
1442    );
1443
1444# sub TIEHASH - implemented in SNMP.xs
1445
1446# sub FETCH - implemented in SNMP.xs
1447
1448sub STORE {
1449    warn "STORE(@_): write access to MIB node not implemented\n";
1450}
1451
1452sub DELETE {
1453    warn "DELETE(@_): write access to MIB node not implemented\n";
1454}
1455
1456sub FIRSTKEY { my $k = keys %node_elements; (each(%node_elements))[0]; }
1457sub NEXTKEY { (each(%node_elements))[0]; }
1458sub EXISTS { exists($node_elements{$_[1]}); }
1459sub CLEAR {
1460    warn "CLEAR(@_): write access to MIB node not implemented\n";
1461}
1462
1463#sub DESTROY {
1464#    warn "DESTROY(@_): write access to MIB node not implemented\n";
1465#    # print "SNMP::MIB::NODE::DESTROY : $_[0]->{label} ($_[0])\n";
1466#}
1467package SNMP::MIB::SAVE_DESCR;
1468
1469sub TIESCALAR { my $class = shift; my $val; bless \$val, $class; }
1470
1471sub FETCH { ${$_[0]}; }
1472
1473sub STORE { SNMP::_set_save_descriptions($_[1]); ${$_[0]} = $_[1]; }
1474
1475sub DELETE { SNMP::_set_save_descriptions(0); ${$_[0]} = 0; }
1476
1477package SNMP::MIB::REPLACE_NEWER;               # Controls MIB parsing
1478
1479sub TIESCALAR { my $class = shift; my $val; bless \$val, $class; }
1480
1481sub FETCH { ${$_[0]}; }
1482
1483sub STORE {
1484    SNMP::_set_replace_newer($_[1]);
1485    ${$_[0]} = $_[1];
1486}
1487
1488sub DELETE {
1489    SNMP::_set_replace_newer(0);
1490    ${$_[0]} = 0;
1491}
1492
1493package SNMP::MIB::MIB_OPTIONS;
1494
1495sub TIESCALAR { my $class = shift; my $val; bless \$val, $class; }
1496
1497sub FETCH { ${$_[0]}; }
1498
1499sub STORE { SNMP::_mib_toggle_options($_[1]); ${$_[0]} = $_[1]; }
1500
1501sub DELETE { SNMP::_mib_toggle_options(0); ${$_[0]} = ''; }
1502
1503package SNMP;
1504END{SNMP::_sock_cleanup() if defined &SNMP::_sock_cleanup;}
1505# Autoload methods go after __END__, and are processed by the autosplit prog.
1506
15071;
1508__END__
1509
1510=head1 NAME
1511
1512SNMP - The Perl5 'SNMP' Extension Module for the Net-SNMP SNMP package.
1513
1514=head1 SYNOPSIS
1515
1516 use SNMP;
1517 ...
1518 $sess = new SNMP::Session(DestHost => localhost, Community => public);
1519 $val = $sess->get('sysDescr.0');
1520 ...
1521 $vars = new SNMP::VarList([sysDescr,0], [sysContact,0], [sysLocation,0]);
1522 @vals = $sess->get($vars);
1523 ...
1524 $vb = new SNMP::Varbind();
1525 do {
1526    $val = $sess->getnext($vb);
1527    print "@{$vb}\n";
1528 } until ($sess->{ErrorNum});
1529 ...
1530 $SNMP::save_descriptions = 1;
1531 SNMP::initMib(); # assuming mib is not already loaded
1532 print "$SNMP::MIB{sysDescr}{description}\n";
1533
1534=head1 DESCRIPTION
1535
1536
1537Note: The perl SNMP 5.0 module which comes with net-snmp 5.0 and
1538higher is different than previous versions in a number of ways.  Most
1539importantly, it behaves like a proper net-snmp application and calls
1540init_snmp properly, which means it will read configuration files and
1541use those defaults where appropriate automatically parse MIB files,
1542etc.  This will likely affect your perl applications if you have, for
1543instance, default values set up in your snmp.conf file (as the perl
1544module will now make use of those defaults).  The documentation,
1545however, has sadly not been updated yet (aside from this note), nor is
1546the read_config default usage implementation fully complete.
1547
1548The basic operations of the SNMP protocol are provided by this module
1549through an object oriented interface for modularity and ease of use.
1550The primary class is SNMP::Session which encapsulates the persistent
1551aspects of a connection between the management application and the
1552managed agent. Internally the class is implemented as a blessed hash
1553reference. This class supplies 'get', 'getnext', 'set', 'fget', and
1554'fgetnext' method calls. The methods take a variety of input argument
1555formats and support both synchronous and asynchronous operation through
1556a polymorphic API (i.e., method behaviour varies dependent on args
1557passed - see below).
1558
1559=head1 SNMP::Session
1560
1561$sess = new SNMP::Session(DestHost => 'host', ...)
1562
1563The following arguments may be passed to new as a hash.
1564
1565=head2 Basic Options
1566
1567=over 4
1568
1569=item DestHost
1570
1571Hostname or IP address of the SNMP agent you want to talk to.
1572Specified in Net-SNMP formatted agent addresses.  These addresses
1573typically look like one of the following:
1574
1575  localhost
1576  tcp:localhost
1577  tls:localhost
1578  tls:localhost:9876
1579  udp6:[::1]:9876
1580  unix:/some/path/to/file/socket
1581
1582Defaults to 'localhost'.
1583
1584=item Version
1585
1586SNMP version to use.
1587
1588The default is taken from library configuration - probably 3 [1, 2
1589(same as 2c), 2c, 3].
1590
1591=item Timeout
1592
1593The number of micro-seconds to wait before resending a request.
1594
1595The default is '1000000'
1596
1597=item Retries
1598
1599The number of times to retry a request.
1600
1601The default is '5'
1602
1603=item RetryNoSuch
1604
1605If enabled NOSUCH errors in 'get' pdus will
1606be repaired, removing the varbind in error, and resent -
1607undef will be returned for all NOSUCH varbinds, when set
1608to '0' this feature is disabled and the entire get request
1609will fail on any NOSUCH error (applies to v1 only)
1610
1611The default is '0'.
1612
1613=back
1614
1615=head2 SNMPv3/TLS Options
1616
1617=over
1618
1619=item OurIdentity
1620
1621Our X.509 identity to use, which should either be a fingerprint or the
1622filename that holds the certificate.
1623
1624=item TheirIdentity
1625
1626The remote server's identity to connect to, specified as either a
1627fingerprint or a file name.  Either this must be specified, or the
1628hostname below along with a trust anchor.
1629
1630=item TheirHostname
1631
1632The remote server's hostname that is expected.  If their certificate
1633was signed by a CA then their hostname presented in the certificate
1634must match this value or the connection fails to be established (to
1635avoid man-in-the-middle attacks).
1636
1637=item TrustCert
1638
1639A trusted certificate to use as trust anchor (like a CA certificate)
1640for verifying a remote server's certificate.  If a CA certificate is
1641used to validate a certificate then the TheirHostname parameter must
1642also be specified to ensure their presented hostname in the certificate
1643matches.
1644
1645=back
1646
1647=head2 SNMPv3/USM Options
1648
1649=over
1650
1651=item SecName
1652
1653The SNMPv3 security name to use (most for SNMPv3 with USM).
1654
1655The default is 'initial'.
1656
1657=item SecLevel
1658
1659The SNMPv3 security level to use [noAuthNoPriv, authNoPriv, authPriv] (v3)
1660
1661The default is 'noAuthNoPriv'.
1662
1663=item SecEngineId
1664
1665The SNMPv3 security engineID to use (if the snmpv3 security model
1666needs it; for example USM). The format is as a string without the leading '0x'.
1667So if snmptrapd.conf has C<-e 0x8000000001020304>, use C<< SecEngineId =>
1668'8000000001020304' >>.
1669
1670The default is <none>, security engineID and it will be probed if not
1671supplied (v3)
1672
1673=item ContextEngineId
1674
1675The SNMPv3 context engineID to use.
1676
1677The default is the <none> and will be set either to the SecEngineId
1678value if set or discovered or will be discovered in other ways if
1679using TLS (RFC5343 based discovery).
1680
1681=item Context
1682
1683The SNMPv3 context name to use.
1684
1685The default is '' (an empty string)
1686
1687=item AuthProto
1688
1689The SNMPv3/USM authentication protocol to use [MD5, SHA].
1690
1691The default is 'MD5'.
1692
1693=item AuthPass
1694
1695The SNMPv3/USM authentication passphrase to use.
1696
1697default <none>, authentication passphrase
1698
1699=item PrivProto
1700
1701The SNMPv3/USM privacy protocol to use [DES, AES].
1702
1703The default is 'DES'.
1704
1705=item PrivPass
1706
1707The SNMPv3/USM privacy passphrase to use.
1708
1709default <none>, privacy passphrase (v3)
1710
1711=item AuthMasterKey
1712
1713=item PrivMasterKey
1714
1715=item AuthLocalizedKey
1716
1717=item PrivLocalizedKey
1718
1719Directly specified SNMPv3 USM user keys (used if you want to specify
1720the keys instead of deriving them from a password as above).
1721
1722=back
1723
1724=head2 SNMPv1 and SNMPv2c Options
1725
1726=over
1727
1728=item Community
1729
1730For SNMPv1 and SNMPv2c, the clear-text community name to use.
1731
1732The default is 'public'.
1733
1734=back
1735
1736=head2 Other Configuration Options
1737
1738=over
1739
1740=item VarFormats
1741
1742default 'undef', used by 'fget[next]', holds an hash
1743reference of output value formatters, (e.g., {<obj> =>
1744<sub-ref>, ... }, <obj> must match the <obj> and format
1745used in the get operation. A special <obj>, '*', may be
1746used to apply all <obj>s, the supplied sub is called to
1747translate the value to a new format. The sub is called
1748passing the Varbind as the arg
1749
1750=item TypeFormats
1751
1752default 'undef', used by 'fget[next]', holds an hash
1753reference of output value formatters, (e.g., {<type> =>
1754<sub-ref>, ... }, the supplied sub is called to translate
1755the value to a new format, unless a VarFormat mathces first
1756(e.g., $sess->{TypeFormats}{INTEGER} = \&mapEnum();
1757although this can be done more efficiently by enabling
1758$SNMP::use_enums or session creation param 'UseEnums')
1759
1760=item UseLongNames
1761
1762defaults to the value of SNMP::use_long_names at time
1763of session creation. set to non-zero to have <tags>
1764for 'getnext' methods generated preferring longer Mib name
1765convention (e.g., system.sysDescr vs just sysDescr)
1766
1767=item UseSprintValue
1768
1769defaults to the value of SNMP::use_sprint_value at time
1770of session creation. set to non-zero to have return values
1771for 'get' and 'getnext' methods formatted with the libraries
1772snprint_value function. This will result in certain data types
1773being returned in non-canonical format Note: values returned
1774with this option set may not be appropriate for 'set' operations
1775(see discussion of value formats in <vars> description section)
1776
1777=item UseEnums
1778
1779defaults to the value of SNMP::use_enums at time of session
1780creation. set to non-zero to have integer return values
1781converted to enumeration identifiers if possible, these values
1782will also be acceptable when supplied to 'set' operations
1783
1784=item UseNumeric
1785
1786defaults to the value of SNMP::use_numeric at time of session
1787creation. set to non-zero to have <tags> for get methods returned
1788as numeric OID's rather than descriptions.  UseLongNames will be
1789set so that the full OID is returned to the caller.
1790
1791=item BestGuess
1792
1793defaults to the value of SNMP::best_guess at time of session
1794creation. this setting controls how <tags> are parsed.  setting to
17950 causes a regular lookup.  setting to 1 causes a regular expression
1796match (defined as -Ib in snmpcmd) and setting to 2 causes a random
1797access lookup (defined as -IR in snmpcmd).
1798
1799=item NonIncreasing
1800
1801defaults to the value of SNMP::non_increasing at time of session
1802creation. this setting controls if a non-increasing OID during
1803bulkwalk will causes an error. setting to 0 causes the default
1804behaviour (which may, in very badly performing agents, result in a never-ending loop).
1805setting to 1 causes an error (OID not increasing) when this error occur.
1806
1807=item ErrorStr
1808
1809read-only, holds the error message assoc. w/ last request
1810
1811=item ErrorNum
1812
1813read-only, holds the snmp_err or staus of last request
1814
1815=item ErrorInd
1816
1817read-only, holds the snmp_err_index when appropriate
1818
1819=back
1820
1821Private variables:
1822
1823=over
1824
1825=item DestAddr
1826
1827internal field used to hold the translated DestHost field
1828
1829=item SessPtr
1830
1831internal field used to cache a created session structure
1832
1833=item RemotePort
1834
1835Obsolete.  Please use the DestHost specifier to indicate the hostname
1836and port combination instead of this paramet.
1837
1838=back
1839
1840=head2 SNMP::Session methods
1841
1842=over
1843
1844=item $sess->update(E<lt>fieldsE<gt>)
1845
1846Updates the SNMP::Session object with the values fields
1847passed in as a hash list (similar to new(E<lt>fieldsE<gt>))
1848B<(WARNING! not fully implemented)>
1849
1850=item $sess->get(E<lt>varsE<gt> [,E<lt>callbackE<gt>])
1851
1852do SNMP GET, multiple <vars> formats accepted.
1853for syncronous operation <vars> will be updated
1854with value(s) and type(s) and will also return
1855retrieved value(s). If <callback> supplied method
1856will operate asynchronously
1857
1858=item $sess->fget(E<lt>varsE<gt> [,E<lt>callbackE<gt>])
1859
1860do SNMP GET like 'get' and format the values according
1861the handlers specified in $sess->{VarFormats} and
1862$sess->{TypeFormats}
1863
1864=item $sess->getnext(E<lt>varsE<gt> [,E<lt>callbackE<gt>])
1865
1866do SNMP GETNEXT, multiple <vars> formats accepted,
1867returns retrieved value(s), <vars> passed as arguments are
1868updated to indicate next lexicographical <obj>,<iid>,<val>,
1869and <type>
1870
1871Note: simple string <vars>,(e.g., 'sysDescr.0')
1872form is not updated. If <callback> supplied method
1873will operate asynchronously
1874
1875=item $sess->fgetnext(E<lt>varsE<gt> [,E<lt>callbackE<gt>])
1876
1877do SNMP GETNEXT like getnext and format the values according
1878the handlers specified in $sess->{VarFormats} and
1879$sess->{TypeFormats}
1880
1881=item $sess->set(E<lt>varsE<gt> [,E<lt>callbackE<gt>])
1882
1883do SNMP SET, multiple <vars> formats accepted.
1884the value field in all <vars> formats must be in a canonical
1885format (i.e., well known format) to ensure unambiguous
1886translation to SNMP MIB data value (see discussion of
1887canonical value format <vars> description section),
1888returns snmp_errno. If <callback> supplied method
1889will operate asynchronously
1890
1891=item $sess->getbulk(E<lt>non-repeatersE<gt>, E<lt>max-repeatersE<gt>, E<lt>varsE<gt>)
1892
1893do an SNMP GETBULK, from the list of Varbinds, the single
1894next lexico instance is fetched for the first n Varbinds
1895as defined by <non-repeaters>. For remaining Varbinds,
1896the m lexico instances are retrieved each of the remaining
1897Varbinds, where m is <max-repeaters>.
1898
1899=item $sess->bulkwalk(E<lt>non-repeatersE<gt>, E<lt>max-repeatersE<gt>, E<lt>varsE<gt> [,E<lt>callbackE<gt>])
1900
1901Do a "bulkwalk" of the list of Varbinds.  This is done by
1902sending a GETBULK request (see getbulk() above) for the
1903Varbinds.  For each requested variable, the response is
1904examined to see if the next lexico instance has left the
1905requested sub-tree.  Any further instances returned for
1906this variable are ignored, and the walk for that sub-tree
1907is considered complete.
1908
1909If any sub-trees were not completed when the end of the
1910responses is reached, another request is composed, consisting
1911of the remaining variables.  This process is repeated until
1912all sub-trees have been completed, or too many packets have
1913been exchanged (to avoid loops).
1914
1915The bulkwalk() method returns an array containing an array of
1916Varbinds, one for each requested variable, in the order of the
1917variable requests.  Upon error, bulkwalk() returns undef and
1918sets $sess->ErrorStr and $sess->ErrorNum.  If a callback is
1919supplied, bulkwalk() returns the SNMP request id, and returns
1920immediately.  The callback will be called with the supplied
1921argument list and the returned variables list.
1922
1923Note: Because the client must "discover" that the tree is
1924complete by comparing the returned variables with those that
1925were requested, there is a potential "gotcha" when using the
1926max-repeaters value.  Consider the following code to print a
1927list of interfaces and byte counts:
1928
1929    $numInts = $sess->get('ifNumber.0');
1930    ($desc, $in, $out) = $sess->bulkwalk(0, $numInts,
1931		  [['ifDescr'], ['ifInOctets'], ['ifOutOctets']]);
1932
1933    for $i (0..($numInts - 1)) {
1934        printf "Interface %4s: %s inOctets, %s outOctets\n",
1935                  $$desc[$i]->val, $$in[$i]->val, $$out[$i]->val;
1936    }
1937
1938This code will produce *two* requests to the agent -- the first
1939to get the interface values, and the second to discover that all
1940the information was in the first packet.  To get around this,
1941use '$numInts + 1' for the max_repeaters value.  This asks the
1942agent to include one additional (unrelated) variable that signals
1943the end of the sub-tree, allowing bulkwalk() to determine that
1944the request is complete.
1945
1946=item $results = $sess->gettable(E<lt>TABLE OIDE<gt>, E<lt>OPTIONSE<gt>)
1947
1948This will retrieve an entire table of data and return a hash reference
1949to that data.  The returned hash reference will have indexes of the
1950OID suffixes for the index data as the key.  The value for each entry
1951will be another hash containing the data for a given row.  The keys to
1952that hash will be the column names, and the values will be the data.
1953
1954Example:
1955
1956  #!/usr/bin/perl
1957
1958  use SNMP;
1959  use Data::Dumper;
1960
1961  my $s = new SNMP::Session(DestHost => 'localhost');
1962
1963  print Dumper($s->gettable('ifTable'));
1964
1965On my machine produces:
1966
1967  $VAR1 = {
1968            '6' => {
1969                     'ifMtu' => '1500',
1970                     'ifPhysAddress' => 'PV',
1971                     # ...
1972                     'ifInUnknownProtos' => '0'
1973                   },
1974            '4' => {
1975                     'ifMtu' => '1480',
1976                     'ifPhysAddress' => '',
1977                     # ...
1978                     'ifInUnknownProtos' => '0'
1979                   },
1980            # ...
1981           };
1982
1983By default, it will try to do as optimized retrieval as possible.
1984It'll request multiple columns at once, and use GETBULK if possible.
1985A few options may be specified by passing in an I<OPTIONS> hash
1986containing various parameters:
1987
1988=over
1989
1990=item noindexes => 1
1991
1992Instructs the code not to parse the indexes and place the results in
1993the second hash.  If you don't need the index data, this will be
1994faster.
1995
1996=item columns => [ colname1, ... ]
1997
1998This specifies which columns to collect.  By default, it will try to
1999collect all the columns defined in the MIB table.
2000
2001=item repeat => I<COUNT>
2002
2003Specifies a GETBULK repeat I<COUNT>.  IE, it will request this many
2004varbinds back per column when using the GETBULK operation.  Shortening
2005this will mean smaller packets which may help going through some
2006systems.  By default, this value is calculated and attempts to guess
2007at what will fit all the results into 1000 bytes.  This calculation is
2008fairly safe, hopefully, but you can either raise or lower the number
2009using this option if desired.  In lossy networks, you want to make
2010sure that the packets don't get fragmented and lowering this value is
2011one way to help that.
2012
2013=item nogetbulk => 1
2014
2015Force the use of GETNEXT rather than GETBULK.  (always true for
2016SNMPv1, as it doesn't have GETBULK anyway).  Some agents are great
2017implementers of GETBULK and this allows you to force the use of
2018GETNEXT operations instead.
2019
2020=item callback => \&subroutine
2021
2022=item callback => [\&subroutine, optarg1, optarg2, ...]
2023
2024If a callback is specified, gettable will return quickly without
2025returning results.  When the results are finally retrieved the
2026callback subroutine will be called (see the other sections defining
2027callback behaviour and how to make use of SNMP::MainLoop which is
2028required for this to work).  An additional argument of the normal hash
2029result will be added to the callback subroutine arguments.
2030
2031Note 1: internally, the gettable function uses it's own callbacks
2032which are passed to getnext/getbulk as appropriate.
2033
2034Note 2: callback support is only available in the SNMP module version
20355.04 and above.  To test for this in code intending to support both
2036versions prior to 5.04 and 5.04 and up, the following should work:
2037
2038  if ($response = $sess->gettable('ifTable', callback => \&my_sub)) {
2039      # got a response, gettable doesn't support callback
2040      my_sub($response);
2041      $no_mainloop = 1;
2042  }
2043
2044Deciding on whether to use SNMP::MainLoop is left as an exercise to
2045the reader since it depends on whether your code uses other callbacks
2046as well.
2047
2048=back
2049
2050=back
2051
2052=head1 SNMP::TrapSession
2053
2054$sess = new SNMP::Session(DestHost => 'host', ...)
2055
2056supports all applicable fields from SNMP::Session
2057(see above)
2058
2059=head2 SNMP::TrapSession methods
2060
2061=over
2062
2063=item $sess->trap(enterprise, agent, generic, specific, uptime, <vars>)
2064
2065    $sess->trap(enterprise=>'.1.3.6.1.4.1.2021', # or 'ucdavis' [default]
2066                agent => '127.0.0.1', # or 'localhost',[dflt 1st intf on host]
2067                generic => specific,  # can be omitted if 'specific' supplied
2068                specific => 5,        # can be omitted if 'generic' supplied
2069                uptime => 1234,       # dflt to localhost uptime (0 on win32)
2070                [[ifIndex, 1, 1],[sysLocation, 0, "here"]]); # optional vars
2071                                                             # always last
2072
2073=item trap(oid, uptime, <vars>) - v2 format
2074
2075    $sess->trap(oid => 'snmpRisingAlarm',
2076                uptime => 1234,
2077                [[ifIndex, 1, 1],[sysLocation, 0, "here"]]); # optional vars
2078                                                             # always last
2079
2080=back
2081
2082=head1 Acceptable variable formats:
2083
2084<vars> may be one of the following forms:
2085
2086=over
2087
2088=item SNMP::VarList
2089
2090represents an array of MIB objects to get or set,
2091implemented as a blessed reference to an array of
2092SNMP::Varbinds, (e.g., [<varbind1>, <varbind2>, ...])
2093
2094=item SNMP::Varbind
2095
2096represents a single MIB object to get or set, implemented as
2097a blessed reference to a 4 element array;
2098[<obj>, <iid>, <val>, <type>].
2099
2100=over
2101
2102=item <obj>
2103
2104one of the following forms:
2105
2106=over
2107
2108=item 1)
2109
2110leaf identifier (e.g., 'sysDescr') assumed to be
2111unique for practical purposes
2112
2113=item 2)
2114
2115fully qualified identifier (e.g.,
2116'.iso.org.dod.internet.mgmt.mib-2.system.sysDescr')
2117
2118=item 3)
2119
2120fully qualified, dotted-decimal, numeric OID (e.g.,
2121'.1.3.6.1.2.1.1.1')
2122
2123=back
2124
2125=item <iid>
2126
2127the dotted-decimal, instance identifier. for
2128scalar MIB objects use '0'
2129
2130=item <val>
2131
2132the SNMP data value retrieved from or being set
2133to the agents MIB. for (f)get(next) operations
2134<val> may have a variety of formats as determined by
2135session and package settings. However for set
2136operations the <val> format must be canonical to
2137ensure unambiguous translation. The canonical forms
2138are as follows:
2139
2140=over
2141
2142=item OBJECTID
2143
2144dotted-decimal (e.g., .1.3.6.1.2.1.1.1)
2145
2146=item OCTETSTR
2147
2148perl scalar containing octets
2149
2150=item INTEGER
2151
2152decimal signed integer (or enum)
2153
2154=item NETADDR
2155
2156dotted-decimal
2157
2158=item IPADDR
2159
2160dotted-decimal
2161
2162=item COUNTER
2163
2164decimal unsigned integer
2165
2166=item COUNTER64
2167
2168decimal unsigned integer
2169
2170=item GAUGE
2171
2172decimal unsigned integer
2173
2174=item UINTEGER
2175
2176decimal unsigned integer
2177
2178=item TICKS
2179
2180decimal unsigned integer
2181
2182=item OPAQUE
2183
2184perl scalar containing octets
2185
2186=item NULL
2187
2188perl scalar containing nothing
2189
2190=back
2191
2192=item <type>
2193
2194SNMP data type (see list above), this field is
2195populated by 'get' and 'getnext' operations. In
2196some cases the programmer needs to populate this
2197field when passing to a 'set' operation. this
2198field need not be supplied when the attribute
2199indicated by <tag> is already described by loaded
2200Mib modules. for 'set's, if a numeric OID is used
2201and the object is not currently in the loaded Mib,
2202the <type> field must be supplied
2203
2204=back
2205
2206=item simple string
2207
2208light weight form of <var> used to 'set' or 'get' a
2209single attribute without constructing an SNMP::Varbind.
2210stored in a perl scalar, has the form '<tag>.<iid>',
2211(e.g., 'sysDescr.0'). for 'set' operations the value
2212is passed as a second arg. Note: This argument form is
2213not updated in get[next] operations as are the other forms.
2214
2215=back
2216
2217=head1 Acceptable callback formats
2218
2219<callback> may be one of the following forms:
2220
2221=over
2222
2223=item without arguments
2224
2225=over
2226
2227=item \&subname
2228
2229=item sub { ... }
2230
2231=back
2232
2233=item or with arguments
2234
2235=over
2236
2237=item [ \&subname, $arg1, ... ]
2238
2239=item [ sub { ... }, $arg1, ... ]
2240
2241=item [ "method", $obj, $arg1, ... ]
2242
2243=back
2244
2245=back
2246
2247callback will be called when response is received or timeout
2248occurs. the last argument passed to callback will be a
2249SNMP::VarList reference. In case of timeout the last argument
2250will be undef.
2251
2252=over
2253
2254=item &SNMP::MainLoop([<timeout>, [<callback>]])
2255
2256to be used with async SNMP::Session
2257calls. MainLoop must be called after initial async calls
2258so return packets from the agent will be processed.
2259If no args supplied this function enters an infinite loop
2260so program must be exited in a callback or externally
2261interrupted. If <timeout(sic)
2262
2263=item &SNMP::finish()
2264
2265This function, when called from an SNMP::MainLoop() callback
2266function, will cause the current SNMP::MainLoop() to return
2267after the callback is completed.  finish() can be used to
2268terminate an otherwise-infinite MainLoop.  A new MainLoop()
2269instance can then be started to handle further requests.
2270
2271=back
2272
2273=head1 SNMP package variables and functions
2274
2275=over
2276
2277=item $SNMP::VERSION
2278
2279the current version specifier (e.g., 3.1.0)
2280
2281=item $SNMP::auto_init_mib
2282
2283default '1', set to 0 to disable automatic reading
2284of the MIB upon session creation. set to non-zero
2285to call initMib at session creation which will result
2286in MIB loading according to Net-SNMP env. variables (see
2287man mib_api)
2288
2289=item $SNMP::verbose
2290
2291default '0', controls warning/info output of
2292SNMP module, 0 => no output, 1 => enables warning/info
2293output from SNMP module itself (is also controlled
2294by SNMP::debugging - see below)
2295
2296=item $SNMP::use_long_names
2297
2298default '0', set to non-zero to enable the use of
2299longer Mib identifiers. see translateObj. will also
2300influence the formatting of <tag> in varbinds returned
2301from 'getnext' operations. Can be set on a per session
2302basis (UseLongNames)
2303
2304=item $SNMP::use_sprint_value
2305
2306default '0', set to non-zero to enable formatting of
2307response values using the snmp libraries snprint_value
2308function. can also be set on a per session basis (see
2309UseSprintValue) Note: returned values may not be
2310suitable for 'set' operations
2311
2312=item $SNMP::use_enums
2313
2314default '0',set non-zero to return values as enums and
2315allow sets using enums where appropriate. integer data
2316will still be accepted for set operations. can also be
2317set on a per session basis (see UseEnums)
2318
2319=item $SNMP::use_numeric
2320
2321default to '0',set to non-zero to have <tags> for 'get'
2322methods returned as numeric OID's rather than descriptions.
2323UseLongNames will be set so that the entire OID will be
2324returned.  Set on a per-session basis (see UseNumeric).
2325
2326=item $SNMP::best_guess
2327
2328default '0'.  This setting controls how <tags> are
2329parsed.  Setting to 0 causes a regular lookup.  Setting
2330to 1 causes a regular expression match (defined as -Ib
2331in snmpcmd) and setting to 2 causes a random access
2332lookup (defined as -IR in snmpcmd).  Can also be set
2333on a per session basis (see BestGuess)
2334
2335=item $SNMP::save_descriptions
2336
2337default '0',set non-zero to have mib parser save
2338attribute descriptions. must be set prior to mib
2339initialization
2340
2341=item $SNMP::debugging
2342
2343default '0', controls debugging output level
2344within SNMP module and libsnmp
2345
2346=over
2347
2348=item 1
2349
2350enables 'SNMP::verbose' (see above)
2351
2352=item 2
2353
2354level 1 plus snmp_set_do_debugging(1)
2355
2356=item 3
2357
2358level 2 plus snmp_set_dump_packet(1)
2359
2360=back
2361
2362=item $SNMP::dump_packet
2363
2364default '0', set [non-]zero to independently set
2365snmp_set_dump_packet()
2366
2367=item SNMP::register_debug_tokens()
2368
2369Allows to register one or more debug tokens, just like the -D option of snmpd.
2370Each debug token enables a group of debug statements. An example:
2371SNMP::register_debug_tokens("tdomain,netsnmp_unix");
2372
2373=back
2374
2375=head1 %SNMP::MIB
2376
2377a tied hash to access parsed MIB information. After
2378the MIB has been loaded this hash allows access to
2379to the parsed in MIB meta-data(the structure of the
2380MIB (i.e., schema)). The hash returns blessed
2381references to SNMP::MIB::NODE objects which represent
2382a single MIB attribute. The nodes can be fetched with
2383multiple 'key' formats - the leaf name (e.g.,sysDescr)
2384or fully/partially qualified name (e.g.,
2385system.sysDescr) or fully qualified numeric OID. The
2386returned node object supports the following fields:
2387
2388=over
2389
2390=item objectID
2391
2392dotted decimal fully qualified OID
2393
2394=item label
2395
2396leaf textual identifier (e.g., 'sysDescr')
2397
2398=item subID
2399
2400leaf numeric OID component of objectID (e.g., '1')
2401
2402=item moduleID
2403
2404textual identifier for module (e.g., 'RFC1213-MIB')
2405
2406=item parent
2407
2408parent node
2409
2410=item children
2411
2412array reference of children nodes
2413
2414=item nextNode
2415
2416next lexico node B<(BUG!does not return in lexico order)>
2417
2418=item type
2419
2420returns application type (see getType for values)
2421
2422=item access
2423
2424returns ACCESS (ReadOnly, ReadWrite, WriteOnly,
2425NoAccess, Notify, Create)
2426
2427=item status
2428
2429returns STATUS (Mandatory, Optional, Obsolete,
2430Deprecated)
2431
2432=item syntax
2433
2434returns 'textualConvention' if defined else 'type'
2435
2436=item textualConvention
2437
2438returns TEXTUAL-CONVENTION
2439
2440=item TCDescription
2441
2442returns the TEXTUAL-CONVENTION's DESCRIPTION field.
2443
2444=item units
2445
2446returns UNITS
2447
2448=item hint
2449
2450returns HINT
2451
2452=item enums
2453
2454returns hash ref {tag => num, ...}
2455
2456=item ranges
2457
2458returns array ref of hash ref [{low => num, high => num}, ...]
2459
2460=item description
2461
2462returns DESCRIPTION ($SNMP::save_descriptions must
2463be set prior to MIB initialization/parsing)
2464
2465=item reference
2466
2467returns the REFERENCE clause
2468
2469=item indexes
2470
2471returns the objects in the INDEX clause
2472
2473=item implied
2474
2475returns true if the last object in the INDEX is IMPLIED
2476
2477=back
2478
2479=head1 MIB Functions
2480
2481=over
2482
2483=item &SNMP::setMib(<file>)
2484
2485allows dynamic parsing of the mib and explicit
2486specification of mib file independent of environment
2487variables. called with no args acts like initMib,
2488loading MIBs indicated by environment variables (see
2489Net-SNMP mib_api docs). passing non-zero second arg
2490forces previous mib to be freed and replaced
2491B<(Note: second arg not working since freeing previous
2492Mib is more involved than before)>.
2493
2494=item &SNMP::initMib()
2495
2496calls library init_mib function if Mib not already
2497loaded - does nothing if Mib already loaded. will
2498parse directories and load modules according to
2499environment variables described in Net-SNMP documentations.
2500(see man mib_api, MIBDIRS, MIBS, MIBFILE(S), etc.)
2501
2502=item &SNMP::addMibDirs(<dir>,...)
2503
2504calls library add_mibdir for each directory
2505supplied. will cause directory(s) to be added to
2506internal list and made available for searching in
2507subsequent loadModules calls
2508
2509=item &SNMP::addMibFiles(<file>,...)
2510
2511calls library read_mib function. The file(s)
2512supplied will be read and all Mib module definitions
2513contained therein will be added to internal mib tree
2514structure
2515
2516=item &SNMP::loadModules(<mod>,...)
2517
2518calls library read_module function. The
2519module(s) supplied will be searched for in the
2520current mibdirs and and added to internal mib tree
2521structure. Passing special <mod>, 'ALL', will cause
2522all known modules to be loaded.
2523
2524=item &SNMP::unloadModules(<mod>,...)
2525
2526B<*Not Implemented*>
2527
2528=item &SNMP::translateObj(<var>[,arg,[arg]])
2529
2530will convert a text obj tag to an OID and vice-versa.
2531Any iid suffix is retained numerically.  Default
2532behaviour when converting a numeric OID to text
2533form is to return leaf identifier only
2534(e.g.,'sysDescr') but when $SNMP::use_long_names
2535is non-zero or a non-zero second arg is supplied it
2536will return a longer textual identifier.  An optional
2537third argument of non-zero will cause the module name
2538to be prepended to the text name (e.g.
2539'SNMPv2-MIB::sysDescr').  When converting a text obj,
2540the $SNMP::best_guess option is used.  If no Mib is
2541loaded when called and $SNMP::auto_init_mib is enabled
2542then the Mib will be loaded. Will return 'undef' upon
2543failure.
2544
2545=item &SNMP::getType(<var>)
2546
2547return SNMP data type for given textual identifier
2548OBJECTID, OCTETSTR, INTEGER, NETADDR, IPADDR, COUNTER
2549GAUGE, TIMETICKS, OPAQUE, or undef
2550
2551=item &SNMP::mapEnum(<var>)
2552
2553converts integer value to enumertion tag defined
2554in Mib or converts tag to integer depending on
2555input. the function will return the corresponding
2556integer value *or* tag for a given MIB attribute
2557and value. The function will sense which direction
2558to perform the conversion. Various arg formats are
2559supported
2560
2561=over
2562
2563=item $val = SNMP::mapEnum($varbind);
2564
2565where $varbind is SNMP::Varbind or equiv.
2566note: $varbind will be updated
2567
2568=item $val = SNMP::mapEnum('ipForwarding', 'forwarding');
2569
2570=item $val = SNMP::mapEnum('ipForwarding', 1);
2571
2572=back
2573
2574=back
2575
2576=head1 Exported SNMP utility functions
2577
2578Note: utility functions do not support async operation yet.
2579
2580=over
2581
2582=item &snmp_get()
2583
2584takes args of SNMP::Session::new followed by those of
2585SNMP::Session::get
2586
2587=item &snmp_getnext()
2588
2589takes args of SNMP::Session::new followed by those of
2590SNMP::Session::getnext
2591
2592=item &snmp_set()
2593
2594takes args of SNMP::Session::new followed by those of
2595SNMP::Session::set
2596
2597=item &snmp_trap()
2598
2599takes args of SNMP::TrapSession::new followed by those of
2600SNMP::TrapSession::trap
2601
2602=back
2603
2604=head1 Trouble Shooting
2605
2606If problems occur there are number areas to look at to narrow down the
2607possibilities.
2608
2609The first step should be to test the Net-SNMP installation
2610independently from the Perl5 SNMP interface.
2611
2612Try running the apps from the Net-SNMP distribution.
2613
2614Make sure your agent (snmpd) is running and properly configured with
2615read-write access for the community you are using.
2616
2617Ensure that your MIBs are installed and enviroment variables are set
2618appropriately (see man mib_api)
2619
2620Be sure to remove old net-snmp installations and ensure headers and
2621libraries from old CMU installations are not being used by mistake.
2622
2623If the problem occurs during compilation/linking check that the snmp
2624library being linked is actually the Net-SNMP library (there have been
2625name conflicts with existing snmp libs).
2626
2627Also check that the header files are correct and up to date.
2628
2629Sometimes compiling the Net-SNMP library with
2630'position-independent-code' enabled is required (HPUX specifically).
2631
2632If you cannot resolve the problem you can post to
2633comp.lang.perl.modules or
2634net-snmp-users@net-snmp-users@lists.sourceforge.net
2635
2636please give sufficient information to analyze the problem (OS type,
2637versions for OS/Perl/Net-SNMP/compiler, complete error output, etc.)
2638
2639=head1 Acknowledgements
2640
2641Many thanks to all those who supplied patches, suggestions and
2642feedback.
2643
2644 Joe Marzot (the original author)
2645 Wes Hardaker and the net-snmp-coders
2646 Dave Perkins
2647 Marcel Wiget
2648 David Blackburn
2649 John Stofell
2650 Gary Hayward
2651 Claire Harrison
2652 Achim Bohnet
2653 Doug Kingston
2654 Jacques Vidrine
2655 Carl Jacobsen
2656 Wayne Marquette
2657 Scott Schumate
2658 Michael Slifcak
2659 Srivathsan Srinivasagopalan
2660 Bill Fenner
2661 Jef Peeraer
2662 Daniel Hagerty
2663 Karl "Rat" Schilke and Electric Lightwave, Inc.
2664 Perl5 Porters
2665 Alex Burger
2666
2667Apologies to any/all who's patch/feature/request was not mentioned or
2668included - most likely it was lost when paying work intruded on my
2669fun. Please try again if you do not see a desired feature. This may
2670actually turn out to be a decent package with such excellent help and
2671the fact that I have more time to work on it than in the past.
2672
2673=head1 AUTHOR
2674
2675bugs, comments, questions to net-snmp-users@lists.sourceforge.net
2676
2677=head1 Copyright
2678
2679     Copyright (c) 1995-2000 G. S. Marzot. All rights reserved.
2680     This program is free software; you can redistribute it and/or
2681     modify it under the same terms as Perl itself.
2682
2683     Copyright (c) 2001-2002 Networks Associates Technology, Inc.  All
2684     Rights Reserved.  This program is free software; you can
2685     redistribute it and/or modify it under the same terms as Perl
2686     itself.
2687
2688=cut
2689