/*
 * 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.
******************************************************************/
/**
 * @defgroup asn1_packet_parse asn1 parsing and datatype manipulation routines.
 * @ingroup library
 *
 * @{
 * 
 * Note on 
 * 
 * Re-allocating reverse ASN.1 encoder functions.  Synopsis:
 *
 * \code
 *
 * u_char *buf = (u_char*)malloc(100);
 * u_char type = (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER);
 * size_t buf_len = 100, offset = 0;
 * long data = 12345;
 * int allow_realloc = 1;
 * 
 * if (asn_realloc_rbuild_int(&buf, &buf_len, &offset, allow_realloc,
 *                            type, &data, sizeof(long)) == 0) {
 *     error;
 * }
 * 
 * \endcode
 *
 * NOTE WELL: after calling one of these functions with allow_realloc
 * non-zero, buf might have moved, buf_len might have grown and
 * offset will have increased by the size of the encoded data.
 * You should **NEVER** do something like this:
 * 
 * \code
 *
 * u_char *buf = (u_char *)malloc(100), *ptr;
 * u_char type = (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER);
 * size_t buf_len = 100, offset = 0;
 * long data1 = 1234, data2 = 5678;
 * int rc = 0, allow_realloc = 1;
 * 
 * rc  = asn_realloc_rbuild_int(&buf, &buf_len, &offset, allow_realloc,
 *                                type, &data1, sizeof(long));
 * ptr = buf[buf_len - offset];   / * points at encoding of data1 * /
 * if (rc == 0) {
 *      error;
 * }
 * rc  = asn_realloc_rbuild_int(&buf, &buf_len, &offset, allow_realloc,
 *                              type, &data2, sizeof(long));
 * make use of ptr here;
 * 
 * \endcode
 * 
 * ptr is **INVALID** at this point.  In general, you should store the
 * offset value and compute pointers when you need them:
 * 
 * 
 * \code
 *
 * u_char *buf = (u_char *)malloc(100), *ptr;
 * u_char type = (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER);
 * size_t buf_len = 100, offset = 0, ptr_offset;
 * long data1 = 1234, data2 = 5678;
 * int rc = 0, allow_realloc = 1;
 * 
 * rc  = asn_realloc_rbuild_int(&buf, &buf_len, &offset, allow_realloc,
 *                              type, &data1, sizeof(long));
 * ptr_offset = offset;
 * if (rc == 0) {
 *      error;
 * }
 * rc  = asn_realloc_rbuild_int(&buf, &buf_len, &offset, allow_realloc,
 *                              type, &data2, sizeof(long));
 * ptr = buf + buf_len - ptr_offset
 * make use of ptr here;
 * 
 * \endcode
 * 
 * 
 * Here, you can see that ptr will be a valid pointer even if the block of
 * memory has been moved, as it may well have been.  Plenty of examples of
 * usage all over asn1.c, snmp_api.c, snmpusm.c.
 * 
 * The other thing you should **NEVER** do is to pass a pointer to a buffer
 * on the stack as the first argument when allow_realloc is non-zero, unless
 * you really know what you are doing and your machine/compiler allows you to
 * free non-heap memory.  There are rumours that such things exist, but many
 * consider them no more than the wild tales of a fool.
 * 
 * Of course, you can pass allow_realloc as zero, to indicate that you do not
 * wish the packet buffer to be reallocated for some reason; perhaps because
 * it is on the stack.  This may be useful to emulate the functionality of
 * the old API:
 *
 * \code 
 * 
 * u_char my_static_buffer[100], *cp = NULL;
 * size_t my_static_buffer_len = 100;
 * float my_pi = (float)22/(float)7;
 * 
 * cp = asn_rbuild_float(my_static_buffer, &my_static_buffer_len,
 *                       ASN_OPAQUE_FLOAT, &my_pi, sizeof(float));
 * if (cp == NULL) {
 * error;
 * }
 * 
 * \endcode
 * 
 * IS EQUIVALENT TO:
 * 
 * \code
 * 
 * u_char my_static_buffer[100];
 * size_t my_static_buffer_len = 100, my_offset = 0;
 * float my_pi = (float)22/(float)7;
 * int rc = 0;
 * 
 * rc = asn_realloc_rbuild_float(&my_static_buffer, &my_static_buffer_len,
 *                               &my_offset, 0,
 *                               ASN_OPAQUE_FLOAT, &my_pi, sizeof(float));
 * if (rc == 0) {
 *   error;
 * }
 * \endcode
 * 
 */


#include <net-snmp/net-snmp-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 <net-snmp/output_api.h>
#include <net-snmp/utilities.h>

#include <net-snmp/library/asn1.h>
#include <net-snmp/library/int64.h>
#include <net-snmp/library/mib.h>

#ifndef NULL
#define NULL	0
#endif

#include <net-snmp/library/snmp_api.h>

/**
 * @internal
 * output an error for a wrong size
 * 
 * @param str        error string
 * @param wrongsize  wrong size
 * @param rightsize  expected size
 */
static
    void
_asn_size_err(const char *str, size_t wrongsize, size_t rightsize)
{
    char            ebuf[128];

    snprintf(ebuf, sizeof(ebuf),
            "%s size %lu: s/b %lu", str,
	    (unsigned long)wrongsize, (unsigned long)rightsize);
    ebuf[ sizeof(ebuf)-1 ] = 0;
    ERROR_MSG(ebuf);
}

/**
 * @internal 
 * output an error for a wrong length
 * 
 * @param str        error string
 * @param wrongsize  wrong  length
 * @param rightsize  expected length
 */
static
    void
_asn_length_err(const char *str, size_t wrongsize, size_t rightsize)
{
    char            ebuf[128];

    snprintf(ebuf, sizeof(ebuf),
            "%s length %lu too large: exceeds %lu", str,
	    (unsigned long)wrongsize, (unsigned long)rightsize);
    ebuf[ sizeof(ebuf)-1 ] = 0;
    ERROR_MSG(ebuf);
}

/**
 * @internal
 * call after asn_parse_length to verify result.
 * 
 * @param str  error string
 * @param bufp start of buffer
 * @param data start of data
 * @param plen  ?
 * @param dlen  ?
 * 
 * @return 1 on error 0 on success
 */
static
    int
_asn_parse_length_check(const char *str,
                        const u_char * bufp, const 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 (plen > 0x7fffffff || header_len > 0x7fffffff ||
        ((size_t) plen + header_len) > dlen) {
        snprintf(ebuf, sizeof(ebuf),
                "%s: message overflow: %d len + %d delta > %d len",
                str, (int) plen, (int) header_len, (int) dlen);
        ebuf[ sizeof(ebuf)-1 ] = 0;
        ERROR_MSG(ebuf);
        return 1;
    }
    return 0;
}


/**
 * @internal 
 * call after asn_build_header to verify result.
 * 
 * @param str     error string to output
 * @param data    data pointer to verify (NULL => error )
 * @param datalen  data len to check
 * @param typedlen  type length
 * 
 * @return 0 on success, 1 on error
 */
static
    int
_asn_build_header_check(const char *str, const u_char * data,
                        size_t datalen, size_t typedlen)
{
    char            ebuf[128];

    if (data == NULL) {
        /*
         * error message is set 
         */
        return 1;
    }
    if (datalen < typedlen) {
        snprintf(ebuf, sizeof(ebuf),
                "%s: bad header, length too short: %lu < %lu", str,
                (unsigned long)datalen, (unsigned long)typedlen);
        ebuf[ sizeof(ebuf)-1 ] = 0;
        ERROR_MSG(ebuf);
        return 1;
    }
    return 0;
}

/**
 * @internal 
 * call after asn_build_header to verify result.
 * 
 * @param str       error string
 * @param pkt       packet to check
 * @param pkt_len  length of the packet
 * @param typedlen length of the type
 * 
 * @return 0 on success 1 on error 
 */
static
    int
_asn_realloc_build_header_check(const char *str,
                                u_char ** pkt,
                                const size_t * pkt_len, size_t typedlen)
{
    char            ebuf[128];

    if (pkt == NULL || *pkt == NULL) {
        /*
         * Error message is set.  
         */
        return 1;
    }

    if (*pkt_len < typedlen) {
        snprintf(ebuf, sizeof(ebuf),
                "%s: bad header, length too short: %lu < %lu", str,
                (unsigned long)*pkt_len, (unsigned long)typedlen);
        ebuf[ sizeof(ebuf)-1 ] = 0;
        ERROR_MSG(ebuf);
        return 1;
    }
    return 0;
}

