--- /dev/null
+##
+## Makefile for libsnmp.
+##
+## because this is for Linux with ELF only, we are building
+## a shared lib, to be installed in /lib.
+##
+
+## set to installation prefix (overridden by configure --prefix=path):
+prefix=@prefix@
+
+VERSION = 3.4
+OSHLIB= libsnmp.so
+ALIB = libsnmp.a
+
+TARG= @SNMPLIB@
+
+CSRCS= snmp_client.c snmp.c snmp_auth.c asn1.c mib.c parse.c snmp_api.c \
+ md5.c usec.c snmpdump.c
+OBJS= snmp_client.o snmp.o snmp_auth.o asn1.o mib.o parse.o snmp_api.o \
+ md5.o usec.o snmpdump.o
+
+CC= @CC@
+FLAGS=
+DEFS= -DBSD4_3 -DSQUID_SNMP
+INCLUDE= -I../include -I./../include -I../src -I./../src
+MIBDEFS= -DMIBFILEPATH=\"$(prefix)/lib\"
+# CFLAGS=-O -DDEBUG -DBSD4_3
+CFLAGS= $(FLAGS) $(DEFS) $(CONFDEFS) $(INCLUDE)
+
+
+all: $(TARG)
+install: all
+$(OSHLIB): ${OBJS}
+ $(CC) -shared -Wl,-soname,$(OSHLIB).$(VERSION) -o $(OSHLIB).$(VERSION) $(OBJS)
+ ln -sf $(OSHLIB).$(VERSION) $(OSHLIB)
+
+$(ALIB): ${OBJS}
+ @AR_R@ $(ALIB) ${OBJS}
+ @RANLIB@ $(ALIB)
+
+##
+## special rule to include -DMIBFILEPATH=...:
+##
+mib.o: mib.c
+ $(CC) $(CFLAGS) $(MIBDEFS) -c mib.c
+
+clean:
+ rm -f core *.o ${TARG} shared/*.o *.a *.so*
+
+
+# install: ../lib/libsnmp.a
+#
+# ../lib/libsnmp.a: libsnmp.a
+# cp libsnmp.a ../lib
+# ranlib ../lib/libsnmp.a
+#
+# lint:
+# lint -nhx $(CSRCS)
+
+asn1.o:
+mib.o:
+mib.o:
+mib.o:
+mib.o:
+parse.o:
+snmp.o:
+snmp.o:
+snmp.o:
+snmp.o:
+snmp_api.o:
+snmp_api.o:
+snmp_api.o:
+snmp_api.o:
+snmp_auth.o:
+snmp_auth.o:
+snmp_auth.o:
+snmp_client.o:
+snmp_client.o:
+snmp_client.o:
+snmp_client.o:
+snmp_client.o:
+
--- /dev/null
+/*
+ * Abstract Syntax Notation One, ASN.1
+ * As defined in ISO/IS 8824 and ISO/IS 8825
+ * This implements a subset of the above International Standards that
+ * is sufficient to implement SNMP.
+ *
+ * Encodes abstract data types into a machine independent stream of bytes.
+ *
+ */
+/**********************************************************************
+ Copyright 1988, 1989, 1991, 1992 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of CMU not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+******************************************************************/
+#ifdef KINETICS
+#include "gw.h"
+#endif
+
+#ifdef linux
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#endif
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#ifdef vms
+#include <in.h>
+#endif
+
+
+#include "asn1.h"
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifdef DEBUG
+#define ERROR(string) printf("%s(%d): %s",__FILE__, __LINE__, string);
+#else
+#define ERROR(string)
+#endif
+
+
+/*
+ * asn_parse_int - pulls a long out of an ASN int type.
+ * On entry, datalength is input as the number of valid bytes following
+ * "data". On exit, it is returned as the number of valid bytes
+ * following the end of this object.
+ *
+ * Returns a pointer to the first byte past the end
+ * of this object (i.e. the start of the next object).
+ * Returns NULL on any error.
+ */
+u_char *
+asn_parse_int(data, datalength, type, intp, intsize)
+ u_char *data; /* IN - pointer to start of object */
+ int *datalength; /* IN/OUT - number of valid bytes left in buffer */
+ u_char *type; /* OUT - asn type of object */
+ long *intp; /* IN/OUT - pointer to start of output buffer */
+ int intsize; /* IN - size of output buffer */
+{
+/*
+ * ASN.1 integer ::= 0x02 asnlength byte {byte}*
+ */
+ u_char *bufp = data;
+ u_long asn_length;
+ long value = 0;
+
+ if (intsize != sizeof (long)){
+ ERROR("not long");
+ return NULL;
+ }
+ *type = *bufp++;
+ bufp = asn_parse_length(bufp, &asn_length);
+ if (bufp == NULL){
+ ERROR("bad length");
+ return NULL;
+ }
+ if (asn_length + (bufp - data) > *datalength){
+ ERROR("overflow of message");
+ return NULL;
+ }
+ if (asn_length > intsize){
+ ERROR("I don't support such large integers");
+ return NULL;
+ }
+ *datalength -= (int)asn_length + (bufp - data);
+ if (*bufp & 0x80)
+ value = -1; /* integer is negative */
+ while(asn_length--)
+ value = (value << 8) | *bufp++;
+ *intp = value;
+ return bufp;
+}
+
+
+/*
+ * asn_parse_unsigned_int - pulls an unsigned long out of an ASN int type.
+ * On entry, datalength is input as the number of valid bytes following
+ * "data". On exit, it is returned as the number of valid bytes
+ * following the end of this object.
+ *
+ * Returns a pointer to the first byte past the end
+ * of this object (i.e. the start of the next object).
+ * Returns NULL on any error.
+ */
+u_char *
+asn_parse_unsigned_int(data, datalength, type, intp, intsize)
+ u_char *data; /* IN - pointer to start of object */
+ int *datalength;/* IN/OUT - number of valid bytes left in buffer */
+ u_char *type; /* OUT - asn type of object */
+ u_long *intp; /* IN/OUT - pointer to start of output buffer */
+ int intsize; /* IN - size of output buffer */
+{
+/*
+ * ASN.1 integer ::= 0x02 asnlength byte {byte}*
+ */
+ u_char *bufp = data;
+ u_long asn_length;
+ u_long value = 0;
+
+ if (intsize != sizeof (long)){
+ ERROR("not long");
+ return NULL;
+ }
+ *type = *bufp++;
+ bufp = asn_parse_length(bufp, &asn_length);
+ if (bufp == NULL){
+ ERROR("bad length");
+ return NULL;
+ }
+ if (asn_length + (bufp - data) > *datalength){
+ ERROR("overflow of message");
+ return NULL;
+ }
+ if ((asn_length > (intsize + 1)) ||
+ ((asn_length == intsize + 1) && *bufp != 0x00)){
+ ERROR("I don't support such large integers");
+ return NULL;
+ }
+ *datalength -= (int)asn_length + (bufp - data);
+ if (*bufp & 0x80)
+ value = -1; /* integer is negative */
+ while(asn_length--)
+ value = (value << 8) | *bufp++;
+ *intp = value;
+ return bufp;
+}
+
+
+/*
+ * asn_build_int - builds an ASN object containing an integer.
+ * On entry, datalength is input as the number of valid bytes following
+ * "data". On exit, it is returned as the number of valid bytes
+ * following the end of this object.
+ *
+ * Returns a pointer to the first byte past the end
+ * of this object (i.e. the start of the next object).
+ * Returns NULL on any error.
+ */
+u_char *
+asn_build_int(data, datalength, type, intp, intsize)
+ u_char *data; /* IN - pointer to start of output buffer */
+ int *datalength;/* IN/OUT - number of valid bytes left in buffer */
+ u_char type; /* IN - asn type of object */
+ long *intp; /* IN - pointer to start of long integer */
+ int intsize; /* IN - size of *intp */
+{
+/*
+ * ASN.1 integer ::= 0x02 asnlength byte {byte}*
+ */
+
+ long integer;
+ u_long mask;
+
+ if (intsize != sizeof (long)) {
+ ERROR("not long");
+ return NULL;
+ }
+ integer = *intp;
+ /*
+ * Truncate "unnecessary" bytes off of the most significant end of this
+ * 2's complement integer. There should be no sequence of 9
+ * consecutive 1's or 0's at the most significant end of the
+ * integer.
+ */
+ mask = 0x1FF << ((8 * (sizeof(int32) - 1)) - 1);
+ /* mask is 0xFF800000 on a big-endian machine */
+ while((((integer & mask) == 0) || ((integer & mask) == mask))
+ && intsize > 1){
+ intsize--;
+ integer <<= 8;
+ }
+ data = asn_build_header(data, datalength, type, intsize);
+ if (data == NULL)
+ return NULL;
+ if (*datalength < intsize)
+ return NULL;
+ *datalength -= intsize;
+ mask = 0xFF << (8 * (sizeof(int32) - 1));
+ /* mask is 0xFF000000 on a big-endian machine */
+ while(intsize--){
+ *data++ = (u_char)((integer & mask) >> (8 * (sizeof(int32) - 1)));
+ integer <<= 8;
+ }
+ return data;
+}
+
+
+/*
+ * asn_build_unsigned_int - builds an ASN object containing an integer.
+ * On entry, datalength is input as the number of valid bytes following
+ * "data". On exit, it is returned as the number of valid bytes
+ * following the end of this object.
+ *
+ * Returns a pointer to the first byte past the end
+ * of this object (i.e. the start of the next object).
+ * Returns NULL on any error.
+ */
+u_char *
+asn_build_unsigned_int(data, datalength, type, intp, intsize)
+ u_char *data; /* IN - pointer to start of output buffer */
+ int *datalength;/* IN/OUT - number of valid bytes left in buffer */
+ u_char type; /* IN - asn type of object */
+ u_long *intp; /* IN - pointer to start of long integer */
+ int intsize; /* IN - size of *intp */
+{
+/*
+ * ASN.1 integer ::= 0x02 asnlength byte {byte}*
+ */
+
+ u_long integer;
+ u_long mask;
+ int add_null_byte = 0;
+
+ if (intsize != sizeof (long)) {
+ ERROR("not long");
+ return NULL;
+ }
+ integer = *intp;
+ mask = 0xFF << (8 * (sizeof(int32) - 1));
+ /* mask is 0xFF000000 on a big-endian machine */
+ if ((u_char)((integer & mask) >> (8 * (sizeof(int32) - 1))) & 0x80){
+ /* if MSB is set */
+ add_null_byte = 1;
+ intsize++;
+ }
+ /*
+ * Truncate "unnecessary" bytes off of the most significant end of this 2's complement integer.
+ * There should be no sequence of 9 consecutive 1's or 0's at the most significant end of the
+ * integer.
+ */
+ mask = 0x1FF << ((8 * (sizeof(int32) - 1)) - 1);
+ /* mask is 0xFF800000 on a big-endian machine */
+ while((((integer & mask) == 0) || ((integer & mask) == mask)) && intsize > 1){
+ intsize--;
+ integer <<= 8;
+ }
+ data = asn_build_header(data, datalength, type, intsize);
+ if (data == NULL)
+ return NULL;
+ if (*datalength < intsize)
+ return NULL;
+ *datalength -= intsize;
+ if (add_null_byte == 1){
+ *data++ = '\0';
+ intsize--;
+ }
+ mask = 0xFF << (8 * (sizeof(int32) - 1));
+ /* mask is 0xFF000000 on a big-endian machine */
+ while(intsize--){
+ *data++ = (u_char)((integer & mask) >> (8 * (sizeof(int32) - 1)));
+ integer <<= 8;
+ }
+ return data;
+}
+
+
+/*
+ * asn_parse_string - pulls an octet string out of an ASN octet string type.
+ * On entry, datalength is input as the number of valid bytes following
+ * "data". On exit, it is returned as the number of valid bytes
+ * following the beginning of the next object.
+ *
+ * "string" is filled with the octet string.
+ *
+ * Returns a pointer to the first byte past the end
+ * of this object (i.e. the start of the next object).
+ * Returns NULL on any error.
+ */
+u_char *
+asn_parse_string(data, datalength, type, string, strlength)
+ u_char *data; /* IN - pointer to start of object */
+ int *datalength; /* IN/OUT - number of valid bytes left in buffer */
+ u_char *type; /* OUT - asn type of object */
+ u_char *string; /* IN/OUT - pointer to start of output buffer */
+ int *strlength; /* IN/OUT - size of output buffer */
+{
+/*
+ * ASN.1 octet string ::= primstring | cmpdstring
+ * primstring ::= 0x04 asnlength byte {byte}*
+ * cmpdstring ::= 0x24 asnlength string {string}*
+ */
+ u_char *bufp = data;
+ u_long asn_length;
+
+ *type = *bufp++;
+ bufp = asn_parse_length(bufp, &asn_length);
+ if (bufp == NULL)
+ return NULL;
+ if (asn_length + (bufp - data) > *datalength){
+ ERROR("overflow of message");
+ return NULL;
+ }
+ if (asn_length > *strlength){
+ ERROR("I don't support such long strings");
+ return NULL;
+ }
+ bcopy((char *)bufp, (char *)string, (int)asn_length);
+ *strlength = (int)asn_length;
+ *datalength -= (int)asn_length + (bufp - data);
+ return bufp + asn_length;
+}
+
+
+/*
+ * asn_build_string - Builds an ASN octet string object containing the input string.
+ * On entry, datalength is input as the number of valid bytes following
+ * "data". On exit, it is returned as the number of valid bytes
+ * following the beginning of the next object.
+ *
+ * Returns a pointer to the first byte past the end
+ * of this object (i.e. the start of the next object).
+ * Returns NULL on any error.
+ */
+u_char *
+asn_build_string(data, datalength, type, string, strlength)
+ u_char *data; /* IN - pointer to start of object */
+ int *datalength; /* IN/OUT - number of valid bytes left in buffer */
+ u_char type; /* IN - ASN type of string */
+ u_char *string; /* IN - pointer to start of input buffer */
+ int strlength; /* IN - size of input buffer */
+{
+/*
+ * ASN.1 octet string ::= primstring | cmpdstring
+ * primstring ::= 0x04 asnlength byte {byte}*
+ * cmpdstring ::= 0x24 asnlength string {string}*
+ * This code will never send a compound string.
+ */
+ data = asn_build_header(data, datalength, type, strlength);
+ if (data == NULL)
+ return NULL;
+ if (*datalength < strlength)
+ return NULL;
+ bcopy((char *)string, (char *)data, strlength);
+ *datalength -= strlength;
+ return data + strlength;
+}
+
+
+/*
+ * asn_parse_header - interprets the ID and length of the current object.
+ * On entry, datalength is input as the number of valid bytes following
+ * "data". On exit, it is returned as the number of valid bytes
+ * in this object following the id and length.
+ *
+ * Returns a pointer to the first byte of the contents of this object.
+ * Returns NULL on any error.
+ */
+u_char *
+asn_parse_header(data, datalength, type)
+ u_char *data; /* IN - pointer to start of object */
+ int *datalength;/* IN/OUT - number of valid bytes left in buffer */
+ u_char *type; /* OUT - ASN type of object */
+{
+ u_char *bufp = data;
+ int header_len;
+ u_long asn_length;
+
+ if (*datalength <= 0) {
+ return NULL;
+ }
+
+ /* this only works on data types < 30, i.e. no extension octets */
+ if (IS_EXTENSION_ID(*bufp)){
+ ERROR("can't process ID >= 30");
+ return NULL;
+ }
+ *type = *bufp;
+ bufp = asn_parse_length(bufp + 1, &asn_length);
+ if (bufp == NULL)
+ return NULL;
+ header_len = bufp - data;
+ if (header_len + asn_length > *datalength){
+ ERROR("asn length too long");
+ return NULL;
+ }
+ *datalength = (int)asn_length;
+ return bufp;
+}
+
+/*
+ * asn_build_header - builds an ASN header for an object with the ID and
+ * length specified.
+ * On entry, datalength is input as the number of valid bytes following
+ * "data". On exit, it is returned as the number of valid bytes
+ * in this object following the id and length.
+ *
+ * This only works on data types < 30, i.e. no extension octets.
+ * The maximum length is 0xFFFF;
+ *
+ * Returns a pointer to the first byte of the contents of this object.
+ * Returns NULL on any error.
+ */
+u_char *
+asn_build_header(data, datalength, type, length)
+ u_char *data; /* IN - pointer to start of object */
+ int *datalength;/* IN/OUT - number of valid bytes left in buffer */
+ u_char type; /* IN - ASN type of object */
+ int length; /* IN - length of object */
+{
+ if (*datalength < 1)
+ return NULL;
+ *data++ = type;
+ (*datalength)--;
+ return asn_build_length(data, datalength, length);
+
+}
+
+/*
+ * asn_build_sequence - builds an ASN header for a sequence with the ID and
+ * length specified.
+ * On entry, datalength is input as the number of valid bytes following
+ * "data". On exit, it is returned as the number of valid bytes
+ * in this object following the id and length.
+ *
+ * This only works on data types < 30, i.e. no extension octets.
+ * The maximum length is 0xFFFF;
+ *
+ * Returns a pointer to the first byte of the contents of this object.
+ * Returns NULL on any error.
+ */
+u_char *
+asn_build_sequence(data, datalength, type, length)
+ u_char *data; /* IN - pointer to start of object */
+ int *datalength;/* IN/OUT - number of valid bytes left in buffer */
+ u_char type; /* IN - ASN type of object */
+ int length; /* IN - length of object */
+{
+ *datalength -= 4;
+ if (*datalength < 0){
+ *datalength += 4; /* fix up before punting */
+ return NULL;
+ }
+ *data++ = type;
+ *data++ = (u_char)(0x02 | ASN_LONG_LEN);
+ *data++ = (u_char)((length >> 8) & 0xFF);
+ *data++ = (u_char)(length & 0xFF);
+ return data;
+}
+
+/*
+ * asn_parse_length - interprets the length of the current object.
+ * On exit, length contains the value of this length field.
+ *
+ * Returns a pointer to the first byte after this length
+ * field (aka: the start of the data field).
+ * Returns NULL on any error.
+ */
+u_char *
+asn_parse_length(data, length)
+ u_char *data; /* IN - pointer to start of length field */
+ u_long *length; /* OUT - value of length field */
+{
+ u_char lengthbyte = *data;
+
+ *length = 0;
+ if (lengthbyte & ASN_LONG_LEN){
+ lengthbyte &= ~ASN_LONG_LEN; /* turn MSb off */
+ if (lengthbyte == 0){
+ ERROR("We don't support indefinite lengths");
+ return NULL;
+ }
+ if (lengthbyte > sizeof(long)){
+ ERROR("we can't support data lengths that long");
+ return NULL;
+ }
+ bcopy((char *)data + 1, (char *)length, (int)lengthbyte);
+ /* XXX: is this useable on a 64bit platform ? */
+ *length = ntohl(*length);
+ *length >>= (8 * ((sizeof (*length)) - lengthbyte));
+ return data + lengthbyte + 1;
+ } else { /* short asnlength */
+ *length = (long)lengthbyte;
+ return data + 1;
+ }
+}
+
+u_char *
+asn_build_length(data, datalength, length)
+ u_char *data; /* IN - pointer to start of object */
+ int *datalength;/* IN/OUT - number of valid bytes left in buffer */
+ int length; /* IN - length of object */
+{
+ u_char *start_data = data;
+
+ /* no indefinite lengths sent */
+ if (length < 0x80){
+ if (*datalength < 1){
+ ERROR("build_length");
+ return NULL;
+ }
+ *data++ = (u_char)length;
+ } else if (length <= 0xFF){
+ if (*datalength < 2){
+ ERROR("build_length");
+ return NULL;
+ }
+ *data++ = (u_char)(0x01 | ASN_LONG_LEN);
+ *data++ = (u_char)length;
+ } else { /* 0xFF < length <= 0xFFFF */
+ if (*datalength < 3){
+ ERROR("build_length");
+ return NULL;
+ }
+ *data++ = (u_char)(0x02 | ASN_LONG_LEN);
+ *data++ = (u_char)((length >> 8) & 0xFF);
+ *data++ = (u_char)(length & 0xFF);
+ }
+ *datalength -= (data - start_data);
+ return data;
+
+}
+
+/*
+ * asn_parse_objid - pulls an object indentifier out of an ASN object identifier type.
+ * On entry, datalength is input as the number of valid bytes following
+ * "data". On exit, it is returned as the number of valid bytes
+ * following the beginning of the next object.
+ *
+ * "objid" is filled with the object identifier.
+ *
+ * Returns a pointer to the first byte past the end
+ * of this object (i.e. the start of the next object).
+ * Returns NULL on any error.
+ */
+u_char *
+asn_parse_objid(data, datalength, type, objid, objidlength)
+ u_char *data; /* IN - pointer to start of object */
+ int *datalength; /* IN/OUT - number of valid bytes left in buffer */
+ u_char *type; /* OUT - ASN type of object */
+ oid *objid; /* IN/OUT - pointer to start of output buffer */
+ int *objidlength; /* IN/OUT - number of sub-id's in objid */
+{
+/*
+ * ASN.1 objid ::= 0x06 asnlength subidentifier {subidentifier}*
+ * subidentifier ::= {leadingbyte}* lastbyte
+ * leadingbyte ::= 1 7bitvalue
+ * lastbyte ::= 0 7bitvalue
+ */
+ u_char *bufp = data;
+ oid *oidp = objid + 1;
+ u_long subidentifier;
+ long length;
+ u_long asn_length;
+
+ *type = *bufp++;
+ bufp = asn_parse_length(bufp, &asn_length);
+ if (bufp == NULL)
+ return NULL;
+ if (asn_length + (bufp - data) > *datalength){
+ ERROR("overflow of message");
+ return NULL;
+ }
+ *datalength -= (int)asn_length + (bufp - data);
+
+ /* Handle invalid object identifier encodings of the form 06 00 robustly */
+ if (asn_length == 0)
+ objid[0] = objid[1] = 0;
+
+ length = asn_length;
+ (*objidlength)--; /* account for expansion of first byte */
+ while (length > 0 && (*objidlength)-- > 0){
+ subidentifier = 0;
+ do { /* shift and add in low order 7 bits */
+ subidentifier = (subidentifier << 7) + (*(u_char *)bufp & ~ASN_BIT8);
+ length--;
+ } while (*(u_char *)bufp++ & ASN_BIT8); /* last byte has high bit clear */
+ if (subidentifier > (u_long)MAX_SUBID){
+ ERROR("subidentifier too long");
+ return NULL;
+ }
+ *oidp++ = (oid)subidentifier;
+ }
+
+ /*
+ * The first two subidentifiers are encoded into the first component
+ * with the value (X * 40) + Y, where:
+ * X is the value of the first subidentifier.
+ * Y is the value of the second subidentifier.
+ */
+ subidentifier = (u_long)objid[1];
+ if (subidentifier == 0x2B){
+ objid[0] = 1;
+ objid[1] = 3;
+ } else {
+ objid[1] = (u_char)(subidentifier % 40);
+ objid[0] = (u_char)((subidentifier - objid[1]) / 40);
+ }
+
+ *objidlength = (int)(oidp - objid);
+ return bufp;
+}
+
+/*
+ * asn_build_objid - Builds an ASN object identifier object containing the
+ * input string.
+ * On entry, datalength is input as the number of valid bytes following
+ * "data". On exit, it is returned as the number of valid bytes
+ * following the beginning of the next object.
+ *
+ * Returns a pointer to the first byte past the end
+ * of this object (i.e. the start of the next object).
+ * Returns NULL on any error.
+ */
+u_char *
+asn_build_objid(data, datalength, type, objid, objidlength)
+ u_char *data; /* IN - pointer to start of object */
+ int *datalength; /* IN/OUT - number of valid bytes left in buffer */
+ u_char type; /* IN - ASN type of object */
+ oid *objid; /* IN - pointer to start of input buffer */
+ int objidlength; /* IN - number of sub-id's in objid */
+{
+/*
+ * ASN.1 objid ::= 0x06 asnlength subidentifier {subidentifier}*
+ * subidentifier ::= {leadingbyte}* lastbyte
+ * leadingbyte ::= 1 7bitvalue
+ * lastbyte ::= 0 7bitvalue
+ */
+ u_char buf[MAX_OID_LEN];
+ u_char *bp = buf;
+ oid *op = objid;
+ int asnlength;
+ u_long subid, mask, testmask;
+ int bits, testbits;
+
+ if (objidlength < 2){
+ *bp++ = 0;
+ objidlength = 0;
+ } else {
+ *bp++ = op[1] + (op[0] * 40);
+ objidlength -= 2;
+ op += 2;
+ }
+
+ while(objidlength-- > 0){
+ subid = *op++;
+ if (subid < 127){ /* off by one? */
+ *bp++ = subid;
+ } else {
+ mask = 0x7F; /* handle subid == 0 case */
+ bits = 0;
+ /* testmask *MUST* !!!! be of an unsigned type */
+ for(testmask = 0x7F, testbits = 0; testmask != 0;
+ testmask <<= 7, testbits += 7){
+ if (subid & testmask){ /* if any bits set */
+ mask = testmask;
+ bits = testbits;
+ }
+ }
+ /* mask can't be zero here */
+ for(;mask != 0x7F; mask >>= 7, bits -= 7){
+ /* fix a mask that got truncated above */
+ if (mask == 0x1E00000)
+ mask = 0xFE00000;
+ *bp++ = (u_char)(((subid & mask) >> bits) | ASN_BIT8);
+ }
+ *bp++ = (u_char)(subid & mask);
+ }
+ }
+ asnlength = bp - buf;
+ data = asn_build_header(data, datalength, type, asnlength);
+ if (data == NULL)
+ return NULL;
+ if (*datalength < asnlength)
+ return NULL;
+ bcopy((char *)buf, (char *)data, asnlength);
+ *datalength -= asnlength;
+ return data + asnlength;
+}
+
+/*
+ * asn_parse_null - Interprets an ASN null type.
+ * On entry, datalength is input as the number of valid bytes following
+ * "data". On exit, it is returned as the number of valid bytes
+ * following the beginning of the next object.
+ *
+ * Returns a pointer to the first byte past the end
+ * of this object (i.e. the start of the next object).
+ * Returns NULL on any error.
+ */
+u_char *
+asn_parse_null(data, datalength, type)
+ u_char *data; /* IN - pointer to start of object */
+ int *datalength; /* IN/OUT - number of valid bytes left in buffer */
+ u_char *type; /* OUT - ASN type of object */
+{
+/*
+ * ASN.1 null ::= 0x05 0x00
+ */
+ u_char *bufp = data;
+ u_long asn_length;
+
+ *type = *bufp++;
+ bufp = asn_parse_length(bufp, &asn_length);
+ if (bufp == NULL)
+ return NULL;
+ if (asn_length != 0){
+ ERROR("Malformed NULL");
+ return NULL;
+ }
+ *datalength -= (bufp - data);
+ return bufp + asn_length;
+}
+
+
+/*
+ * asn_build_null - Builds an ASN null object.
+ * On entry, datalength is input as the number of valid bytes following
+ * "data". On exit, it is returned as the number of valid bytes
+ * following the beginning of the next object.
+ *
+ * Returns a pointer to the first byte past the end
+ * of this object (i.e. the start of the next object).
+ * Returns NULL on any error.
+ */
+u_char *
+asn_build_null(data, datalength, type)
+ u_char *data; /* IN - pointer to start of object */
+ int *datalength; /* IN/OUT - number of valid bytes left in buffer */
+ u_char type; /* IN - ASN type of object */
+{
+/*
+ * ASN.1 null ::= 0x05 0x00
+ */
+ return asn_build_header(data, datalength, type, 0);
+}
+
+/*
+ * asn_parse_bitstring - pulls a bitstring out of an ASN bitstring type.
+ * On entry, datalength is input as the number of valid bytes following
+ * "data". On exit, it is returned as the number of valid bytes
+ * following the beginning of the next object.
+ *
+ * "string" is filled with the bit string.
+ *
+ * Returns a pointer to the first byte past the end
+ * of this object (i.e. the start of the next object).
+ * Returns NULL on any error.
+ */
+u_char *
+asn_parse_bitstring(data, datalength, type, string, strlength)
+ u_char *data; /* IN - pointer to start of object */
+ int *datalength; /* IN/OUT - number of valid bytes left in buffer */
+ u_char *type; /* OUT - asn type of object */
+ u_char *string; /* IN/OUT - pointer to start of output buffer */
+ int *strlength; /* IN/OUT - size of output buffer */
+{
+/*
+ * bitstring ::= 0x03 asnlength unused {byte}*
+ */
+ u_char *bufp = data;
+ u_long asn_length;
+
+ *type = *bufp++;
+ bufp = asn_parse_length(bufp, &asn_length);
+ if (bufp == NULL)
+ return NULL;
+ if (asn_length + (bufp - data) > *datalength){
+ ERROR("overflow of message");
+ return NULL;
+ }
+ if (asn_length > *strlength){
+ ERROR("I don't support such long bitstrings");
+ return NULL;
+ }
+ if (asn_length < 1){
+ ERROR("Invalid bitstring");
+ return NULL;
+ }
+ if (/** *bufp < 0 || **/ *bufp > 7){
+ ERROR("Invalid bitstring");
+ return NULL;
+ }
+ bcopy((char *)bufp, (char *)string, (int)asn_length);
+ *strlength = (int)asn_length;
+ *datalength -= (int)asn_length + (bufp - data);
+ return bufp + asn_length;
+}
+
+
+/*
+ * asn_build_bitstring - Builds an ASN bit string object containing the
+ * input string.
+ * On entry, datalength is input as the number of valid bytes following
+ * "data". On exit, it is returned as the number of valid bytes
+ * following the beginning of the next object.
+ *
+ * Returns a pointer to the first byte past the end
+ * of this object (i.e. the start of the next object).
+ * Returns NULL on any error.
+ */
+u_char *
+asn_build_bitstring(data, datalength, type, string, strlength)
+ u_char *data; /* IN - pointer to start of object */
+ int *datalength; /* IN/OUT - number of valid bytes left in buffer */
+ u_char type; /* IN - ASN type of string */
+ u_char *string; /* IN - pointer to start of input buffer */
+ int strlength; /* IN - size of input buffer */
+{
+/*
+ * ASN.1 bit string ::= 0x03 asnlength unused {byte}*
+ */
+ if (strlength < 1 || /** *string < 0 || **/ *string > 7){
+ ERROR("Building invalid bitstring");
+ return NULL;
+ }
+ data = asn_build_header(data, datalength, type, strlength);
+ if (data == NULL)
+ return NULL;
+ if (*datalength < strlength)
+ return NULL;
+ bcopy((char *)string, (char *)data, strlength);
+ *datalength -= strlength;
+ return data + strlength;
+}
+
+
+/*
+ * asn_parse_unsigned_int64 - pulls a 64 bit unsigned long out of an ASN int
+ * type.
+ * On entry, datalength is input as the number of valid bytes following
+ * "data". On exit, it is returned as the number of valid bytes
+ * following the end of this object.
+ *
+ * Returns a pointer to the first byte past the end
+ * of this object (i.e. the start of the next object).
+ * Returns NULL on any error.
+ */
+u_char *
+asn_parse_unsigned_int64(data, datalength, type, cp, countersize)
+ u_char *data; /* IN - pointer to start of object */
+ int *datalength;/* IN/OUT - number of valid bytes left in buffer */
+ u_char *type; /* OUT - asn type of object */
+ struct counter64 *cp; /* IN/OUT -pointer to counter struct */
+ int countersize;/* IN - size of output buffer */
+{
+/*
+ * ASN.1 integer ::= 0x02 asnlength byte {byte}*
+ */
+ u_char *bufp = data;
+ u_long asn_length;
+ u_long low = 0, high = 0;
+ int intsize = 4;
+
+ if (countersize != sizeof(struct counter64)){
+ ERROR("not counter64 size");
+ return NULL;
+ }
+ *type = *bufp++;
+ bufp = asn_parse_length(bufp, &asn_length);
+ if (bufp == NULL){
+ ERROR("bad length");
+ return NULL;
+ }
+ if (asn_length + (bufp - data) > *datalength){
+ ERROR("overflow of message");
+ return NULL;
+ }
+ if ((asn_length > (intsize * 2 + 1)) ||
+ ((asn_length == (intsize * 2) + 1) && *bufp != 0x00)){
+ ERROR("I don't support such large integers");
+ return NULL;
+ }
+ *datalength -= (int)asn_length + (bufp - data);
+ if (*bufp & 0x80){
+ low = -1; /* integer is negative */
+ high = -1;
+ }
+ while(asn_length--){
+ high = (high << 8) | ((low & 0xFF000000) >> 24);
+ low = (low << 8) | *bufp++;
+ }
+ cp->low = low;
+ cp->high = high;
+ return bufp;
+}
+
+
+/*
+ * asn_build_unsigned_int64 - builds an ASN object containing a 64 bit integer.
+ * On entry, datalength is input as the number of valid bytes following
+ * "data". On exit, it is returned as the number of valid bytes
+ * following the end of this object.
+ *
+ * Returns a pointer to the first byte past the end
+ * of this object (i.e. the start of the next object).
+ * Returns NULL on any error.
+ */
+u_char *
+asn_build_unsigned_int64(data, datalength, type, cp, countersize)
+ u_char *data; /* IN - pointer to start of output buffer */
+ int *datalength;/* IN/OUT - number of valid bytes left in buffer */
+ u_char type; /* IN - asn type of object */
+ struct counter64 *cp; /* IN - pointer to counter struct */
+ int countersize; /* IN - size of *intp */
+{
+/*
+ * ASN.1 integer ::= 0x02 asnlength byte {byte}*
+ */
+
+ u_int32 low, high;
+ u_int32 mask, mask2;
+ int add_null_byte = 0;
+ int intsize;
+
+ if (countersize != sizeof (struct counter64)) {
+ ERROR("not counter64 size");
+ return NULL;
+ }
+ intsize = 8;
+ low = cp->low;
+ high = cp->high;
+ mask = 0xFF << (8 * (sizeof(int32) - 1));
+ /* mask is 0xFF000000 on a big-endian machine */
+ if ((u_char)((high & mask) >> (8 * (sizeof(int32) - 1))) & 0x80) {
+ /* if MSB is set */
+ add_null_byte = 1;
+ intsize++;
+ }
+ /*
+ * Truncate "unnecessary" bytes off of the most significant end of this 2's
+ * complement integer.
+ * There should be no sequence of 9 consecutive 1's or 0's at the most
+ * significant end of the integer.
+ */
+ mask2 = 0x1FF << ((8 * (sizeof(int32) - 1)) - 1);
+ /* mask2 is 0xFF800000 on a big-endian machine */
+ while((((high & mask2) == 0) || ((high & mask2) == mask2))
+ && intsize > 1){
+ intsize--;
+ high = (high << 8)
+ | ((low & mask) >> (8 * (sizeof(int32) - 1)));
+ low <<= 8;
+ }
+ data = asn_build_header(data, datalength, type, intsize);
+ if (data == NULL)
+ return NULL;
+ if (*datalength < intsize)
+ return NULL;
+ *datalength -= intsize;
+ if (add_null_byte == 1){
+ *data++ = '\0';
+ intsize--;
+ }
+ while(intsize--){
+ *data++ = (u_char)((high & mask) >> (8 * (sizeof(int32) - 1)));
+ high = (high << 8)
+ | ((low & mask) >> (8 * (sizeof(int32) - 1)));
+ low <<= 8;
+
+ }
+ return data;
+}
+
+
--- /dev/null
+/**********************************************************************
+ Copyright 1988, 1989, 1991, 1992 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of CMU not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+******************************************************************/
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+
+#include "squid.h"
+
+#ifdef linux
+#include <stdlib.h>
+#include <string.h>
+#endif
+
+
+#include "asn1.h"
+#include "snmp.h"
+#include "snmp_impl.h"
+#include "snmp_api.h"
+#include "parse.h"
+
+#include "mib.h"
+
+#include "util.h"
+
+/* fwd: */
+static void sprint_by_type();
+char *snmp_new_prefix(char *);
+static void set_functions();
+static int parse_subtree();
+static int lc_cmp();
+static void sprint_variable();
+static struct tree *get_symbol();
+
+static char *
+uptimeString(timeticks, buf)
+ u_long timeticks;
+ char *buf;
+{
+ int seconds, minutes, hours, days;
+
+ timeticks /= 100;
+ days = timeticks / (60 * 60 * 24);
+ timeticks %= (60 * 60 * 24);
+
+ hours = timeticks / (60 * 60);
+ timeticks %= (60 * 60);
+
+ minutes = timeticks / 60;
+ seconds = timeticks % 60;
+
+ if (days == 0){
+ snprintf(buf,32, "%d:%02d:%02d", hours, minutes, seconds);
+ } else if (days == 1) {
+ snprintf(buf,32, "%d day, %d:%02d:%02d", days, hours, minutes, seconds);
+ } else {
+ snprintf(buf,32, "%d days, %d:%02d:%02d", days, hours, minutes, seconds);
+ }
+ return buf;
+}
+
+static void
+sprint_hexstring(buf, cp, len)
+ char *buf;
+ u_char *cp;
+ int len;
+{
+
+ for(; len >= 16; len -= 16){
+ sprintf(buf, "%02X %02X %02X %02X %02X %02X %02X %02X ", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
+ buf += strlen(buf);
+ cp += 8;
+ sprintf(buf, "%02X %02X %02X %02X %02X %02X %02X %02X\n", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
+ buf += strlen(buf);
+ cp += 8;
+ }
+ for(; len > 0; len--){
+ sprintf(buf, "%02X ", *cp++);
+ buf += strlen(buf);
+ }
+ *buf = '\0';
+}
+
+static void
+sprint_asciistring(buf, cp, len)
+ char *buf;
+ u_char *cp;
+ int len;
+{
+ int x;
+
+ for(x = 0; x < len; x++){
+ if (isprint(*cp)){
+ *buf++ = *cp++;
+ } else {
+ *buf++ = '.';
+ cp++;
+ }
+#if 0
+ if ((x % 48) == 47)
+ *buf++ = '\n';
+#endif
+ }
+ *buf = '\0';
+}
+
+#ifdef UNUSED
+int
+read_rawobjid(input, output, out_len)
+ char *input;
+ oid *output;
+ int *out_len;
+{
+ char buf[12], *cp;
+ oid *op = output;
+ u_long subid;
+
+ while(*input != '\0'){
+ if (!isdigit(*input))
+ break;
+ cp = buf;
+ while(isdigit(*input))
+ *cp++ = *input++;
+ *cp = '\0';
+ subid = atoi(buf);
+ if(subid > MAX_SUBID){
+ fprintf(stderr, "sub-identifier too large: %s\n", buf);
+ return 0;
+ }
+ if((*out_len)-- <= 0){
+ fprintf(stderr, "object identifier too long\n");
+ return 0;
+ }
+ *op++ = subid;
+ if(*input++ != '.')
+ break;
+ }
+ *out_len = op - output;
+ if (*out_len == 0)
+ return 0;
+ return 1;
+}
+
+#endif /* UNUSED */
+
+/*
+ 0
+ < 4
+ hex
+
+ 0 ""
+ < 4 hex Hex: oo oo oo
+ < 4 "fgh" Hex: oo oo oo
+ > 4 hex Hex: oo oo oo oo oo oo oo oo
+ > 4 "this is a test"
+
+ */
+static void
+sprint_octet_string(buf, var, enums)
+ char *buf;
+ struct variable_list *var;
+ struct enum_list *enums;
+{
+ int hex, x;
+ u_char *cp;
+
+ if (var->type != ASN_OCTET_STR){
+ sprintf(buf, "Wrong Type (should be OCTET STRING): ");
+ buf += strlen(buf);
+ sprint_by_type(buf, var, (struct enum_list *)NULL);
+ return;
+ }
+ hex = 0;
+ for(cp = var->val.string, x = 0; x < var->val_len; x++, cp++){
+ if (!(isprint(*cp) || isspace(*cp)))
+ hex = 1;
+ }
+ if (var->val_len == 0){
+ strcpy(buf, "\"\"");
+ return;
+ }
+ if (!hex){
+ *buf++ = '"';
+ sprint_asciistring(buf, var->val.string, var->val_len);
+ buf += strlen(buf);
+ *buf++ = '"';
+ *buf = '\0';
+ }
+ if (hex || var->val_len <= 4){
+ sprintf(buf, " Hex: ");
+ buf += strlen(buf);
+ sprint_hexstring(buf, var->val.string, var->val_len);
+ }
+}
+
+static void
+sprint_opaque(buf, var, enums)
+ char *buf;
+ struct variable_list *var;
+ struct enum_list *enums;
+{
+
+ if (var->type != OPAQUE){
+ sprintf(buf, "Wrong Type (should be Opaque): ");
+ buf += strlen(buf);
+ sprint_by_type(buf, var, (struct enum_list *)NULL);
+ return;
+ }
+ sprintf(buf, "OPAQUE: ");
+ buf += strlen(buf);
+ sprint_hexstring(buf, var->val.string, var->val_len);
+}
+
+static void
+sprint_object_identifier(buf, var, enums)
+ char *buf;
+ struct variable_list *var;
+ struct enum_list *enums;
+{
+ if (var->type != ASN_OBJECT_ID){
+ sprintf(buf, "Wrong Type (should be OBJECT IDENTIFIER): ");
+ buf += strlen(buf);
+ sprint_by_type(buf, var, (struct enum_list *)NULL);
+ return;
+ }
+ sprintf(buf, "OID: ");
+ buf += strlen(buf);
+ sprint_objid(buf, (oid *)(var->val.objid), var->val_len / sizeof(oid));
+}
+
+static void
+sprint_timeticks(buf, var, enums)
+ char *buf;
+ struct variable_list *var;
+ struct enum_list *enums;
+{
+ char timebuf[32];
+
+ if (var->type != TIMETICKS){
+ sprintf(buf, "Wrong Type (should be Timeticks): ");
+ buf += strlen(buf);
+ sprint_by_type(buf, var, (struct enum_list *)NULL);
+ return;
+ }
+ sprintf(buf, "Timeticks: (%ld) %s", *(u_long *)(var->val.integer),
+ uptimeString(*(u_long *)(var->val.integer), timebuf));
+}
+
+static void
+sprint_integer(buf, var, enums)
+ char *buf;
+ struct variable_list *var;
+ struct enum_list *enums;
+{
+ char *enum_string = NULL;
+
+ if (var->type != ASN_INTEGER){
+ sprintf(buf, "Wrong Type (should be INTEGER): ");
+ buf += strlen(buf);
+ sprint_by_type(buf, var, (struct enum_list *)NULL);
+ return;
+ }
+ for (; enums; enums = enums->next)
+ if (enums->value == *var->val.integer){
+ enum_string = enums->label;
+ break;
+ }
+ if (enum_string == NULL)
+ sprintf(buf, "%ld", *var->val.integer);
+ else
+ sprintf(buf, "%s(%ld)", enum_string, *var->val.integer);
+}
+
+static void
+sprint_uinteger(buf, var, enums)
+ char *buf;
+ struct variable_list *var;
+ struct enum_list *enums;
+{
+ char *enum_string = NULL;
+
+ if (var->type != UINTEGER){
+ sprintf(buf, "Wrong Type (should be UInteger32): ");
+ buf += strlen(buf);
+ sprint_by_type(buf, var, (struct enum_list *)NULL);
+ return;
+ }
+ for (; enums; enums = enums->next)
+ if (enums->value == *var->val.integer){
+ enum_string = enums->label;
+ break;
+ }
+ if (enum_string == NULL)
+ sprintf(buf, "%ld", *var->val.integer);
+ else
+ sprintf(buf, "%s(%ld)", enum_string, *var->val.integer);
+}
+
+static void
+sprint_gauge(buf, var, enums)
+ char *buf;
+ struct variable_list *var;
+ struct enum_list *enums;
+{
+ if (var->type != GAUGE){
+ sprintf(buf, "Wrong Type (should be Gauge): ");
+ buf += strlen(buf);
+ sprint_by_type(buf, var, (struct enum_list *)NULL);
+ return;
+ }
+ sprintf(buf, "Gauge: %lu", *var->val.integer);
+}
+
+static void
+sprint_counter(buf, var, enums)
+ char *buf;
+ struct variable_list *var;
+ struct enum_list *enums;
+{
+ if (var->type != COUNTER){
+ sprintf(buf, "Wrong Type (should be Counter): ");
+ buf += strlen(buf);
+ sprint_by_type(buf, var, (struct enum_list *)NULL);
+ return;
+ }
+ sprintf(buf, "%lu", *var->val.integer);
+}
+
+static void
+sprint_networkaddress(buf, var, enums)
+ char *buf;
+ struct variable_list *var;
+ struct enum_list *enums;
+{
+ int x, len;
+ u_char *cp;
+
+ sprintf(buf, "Network Address: ");
+ buf += strlen(buf);
+ cp = var->val.string;
+ len = var->val_len;
+ for(x = 0; x < len; x++){
+ sprintf(buf, "%02X", *cp++);
+ buf += strlen(buf);
+ if (x < (len - 1))
+ *buf++ = ':';
+ }
+}
+
+static void
+sprint_ipaddress(buf, var, enums)
+ char *buf;
+ struct variable_list *var;
+ struct enum_list *enums;
+{
+ u_char *ip;
+
+ if (var->type != IPADDRESS){
+ sprintf(buf, "Wrong Type (should be Ipaddress): ");
+ buf += strlen(buf);
+ sprint_by_type(buf, var, (struct enum_list *)NULL);
+ return;
+ }
+ ip = var->val.string;
+ sprintf(buf, "IpAddress: %d.%d.%d.%d",ip[0], ip[1], ip[2], ip[3]);
+}
+
+#if 0
+static void
+sprint_unsigned_short(buf, var, enums)
+ char *buf;
+ struct variable_list *var;
+ struct enum_list *enums;
+{
+ if (var->type != ASN_INTEGER){
+ sprintf(buf, "Wrong Type (should be INTEGER): ");
+ buf += strlen(buf);
+ sprint_by_type(buf, var, (struct enum_list *)NULL);
+ return;
+ }
+ sprintf(buf, "INT: %lu", *var->val.integer);
+}
+#endif
+
+static void
+sprint_null(buf, var, enums)
+ char *buf;
+ struct variable_list *var;
+ struct enum_list *enums;
+{
+ if (var->type != ASN_NULL){
+ sprintf(buf, "Wrong Type (should be NULL): ");
+ buf += strlen(buf);
+ sprint_by_type(buf, var, (struct enum_list *)NULL);
+ return;
+ }
+ sprintf(buf, "NULL");
+}
+
+static void
+sprint_bitstring(buf, var, enums)
+ char *buf;
+ struct variable_list *var;
+ struct enum_list *enums;
+{
+ int len, bit;
+ u_char *cp;
+ char *enum_string;
+
+ if (var->type != ASN_BIT_STR){
+ sprintf(buf, "Wrong Type (should be BIT STRING): ");
+ buf += strlen(buf);
+ sprint_by_type(buf, var, (struct enum_list *)NULL);
+ return;
+ }
+ sprintf(buf, "BIT_STRING: ");
+ buf += strlen(buf);
+ sprint_hexstring(buf, var->val.bitstring, var->val_len);
+ buf += strlen(buf);
+
+ cp = var->val.bitstring + 1;
+ for(len = 0; len < var->val_len - 1; len++){
+ for(bit = 0; bit < 8; bit++){
+ if (*cp & (0x80 >> bit)){
+ enum_string = NULL;
+ for (; enums; enums = enums->next)
+ if (enums->value == (len * 8) + bit){
+ enum_string = enums->label;
+ break;
+ }
+ if (enum_string == NULL)
+ sprintf(buf, "%d ", (len * 8) + bit);
+ else
+ sprintf(buf, "%s(%d) ", enum_string, (len * 8) + bit);
+ buf += strlen(buf);
+ }
+ }
+ cp ++;
+ }
+}
+
+static void
+sprint_nsapaddress(buf, var, enums)
+ char *buf;
+ struct variable_list *var;
+ struct enum_list *enums;
+{
+ if (var->type != NSAP){
+ sprintf(buf, "Wrong Type (should be NsapAddress): ");
+ buf += strlen(buf);
+ sprint_by_type(buf, var, (struct enum_list *)NULL);
+ return;
+ }
+ sprintf(buf, "NsapAddress: ");
+ buf += strlen(buf);
+ sprint_hexstring(buf, var->val.string, var->val_len);
+}
+
+static void
+sprint_counter64(buf, var, enums)
+ char *buf;
+ struct variable_list *var;
+ struct enum_list *enums;
+{
+ if (var->type != COUNTER64){
+ sprintf(buf, "Wrong Type (should be Counter64): ");
+ buf += strlen(buf);
+ sprint_by_type(buf, var, (struct enum_list *)NULL);
+ return;
+ }
+/* XXX */
+ sprintf(buf, "Counter64: ");
+ buf += strlen(buf);
+
+ sprint_hexstring(buf, &var->val.counter64->high,
+ sizeof(var->val.counter64->high));
+ buf += strlen(buf);
+ sprint_hexstring(buf, &var->val.counter64->low,
+ sizeof(var->val.counter64->low));
+}
+
+
+static void
+sprint_unknowntype(buf, var, enums)
+ char *buf;
+ struct variable_list *var;
+ struct enum_list *enums;
+{
+/* sprintf(buf, "Variable has bad type"); */
+ sprint_by_type(buf, var, NULL);
+}
+
+static void
+sprint_badtype(buf, var, enums)
+ char *buf;
+ struct variable_list *var;
+ struct enum_list *enums;
+{
+ sprintf(buf, "Variable has bad type");
+}
+
+static void
+sprint_by_type(buf, var, enums)
+ char *buf;
+ struct variable_list *var;
+ struct enum_list *enums;
+{
+ switch (var->type){
+ case ASN_INTEGER:
+ sprint_integer(buf, var, enums);
+ break;
+ case ASN_OCTET_STR:
+ sprint_octet_string(buf, var, enums);
+ break;
+ case OPAQUE:
+ sprint_opaque(buf, var, enums);
+ break;
+ case ASN_OBJECT_ID:
+ sprint_object_identifier(buf, var, enums);
+ break;
+ case TIMETICKS:
+ sprint_timeticks(buf, var, enums);
+ break;
+ case GAUGE:
+ sprint_gauge(buf, var, enums);
+ break;
+ case COUNTER:
+ sprint_counter(buf, var, enums);
+ break;
+ case IPADDRESS:
+ sprint_ipaddress(buf, var, enums);
+ break;
+ case ASN_NULL:
+ sprint_null(buf, var, enums);
+ break;
+ case UINTEGER:
+ sprint_uinteger(buf, var, enums);
+ break;
+ default:
+ sprint_badtype(buf, var, enums);
+ break;
+ }
+}
+
+struct tree *get_symbol();
+
+oid RFC1213_MIB[] = { 1, 3, 6, 1, 2, 1 };
+unsigned char RFC1213_MIB_text[] = ".iso.org.dod.internet.mgmt.mib-2";
+unsigned char EXPERIMENTAL_MIB_text[] = ".iso.org.dod.internet.experimental";
+unsigned char PRIVATE_MIB_text[] = ".iso.org.dod.internet.private";
+unsigned char PARTY_MIB_text[] = ".iso.org.dod.internet.snmpParties";
+unsigned char SECRETS_MIB_text[] = ".iso.org.dod.internet.snmpSecrets";
+struct tree *Mib = 0;
+
+static char Standard_Prefix[] = ".1.3.6.1.2.1.";
+static char Prefix[256];
+static int Suffix;
+
+void
+init_mib()
+{
+ char *file, *getenv(), *prefix;
+
+ Mib = 0;
+ file = Config.Snmp.mibPath;
+ if (file)
+ Mib = read_mib(file);
+ if (!Mib)
+ Mib = read_mib("mib.txt");
+#ifdef MIBFILEPATH
+ if (!Mib)
+ {
+ char tmp [1024];
+ sprintf (tmp, "%s/mib.txt", MIBFILEPATH);
+ Mib = read_mib(tmp);
+ }
+#endif
+ if (!Mib)
+ Mib = read_mib("/etc/mib.txt");
+ if (!Mib){
+ fprintf(stderr, "Couldn't find mib file\n");
+ exit(2);
+ }
+ prefix = getenv("PREFIX");
+ if (! prefix) {
+ prefix = Standard_Prefix;
+ }
+
+ /* save prefix: */
+ snmp_new_prefix (prefix);
+
+ if (getenv("SUFFIX"))
+ Suffix = TRUE;
+ else
+ Suffix = FALSE;
+ set_functions(Mib);
+}
+
+
+/*
+ * Phil Wood <cpw@lanl.gov>:
+ *
+ * [...] I made an addition to mib.c to accomodate some old perl snmp
+ * code for a program called vulture that used a global pointer to the
+ * prefix to change things.
+ */
+
+char *
+snmp_new_prefix (char *prefix)
+{
+ char *lastchar;
+ int plen;
+
+ if (prefix) {
+ lastchar = ".";
+ if (*prefix == '.') { prefix++; }
+ if ((plen = strlen (prefix))) {
+ lastchar = prefix + plen - 1;
+ }
+ strncpy (Prefix, prefix, sizeof (Prefix) - 2);
+ Prefix [sizeof (Prefix) - 2] = 0;
+ if (*lastchar != '.') {
+ Prefix [plen++] = '.';
+ Prefix [plen] = 0;
+ }
+ return Prefix;
+ }
+ return (char *)NULL;
+}
+
+
+
+static void
+set_functions(subtree)
+ struct tree *subtree;
+{
+ for(; subtree; subtree = subtree->next_peer){
+ switch(subtree->type){
+ case TYPE_OBJID:
+ subtree->printer = sprint_object_identifier;
+ break;
+ case TYPE_OCTETSTR:
+ subtree->printer = sprint_octet_string;
+ break;
+ case TYPE_INTEGER:
+ subtree->printer = sprint_integer;
+ break;
+ case TYPE_NETADDR:
+ subtree->printer = sprint_networkaddress;
+ break;
+ case TYPE_IPADDR:
+ subtree->printer = sprint_ipaddress;
+ break;
+ case TYPE_COUNTER:
+ subtree->printer = sprint_counter;
+ break;
+ case TYPE_GAUGE:
+ subtree->printer = sprint_gauge;
+ break;
+ case TYPE_TIMETICKS:
+ subtree->printer = sprint_timeticks;
+ break;
+ case TYPE_OPAQUE:
+ subtree->printer = sprint_opaque;
+ break;
+ case TYPE_NULL:
+ subtree->printer = sprint_null;
+ break;
+ case TYPE_BITSTRING:
+ subtree->printer = sprint_bitstring;
+ break;
+ case TYPE_NSAPADDRESS:
+ subtree->printer = sprint_nsapaddress;
+ break;
+ case TYPE_COUNTER64:
+ subtree->printer = sprint_counter64;
+ break;
+ case TYPE_UINTEGER:
+ subtree->printer = sprint_uinteger;
+ break;
+ case TYPE_OTHER:
+ default:
+ subtree->printer = sprint_unknowntype;
+ break;
+ }
+ set_functions(subtree->child_list);
+ }
+}
+
+#ifdef testing
+int snmp_dump_packet = 0;
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ oid objid[64];
+ int objidlen = sizeof (objid);
+ int count;
+ struct variable variable;
+
+ init_mib();
+ if (argc < 2)
+ print_subtree(Mib, 0);
+ variable.type = ASN_INTEGER;
+ variable.val.integer = 3;
+ variable.val_len = 4;
+ for (argc--; argc; argc--, argv++) {
+ objidlen = sizeof (objid);
+ printf("read_objid(%s) = %d\n",
+ argv[1], read_objid(argv[1], objid, &objidlen));
+ for(count = 0; count < objidlen; count++)
+ printf("%d.", objid[count]);
+ printf("\n");
+ print_variable(objid, objidlen, &variable);
+ }
+}
+
+#endif testing
+
+
+#if 0
+static struct tree *
+find_rfc1213_mib(root)
+ struct tree *root;
+{
+ oid *op = RFC1213_MIB;
+ struct tree *tp;
+ int len;
+
+ for(len = sizeof(RFC1213_MIB)/sizeof(oid); len; len--, op++){
+ for(tp = root; tp; tp = tp->next_peer){
+ if (tp->subid == *op){
+ root = tp->child_list;
+ break;
+ }
+ }
+ if (tp == NULL)
+ return NULL;
+ }
+ return root;
+}
+#endif
+
+int read_objid(input, output, out_len)
+ char *input;
+ oid *output;
+ int *out_len; /* number of subid's in "output" */
+{
+ struct tree *root = Mib;
+ oid *op = output;
+ char buf[512];
+
+ bzero (buf, sizeof(buf));
+
+ if (*input == '.')
+ input++;
+ else {
+ strcpy(buf, Prefix);
+ strcat(buf, input);
+ input = buf;
+ }
+
+ if (root == NULL){
+ fprintf(stderr, "Mib not initialized. Exiting.\n");
+ exit(1);
+ }
+ if ((*out_len =
+ parse_subtree(root, input, output, out_len)) == 0)
+ return (0);
+ *out_len += output - op;
+
+ return (1);
+}
+
+#ifdef notdef
+int read_objid(input, output, out_len)
+ char *input;
+ oid *output;
+ int *out_len; /* number of subid's in "output" */
+{
+ struct tree *root = Mib;
+ oid *op = output;
+ int i;
+
+ if (*input == '.')
+ input++;
+ else {
+ root = find_rfc1213_mib(root);
+ for (i = 0; i < sizeof (RFC1213_MIB)/sizeof(oid); i++) {
+ if ((*out_len)-- > 0)
+ *output++ = RFC1213_MIB[i];
+ else {
+ fprintf(stderr, "object identifier too long\n");
+ return (0);
+ }
+ }
+ }
+
+ if (root == NULL){
+ fprintf(stderr, "Mib not initialized. Exiting.\n");
+ exit(1);
+ }
+ if ((*out_len =
+ parse_subtree(root, input, output, out_len)) == 0)
+ return (0);
+ *out_len += output - op;
+
+ return (1);
+}
+#endif
+
+static int
+parse_subtree(subtree, input, output, out_len)
+ struct tree *subtree;
+ char *input;
+ oid *output;
+ int *out_len; /* number of subid's */
+{
+ char buf[128], *to = buf;
+ u_long subid = 0;
+ struct tree *tp;
+
+ /*
+ * No empty strings. Can happen if there is a trailing '.' or two '.'s
+ * in a row, i.e. "..".
+ */
+ if ((*input == '\0') ||
+ (*input == '.'))
+ return (0);
+
+ if (isdigit(*input)) {
+ /*
+ * Read the number, then try to find it in the subtree.
+ */
+ while (isdigit(*input)) {
+ subid *= 10;
+ subid += *input++ - '0';
+ }
+ for (tp = subtree; tp; tp = tp->next_peer) {
+ if (tp->subid == subid)
+ goto found;
+ }
+ tp = NULL;
+ }
+ else {
+ /*
+ * Read the name into a buffer.
+ */
+ while ((*input != '\0') &&
+ (*input != '.')) {
+ *to++ = *input++;
+ }
+ *to = '\0';
+
+ /*
+ * Find the name in the subtree;
+ */
+ for (tp = subtree; tp; tp = tp->next_peer) {
+ if (lc_cmp(tp->label, buf) == 0) {
+ subid = tp->subid;
+ goto found;
+ }
+ }
+
+ /*
+ * If we didn't find the entry, punt...
+ */
+ if (tp == NULL) {
+ fprintf(stderr, "sub-identifier not found: %s\n", buf);
+ return (0);
+ }
+ }
+
+found:
+ if(subid > (u_long)MAX_SUBID){
+ fprintf(stderr, "sub-identifier too large: %s\n", buf);
+ return (0);
+ }
+
+ if ((*out_len)-- <= 0){
+ fprintf(stderr, "object identifier too long\n");
+ return (0);
+ }
+ *output++ = subid;
+
+ if (*input != '.')
+ return (1);
+ if ((*out_len =
+ parse_subtree(tp ? tp->child_list : NULL, ++input, output, out_len)) == 0)
+ return (0);
+ return (++*out_len);
+}
+
+void
+sprint_objid(buf, objid, objidlen)
+ char *buf;
+ oid *objid;
+ int objidlen; /* number of subidentifiers */
+{
+ char tempbuf[2048], *cp;
+ struct tree *subtree = Mib;
+
+ *tempbuf = '.'; /* this is a fully qualified name */
+ get_symbol(objid, objidlen, subtree, tempbuf + 1);
+ if (Suffix){
+ for(cp =tempbuf; *cp; cp++)
+ ;
+ while(cp >= tempbuf){
+ if (isalpha(*cp))
+ break;
+ cp--;
+ }
+ while(cp >= tempbuf){
+ if (*cp == '.')
+ break;
+ cp--;
+ }
+ cp++;
+ if (cp < tempbuf)
+ cp = tempbuf;
+
+ } else {
+ cp = tempbuf;
+ if ((strlen(tempbuf) > strlen((char *)RFC1213_MIB_text))
+ && !bcmp(tempbuf, (char *)RFC1213_MIB_text,
+ strlen((char *)RFC1213_MIB_text))){
+ cp += sizeof(RFC1213_MIB_text);
+ }
+ if ((strlen(tempbuf) > strlen((char *)EXPERIMENTAL_MIB_text))
+ && !bcmp(tempbuf, (char *) EXPERIMENTAL_MIB_text,
+ strlen((char *)EXPERIMENTAL_MIB_text))){
+ cp += sizeof(EXPERIMENTAL_MIB_text);
+ }
+ if ((strlen(tempbuf) > strlen((char *)PRIVATE_MIB_text))
+ && !bcmp(tempbuf, (char *) PRIVATE_MIB_text,
+ strlen((char *)PRIVATE_MIB_text))){
+ cp += sizeof(PRIVATE_MIB_text);
+ }
+ if ((strlen(tempbuf) > strlen((char *)PARTY_MIB_text))
+ && !bcmp(tempbuf, (char *) PARTY_MIB_text,
+ strlen((char *)PARTY_MIB_text))){
+ cp += sizeof(PARTY_MIB_text);
+ }
+ if ((strlen(tempbuf) > strlen((char *)SECRETS_MIB_text))
+ && !bcmp(tempbuf, (char *) SECRETS_MIB_text,
+ strlen((char *)SECRETS_MIB_text))){
+ cp += sizeof(SECRETS_MIB_text);
+ }
+ }
+ strcpy(buf, cp);
+}
+
+
+void
+print_objid(objid, objidlen)
+ oid *objid;
+ int objidlen; /* number of subidentifiers */
+{
+ char buf[256];
+
+ sprint_objid(buf, objid, objidlen);
+ printf("%s\n", buf);
+}
+
+
+void
+print_variable(objid, objidlen, variable)
+ oid *objid;
+ int objidlen;
+ struct variable_list *variable;
+{
+ char buf[2048];
+
+ sprint_variable(buf, objid, objidlen, variable);
+ printf("%s", buf);
+}
+
+static void
+sprint_variable(buf, objid, objidlen, variable)
+ char *buf;
+ oid *objid;
+ int objidlen;
+ struct variable_list *variable;
+{
+ char tempbuf[2048];
+ struct tree *subtree = Mib;
+
+ sprint_objid(buf, objid, objidlen);
+ buf += strlen(buf);
+ strcat(buf, " = ");
+ buf += strlen(buf);
+
+ if (variable->type == SNMP_NOSUCHOBJECT)
+ sprintf(buf, "No Such Object available on this agent\n");
+ else if (variable->type == SNMP_NOSUCHINSTANCE)
+ sprintf(buf, "No Such Instance currently exists\n");
+ else if (variable->type == SNMP_ENDOFMIBVIEW)
+ sprintf(buf, "No more variables left in this MIB View\n");
+ else {
+ *tempbuf = '.'; /* this is a fully qualified name */
+ subtree = get_symbol(objid, objidlen, subtree, tempbuf + 1);
+ buf += strlen(buf);
+ if (subtree->printer)
+ (*subtree->printer)(buf, variable, subtree->enums);
+ else {
+ sprint_by_type(buf, variable, subtree->enums);
+ }
+ strcat(buf, "\n");
+ }
+}
+
+void
+sprint_value(buf, objid, objidlen, variable)
+ char *buf;
+ oid *objid;
+ int objidlen;
+ struct variable_list *variable;
+{
+ char tempbuf[2048];
+ struct tree *subtree = Mib;
+
+ if (variable->type == SNMP_NOSUCHOBJECT)
+ sprintf(buf, "No Such Object available on this agent\n");
+ else if (variable->type == SNMP_NOSUCHINSTANCE)
+ sprintf(buf, "No Such Instance currently exists\n");
+ else if (variable->type == SNMP_ENDOFMIBVIEW)
+ sprintf(buf, "No more variables left in this MIB View\n");
+ else {
+ subtree = get_symbol(objid, objidlen, subtree, tempbuf);
+ if (subtree->printer)
+ (*subtree->printer)(buf, variable, subtree->enums);
+ else {
+ sprint_by_type(buf, variable, subtree->enums);
+ }
+ }
+}
+
+void
+print_value(objid, objidlen, variable)
+ oid *objid;
+ int objidlen;
+ struct variable_list *variable;
+{
+ char tempbuf[2048];
+
+ sprint_value(tempbuf, objid, objidlen, variable);
+ printf("%s\n", tempbuf);
+}
+
+static struct tree *
+get_symbol(objid, objidlen, subtree, buf)
+ oid *objid;
+ int objidlen;
+ struct tree *subtree;
+ char *buf;
+{
+ struct tree *return_tree = NULL;
+
+ for(; subtree; subtree = subtree->next_peer){
+ if (*objid == subtree->subid){
+ strcpy(buf, subtree->label);
+ goto found;
+ }
+ }
+
+ /* subtree not found */
+ while(objidlen--){ /* output rest of name, uninterpreted */
+ sprintf(buf, "%lu.", *objid++);
+ while(*buf)
+ buf++;
+ }
+ *(buf - 1) = '\0'; /* remove trailing dot */
+ return NULL;
+
+found:
+ if (objidlen > 1){
+ while(*buf)
+ buf++;
+ *buf++ = '.';
+ *buf = '\0';
+ return_tree = get_symbol(objid + 1, objidlen - 1, subtree->child_list,
+ buf);
+ }
+ if (return_tree != NULL)
+ return return_tree;
+ else
+ return subtree;
+}
+
+
+static int
+lc_cmp(s1, s2)
+ char *s1, *s2;
+{
+ char c1, c2;
+
+ while(*s1 && *s2){
+ if (isupper(*s1))
+ c1 = tolower(*s1);
+ else
+ c1 = *s1;
+ if (isupper(*s2))
+ c2 = tolower(*s2);
+ else
+ c2 = *s2;
+ if (c1 != c2)
+ return ((c1 - c2) > 0 ? 1 : -1);
+ s1++;
+ s2++;
+ }
+
+ if (*s1)
+ return -1;
+ if (*s2)
+ return 1;
+ return 0;
+}
+
+/*
+ * Clone of get_symbol that doesn't take a buffer argument
+ */
+static struct tree *
+get_tree(objid, objidlen, subtree)
+ oid *objid;
+ int objidlen;
+ struct tree *subtree;
+{
+ struct tree *return_tree = NULL;
+
+ for(; subtree; subtree = subtree->next_peer){
+ if (*objid == subtree->subid)
+ goto found;
+ }
+
+ return NULL;
+
+found:
+ if (objidlen > 1)
+ return_tree = get_tree(objid + 1, objidlen - 1, subtree->child_list);
+ if (return_tree != NULL)
+ return return_tree;
+ else
+ return subtree;
+}
+
+
+#if 0
+static char *
+get_description(objid, objidlen)
+ oid *objid;
+ int objidlen; /* number of subidentifiers */
+{
+ struct tree *subtree = Mib;
+
+ subtree = get_tree(objid, objidlen, subtree);
+ if (subtree)
+ return (subtree->description);
+ else
+ return NULL;
+}
+#endif
+
+
+#if 0
+static void
+print_description(objid, objidlen)
+ oid *objid;
+ int objidlen; /* number of subidentifiers */
+{
+ char *desc = get_description(objid, objidlen);
+
+ if (desc && desc[0] != '\0')
+ printf("Description: \"%s\"\n", desc);
+ else
+ printf("No description\n");
+}
+#endif
+
+
+static struct tree *
+find_node(name, subtree)
+ char *name;
+ struct tree *subtree;
+{
+ struct tree *tp, *ret;
+
+ for(tp = subtree; tp; tp = tp->next_peer){
+ if (!strcasecmp(name, tp->label))
+ return tp;
+ ret = find_node(name, tp->child_list);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+
+#if 0
+static int
+get_node(name, objid, objidlen)
+ char *name;
+ oid *objid;
+ int *objidlen;
+{
+ struct tree *tp;
+ oid newname[64], *op;
+
+ tp = find_node(name, Mib);
+ if (tp){
+ for(op = newname + 63; op >= newname; op--){
+ *op = tp->subid;
+ tp = tp->parent;
+ if (tp == NULL)
+ break;
+ }
+ if (newname + 64 - op > *objidlen)
+ return 0;
+ *objidlen = newname + 64 - op;
+ bcopy(op, objid, (newname + 64 - op) * sizeof(oid));
+ return 1;
+ } else {
+ return 0;
+ }
+
+
+}
+#endif
--- /dev/null
+/******************************************************************
+ Copyright 1989, 1991, 1992 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of CMU not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+******************************************************************/
+/*
+ * parse.c
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#ifdef linux
+#include <stdlib.h>
+#include <string.h>
+#endif
+#include <malloc.h>
+
+
+#include "parse.h"
+
+/* A quoted string value-- too long for a general "token" */
+char *quoted_string_buffer;
+
+/*
+ * This is one element of an object identifier with either an integer
+ * subidentifier, or a textual string label, or both.
+ * The subid is -1 if not present, and label is NULL if not present.
+ */
+struct subid {
+ int subid;
+ char *label;
+};
+
+/* use large token buffer in case of very long tokens: */
+#define MAXTC 1024
+struct tc { /* textual conventions */
+ int type;
+ char descriptor[MAXTOKEN];
+ struct enum_list *enums;
+} tclist[MAXTC];
+
+
+
+int Line = 1;
+
+#define SYNTAX_MASK 0x80
+/* types of tokens
+ Tokens wiht the SYNTAX_MASK bit set are syntax tokens */
+#define CONTINUE -1
+#define ENDOFFILE 0
+#define LABEL 1
+#define SUBTREE 2
+#define SYNTAX 3
+#define OBJID (4 | SYNTAX_MASK)
+#define OCTETSTR (5 | SYNTAX_MASK)
+#define INTEGER (6 | SYNTAX_MASK)
+#define NETADDR (7 | SYNTAX_MASK)
+#define IPADDR (8 | SYNTAX_MASK)
+#define COUNTER (9 | SYNTAX_MASK)
+#define GAUGE (10 | SYNTAX_MASK)
+#define TIMETICKS (11 | SYNTAX_MASK)
+#define OPAQUE (12 | SYNTAX_MASK)
+#define NUL (13 | SYNTAX_MASK)
+#define SEQUENCE 14
+#define OF 15 /* SEQUENCE OF */
+#define OBJTYPE 16
+#define ACCESS 17
+#define READONLY 18
+#define READWRITE 19
+#define WRITEONLY 20
+#define NOACCESS 21
+#define STATUS 22
+#define MANDATORY 23
+#define OPTIONAL 24
+#define OBSOLETE 25
+/* #define RECOMMENDED 26 */
+#define PUNCT 27
+#define EQUALS 28
+#define NUMBER 29
+#define LEFTBRACKET 30
+#define RIGHTBRACKET 31
+#define LEFTPAREN 32
+#define RIGHTPAREN 33
+#define COMMA 34
+#define DESCRIPTION 35
+#define QUOTESTRING 36
+#define INDEX 37
+#define DEFVAL 38
+#define DEPRECATED 39
+#define SIZE 40
+#define BITSTRING (41 | SYNTAX_MASK)
+#define NSAPADDRESS (42 | SYNTAX_MASK)
+#define COUNTER64 (43 | SYNTAX_MASK)
+#define OBJGROUP 44
+#define NOTIFTYPE 45
+#define AUGMENTS 46
+#define COMPLIANCE 47
+#define READCREATE 48
+#define UNITS 49
+#define REFERENCE 50
+#define NUM_ENTRIES 51
+#define MODULEIDENTITY 52
+#define LASTUPDATED 53
+#define ORGANIZATION 54
+#define CONTACTINFO 55
+#define UINTEGER32 (56 | SYNTAX_MASK)
+#define CURRENT 57
+#define DEFINITIONS 58
+#define END 59
+#define SEMI 60
+
+struct tok {
+ char *name; /* token name */
+ int len; /* length not counting nul */
+ int token; /* value */
+ int hash; /* hash of name */
+ struct tok *next; /* pointer to next in hash table */
+};
+
+
+struct tok tokens[] = {
+ { "obsolete", sizeof ("obsolete")-1, OBSOLETE },
+ { "Opaque", sizeof ("Opaque")-1, OPAQUE },
+/* { "recommended", sizeof("recommended")-1, RECOMMENDED }, */
+ { "optional", sizeof ("optional")-1, OPTIONAL },
+ { "LAST-UPDATED", sizeof ("LAST-UPDATED")-1, LASTUPDATED },
+ { "ORGANIZATION", sizeof ("ORGANIZATION")-1, ORGANIZATION },
+ { "CONTACT-INFO", sizeof ("CONTACT-INFO")-1, CONTACTINFO },
+ { "MODULE-IDENTITY", sizeof ("MODULE-IDENTITY")-1, MODULEIDENTITY },
+ { "MODULE-COMPLIANCE", sizeof ("MODULE-COMPLIANCE")-1, COMPLIANCE },
+ { "DEFINITIONS", sizeof("DEFINITIONS")-1, DEFINITIONS},
+ { "END", sizeof("END")-1, END},
+ { ";", sizeof(";")-1, SEMI},
+ { "AUGMENTS", sizeof ("AUGMENTS")-1, AUGMENTS },
+ { "not-accessible", sizeof ("not-accessible")-1, NOACCESS },
+ { "write-only", sizeof ("write-only")-1, WRITEONLY },
+ { "NsapAddress", sizeof("NsapAddress")-1, NSAPADDRESS},
+ { "UNITS", sizeof("Units")-1, UNITS},
+ { "REFERENCE", sizeof("REFERENCE")-1, REFERENCE},
+ { "NUM-ENTRIES", sizeof("NUM-ENTRIES")-1, NUM_ENTRIES},
+ { "BITSTRING", sizeof("BitString")-1, BITSTRING},
+ { "BIT", sizeof("BIT")-1, CONTINUE},
+ { "Counter64", sizeof("Counter64")-1, COUNTER64},
+ { "TimeTicks", sizeof ("TimeTicks")-1, TIMETICKS },
+ { "NOTIFICATION-TYPE", sizeof ("NOTIFICATION-TYPE")-1, NOTIFTYPE },
+ { "OBJECT-GROUP", sizeof ("OBJECT-GROUP")-1, OBJGROUP },
+ { "OBJECTIDENTIFIER", sizeof ("OBJECTIDENTIFIER")-1, OBJID },
+ /*
+ * This CONTINUE appends the next word onto OBJECT,
+ * hopefully matching OBJECTIDENTIFIER above.
+ */
+ { "OBJECT", sizeof ("OBJECT")-1, CONTINUE },
+ { "NetworkAddress", sizeof ("NetworkAddress")-1, NETADDR },
+ { "Gauge", sizeof ("Gauge")-1, GAUGE },
+ { "read-write", sizeof ("read-write")-1, READWRITE },
+ { "read-create", sizeof ("read-create")-1, READCREATE },
+ { "OCTETSTRING", sizeof ("OCTETSTRING")-1, OCTETSTR },
+ { "OCTET", sizeof ("OCTET")-1, -1 },
+ { "OF", sizeof ("OF")-1, OF },
+ { "SEQUENCE", sizeof ("SEQUENCE")-1, SEQUENCE },
+ { "NULL", sizeof ("NULL")-1, NUL },
+ { "IpAddress", sizeof ("IpAddress")-1, IPADDR },
+ { "UInteger32", sizeof ("UInteger32")-1, UINTEGER32 },
+ { "INTEGER", sizeof ("INTEGER")-1, INTEGER },
+ { "Counter", sizeof ("Counter")-1, COUNTER },
+ { "read-only", sizeof ("read-only")-1, READONLY },
+ { "DESCRIPTION", sizeof ("DESCRIPTION")-1, DESCRIPTION },
+ { "INDEX", sizeof ("INDEX")-1, INDEX },
+ { "DEFVAL", sizeof ("DEFVAL")-1, DEFVAL },
+ { "deprecated", sizeof ("deprecated")-1, DEPRECATED },
+ { "SIZE", sizeof ("SIZE")-1, SIZE },
+ { "MAX-ACCESS", sizeof ("MAX-ACCESS")-1, ACCESS },
+ { "ACCESS", sizeof ("ACCESS")-1, ACCESS },
+ { "mandatory", sizeof ("mandatory")-1, MANDATORY },
+ { "current", sizeof ("current")-1, CURRENT },
+ { "STATUS", sizeof ("STATUS")-1, STATUS },
+ { "SYNTAX", sizeof ("SYNTAX")-1, SYNTAX },
+ { "OBJECT-TYPE", sizeof ("OBJECT-TYPE")-1, OBJTYPE },
+ { "{", sizeof ("{")-1, LEFTBRACKET },
+ { "}", sizeof ("}")-1, RIGHTBRACKET },
+ { "::=", sizeof ("::=")-1, EQUALS },
+ { "(", sizeof ("(")-1, LEFTPAREN },
+ { ")", sizeof (")")-1, RIGHTPAREN },
+ { ",", sizeof (",")-1, COMMA },
+ { NULL }
+};
+
+#define HASHSIZE 32
+#define BUCKET(x) (x & 0x01F)
+
+struct tok *buckets[HASHSIZE];
+
+static void do_subtree();
+static int get_token();
+static int parseQuoteString();
+static int tossObjectIdentifier();
+
+static void
+hash_init()
+{
+ struct tok *tp;
+ char *cp;
+ int h;
+ int b;
+
+ bzero((char *)buckets, sizeof(buckets));
+ for (tp = tokens; tp->name; tp++) {
+ for (h = 0, cp = tp->name; *cp; cp++)
+ h += *cp;
+ tp->hash = h;
+ b = BUCKET(h);
+ if (buckets[b])
+ tp->next = buckets[b]; /* BUG ??? */
+ buckets[b] = tp;
+ }
+}
+
+#define NHASHSIZE 128
+#define NBUCKET(x) (x & 0x7F)
+struct node *nbuckets[NHASHSIZE];
+
+void
+init_node_hash(nodes)
+ struct node *nodes;
+{
+ struct node *np, *nextp;
+ char *cp;
+ int hash;
+
+ bzero((char *)nbuckets,sizeof(nbuckets));
+ for(np = nodes; np;){
+ nextp = np->next;
+ hash = 0;
+ for(cp = np->parent; *cp; cp++)
+ hash += *cp;
+ np->next = nbuckets[NBUCKET(hash)];
+ nbuckets[NBUCKET(hash)] = np;
+ np = nextp;
+ }
+}
+
+static char *
+Malloc(num)
+ unsigned num;
+{
+#ifndef linux
+ char* calloc();
+#endif
+
+ /* this is to fix (what seems to be) a problem with the IBM RT C
+ library malloc */
+ if (num < 16)
+ num = 16;
+ return (char *)calloc(1, num);
+}
+
+static void
+print_error(string, token, type)
+ char *string;
+ char *token;
+ int type;
+{
+ if (type == ENDOFFILE)
+ fprintf(stderr, "%s(EOF): On or around line %d\n", string, Line);
+ else if (token)
+ fprintf(stderr, "%s(%s): On or around line %d\n", string, token, Line);
+ else
+ fprintf(stderr, "%s: On or around line %d\n", string, Line);
+}
+
+#ifdef TEST
+print_subtree(tree, count)
+ struct tree *tree;
+ int count;
+{
+ struct tree *tp;
+ int i;
+
+ for(i = 0; i < count; i++)
+ printf(" ");
+ printf("Children of %s:\n", tree->label);
+ count++;
+ for(tp = tree->child_list; tp; tp = tp->next_peer){
+ for(i = 0; i < count; i++)
+ printf(" ");
+ printf("%s\n", tp->label);
+ }
+ for(tp = tree->child_list; tp; tp = tp->next_peer){
+ print_subtree(tp, count);
+ }
+}
+#endif /* TEST */
+
+int translation_table[256];
+
+void
+build_translation_table()
+{
+ int count;
+
+ for(count = 0; count < 256; count++){
+ switch(count){
+ case OBJID:
+ translation_table[count] = TYPE_OBJID;
+ break;
+ case OCTETSTR:
+ translation_table[count] = TYPE_OCTETSTR;
+ break;
+ case INTEGER:
+ translation_table[count] = TYPE_INTEGER;
+ break;
+ case NETADDR:
+ translation_table[count] = TYPE_IPADDR;
+ break;
+ case IPADDR:
+ translation_table[count] = TYPE_IPADDR;
+ break;
+ case COUNTER:
+ translation_table[count] = TYPE_COUNTER;
+ break;
+ case GAUGE:
+ translation_table[count] = TYPE_GAUGE;
+ break;
+ case TIMETICKS:
+ translation_table[count] = TYPE_TIMETICKS;
+ break;
+ case OPAQUE:
+ translation_table[count] = TYPE_OPAQUE;
+ break;
+ case NUL:
+ translation_table[count] = TYPE_NULL;
+ break;
+ case COUNTER64:
+ translation_table[count] = TYPE_COUNTER64;
+ break;
+ case BITSTRING:
+ translation_table[count] = TYPE_BITSTRING;
+ break;
+ case NSAPADDRESS:
+ translation_table[count] = TYPE_NSAPADDRESS;
+ break;
+ case UINTEGER32:
+ translation_table[count] = TYPE_UINTEGER;
+ break;
+ default:
+ translation_table[count] = TYPE_OTHER;
+ break;
+ }
+ }
+}
+
+static struct tree *
+build_tree(nodes)
+ struct node *nodes;
+{
+ struct node *np;
+ struct tree *tp, *lasttp;
+ int bucket, nodes_left = 0;
+
+ build_translation_table();
+ /* grow tree from this root node */
+ init_node_hash(nodes);
+
+ /* build root node */
+ tp = (struct tree *)Malloc(sizeof(struct tree));
+ tp->parent = NULL;
+ tp->next_peer = NULL;
+ tp->child_list = NULL;
+ tp->enums = NULL;
+ strcpy(tp->label, "joint-iso-ccitt");
+ tp->subid = 2;
+ tp->type = 0;
+ tp->description = 0;
+ /* XXX nodes isn't needed in do_subtree() ??? */
+ do_subtree(tp, &nodes);
+ lasttp = tp;
+
+ /* build root node */
+ tp = (struct tree *)Malloc(sizeof(struct tree));
+ tp->parent = NULL;
+ tp->next_peer = lasttp;
+ tp->child_list = NULL;
+ tp->enums = NULL;
+ strcpy(tp->label, "ccitt");
+ tp->subid = 0;
+ tp->type = 0;
+ tp->description = 0;
+ /* XXX nodes isn't needed in do_subtree() ??? */
+ do_subtree(tp, &nodes);
+ lasttp = tp;
+
+ /* build root node */
+ tp = (struct tree *)Malloc(sizeof(struct tree));
+ tp->parent = NULL;
+ tp->next_peer = lasttp;
+ tp->child_list = NULL;
+ tp->enums = NULL;
+ strcpy(tp->label, "iso");
+ tp->subid = 1;
+ tp->type = 0;
+ tp->description = 0;
+ /* XXX nodes isn't needed in do_subtree() ??? */
+ do_subtree(tp, &nodes);
+
+
+#ifdef TEST
+ print_subtree(tp, 0);
+#endif /* TEST */
+ /* If any nodes are left, the tree is probably inconsistent */
+ for(bucket = 0; bucket < NHASHSIZE; bucket++){
+ if (nbuckets[bucket]){
+ nodes_left = 1;
+ break;
+ }
+ }
+ if (nodes_left){
+ fprintf(stderr, "The mib description doesn't seem to be consistent.\n");
+ fprintf(stderr, "Some nodes couldn't be linked under the \"iso\" tree.\n");
+ fprintf(stderr, "these nodes are left:\n");
+ for(bucket = 0; bucket < NHASHSIZE; bucket++){
+ for(np = nbuckets[bucket]; np; np = np->next)
+ fprintf(stderr, "%s ::= { %s %ld } (%d)\n", np->label,
+ np->parent, np->subid, np->type);
+ }
+ }
+ return tp;
+}
+
+/*
+ * Find all the children of root in the list of nodes. Link them into the
+ * tree and out of the nodes list.
+ */
+static void
+do_subtree(root, nodes)
+ struct tree *root;
+ struct node **nodes;
+{
+ struct tree *tp;
+ struct tree *peer = NULL;
+ struct node *np, **headp;
+ struct node *oldnp = NULL, *child_list = NULL, *childp = NULL;
+ char *cp;
+ int hash;
+
+ tp = root;
+ hash = 0;
+ for(cp = tp->label; *cp; cp++)
+ hash += *cp;
+ headp = &nbuckets[NBUCKET(hash)];
+ /*
+ * Search each of the nodes for one whose parent is root, and
+ * move each into a separate list.
+ */
+ for(np = *headp; np; np = np->next){
+ if ((*tp->label != *np->parent) || strcmp(tp->label, np->parent)){
+ if ((*tp->label == *np->label) && !strcmp(tp->label, np->label)){
+ /* if there is another node with the same label, assume that
+ * any children after this point in the list belong to the other node.
+ * This adds some scoping to the table and allows vendors to
+ * reuse names such as "ip".
+ */
+ break;
+ }
+ oldnp = np;
+ } else {
+ if (child_list == NULL){
+ child_list = childp = np; /* first entry in child list */
+ } else {
+ childp->next = np;
+ childp = np;
+ }
+ /* take this node out of the node list */
+ if (oldnp == NULL){
+ *headp = np->next; /* fix root of node list */
+ } else {
+ oldnp->next = np->next; /* link around this node */
+ }
+ }
+ }
+ if (childp)
+ childp->next = 0; /* re-terminate list */
+ /*
+ * Take each element in the child list and place it into the tree.
+ */
+ for(np = child_list; np; np = np->next){
+ tp = (struct tree *)Malloc(sizeof(struct tree));
+ tp->parent = root;
+ tp->next_peer = NULL;
+ tp->child_list = NULL;
+ strcpy(tp->label, np->label);
+ tp->subid = np->subid;
+ tp->type = translation_table[np->type];
+ tp->enums = np->enums;
+ np->enums = NULL; /* so we don't free them later */
+ tp->description = np->description; /* steals memory from np */
+ np->description = NULL; /* so we don't free it later */
+ if (root->child_list == NULL){
+ root->child_list = tp;
+ } else {
+ peer->next_peer = tp;
+ }
+ peer = tp;
+/* if (tp->type == TYPE_OTHER) */
+ do_subtree(tp, nodes); /* recurse on this child if it isn't
+ an end node */
+ }
+ /* free all nodes that were copied into tree */
+ oldnp = NULL;
+ for(np = child_list; np; np = np->next){
+ if (oldnp)
+ free(oldnp);
+ oldnp = np;
+ }
+ if (oldnp)
+ free(oldnp);
+}
+
+
+/*
+ * Takes a list of the form:
+ * { iso org(3) dod(6) 1 }
+ * and creates several nodes, one for each parent-child pair.
+ * Returns NULL on error.
+ */
+static int
+getoid(fp, oid, length)
+ FILE *fp;
+ struct subid *oid; /* an array of subids */
+ int length; /* the length of the array */
+{
+ int count;
+ int type;
+ char token[MAXTOKEN];
+ char *cp;
+
+ if ((type = get_token(fp, token)) != LEFTBRACKET){
+ print_error("Expected \"{\"", token, type);
+ return 0;
+ }
+ type = get_token(fp, token);
+ for(count = 0; count < length; count++, oid++){
+ oid->label = 0;
+ oid->subid = -1;
+ if (type == RIGHTBRACKET){
+ return count;
+ } else if (type != LABEL && type != NUMBER){
+ print_error("Not valid for object identifier", token, type);
+ return 0;
+ }
+ if (type == LABEL){
+ /* this entry has a label */
+ cp = (char *)Malloc((unsigned)strlen(token) + 1);
+ strcpy(cp, token);
+ oid->label = cp;
+ type = get_token(fp, token);
+ if (type == LEFTPAREN){
+ type = get_token(fp, token);
+ if (type == NUMBER){
+ oid->subid = atoi(token);
+ if ((type = get_token(fp, token)) != RIGHTPAREN){
+ print_error("Unexpected a closing parenthesis", token, type);
+ return 0;
+ }
+ } else {
+ print_error("Expected a number", token, type);
+ return 0;
+ }
+ } else {
+ continue;
+ }
+ } else {
+ /* this entry has just an integer sub-identifier */
+ oid->subid = atoi(token);
+ }
+ type = get_token(fp, token);
+ }
+ return count;
+
+
+}
+
+static void
+free_node(np)
+ struct node *np;
+{
+ struct enum_list *ep, *tep;
+
+ ep = np->enums;
+ while(ep){
+ tep = ep;
+ ep = ep->next;
+ free((char *)tep);
+ }
+ free((char *)np);
+}
+
+/*
+ * Parse an entry of the form:
+ * label OBJECT IDENTIFIER ::= { parent 2 }
+ * The "label OBJECT IDENTIFIER" portion has already been parsed.
+ * Returns 0 on error.
+ */
+static struct node *
+parse_objectid(fp, name)
+ FILE *fp;
+ char *name;
+{
+ int type;
+ char token[MAXTOKEN];
+ int count;
+ struct subid *op, *nop;
+ int length;
+ struct subid oid[32];
+ struct node *np, *root, *oldnp = NULL;
+
+ type = get_token(fp, token);
+ if (type != EQUALS){
+ print_error("Bad format", token, type);
+ return 0;
+ }
+ if ((length = getoid(fp, oid, 32)) != 0){
+ np = root = (struct node *)Malloc(sizeof(struct node));
+ bzero((char *)np, sizeof(struct node));
+ /*
+ * For each parent-child subid pair in the subid array,
+ * create a node and link it into the node list.
+ */
+ for(count = 0, op = oid, nop=oid+1; count < (length - 2); count++,
+ op++, nop++){
+ /* every node must have parent's name and child's name or number */
+ if (op->label && (nop->label || (nop->subid != -1))){
+ strcpy(np->parent, op->label);
+ if (nop->label)
+ strcpy(np->label, nop->label);
+ if (nop->subid != -1)
+ np->subid = nop->subid;
+ np->type = 0;
+ np->enums = 0;
+ /* set up next entry */
+ np->next = (struct node *)Malloc(sizeof(*np->next));
+ bzero((char *)np->next, sizeof(struct node));
+ oldnp = np;
+ np = np->next;
+ }
+ }
+ np->next = (struct node *)NULL;
+ /*
+ * The above loop took care of all but the last pair. This pair is taken
+ * care of here. The name for this node is taken from the label for this
+ * entry.
+ * np still points to an unused entry.
+ */
+ if (count == (length - 2)){
+ if (op->label){
+ strcpy(np->parent, op->label);
+ strcpy(np->label, name);
+ if (nop->subid != -1)
+ np->subid = nop->subid;
+ else
+ print_error("Warning: This entry is pretty silly",
+ np->label, type);
+ } else {
+ free_node(np);
+ if (oldnp)
+ oldnp->next = NULL;
+ else
+ return NULL;
+ }
+ } else {
+ print_error("Missing end of oid", (char *)NULL, type);
+ free_node(np); /* the last node allocated wasn't used */
+ if (oldnp)
+ oldnp->next = NULL;
+ return NULL;
+ }
+ /* free the oid array */
+ for(count = 0, op = oid; count < length; count++, op++){
+ if (op->label)
+ free(op->label);
+ op->label = 0;
+ }
+ return root;
+ } else {
+ print_error("Bad object identifier", (char *)NULL, type);
+ return 0;
+ }
+}
+
+static int
+get_tc(descriptor, ep)
+ char *descriptor;
+ struct enum_list **ep;
+{
+ int i;
+
+ for(i = 0; i < MAXTC; i++){
+ if (tclist[i].type == 0)
+ break;
+ if (!strcmp(descriptor, tclist[i].descriptor)){
+ *ep = tclist[i].enums;
+ return tclist[i].type;
+ }
+ }
+ return LABEL;
+}
+
+/*
+ * Parses an asn type. Structures are ignored by this parser.
+ * Returns NULL on error.
+ */
+static int
+parse_asntype(fp, name, ntype, ntoken)
+ FILE *fp;
+ char *name;
+ int *ntype;
+ char *ntoken;
+{
+ int type, i;
+ char token[MAXTOKEN];
+ struct enum_list *ep = 0;
+ struct tc *tcp;
+ int level;
+
+ type = get_token(fp, token);
+ if (type == SEQUENCE){
+ while((type = get_token(fp, token)) != ENDOFFILE){
+ if (type == RIGHTBRACKET){
+ *ntype = get_token(fp, ntoken);
+ return 1;
+ }
+ }
+ print_error("Expected \"}\"", token, type);
+ return 0;
+ } else {
+ if (!strcmp(token, "TEXTUAL-CONVENTION")){
+ while (type != SYNTAX)
+ type = get_token(fp, token);
+ type = get_token(fp, token);
+ }
+ /* textual convention */
+ for(i = 0; i < MAXTC; i++){
+ if (tclist[i].type == 0)
+ break;
+ }
+ if (i == MAXTC){
+ print_error("No more textual conventions possible.", token, type);
+ return 0;
+ }
+ tcp = &tclist[i];
+ strcpy(tcp->descriptor, name);
+ if (!(type & SYNTAX_MASK)){
+ print_error("Textual convention doesn't map to real type.", token,
+ type);
+ return 0;
+ }
+ tcp->type = type;
+ *ntype = get_token(fp, ntoken);
+ if (*ntype == LEFTPAREN){
+ level = 1;
+ /* don't record any constraints for now */
+ while(level > 0){
+ *ntype = get_token(fp, ntoken);
+ if (*ntype == LEFTPAREN)
+ level++;
+ if (*ntype == RIGHTPAREN)
+ level--;
+ }
+ *ntype = get_token(fp, ntoken);
+ } else if (*ntype == LEFTBRACKET) {
+ /* if there is an enumeration list, parse it */
+ while((type = get_token(fp, token)) != ENDOFFILE){
+ if (type == RIGHTBRACKET)
+ break;
+ if (type == LABEL){
+ /* this is an enumerated label */
+ if (tcp->enums == 0){
+ ep = tcp->enums = (struct enum_list *)
+ Malloc(sizeof(struct enum_list));
+ } else {
+ ep->next = (struct enum_list *)
+ Malloc(sizeof(struct enum_list));
+ ep = ep->next;
+ }
+ ep->next = 0;
+ /* a reasonable approximation for the length */
+ ep->label =
+ (char *)Malloc((unsigned)strlen(token) + 1);
+ strcpy(ep->label, token);
+ type = get_token(fp, token);
+ if (type != LEFTPAREN){
+ print_error("Expected \"(\"", token, type);
+ /* free_node(np); */
+ return 0;
+ }
+ type = get_token(fp, token);
+ if (type != NUMBER){
+ print_error("Expected integer", token, type);
+ /* free_node(np); */
+ return 0;
+ }
+ ep->value = atoi(token);
+ type = get_token(fp, token);
+ if (type != RIGHTPAREN){
+ print_error("Expected \")\"", token, type);
+ /* free_node(np); */
+ return 0;
+ }
+ }
+ }
+ if (type == ENDOFFILE){
+ print_error("Expected \"}\"", token, type);
+ /* free_node(np); */
+ return 0;
+ }
+ *ntype = get_token(fp, ntoken);
+ }
+ return 1;
+ }
+}
+
+
+/*
+ * Parses an OBJECT TYPE macro.
+ * Returns 0 on error.
+ */
+static struct node *
+parse_objecttype(fp, name)
+ FILE *fp;
+ char *name;
+{
+ int type;
+ char token[MAXTOKEN];
+ int count, length;
+ struct subid oid[32];
+ char syntax[MAXTOKEN];
+ int nexttype, tctype;
+ char nexttoken[MAXTOKEN];
+ struct node *np;
+ struct enum_list *ep = 0;
+
+ type = get_token(fp, token);
+ if (type != SYNTAX){
+ print_error("Bad format for OBJECT TYPE", token, type);
+ return 0;
+ }
+ np = (struct node *)Malloc(sizeof(struct node));
+ np->next = 0;
+ np->enums = 0;
+ np->description = NULL; /* default to an empty description */
+ type = get_token(fp, token);
+ if (type == LABEL){
+ tctype = get_tc(token, &(np->enums));
+#if 0
+ if (tctype == LABEL){
+ print_error("No known translation for type", token, type);
+ return 0;
+ }
+#endif
+ type = tctype;
+ }
+ np->type = type;
+ nexttype = get_token(fp, nexttoken);
+ switch(type){
+ case SEQUENCE:
+ strcpy(syntax, token);
+ if (nexttype == OF){
+ strcat(syntax, " ");
+ strcat(syntax, nexttoken);
+ nexttype = get_token(fp, nexttoken);
+ strcat(syntax, " ");
+ strcat(syntax, nexttoken);
+ nexttype = get_token(fp, nexttoken);
+ }
+ break;
+ case INTEGER:
+ case UINTEGER32:
+ strcpy(syntax, token);
+ if (nexttype == LEFTBRACKET) {
+ /* if there is an enumeration list, parse it */
+ while((type = get_token(fp, token)) != ENDOFFILE){
+ if (type == RIGHTBRACKET)
+ break;
+ if (type == LABEL){
+ /* this is an enumerated label */
+ if (np->enums == 0){
+ ep = np->enums = (struct enum_list *)
+ Malloc(sizeof(struct enum_list));
+ } else {
+ ep->next = (struct enum_list *)
+ Malloc(sizeof(struct enum_list));
+ ep = ep->next;
+ }
+ ep->next = 0;
+ /* a reasonable approximation for the length */
+ ep->label =
+ (char *)Malloc((unsigned)strlen(token) + 1);
+ strcpy(ep->label, token);
+ type = get_token(fp, token);
+ if (type != LEFTPAREN){
+ print_error("Expected \"(\"", token, type);
+ free_node(np);
+ return 0;
+ }
+ type = get_token(fp, token);
+ if (type != NUMBER){
+ print_error("Expected integer", token, type);
+ free_node(np);
+ return 0;
+ }
+ ep->value = atoi(token);
+ type = get_token(fp, token);
+ if (type != RIGHTPAREN){
+ print_error("Expected \")\"", token, type);
+ free_node(np);
+ return 0;
+ }
+ }
+ }
+ if (type == ENDOFFILE){
+ print_error("Expected \"}\"", token, type);
+ free_node(np);
+ return 0;
+ }
+ nexttype = get_token(fp, nexttoken);
+ } else if (nexttype == LEFTPAREN){
+ /* ignore the "constrained integer" for now */
+ nexttype = get_token(fp, nexttoken);
+ nexttype = get_token(fp, nexttoken);
+ nexttype = get_token(fp, nexttoken);
+ }
+ break;
+ case BITSTRING:
+ strcpy(syntax, token);
+ if (nexttype == LEFTBRACKET) {
+ /* if there is an enumeration list, parse it */
+ while((type = get_token(fp, token)) != ENDOFFILE){
+ if (type == RIGHTBRACKET)
+ break;
+ if (type == LABEL){
+ /* this is an enumerated label */
+ if (np->enums == 0){
+ ep = np->enums = (struct enum_list *)
+ Malloc(sizeof(struct enum_list));
+ } else {
+ ep->next = (struct enum_list *)
+ Malloc(sizeof(struct enum_list));
+ ep = ep->next;
+ }
+ ep->next = 0;
+ /* a reasonable approximation for the length */
+ ep->label =
+ (char *)Malloc((unsigned)strlen(token) + 1);
+ strcpy(ep->label, token);
+ type = get_token(fp, token);
+ if (type != LEFTPAREN){
+ print_error("Expected \"(\"", token, type);
+ free_node(np);
+ return 0;
+ }
+ type = get_token(fp, token);
+ if (type != NUMBER){
+ print_error("Expected integer", token, type);
+ free_node(np);
+ return 0;
+ }
+ ep->value = atoi(token);
+ type = get_token(fp, token);
+ if (type != RIGHTPAREN){
+ print_error("Expected \")\"", token, type);
+ free_node(np);
+ return 0;
+ }
+ }
+ }
+ if (type == ENDOFFILE){
+ print_error("Expected \"}\"", token, type);
+ free_node(np);
+ return 0;
+ }
+ nexttype = get_token(fp, nexttoken);
+ } else if (nexttype == LEFTPAREN){
+ /* ignore the "constrained integer" for now */
+ nexttype = get_token(fp, nexttoken);
+ nexttype = get_token(fp, nexttoken);
+ nexttype = get_token(fp, nexttoken);
+ }
+ break;
+ case OCTETSTR:
+ strcpy(syntax, token);
+ /* ignore the "constrained octet string" for now */
+ if (nexttype == LEFTPAREN) {
+ nexttype = get_token(fp, nexttoken);
+ if (nexttype == SIZE) {
+ nexttype = get_token(fp, nexttoken);
+ if (nexttype == LEFTPAREN) {
+ nexttype = get_token(fp, nexttoken); /* 0..255 */
+ nexttype = get_token(fp, nexttoken); /* ) */
+ nexttype = get_token(fp, nexttoken); /* ) */
+ if (nexttype == RIGHTPAREN)
+ {
+ nexttype = get_token(fp, nexttoken);
+ break;
+ }
+ }
+ }
+ print_error("Bad syntax", token, type);
+ free_node(np);
+ return 0;
+ }
+ break;
+ case OBJID:
+ case NETADDR:
+ case IPADDR:
+ case COUNTER:
+ case GAUGE:
+ case TIMETICKS:
+ case OPAQUE:
+ case NUL:
+ case LABEL:
+ case NSAPADDRESS:
+ case COUNTER64:
+ strcpy(syntax, token);
+ break;
+ default:
+ print_error("Bad syntax", token, type);
+ free_node(np);
+ return 0;
+ }
+ if (nexttype == UNITS){
+ type = get_token(fp, token);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", token, type);
+ free_node(np);
+ return 0;
+ }
+ nexttype = get_token(fp, nexttoken);
+ }
+ if (nexttype != ACCESS){
+ print_error("Should be ACCESS", nexttoken, nexttype);
+ free_node(np);
+ return 0;
+ }
+ type = get_token(fp, token);
+ if (type != READONLY && type != READWRITE && type != WRITEONLY
+ && type != NOACCESS && type != READCREATE){
+ print_error("Bad access type", nexttoken, nexttype);
+ free_node(np);
+ return 0;
+ }
+ type = get_token(fp, token);
+ if (type != STATUS){
+ print_error("Should be STATUS", token, nexttype);
+ free_node(np);
+ return 0;
+ }
+ type = get_token(fp, token);
+ if (type != MANDATORY && type != CURRENT && type != OPTIONAL && type != OBSOLETE && type != DEPRECATED){
+ print_error("Bad status", token, type);
+ free_node(np);
+ return 0;
+ }
+ /*
+ * Optional parts of the OBJECT-TYPE macro
+ */
+ type = get_token(fp, token);
+ while (type != EQUALS) {
+ switch (type) {
+ case DESCRIPTION:
+ type = get_token(fp, token);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", token, type);
+ free_node(np);
+ return 0;
+ }
+#ifdef TEST
+printf("Description== \"%.50s\"\n", quoted_string_buffer);
+#endif
+ np->description = quoted_string_buffer;
+ quoted_string_buffer = (char *)calloc(1, MAXQUOTESTR);
+ break;
+
+ case REFERENCE:
+ type = get_token(fp, token);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", token, type);
+ free_node(np);
+ return 0;
+ }
+ break;
+ case INDEX:
+ case DEFVAL:
+ case AUGMENTS:
+ case NUM_ENTRIES:
+ if (tossObjectIdentifier(fp) != OBJID) {
+ print_error("Bad Object Identifier", token, type);
+ free_node(np);
+ return 0;
+ }
+ break;
+
+ default:
+ print_error("Bad format of optional clauses", token,type);
+ free_node(np);
+ return 0;
+
+ }
+ type = get_token(fp, token);
+ }
+ if (type != EQUALS){
+ print_error("Bad format", token, type);
+ free_node(np);
+ return 0;
+ }
+ length = getoid(fp, oid, 32);
+ if (length > 1 && length <= 32){
+ /* just take the last pair in the oid list */
+ if (oid[length - 2].label)
+ strncpy(np->parent, oid[length - 2].label, MAXLABEL);
+ strcpy(np->label, name);
+ if (oid[length - 1].subid != -1)
+ np->subid = oid[length - 1].subid;
+ else
+ print_error("Warning: This entry is pretty silly", np->label, type);
+ } else {
+ print_error("No end to oid", (char *)NULL, type);
+ free_node(np);
+ np = 0;
+ }
+ /* free oid array */
+ for(count = 0; count < length; count++){
+ if (oid[count].label)
+ free(oid[count].label);
+ oid[count].label = 0;
+ }
+ return np;
+}
+
+
+/*
+ * Parses an OBJECT GROUP macro.
+ * Returns 0 on error.
+ */
+static struct node *
+parse_objectgroup(fp, name)
+ FILE *fp;
+ char *name;
+{
+ int type;
+ char token[MAXTOKEN];
+ int count, length;
+ struct subid oid[32];
+ struct node *np;
+
+ np = (struct node *)Malloc(sizeof(struct node));
+ np->type = 0;
+ np->next = 0;
+ np->enums = 0;
+ np->description = NULL; /* default to an empty description */
+ type = get_token(fp, token);
+ while (type != EQUALS) {
+ switch (type) {
+ case DESCRIPTION:
+ type = get_token(fp, token);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", token, type);
+ free_node(np);
+ return 0;
+ }
+#ifdef TEST
+printf("Description== \"%.50s\"\n", quoted_string_buffer);
+#endif
+ np->description = quoted_string_buffer;
+ quoted_string_buffer = (char *)calloc(1, MAXQUOTESTR);
+ break;
+
+ default:
+ /* NOTHING */
+ break;
+ }
+ type = get_token(fp, token);
+ }
+ length = getoid(fp, oid, 32);
+ if (length > 1 && length <= 32){
+ /* just take the last pair in the oid list */
+ if (oid[length - 2].label)
+ strncpy(np->parent, oid[length - 2].label, MAXLABEL);
+ strcpy(np->label, name);
+ if (oid[length - 1].subid != -1)
+ np->subid = oid[length - 1].subid;
+ else
+ print_error("Warning: This entry is pretty silly", np->label, type);
+ } else {
+ print_error("No end to oid", (char *)NULL, type);
+ free_node(np);
+ np = 0;
+ }
+ /* free oid array */
+ for(count = 0; count < length; count++){
+ if (oid[count].label)
+ free(oid[count].label);
+ oid[count].label = 0;
+ }
+ return np;
+}
+
+/*
+ * Parses a NOTIFICATION-TYPE macro.
+ * Returns 0 on error.
+ */
+static struct node *
+parse_notificationDefinition(fp, name)
+ FILE *fp;
+ char *name;
+{
+ int type;
+ char token[MAXTOKEN];
+ int count, length;
+ struct subid oid[32];
+ struct node *np;
+
+ np = (struct node *)Malloc(sizeof(struct node));
+ np->type = 0;
+ np->next = 0;
+ np->enums = 0;
+ np->description = NULL; /* default to an empty description */
+ type = get_token(fp, token);
+ while (type != EQUALS) {
+ switch (type) {
+ case DESCRIPTION:
+ type = get_token(fp, token);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", token, type);
+ free_node(np);
+ return 0;
+ }
+#ifdef TEST
+printf("Description== \"%.50s\"\n", quoted_string_buffer);
+#endif
+ np->description = quoted_string_buffer;
+ quoted_string_buffer = (char *)calloc(1, MAXQUOTESTR);
+ break;
+
+ default:
+ /* NOTHING */
+ break;
+ }
+ type = get_token(fp, token);
+ }
+ length = getoid(fp, oid, 32);
+ if (length > 1 && length <= 32){
+ /* just take the last pair in the oid list */
+ if (oid[length - 2].label)
+ strncpy(np->parent, oid[length - 2].label, MAXLABEL);
+ strcpy(np->label, name);
+ if (oid[length - 1].subid != -1)
+ np->subid = oid[length - 1].subid;
+ else
+ print_error("Warning: This entry is pretty silly", np->label, type);
+ } else {
+ print_error("No end to oid", (char *)NULL, type);
+ free_node(np);
+ np = 0;
+ }
+ /* free oid array */
+ for(count = 0; count < length; count++){
+ if (oid[count].label)
+ free(oid[count].label);
+ oid[count].label = 0;
+ }
+ return np;
+}
+
+/*
+ * Parses a compliance macro
+ * Returns 0 on error.
+ */
+static struct node *
+parse_compliance(fp, name)
+ FILE *fp;
+ char *name;
+{
+ int type;
+ char token[MAXTOKEN];
+ int count, length;
+ struct subid oid[32];
+ struct node *np;
+
+ np = (struct node *)Malloc(sizeof(struct node));
+ np->type = 0;
+ np->next = 0;
+ np->enums = 0;
+ np->description = NULL; /* default to an empty description */
+ type = get_token(fp, token);
+ while (type != EQUALS) {
+ type = get_token(fp, token);
+ }
+ length = getoid(fp, oid, 32);
+ if (length > 1 && length <= 32){
+ /* just take the last pair in the oid list */
+ if (oid[length - 2].label)
+ strncpy(np->parent, oid[length - 2].label, MAXLABEL);
+ strcpy(np->label, name);
+ if (oid[length - 1].subid != -1)
+ np->subid = oid[length - 1].subid;
+ else
+ print_error("Warning: This entry is pretty silly", np->label, type);
+ } else {
+ print_error("No end to oid", (char *)NULL, type);
+ free_node(np);
+ np = 0;
+ }
+ /* free oid array */
+ for(count = 0; count < length; count++){
+ if (oid[count].label)
+ free(oid[count].label);
+ oid[count].label = 0;
+ }
+ return np;
+}
+
+
+
+/*
+ * Parses a module identity macro
+ * Returns 0 on error.
+ */
+static struct node *
+parse_moduleIdentity(fp, name)
+ FILE *fp;
+ char *name;
+{
+ int type;
+ char token[MAXTOKEN];
+ int count, length;
+ struct subid oid[32];
+ struct node *np;
+
+ np = (struct node *)Malloc(sizeof(struct node));
+ np->type = 0;
+ np->next = 0;
+ np->enums = 0;
+ np->description = NULL; /* default to an empty description */
+ type = get_token(fp, token);
+ while (type != EQUALS) {
+ type = get_token(fp, token);
+ }
+ length = getoid(fp, oid, 32);
+ if (length > 1 && length <= 32){
+ /* just take the last pair in the oid list */
+ if (oid[length - 2].label)
+ strncpy(np->parent, oid[length - 2].label, MAXLABEL);
+ strcpy(np->label, name);
+ if (oid[length - 1].subid != -1)
+ np->subid = oid[length - 1].subid;
+ else
+ print_error("Warning: This entry is pretty silly", np->label, type);
+ } else {
+ print_error("No end to oid", (char *)NULL, type);
+ free_node(np);
+ np = 0;
+ }
+ /* free oid array */
+ for(count = 0; count < length; count++){
+ if (oid[count].label)
+ free(oid[count].label);
+ oid[count].label = 0;
+ }
+ return np;
+}
+
+int parse_mib_header(fp, name)
+ FILE *fp;
+ char *name;
+{
+ int type = DEFINITIONS;
+ char token[MAXTOKEN];
+
+ /* This probably isn't good enough. If there is no
+ imports clause we can't go around waiting (forever) for a semicolon.
+ We need to check for semi following an EXPORTS clause or an IMPORTS
+ clause of both. Look for BEGIN; in my initial MIBs to see those
+ that I needed to hack to get to parse because they didn't have
+ an IMPORTS or and EXPORTS clause.
+ */
+ while(type != SEMI && type != ENDOFFILE){
+ type = get_token(fp, token);
+ }
+ return (type == SEMI);
+}
+
+
+
+/*
+ * Parses a mib file and returns a linked list of nodes found in the file.
+ * Returns NULL on error.
+ */
+static struct node *
+parse(fp)
+ FILE *fp;
+{
+ char token[MAXTOKEN];
+ char name[MAXTOKEN];
+ int type = 1;
+#define BETWEEN_MIBS 1
+#define IN_MIB 2
+ int state = BETWEEN_MIBS;
+ struct node *np = 0, *root = NULL;
+
+ hash_init();
+ quoted_string_buffer = (char *)calloc(1, MAXQUOTESTR); /* free this later */
+ bzero(tclist, 64 * sizeof(struct tc));
+
+ while(type != ENDOFFILE){
+ type = get_token(fp, token);
+skipget:
+ if (type == END){
+ if (state != IN_MIB){
+ print_error("Error, end before start of MIB.", (char *)NULL, type);
+ return NULL;
+ }
+ state = BETWEEN_MIBS;
+ continue;
+ } else if (type != LABEL){
+ if (type == ENDOFFILE){
+ return root;
+ }
+ print_error(token, "is a reserved word", type);
+ return NULL;
+ }
+ strncpy(name, token, MAXTOKEN);
+ type = get_token(fp, token);
+ if (type == DEFINITIONS){
+ if (state != BETWEEN_MIBS){
+ print_error("Error, nested MIBS.", (char *)NULL, type);
+ return NULL;
+ }
+ state = IN_MIB;
+ if (!parse_mib_header(fp, name)){
+ print_error("Bad parse of module header", (char *)NULL, type);
+ return NULL;
+ }
+ } else if (type == OBJTYPE){
+ if (root == NULL){
+ /* first link in chain */
+ np = root = parse_objecttype(fp, name);
+ if (np == NULL){
+ print_error("Bad parse of object type", (char *)NULL,
+ type);
+ return NULL;
+ }
+ } else {
+ np->next = parse_objecttype(fp, name);
+ if (np->next == NULL){
+ print_error("Bad parse of objecttype", (char *)NULL,
+ type);
+ return NULL;
+ }
+ }
+ /* now find end of chain */
+ while(np->next)
+ np = np->next;
+ } else if (type == OBJGROUP){
+ if (root == NULL){
+ /* first link in chain */
+ np = root = parse_objectgroup(fp, name);
+ if (np == NULL){
+ print_error("Bad parse of object group", (char *)NULL,
+ type);
+ return NULL;
+ }
+ } else {
+ np->next = parse_objectgroup(fp, name);
+ if (np->next == NULL){
+ print_error("Bad parse of objectgroup", (char *)NULL,
+ type);
+ return NULL;
+ }
+ }
+ /* now find end of chain */
+ while(np->next)
+ np = np->next;
+ } else if (type == NOTIFTYPE){
+ if (root == NULL){
+ /* first link in chain */
+ np = root = parse_notificationDefinition(fp, name);
+ if (np == NULL){
+ print_error("Bad parse of notification definition",
+ (char *)NULL, type);
+ return NULL;
+ }
+ } else {
+ np->next = parse_notificationDefinition(fp, name);
+ if (np->next == NULL){
+ print_error("Bad parse of notification definition",
+ (char *)NULL, type);
+ return NULL;
+ }
+ }
+ /* now find end of chain */
+ while(np->next)
+ np = np->next;
+ } else if (type == COMPLIANCE){
+ if (root == NULL){
+ /* first link in chain */
+ np = root = parse_compliance(fp, name);
+ if (np == NULL){
+ print_error("Bad parse of module compliance", (char *)NULL,
+ type);
+ return NULL;
+ }
+ } else {
+ np->next = parse_compliance(fp, name);
+ if (np->next == NULL){
+ print_error("Bad parse of module compliance", (char *)NULL,
+ type);
+ return NULL;
+ }
+ }
+ /* now find end of chain */
+ while(np->next)
+ np = np->next;
+ } else if (type == MODULEIDENTITY){
+ if (root == NULL){
+ /* first link in chain */
+ np = root = parse_moduleIdentity(fp, name);
+ if (np == NULL){
+ print_error("Bad parse of module identity", (char *)NULL,
+ type);
+ return NULL;
+ }
+ } else {
+ np->next = parse_moduleIdentity(fp, name);
+ if (np->next == NULL){
+ print_error("Bad parse of module identity", (char *)NULL,
+ type);
+ return NULL;
+ }
+ }
+ /* now find end of chain */
+ while(np->next)
+ np = np->next;
+ } else if (type == OBJID){
+ if (root == NULL){
+ /* first link in chain */
+ np = root = parse_objectid(fp, name);
+ if (np == NULL){
+ print_error("Bad parse of object id", (char *)NULL, type);
+ return NULL;
+ }
+ } else {
+ np->next = parse_objectid(fp, name);
+ if (np->next == NULL){
+ print_error("Bad parse of object type", (char *)NULL,
+ type);
+ return NULL;
+ }
+ }
+ /* now find end of chain */
+ while(np->next)
+ np = np->next;
+ } else if (type == EQUALS){
+ if (!parse_asntype(fp, name, &type, token)){
+ print_error("Bad parse of ASN type definition.", NULL, EQUALS);
+ return NULL;
+ }
+ goto skipget;
+ } else if (type == ENDOFFILE){
+ break;
+ } else {
+ print_error("Bad operator", (char *)NULL, type);
+ return NULL;
+ }
+ }
+#ifdef TEST
+{
+ struct enum_list *ep;
+
+ for(np = root; np; np = np->next){
+ printf("%s ::= { %s %d } (%d)\n", np->label, np->parent, np->subid,
+ np->type);
+ if (np->enums){
+ printf("Enums: \n");
+ for(ep = np->enums; ep; ep = ep->next){
+ printf("%s(%d)\n", ep->label, ep->value);
+ }
+ }
+ }
+}
+#endif /* TEST */
+ return root;
+}
+
+/*
+ * Parses a token from the file. The type of the token parsed is returned,
+ * and the text is placed in the string pointed to by token.
+ */
+static int
+get_token(fp, token)
+ FILE *fp;
+ char *token;
+{
+ static char last = ' ';
+ int ch;
+ char *cp = token;
+ int hash = 0;
+ struct tok *tp;
+
+ *cp = 0;
+ ch = last;
+ /* skip all white space */
+ while(isspace(ch) && ch != -1){
+ ch = getc(fp);
+ if (ch == '\n')
+ Line++;
+ }
+ if (ch == -1) {
+ return ENDOFFILE;
+ } else if (ch == '"') {
+ return parseQuoteString(fp, token);
+ }
+
+ /*
+ * Accumulate characters until end of token is found. Then attempt to
+ * match this token as a reserved word. If a match is found, return the
+ * type. Else it is a label.
+ */
+ do {
+ if (ch == '\n')
+ Line++;
+ if (isspace(ch) || ch == '(' || ch == ')' || ch == '{' || ch == '}' ||
+ ch == ',' || ch == ';'){
+ if (!isspace(ch) && *token == 0){
+ hash += ch;
+ *cp++ = ch;
+ last = ' ';
+ } else {
+ last = ch;
+ }
+ *cp = '\0';
+
+ for (tp = buckets[BUCKET(hash)]; tp; tp = tp->next) {
+ if ((tp->hash == hash) && (strcmp(tp->name, token) == 0))
+ break;
+ }
+ if (tp){
+ if (tp->token == CONTINUE)
+ continue;
+ return (tp->token);
+ }
+
+ if (token[0] == '-' && token[1] == '-'){
+ /* strip comment */
+ if (ch != '\n'){
+ while ((ch = getc(fp)) != -1)
+ if (ch == '\n'){
+ Line++;
+ break;
+ }
+ }
+ if (ch == -1)
+ return ENDOFFILE;
+ last = ch;
+ return get_token(fp, token);
+ }
+ for(cp = token; *cp; cp++)
+ if (!isdigit(*cp))
+ return LABEL;
+ return NUMBER;
+ } else {
+ hash += ch;
+ *cp++ = ch;
+ if (ch == '\n')
+ Line++;
+ }
+
+ } while ((ch = getc(fp)) != -1);
+ return ENDOFFILE;
+}
+
+struct tree *
+read_mib(filename)
+ char *filename;
+{
+ FILE *fp;
+ struct node *nodes;
+ struct tree *tree;
+
+ fp = fopen(filename, "r");
+ if (fp == NULL)
+ return NULL;
+ nodes = parse(fp);
+ if (!nodes){
+ fprintf(stderr, "Mib table is bad. Exiting\n");
+ exit(1);
+ }
+ tree = build_tree(nodes);
+ fclose(fp);
+ return tree;
+}
+
+
+#ifdef TEST
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ FILE *fp;
+ struct node *nodes;
+ struct tree *tp;
+
+ fp = fopen("mib.txt", "r");
+ if (fp == NULL){
+ fprintf(stderr, "open failed\n");
+ return 1;
+ }
+ nodes = parse(fp);
+ tp = build_tree(nodes);
+ print_subtree(tp, 0);
+ fclose(fp);
+}
+
+#endif /* TEST */
+
+static int
+parseQuoteString(fp, token)
+ FILE *fp;
+ char *token;
+{
+ int ch;
+
+ ch = ' ';
+ *token = '\0'; /* make the token empty */
+
+ while(ch != -1) {
+ ch = getc(fp);
+ if (ch == '\n')
+ Line++;
+ else if (ch == '"') {
+ return QUOTESTRING;
+ }
+
+ }
+
+ return 0;
+}
+
+/*
+ * This routine parses a string like { blah blah blah } and returns OBJID if
+ * it is well formed, and NULL if not.
+ */
+static int
+tossObjectIdentifier(fp)
+ FILE *fp;
+{
+ int ch;
+
+ ch = getc(fp);
+/* ch = last; = ' '? */
+ /* skip all white space */
+ while(isspace(ch) && ch != -1){
+ ch = getc(fp);
+ if (ch == '\n')
+ Line++;
+ }
+ if (ch != '{')
+ return 0;
+
+ while(ch != -1) {
+ ch = getc(fp);
+
+ if (ch == '\n')
+ Line++;
+ else if (ch == '}')
+ return OBJID;
+ }
+
+/* last = ch;*/
+ return 0;
+}
--- /dev/null
+/***********************************************************
+ Copyright 1989 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of CMU not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+******************************************************************/
+/*
+ * snmp_api.c - API for access to snmp.
+ */
+#define DEBUG_SNMPTRACE 0 /* set to 1 to print all SNMP actions */
+#define DEBUG_SNMPFULLDUMP 0 /* set to 1 to dump all SNMP packets */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#ifdef linux
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#endif
+
+
+#include "asn1.h"
+#include "snmp.h"
+#include "snmp_impl.h"
+#include "snmp_api.h"
+#include "snmp_client.h"
+
+#define PACKET_LENGTH 4500
+
+#ifndef BSD4_3
+#define BSD4_2
+#endif
+
+#if !defined(BSD4_3) && !defined(linux) && !defined(sun)
+
+typedef long fd_mask;
+#define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */
+
+#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
+#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
+#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
+#endif
+
+oid default_enterprise[] = {1, 3, 6, 1, 4, 1, 3, 1, 1}; /* enterprises.cmu.systems.cmuSNMP */
+
+#define DEFAULT_COMMUNITY "public"
+#define DEFAULT_REMPORT SNMP_PORT
+#define DEFAULT_ENTERPRISE default_enterprise
+#define DEFAULT_TIME 0
+#define DEFAULT_MMS 1389 /* large, randomly picked for testing purposes */
+
+/*
+ * Internal information about the state of the snmp session.
+ */
+struct snmp_internal_session {
+ int sd; /* socket descriptor for this connection */
+ ipaddr addr; /* address of connected peer */
+ struct request_list *requests;/* Info about outstanding requests */
+};
+
+/*
+ * A list of all the outstanding requests for a particular session.
+ */
+struct request_list {
+ struct request_list *next_request;
+ u_long request_id; /* request id */
+ int retries; /* Number of retries */
+ u_long timeout; /* length to wait for timeout */
+ struct timeval time; /* Time this request was made */
+ struct timeval expire; /* time this request is due to expire */
+ struct snmp_pdu *pdu; /* The pdu for this request (saved so it can be retransmitted */
+};
+
+/*
+ * The list of active/open sessions.
+ */
+struct session_list {
+ struct session_list *next;
+ struct snmp_session *session;
+ struct snmp_internal_session *internal;
+};
+
+struct session_list *Sessions = NULL;
+
+u_long Reqid = 0;
+int snmp_errno = 0;
+
+char *api_errors[4] = {
+ "Unknown session",
+ "Unknown host",
+ "Invalid local port",
+ "Unknown Error"
+};
+
+
+void sync_with_agent();
+int parse_app_community_string();
+void snmp_synch_setup();
+int snmp_synch_response();
+void md5Digest();
+
+
+static char *
+api_errstring(snmp_errnumber)
+ int snmp_errnumber;
+{
+ if (snmp_errnumber <= SNMPERR_BAD_SESSION && snmp_errnumber >= SNMPERR_GENERR){
+ return api_errors[snmp_errnumber + 4];
+ } else {
+ return "Unknown Error";
+ }
+}
+
+#if 0
+/*
+ * Gets initial request ID for all transactions.
+ */
+static void
+init_snmp(){
+ struct timeval tv;
+
+ gettimeofday(&tv, (struct timezone *)0);
+ srandom(tv.tv_sec ^ tv.tv_usec);
+ Reqid = random();
+}
+#endif
+
+
+/*
+ * Dump snmp packet to stdout:
+ */
+static void
+snmp_print_packet(packet, length, addr, code)
+ char *packet;
+ int length;
+ ipaddr addr;
+ int code;
+{
+ if(length < 0) {
+ return;
+ }
+ if (code <= 0) { /* received */
+ printf("\nReceived %4d bytes from ", length);
+ }
+ else { /* sending */
+ printf("\nSending %4d bytes to ", length);
+ }
+ printf("%s:", inet_ntoa(addr.sin_addr));
+#if DEBUG_SNMPFULLDUMP
+ for (count = 0; count < length; count++) {
+ if ((count & 15) == 0) {
+ printf("\n ");
+ }
+ printf("%02X ", (int)(packet[count]&255));
+ }
+#endif
+ fflush(stdout);
+}
+
+#if DEBUG_SNMPTRACE
+/*
+ * Print request
+ */
+#define TRACE_SEND (0)
+#define TRACE_RECV (1)
+#define TRACE_TIMEOUT (2)
+static void
+snmp_print_trace(slp, rp, code)
+ struct session_list *slp;
+ struct request_list *rp;
+ int code;
+{
+ int reqid=0, retries=1;
+ if( rp ){
+ reqid = rp->request_id;
+ retries = rp->retries;
+ }
+ printf("\n Session %2d ReqId %4d ", slp->internal->sd, reqid);
+ switch(code)
+ {
+ case TRACE_SEND: printf("send pdu (%d)", retries);break;
+ case TRACE_RECV: printf("recv pdu");break;
+ case TRACE_TIMEOUT: printf("time out");break;
+ }
+ fflush(stdout);
+}
+#endif /* DEBUG_SNMPTRACE */
+
+
+
+
+/*
+ * Sets up the session with the snmp_session information provided
+ * by the user. Then opens and binds the necessary UDP port.
+ * A handle to the created session is returned (this is different than
+ * the pointer passed to snmp_open()). On any error, NULL is returned
+ * and snmp_errno is set to the appropriate error code.
+ */
+struct snmp_session *
+snmp_open(session)
+ struct snmp_session *session;
+{
+ struct session_list *slp;
+ struct snmp_internal_session *isp;
+ u_char *cp;
+ int sd;
+ u_long addr;
+ struct sockaddr_in me;
+ struct hostent *hp;
+ struct servent *servp;
+ extern int check_received_pkt();
+
+ /* Copy session structure and link into list */
+ slp = (struct session_list *)calloc(1, sizeof(struct session_list));
+ slp->internal = isp = (struct snmp_internal_session *)calloc(1, sizeof(struct snmp_internal_session));
+ bzero((char *)isp, sizeof(struct snmp_internal_session));
+ slp->internal->sd = -1; /* mark it not set */
+ slp->session = (struct snmp_session *)calloc(1, sizeof(struct snmp_session));
+ bcopy((char *)session, (char *)slp->session, sizeof(struct snmp_session));
+ session = slp->session;
+ /* now link it in. */
+ slp->next = Sessions;
+ Sessions = slp;
+
+ /*
+ * session now points to the new structure that still contains pointers to
+ * data allocated elsewhere. Some of this data is copied to space malloc'd
+ * here, and the pointer replaced with the new one.
+ */
+
+ if (session->peername != NULL){
+ cp = (u_char *)calloc(1, (unsigned)strlen(session->peername) + 1);
+ strcpy((char *)cp, session->peername);
+ session->peername = (char *)cp;
+ }
+
+ if (session->retries == SNMP_DEFAULT_RETRIES)
+ session->retries = SNMP_API_DEFAULT_RETRIES;
+ if (session->timeout == SNMP_DEFAULT_TIMEOUT)
+ session->timeout = SNMP_API_DEFAULT_TIMEOUT;
+ if (session->MMS == 0 )
+ session->MMS = DEFAULT_MMS;
+ isp->requests = NULL;
+
+
+ /* Fill in defaults if necessary */
+ if (session->community_len != SNMP_DEFAULT_COMMUNITY_LEN){
+ if( *session->community == '+' ) {
+ session->community_len--;
+ cp = (u_char *) calloc(1, (unsigned) session->community_len);
+ bcopy((char *) session->community+1, (char *)cp,
+ session->community_len);
+ session->version = SNMP_VERSION_2C;
+ } else {
+ cp = (u_char *) calloc(1, (unsigned) session->community_len);
+ bcopy((char *) session->community, (char *) cp,
+ session->community_len);
+ }
+
+ } else {
+ session->community_len = strlen(DEFAULT_COMMUNITY);
+ cp = (u_char *)calloc(1, (unsigned)session->community_len);
+ bcopy((char *)DEFAULT_COMMUNITY, (char *)cp, session->community_len);
+ }
+
+ /* Set up connections */
+ sd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sd < 0){
+ perror("socket");
+ snmp_errno = SNMPERR_GENERR;
+ if (!snmp_close(session)){
+ fprintf(stderr, "Couldn't abort session: %s. Exiting\n", api_errstring(snmp_errno));
+ exit(1);
+ }
+ return 0;
+ }
+ isp->sd = sd;
+
+ if (session->peername != SNMP_DEFAULT_PEERNAME){
+ if ((addr = inet_addr(session->peername)) != -1){
+ bcopy((char *)&addr, (char *)&isp->addr.sin_addr, sizeof(isp->addr.sin_addr));
+ } else {
+ hp = gethostbyname(session->peername);
+ if (hp == NULL){
+ fprintf(stderr, "unknown host: %s\n", session->peername);
+ snmp_errno = SNMPERR_BAD_ADDRESS;
+ if (!snmp_close(session)){
+ fprintf(stderr, "Couldn't abort session: %s. Exiting\n", api_errstring(snmp_errno));
+ exit(2);
+ }
+ return 0;
+ } else {
+ bcopy((char *)hp->h_addr, (char *)&isp->addr.sin_addr, hp->h_length);
+ }
+ }
+ isp->addr.sin_family = AF_INET;
+ if (session->remote_port == SNMP_DEFAULT_REMPORT){
+ /*servp = getservbyname("snmp", "udp");*/
+ servp=NULL;
+ if (servp != NULL){
+ isp->addr.sin_port = servp->s_port;
+ } else {
+ isp->addr.sin_port = htons(SNMP_PORT);
+ }
+ } else {
+ isp->addr.sin_port = htons(session->remote_port);
+ }
+ } else {
+ isp->addr.sin_addr.s_addr = SNMP_DEFAULT_ADDRESS;
+ }
+
+ me.sin_family = AF_INET;
+ me.sin_addr.s_addr = INADDR_ANY;
+ me.sin_port = htons(session->local_port);
+ if (bind(sd, (struct sockaddr *)&me, sizeof(me)) != 0){
+ perror("bind");
+ snmp_errno = SNMPERR_BAD_LOCPORT;
+ if (! snmp_close(session)){
+ fprintf(stderr, "Couldn't abort session: %s. Exiting\n",
+ api_errstring(snmp_errno));
+ exit(3);
+ }
+ return 0;
+ }
+
+ if( *cp == '/' ) {
+ session->authenticator = check_received_pkt;
+ sync_with_agent(session);
+ parse_app_community_string( session );
+ session->qoS |= USEC_QOS_GENREPORT;
+ }
+
+ /* replace comm pointer with pointer to new data: */
+ session->community = cp;
+
+ return session;
+}
+
+void
+sync_with_agent( session )
+struct snmp_session *session;
+{
+ struct snmp_pdu *pdu, *response = 0;
+ int status;
+
+ session->qoS = USEC_QOS_GENREPORT;
+ session->userLen = 6;
+ session->version = SNMP_VERSION_2;
+ strcpy( session->userName, "public" );
+
+ snmp_synch_setup(session);
+ pdu = snmp_pdu_create(GET_REQ_MSG);
+ status = snmp_synch_response(session, pdu, &response);
+
+ if (status == STAT_SUCCESS){
+ memcpy( session->agentID, response->params.agentID, 12 );
+
+ /* save the clocks -- even though they are not authentic */
+ session->agentBoots = response->params.agentBoots;
+ session->agentTime = response->params.agentTime;
+ session->agentClock = response->params.agentTime - time(NULL);
+
+ } else {
+ if (status == STAT_TIMEOUT){
+ printf("No Response from %s\n", session->peername);
+ } else { /* status == STAT_ERROR */
+ printf("An error occurred, Quitting\n");
+ }
+ exit( -1 );
+ }
+
+ /** freed to early:
+ snmp_free_pdu(pdu);
+ if (response) snmp_free_pdu(response);
+ **/
+}
+#if 1 /***/
+/*
+ * Unlink one element from input request list,
+ * then free it and it's pdu.
+ */
+static void
+free_one_request(isp, orp)
+ struct snmp_internal_session *isp;
+ struct request_list *orp;
+{
+ struct request_list *rp;
+ if(! orp) return;
+ if(isp->requests == orp){
+ isp->requests = orp->next_request; /* unlink head */
+ }
+ else{
+ for(rp = isp->requests; rp; rp = rp->next_request){
+ if(rp->next_request == orp){
+ rp->next_request = orp->next_request; /* unlink element */
+ break;
+ }
+ }
+ }
+ if (orp->pdu != NULL){
+ snmp_free_pdu(orp->pdu);
+ }
+ free((char *)orp);
+}
+#endif /***/
+/*
+ * Free each element in the input request list.
+ */
+static void
+free_request_list(rp)
+ struct request_list *rp;
+{
+ struct request_list *orp;
+
+ while(rp){
+ orp = rp;
+ rp = rp->next_request;
+ if (orp->pdu != NULL)
+ snmp_free_pdu(orp->pdu);
+ free((char *)orp);
+ }
+}
+
+/*
+ * Close the input session. Frees all data allocated for the session,
+ * dequeues any pending requests, and closes any sockets allocated for
+ * the session. Returns 0 on error, 1 otherwise.
+ */
+int
+snmp_close(session)
+ struct snmp_session *session;
+{
+ struct session_list *slp = NULL, *oslp = NULL;
+
+ if (Sessions->session == session){ /* If first entry */
+ slp = Sessions;
+ Sessions = slp->next;
+ } else {
+ for(slp = Sessions; slp; slp = slp->next){
+ if (slp->session == session){
+ if (oslp) /* if we found entry that points here */
+ oslp->next = slp->next; /* link around this entry */
+ break;
+ }
+ oslp = slp;
+ }
+ }
+ /* If we found the session, free all data associated with it */
+ if (slp){
+ if (slp->session->community != NULL)
+ free((char *)slp->session->community);
+ if(slp->session->peername != NULL)
+ free((char *)slp->session->peername);
+ free((char *)slp->session);
+ if (slp->internal->sd != -1)
+ close(slp->internal->sd);
+ free_request_list(slp->internal->requests);
+ free((char *)slp->internal);
+ free((char *)slp);
+ } else {
+ snmp_errno = SNMPERR_BAD_SESSION;
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Takes a session and a pdu and serializes the ASN PDU into the area
+ * pointed to by packet. out_length is the size of the data area available.
+ * Returns the length of the completed packet in out_length. If any errors
+ * occur, -1 is returned. If all goes well, 0 is returned.
+ */
+int
+snmp_build(session, pdu, packet, out_length, is_agent)
+ struct snmp_session *session;
+ struct snmp_pdu *pdu;
+ u_char *packet;
+ int *out_length;
+ int is_agent;
+{
+ u_char buf[PACKET_LENGTH];
+ u_char *cp;
+ struct variable_list *vp;
+ int length;
+ int totallength;
+
+ length = *out_length;
+ cp = packet;
+ for(vp = pdu->variables; vp; vp = vp->next_variable){
+ cp = snmp_build_var_op(cp, vp->name, &vp->name_length, vp->type, vp->val_len, (u_char *)vp->val.string, &length);
+ if (cp == NULL)
+ return -1;
+ }
+ totallength = cp - packet;
+
+ length = PACKET_LENGTH;
+ cp = asn_build_header(buf, &length, (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR), totallength);
+ if (cp == NULL)
+ return -1;
+ bcopy((char *)packet, (char *)cp, totallength);
+ totallength += cp - buf;
+
+ length = *out_length;
+ if (pdu->command != TRP_REQ_MSG){
+ /* request id */
+ cp = asn_build_int(packet, &length,
+ (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
+ (long *)&pdu->reqid, sizeof(pdu->reqid));
+ if (cp == NULL)
+ return -1;
+ /* error status */
+ cp = asn_build_int(cp, &length,
+ (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
+ (long *)&pdu->errstat, sizeof(pdu->errstat));
+ if (cp == NULL)
+ return -1;
+ /* error index */
+ cp = asn_build_int(cp, &length,
+ (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
+ (long *)&pdu->errindex, sizeof(pdu->errindex));
+ if (cp == NULL)
+ return -1;
+ } else { /* this is a trap message */
+ /* enterprise */
+ cp = asn_build_objid(packet, &length,
+ (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
+ (oid *)pdu->enterprise, pdu->enterprise_length);
+ if (cp == NULL)
+ return -1;
+ /* agent-addr */
+ cp = asn_build_string(cp, &length, (u_char)IPADDRESS,
+ (u_char *)&pdu->agent_addr.sin_addr.s_addr, sizeof(pdu->agent_addr.sin_addr.s_addr));
+ if (cp == NULL)
+ return -1;
+ /* generic trap */
+ cp = asn_build_int(cp, &length,
+ (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
+ (long *)&pdu->trap_type, sizeof(pdu->trap_type));
+ if (cp == NULL)
+ return -1;
+ /* specific trap */
+ cp = asn_build_int(cp, &length,
+ (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
+ (long *)&pdu->specific_type, sizeof(pdu->specific_type));
+ if (cp == NULL)
+ return -1;
+ /* timestamp */
+ cp = asn_build_int(cp, &length, (u_char)TIMETICKS,
+ (long *)&pdu->time, sizeof(pdu->time));
+ if (cp == NULL)
+ return -1;
+ }
+ if (length < totallength)
+ return -1;
+ bcopy((char *)buf, (char *)cp, totallength);
+ totallength += cp - packet;
+
+ length = PACKET_LENGTH;
+ cp = asn_build_header(buf, &length, (u_char)pdu->command, totallength);
+ if (cp == NULL)
+ return -1;
+ if (length < totallength)
+ return -1;
+ bcopy((char *)packet, (char *)cp, totallength);
+ totallength += cp - buf;
+
+ length = *out_length;
+
+ cp = snmp_auth_build( packet, &length, session, is_agent, totallength );
+ if (cp == NULL)
+ return -1;
+ if ((*out_length - (cp - packet)) < totallength)
+ return -1;
+ bcopy((char *)buf, (char *)cp, totallength);
+ totallength += cp - packet;
+ *out_length = totallength;
+
+ if( session->qoS & USEC_QOS_AUTH )
+ md5Digest( packet, totallength, cp - (session->contextLen + 16),
+ cp - (session->contextLen + 16) );
+
+ return 0;
+}
+
+/*
+ * Parses the packet received on the input session, and places the data into
+ * the input pdu. length is the length of the input packet. If any errors
+ * are encountered, -1 is returned. Otherwise, a 0 is returned.
+ */
+static int
+snmp_parse(session, pdu, data, length)
+ struct snmp_session *session;
+ struct snmp_pdu *pdu;
+ u_char *data;
+ int length;
+{
+ u_char msg_type;
+ u_char type;
+ u_char *var_val;
+ long version;
+ int len, four;
+ u_char community[256];
+ int community_length = 256;
+ struct variable_list *vp = 0;
+ oid objid[MAX_NAME_LEN], *op;
+ u_char *origdata = data;
+ int origlength = length;
+ int ret = 0;
+ u_char *save_data;
+
+ /* authenticates message and returns length if valid */
+ data = snmp_auth_parse(data, &length, community, &community_length, &version);
+ if (data == NULL)
+ return -1;
+
+ if( version != SNMP_VERSION_1 && version != SNMP_VERSION_2C && version != SNMP_VERSION_2 ) {
+ fprintf(stderr, "Wrong version: %ld\n", version);
+ return -1;
+ }
+
+ save_data = data;
+
+ data = asn_parse_header(data, &length, &msg_type);
+ if (data == NULL)
+ return -1;
+ pdu->command = msg_type;
+
+ if( session->authenticator ) {
+ ret = session->authenticator( origdata, origlength, save_data - community_length,
+ community_length, session, pdu );
+ if( ret < 0 ) return ret;
+ }
+
+ if (pdu->command != TRP_REQ_MSG){
+ data = asn_parse_int(data, &length, &type, (long *)&pdu->reqid, sizeof(pdu->reqid));
+ if (data == NULL)
+ return -1;
+ data = asn_parse_int(data, &length, &type, (long *)&pdu->errstat, sizeof(pdu->errstat));
+ if (data == NULL)
+ return -1;
+ data = asn_parse_int(data, &length, &type, (long *)&pdu->errindex, sizeof(pdu->errindex));
+ if (data == NULL)
+ return -1;
+ } else {
+ pdu->enterprise_length = MAX_NAME_LEN;
+ data = asn_parse_objid(data, &length, &type, objid, &pdu->enterprise_length);
+ if (data == NULL)
+ return -1;
+ pdu->enterprise = (oid *)calloc(1, pdu->enterprise_length * sizeof(oid));
+ bcopy((char *)objid, (char *)pdu->enterprise, pdu->enterprise_length * sizeof(oid));
+
+ four = 4;
+ data = asn_parse_string(data, &length, &type, (u_char *)&pdu->agent_addr.sin_addr.s_addr, &four);
+ if (data == NULL)
+ return -1;
+ data = asn_parse_int(data, &length, &type, (long *)&pdu->trap_type, sizeof(pdu->trap_type));
+ if (data == NULL)
+ return -1;
+ data = asn_parse_int(data, &length, &type, (long *)&pdu->specific_type, sizeof(pdu->specific_type));
+ if (data == NULL)
+ return -1;
+ data = asn_parse_int(data, &length, &type, (long *)&pdu->time, sizeof(pdu->time));
+ if (data == NULL)
+ return -1;
+ }
+ data = asn_parse_header(data, &length, &type);
+ if (data == NULL)
+ return -1;
+ if (type != (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR))
+ return -1;
+ while((int)length > 0){
+ if (pdu->variables == NULL){
+ pdu->variables = vp = (struct variable_list *)calloc(1, sizeof(struct variable_list));
+ } else {
+ vp->next_variable = (struct variable_list *)calloc(1, sizeof(struct variable_list));
+ vp = vp->next_variable;
+ }
+ vp->next_variable = NULL;
+ vp->val.string = NULL;
+ vp->name = NULL;
+ vp->name_length = MAX_NAME_LEN;
+ data = snmp_parse_var_op(data, objid, &vp->name_length, &vp->type, &vp->val_len, &var_val, (int *)&length);
+ if (data == NULL)
+ return -1;
+ op = (oid *)calloc(1, (unsigned)vp->name_length * sizeof(oid));
+ bcopy((char *)objid, (char *)op, vp->name_length * sizeof(oid));
+ vp->name = op;
+
+ len = PACKET_LENGTH;
+ switch((short)vp->type){
+ case ASN_INTEGER:
+ vp->val.integer = (long *)calloc(1, sizeof(long));
+ vp->val_len = sizeof(long);
+ asn_parse_int(var_val, &len, &vp->type, (long *)vp->val.integer, sizeof(vp->val.integer));
+ break;
+ case COUNTER:
+ case GAUGE:
+ case TIMETICKS:
+ case UINTEGER:
+ vp->val.integer = (long *)calloc(1, sizeof(unsigned long));
+ vp->val_len = sizeof(unsigned long);
+ asn_parse_unsigned_int(var_val, &len, &vp->type, (unsigned long *)vp->val.integer, sizeof(vp->val.integer));
+ break;
+ case COUNTER64:
+ vp->val.counter64 = (struct counter64 *)calloc(1, sizeof(struct counter64) );
+ vp->val_len = sizeof(struct counter64);
+ asn_parse_unsigned_int64(var_val, &len, &vp->type,
+ (struct counter64 *)vp->val.counter64,
+ sizeof(*vp->val.counter64));
+ break;
+ case ASN_OCTET_STR:
+ case IPADDRESS:
+ case OPAQUE:
+ case NSAP:
+ vp->val.string = (u_char *)calloc(1, (unsigned)vp->val_len);
+ asn_parse_string(var_val, &len, &vp->type, vp->val.string, &vp->val_len);
+ break;
+ case ASN_OBJECT_ID:
+ vp->val_len = MAX_NAME_LEN;
+ asn_parse_objid(var_val, &len, &vp->type, objid, &vp->val_len);
+ vp->val_len *= sizeof(oid);
+ vp->val.objid = (oid *)calloc(1, (unsigned)vp->val_len);
+ bcopy((char *)objid, (char *)vp->val.objid, vp->val_len);
+ break;
+ case SNMP_NOSUCHOBJECT:
+ case SNMP_NOSUCHINSTANCE:
+ case SNMP_ENDOFMIBVIEW:
+ case ASN_NULL:
+ break;
+ default:
+ fprintf(stderr, "bad type returned (%x)\n", vp->type);
+ break;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Sends the input pdu on the session after calling snmp_build to create
+ * a serialized packet. If necessary, set some of the pdu data from the
+ * session defaults. Add a request corresponding to this pdu to the list
+ * of outstanding requests on this session, then send the pdu.
+ * Returns the request id of the generated packet if applicable, otherwise 1.
+ * On any error, 0 is returned.
+ * The pdu is freed by snmp_send() unless a failure occured.
+ */
+int
+snmp_send(session, pdu)
+ struct snmp_session *session;
+ struct snmp_pdu *pdu;
+{
+ struct session_list *slp;
+ struct snmp_internal_session *isp = NULL;
+ u_char packet[PACKET_LENGTH];
+ int length = PACKET_LENGTH;
+ struct request_list *rp;
+ struct timeval tv;
+
+ for(slp = Sessions; slp; slp = slp->next){
+ if (slp->session == session){
+ isp = slp->internal;
+ break;
+ }
+ }
+
+ if (! pdu) {
+ snmp_errno = SNMPERR_GENERR;
+ return 0;
+ }
+
+ if (isp == NULL){
+ snmp_errno = SNMPERR_BAD_SESSION;
+ return 0;
+ }
+ if (pdu->command == GET_REQ_MSG || pdu->command == GETNEXT_REQ_MSG
+ || pdu->command == GET_RSP_MSG || pdu->command == SET_REQ_MSG
+ || pdu->command == GETBULK_REQ_MSG ){
+ if (pdu->reqid == SNMP_DEFAULT_REQID)
+ pdu->reqid = ++Reqid;
+ if (pdu->errstat == SNMP_DEFAULT_ERRSTAT)
+ pdu->errstat = 0;
+ if (pdu->errindex == SNMP_DEFAULT_ERRINDEX)
+ pdu->errindex = 0;
+ } else {
+ /* fill in trap defaults */
+ pdu->reqid = 1; /* give a bogus non-error reqid for traps */
+ if (pdu->enterprise_length == SNMP_DEFAULT_ENTERPRISE_LENGTH){
+ pdu->enterprise = (oid *)calloc(1, sizeof(DEFAULT_ENTERPRISE));
+ bcopy((char *)DEFAULT_ENTERPRISE, (char *)pdu->enterprise, sizeof(DEFAULT_ENTERPRISE));
+ pdu->enterprise_length = sizeof(DEFAULT_ENTERPRISE)/sizeof(oid);
+ }
+ if (pdu->time == SNMP_DEFAULT_TIME)
+ pdu->time = DEFAULT_TIME;
+ }
+ if (pdu->address.sin_addr.s_addr == SNMP_DEFAULT_ADDRESS){
+ if (isp->addr.sin_addr.s_addr != SNMP_DEFAULT_ADDRESS){
+ bcopy((char *)&isp->addr, (char *)&pdu->address, sizeof(pdu->address));
+ } else {
+ fprintf(stderr, "No remote IP address specified\n");
+ snmp_errno = SNMPERR_BAD_ADDRESS;
+ return 0;
+ }
+ }
+
+
+ if (snmp_build(session, pdu, packet, &length, 0) < 0){
+ fprintf(stderr, "Error building packet\n");
+ snmp_errno = SNMPERR_GENERR;
+ return 0;
+ }
+#if 1 /***/
+ if (snmp_dump_packet){
+ snmp_print_packet(packet, length, pdu->address, 1);
+ }
+#endif /***/
+
+ gettimeofday(&tv, (struct timezone *)0);
+ if (sendto(isp->sd, (char *)packet, length, 0, (struct sockaddr *)&pdu->address, sizeof(pdu->address)) < 0){
+ perror("sendto");
+ snmp_errno = SNMPERR_GENERR;
+ return 0;
+ }
+ if (pdu->command == GET_REQ_MSG || pdu->command == GETNEXT_REQ_MSG
+ || pdu->command == SET_REQ_MSG || pdu->command == GETBULK_REQ_MSG ){
+ /* set up to expect a response */
+ rp = (struct request_list *)calloc(1, sizeof(struct request_list));
+#if 1 /***/
+ if(! rp){
+ fprintf(stderr, "Out of memory!\n");
+ snmp_errno = SNMPERR_GENERR;
+ return 0;
+ }
+#endif /***/
+ rp->next_request = isp->requests;
+ isp->requests = rp;
+ rp->pdu = pdu;
+ rp->request_id = pdu->reqid;
+
+ rp->retries = 1;
+ rp->timeout = session->timeout;
+ rp->time = tv;
+ tv.tv_usec += rp->timeout;
+ tv.tv_sec += tv.tv_usec / 1000000L;
+ tv.tv_usec %= 1000000L;
+ rp->expire = tv;
+#if DEBUG_SNMPTRACE
+ snmp_print_trace(slp, rp, TRACE_SEND);
+#endif
+ }
+ return pdu->reqid;
+}
+
+/*
+ * Frees the pdu and any malloc'd data associated with it.
+ */
+void
+snmp_free_pdu(struct snmp_pdu * pdu)
+{
+ struct variable_list *vp, *ovp;
+
+ if (! pdu)
+ return;
+
+ vp = pdu->variables;
+ while(vp){
+ if (vp->name) {
+ free((char *)vp->name);
+ }
+ if (vp->val.string) {
+ free((char *)vp->val.string);
+ }
+ ovp = vp;
+ vp = vp->next_variable;
+ free((char *)ovp);
+ }
+ if (pdu->enterprise) {
+ free((char *)pdu->enterprise);
+ }
+ free((char *)pdu);
+}
+
+
+/*
+ * Checks to see if any of the fd's set in the fdset belong to
+ * snmp. Each socket with it's fd set has a packet read from it
+ * and snmp_parse is called on the packet received. The resulting pdu
+ * is passed to the callback routine for that session. If the callback
+ * routine returns successfully, the pdu and it's request are deleted.
+ */
+void
+snmp_read(fdset)
+ fd_set *fdset;
+{
+ struct session_list *slp;
+ struct snmp_session *sp;
+ struct snmp_internal_session *isp;
+ u_char packet[PACKET_LENGTH];
+ struct sockaddr_in from;
+ ssize_t length, fromlength;
+ struct snmp_pdu *pdu;
+ struct request_list *rp /**, *orp **/ ;
+
+ for(slp = Sessions; slp; slp = slp->next){
+ if (FD_ISSET(slp->internal->sd, fdset)){
+ sp = slp->session;
+ isp = slp->internal;
+ fromlength = sizeof(from);
+ length = recvfrom(isp->sd, (char *)packet, PACKET_LENGTH, 0, (struct sockaddr *)&from, &fromlength);
+ if (length == -1) {
+ perror("recvfrom");
+ return;
+ }
+#if 1 /***/
+ if (snmp_dump_packet){
+ snmp_print_packet(packet, length, from, 0);
+ }
+ pdu = (struct snmp_pdu *)calloc(1, sizeof(struct snmp_pdu));
+
+ if(! pdu){
+ fprintf(stderr, "Out of memory!\n");
+ snmp_errno = SNMPERR_GENERR;
+ return;
+ }
+#endif /***/
+
+ pdu->address = from;
+ pdu->reqid = 0;
+ pdu->variables = NULL;
+ pdu->enterprise = NULL;
+ pdu->enterprise_length = 0;
+ if (snmp_parse(sp, pdu, packet, length) < 0){
+ fprintf(stderr, "Mangled packet\n");
+ snmp_free_pdu(pdu);
+ return;
+ }
+
+ if (pdu->command == GET_RSP_MSG || pdu->command == REPORT_MSG){
+#if 1 /***/ /* bug fix: illegal re-use of rp killed some requests */
+ struct request_list *rp_next= 0;
+ for(rp = isp->requests; rp; rp = rp_next){
+ rp_next = rp->next_request;
+ if (rp->request_id == pdu->reqid){
+#if DEBUG_SNMPTRACE
+ snmp_print_trace(slp, rp, TRACE_RECV);
+#endif
+ if (sp->callback(RECEIVED_MESSAGE, sp, pdu->reqid, pdu, sp->callback_magic) == 1){
+ /* successful, so delete request */
+ free_one_request(isp, rp);
+ break; /* no more request with the same reqid */
+ }
+ }
+ }
+#endif /***/
+ } else if (pdu->command == GET_REQ_MSG
+ || pdu->command == GETNEXT_REQ_MSG
+ || pdu->command == GETBULK_REQ_MSG
+ || pdu->command == TRP_REQ_MSG || pdu->command == SET_REQ_MSG){
+#if DEBUG_SNMPTRACE
+ snmp_print_trace(slp, NULL, TRACE_RECV);
+#endif
+ sp->callback(RECEIVED_MESSAGE, sp, pdu->reqid, pdu, sp->callback_magic);
+ }
+ snmp_free_pdu(pdu);
+ }
+ }
+}
+
+/*
+ * Returns info about what snmp requires from a select statement.
+ * numfds is the number of fds in the list that are significant.
+ * All file descriptors opened for SNMP are OR'd into the fdset.
+ * If activity occurs on any of these file descriptors, snmp_read
+ * should be called with that file descriptor set
+ *
+ * The timeout is the latest time that SNMP can wait for a timeout. The
+ * select should be done with the minimum time between timeout and any other
+ * timeouts necessary. This should be checked upon each invocation of select.
+ * If a timeout is received, snmp_timeout should be called to check if the
+ * timeout was for SNMP. (snmp_timeout is idempotent)
+ *
+ * Block is 1 if the select is requested to block indefinitely, rather
+ * than time out. If block is input as 1, the timeout value will be
+ * treated as undefined, but it must be available for setting in
+ * snmp_select_info. On return, if block is true, the value of
+ * timeout will be undefined.
+ *
+ * snmp_select_info returns the number of open sockets. (i.e. The
+ * number of sessions open)
+ */
+int
+snmp_select_info(numfds, fdset, timeout, block)
+ int *numfds;
+ fd_set *fdset;
+ struct timeval *timeout;
+ int *block; /* should the select block until input arrives (i.e. no input) */
+{
+ struct session_list *slp;
+ struct snmp_internal_session *isp;
+ struct request_list *rp;
+ struct timeval now, earliest;
+ int active = 0, requests = 0;
+
+ timerclear(&earliest);
+ /*
+ * For each request outstanding, add it's socket to the fdset,
+ * and if it is the earliest timeout to expire, mark it as lowest.
+ */
+ for(slp = Sessions; slp; slp = slp->next){
+ active++;
+ isp = slp->internal;
+ if ((isp->sd + 1) > *numfds)
+ *numfds = (isp->sd + 1);
+ FD_SET(isp->sd, fdset);
+ if (isp->requests){
+ /* found another session with outstanding requests */
+ requests++;
+ for(rp = isp->requests; rp; rp = rp->next_request){
+ if (!timerisset(&earliest) || timercmp(&rp->expire, &earliest, <))
+ earliest = rp->expire;
+ }
+ }
+ }
+ if (requests == 0) /* if none are active, skip arithmetic */
+ return active;
+
+ /*
+ * Now find out how much time until the earliest timeout. This
+ * transforms earliest from an absolute time into a delta time, the
+ * time left until the select should timeout.
+ */
+ gettimeofday(&now, (struct timezone *)0);
+ earliest.tv_sec--; /* adjust time to make arithmetic easier */
+ earliest.tv_usec += 1000000L;
+ earliest.tv_sec -= now.tv_sec;
+ earliest.tv_usec -= now.tv_usec;
+ while (earliest.tv_usec >= 1000000L){
+ earliest.tv_usec -= 1000000L;
+ earliest.tv_sec += 1;
+ }
+ if (earliest.tv_sec < 0){
+ earliest.tv_sec = 0;
+ earliest.tv_usec = 0;
+ }
+
+ /* if it was blocking before or our delta time is less, reset timeout */
+ if (*block == 1 || timercmp(&earliest, timeout, <)){
+ *timeout = earliest;
+ *block = 0;
+ }
+ return active;
+}
+
+/*
+ * snmp_timeout should be called whenever the timeout from
+ * snmp_select_info expires, but it is idempotent, so snmp_timeout can
+ * be polled (probably a cpu expensive proposition). snmp_timeout
+ * checks to see if any of the sessions have an outstanding request
+ * that has timed out. If it finds one (or more), and that pdu has
+ * more retries available, a new packet is formed from the pdu and is
+ * resent. If there are no more retries available, the callback for
+ * the session is used to alert the user of the timeout.
+ */
+void
+snmp_timeout()
+{
+ struct session_list *slp;
+ struct snmp_session *sp;
+ struct snmp_internal_session *isp;
+#if 1 /***/ /* replacing freeme by simpler and safer rp_next*/
+ struct request_list *rp, *rp_next = 0;
+#endif /***/
+ struct timeval now;
+
+ gettimeofday(&now, (struct timezone *)0);
+ /*
+ * For each request outstanding, check to see if it has expired.
+ */
+ for(slp = Sessions; slp; slp = slp->next){
+ sp = slp->session;
+ isp = slp->internal;
+#if 1 /***/ /* simplification */
+ for(rp = isp->requests; rp; rp = rp_next){
+ rp_next = rp->next_request;
+ if (timercmp(&rp->expire, &now, <)){
+ /* this timer has expired */
+ if (rp->retries >= sp->retries){
+#if DEBUG_SNMPTRACE
+ snmp_print_trace(slp, rp, TRACE_TIMEOUT);
+#endif
+ /* No more chances, delete this entry */
+ sp->callback(TIMED_OUT, sp, rp->pdu->reqid, rp->pdu, sp->callback_magic);
+ free_one_request(isp, rp);
+ continue;
+#endif /***/
+ } else {
+ u_char packet[PACKET_LENGTH];
+ int length = PACKET_LENGTH;
+ struct timeval tv;
+
+ /* retransmit this pdu */
+ rp->retries++;
+ rp->timeout <<= 1;
+ if (snmp_build(sp, rp->pdu, packet, &length, 0) < 0){
+ fprintf(stderr, "Error building packet\n");
+ }
+#if 1 /***/
+ if (snmp_dump_packet){
+ snmp_print_packet(packet, length, rp->pdu->address, 1);
+ }
+#endif /***/
+ gettimeofday(&tv, (struct timezone *)0);
+ if (sendto(isp->sd, (char *)packet, length, 0, (struct sockaddr *)&rp->pdu->address, sizeof(rp->pdu->address)) < 0){
+ perror("sendto");
+ }
+ rp->time = tv;
+ tv.tv_usec += rp->timeout;
+ tv.tv_sec += tv.tv_usec / 1000000L;
+ tv.tv_usec %= 1000000L;
+ rp->expire = tv;
+#if DEBUG_SNMPTRACE
+ snmp_print_trace(slp, rp, TRACE_SEND);
+#endif
+ }
+ }
+#if 1 /***/ /* simplification */
+ }
+#endif /***/
+ }
+}
+
+