Mercurial > flash_v2
diff packages/net/snmp/lib/current/src/asn1.c @ 103:95f3e12a6327 ecos-sw-2000-06-23
Merge from eCos master repository on 2000-06-23-16:41:10-BST
| author | jlarmour |
|---|---|
| date | Fri, 23 Jun 2000 17:06:31 +0000 |
| parents | |
| children | e0c0827131d1 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/net/snmp/lib/current/src/asn1.c @@ -0,0 +1,1952 @@ +//========================================================================== +// +// ./lib/current/src/asn1.c +// +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Red Hat eCos Public License +// Version 1.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://www.redhat.com/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Configurable Operating System, +// released September 30, 1998. +// +// The Initial Developer of the Original Code is Red Hat. +// Portions created by Red Hat are +// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//####UCDSNMPCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from the UCD-SNMP +// project, <http://ucd-snmp.ucdavis.edu/> from the University of +// California at Davis, which was originally based on the Carnegie Mellon +// University SNMP implementation. Portions of this software are therefore +// covered by the appropriate copyright disclaimers included herein. +// +// The release used was version 4.1.2 of May 2000. "ucd-snmp-4.1.2" +// ------------------------------------------- +// +//####UCDSNMPCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): hmt +// Contributors: hmt +// Date: 2000-05-30 +// Purpose: Port of UCD-SNMP distribution to eCos. +// Description: +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== +/******************************************************************** + Copyright 1989, 1991, 1992 by Carnegie Mellon University + + Derivative Work - +Copyright 1996, 1998, 1999, 2000 The Regents of the University of California + + 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 appears in all copies and +that both that copyright notice and this permission notice appear in +supporting documentation, and that the name of CMU and The Regents of +the University of California not be used in advertising or publicity +pertaining to distribution of the software without specific written +permission. + +CMU AND THE REGENTS OF THE UNIVERSITY OF CALIFORNIA DISCLAIM ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CMU OR +THE REGENTS OF THE UNIVERSITY OF CALIFORNIA BE LIABLE FOR ANY SPECIAL, +INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +FROM THE 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. +*********************************************************************/ +/* + * 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. +******************************************************************/ +#include <config.h> + +#ifdef KINETICS +#include "gw.h" +#endif + +#if HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif + +#include <sys/types.h> +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#if HAVE_WINSOCK_H +#include <winsock.h> +#endif +#if HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif + +#ifdef vms +#include <in.h> +#endif + +#if HAVE_DMALLOC_H +#include <dmalloc.h> +#endif + +#include "asn1.h" +#include "int64.h" +#include "snmp_debug.h" +#include "mib.h" + +#ifndef NULL +#define NULL 0 +#endif + +#include "snmp_api.h" +#include "snmp_impl.h" /* to define ERROR_MSG */ + +static +void _asn_size_err(const char *str, size_t wrongsize, size_t rightsize) +{ + char ebuf[128]; + + sprintf(ebuf,"%s size %d: s/b %d",str, wrongsize, rightsize); + ERROR_MSG(ebuf); +} + +static +void _asn_length_err(const char *str, size_t wrongsize, size_t rightsize) +{ + char ebuf[128]; + + sprintf(ebuf,"%s length %d too large: exceeds %d",str, wrongsize, rightsize); + ERROR_MSG(ebuf); +} + +/* + * call after asn_parse_length to verify result. + */ +static +int _asn_parse_length_check(const char *str, + u_char *bufp, u_char *data, + u_long plen, size_t dlen) +{ + char ebuf[128]; + size_t header_len; + + if (bufp == NULL){ + /* error message is set */ + return 1; + } + header_len = bufp - data; + if (((size_t)plen + header_len) > dlen){ + sprintf(ebuf, "%s: message overflow: %d len + %d delta > %d len", + str, (int)plen, (int)header_len, (int)dlen); + ERROR_MSG(ebuf); + return 1; + } + return 0; +} + +/* + * call after asn_build_header to verify result. + */ +static +int _asn_build_header_check(const char *str, u_char *data, + size_t datalen, size_t typedlen) +{ + char ebuf[128]; + + if (data == NULL){ + /* error message is set */ + return 1; + } + if (datalen < typedlen){ + sprintf(ebuf, "%s: bad header, length too short: %d < %d", str, datalen, typedlen); + ERROR_MSG(ebuf); + return 1; + } + return 0; +} + +/* checks the incoming packet for validity and returns its size or 0 */ +int +asn_check_packet (u_char *pkt, size_t len) +{ + u_long asn_length; + + if (len < 2) + return 0; /* always too short */ + + if (*pkt != (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR)) + return -1; /* wrong type */ + + if (*(pkt+1) & 0x80) { + /* long length */ + if ((int)len < (int)(*(pkt+1) & ~0x80)+2) + return 0; /* still to short, incomplete length */ + asn_parse_length(pkt+1, &asn_length); + return (asn_length + 2 + (*(pkt+1) & ~0x80)); + } else { + /* short length */ + return (*(pkt+1) + 2); + } +} + +static +int _asn_bitstring_check(const char * str, u_long asn_length, u_char datum) +{ + char ebuf[128]; + + if (asn_length < 1){ + sprintf(ebuf,"%s: length %d too small", str, (int)asn_length); + ERROR_MSG(ebuf); + return 1; + } + if (datum > 7){ + sprintf(ebuf,"%s: datum %d >7: too large", str, (int)(datum)); + ERROR_MSG(ebuf); + return 1; + } + return 0; +} + +/* + * 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( + 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 +*/ + +u_char * +asn_parse_int(u_char *data, + size_t *datalength, + u_char *type, + long *intp, + size_t intsize) +{ +/* + * ASN.1 integer ::= 0x02 asnlength byte {byte}* + */ + static const char *errpre = "parse int"; + register u_char *bufp = data; + u_long asn_length; + register long value = 0; + + if (intsize != sizeof (long)){ + _asn_size_err(errpre, intsize, sizeof(long)); + return NULL; + } + *type = *bufp++; + bufp = asn_parse_length(bufp, &asn_length); + if (_asn_parse_length_check(errpre, bufp, data, asn_length, *datalength)) + return NULL; + + if ((size_t)asn_length > intsize){ + _asn_length_err(errpre, (size_t)asn_length, intsize); + return NULL; + } + + *datalength -= (int)asn_length + (bufp - data); + if (*bufp & 0x80) + value = -1; /* integer is negative */ + + DEBUGDUMPSETUP("dump_recv", data, bufp - data + asn_length); + + while(asn_length--) + value = (value << 8) | *bufp++; + + DEBUGMSG(("dump_recv", " ASN Integer:\t%ld (0x%.2X)\n", value, value)); + + *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( + 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 + */ +u_char * +asn_parse_unsigned_int(u_char *data, + size_t *datalength, + u_char *type, + u_long *intp, + size_t intsize) +{ +/* + * ASN.1 integer ::= 0x02 asnlength byte {byte}* + */ + static const char *errpre = "parse uint"; + register u_char *bufp = data; + u_long asn_length; + register u_long value = 0; + + if (intsize != sizeof (long)){ + _asn_size_err(errpre, intsize, sizeof(long)); + return NULL; + } + *type = *bufp++; + bufp = asn_parse_length(bufp, &asn_length); + if (_asn_parse_length_check(errpre, bufp, data, asn_length, *datalength)) + return NULL; + + if (((int)asn_length > (intsize + 1)) || + (((int)asn_length == intsize + 1) && *bufp != 0x00)){ + _asn_length_err(errpre, (size_t)asn_length, intsize); + return NULL; + } + *datalength -= (int)asn_length + (bufp - data); + if (*bufp & 0x80) + value = ~value; /* integer is negative */ + + DEBUGDUMPSETUP("dump_recv", data, bufp - data + asn_length); + + while(asn_length--) + value = (value << 8) | *bufp++; + + DEBUGMSG(("dump_recv", " ASN UInteger:\t%ld (0x%.2X)\n", value, value)); + + *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( + u_char *data IN - pointer to start of output buffer + int *datalength IN/OUT - number of valid bytes left in buffer + int type IN - asn type of object + long *intp IN - pointer to start of long integer + int intsize IN - size of input buffer + */ +u_char * +asn_build_int(u_char *data, + size_t *datalength, + u_char type, + long *intp, + size_t intsize) +{ +/* + * ASN.1 integer ::= 0x02 asnlength byte {byte}* + */ + static const char *errpre = "build int"; + register long integer; + register u_long mask; + + if (intsize != sizeof (long)){ + _asn_size_err(errpre, intsize, sizeof(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 = ((u_long) 0x1FF) << ((8 * (sizeof(long) - 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 (_asn_build_header_check(errpre,data,*datalength,intsize)) + return NULL; + + *datalength -= intsize; + mask = ((u_long) 0xFF) << (8 * (sizeof(long) - 1)); + /* mask is 0xFF000000 on a big-endian machine */ + while(intsize--){ + *data++ = (u_char)((integer & mask) >> (8 * (sizeof(long) - 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( + 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 input buffer + */ +u_char * +asn_build_unsigned_int(u_char *data, + size_t *datalength, + u_char type, + u_long *intp, + size_t intsize) +{ +/* + * ASN.1 integer ::= 0x02 asnlength byte {byte}* + */ + static const char *errpre = "build uint"; + register u_long integer; + register u_long mask; + int add_null_byte = 0; + + if (intsize != sizeof (long)){ + _asn_size_err(errpre, intsize, sizeof(long)); + return NULL; + } + integer = *intp; + mask = ((u_long) 0xFF) << (8 * (sizeof(long) - 1)); + /* mask is 0xFF000000 on a big-endian machine */ + if ((u_char)((integer & mask) >> (8 * (sizeof(long) - 1))) & 0x80){ + /* if MSB is set */ + add_null_byte = 1; + intsize++; + } else { + /* + * 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 = ((u_long) 0x1FF) << ((8 * (sizeof(long) - 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 (_asn_build_header_check(errpre,data,*datalength,intsize)) + return NULL; + + *datalength -= intsize; + if (add_null_byte == 1){ + *data++ = '\0'; + intsize--; + } + mask = ((u_long) 0xFF) << (8 * (sizeof(long) - 1)); + /* mask is 0xFF000000 on a big-endian machine */ + while(intsize--){ + *data++ = (u_char)((integer & mask) >> (8 * (sizeof(long) - 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( + * 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 * +asn_parse_string(u_char *data, + size_t *datalength, + u_char *type, + u_char *string, + size_t *strlength) +{ + static const char *errpre = "parse string"; + u_char *bufp = data; + u_long asn_length; + + *type = *bufp++; + bufp = asn_parse_length(bufp, &asn_length); + if (_asn_parse_length_check(errpre, bufp, data, asn_length, *datalength)) + return NULL; + + if ((int)asn_length > *strlength){ + _asn_length_err(errpre, (size_t)asn_length, *strlength); + return NULL; + } + + DEBUGDUMPSETUP("dump_recv", data, bufp - data + asn_length); + + memmove(string, bufp, asn_length); + if (*strlength > (int)asn_length) + string[asn_length] = 0; + *strlength = (int)asn_length; + *datalength -= (int)asn_length + (bufp - data); + + DEBUGIF("dump_recv") { + char *buf = (char *)malloc(1+asn_length); + sprint_asciistring(buf, string, asn_length); + DEBUGMSG(("dump_recv", " ASN String:\t%s\n", buf)); + free (buf); + } + + 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( + 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 + u_char *string IN - pointer to start of input buffer + int strlength IN - size of input buffer + */ +u_char * +asn_build_string(u_char *data, + size_t *datalength, + u_char type, + const u_char *string, + size_t strlength) +{ +/* + * 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 (_asn_build_header_check("build string", data, *datalength, strlength)) + return NULL; + + if (strlength) { + if (string == NULL) { + memset(data, 0, strlength); + } else { + memmove(data, string, 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( + 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 * +asn_parse_header(u_char *data, + size_t *datalength, + u_char *type) +{ + register u_char *bufp; + u_long asn_length; + + if (!data || !datalength || !type) { + ERROR_MSG("parse header: NULL pointer"); + return NULL; + } + bufp = data; + /* this only works on data types < 30, i.e. no extension octets */ + if (IS_EXTENSION_ID(*bufp)){ + ERROR_MSG("can't process ID >= 30"); + return NULL; + } + *type = *bufp; + bufp = asn_parse_length(bufp + 1, &asn_length); + if (_asn_parse_length_check("parse header", bufp, data, asn_length, *datalength)) + return NULL; + + DEBUGDUMPSETUP("dump_recv", data, (bufp-data)); + DEBUGMSG(("dump_recv", " ASN Header: 0x%.2X, len = %d (0x%X)\n", *data, + asn_length, asn_length)); + +#ifdef OPAQUE_SPECIAL_TYPES + + if ((*type == ASN_OPAQUE) && + (*bufp == ASN_OPAQUE_TAG1)) { + DEBUGINDENTMORE(); + DEBUGDUMPSETUP("dump_recv", data, 1); + DEBUGMSG(("dump_recv", "Opaque:\t%.2x\n", *bufp)); + DEBUGINDENTLESS(); + + /* check if 64-but counter */ + switch(*(bufp+1)) { + case ASN_OPAQUE_COUNTER64: + case ASN_OPAQUE_U64: + case ASN_OPAQUE_FLOAT: + case ASN_OPAQUE_DOUBLE: + case ASN_OPAQUE_I64: + *type = *(bufp+1); + break; + + default: + /* just an Opaque */ + *datalength = (int)asn_length; + return bufp; + } + /* value is encoded as special format */ + bufp = asn_parse_length(bufp + 2, &asn_length); + if (_asn_parse_length_check("parse opaque header", bufp, data, + asn_length, *datalength)) + return NULL; + } +#endif /* OPAQUE_SPECIAL_TYPES */ + + *datalength = (int)asn_length; + + return bufp; +} + +/* + * same as asn_parse_header with test for expected type. + */ +u_char * +asn_parse_sequence(u_char *data, + size_t *datalength, + u_char *type, + u_char expected_type, /* must be this type */ + const char *estr) /* error message prefix */ +{ + data = asn_parse_header(data, datalength, type); + if (data && (*type != expected_type)) { + char ebuf[128]; + sprintf(ebuf, "%s header type %02X: s/b %02X", estr, + (u_char)*type, (u_char)expected_type); + ERROR_MSG(ebuf); + return NULL; + } + return data; +} + + + +/* + * 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( + u_char *data IN - pointer to start of object + size_t *datalength IN/OUT - number of valid bytes left in buffer + u_char type IN - asn type of object + size_t length IN - length of object + */ +u_char * +asn_build_header (u_char *data, + size_t *datalength, + u_char type, + size_t length) +{ + char ebuf[128]; + + if (*datalength < 1){ + sprintf(ebuf, "bad header length < 1 :%d, %d", *datalength, length); + ERROR_MSG(ebuf); + 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( + 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 + */ +u_char * +asn_build_sequence(u_char *data, + size_t *datalength, + u_char type, + size_t length) +{ + static const char *errpre = "build seq"; + char ebuf[128]; + + if (*datalength < 4){ + sprintf(ebuf, "%s: length %d < 4: PUNT", errpre, (int)*datalength); + ERROR_MSG(ebuf); + return NULL; + } + *datalength -= 4; + *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( + u_char *data IN - pointer to start of length field + u_long *length OUT - value of length field + */ +u_char * +asn_parse_length(u_char *data, + u_long *length) +{ + static const char *errpre = "parse length"; + char ebuf[128]; + register u_char lengthbyte; + + if (!data || !length) { + ERROR_MSG("parse length: NULL pointer"); + return NULL; + } + lengthbyte = *data; + + if (lengthbyte & ASN_LONG_LEN){ + lengthbyte &= ~ASN_LONG_LEN; /* turn MSb off */ + if (lengthbyte == 0){ + sprintf(ebuf, "%s: indefinite length not supported", errpre); + ERROR_MSG(ebuf); + return NULL; + } + if (lengthbyte > sizeof(long)){ + sprintf(ebuf, "%s: data length %d > %d not supported", errpre, + lengthbyte, sizeof(long)); + ERROR_MSG(ebuf); + return NULL; + } + data++; + *length = 0; /* protect against short lengths */ + while(lengthbyte--) { + *length <<= 8; + *length |= *data++; + } + return data; + } else { /* short asnlength */ + *length = (long)lengthbyte; + return data + 1; + } +} + +/* + + u_char * asn_build_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 * +asn_build_length(u_char *data, + size_t *datalength, + size_t length) +{ + static const char *errpre = "build length"; + char ebuf[128]; + + u_char *start_data = data; + + /* no indefinite lengths sent */ + if (length < 0x80){ + if (*datalength < 1){ + sprintf(ebuf, "%s: bad length < 1 :%d, %d",errpre,*datalength,length); + ERROR_MSG(ebuf); + return NULL; + } + *data++ = (u_char)length; + } else if (length <= 0xFF){ + if (*datalength < 2){ + sprintf(ebuf, "%s: bad length < 2 :%d, %d",errpre,*datalength,length); + ERROR_MSG(ebuf); + return NULL; + } + *data++ = (u_char)(0x01 | ASN_LONG_LEN); + *data++ = (u_char)length; + } else { /* 0xFF < length <= 0xFFFF */ + if (*datalength < 3){ + sprintf(ebuf, "%s: bad length < 3 :%d, %d",errpre,*datalength,length); + ERROR_MSG(ebuf); + 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( + 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 + */ +u_char * +asn_parse_objid(u_char *data, + size_t *datalength, + u_char *type, + oid *objid, + size_t *objidlength) +{ +/* + * ASN.1 objid ::= 0x06 asnlength subidentifier {subidentifier}* + * subidentifier ::= {leadingbyte}* lastbyte + * leadingbyte ::= 1 7bitvalue + * lastbyte ::= 0 7bitvalue + */ + register u_char *bufp = data; + register oid *oidp = objid + 1; + register u_long subidentifier; + register long length; + u_long asn_length; + + *type = *bufp++; + bufp = asn_parse_length(bufp, &asn_length); + if (_asn_parse_length_check("parse objid", bufp, data, + asn_length, *datalength)) + return NULL; + + *datalength -= (int)asn_length + (bufp - data); + + DEBUGDUMPSETUP("dump_recv", data, bufp - data + asn_length); + + /* 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 */ +/*?? note, this test will never be true, since the largest value + of subidentifier is the value of MAX_SUBID! */ + if (subidentifier > (u_long)MAX_SUBID){ + ERROR_MSG("subidentifier too large"); + 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 { + if (subidentifier < 40) { + objid[0] = 0; + objid[1] = subidentifier; + } else if (subidentifier < 80) { + objid[0] = 1; + objid[1] = subidentifier - 40; + } else if (subidentifier < 120) { + objid[0] = 2; + objid[1] = subidentifier - 80; + } else { + objid[1] = (subidentifier % 40); + objid[0] = ((subidentifier - objid[1]) / 40); + } + } + + *objidlength = (int)(oidp - objid); + + DEBUGMSG(("dump_recv", " ASN ObjID: ")); + DEBUGMSGOID(("dump_recv", objid, *objidlength)); + DEBUGMSG(("dump_recv", "\n")); + 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( + u_char *data IN - pointer to start of object + int *datalength IN/OUT - number of valid bytes left in buffer + int 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 + */ +u_char * +asn_build_objid(u_char *data, + size_t *datalength, + u_char type, + oid *objid, + size_t objidlength) +{ +/* + * ASN.1 objid ::= 0x06 asnlength subidentifier {subidentifier}* + * subidentifier ::= {leadingbyte}* lastbyte + * leadingbyte ::= 1 7bitvalue + * lastbyte ::= 0 7bitvalue + */ + size_t asnlength; + register oid *op = objid; + u_char objid_size[MAX_OID_LEN]; + register u_long objid_val; + u_long first_objid_val; + register int i; + + /* check if there are at least 2 sub-identifiers */ + if (objidlength == 0){ + /* there are not, so make OID have two with value of zero */ + objid_val = 0; + objidlength = 2; + } else if (objidlength == 1){ + /* encode the first value */ + objid_val = (op[0] * 40); + objidlength = 2; + op++; + } else { + /* combine the first two values */ + if ( op[1] > 40 ) { + ERROR_MSG("build objid: bad second subidentifier"); + return NULL; + } + objid_val = (op[0] * 40) + op[1]; + op += 2; + } + first_objid_val = objid_val; + + /* calculate the number of bytes needed to store the encoded value */ + for (i = 1, asnlength = 0;;) { + if (objid_val < (unsigned)0x80) { + objid_size[i] = 1; + asnlength += 1; + } else if (objid_val < (unsigned)0x4000) { + objid_size[i] = 2; + asnlength += 2; + } else if (objid_val < (unsigned)0x200000) { + objid_size[i] = 3; + asnlength += 3; + } else if (objid_val < (unsigned)0x10000000) { + objid_size[i] = 4; + asnlength += 4; + } else { + objid_size[i] = 5; + asnlength += 5; + } + i++; + if (i >= (int)objidlength) + break; + objid_val = *op++; + } + + /* store the ASN.1 tag and length */ + data = asn_build_header(data, datalength, type, asnlength); + if (_asn_build_header_check("build objid", data, *datalength, asnlength)) + return NULL; + + /* store the encoded OID value */ + for (i = 1, objid_val = first_objid_val, op = objid+2; + i < (int)objidlength; + i++) { + if (i != 1) objid_val = *op++; + switch (objid_size[i]) { + case 1: + *data++ = (u_char)objid_val; + break; + + case 2: + *data++ = (u_char)((objid_val>>7) | 0x80); + *data++ = (u_char)(objid_val & 0x07f); + break; + + case 3: + *data++ = (u_char)((objid_val>>14) | 0x80); + *data++ = (u_char)((objid_val>>7 & 0x7f) | 0x80); + *data++ = (u_char)(objid_val & 0x07f); + break; + + case 4: + *data++ = (u_char)((objid_val>>21) | 0x80); + *data++ = (u_char)((objid_val>>14 & 0x7f) | 0x80); + *data++ = (u_char)((objid_val>>7 & 0x7f) | 0x80); + *data++ = (u_char)(objid_val & 0x07f); + break; + + case 5: + *data++ = (u_char)((objid_val>>28) | 0x80); + *data++ = (u_char)((objid_val>>21 & 0x7f) | 0x80); + *data++ = (u_char)((objid_val>>14 & 0x7f) | 0x80); + *data++ = (u_char)((objid_val>>7 & 0x7f) | 0x80); + *data++ = (u_char)(objid_val & 0x07f); + break; + } + } + + /* return the length and data ptr */ + *datalength -= asnlength; + return data; +} + +/* + * 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( + 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 * +asn_parse_null(u_char *data, + size_t *datalength, + u_char *type) +{ +/* + * ASN.1 null ::= 0x05 0x00 + */ + register u_char *bufp = data; + u_long asn_length; + + *type = *bufp++; + bufp = asn_parse_length(bufp, &asn_length); + if (bufp == NULL){ + ERROR_MSG("parse null: bad length"); + return NULL; + } + if (asn_length != 0){ + ERROR_MSG("parse null: malformed ASN.1 null"); + return NULL; + } + + *datalength -= (bufp - data); + + DEBUGDUMPSETUP("dump_recv", data, bufp - data); + DEBUGMSG(("dump_recv", " ASN NULL\n")); + + 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( + 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 + */ +u_char * +asn_build_null(u_char *data, + size_t *datalength, + u_char type) +{ +/* + * 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( + u_char *data IN - pointer to start of object + size_t *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 + size_t *strlength IN/OUT - size of output buffer + */ +u_char * +asn_parse_bitstring(u_char *data, + size_t *datalength, + u_char *type, + u_char *string, + size_t *strlength) +{ +/* + * bitstring ::= 0x03 asnlength unused {byte}* + */ + static const char *errpre = "parse bitstring"; + register u_char *bufp = data; + u_long asn_length; + + *type = *bufp++; + bufp = asn_parse_length(bufp, &asn_length); + if (_asn_parse_length_check(errpre, bufp, data, + asn_length, *datalength)) + return NULL; + + if ((size_t)asn_length > *strlength){ + _asn_length_err(errpre, (size_t)asn_length, *strlength); + return NULL; + } + if (_asn_bitstring_check(errpre, asn_length, *bufp)) + return NULL; + + DEBUGDUMPSETUP("dump_recv", data, bufp - data); + DEBUGMSG(("dump_recv", " ASN Bitstring: ")); + DEBUGMSGHEX(("dump_recv", data, asn_length)); + + memmove(string, bufp, 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( + 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 + u_char *string IN - pointer to start of input buffer + int strlength IN - size of input buffer + */ +u_char * +asn_build_bitstring(u_char *data, + size_t *datalength, + u_char type, + u_char *string, + size_t strlength) +{ +/* + * ASN.1 bit string ::= 0x03 asnlength unused {byte}* + */ + static const char *errpre = "build bitstring"; + if (_asn_bitstring_check(errpre, strlength, *string)) + return NULL; + + data = asn_build_header(data, datalength, type, strlength); + if (_asn_build_header_check(errpre,data,*datalength,strlength)) + return NULL; + + memmove(data, string, 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( + 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 + */ +u_char * +asn_parse_unsigned_int64(u_char *data, + size_t *datalength, + u_char *type, + struct counter64 *cp, + size_t countersize) +{ +/* + * ASN.1 integer ::= 0x02 asnlength byte {byte}* + */ + static const char *errpre = "parse uint64"; + const int uint64sizelimit = (4 * 2) + 1; + register u_char *bufp = data; + u_long asn_length; + register u_long low = 0, high = 0; + + if (countersize != sizeof(struct counter64)){ + _asn_size_err(errpre, countersize, sizeof(struct counter64)); + return NULL; + } + *type = *bufp++; + bufp = asn_parse_length(bufp, &asn_length); + if (_asn_parse_length_check(errpre, bufp, data, asn_length, *datalength)) + return NULL; + + DEBUGDUMPSETUP("dump_recv", data, bufp - data); +#ifdef OPAQUE_SPECIAL_TYPES +/* 64 bit counters as opaque */ + if ((*type == ASN_OPAQUE) && + (asn_length <= ASN_OPAQUE_COUNTER64_MX_BER_LEN) && + (*bufp == ASN_OPAQUE_TAG1) && + ((*(bufp+1) == ASN_OPAQUE_COUNTER64) || + (*(bufp+1) == ASN_OPAQUE_U64))) { + DEBUGMSG(("dump_recv", "Opaque %.2x %.2x: ", *bufp, *(bufp+1))); + + /* change type to Counter64 or U64 */ + *type = *(bufp+1); + /* value is encoded as special format */ + bufp = asn_parse_length(bufp + 2, &asn_length); + if (_asn_parse_length_check("parse opaque uint64", bufp, data, + asn_length, *datalength)) + return NULL; + } +#endif /* OPAQUE_SPECIAL_TYPES */ + if (((int)asn_length > uint64sizelimit) || + (((int)asn_length == uint64sizelimit) && *bufp != 0x00)){ + _asn_length_err(errpre, (size_t)asn_length, uint64sizelimit); + return NULL; + } + *datalength -= (int)asn_length + (bufp - data); + if (*bufp & 0x80){ + low = ~low; /* integer is negative */ + high = ~high; + } + + while(asn_length--){ + high = (high << 8) | ((low & 0xFF000000) >> 24); + low = (low << 8) | *bufp++; + } + + cp->low = low; + cp->high = high; + + DEBUGIF("dump_recv") { + char i64buf[I64CHARSZ+1]; + printU64(i64buf, cp); + } + + 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( + u_char *data IN - pointer to start of output buffer + size_t *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 + size_t countersize IN - size of input buffer + */ +u_char * +asn_build_unsigned_int64(u_char *data, + size_t *datalength, + u_char type, + struct counter64 *cp, + size_t countersize) +{ +/* + * ASN.1 integer ::= 0x02 asnlength byte {byte}* + */ + + register u_long low, high; + register u_long mask, mask2; + int add_null_byte = 0; + size_t intsize; + + if (countersize != sizeof(struct counter64)){ + _asn_size_err("build uint64", countersize, sizeof(struct counter64)); + return NULL; + } + intsize = 8; + low = cp->low; + high = cp->high; + mask = ((u_long) 0xFF) << (8 * (sizeof(long) - 1)); + /* mask is 0xFF000000 on a big-endian machine */ + if ((u_char)((high & mask) >> (8 * (sizeof(long) - 1))) & 0x80){ + /* if MSB is set */ + add_null_byte = 1; + intsize++; + } else { + /* + * 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 = ((u_long) 0x1FF) << ((8 * (sizeof(long) - 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(long) - 1))); + low <<= 8; + } + } +#ifdef OPAQUE_SPECIAL_TYPES +/* encode a Counter64 as an opaque (it also works in SNMPv1) */ + /* turn into Opaque holding special tagged value */ + if (type == ASN_OPAQUE_COUNTER64) { + /* put the tag and length for the Opaque wrapper */ + data = asn_build_header(data, datalength, ASN_OPAQUE, intsize+3); + if (_asn_build_header_check("build counter u64", data, *datalength, intsize+3)) + return NULL; + + /* put the special tag and length */ + *data++ = ASN_OPAQUE_TAG1; + *data++ = ASN_OPAQUE_COUNTER64; + *data++ = (u_char)intsize; + *datalength = *datalength - 3; + } + else +/* Encode the Unsigned int64 in an opaque */ + /* turn into Opaque holding special tagged value */ + if (type == ASN_OPAQUE_U64) { + /* put the tag and length for the Opaque wrapper */ + data = asn_build_header(data, datalength, ASN_OPAQUE, intsize+3); + if (_asn_build_header_check("build opaque u64", data, *datalength, intsize+3)) + return NULL; + + /* put the special tag and length */ + *data++ = ASN_OPAQUE_TAG1; + *data++ = ASN_OPAQUE_U64; + *data++ = (u_char)intsize; + *datalength = *datalength - 3; + } + else + { +#endif /* OPAQUE_SPECIAL_TYPES */ + data = asn_build_header(data, datalength, type, intsize); + if (_asn_build_header_check("build uint64", data, *datalength, intsize)) + return NULL; + +#ifdef OPAQUE_SPECIAL_TYPES + } +#endif /* OPAQUE_SPECIAL_TYPES */ + *datalength -= intsize; + if (add_null_byte == 1){ + *data++ = '\0'; + intsize--; + } + while(intsize--){ + *data++ = (u_char)((high & mask) >> (8 * (sizeof(long) - 1))); + high = (high << 8) + | ((low & mask) >> (8 * (sizeof(long) - 1))); + low <<= 8; + + } + return data; +} + +#ifdef OPAQUE_SPECIAL_TYPES + +/* + + u_char * asn_parse_signed_int64( + 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 + */ + +u_char * +asn_parse_signed_int64(u_char *data, + size_t *datalength, + u_char *type, + struct counter64 *cp, + size_t countersize) +{ + static const char *errpre = "parse int64"; + const int int64sizelimit = (4 * 2) + 1; + char ebuf[128]; + register u_char *bufp = data; + u_long asn_length; + register u_int low = 0, high = 0; + + if (countersize != sizeof(struct counter64)){ + _asn_size_err(errpre, countersize, sizeof(struct counter64)); + return NULL; + } + *type = *bufp++; + bufp = asn_parse_length(bufp, &asn_length); + if (_asn_parse_length_check(errpre, bufp, data, asn_length, *datalength)) + return NULL; + + DEBUGDUMPSETUP("dump_recv", data, bufp - data); + if ((*type == ASN_OPAQUE) && + (asn_length <= ASN_OPAQUE_COUNTER64_MX_BER_LEN) && + (*bufp == ASN_OPAQUE_TAG1) && + (*(bufp+1) == ASN_OPAQUE_I64)) { + DEBUGMSG(("dump_recv", "Opaque %.2x %.2x: ", *bufp, *(bufp+1))); + /* change type to Int64 */ + *type = *(bufp+1); + /* value is encoded as special format */ + bufp = asn_parse_length(bufp + 2, &asn_length); + if (_asn_parse_length_check("parse opaque int64", bufp, data, + asn_length, *datalength)) + return NULL; + } + /* this should always have been true until snmp gets int64 PDU types */ + else { + sprintf(ebuf, "%s: wrong type: %d, len %d, buf bytes (%02X,%02X)", + errpre, *type, (int)asn_length, *bufp, *(bufp+1)); + ERROR_MSG(ebuf); + return NULL; + } + if (((int)asn_length > int64sizelimit) || + (((int)asn_length == int64sizelimit) && *bufp != 0x00)){ + _asn_length_err(errpre, (size_t)asn_length, int64sizelimit); + return NULL; + } + *datalength -= (int)asn_length + (bufp - data); + if (*bufp & 0x80){ + low = ~low; /* integer is negative */ + high = ~high; + } + + while(asn_length--){ + high = (high << 8) | ((low & 0xFF000000) >> 24); + low = (low << 8) | *bufp++; + } + + cp->low = low; + cp->high = high; + + DEBUGIF("dump_recv") { + char i64buf[I64CHARSZ+1]; + printI64(i64buf, cp); + } + + return bufp; +} + + +/* + + u_char * asn_build_signed_int64( + 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 + struct counter64 *cp IN - pointer to counter struct + int countersize IN - size of input buffer + */ +u_char * +asn_build_signed_int64(u_char *data, + size_t *datalength, + u_char type, + struct counter64 *cp, + size_t countersize) +{ +/* + * ASN.1 integer ::= 0x02 asnlength byte {byte}* + */ + + struct counter64 c64; + register u_int mask, mask2; + u_long low, high; + size_t intsize; + + if (countersize != sizeof(struct counter64)){ + _asn_size_err("build int64", countersize, sizeof(struct counter64)); + return NULL; + } + intsize = 8; + memcpy(&c64, cp, sizeof(struct counter64)); /* we're may modify it */ + low = c64.low; + high = c64.high; + + /* + * 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 = ((u_int) 0xFF) << (8 * (sizeof(u_int) - 1)); + mask2 = ((u_int) 0x1FF) << ((8 * (sizeof(u_int) - 1)) - 1); + /* mask is 0xFF800000 on a big-endian machine */ + while((((high & mask2) == 0) || ((high & mask2) == mask2)) && intsize > 1){ + intsize--; + high = (high << 8) + | ((low & mask) >> (8 * (sizeof(u_int) - 1))); + low <<= 8; + } + /* until a real int64 gets incorperated into SNMP, we are going to + encode it as an opaque instead. First, we build the opaque + header and then the int64 tag type we use to mark it as an + int64 in the opaque string. */ + data = asn_build_header(data, datalength, ASN_OPAQUE, intsize+3); + if (_asn_build_header_check("build int64", data, *datalength, intsize+3)) + return NULL; + + *data++ = ASN_OPAQUE_TAG1; + *data++ = ASN_OPAQUE_I64; + *data++ = (u_char)intsize; + *datalength -= (3 + intsize); + + while(intsize--){ + *data++ = (u_char)((high & mask) >> (8 * (sizeof(u_int) - 1))); + high = (high << 8) + | ((low & mask) >> (8 * (sizeof(u_int) - 1))); + low <<= 8; + } + return data; +} + +/* + * asn_parse_float - pulls a single precision floating-point out of an opaque 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_float( + 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 + float *floatp IN/OUT - pointer to float + int floatsize IN - size of output buffer + */ +u_char * +asn_parse_float(u_char *data, + size_t *datalength, + u_char *type, + float *floatp, + size_t floatsize) +{ + register u_char *bufp = data; + u_long asn_length; + union { + float floatVal; + long longVal; + u_char c[sizeof(float)]; + } fu; + + if (floatsize != sizeof(float)){ + _asn_size_err("parse float", floatsize, sizeof(float)); + return NULL; + } + *type = *bufp++; + bufp = asn_parse_length(bufp, &asn_length); + if (_asn_parse_length_check("parse float", bufp, data, + asn_length, *datalength)) + return NULL; + + DEBUGDUMPSETUP("dump_recv", data, bufp - data + asn_length); +/* the float is encoded as an opaque */ + if ((*type == ASN_OPAQUE) && + (asn_length == ASN_OPAQUE_FLOAT_BER_LEN) && + (*bufp == ASN_OPAQUE_TAG1) && + (*(bufp+1) == ASN_OPAQUE_FLOAT)) { + DEBUGMSG(("dump_recv", "Opaque %.2x %.2x: ", *bufp, *(bufp+1))); + + /* value is encoded as special format */ + bufp = asn_parse_length(bufp + 2, &asn_length); + if (_asn_parse_length_check("parse opaque float", bufp, data, + asn_length, *datalength)) + return NULL; + + /* change type to Float */ + *type = ASN_OPAQUE_FLOAT; + } + + if (asn_length != sizeof(float)) { + _asn_size_err("parse seq float", asn_length, sizeof(float)); + return NULL; + } + + *datalength -= (int)asn_length + (bufp - data); + memcpy(&fu.c[0], bufp, asn_length); + + /* correct for endian differences */ + fu.longVal = ntohl(fu.longVal); + + *floatp = fu.floatVal; + + DEBUGMSG(("dump_recv", "%f",*floatp)); + return bufp; +} + +/* + * asn_build_float - builds an ASN object containing a single precision floating-point + * number in an Opaque value. + * + * 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_float( + 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 + float *floatp IN - pointer to float + int floatsize IN - size of input buffer + */ +u_char * +asn_build_float(u_char *data, + size_t *datalength, + u_char type, + float *floatp, + size_t floatsize) +{ + union { + float floatVal; + int intVal; + u_char c[sizeof(float)]; + } fu; + + if (floatsize != sizeof (float)) { + _asn_size_err("build float", floatsize, sizeof(float)); + return NULL; + } +/* encode the float as an opaque */ + /* turn into Opaque holding special tagged value */ + + /* put the tag and length for the Opaque wrapper */ + data = asn_build_header(data, datalength, ASN_OPAQUE, floatsize+3); + if (_asn_build_header_check("build float", data, *datalength, (floatsize+3))) + return NULL; + + /* put the special tag and length */ + *data++ = ASN_OPAQUE_TAG1; + *data++ = ASN_OPAQUE_FLOAT; + *data++ = (u_char)floatsize; + *datalength = *datalength - 3; + + fu.floatVal = *floatp; + /* correct for endian differences */ + fu.intVal = htonl(fu.intVal); + + *datalength -= floatsize; + memcpy(data, &fu.c[0], floatsize); + + data += floatsize; + return data; +} + +/* + + u_char * asn_parse_double( + 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 + double *doublep IN/OUT - pointer to double + int doublesize IN - size of output buffer + */ +u_char * +asn_parse_double(u_char *data, + size_t *datalength, + u_char *type, + double *doublep, + size_t doublesize) +{ + register u_char *bufp = data; + u_long asn_length; + long tmp; + union { + double doubleVal; + int intVal[2]; + u_char c[sizeof(double)]; + } fu; + + + if (doublesize != sizeof(double)){ + _asn_size_err("parse double", doublesize, sizeof(double)); + return NULL; + } + *type = *bufp++; + bufp = asn_parse_length(bufp, &asn_length); + if (_asn_parse_length_check("parse double", bufp, data, + asn_length, *datalength)) + return NULL; + + DEBUGDUMPSETUP("dump_recv", data, bufp - data + asn_length); +/* the double is encoded as an opaque */ + if ((*type == ASN_OPAQUE) && + (asn_length == ASN_OPAQUE_DOUBLE_BER_LEN) && + (*bufp == ASN_OPAQUE_TAG1) && + (*(bufp+1) == ASN_OPAQUE_DOUBLE)) { + DEBUGMSG(("dump_recv", "Opaque %.2x %.2x: ", *bufp, *(bufp+1))); + + /* value is encoded as special format */ + bufp = asn_parse_length(bufp + 2, &asn_length); + if (_asn_parse_length_check("parse opaque double", bufp, data, + asn_length, *datalength)) + return NULL; + + /* change type to Double */ + *type = ASN_OPAQUE_DOUBLE; + } + + if (asn_length != sizeof(double)) { + _asn_size_err("parse seq double", asn_length, sizeof(double)); + return NULL; + } + *datalength -= (int)asn_length + (bufp - data); + memcpy(&fu.c[0], bufp, asn_length); + + /* correct for endian differences */ + + tmp = ntohl(fu.intVal[0]); + fu.intVal[0] = ntohl(fu.intVal[1]); + fu.intVal[1] = tmp; + + *doublep = fu.doubleVal; + DEBUGMSG(("dump_recv", "%d",*doublep)); + + return bufp; +} + +/* + + u_char * asn_build_double( + 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 + double *doublep IN - pointer to double + int doublesize IN - size of input buffer + */ +u_char * +asn_build_double(u_char *data, + size_t *datalength, + u_char type, + double* doublep, + size_t doublesize) +{ + long tmp; + union { + double doubleVal; + int intVal[2]; + u_char c[sizeof(double)]; + } fu; + + if (doublesize != sizeof(double)){ + _asn_size_err("build double", doublesize, sizeof(double)); + return NULL; + } + +/* encode the double as an opaque */ + /* turn into Opaque holding special tagged value */ + + /* put the tag and length for the Opaque wrapper */ + data = asn_build_header(data, datalength, ASN_OPAQUE, doublesize+3); + if (_asn_build_header_check("build double", data, *datalength, doublesize+3)) + return NULL; + + /* put the special tag and length */ + *data++ = ASN_OPAQUE_TAG1; + *data++ = ASN_OPAQUE_DOUBLE; + *data++ = (u_char)doublesize; + *datalength = *datalength - 3; + + fu.doubleVal = *doublep; + /* correct for endian differences */ + tmp = htonl(fu.intVal[0]); + fu.intVal[0] = htonl(fu.intVal[1]); + fu.intVal[1] = tmp; + *datalength -= doublesize; + memcpy(data, &fu.c[0], doublesize); + + data += doublesize; + return data; +} + +#endif /* OPAQUE_SPECIAL_TYPES */ +