/**
 * @internal 
 * checks the incoming packet for validity and returns its size or 0 
 * 
 * @param pkt The packet 
 * @param len The length to check 
 * 
 * @return The size of the packet if valid; 0 otherwise
 */
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, size_t asn_length, u_char datum)
{
    char            ebuf[128];

    if (asn_length < 1) {
        snprintf(ebuf, sizeof(ebuf),
                "%s: length %d too small", str, (int) asn_length);
        ebuf[ sizeof(ebuf)-1 ] = 0;
        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;
}

/**
 * @internal 
 * asn_parse_int - pulls a long out of an 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.
 *  
 * @param data       IN - pointer to start of object
 * @param datalength IN/OUT - number of valid bytes left in buffer
 * @param type       OUT - asn type of object
 * @param intp       IN/OUT - pointer to start of output buffer
 * @param intsize    IN - size of output buffer
 * 
 * @return 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,
              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("recv", data, bufp - data + asn_length);

    while (asn_length--)
        value = (value << 8) | *bufp++;

    DEBUGMSG(("dumpv_recv", "  Integer:\t%ld (0x%.2X)\n", value, value));

    *intp = value;
    return bufp;
}


/**
 * @internal 
 * 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.
 *  
 * @param data       IN - pointer to start of object
 * @param datalength IN/OUT - number of valid bytes left in buffer
 * @param type       OUT - asn type of object
 * @param intp       IN/OUT - pointer to start of output buffer
 * @param intsize    IN - size of output buffer
 * 
 * @return 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,
                       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("recv", data, bufp - data + asn_length);

    while (asn_length--)
        value = (value << 8) | *bufp++;

    DEBUGMSG(("dumpv_recv", "  UInteger:\t%ld (0x%.2X)\n", value, value));

    *intp = value;
    return bufp;
}


/**
 * @internal 
 * 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.
 * 
 * 
 * @param data         IN - pointer to start of output buffer
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         IN  - asn type of objec
 * @param intp         IN - pointer to start of long integer
 * @param intsize      IN - size of input buffer
 * 
 * @return  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,
           size_t * datalength, u_char type, const 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;
#ifndef SNMP_NO_DEBUGGING
    u_char         *initdatap = data;
#endif

    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;
    }
    DEBUGDUMPSETUP("send", initdatap, data - initdatap);
    DEBUGMSG(("dumpv_send", "  Integer:\t%ld (0x%.2X)\n", *intp, *intp));
    return data;
}



/**
 * @internal 
 * 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.
 * 
 * 
 * @param data         IN - pointer to start of output buffer
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         IN  - asn type of objec
 * @param intp         IN - pointer to start of long integer
 * @param intsize      IN - size of input buffer
 * 
 * @return  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,
                       size_t * datalength,
                       u_char type, const 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;
#ifndef SNMP_NO_DEBUGGING
    u_char         *initdatap = data;
#endif

    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;
    }
    DEBUGDUMPSETUP("send", initdatap, data - initdatap);
    DEBUGMSG(("dumpv_send", "  UInteger:\t%ld (0x%.2X)\n", *intp, *intp));
    return data;
}


/**
 * @internal 
 * 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.
 * ASN.1 octet string   ::=      primstring | cmpdstring
 * primstring           ::= 0x04 asnlength byte {byte}*
 * cmpdstring           ::= 0x24 asnlength string {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.
 * 
 * @param data        IN - pointer to start of object
 * @param datalength  IN/OUT - number of valid bytes left in buffer
 * @param type        OUT - asn type of object 
 * @param string      IN/OUT - pointer to start of output buffer
 * @param strlength   IN/OUT - size of output buffer
 * 
 * @return  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,
                 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("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("dumpv_recv") {
        u_char         *buf = (u_char *) malloc(1 + asn_length);
        size_t          l = (buf != NULL) ? (1 + asn_length) : 0, ol = 0;

        if (sprint_realloc_asciistring
            (&buf, &l, &ol, 1, string, asn_length)) {
            DEBUGMSG(("dumpv_recv", "  String:\t%s\n", buf));
        } else {
            if (buf == NULL) {
                DEBUGMSG(("dumpv_recv", "  String:\t[TRUNCATED]\n"));
            } else {
                DEBUGMSG(("dumpv_recv", "  String:\t%s [TRUNCATED]\n",
                          buf));
            }
        }
        if (buf != NULL) {
            free(buf);
        }
    }

    return bufp + asn_length;
}


/**
 * @internal
 * 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.
 *
 * @param data         IN - pointer to start of object
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         IN - asn type of object
 * @param string       IN - pointer to start of input buffer
 * @param strlength    IN - size of input buffer
 * @return  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,
                 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.
     */
#ifndef SNMP_NO_DEBUGGING
    u_char         *initdatap = data;
#endif
    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;
    DEBUGDUMPSETUP("send", initdatap, data - initdatap + strlength);
    DEBUGIF("dumpv_send") {
        u_char         *buf = (u_char *) malloc(1 + strlength);
        size_t          l = (buf != NULL) ? (1 + strlength) : 0, ol = 0;

        if (sprint_realloc_asciistring
            (&buf, &l, &ol, 1, string, strlength)) {
            DEBUGMSG(("dumpv_send", "  String:\t%s\n", buf));
        } else {
            if (buf == NULL) {
                DEBUGMSG(("dumpv_send", "  String:\t[TRUNCATED]\n"));
            } else {
                DEBUGMSG(("dumpv_send", "  String:\t%s [TRUNCATED]\n",
                          buf));
            }
        }
        if (buf != NULL) {
            free(buf);
        }
    }
    return data + strlength;
}



/**
 * @internal
 * 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.
 *
 *
 * @param data         IN - pointer to start of object
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         OUT - asn type of object
 * @return  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, 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;

#ifdef DUMP_PRINT_HEADERS
    DEBUGDUMPSETUP("recv", data, (bufp - data));
    DEBUGMSG(("dumpv_recv", "  Header: 0x%.2X, len = %d (0x%X)\n", *data,
              asn_length, asn_length));
#else
    /*
     * DEBUGMSGHEXTLI(("recv",data,(bufp-data)));
     * DEBUGMSG(("dumpH_recv","\n"));
     */
#endif

#ifdef OPAQUE_SPECIAL_TYPES

    if ((*type == ASN_OPAQUE) && (*bufp == ASN_OPAQUE_TAG1)) {

        /*
         * 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;
}

/**
 * @internal
 * same as asn_parse_header with test for expected type
 *
 * @see asn_parse_header
 *
 * @param data          IN - pointer to start of object
 * @param datalength    IN/OUT - number of valid bytes left in buffer
 * @param type          OUT - asn type of object
 * @param expected_type IN expected type
 * @return  Returns a pointer to the first byte of the contents of this object.
 *          Returns NULL on any error.
 *
 */
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];
        snprintf(ebuf, sizeof(ebuf),
                 "%s header type %02X: s/b %02X", estr,
                (u_char) * type, (u_char) expected_type);
        ebuf[ sizeof(ebuf)-1 ] = 0;
        ERROR_MSG(ebuf);
        return NULL;
    }
    return data;
}



