Article 6169 of comp.lang.perl:
Xref: feenix.metronet.com comp.lang.perl:6169
Newsgroups: comp.lang.perl
Path: feenix.metronet.com!news.ecn.bgu.edu!usenet.ins.cwru.edu!howland.reston.ans.net!europa.eng.gtefsd.com!uunet!infonode!ingr!b30news!guy!gmstreet
From: gmstreet@guy.b30.ingr.com (Guy Streeter)
Subject: Re: SNMP via Perl?
Message-ID: <gmstreet.749140012@guy>
Sender: usenet@b30news.b30.ingr.com (Usenet Feed)
Reply-To: gmstreet@guy.b30.ingr.com
Organization: evidently not
References: <27l84a$dj8@geraldo.cc.utexas.edu> <CDqIvB.980@kirk.Bond.edu.au> <1993Sep22.180635.10768@leland.Stanford.EDU>
Date: Mon, 27 Sep 1993 14:26:52 GMT
Lines: 1681

A note to those using the snmperl package.  I don't have the time
right now to work up documentation, etc., but a reasonably good hacker
(I've done it) can make this work as an SNMPv1 client using the SNMPv2
CMU package, which is generally more reliable than the 1.1b version
referenced in the README.  I recall that it's just a matter of fixing
some #include references.

--Guy Streeter

---- Cut Here and feed the following to sh ----
#!/bin/sh
# This is a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 10/05/1992 17:56 UTC by guy@guy
# Source directory /usr2/perl/snmp
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#   3433 -r--r--r-- README
#    607 -r--r--r-- Makefile
#   9722 -r--r--r-- snmp.c
#  16764 -r--r--r-- mib.c
#   2427 -r--r--r-- snmp.pl
#   4723 -r-xr-xr-x snmp-tracer
#
# ============= README ==============
if test -f 'README' -a X"$1" != X"-c"; then
	echo 'x - skipping README (File already exists)'
else
sed 's/^X//' << 'SHAR_EOF' > 'README' &&
X
XREADME	$Revision: 1.2 $
X
XThis directory contains the source code to add callable C subroutines
Xto perl.  The subroutines implement the SNMP functions "get",
X"getnext", and "set".  They use the freely-distributable SNMP package
X(version 1.1b) from CMU.
X
XThis file tells how to build and use this package.  The following
Xfiles should be present:
X
XREADME	 -- This file.
X
Xsnmp.c	 -- The callable interface to perl.
X
Xmib.c	 -- The only file from the CMU SNMP package which has been
X	 modified for use here.
X
XMakefile -- The Makefile (You'll need to edit this).
X
Xsnmp.pl	 -- A small package of useful routines.
X
Xsnmp-tracer -- An incomplete attempt to implement trace-route.
X	    Supplied as an example of the interface.
X
XSETUP:
X  These files should be in a subdirectory of the main perl source
Xdirectory.  If not, you must change the SRC variable in the Makefile
Xto point to the perl source directory.  Perl should be built.
XSpecifically, "uperl.o" should be present in the perl directory.
X
X  You must have the CMU SNMP package.  This can be retrieved from
Xlancaster.andrew.cmu.edu as pub/snmp1.1b.tar, via anonymous FTP.  You
Xdon't have to build all the applications (some of them won't work
Xunless you are a BSD system), you just need to build libsnmp.a.
X
XBUILD:
X  Once you have perl and the CMU SNMP package:
X1) Copy libsnmp.a from the CMU package to this directory (we will
Xmodify it).
X2) Edit "Makefile".  See especially the CC and INCS variables.
X3) make.
X
XMake should compile mib.c and replace mib.o in libsnmp.a, compile
Xsnmp.c, and create an executable called "snmperl".  Installation is
Xleft to the user.
X
XUSE:
X  There are four subroutines defined in the callable interface:
Xsnmp_get, snmp_next, snmp_set, and snmp_error.
X
X  snmp_get and snmp_next implement the GET and GETNEXT operations,
Xrespectively.  The first two calling arguments are the hostname and
XCommunity string.  The IP address of the host, as a dotted-quad ASCII
Xstring, may be used as the hostname.  The rest of the calling
Xarguments are a list of variables.  See the CMU package documentation
Xfor how variables may be specified.
X  snmp_set also takes hostname and Community string as arguments.  The
Xremaining arguments are a list of triples consisting of variable name,
Xvariable type, and value.  The variable type is a string, such as
X"INTEGER" or "IpAddress".
X  snmp_get, snmp_next, and snmp_set return a list containing
Xalternating variables and values.  snmp_get and snmp_next will simply
Xomit non-existent variables on return.  snmp_set will fail completely
Xif one of the specified variables does not exist (or is read-only).
X  snmp_error will return a text string containing some error
Xinformation about the most recent snmp_get|next|set call, if it had an
Xerror.
X
XOTHER NOTES:
X  I didn't find all the places where the CMU library writes to stderr
Xor calls exit() directly.
X  The changes I made to mib.c involve the formatting of variable values
Xfor return to the caller.  I took out the descriptive prefix so the
Xstring contains only the value.
X  Enumerated types are returned as a string containing the symbolic
Xrepresentation followed in parentheses by the numeric.
X
XDISTRIBUTION and OWNERSHIP
X  perl and the CMU SNMP package have their own statements.  Read them.
XThe work I've done is free and clear.  Just don't say you wrote it if
Xyou didn't, and don't say I wrote it if you change it.
X
XGuy Streeter
Xstreeter@ingr.com
XApril 1, 1992 (not a joke!)
SHAR_EOF
chmod 0444 README ||
echo 'restore of README failed'
Wc_c="`wc -c < 'README'`"
test 3433 -eq "$Wc_c" ||
	echo 'README: original size 3433, current size' "$Wc_c"
fi
# ============= Makefile ==============
if test -f 'Makefile' -a X"$1" != X"-c"; then
	echo 'x - skipping Makefile (File already exists)'
else
sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
X#
X# $Revision: 1.3 $
X#
X# define your C compiler command here
XCC = gcc -mc300 -O -s -fwritable-strings -fstrength-reduce -fpcc-struct-return
X
X# point this to the "include" directory of the CMU SNMP source. 
XINCS=-I/usr2/guy/src/cmu-snmp/include
X
XSRC = ..
XLDLIBS = libsnmp.a `. $(SRC)/config.sh; echo $$libs`
X
Xsnmperl: $(SRC)/uperl.o snmp.o libsnmp.a
X	$(CC) $(SRC)/uperl.o snmp.o $(LDLIBS) -o snmperl
X
Xsnmp.o : snmp.c
X	$(CC) -c -I$(SRC) $(INCS) snmp.c
X
Xmib.o : mib.c
X	$(CC) -c $(INCS) mib.c
X
Xlibsnmp.a: libsnmp.a(mib.o)
X
Xshar: README Makefile snmp.c mib.c snmp.pl snmp-tracer
X	shar -v -c -F $? > snmperl.shar
SHAR_EOF
chmod 0444 Makefile ||
echo 'restore of Makefile failed'
Wc_c="`wc -c < 'Makefile'`"
test 607 -eq "$Wc_c" ||
	echo 'Makefile: original size 607, current size' "$Wc_c"
