]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
SNMP, cmu derived, heavily modified
authorkostas <>
Fri, 21 Nov 1997 07:40:34 +0000 (07:40 +0000)
committerkostas <>
Fri, 21 Nov 1997 07:40:34 +0000 (07:40 +0000)
snmplib/Makefile.in [new file with mode: 0644]
snmplib/asn1.c [new file with mode: 0644]
snmplib/mib.c [new file with mode: 0644]
snmplib/parse.c [new file with mode: 0644]
snmplib/snmp_api.c [new file with mode: 0644]

diff --git a/snmplib/Makefile.in b/snmplib/Makefile.in
new file mode 100644 (file)
index 0000000..498fe40
--- /dev/null
@@ -0,0 +1,82 @@
+##
+## 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:
+
diff --git a/snmplib/asn1.c b/snmplib/asn1.c
new file mode 100644 (file)
index 0000000..55d6072
--- /dev/null
@@ -0,0 +1,994 @@
+/*
+ * 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;
+}
+
+
diff --git a/snmplib/mib.c b/snmplib/mib.c
new file mode 100644 (file)
index 0000000..98a0f8a
--- /dev/null
@@ -0,0 +1,1248 @@
+/**********************************************************************
+       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
diff --git a/snmplib/parse.c b/snmplib/parse.c
new file mode 100644 (file)
index 0000000..dc1b239
--- /dev/null
@@ -0,0 +1,1795 @@
+/******************************************************************
+       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;
+}
diff --git a/snmplib/snmp_api.c b/snmplib/snmp_api.c
new file mode 100644 (file)
index 0000000..65646b1
--- /dev/null
@@ -0,0 +1,1153 @@
+/***********************************************************
+       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 /***/
+    }
+}
+
+