/**
 * @internal
 * 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.
 *
 * @param data         IN - pointer to start of object
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         IN - asn type of object
 * @param length       IN - length of object
 * @return 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,
                 size_t * datalength, u_char type, size_t length)
{
    char            ebuf[128];

    if (*datalength < 1) {
        snprintf(ebuf, sizeof(ebuf),
                "bad header length < 1 :%lu, %lu",
		(unsigned long)*datalength, (unsigned long)length);
        ebuf[ sizeof(ebuf)-1 ] = 0;
        ERROR_MSG(ebuf);
        return NULL;
    }
    *data++ = type;
    (*datalength)--;
    return asn_build_length(data, datalength, length);
}

/**
 * @internal
 * 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.
 *
 * @param data         IN - pointer to start of object
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         IN - asn type of object
 * @param length       IN - length of object
 *
 * @return 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,
                   size_t * datalength, u_char type, size_t length)
{
    static const char *errpre = "build seq";
    char            ebuf[128];

    if (*datalength < 4) {
        snprintf(ebuf, sizeof(ebuf),
                "%s: length %d < 4: PUNT", errpre,
                (int) *datalength);
        ebuf[ sizeof(ebuf)-1 ] = 0;
        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;
}

/**
 * @internal
 * 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.
 *
 * @param data         IN - pointer to start of length field
 * @param length       OUT - value of length field
 *
 *  @return 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, 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) {
            snprintf(ebuf, sizeof(ebuf),
                     "%s: indefinite length not supported", errpre);
            ebuf[ sizeof(ebuf)-1 ] = 0;
            ERROR_MSG(ebuf);
            return NULL;
        }
        if (lengthbyte > sizeof(long)) {
            snprintf(ebuf, sizeof(ebuf),
                    "%s: data length %d > %lu not supported", errpre,
                    lengthbyte, (unsigned long)sizeof(long));
            ebuf[ sizeof(ebuf)-1 ] = 0;
            ERROR_MSG(ebuf);
            return NULL;
        }
        data++;
        *length = 0;            /* protect against short lengths */
        while (lengthbyte--) {
            *length <<= 8;
            *length |= *data++;
        }
        if ((long) *length < 0) {
            snprintf(ebuf, sizeof(ebuf),
                     "%s: negative data length %ld\n", errpre,
                     (long) *length);
            ebuf[ sizeof(ebuf)-1 ] = 0;
            ERROR_MSG(ebuf);
            return NULL;
        }
        return data;
    } else {                    /* short asnlength */
        *length = (long) lengthbyte;
        return data + 1;
    }
}

/**
 * @internal
 * asn_build_length - builds an ASN header for a length with
 * 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 length.
 *
 *
 *  Returns a pointer to the first byte of the contents of this object.
 *  Returns NULL on any error.
 *
 * @param data         IN - pointer to start of object
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param length       IN - length of object
 *
 * @return Returns a pointer to the first byte of the contents of this object.
 *         Returns NULL on any error.
 */
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) {
            snprintf(ebuf, sizeof(ebuf),
                    "%s: bad length < 1 :%lu, %lu", errpre,
                    (unsigned long)*datalength, (unsigned long)length);
            ebuf[ sizeof(ebuf)-1 ] = 0;
            ERROR_MSG(ebuf);
            return NULL;
        }
        *data++ = (u_char) length;
    } else if (length <= 0xFF) {
        if (*datalength < 2) {
            snprintf(ebuf, sizeof(ebuf),
                    "%s: bad length < 2 :%lu, %lu", errpre,
                    (unsigned long)*datalength, (unsigned long)length);
            ebuf[ sizeof(ebuf)-1 ] = 0;
            ERROR_MSG(ebuf);
            return NULL;
        }
        *data++ = (u_char) (0x01 | ASN_LONG_LEN);
        *data++ = (u_char) length;
    } else {                    /* 0xFF < length <= 0xFFFF */
        if (*datalength < 3) {
            snprintf(ebuf, sizeof(ebuf),
                    "%s: bad length < 3 :%lu, %lu", errpre,
                    (unsigned long)*datalength, (unsigned long)length);
            ebuf[ sizeof(ebuf)-1 ] = 0;
            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;

}

/**
 * @internal
 * 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.
 *
 * @param data         IN - pointer to start of object
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         OUT - asn type of object
 * @param objid        IN/OUT - pointer to start of output buffer
 * @param objidlength  IN/OUT - number of sub-id's in objid
 *
 *  @return 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,
                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("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! 
         *
         * Yes: PC-LINT says the same thing
         * No!: the agent can be configured to use a char instead of a long
         *      for OIDs, in which case this test is essential.
         */
        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 {
            objid[0] = 2;
            objid[1] = subidentifier - 80;
        }
    }

    *objidlength = (int) (oidp - objid);

    DEBUGMSG(("dumpv_recv", "  ObjID: "));
    DEBUGMSGOID(("dumpv_recv", objid, *objidlength));
    DEBUGMSG(("dumpv_recv", "\n"));
    return bufp;
}

/**
 * @internal
 * 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.
 *
 * @param data         IN - pointer to start of object
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         IN - asn type of object
 * @param objid        IN - pointer to start of input buffer
 * @param objidlength  IN - number of sub-id's in objid
 *
 * @return   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,
                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;
#ifndef SNMP_NO_DEBUGGING
    u_char         *initdatap = data;
#endif

    /*
     * 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 (objid[0] > 2) {
        ERROR_MSG("build objid: bad first subidentifier");
        return NULL;
    } 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) &&
            (op[0] < 2)) {
            ERROR_MSG("build objid: bad second subidentifier");
            return NULL;
        }
        objid_val = (op[0] * 40) + op[1];
        op += 2;
    }
    first_objid_val = objid_val;

    /*
     * ditch illegal calls now 
     */
    if (objidlength > MAX_OID_LEN)
        return NULL;

    /*
     * 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++;	/* XXX - doesn't handle 2.X (X > 40) */
    }

    /*
     * 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;
    DEBUGDUMPSETUP("send", initdatap, data - initdatap);
    DEBUGMSG(("dumpv_send", "  ObjID: "));
    DEBUGMSGOID(("dumpv_send", objid, objidlength));
    DEBUGMSG(("dumpv_send", "\n"));
    return data;
}

/**
 * @internal
 * 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.
 *
 * @param data         IN - pointer to start of object
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         OUT - asn type of object
 *  @return 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, 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("recv", data, bufp - data);
    DEBUGMSG(("dumpv_recv", "  NULL\n"));

    return bufp + asn_length;
}


/**
 * @internal
 * 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.
 *
 * @param data         IN - pointer to start of object
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         IN - asn type of object
 * @retun  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, size_t * datalength, u_char type)
{
    /*
     * ASN.1 null ::= 0x05 0x00
     */
#ifndef SNMP_NO_DEBUGGING
    u_char         *initdatap = data;
#endif
    data = asn_build_header(data, datalength, type, 0);
    DEBUGDUMPSETUP("send", initdatap, data - initdatap);
    DEBUGMSG(("dumpv_send", "  NULL\n"));
    return data;
}