fi
# ============= snmp.c ==============
if test -f 'snmp.c' -a X"$1" != X"-c"; then
	echo 'x - skipping snmp.c (File already exists)'
else
sed 's/^X//' << 'SHAR_EOF' > 'snmp.c' &&
X/*
X * Guy Streeter, 31-mar-92
X *
X * $Revision: 1.3 $
X *
X * 15-may-92 Fix the handling of Octet Strings, so they come out unmodified.
X *
X *  A lot of this code was derived by example from the CMU SNMP package,
X * whose copyright notice follows:
X */
X/***********************************************************
X	Copyright 1988, 1989 by Carnegie Mellon University
X
X                      All Rights Reserved
X
XPermission to use, copy, modify, and distribute this software and its 
Xdocumentation for any purpose and without fee is hereby granted, 
Xprovided that the above copyright notice appear in all copies and that
Xboth that copyright notice and this permission notice appear in 
Xsupporting documentation, and that the name of CMU not be
Xused in advertising or publicity pertaining to distribution of the
Xsoftware without specific, written prior permission.  
X
XCMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
XALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
XCMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
XANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
XWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
XARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
XSOFTWARE.
X******************************************************************/
X#include "EXTERN.h"
X#include "perl.h"
X
X#include <sys/types.h>
X#include <netinet/in.h>
X#include <sys/time.h>
X#ifdef DEBUG_MALLOC
X#include "/usr/local/include/malloc.h"
X#endif
X#include "snmp.h"
X#include "snmp_impl.h"
X#include "asn1.h"
X#include "snmp_api.h"
X#include "snmp_client.h"
X
Xextern int snmp_errno;
Xextern struct tree *Mib;
X
Xint snmp_dump_packet = 0;
X
Xstatic int last_status;
Xstatic int last_errstat;
Xstatic char err_text[1024];
X
Xenum usersubs {
X    US_snmp_get,
X    US_snmp_next,
X    US_snmp_set,
X    US_snmp_error,
X};
X
Xint
Xuserinit()
X{
X    static void usersub();
X    static char *filename = "snmp.c";
X
X    make_usub("snmp_get", US_snmp_get, usersub, filename);
X    make_usub("snmp_next", US_snmp_next, usersub, filename);
X    make_usub("snmp_set", US_snmp_set, usersub, filename);
X    make_usub("snmp_error", US_snmp_error, usersub, filename);
X    /* ../perl.h declares this routine as returning an int, but the
X       documentation doesn't define the return value, and the code
X       doesn't check it */
X    return 0;
X}
X
X
Xstatic char *
Xsnmp_error()
X{
X    if (err_text[0] != '\0') return err_text;
X
X    if (last_status == STAT_SUCCESS) return snmp_errstring(last_errstat);
X
X    if (last_status == STAT_TIMEOUT) return "Timeout ocurred";
X
X    return "A general error ocurred";
X}
X
X
Xstatic int
Xusersub(ix, sp, items)
X  int ix;
X  register int sp;
X  register int items;
X{
X    STR **st = stack->ary_array + sp;
X    struct snmp_session session, *ss;
X    struct snmp_pdu *pdu, *response = NULL;
X    oid name[MAX_NAME_LEN];
X    int name_length;
X    char buf[1024];
X    int i;
X
X    switch (ix)
X    {
X      case US_snmp_get:
X      case US_snmp_next:
X      case US_snmp_set:
X      {
X	  char *host = (char *)str_get(st[1]);
X	  char *community = (char *)str_get(st[2]);
X
X	  last_status = 0;
X	  err_text[0] = '\0';
X
X	  if (Mib == (struct tree *)0) init_mib();
X
X	  bzero((char *)&session, sizeof(struct snmp_session));
X	  session.peername = host;
X	  session.community = community;
X	  session.community_len = strlen(community);
X	  session.retries = SNMP_DEFAULT_RETRIES;
X	  session.timeout = SNMP_DEFAULT_TIMEOUT;
X	  session.authenticator = NULL;
X
X	  snmp_synch_setup(&session);
X	  ss = snmp_open(&session);
X	  if (ss == NULL)
X	  {
X	      strcpy(err_text,"Couldn't open snmp");
X	      return NULL;
X	  }
X
X	  if (ix == US_snmp_set) 
X	  {
X	      struct variable_list *vars = NULL;
X
X	      if (items < 5)
X		fatal(
X	   "Usage: &snmp_set($host, $community, $variable, $type, $value...)");
X	      pdu = snmp_pdu_create(SET_REQ_MSG);
X
X	      for (i = 3; i <= items; )
X	      {
X		  struct variable_list *vp;
X		  char *type, *variable = (char *)str_get(st[i]);
X
X		  name_length = MAX_NAME_LEN;
X		  if ( !read_objid(variable, name, &name_length))
X		  {
X		      sprintf(err_text,"Invalid object identifier: %s",
X			      variable);
X		      snmp_free_pdu(pdu);
X		      snmp_close(ss);
X		      return NULL;
X		  }
X
X		  if ((vp = (struct variable_list *)malloc(sizeof *vp))
X		      == NULL) fatal ("malloc() failed, line %d",__LINE__);
X		  if (vars == NULL)
X		    pdu->variables = vp;
X		  else
X		    vars->next_variable = vp;
X		  vars = vp;
X		  vars->next_variable = NULL;
X
X		  vp->name_length = name_length;
X		  if ((vp->name = (oid *)malloc(vp->name_length * sizeof(oid)))
X		      == NULL) fatal("malloc() failed, line %d",__LINE__);
X		  bcopy((char *)name, (char *)vp->name,
X			vp->name_length * sizeof(oid));
X
X		  type = (char *)str_get(st[++i]);
X		  variable = (char *)str_get(st[++i]);
X
X		  if (strcmp(type, "INTEGER") == 0)
X		  {
X		      vp->type = INTEGER;
X		      if ((vp->val.integer = (long *)malloc(sizeof(long)))
X			  == NULL) fatal("malloc() failed");
X		      vp->val_len = sizeof(long);
X		      *(vp->val.integer) = atoi(variable);
X		  }
X		  else if (strcmp(type, "DisplayString") == 0)
X		  {
X		      vp->type = STRING;
X		      if ((vp->val.string = strdup(variable)) == NULL)
X			fatal("strdup() failed");
X		      vp->val_len = strlen(variable);
X		  }
X		  else if (strcmp(type, "OCTET STRING") == 0)
X		  {
X		      vp->type = STRING;
X		      vp->val_len = st[i]->str_cur;
X		      if ((vp->val.string = (char *)malloc(vp->val_len))
X			  == NULL) fatal("malloc() failed");
X		      bcopy(st[i]->str_ptr, vp->val.string, vp->val_len);
X		  }
X		  else if (strcmp(type, "NULL") == 0)
X		  {
X		      vp->type = NULLOBJ;
X		      vp->val_len = 0;
X		      vp->val.string = NULL;
X		  }
X		  else if (strcmp(type, "OBJECT IDENTIFIER") == 0)
X		  {
X		      vp->type = OBJID;
X		      vp->val_len = MAX_NAME_LEN;
X		      read_objid(variable, name, &vp->val_len);
X		      vp->val_len *= sizeof(oid);
X		      if ((vp->val.objid = (oid *)malloc(vp->val_len))
X			  == NULL) fatal("malloc() failed");
X		      bcopy((char *)name, (char *)vp->val.objid,
X			    vp->val_len);
X		  }
X		  else if (strcmp(type, "TimeTicks") == 0)
X		  {
X		      vp->type = TIMETICKS;
X		      if ((vp->val.integer = (long *)malloc(sizeof(long)))
X			  == NULL) fatal("malloc() failed");
X		      vp->val_len = sizeof(long);
X		      *(vp->val.integer) = atoi(variable);
X		  }
X		  else if (strcmp(type, "IpAddress") == 0)
X		  {
X		      vp->type = IPADDRESS;
X		      if ((vp->val.integer = (long *)malloc(sizeof(long)))
X			  == NULL) fatal("malloc() failed");
X		      vp->val_len = sizeof(long);
X		      *(vp->val.integer) = inet_addr(variable);
X		  }
X		  else if (strcmp(type, "Counter") == 0)
X		  {
X		      vp->type = COUNTER;
X		      if ((vp->val.integer = (long *)malloc(sizeof(long)))
X			  == NULL) fatal("malloc() failed");
X		      vp->val_len = sizeof(long);
X		      *(vp->val.integer) = atoi(variable);
X		  }
X		  else if (strcmp(type, "Gauge") == 0)
X		  {
X		      vp->type = GAUGE;
X		      if ((vp->val.integer = (long *)malloc(sizeof(long)))
X			  == NULL) fatal("malloc() failed");
X		      vp->val_len = sizeof(long);
X		      *(vp->val.integer) = atoi(variable);
X		  }
X		  else if (strcmp(type, "Opaque") == 0)
X		  {
X		      vp->type = OPAQUE;
X		      vp->val_len = st[i]->str_cur;
X		      if ((vp->val.string = (char *)malloc(vp->val_len))
X			  == NULL) fatal("malloc() failed");
X		      bcopy(st[i]->str_ptr, vp->val.string, vp->val_len);
X		  }
X		  else
X		    fatal(
X		     "snmp_set() bad type: use one of INTEGER, Counter, etc.");
X
X		  i++;
X	      }
X	  }
X	  else 
X	  {
X	      if (items < 3)
X		fatal("Usage: &snmp_%s($host, $community, $variable, ...)",
X		      ix == US_snmp_get ? "get" : "next");
X	      pdu = snmp_pdu_create(ix == US_snmp_get
X				    ? GET_REQ_MSG : GETNEXT_REQ_MSG);
X
X	      for (i = 3; i <= items; i++)
X	      {
X		  char *variable = (char *)str_get(st[i]);
X
X		  name_length = MAX_NAME_LEN;
X		  if ( !read_objid(variable, name, &name_length))
X		  {
X		      sprintf(err_text,"Invalid object identifier: %s",
X			      variable);
X		      snmp_free_pdu(pdu);
X		      snmp_close(ss);
X		      return NULL;
X		  }
X
X		  snmp_add_null_var(pdu, name, name_length);
X	      }
X	  }
X
Xretry:	    
X	  last_status = snmp_synch_response(ss, pdu, &response);
X
X	  i = 0;
X	  if (last_status == STAT_SUCCESS)
X	  {
X	      last_errstat = response->errstat;
X	      if (response->errstat == SNMP_ERR_NOERROR)
X	      {
X		  struct variable_list *vars;
X
X		  for (vars = response->variables;
X		       vars != NULL;
X		       vars = vars->next_variable)
X		  {
X		      i++;
X		      astore(stack, sp + i*2, Nullstr);
X		      st = stack->ary_array + sp + (i-1)*2;
X		      buf[0] = '.';
X		      get_symbol(vars->name,vars->name_length, Mib, buf+1);
X		      st[0] = str_2mortal(str_make(buf,strlen(buf)));
X
X		      if (vars->type == ASN_OCTET_STR)
X		      {
X			  if (vars->val_len == 0)
X			    st[1] = str_2mortal(&str_undef);
X			  else
X			    st[1] = str_2mortal(str_make(vars->val.string,
X							 vars->val_len));
X		      }
X		      else 
X		      {
X			  sprint_value(buf, vars->name, vars->name_length,
X				       vars);
X			  if (buf[0] == '\0')
X			    st[1] = str_2mortal(&str_undef);
X			  else
X			    st[1] = str_2mortal(str_make(buf,strlen(buf)));
X		      }
X		  }
X
X	      }
X	      else
X		if (ix != US_snmp_set &&
X		    (pdu = snmp_fix_pdu(response, ix == US_snmp_get
X					? GET_REQ_MSG : GETNEXT_REQ_MSG))
X		    != NULL)
X		  goto retry;
X	  }
X
X	  if (response) snmp_free_pdu(response);
X
X	  snmp_close(ss);
X
X	  err_text[0] = '\0';
X
X	  return sp + i*2 - 1;
X      }
X
X      case US_snmp_error:
X	if (items != 0)
X	  fatal("Usage: &snmp_error()");
X	else
X	  str_set(st[0], (char*) snmp_error());
X	return sp;
X    }
X    fatal("Can't happen");
X}
SHAR_EOF
chmod 0444 snmp.c ||
echo 'restore of snmp.c failed'
Wc_c="`wc -c < 'snmp.c'`"
test 9722 -eq "$Wc_c" ||
	echo 'snmp.c: original size 9722, current size' "$Wc_c"
