From: kostas <> Date: Fri, 21 Nov 1997 07:40:34 +0000 (+0000) Subject: SNMP, cmu derived, heavily modified X-Git-Tag: SQUID_3_0_PRE1~4503 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=627f6d02e9baad40c38bd35ef893fc6bc9a22ca1;p=thirdparty%2Fsquid.git SNMP, cmu derived, heavily modified --- diff --git a/snmplib/Makefile.in b/snmplib/Makefile.in new file mode 100644 index 0000000000..498fe40f78 --- /dev/null +++ b/snmplib/Makefile.in @@ -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 index 0000000000..55d60721e1 --- /dev/null +++ b/snmplib/asn1.c @@ -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 +#include +#include +#endif + +#include +#include + +#ifdef vms +#include +#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 index 0000000000..98a0f8a908 --- /dev/null +++ b/snmplib/mib.c @@ -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 +#include +#include +#include +#include + +#include "squid.h" + +#ifdef linux +#include +#include +#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 : + * + * [...] 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 index 0000000000..dc1b239b1d --- /dev/null +++ b/snmplib/parse.c @@ -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 +#include +#include +#ifdef linux +#include +#include +#endif +#include + + +#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 index 0000000000..65646b1c7f --- /dev/null +++ b/snmplib/snmp_api.c @@ -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 +#include +#include +#include +#include +#include +#include +#ifdef linux +#include +#include +#include +#include +#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 /***/ + } +} + +