/**
 * @internal
 * 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.
 *
 * @param data         IN - pointer to start of object
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         OUT - asn type of object
 * @param string       IN/OUT - pointer to start of output buffer
 * @param strlength    IN/OUT - size of output buffer
 * @return 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,
                    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("recv", data, bufp - data);
    DEBUGMSG(("dumpv_recv", "  Bitstring: "));
    DEBUGMSGHEX(("dumpv_recv", data, asn_length));
    DEBUGMSG(("dumpv_recv", "\n"));

    memmove(string, bufp, asn_length);
    *strlength = (int) asn_length;
    *datalength -= (int) asn_length + (bufp - data);
    return bufp + asn_length;
}


/**
 * @internal
 * 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.
 *
 * @param data         IN - pointer to start of object
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         IN - asn type of object
 * @param string       IN - pointer to start of input buffer
 * @param strlength    IN - size of input buffer
 * @return 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,
                    size_t * datalength,
                    u_char type, const 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, (u_char)((string) ? *string :  0)))
        return NULL;

    data = asn_build_header(data, datalength, type, strlength);
    if (_asn_build_header_check(errpre, data, *datalength, strlength))
        return NULL;

    if (strlength > 0 && string)
        memmove(data, string, strlength);
    else if (strlength > 0 && !string) {
        ERROR_MSG("no string passed into asn_build_bitstring\n");
        return NULL;
    }

    *datalength -= strlength;
    DEBUGDUMPSETUP("send", data, strlength);
    DEBUGMSG(("dumpv_send", "  Bitstring: "));
    DEBUGMSGHEX(("dumpv_send", data, strlength));
    DEBUGMSG(("dumpv_send", "\n"));
    return data + strlength;
}

/**
 * @internal
 * 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.
 *
 * @param data         IN - pointer to start of object
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         OUT - asn type of object
 * @param cp           IN/OUT - pointer to counter struct
 * @param countersize  IN - size of output buffer
 * @return  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,
                         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("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))) {
        /*
         * 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("dumpv_recv") {
        char            i64buf[I64CHARSZ + 1];
        printU64(i64buf, cp);
        DEBUGMSG(("dumpv_recv", "Counter64: %s", i64buf));
    }

    return bufp;
}


/**
 * @internal
 * 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.
 *
 * @param data         IN - pointer to start of output buffer
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         IN  - asn type of object
 * @param cp           IN - pointer to counter struct
 * @param countersize  IN - size of input buffer
 * @return  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,
                         size_t * datalength,
                         u_char type,
                         const 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;
#ifndef SNMP_NO_DEBUGGING
    u_char         *initdatap = data;
#endif

    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;

    }
    DEBUGDUMPSETUP("send", initdatap, data - initdatap);
    DEBUGIF("dumpv_send") {
        char            i64buf[I64CHARSZ + 1];
        printU64(i64buf, cp);
        DEBUGMSG(("dumpv_send", i64buf));
    }
    return data;
}

#ifdef OPAQUE_SPECIAL_TYPES


/**
 * @internal
 * asn_parse_signed_int64 - pulls a 64 bit signed 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.
 
 * @param data         IN - pointer to start of object
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         OUT - asn type of object
 * @param cp           IN/OUT - pointer to counter struct
 * @param countersize  IN - size of output buffer
 * @return  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_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("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)) {
        /*
         * 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 {
        snprintf(ebuf, sizeof(ebuf),
                "%s: wrong type: %d, len %d, buf bytes (%02X,%02X)",
                errpre, *type, (int) asn_length, *bufp, *(bufp + 1));
        ebuf[ sizeof(ebuf)-1 ] = 0;
        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("dumpv_recv") {
        char            i64buf[I64CHARSZ + 1];
        printI64(i64buf, cp);
        DEBUGMSG(("dumpv_recv", "Integer64: %s", i64buf));
    }

    return bufp;
}



/**
 * @internal
 * asn_build_signed_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.
 *
 * @param data         IN - pointer to start of output buffer
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         IN  - asn type of object
 * @param cp           IN - pointer to counter struct
 * @param countersize  IN - size of input buffer
 * @return  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_signed_int64(u_char * data,
                       size_t * datalength,
                       u_char type,
                       const 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;
#ifndef SNMP_NO_DEBUGGING
    u_char         *initdatap = data;
#endif

    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;
    }
    DEBUGDUMPSETUP("send", initdatap, data - initdatap);
    DEBUGIF("dumpv_send") {
        char            i64buf[I64CHARSZ + 1];
        printU64(i64buf, cp);
        DEBUGMSG(("dumpv_send", i64buf));
    }
    return data;
}


/**
 * @internal
 * 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.
 *
 * @param data         IN - pointer to start of object
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         OUT - asn type of object
 * @param floatp       IN/OUT - pointer to float
 * @param floatsize    IN - size of output buffer
 * @return  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,
                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("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)) {

        /*
         * 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(("dumpv_recv", "Opaque float: %f\n", *floatp));
    return bufp;
}

/**
 * @internal
 * 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.
 *
 * @param data         IN - pointer to start of object
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         IN - asn type of object
 * @param floatp       IN - pointer to float
 * @param floatsize    IN - size of input buffer
 * @return  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,
                size_t * datalength,
                u_char type, const float *floatp, size_t floatsize)
{
    union {
        float           floatVal;
        int             intVal;
        u_char          c[sizeof(float)];
    } fu;
#ifndef SNMP_NO_DEBUGGING
    u_char         *initdatap = data;
#endif

    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);

    DEBUGDUMPSETUP("send", initdatap, data - initdatap);
    DEBUGMSG(("dumpv_send", "Opaque float: %f\n", *floatp));
    data += floatsize;
    return data;
}


/**
 * @internal
 * asn_parse_double - pulls a double 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.
 *
 * @param data         IN - pointer to start of object
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         OUT - asn type of object
 * @param doublep       IN/OUT - pointer to double
 * @param doublesize    IN - size of output buffer
 * @return  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_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("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)) {

        /*
         * 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(("dumpv_recv", "  Opaque Double:\t%f\n", *doublep));

    return bufp;
}


/**
 * @internal
 * asn_build_double - builds an ASN object containing a double
 *                    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.
 *
 * @param data         IN - pointer to start of object
 * @param datalength   IN/OUT - number of valid bytes left in buffer
 * @param type         IN - asn type of object
 * @param doublep      IN - pointer to double
 * @param doublesize   IN - size of input buffer
 * @return  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_double(u_char * data,
                 size_t * datalength,
                 u_char type, const double *doublep, size_t doublesize)
{
    long            tmp;
    union {
        double          doubleVal;
        int             intVal[2];
        u_char          c[sizeof(double)];
    } fu;
#ifndef SNMP_NO_DEBUGGING
    u_char         *initdatap = data;
#endif

    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;
    DEBUGDUMPSETUP("send", initdatap, data - initdatap);
    DEBUGMSG(("dumpv_send", "  Opaque double: %f", *doublep));
    return data;
}

#endif                          /* OPAQUE_SPECIAL_TYPES */


/**
 * @internal
 * This function increases the size of the buffer pointed to by *pkt, which
 * is initially of size *pkt_len.  Contents are preserved **AT THE TOP END OF 
 * THE BUFFER** (hence making this function useful for reverse encoding).
 * You can change the reallocation scheme, but you **MUST** guarantee to
 * allocate **AT LEAST** one extra byte.  If memory cannot be reallocated,
 * then return 0; otherwise return 1.   
 * 
 * @param pkt     buffer to increase
 * @param pkt_len initial buffer size
 * 
 * @return 1 on success 0 on error (memory cannot be reallocated)
 */
int
asn_realloc(u_char ** pkt, size_t * pkt_len)
{
    if (pkt != NULL && pkt_len != NULL) {
        size_t          old_pkt_len = *pkt_len;

        DEBUGMSGTL(("asn_realloc", " old_pkt %08p, old_pkt_len %08x\n",
                    *pkt, old_pkt_len));

        if (snmp_realloc(pkt, pkt_len)) {
            DEBUGMSGTL(("asn_realloc", " new_pkt %08p, new_pkt_len %08x\n",
                        *pkt, *pkt_len));
            DEBUGMSGTL(("asn_realloc",
                        " memmove(%08p + %08x, %08p, %08x)\n", *pkt,
                        (*pkt_len - old_pkt_len), *pkt, old_pkt_len));
            memmove(*pkt + (*pkt_len - old_pkt_len), *pkt, old_pkt_len);
            memset(*pkt, (int) ' ', *pkt_len - old_pkt_len);
            return 1;
        } else {
            DEBUGMSG(("asn_realloc", " CANNOT REALLOC()\n"));
        }
    }
    return 0;
}

#ifdef USE_REVERSE_ASNENCODING

/**
 * @internal
 * reverse  builds an ASN header for a length with
 * length specified.
 * 
 * @param pkt     IN/OUT address of the begining of the buffer.
 * @param pkt_len IN/OUT address to an integer containing the size of pkt.
 * @param offset  IN/OUT offset to the start of the buffer where to write
 * @param r       IN if not zero reallocate the buffer to fit the 
 *                needed size.
 * @param length  IN - length of object
 *
 * @return 1 on success, 0 on error
 */
int
asn_realloc_rbuild_length(u_char ** pkt, size_t * pkt_len,
                          size_t * offset, int r, size_t length)
{
    static const char *errpre = "build length";
    char            ebuf[128];
    int             tmp_int;
    size_t          start_offset = *offset;

    if (length <= 0x7f) {
        if (((*pkt_len - *offset) < 1)
            && !(r && asn_realloc(pkt, pkt_len))) {
            snprintf(ebuf, sizeof(ebuf),
                    "%s: bad length < 1 :%ld, %lu", errpre,
                    (long)(*pkt_len - *offset), (unsigned long)length);
            ebuf[ sizeof(ebuf)-1 ] = 0;
            ERROR_MSG(ebuf);
            return 0;
        }
        *(*pkt + *pkt_len - (++*offset)) = length;
    } else {
        while (length > 0xff) {
            if (((*pkt_len - *offset) < 1)
                && !(r && asn_realloc(pkt, pkt_len))) {
                snprintf(ebuf, sizeof(ebuf),
                        "%s: bad length < 1 :%ld, %lu", errpre,
                        (long)(*pkt_len - *offset), (unsigned long)length);
                ebuf[ sizeof(ebuf)-1 ] = 0;
                ERROR_MSG(ebuf);
                return 0;
            }
            *(*pkt + *pkt_len - (++*offset)) = length & 0xff;
            length >>= 8;
        }

        while ((*pkt_len - *offset) < 2) {
            if (!(r && asn_realloc(pkt, pkt_len))) {
                snprintf(ebuf, sizeof(ebuf),
                        "%s: bad length < 1 :%ld, %lu", errpre,
                        (long)(*pkt_len - *offset), (unsigned long)length);
                ebuf[ sizeof(ebuf)-1 ] = 0;
                ERROR_MSG(ebuf);
                return 0;
            }
        }

        *(*pkt + *pkt_len - (++*offset)) = length & 0xff;
        tmp_int = *offset - start_offset;
        *(*pkt + *pkt_len - (++*offset)) = tmp_int | 0x80;
    }

    return 1;
}