fi
# ============= mib.c ==============
if test -f 'mib.c' -a X"$1" != X"-c"; then
	echo 'x - skipping mib.c (File already exists)'
else
sed 's/^X//' << 'SHAR_EOF' > 'mib.c' &&
X/*
X * MODIFIED
X *
X * For use in callable subroutines in perl, I modified the sprint_<type>
X * routines so the don't prepend "<type>: " to the strings they create.
X *			Guy Streeter, 2-20-1992
X *
X * $Revision: 1.2 $
X *
X */
X/***********************************************************
X	Copyright 1988, 1989 by Carnegie Mellon University
X
X                      All Rights Reserved
X
XPermission to use, copy, modify, and distribute this software and its 
Xdocumentation for any purpose and without fee is hereby granted, 
Xprovided that the above copyright notice appear in all copies and that
Xboth that copyright notice and this permission notice appear in 
Xsupporting documentation, and that the name of CMU not be
Xused in advertising or publicity pertaining to distribution of the
Xsoftware without specific, written prior permission.  
X
XCMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
XALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
XCMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
XANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
XWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
XARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
XSOFTWARE.
X******************************************************************/
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include <netinet/in.h>
X#include <sys/time.h>
X#include "asn1.h"
X#include "snmp_impl.h"
X#include "snmp_api.h"
X#include "parse.h"
X#include "snmp.h"
X
Xstatic void sprint_by_type();
Xstatic set_functions(), parse_subtree(), lc_cmp();
X
Xoid RFC1066_MIB[] = { 1, 3, 6, 1, 2, 1 };
Xunsigned char RFC1066_MIB_text[] = ".iso.org.dod.internet.mgmt.mib";
Xstruct tree *Mib = 0;
X
X
Xstatic char *uptimeString(timeticks, buf)
X
Xregister long timeticks;
Xchar *buf;
X
X{
X    int	seconds, minutes, hours, days;
X
X    timeticks /= 100;
X    days = timeticks / (60 * 60 * 24);
X    timeticks %= (60 * 60 * 24);
X
X    hours = timeticks / (60 * 60);
X    timeticks %= (60 * 60);
X
X    minutes = timeticks / 60;
X    seconds = timeticks % 60;
X
X    if (days == 0){
X		sprintf(buf, "%d:%02d:%02d", hours, minutes, seconds);
X    } else if (days == 1) {
X		sprintf(buf, "%d day, %d:%02d:%02d", days, hours, minutes, seconds);
X    } else {
X		sprintf(buf, "%d days, %d:%02d:%02d", days, hours, minutes, seconds);
X    }
X    return buf;
X}
X
X
Xstatic sprint_hexstring(buf, cp, len)
X
Xchar *buf;
Xu_char  *cp;
Xint	    len;
X
X{
X
X    for(; len >= 16; len -= 16){
X		sprintf(buf, "%02X %02X %02X %02X %02X %02X %02X %02X ", 
X		        cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
X		buf += strlen(buf);
X		cp += 8;
X		sprintf(buf, "%02X %02X %02X %02X %02X %02X %02X %02X\n", 
X		        cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
X		buf += strlen(buf);
X		cp += 8;
X    }
X
X    for(; len > 0; len--){
X		sprintf(buf, "%02X ", *cp++);
X		buf += strlen(buf);
X    }
X    *buf = '\0';
X}
X
X
Xstatic sprint_asciistring(buf, cp, len)
X
Xchar *buf;
Xu_char  *cp;
Xint	    len;
X
X{
X    int	x;
X
X    for(x = 0; x < len; x++){
X		if (*cp){
X		    *buf++ = *cp++;
X		} else {
X		    *buf++ = '.';
X		    cp++;
X		}
X    }
X    *buf = '\0';
X}
X
X
Xstatic void sprint_octet_string(buf, var)
X
Xchar *buf;
Xstruct variable_list *var;
X
X{
X    if (var->type != ASN_OCTET_STR){
X		sprintf(buf, "Wrong Type (should be OCTET STRING): ");
X		buf += strlen(buf);
X		sprint_by_type(buf, var, (struct enum_list *)NULL);
X		return;
X    }
X    sprint_asciistring(buf, var->val.string, var->val_len);
X}
X
X
Xstatic void sprint_ascii_string(buf, var)
X
Xchar *buf;
Xstruct variable_list *var;
X
X{
X
X    if (var->type != ASN_OCTET_STR){
X		sprintf(buf, "Wrong Type (should be OCTET STRING): ");
X		buf += strlen(buf);
X		sprint_by_type(buf, var, (struct enum_list *)NULL);
X		return;
X    }
X	sprint_asciistring(buf, var->val.string, var->val_len);
X}
X
X
Xstatic void sprint_opaque(buf, var)
X
Xchar *buf;
Xstruct variable_list *var;
X
X{
X
X    if (var->type != OPAQUE){
X		sprintf(buf, "Wrong Type (should be Opaque): ");
X		buf += strlen(buf);
X		sprint_by_type(buf, var, (struct enum_list *)NULL);
X		return;
X    }
X    sprint_hexstring(buf, var->val.string, var->val_len);
X}
X
X
Xstatic void sprint_object_identifier(buf, var)
X
Xchar *buf;
Xstruct variable_list *var;
X
X{
X    if (var->type != ASN_OBJECT_ID){
X		sprintf(buf, "Wrong Type (should be OBJECT IDENTIFIER): ");
X		buf += strlen(buf);
X		sprint_by_type(buf, var, (struct enum_list *)NULL);
X		return;
X    }
X    sprint_objid(buf, (oid *)(var->val.objid), var->val_len / sizeof(oid));
X}
X
X
Xstatic void sprint_timeticks(buf, var)
X
Xchar *buf;
Xstruct variable_list *var;
X
X{
X    char timebuf[32];
X
X    if (var->type != TIMETICKS){
X		sprintf(buf, "Wrong Type (should be Timeticks): ");
X		buf += strlen(buf);
X		sprint_by_type(buf, var, (struct enum_list *)NULL);
X		return;
X    }
X    sprintf(buf, "%d (%s)", 
X            *(var->val.integer), uptimeString(*(var->val.integer), timebuf));
X}
X
X
Xstatic void sprint_integer(buf, var, enums)
X
Xchar *buf;
Xstruct variable_list *var;
Xstruct enum_list	    *enums;
X
X{
X    char    *enum_string = NULL;
X
X    if (var->type != ASN_INTEGER){
X		sprintf(buf, "Wrong Type (should be INTEGER): ");
X		buf += strlen(buf);
X		sprint_by_type(buf, var, (struct enum_list *)NULL);
X		return;
X    }
X    for (; enums; enums = enums->next) {
X		if (enums->value == *var->val.integer){
X		    enum_string = enums->label;
X		    break;
X		}
X	}
X
X    if (enum_string == NULL) {
X		sprintf(buf, "%d", *var->val.integer);
X	} else {
X		sprintf(buf, "%s(%d)", enum_string, *var->val.integer);
X	}
X}
X
X
Xstatic void sprint_gauge(buf, var)
X
Xchar *buf;
Xstruct variable_list *var;
X
X{
X    if (var->type != GAUGE){
X		sprintf(buf, "Wrong Type (should be Gauge): ");
X		buf += strlen(buf);
X		sprint_by_type(buf, var, (struct enum_list *)NULL);
X		return;
X    }
X    sprintf(buf, "%lu", *var->val.integer);
X}
X
X
Xstatic void sprint_counter(buf, var)
X
Xchar *buf;
Xstruct variable_list *var;
X
X{
X    if (var->type != COUNTER){
X		sprintf(buf, "Wrong Type (should be Counter): ");
X		buf += strlen(buf);
X		sprint_by_type(buf, var, (struct enum_list *)NULL);
X		return;
X    }
X    sprintf(buf, "%lu", *var->val.integer);
X}
X
X
Xstatic void sprint_networkaddress(buf, var)
X
Xchar *buf;
Xstruct variable_list *var;
X
X{
X    int x, len;
X    u_char *cp;
X
X    cp = var->val.string;    
X    len = var->val_len;
X    for(x = 0; x < len; x++){
X		sprintf(buf, "%02X", *cp++);
X		buf += strlen(buf);
X		if (x < (len - 1)) {
X		    *buf++ = ':';
X		}
X    }
X}
X
X
Xstatic void sprint_ipaddress(buf, var)
X
Xchar *buf;
Xstruct variable_list *var;
X
X{
X    u_char *ip;
X
X    if (var->type != IPADDRESS){
X		sprintf(buf, "Wrong Type (should be Ipaddress): ");
X		buf += strlen(buf);
X		sprint_by_type(buf, var, (struct enum_list *)NULL);
X		return;
X    }
X    ip = var->val.string;
X    sprintf(buf, "%d.%d.%d.%d",ip[0], ip[1], ip[2], ip[3]);
X}
X
X
Xstatic void sprint_unsigned_short(buf, var)
X
Xchar *buf;
Xstruct variable_list *var;
X
X{
X    if (var->type != ASN_INTEGER){
X		sprintf(buf, "Wrong Type (should be INTEGER): ");
X		buf += strlen(buf);
X		sprint_by_type(buf, var, (struct enum_list *)NULL);
X		return;
X    }
X    sprintf(buf, "%lu", *var->val.integer);
X}
X
X
Xstatic void sprint_null(buf, var)
X
Xchar *buf;
Xstruct variable_list *var;
X
X{
X    if (var->type != ASN_NULL){
X		sprintf(buf, "Wrong Type (should be NULL): ");
X		buf += strlen(buf);
X		sprint_by_type(buf, var, (struct enum_list *)NULL);
X		return;
X    }
X    sprintf(buf, "NULL");
X}
X
X
Xstatic void sprint_unknowntype(buf, var)
X
Xchar *buf;
Xstruct variable_list *var;
X
X{
X	/*    sprintf(buf, "Variable has bad type"); */
X    sprint_by_type(buf, var, NULL);
X}
X
X
Xstatic void sprint_badtype(buf)
X
Xchar *buf;
X
X{
X    sprintf(buf, "Variable has bad type");
X}
X
X
Xstatic void sprint_by_type(buf, var, enums)
X
Xchar *buf;
Xstruct variable_list *var;
Xstruct enum_list	    *enums;
X
X{
X    switch (var->type){
X		case ASN_INTEGER:
X		    sprint_integer(buf, var, enums);
X	    break;
X		case ASN_OCTET_STR:
X		    sprint_octet_string(buf, var);
X	    break;
X		case OPAQUE:
X		    sprint_opaque(buf, var);
X	    break;
X		case ASN_OBJECT_ID:
X		    sprint_object_identifier(buf, var);
X	    break;
X		case TIMETICKS:
X		    sprint_timeticks(buf, var);
X	    break;
X		case GAUGE:
X		    sprint_gauge(buf, var);
X	    break;
X		case COUNTER:
X		    sprint_counter(buf, var);
X	    break;
X		case IPADDRESS:
X		    sprint_ipaddress(buf, var);
X	    break;
X		case ASN_NULL:
X		    sprint_null(buf, var);
X	    break;
X		default:
X		    sprint_badtype(buf);
X	    break;
X    }
X}
X
X
Xinit_mib()
X{
X    char *file, *getenv();
X
X    file = getenv("MIBFILE");
X    if (file) {
X		Mib = read_mib(file);
X	}
X    if (!Mib) {
X		Mib = read_mib("mib.txt");
X	}
X    if (!Mib) {
X		Mib = read_mib("/etc/mib.txt");
X	}
X    if (!Mib){
X		fprintf(stderr, "Couldn't find mib file\n");
X		exit(2);
X    }
X    set_functions(Mib);
X}
X
X
Xstatic set_functions(subtree)
X
Xstruct tree *subtree;
X
X{
X    for(; subtree; subtree = subtree->next_peer){
X
X		switch(subtree->type){
X		    case TYPE_OBJID:
X				subtree->printer = sprint_object_identifier;
X			break;
X		    case TYPE_OCTETSTR:
X				subtree->printer = sprint_octet_string;
X			break;
X		    case TYPE_INTEGER:
X				subtree->printer = sprint_integer;
X			break;
X		    case TYPE_NETADDR:
X				subtree->printer = sprint_networkaddress;
X			break;
X		    case TYPE_IPADDR:
X				subtree->printer = sprint_ipaddress;
X			break;
X		    case TYPE_COUNTER:
X				subtree->printer = sprint_counter;
X			break;
X		    case TYPE_GAUGE:
X				subtree->printer = sprint_gauge;
X			break;
X		    case TYPE_TIMETICKS:
X				subtree->printer = sprint_timeticks;
X			break;
X		    case TYPE_OPAQUE:
X				subtree->printer = sprint_opaque;
X			break;
X		    case TYPE_NULL:
X				subtree->printer = sprint_null;
X			break;
X		    case TYPE_DISPLAYSTR:
X				subtree->printer = sprint_ascii_string;
X			break;
X		    case TYPE_OTHER:
X		    default:
X				subtree->printer = sprint_unknowntype;
X			break;
X		}
X	set_functions(subtree->child_list);
X    }
X}
X
X
Xstatic struct tree *find_rfc1066_mib(root)
X
Xstruct tree *root;
X
X{
X    oid *op = RFC1066_MIB;
X    struct tree *tp;
X    int len;
X
X    for(len = sizeof(RFC1066_MIB)/sizeof(oid); len; len--, op++){
X		for(tp = root; tp; tp = tp->next_peer){
X		    if (tp->subid == *op){
X				root = tp->child_list;
X				break;
X		    }
X		}
X		if (tp == NULL) {
X		    return NULL;
X		}
X    }
X    return root;
X}
X
X
Xint read_objid(input, output, out_len)
X
Xchar *input;
Xoid *output;
Xint	*out_len;   /* number of subid's in "output" */
X
X{
X    struct tree *root = Mib;
X    oid *op = output;
X    int i;
X
X    if (*input == '.') {
X		input++;
X	} else {
X		root = find_rfc1066_mib(root);
X		for (i = 0; i < sizeof (RFC1066_MIB)/sizeof(oid); i++) {
X		    if ((*out_len)-- > 0) {
X				*output++ = RFC1066_MIB[i];
X			} else {
X				fprintf(stderr, "object identifier too long\n");
X				return (0);
X	    	}
X		}
X    }
X
X    if (root == NULL){
X		fprintf(stderr, "Mib not initialized.  Exiting.\n");
X		exit(1);
X    }
X    if ((*out_len =	 parse_subtree(root, input, output, out_len)) == 0) {
X		return (0);
X	}
X	
X    *out_len += output - op;
X
X    return (1);
X}
X
X
Xstatic parse_subtree(subtree, input, output, out_len)
X
Xstruct tree *subtree;
Xchar *input;
Xoid	*output;
Xint	*out_len;   /* number of subid's */
X
X{
X    char buf[128], *to = buf;
X    u_long subid = 0;
X    struct tree *tp;
X
X    /*
X     * No empty strings.  Can happen if there is a trailing '.' or two '.'s
X     * in a row, i.e. "..".
X     */
X
X    if ((*input == '\0') || (*input == '.')) {
X		return (0);
X	}
X	
X    if (isdigit(*input)) {
X		/*
X		 * Read the number, then try to find it in the subtree.
X		 */
X		while (isdigit(*input)) {
X		    subid *= 10;
X		    subid += *input++ - '0';
X		}
X		for (tp = subtree; tp; tp = tp->next_peer) {
X		    if (tp->subid == subid)
X			goto found;
X		}
X		tp = NULL;
X    } else {
X		/*
X		 * Read the name into a buffer.
X		 */
X		while ((*input != '\0') && (*input != '.')) {
X		    *to++ = *input++;
X		}
X		*to = '\0';
X
X		/*
X		 * Find the name in the subtree;
X		 */
X		for (tp = subtree; tp; tp = tp->next_peer) {
X		    if (lc_cmp(tp->label, buf) == 0) {
X				subid = tp->subid;
X				goto found;
X		    }
X		}
X
X		/*
X		 * If we didn't find the entry, punt...
X		 */
X		if (tp == NULL) {
X		    fprintf(stderr, "sub-identifier not found: %s\n", buf);
X		    return (0);
X		}
X    }
X
Xfound:
X    if(subid > (u_long)MAX_SUBID){
X		fprintf(stderr, "sub-identifier too large: %s\n", buf);
X		return (0);
X    }
X
X    if ((*out_len)-- <= 0){
X		fprintf(stderr, "object identifier too long\n");
X		return (0);
X    }
X    *output++ = subid;
X
X    if (*input != '.') {
X		return (1);
X	}
X    if ((*out_len = parse_subtree(tp ? tp->child_list : NULL, 
X                                  ++input, output, out_len)) == 0) {
X		return (0);
X	}
X    return (++*out_len);
X}
X
X
Xstruct tree * get_symbol(objid, objidlen, subtree, buf)
X
Xoid	    *objid;
Xint	    objidlen;
Xstruct tree    *subtree;
Xchar    *buf;
X
X{
X    struct tree    *return_tree = NULL;
X
X    for(; subtree; subtree = subtree->next_peer){
X		if (*objid == subtree->subid){
X		    strcpy(buf, subtree->label);
X		    goto found;
X		}
X    }
X
X    /* subtree not found */
X    while(objidlen--){	/* output rest of name, uninterpreted */
X		sprintf(buf, "%u.", *objid++);
X		while(*buf) {
X		    buf++;
X		}
X    }
X    *(buf - 1) = '\0'; /* remove trailing dot */
X    return NULL;
X
Xfound:
X    if (objidlen > 1){
X		while(*buf) {
X		    buf++;
X		}
X		*buf++ = '.';
X		*buf = '\0';
X		return_tree = get_symbol(objid + 1, objidlen - 1, 
X		                         subtree->child_list, buf);
X    } 
X    if (return_tree != NULL) {
X		return return_tree;
X	} else {
X		return subtree;
X	}
X}
X
X
Xprint_objid(objid, objidlen)
X
Xoid	    *objid;
Xint	    objidlen;	/* number of subidentifiers */
X
X{
X    char    buf[256];
X    struct tree    *subtree = Mib;
X
X    *buf = '.';	/* this is a fully qualified name */
X    get_symbol(objid, objidlen, subtree, buf + 1);
X    printf("%s\n", buf);
X        
X}
X
X
Xsprint_objid(buf, objid, objidlen)
X
Xchar *buf;
Xoid	    *objid;
Xint	    objidlen;	/* number of subidentifiers */
X
X{
X    struct tree    *subtree = Mib;
X
X    *buf = '.';	/* this is a fully qualified name */
X    get_symbol(objid, objidlen, subtree, buf + 1);
X}
X
X
Xprint_variable(objid, objidlen, variable)
X
Xoid     *objid;
Xint	    objidlen;
Xstruct  variable_list *variable;
X
X{
X    char    buf[512], *cp;
X    struct tree    *subtree = Mib;
X
X    *buf = '.';	/* this is a fully qualified name */
X    subtree = get_symbol(objid, objidlen, subtree, buf + 1);
X    cp = buf;
X    if ( (strlen(buf) >= strlen((char *)RFC1066_MIB_text)) && 
X         ! bcmp( buf, (char *)RFC1066_MIB_text,
X                 strlen((char *)RFC1066_MIB_text))){
X	    cp += sizeof(RFC1066_MIB_text);
X    }
X    printf("Name: %s\n", cp);
X    *buf = '\0';
X    if (subtree->printer) {
X		(*subtree->printer)(buf, variable, subtree->enums);
X	} else {
X		sprint_by_type(buf, variable, subtree->enums);
X    }
X    printf("%s\n", buf);
X}
X
X
Xsprint_variable(buf, objid, objidlen, variable)
X
Xchar *buf;
Xoid     *objid;
Xint	    objidlen;
Xstruct  variable_list *variable;
X
X{
X    char    tempbuf[512], *cp;
X    struct tree    *subtree = Mib;
X
X    *tempbuf = '.';	/* this is a fully qualified name */
X    subtree = get_symbol(objid, objidlen, subtree, tempbuf + 1);
X    cp = tempbuf;
X    if ( (strlen(tempbuf) >= strlen((char *)RFC1066_MIB_text)) && 
X	     ! bcmp(tempbuf, (char *)RFC1066_MIB_text,
X		        strlen((char *)RFC1066_MIB_text))){
X	    cp += sizeof(RFC1066_MIB_text);
X    }
X    sprintf(buf, "Name: %s\n", cp);
X    buf += strlen(buf);
X    if (subtree->printer) {
X		(*subtree->printer)(buf, variable, subtree->enums);
X     } else {
X		sprint_by_type(buf, variable, subtree->enums);
X    }
X    strcat(buf, "\n");
X}
X
X
Xsprint_value(buf, objid, objidlen, variable)
X
Xchar *buf;
Xoid     *objid;
Xint	    objidlen;
Xstruct  variable_list *variable;
X
X{
X    char    tempbuf[512];
X    struct tree    *subtree = Mib;
X
X    subtree = get_symbol(objid, objidlen, subtree, tempbuf);
X    if (subtree->printer) {
X		(*subtree->printer)(buf, variable, subtree->enums);
X	} else {
X		sprint_by_type(buf, variable, subtree->enums);
X    }
X}
X
X
Xprint_value(objid, objidlen, variable)
X
Xoid     *objid;
Xint	    objidlen;
Xstruct  variable_list *variable;
X
X{
X    char    tempbuf[512];
X    struct tree    *subtree = Mib;
X
X    subtree = get_symbol(objid, objidlen, subtree, tempbuf);
X    if (subtree->printer) {
X		(*subtree->printer)(tempbuf, variable, subtree->enums);
X	} else {
X		sprint_by_type(tempbuf, variable, subtree->enums);
X    }
X    printf("%s\n", tempbuf);
X}
X
X
Xstatic int lc_cmp(s1, s2)
Xchar *s1, *s2;
X
X{
X    char c1, c2;
X
X    while(*s1 && *s2){
X		if (isupper(*s1)) {
X		    c1 = tolower(*s1);
X		} else {
X		    c1 = *s1;
X		}
X		if (isupper(*s2)) {
X		    c2 = tolower(*s2);
X		} else {
X		    c2 = *s2;
X		}
X		
X		if (c1 != c2) {
X		    return ((c1 - c2) > 0 ? 1 : -1);
X		}
X
X		s1++;
X		s2++;
X    }
X
X    if (*s1) {
X		return -1;
X	}
X    if (*s2) {
X		return 1;
X	}
X    return 0;
X}
X
SHAR_EOF
chmod 0444 mib.c ||
echo 'restore of mib.c failed'
Wc_c="`wc -c < 'mib.c'`"
test 16764 -eq "$Wc_c" ||
	echo 'mib.c: original size 16764, current size' "$Wc_c"
fi
# ============= snmp.pl ==============
if test -f 'snmp.pl' -a X"$1" != X"-c"; then
	echo 'x - skipping snmp.pl (File already exists)'
else
sed 's/^X//' << 'SHAR_EOF' > 'snmp.pl' &&
X#
X# a few useful routines.
X#
X# $Revision: 1.2 $
X#
Xpackage SNMP;
X
X# not much debug output here, but it can be turned on by setting
X# $SNMP'debug to a positive value.
X
X$debug = 0;
X
X# &inet_addr takes an IP address in ASCII dotted quad form (0.0.0.0) and
X# returns an integer in network byte-order.
X
Xsub main'inet_addr {			# @_ = (dotted quad)
X    return unpack("N",pack("C4",split(/\./,$_[0])));
X}
X
X# &inet_ntoa takes an integer IP address in network byte-order and returns
X# an ASCII string in dotted decimal notation.
X
Xsub main'inet_ntoa {			# @_ = (integer)
X    return join('.',unpack('C4',pack('N',$_[0])));
X}
X
X# &dottedquad takes a hostname or dotted-quad IP address and returns
X# a dotted-quad address.
X
Xsub main'dottedquad {		# @_ = (hostname or dotted quad)
X    if ($_[0] =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
X	return $_[0];
X    } else {
X	$saddr = (gethostbyname($_[0]))[4] || return undef;
X	return join('.',unpack("C4",$saddr));
X    }
X}
X
X# $SNMP'communityFile specifies the location of a DBM file that associates
X# a host with a Community string.
X
X$communityFile = "/etc/mib.commun";
X
X# &communityString returns the Community string associated with a given
X# key.  For simplicity the key should be the dotted-decimal ASCII form
X# of the address of the host, but the routine doesn't care what the key
X# is.  "public" is returned if the key or the file doesn't exist.
X
Xsub main'communityString {		# @_ = (dotted quad)
X    if (undef %communityArray) {
X	warn "opening $communityFile for reading\n" if $debug;
X	dbmopen(%communityArray,$communityFile,undef)
X	    || warn "somebody stole $communityFile!\n" if $debug;
X    }
X    warn "using default community\n" if $debug > 1
X	&& ! $communityArray{@_[0]};
X    return $communityArray{@_[0]} || "public";
X}
X
X# &snmp_getvalues() takes a hostname, community string, and list of questions.
X# It returns an associative array whose indices are the questions and whose
X# values are the responses.  If no questions were answered, nil is returned.
X
Xsub main'snmp_getvalues {
X    local (
X	   $host,
X	   $comm,
X	   @q,
X	   @result,
X	   %retval
X	   );
X    $host = shift _;
X    $comm = shift _;
X    @q = @_;
X
X    (@result = &main'snmp_get($host,$comm,@q)) || return ();
Xloop:
X    while (@result)
X    {
X	foreach $q (@q)
X	{
X	    if ($result[0] =~ /$q/)
X	    {
X		shift result;
X		$retval{$q} = shift result;
X		next loop;
X	    }
X	}
X	die "Unexpected result: $result[0]\n";
X    }
X    return %retval;
X}
SHAR_EOF
chmod 0444 snmp.pl ||
echo 'restore of snmp.pl failed'
Wc_c="`wc -c < 'snmp.pl'`"
test 2427 -eq "$Wc_c" ||
	echo 'snmp.pl: original size 2427, current size' "$Wc_c"
