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: 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> <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 X#include X#include 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_ X * routines so the don't prepend ": " 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 X#include X#include X#include X#include 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