/**
 * @internal
 * builds an ASN header for an object with the ID and
 * length specified.
 *
 * @see asn_build_header
 * 
 * @param pkt     IN/OUT address of the begining of the buffer.
 * @param pkt_len IN/OUT address to an integer containing the size of pkt.
 * @param offset  IN/OUT offset to the start of the buffer where to write
 * @param r       IN if not zero reallocate the buffer to fit the 
 *                needed size.
 * @param type   IN - type of object
 * @param length   IN - length of object
 *
 * @return 1 on success, 0 on error
 */
int
asn_realloc_rbuild_header(u_char ** pkt, size_t * pkt_len,
                          size_t * offset, int r,
                          u_char type, size_t length)
{
    char            ebuf[128];

    if (asn_realloc_rbuild_length(pkt, pkt_len, offset, r, length)) {
        if (((*pkt_len - *offset) < 1)
            && !(r && asn_realloc(pkt, pkt_len))) {
            snprintf(ebuf, sizeof(ebuf),
                    "bad header length < 1 :%ld, %lu",
                    (long)(*pkt_len - *offset), (unsigned long)length);
            ebuf[ sizeof(ebuf)-1 ] = 0;
            ERROR_MSG(ebuf);
            return 0;
        }
        *(*pkt + *pkt_len - (++*offset)) = type;
        return 1;
    }
    return 0;
}

/**
 * @internal
 * builds an ASN object containing an int.
 *
 * @see asn_build_int
 * 
 * @param pkt     IN/OUT address of the begining of the buffer.
 * @param pkt_len IN/OUT address to an integer containing the size of pkt.
 * @param offset  IN/OUT offset to the start of the buffer where to write
 * @param r       IN if not zero reallocate the buffer to fit the 
 *                needed size.
 * @param type    IN - type of object
 * @param intp    IN - pointer to start of long integer
 * @param intsize IN - size of input buffer
 *
 * @return 1 on success, 0 on error
 */
int
asn_realloc_rbuild_int(u_char ** pkt, size_t * pkt_len,
                       size_t * offset, int r,
                       u_char type, const long *intp, size_t intsize)
{
    static const char *errpre = "build int";
    register long   integer = *intp;
    int             testvalue = (*intp < 0) ? -1 : 0;
    size_t          start_offset = *offset;

    if (intsize != sizeof(long)) {
        _asn_size_err(errpre, intsize, sizeof(long));
        return 0;
    }

    if (((*pkt_len - *offset) < 1) && !(r && asn_realloc(pkt, pkt_len))) {
        return 0;
    }
    *(*pkt + *pkt_len - (++*offset)) = (u_char) integer;
    integer >>= 8;

    while (integer != testvalue) {
        if (((*pkt_len - *offset) < 1)
            && !(r && asn_realloc(pkt, pkt_len))) {
            return 0;
        }
        *(*pkt + *pkt_len - (++*offset)) = (u_char) integer;
        integer >>= 8;
    }

    if ((*(*pkt + *pkt_len - *offset) & 0x80) != (testvalue & 0x80)) {
        /*
         * Make sure left most bit is representational of the rest of the bits
         * that aren't encoded.  
         */
        if (((*pkt_len - *offset) < 1)
            && !(r && asn_realloc(pkt, pkt_len))) {
            return 0;
        }
        *(*pkt + *pkt_len - (++*offset)) = testvalue & 0xff;
    }

    if (asn_realloc_rbuild_header(pkt, pkt_len, offset, r, type,
                                  (*offset - start_offset))) {
        if (_asn_realloc_build_header_check(errpre, pkt, pkt_len,
                                            (*offset - start_offset))) {
            return 0;
        } else {
            DEBUGDUMPSETUP("send", (*pkt + *pkt_len - *offset),
                           (*offset - start_offset));
            DEBUGMSG(("dumpv_send", "  Integer:\t%ld (0x%.2X)\n", *intp,
                      *intp));
            return 1;
        }
    }

    return 0;
}

/**
 * @internal
 * builds an ASN object containing an string.
 *
 * @see asn_build_string 
 * 
 * @param pkt     IN/OUT address of the begining of the buffer.
 * @param pkt_len IN/OUT address to an integer containing the size of pkt.
 * @param offset  IN/OUT offset to the start of the buffer where to write
 * @param r       IN if not zero reallocate the buffer to fit the 
 *                needed size.
 * @param type    IN - type of object
 * @param string    IN - pointer to start of the string
 * @param strlength IN - size of input buffer
 *
 * @return 1 on success, 0 on error
 */

int
asn_realloc_rbuild_string(u_char ** pkt, size_t * pkt_len,
                          size_t * offset, int r,
                          u_char type,
                          const u_char * string, size_t strlength)
{
    static const char *errpre = "build string";
    size_t          start_offset = *offset;

    while ((*pkt_len - *offset) < strlength) {
        if (!(r && asn_realloc(pkt, pkt_len))) {
            return 0;
        }
    }

    *offset += strlength;
    memcpy(*pkt + *pkt_len - *offset, string, strlength);

    if (asn_realloc_rbuild_header
        (pkt, pkt_len, offset, r, type, strlength)) {
        if (_asn_realloc_build_header_check
            (errpre, pkt, pkt_len, strlength)) {
            return 0;
        } else {
            DEBUGDUMPSETUP("send", (*pkt + *pkt_len - *offset),
                           *offset - start_offset);
            DEBUGIF("dumpv_send") {
                if (strlength == 0) {
                    DEBUGMSG(("dumpv_send", "  String: [NULL]\n"));
                } else {
                    u_char         *buf = (u_char *) malloc(2 * strlength);
                    size_t          l =
                        (buf != NULL) ? (2 * strlength) : 0, ol = 0;

                    if (sprint_realloc_asciistring
                        (&buf, &l, &ol, 1, string, strlength)) {
                        DEBUGMSG(("dumpv_send", "  String:\t%s\n", buf));
                    } else {
                        if (buf == NULL) {
                            DEBUGMSG(("dumpv_send",
                                      "  String:\t[TRUNCATED]\n"));
                        } else {
                            DEBUGMSG(("dumpv_send",
                                      "  String:\t%s [TRUNCATED]\n", buf));
                        }
                    }
                    if (buf != NULL) {
                        free(buf);
                    }
                }
            }
        }
        return 1;
    }

    return 0;
}

/**
 * @internal
 * builds an ASN object containing an unsigned int.
 *
 * @see asn_build_unsigned_int
 * 
 * @param pkt     IN/OUT address of the begining of the buffer.
 * @param pkt_len IN/OUT address to an integer containing the size of pkt.
 * @param offset  IN/OUT offset to the start of the buffer where to write
 * @param r       IN if not zero reallocate the buffer to fit the 
 *                needed size.
 * @param type    IN - type of object
 * @param intp    IN - pointer to start of unsigned int
 * @param intsize IN - size of input buffer
 *
 * @return 1 on success, 0 on error
 */