fi
# ============= snmp-tracer ==============
if test -f 'snmp-tracer' -a X"$1" != X"-c"; then
	echo 'x - skipping snmp-tracer (File already exists)'
else
sed 's/^X//' << 'SHAR_EOF' > 'snmp-tracer' &&
X#!/usr/local/bin/snmperl
X
X#
X# an SNMP implementation of 'traceroute'.  Only works if all the intermediate
X# systems are MIB-II.
X#
X# Guy Streeter	$Revision: 1.2 $
X#
X
Xrequire 'snmp.pl';
X
Xsub subnet {			# @_ = (dotted quad)
X    split(/\./,$destination);
X
X    if ($_[1] < 128) {
X	return "255.0.0.0";
X    } else {
X	if ($_[1] < 192) {
X	    return "255.255.0.0";
X	} else {
X	    return "255.255.255.0";
X	}
X    }
X}
X
Xsub findMask {
X    return $netmask{$start} if $netmask{$start};
X    local($dnum) = &inet_addr($destination);
X    local($ipAdEntAddr) = "ip.ipAddrTable.ipAddrEntry.ipAdEntAddr";
X    local($ipAdEntNetMask) = "ip.ipAddrTable.ipAddrEntry.ipAdEntNetMask";
X    local($nextAddr) = $ipAdEntAddr;
X    local($nextMask) = $ipAdEntNetMask;
X    local($addrValue,$maskValue,$mnum);
X    while ($nextMask) {
X	($nextAddr,$addrValue,$nextMask,$maskValue) =
X	    &snmp_next($start,&communityString($start),$nextAddr,$nextMask);
X	last unless $nextMask =~ /$ipAdEntNetMask/;
X	$mnum = &inet_addr($maskValue);
X	if (($dnum & $mnum) == (&inet_addr($addrValue) & $mnum)) {
X	    return $netmask{$start} = $maskValue;
X	}
X    }
X    return $netmask{$start} = &subnet($destination);
X}
X
Xwhile ($a=shift(ARGV)) {
X    $debug++, next if $a eq "-d";
X    die "Usage: $0 [-d] destination [start]\n" if $a =~ /-.*/;
X    $destination = $a, next unless $destination;
X    $start = $a, next unless $start;
X    die "Usage: $0 [-d] destination [start]\n";
X}
X
X$SNMP'debug = $debug;
X
Xdie "Usage: $0 [-d] destination [start]\n" unless $destination;
X
Xchop($start = `uname -n`) unless $start;
X
Xprint "from $start to $destination:\n";
X
X$start = &dottedquad($start) || die "Address lookup failed for $start\n";
X$destination = &dottedquad($destination)
X    || die "Address lookup failed for $destination\n";
X
X$dest_num = &inet_addr($destination);
X
X$ipRouteDest = "ip.ipRoutingTable.ipRouteEntry.ipRouteDest";
X$ipRouteNextHop = "ip.ipRoutingTable.ipRouteEntry.ipRouteNextHop";
X$ipRouteType = "ip.ipRoutingTable.ipRouteEntry.ipRouteType";
X$ipRouteMask = "ip.ipRoutingTable.ipRouteEntry.ipRouteMask";
X
X$| = 1;				# unbuffered stdout
X$indent = " ";
X%netmask = ();
X
Xwhile ($start && $start ne $destination) {
X    print $indent, "from $start";
X    $indent .= " ";
X
X    $nextDest = $ipRouteDest;
X    $nextNextHop = $ipRouteNextHop;
X    $nextType = $ipRouteType;
X    $nextMask = $ipRouteMask;
X    undef $hop;
X
X    $tryDest = &inet_ntoa(&inet_addr($destination) & &inet_addr(&findMask));
X    if (@result = &snmp_get($start,&communityString($start),
X			    "$nextDest.$tryDest","$nextNextHop.$tryDest",
X			    "$nextType.$tryDest")) {
X	undef $valueOfNextHop;
X	if ($result[0] =~ /$ipRouteDest/) {
X	    shift(result);
X	    shift(result);
X	}
X	if ($result[0] =~ /$ipRouteNextHop/) {
X	    shift(result);
X	    $valueOfNextHop = shift(result);
X	}
X	if ($result[0] =~ /$ipRouteType/) {
X	    shift(result);
X	    $valueOfType = shift(result);
X	}
X	if ($valueOfType &&
X	    !($valueOfType =~ /invalid/ || $valueOfType == 2))
X	{
X	    $hop = $valueOfNextHop;
X	    undef $nextDest if $valueOfType;
X	}
X    }
X
X    undef $default;
X
X    while ($nextDest) {
X	@questions = ($nextDest,$nextNextHop,$nextType);
X	push(questions,$nextMask) if $nextMask;
X	if ($debug > 2) {
X	    warn "asking @questions\nof $start ",
X	    &communityString($start),"\n";
X	}
X	@result = &snmp_next($start,&communityString($start),@questions);
X	die "Something is foobar" unless @result;
X
X	undef $nextDest;
X	undef $nextMask;
X
X	if ($result[0] =~ /$ipRouteDest/) {
X	    $nextDest = shift(result);
X	    $valueOfDest = shift(result);
X	}
X	if ($result[0] =~ /$ipRouteNextHop/) {
X	    $nextNextHop = shift(result);
X	    $valueOfNextHop = shift(result);
X	}
X	if ($result[0] =~ /$ipRouteType/) {
X	    $nextType = shift(result);
X	    $valueOfType = shift(result);
X	}
X	if ($result[0] =~ /$ipRouteMask/) {
X	    $nextMask = shift(result);
X	    $valueOfMask = shift(result);
X	}
X
X	last unless $nextDest;
X
X	next if $valueOfType =~ /invalid/ || $valueOfType == 2;
X
X	$valueOfMask = &findMask unless $nextMask;
X
X	if ($debug) {
X	    warn "mask is $valueOfMask\n";
X	    warn "mask is from default\n" unless $nextMask;
X	}
X
X	$thisdest = &inet_addr($valueOfDest);
X	if ($thisdest == 0) {
X	    warn "default route discovered: $valueOfNextHop\n" if $debug;
X	    $default = $valueOfNextHop;
X	    next;
X	}
X	$thismask = &inet_addr($valueOfMask);
X
X	printf(STDERR "comparing %lx and %lx, mask %lx\n", $dest_num,
X	       $thisdest, $thismask) if $debug > 1;
X	if (($dest_num & $thismask) == ($thisdest & $thismask))
X	{
X	    $hop = $valueOfNextHop;
X	    last;
X	}
X    }
X
X    $start = $hop ? $hop : $default;
X    print " to $start, type = $valueOfType\n";
X
X    exit 0 if (($dest_num & $thismask) == ($thismask & &inet_addr($start)));
X}
X
Xdie "I got to here somehow";
SHAR_EOF
chmod 0555 snmp-tracer ||
echo 'restore of snmp-tracer failed'
Wc_c="`wc -c < 'snmp-tracer'`"
test 4723 -eq "$Wc_c" ||
	echo 'snmp-tracer: original size 4723, current size' "$Wc_c"
fi
exit 0