int
asn_realloc_rbuild_unsigned_int(u_char ** pkt, size_t * pkt_len,
                                size_t * offset, int r,
                            u_char type, const u_long * intp, size_t intsize)
{
    static const char *errpre = "build uint";
    register u_long integer = *intp;
    size_t          start_offset = *offset;

    if (intsize != sizeof(unsigned long)) {
        _asn_size_err(errpre, intsize, sizeof(unsigned long));
        return 0;
    }

    if (((*pkt_len - *offset) < 1) && !(r && asn_realloc(pkt, pkt_len))) {
        return 0;
    }
    *(*pkt + *pkt_len - (++*offset)) = (u_char) integer;
    integer >>= 8;

    while (integer != 0) {
        if (((*pkt_len - *offset) < 1)
            && !(r && asn_realloc(pkt, pkt_len))) {
            return 0;
        }
        *(*pkt + *pkt_len - (++*offset)) = (u_char) integer;
        integer >>= 8;
    }

    if ((*(*pkt + *pkt_len - *offset) & 0x80) != (0 & 0x80)) {
        /*
         * Make sure left most bit is representational of the rest of the bits
         * that aren't encoded.  
         */
        if (((*pkt_len - *offset) < 1)
            && !(r && asn_realloc(pkt, pkt_len))) {
            return 0;
        }
        *(*pkt + *pkt_len - (++*offset)) = 0;
    }

    if (asn_realloc_rbuild_header(pkt, pkt_len, offset, r, type,
                                  (*offset - start_offset))) {
        if (_asn_realloc_build_header_check(errpre, pkt, pkt_len,
                                            (*offset - start_offset))) {
            return 0;
        } else {
            DEBUGDUMPSETUP("send", (*pkt + *pkt_len - *offset),
                           (*offset - start_offset));
            DEBUGMSG(("dumpv_send", "  UInteger:\t%lu (0x%.2X)\n", *intp,
                      *intp));
            return 1;
        }
    }

    return 0;
}

/**
 * @internal
 * builds an ASN object containing an sequence.
 *
 * @see asn_build_sequence
 * 
 * @param pkt     IN/OUT address of the begining of the buffer.
 * @param pkt_len IN/OUT address to an integer containing the size of pkt.
 * @param offset  IN/OUT offset to the start of the buffer where to write
 * @param r       IN if not zero reallocate the buffer to fit the 
 *                needed size.
 * @param type    IN - type of object
 * @param length IN - length of object
 *
 * @return 1 on success, 0 on error
 */

int
asn_realloc_rbuild_sequence(u_char ** pkt, size_t * pkt_len,
                            size_t * offset, int r,
                            u_char type, size_t length)
{
    return asn_realloc_rbuild_header(pkt, pkt_len, offset, r, type,
                                     length);
}

/**
 * @internal
 * builds an ASN object containing an objid.
 *
 * @see asn_build_objid
 * 
 * @param pkt     IN/OUT address of the begining of the buffer.
 * @param pkt_len IN/OUT address to an integer containing the size of pkt.
 * @param offset  IN/OUT offset to the start of the buffer where to write
 * @param r       IN if not zero reallocate the buffer to fit the 
 *                needed size.
 * @param type    IN - type of object
 * @param objid   IN - pointer to the object id
 * @param objidlength  IN - length of the input 
 *
 * @return 1 on success, 0 on error
 */

int
asn_realloc_rbuild_objid(u_char ** pkt, size_t * pkt_len,
                         size_t * offset, int r,
                         u_char type,
                         const oid * objid, size_t objidlength)
{
    /*
     * ASN.1 objid ::= 0x06 asnlength subidentifier {subidentifier}*
     * subidentifier ::= {leadingbyte}* lastbyte
     * leadingbyte ::= 1 7bitvalue
     * lastbyte ::= 0 7bitvalue
     */
    register size_t i;
    register oid    tmpint;
    size_t          start_offset = *offset;
    const char     *errpre = "build objid";

    /*
     * Check if there are at least 2 sub-identifiers.  
     */
    if (objidlength == 0) {
        /*
         * There are not, so make OID have two with value of zero.  
         */
        while ((*pkt_len - *offset) < 2) {
            if (!(r && asn_realloc(pkt, pkt_len))) {
                return 0;
            }
        }

        *(*pkt + *pkt_len - (++*offset)) = 0;
        *(*pkt + *pkt_len - (++*offset)) = 0;
    } else if (objid[0] > 2) {
        ERROR_MSG("build objid: bad first subidentifier");
        return 0;
    } else if (objidlength == 1) {
        /*
         * Encode the first value.  
         */
        if (((*pkt_len - *offset) < 1)
            && !(r && asn_realloc(pkt, pkt_len))) {
            return 0;
        }
        *(*pkt + *pkt_len - (++*offset)) = (u_char) objid[0];
    } else {
        for (i = objidlength; i > 2; i--) {
            tmpint = objid[i - 1];

            if (((*pkt_len - *offset) < 1)
                && !(r && asn_realloc(pkt, pkt_len))) {
                return 0;
            }
            *(*pkt + *pkt_len - (++*offset)) = (u_char) tmpint & 0x7f;
            tmpint >>= 7;

            while (tmpint > 0) {
                if (((*pkt_len - *offset) < 1)
                    && !(r && asn_realloc(pkt, pkt_len))) {
                    return 0;
                }
                *(*pkt + *pkt_len - (++*offset)) =
                    (u_char) ((tmpint & 0x7f) | 0x80);
                tmpint >>= 7;
            }
        }

        /*
         * Combine the first two values.  
         */
        if ((objid[1] > 40) &&
            (objid[0] < 2)) {
            ERROR_MSG("build objid: bad second subidentifier");
            return 0;
        }
        tmpint = ((objid[0] * 40) + objid[1]);
        if (((*pkt_len - *offset) < 1)
            && !(r && asn_realloc(pkt, pkt_len))) {
            return 0;
        }
        *(*pkt + *pkt_len - (++*offset)) = (u_char) tmpint & 0x7f;
        tmpint >>= 7;

        while (tmpint > 0) {
            if (((*pkt_len - *offset) < 1)
                && !(r && asn_realloc(pkt, pkt_len))) {
                return 0;
            }
            *(*pkt + *pkt_len - (++*offset)) =
                (u_char) ((tmpint & 0x7f) | 0x80);
            tmpint >>= 7;
        }
    }

    tmpint = *offset - start_offset;
    if (asn_realloc_rbuild_header(pkt, pkt_len, offset, r, type,
                                  (*offset - start_offset))) {
        if (_asn_realloc_build_header_check(errpre, pkt, pkt_len,
                                            (*offset - start_offset))) {
            return 0;
        } else {
            DEBUGDUMPSETUP("send", (*pkt + *pkt_len - *offset),
                           (*offset - start_offset));
            DEBUGMSG(("dumpv_send", "  ObjID: "));
            DEBUGMSGOID(("dumpv_send", objid, objidlength));
            DEBUGMSG(("dumpv_send", "\n"));
            return 1;
        }
    }

    return 0;
}

/**
 * @internal
 * builds an ASN object containing an null object.
 *
 * @see asn_build_null
 * 
 * @param pkt     IN/OUT address of the begining of the buffer.
 * @param pkt_len IN/OUT address to an integer containing the size of pkt.
 * @param offset  IN/OUT offset to the start of the buffer where to write
 * @param r       IN if not zero reallocate the buffer to fit the 
 *                needed size.
 * @param type    IN - type of object
 *
 * @return 1 on success, 0 on error
 */

int
asn_realloc_rbuild_null(u_char ** pkt, size_t * pkt_len,
                        size_t * offset, int r, u_char type)
{
    /*
     * ASN.1 null ::= 0x05 0x00
     */
    size_t          start_offset = *offset;

    if (asn_realloc_rbuild_header(pkt, pkt_len, offset, r, type, 0)) {
        DEBUGDUMPSETUP("send", (*pkt + *pkt_len - *offset),
                       (*offset - start_offset));
        DEBUGMSG(("dumpv_send", "  NULL\n"));
        return 1;
    } else {
        return 0;
    }
}

/**
 * @internal
 * builds an ASN object containing an bitstring.
 *
 * @see asn_build_bitstring
 * 
 * @param pkt     IN/OUT address of the begining of the buffer.
 * @param pkt_len IN/OUT address to an integer containing the size of pkt.
 * @param offset  IN/OUT offset to the start of the buffer where to write
 * @param r       IN if not zero reallocate the buffer to fit the 
 *                needed size.
 * @param type    IN - type of object
 * @param string   IN - pointer to the string
 * @param strlength  IN - length of the input 
 *
 * @return 1 on success, 0 on error
 */

int
asn_realloc_rbuild_bitstring(u_char ** pkt, size_t * pkt_len,
                             size_t * offset, int r,
                             u_char type,
                             const u_char * string, size_t strlength)
{
    /*
     * ASN.1 bit string ::= 0x03 asnlength unused {byte}*
     */
    static const char *errpre = "build bitstring";
    size_t          start_offset = *offset;

    while ((*pkt_len - *offset) < strlength) {
        if (!(r && asn_realloc(pkt, pkt_len))) {
            return 0;
        }
    }

    *offset += strlength;
    memcpy(*pkt + *pkt_len - *offset, string, strlength);

    if (asn_realloc_rbuild_header
        (pkt, pkt_len, offset, r, type, strlength)) {
        if (_asn_realloc_build_header_check
            (errpre, pkt, pkt_len, strlength)) {
            return 0;
        } else {
            DEBUGDUMPSETUP("send", (*pkt + *pkt_len - *offset),
                           *offset - start_offset);
            DEBUGIF("dumpv_send") {
                if (strlength == 0) {
                    DEBUGMSG(("dumpv_send", "  Bitstring: [NULL]\n"));
                } else {
                    u_char         *buf = (u_char *) malloc(2 * strlength);
                    size_t          l =
                        (buf != NULL) ? (2 * strlength) : 0, ol = 0;

                    if (sprint_realloc_asciistring
                        (&buf, &l, &ol, 1, string, strlength)) {
                        DEBUGMSG(("dumpv_send", "  Bitstring:\t%s\n",
                                  buf));
                    } else {
                        if (buf == NULL) {
                            DEBUGMSG(("dumpv_send",
                                      "  Bitstring:\t[TRUNCATED]\n"));
                        } else {
                            DEBUGMSG(("dumpv_send",
                                      "  Bitstring:\t%s [TRUNCATED]\n",
                                      buf));
                        }
                    }
                    if (buf != NULL) {
                        free(buf);
                    }
                }
            }
        }
        return 1;
    }

    return 0;
}

/**
 * @internal
 * builds an ASN object containing an unsigned int64.
 *
 * @see asn_build_unsigned_int64
 * 
 * @param pkt     IN/OUT address of the begining of the buffer.
 * @param pkt_len IN/OUT address to an integer containing the size of pkt.
 * @param offset  IN/OUT offset to the start of the buffer where to write
 * @param r       IN if not zero reallocate the buffer to fit the 
 *                needed size.
 * @param type    IN - type of object
 * @param cp           IN - pointer to counter struct
 * @param countersize  IN - size of input buffer
 *
 * @return 1 on success, 0 on error
 */
int
asn_realloc_rbuild_unsigned_int64(u_char ** pkt, size_t * pkt_len,
                                  size_t * offset, int r,
                                  u_char type,
                               const struct counter64 *cp, size_t countersize)
{
    /*
     * ASN.1 integer ::= 0x02 asnlength byte {byte}*
     */
    register u_long low = cp->low, high = cp->high;
    size_t          intsize, start_offset = *offset;
    int             count;

    if (countersize != sizeof(struct counter64)) {
        _asn_size_err("build uint64", countersize,
                      sizeof(struct counter64));
        return 0;
    }

    /*
     * Encode the low 4 bytes first.  
     */
    if (((*pkt_len - *offset) < 1) && !(r && asn_realloc(pkt, pkt_len))) {
        return 0;
    }
    *(*pkt + *pkt_len - (++*offset)) = (u_char) low;
    low >>= 8;
    count = 1;

    while (low != 0) {
        count++;
        if (((*pkt_len - *offset) < 1)
            && !(r && asn_realloc(pkt, pkt_len))) {
            return 0;
        }
        *(*pkt + *pkt_len - (++*offset)) = (u_char) low;
        low >>= 8;
    }

    /*
     * Then the high byte if present.  
     */
    if (high) {
        /*
         * Do the rest of the low byte.  
         */
        for (; count < 4; count++) {
            if (((*pkt_len - *offset) < 1)
                && !(r && asn_realloc(pkt, pkt_len))) {
                return 0;
            }
            *(*pkt + *pkt_len - (++*offset)) = 0;
        }

        /*
         * Do high byte.  
         */
        if (((*pkt_len - *offset) < 1)
            && !(r && asn_realloc(pkt, pkt_len))) {
            return 0;
        }
        *(*pkt + *pkt_len - (++*offset)) = (u_char) high;
        high >>= 8;

        while (high != 0) {
            if (((*pkt_len - *offset) < 1)
                && !(r && asn_realloc(pkt, pkt_len))) {
                return 0;
            }
            *(*pkt + *pkt_len - (++*offset)) = (u_char) high;
            high >>= 8;
        }
    }

    if ((*(*pkt + *pkt_len - *offset) & 0x80) != (0 & 0x80)) {
        /*
         * Make sure left most bit is representational of the rest of the bits
         * that aren't encoded.  
         */
        if (((*pkt_len - *offset) < 1)
            && !(r && asn_realloc(pkt, pkt_len))) {
            return 0;
        }
        *(*pkt + *pkt_len - (++*offset)) = 0;
    }

    intsize = *offset - start_offset;

#ifdef OPAQUE_SPECIAL_TYPES
    /*
     * Encode a Counter64 as an opaque (it also works in SNMPv1).  
     */
    if (type == ASN_OPAQUE_COUNTER64) {
        while ((*pkt_len - *offset) < 5) {
            if (!(r && asn_realloc(pkt, pkt_len))) {
                return 0;
            }
        }

        *(*pkt + *pkt_len - (++*offset)) = (u_char) intsize;
        *(*pkt + *pkt_len - (++*offset)) = ASN_OPAQUE_COUNTER64;
        *(*pkt + *pkt_len - (++*offset)) = ASN_OPAQUE_TAG1;

        /*
         * Put the tag and length for the Opaque wrapper.  
         */
        if (asn_realloc_rbuild_header(pkt, pkt_len, offset, r,
                                      ASN_OPAQUE, intsize + 3)) {
            if (_asn_realloc_build_header_check
                ("build counter u64", pkt, pkt_len, intsize + 3)) {
                return 0;
            }
        } else {
            return 0;
        }
    } else if (type == ASN_OPAQUE_U64) {
        /*
         * Encode the Unsigned int64 in an opaque.  
         */
        while ((*pkt_len - *offset) < 5) {
            if (!(r && asn_realloc(pkt, pkt_len))) {
                return 0;
            }
        }

        *(*pkt + *pkt_len - (++*offset)) = (u_char) intsize;
        *(*pkt + *pkt_len - (++*offset)) = ASN_OPAQUE_U64;
        *(*pkt + *pkt_len - (++*offset)) = ASN_OPAQUE_TAG1;

        /*
         * Put the tag and length for the Opaque wrapper.  
         */
        if (asn_realloc_rbuild_header(pkt, pkt_len, offset, r,
                                      ASN_OPAQUE, intsize + 3)) {
            if (_asn_realloc_build_header_check
                ("build counter u64", pkt, pkt_len, intsize + 3)) {
                return 0;
            }
        } else {
            return 0;
        }
    } else {

#endif                          /* OPAQUE_SPECIAL_TYPES */
        if (asn_realloc_rbuild_header
            (pkt, pkt_len, offset, r, type, intsize)) {
            if (_asn_realloc_build_header_check
                ("build uint64", pkt, pkt_len, intsize)) {
                return 0;
            }
        } else {
            return 0;
        }
#ifdef OPAQUE_SPECIAL_TYPES
    }
#endif                          /* OPAQUE_SPECIAL_TYPES */

    DEBUGDUMPSETUP("send", (*pkt + *pkt_len - *offset), intsize);
    DEBUGMSG(("dumpv_send", "  U64:\t%lu %lu\n", cp->high, cp->low));
    return 1;
}

#ifdef OPAQUE_SPECIAL_TYPES


/**
 * @internal
 * builds an ASN object containing an signed int64.
 *
 * @see asn_build_signed_int64
 * 
 * @param pkt     IN/OUT address of the begining of the buffer.
 * @param pkt_len IN/OUT address to an integer containing the size of pkt.
 * @param offset  IN/OUT offset to the start of the buffer where to write
 * @param r       IN if not zero reallocate the buffer to fit the 
 *                needed size.
 * @param type    IN - type of object
 * @param cp           IN - pointer to counter struct
 * @param countersize  IN - size of input buffer
 *
 * @return 1 on success, 0 on error
 */
int
asn_realloc_rbuild_signed_int64(u_char ** pkt, size_t * pkt_len,
                                size_t * offset, int r,
                                u_char type,
                                const struct counter64 *cp, size_t countersize)
{
    /*
     * ASN.1 integer ::= 0x02 asnlength byte {byte}*
     */
    register u_long low = cp->low, high = cp->high;
    size_t          intsize, start_offset = *offset;
    int             count, testvalue = (high & 0x80000000) ? -1 : 0;

    if (countersize != sizeof(struct counter64)) {
        _asn_size_err("build uint64", countersize,
                      sizeof(struct counter64));
        return 0;
    }

    /*
     * Encode the low 4 bytes first.  
     */
    if (((*pkt_len - *offset) < 1) && !(r && asn_realloc(pkt, pkt_len))) {
        return 0;
    }
    *(*pkt + *pkt_len - (++*offset)) = (u_char) low;
    low >>= 8;
    count = 1;

    while ((int) low != testvalue) {
        count++;
        if (((*pkt_len - *offset) < 1)
            && !(r && asn_realloc(pkt, pkt_len))) {
            return 0;
        }
        *(*pkt + *pkt_len - (++*offset)) = (u_char) low;
        low >>= 8;
    }

    /*
     * Then the high byte if present.  
     */
    if (high) {
        /*
         * Do the rest of the low byte.  
         */
        for (; count < 4; count++) {
            if (((*pkt_len - *offset) < 1)
                && !(r && asn_realloc(pkt, pkt_len))) {
                return 0;
            }
            *(*pkt + *pkt_len - (++*offset)) = (testvalue == 0) ? 0 : 0xff;
        }

        /*
         * Do high byte.  
         */
        if (((*pkt_len - *offset) < 1)
            && !(r && asn_realloc(pkt, pkt_len))) {
            return 0;
        }
        *(*pkt + *pkt_len - (++*offset)) = (u_char) high;
        high >>= 8;

        while ((int) high != testvalue) {
            if (((*pkt_len - *offset) < 1)
                && !(r && asn_realloc(pkt, pkt_len))) {
                return 0;
            }
            *(*pkt + *pkt_len - (++*offset)) = (u_char) high;
            high >>= 8;
        }
    }

    if ((*(*pkt + *pkt_len - *offset) & 0x80) != (0 & 0x80)) {
        /*
         * Make sure left most bit is representational of the rest of the bits
         * that aren't encoded.  
         */
        if (((*pkt_len - *offset) < 1)
            && !(r && asn_realloc(pkt, pkt_len))) {
            return 0;
        }
        *(*pkt + *pkt_len - (++*offset)) = (testvalue == 0) ? 0 : 0xff;
    }

    intsize = *offset - start_offset;

    while ((*pkt_len - *offset) < 5) {
        if (!(r && asn_realloc(pkt, pkt_len))) {
            return 0;
        }
    }

    *(*pkt + *pkt_len - (++*offset)) = (u_char) intsize;
    *(*pkt + *pkt_len - (++*offset)) = ASN_OPAQUE_I64;
    *(*pkt + *pkt_len - (++*offset)) = ASN_OPAQUE_TAG1;

    /*
     * Put the tag and length for the Opaque wrapper.  
     */
    if (asn_realloc_rbuild_header(pkt, pkt_len, offset, r,
                                  ASN_OPAQUE, intsize + 3)) {
        if (_asn_realloc_build_header_check
            ("build counter u64", pkt, pkt_len, intsize + 3)) {
            return 0;
        }
    } else {
        return 0;
    }

    DEBUGDUMPSETUP("send", (*pkt + *pkt_len - *offset), intsize);
    DEBUGMSG(("dumpv_send", "  UInt64:\t%lu %lu\n", cp->high, cp->low));
    return 1;
}

/**
 * @internal
 * builds an ASN object containing an float.
 *
 * @see asn_build_float
 * 
 * @param pkt     IN/OUT address of the begining of the buffer.
 * @param pkt_len IN/OUT address to an integer containing the size of pkt.
 * @param offset  IN/OUT offset to the start of the buffer where to write
 * @param r       IN if not zero reallocate the buffer to fit the 
 *                needed size.
 * @param type       IN - type of object
 * @param floatp     IN - pointer to the float
 * @param floatsize  IN - size of input buffer
 *
 * @return 1 on success, 0 on error
 */

int
asn_realloc_rbuild_float(u_char ** pkt, size_t * pkt_len,
                         size_t * offset, int r,
                         u_char type, const float *floatp, size_t floatsize)
{
    size_t          start_offset = *offset;
    union {
        float           floatVal;
        int             intVal;
        u_char          c[sizeof(float)];
    } fu;

    /*
     * Floatsize better not be larger than realistic.  
     */
    if (floatsize != sizeof(float) || floatsize > 122) {
        return 0;
    }

    while ((*pkt_len - *offset) < floatsize + 3) {
        if (!(r && asn_realloc(pkt, pkt_len))) {
            return 0;
        }
    }

    /*
     * Correct for endian differences and copy value.  
     */
    fu.floatVal = *floatp;
    fu.intVal = htonl(fu.intVal);
    *offset += floatsize;
    memcpy(*pkt + *pkt_len - *offset, &(fu.c[0]), floatsize);

    /*
     * Put the special tag and length (3 bytes).  
     */
    *(*pkt + *pkt_len - (++*offset)) = (u_char) floatsize;
    *(*pkt + *pkt_len - (++*offset)) = ASN_OPAQUE_FLOAT;
    *(*pkt + *pkt_len - (++*offset)) = ASN_OPAQUE_TAG1;

    /*
     * Put the tag and length for the Opaque wrapper.  
     */
    if (asn_realloc_rbuild_header(pkt, pkt_len, offset, r,
                                  ASN_OPAQUE, floatsize + 3)) {
        if (_asn_realloc_build_header_check("build float", pkt, pkt_len,
                                            floatsize + 3)) {
            return 0;
        } else {
            DEBUGDUMPSETUP("send", (*pkt + *pkt_len - *offset),
                           *offset - start_offset);
            DEBUGMSG(("dumpv_send", "Opaque Float:\t%f\n", *floatp));
            return 1;
        }
    }

    return 0;
}

/**
 * @internal
 * builds an ASN object containing an double.
 *
 * @see asn_build_double
 * 
 * @param pkt     IN/OUT address of the begining of the buffer.
 * @param pkt_len IN/OUT address to an integer containing the size of pkt.
 * @param offset  IN/OUT offset to the start of the buffer where to write
 * @param r       IN if not zero reallocate the buffer to fit the 
 *                needed size.
 * @param type    IN - type of object
 * @param doublep           IN - pointer to double
 * @param doublesize  IN - size of input buffer
 *
 * @return 1 on success, 0 on error
 */

int
asn_realloc_rbuild_double(u_char ** pkt, size_t * pkt_len,
                          size_t * offset, int r,
                          u_char type, const double *doublep, size_t doublesize)
{
    size_t          start_offset = *offset;
    long            tmp;
    union {
        double          doubleVal;
        int             intVal[2];
        u_char          c[sizeof(double)];
    } fu;

    /*
     * Doublesize better not be larger than realistic.  
     */
    if (doublesize != sizeof(double) || doublesize > 122) {
        return 0;
    }

    while ((*pkt_len - *offset) < doublesize + 3) {
        if (!(r && asn_realloc(pkt, pkt_len))) {
            return 0;
        }
    }

    /*
     * Correct for endian differences and copy value.  
     */
    fu.doubleVal = *doublep;
    tmp = htonl(fu.intVal[0]);
    fu.intVal[0] = htonl(fu.intVal[1]);
    fu.intVal[1] = tmp;
    *offset += doublesize;
    memcpy(*pkt + *pkt_len - *offset, &(fu.c[0]), doublesize);

    /*
     * Put the special tag and length (3 bytes).  
     */
    *(*pkt + *pkt_len - (++*offset)) = (u_char) doublesize;
    *(*pkt + *pkt_len - (++*offset)) = ASN_OPAQUE_DOUBLE;
    *(*pkt + *pkt_len - (++*offset)) = ASN_OPAQUE_TAG1;

    /*
     * Put the tag and length for the Opaque wrapper.  
     */
    if (asn_realloc_rbuild_header(pkt, pkt_len, offset, r,
                                  ASN_OPAQUE, doublesize + 3)) {
        if (_asn_realloc_build_header_check("build float", pkt, pkt_len,
                                            doublesize + 3)) {
            return 0;
        } else {
            DEBUGDUMPSETUP("send", (*pkt + *pkt_len - *offset),
                           *offset - start_offset);
            DEBUGMSG(("dumpv_send", "  Opaque Double:\t%f\n", *doublep));
            return 1;
        }
    }

    return 0;
}

#endif                          /* OPAQUE_SPECIAL_TYPES */
#endif                          /*  USE_REVERSE_ASNENCODING  */
/**
 * @}
 */
