// asn1.cpp,v 1.13 2005/10/05 10:23:08 jwillemsen Exp

// ============================================================================
//
// = LIBRARY
//    asnmp
//
// = FILENAME
//     asn1.cpp
//
// = DESCRIPTION
//  The Vb class is an encapsulation of the snmp variable binding.
// This module contains the class definition for the variable binding (VB)
// class. The VB class is an encapsulation of a SNMP VB. A VB object is
// composed of one SNMP++ Oid and one SMI value. The Vb class utilizes Oid
// objects and thus requires the Oid class. To use this class,
// set oid, value then call valid() to be sure object was constructed correctly.
//
// = AUTHOR
//   S. Waldbusser (assumed)
//   Michael R MacFaden  mrm@cisco.com - rework & ACE port
// ============================================================================
/**********************************************************************
// *
   * Abstract Syntax Notation One, ASN.1
   * As defined in ISO/IS 8824 and ISO/IS 8825
   * This implements a subset of the above International Standards that
   * is sufficient to implement SNMP.
   *
   * Encodes abstract data types into a machine independent stream of bytes.
   *
   Copyright 1988, 1989, 1991, 1992 by Carnegie Mellon University

   All Rights Reserved

   Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee is hereby granted,
   provided that the above copyright notice appear in all copies and that
   both that copyright notice and this permission notice appear in
   supporting documentation, and that the name of CMU not be
   used in advertising or publicity pertaining to distribution of the
   software without specific, written prior permission.

   CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
   CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
   ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
   WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
   ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
   SOFTWARE.
   ******************************************************************/

#include "asnmp/asn1.h"
#include "asnmp/snmp.h"
#include "ace/OS_NS_string.h"

ACE_RCSID(asnmp, asn1, "asn1.cpp,v 1.13 2005/10/05 10:23:08 jwillemsen Exp")

/*
 * parse_int - pulls a long out of an ASN int type.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   following the end of this object.
 *
 *  Returns a pointer to the first byte past the end
 *   of this object (i.e. the start of the next object).
 *  Returns 0 on any error.
 */
u_char * asn1::parse_int( u_char *data,
                              int *datalength,
                              u_char *type,
                              long int *intp,
                              int intsize)
{
  ACE_TRACE("asn1::parse_int");
  /*
   * ASN.1 integer ::= 0x02 asnlength byte {byte}*
   */
  u_char *bufp = data;
  u_long            asn_length;
  long   value = 0;

  if (intsize != sizeof (long)){
    ASNERROR("not long");
    return 0;
  }
  *type = *bufp++;
  bufp =asn1::parse_length(bufp, &asn_length);
  if (bufp == 0){
    ASNERROR("bad length");
    return 0;
  }
  if ((int)(asn_length + (bufp - data)) > *datalength){
    ASNERROR("overflow of message");
    return 0;
  }
  if ((int)asn_length > intsize){
    ASNERROR("I don't support such large integers");
    return 0;
  }
  *datalength -= (int)asn_length + (bufp - data);
  if (*bufp & 0x80)
    value = -1; /* integer is negative */
  while(asn_length--)
    value = (value << 8) | *bufp++;
  *intp = value;
  return bufp;
}


/*
 * parse_unsigned_int - pulls an u_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 0 on any error.
 */
u_char * asn1::parse_unsigned_int( u_char *data,
                                       int *datalength,
                                       u_char *type,
                                       u_long *intp,
                                       int      intsize)
{
  ACE_TRACE("asn1::parse_unsigned_int");
  /*
   * ASN.1 integer ::= 0x02 asnlength byte {byte}*
   */
  u_char *bufp = data;
  u_long            asn_length;
  u_long value = 0;

  if (intsize != sizeof (long)){
    ASNERROR("not long");
    return 0;
  }
  *type = *bufp++;
  bufp = asn1::parse_length(bufp, &asn_length);
  if (bufp == 0){
    ASNERROR("bad length");
    return 0;
  }
  if ((int)(asn_length + (bufp - data)) > *datalength){
    ASNERROR("overflow of message");
    return 0;
  }
  if (((int)asn_length > (intsize + 1)) ||
      (((int)asn_length == intsize + 1) && *bufp != 0x00)){
    ASNERROR("I don't support such large integers");
    return 0;
  }
  *datalength -= (int)asn_length + (bufp - data);
  if (*bufp & 0x80)
    value = (u_long) -1;
  while(asn_length--)
    value = (value << 8) | *bufp++;
  *intp = value;
  return bufp;
}


/*
 * 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 0 on any error.
 */
u_char * asn1::build_int( u_char *data,
                              int *datalength,
                              u_char type,
                              long *intp,
                              int intsize)
{
  ACE_TRACE("asn1::build_int");
  /*
   * ASN.1 integer ::= 0x02 asnlength byte {byte}*
   */

  long integer;
  u_long mask;

  if (intsize != sizeof (long))
    return 0;
  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(u_long) - 1)) - 1);
  /* mask is 0xFF800000 on a big-endian machine */
  while((((integer & mask) == 0) || ((integer & mask) == mask))
        && intsize > 1){
    intsize--;
    integer <<= 8;
  }
  data = asn1::build_header(data, datalength, type, intsize);
  if (data == 0)
    return 0;
  if (*datalength < intsize)
    return 0;
  *datalength -= intsize;
  mask = u_long (0xFF) << (8 * (sizeof(u_long) - 1));
  /* mask is 0xFF000000 on a big-endian machine */
  while(intsize--){
    *data++ = (u_char)((integer & mask) >> (8 * (sizeof(long) - 1)));
    integer <<= 8;
  }
  return data;
}


/*
 * 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 0 on any error.
 */
u_char * asn1::build_unsigned_int( u_char *data,
                                       int *datalength,
                                       u_char type,
                                       u_long *intp,
                                       int intsize)
{
  ACE_TRACE("asn1::build_unsigned_int");
  /*
   * ASN.1 integer ::= 0x02 asnlength byte {byte}*
   */

  u_long integer;
  u_long mask;
  int add_null_byte = 0;

  if (intsize != sizeof (long))
    return 0;
  integer = *intp;
  mask = u_long (0xFF) << (8 * (sizeof(u_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++;
  }
  /*
   * 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(u_long) - 1)) - 1);
  /* mask is 0xFF800000 on a big-endian machine */
  while((((integer & mask) == 0) || ((integer & mask) == mask)) && intsize > 1){
    intsize--;
    integer <<= 8;
  }
  data = asn1::build_header(data, datalength, type, intsize);
  if (data == 0)
    return 0;
  if (*datalength < intsize)
    return 0;
  *datalength -= intsize;
  if (add_null_byte == 1){
    *data++ = '\0';
    intsize--;
  }
  mask = u_long (0xFF) << (8 * (sizeof(u_long) - 1));
  /* mask is 0xFF000000 on a big-endian machine */
  while(intsize--){
    *data++ = (u_char)((integer & mask) >> (8 * (sizeof(long) - 1)));
    integer <<= 8;
  }
  return data;
}


/*
 * parse_string - pulls an octet string out of an ASN octet string type.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   following the beginning of the next object.
 *
 *  "string" is filled with the octet string.
 *
 *  Returns a pointer to the first byte past the end
 *   of this object (i.e. the start of the next object).
 *  Returns 0 on any error.
 */
u_char * asn1::parse_string( u_char     *data,
                                 int *datalength,
                                 u_char *type,
                                 u_char *string,
                                 int *strlength)
{
  ACE_TRACE("asn1::parse_string");
  /*
   * ASN.1 octet string ::= primstring | cmpdstring
   * primstring ::= 0x04 asnlength byte {byte}*
   * cmpdstring ::= 0x24 asnlength string {string}*
   */
  u_char *bufp = data;
  u_long            asn_length;

  *type = *bufp++;
  bufp = asn1::parse_length(bufp, &asn_length);
  if (bufp == 0)
    return 0;
  if ((int)(asn_length + (bufp - data)) > *datalength){
    ASNERROR("overflow of message");
    return 0;
  }
  if ((int)asn_length > *strlength){
    ASNERROR("I don't support such long strings");
    return 0;
  }
  // fixed
  ACE_OS::memcpy((char *)string, (char *)bufp,  (int)asn_length);
  *strlength = (int)asn_length;
  *datalength -= (int)asn_length + (bufp - data);
  return bufp + asn_length;
}


/*
 * 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 0 on any error.
 */
u_char * asn1::build_string( u_char *data,
                                 int *datalength,
                                 u_char type,
                                 u_char *string,
                                 int strlength)
{
  ACE_TRACE("asn1::build_string");
  /*
   * ASN.1 octet string ::= primstring | cmpdstring
   * primstring ::= 0x04 asnlength byte {byte}*
   * cmpdstring ::= 0x24 asnlength string {string}*
   * This code will never send a compound string.
   */
  data = asn1::build_header(data, datalength, type, strlength);
  if (data == 0)
    return 0;
  if (*datalength < strlength)
    return 0;
  // fixed
  ACE_OS::memcpy((u_char *)data,(u_char *)string, strlength);
  *datalength -= strlength;
  return data + strlength;
}


/*
 * 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 0 on any error.
 */
u_char *asn1::parse_header( u_char *data,
                                int *datalength,
                                u_char *type)
{
  ACE_TRACE("asn1::parse_header");
  u_char *bufp = data;
  register int header_len;
  u_long            asn_length;

  /* this only works on data types < 30, i.e. no extension octets */
  if (IS_EXTENSION_ID(*bufp)){
    ASNERROR("can't process ID >= 30");
    return 0;
  }
  *type = *bufp;
  bufp = asn1::parse_length(bufp + 1, &asn_length);
  if (bufp == 0)
    return 0;
  header_len = bufp - data;
  if ((int)(header_len + asn_length) > *datalength){
    ASNERROR("asn length too long");
    return 0;
  }
  *datalength = (int)asn_length;
  return bufp;
}

/*
 * asn1::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 0 on any error.
 */
u_char * asn1::build_header( u_char *data,
                                 int *datalength,
                                 u_char type,
                                 int length)
{
  ACE_TRACE("asn1::build_header");
  if (*datalength < 1)
    return 0;
  *data++ = type;
  (*datalength)--;
  return asn1::build_length(data, datalength, length);

}

/*
 * asn_build_sequence - builds an ASN header for a sequence with the ID and
 * length specified.
 *  On entry, datalength is input as the number of valid bytes following
 *   "data".  On exit, it is returned as the number of valid bytes
 *   in this object following the id and length.
 *
 *  This only works on data types < 30, i.e. no extension octets.
 *  The maximum length is 0xFFFF;
 *
 *  Returns a pointer to the first byte of the contents of this object.
 *  Returns 0 on any error.
 */
u_char * asn1::build_sequence( u_char *data,
                                   int *datalength,
                                   u_char type,
                                   int length)
{
  ACE_TRACE("asn1::build_sequence");
  *datalength -= 4;
  if (*datalength < 0){
    *datalength += 4;   /* fix up before punting */
    return 0;
  }
  *data++ = type;
  *data++ = (u_char)(0x02 | ASN_LONG_LEN);
  *data++ = (u_char)((length >> 8) & 0xFF);
  *data++ = (u_char)(length & 0xFF);
  return data;
}

/*
 * 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 0 on any error.
 */
u_char * asn1::parse_length( u_char *data,
                                 u_long  *length)
{
  ACE_TRACE("asn1::parse_length");
  u_char lengthbyte = *data;

  if (lengthbyte & ASN_LONG_LEN){
    lengthbyte &= ~ASN_LONG_LEN;        /* turn MSb off */
    if (lengthbyte == 0){
      ASNERROR("We don't support indefinite lengths");
      return 0;
    }
    if (lengthbyte > sizeof(long)){
      ASNERROR("we can't support data lengths that long");
      return 0;
    }
    // fixed
    ACE_OS::memcpy((char *)length, (char *)data + 1, (int)lengthbyte);
    *length = ntohl(*length);
    *length >>= (8 * ((sizeof *length) - lengthbyte));
    return data + lengthbyte + 1;
  } else { /* short asnlength */
    *length = (long)lengthbyte;
    return data + 1;
  }
}

u_char *asn1::build_length( u_char *data,
                                int *datalength,
                                int length)
{
  ACE_TRACE("asn1::build_length");
  u_char    *start_data = data;

  /* no indefinite lengths sent */
  if (length < 0x80){
    if (*datalength < 1){
      ASNERROR("build_length");
      return 0;
    }
    *data++ = (u_char)length;
  } else if (length <= 0xFF){
    if (*datalength < 2){
      ASNERROR("build_length");
      return 0;
    }
    *data++ = (u_char)(0x01 | ASN_LONG_LEN);
    *data++ = (u_char)length;
  } else { /* 0xFF < length <= 0xFFFF */
    if (*datalength < 3){
      ASNERROR("build_length");
      return 0;
    }
    *data++ = (u_char)(0x02 | ASN_LONG_LEN);
    *data++ = (u_char)((length >> 8) & 0xFF);
    *data++ = (u_char)(length & 0xFF);
  }
  *datalength -= (data - start_data);
  return data;

}

/*
 * 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 0 on any error.
 */
u_char *asn1::parse_objid( u_char *data,
                               int *datalength,
                               u_char *type,
                               oid *objid,
                               int *objidlength)
{
  ACE_TRACE("asn1::parse_objid");
  /*
   * ASN.1 objid ::= 0x06 asnlength subidentifier {subidentifier}*
   * subidentifier ::= {leadingbyte}* lastbyte
   * leadingbyte ::= 1 7bitvalue
   * lastbyte ::= 0 7bitvalue
   */
  u_char *bufp = data;
  oid *oidp = objid + 1;
  u_long subidentifier;
  long   length;
  u_long            asn_length;

  *type = *bufp++;
  bufp = asn1::parse_length(bufp, &asn_length);
  if (bufp == 0)
    return 0;
  if ((int)asn_length + (bufp - data) > *datalength){
    ASNERROR("overflow of message");
    return 0;
  }
  *datalength -= (int)asn_length + (bufp - data);

  /* Handle invalid object identifier encodings of the form 06 00 robustly */
  if (asn_length == 0)
    objid[0] = objid[1] = 0;

  length = asn_length;
  (*objidlength)--;     /* account for expansion of first byte */
  while (length > 0 && (*objidlength)-- > 0){
    subidentifier = 0;
    do {        /* shift and add in low order 7 bits */
      subidentifier = (subidentifier << 7) + (*(u_char *)bufp & ~ASN_BIT8);
      length--;
    } while (*(u_char *)bufp++ & ASN_BIT8);     /* last byte has high bit clear */
    if (subidentifier > (u_long)MAX_SUBID){
      ASNERROR("subidentifier too long");
      return 0;
    }
    *oidp++ = (oid)subidentifier;
  }

  /*
   * The first two subidentifiers are encoded into the first component
   * with the value (X * 40) + Y, where:
   *    X is the value of the first subidentifier.
   *  Y is the value of the second subidentifier.
   */
  subidentifier = (u_long)objid[1];
  if (subidentifier == 0x2B){
    objid[0] = 1;
    objid[1] = 3;
  } else {
    objid[1] = (u_char)(subidentifier % 40);
    objid[0] = (u_char)((subidentifier - objid[1]) / 40);
  }

  *objidlength = (int)(oidp - objid);
  return bufp;
}

/*
 * 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 0 on any error.
 */
u_char *asn1::build_objid( u_char *data,
                               int *datalength,
                               u_char type,
                               oid *objid,
                               int objidlength)
{
  ACE_TRACE("asn1::build_objid");
  /*
   * ASN.1 objid ::= 0x06 asnlength subidentifier {subidentifier}*
   * subidentifier ::= {leadingbyte}* lastbyte
   * leadingbyte ::= 1 7bitvalue
   * lastbyte ::= 0 7bitvalue
   */
  u_char buf[MAX_OID_LEN];
  u_char *bp = buf;
  oid *op = objid;
  int    asnlength;
  u_long subid, mask, testmask;
  int bits, testbits;

  if (objidlength < 2){
    *bp++ = 0;
    objidlength = 0;
  } else {
    *bp++ = (u_char) (op[1] + (op[0] * 40));
    objidlength -= 2;
    op += 2;
  }

  while(objidlength-- > 0){
    subid = *op++;
    if (subid < 127){ /* off by one? */
      *bp++ = (u_char )subid;
    } else {
      mask = 0x7F; /* handle subid == 0 case */
      bits = 0;
      /* testmask *MUST* !!!! be of an u_type */
      for(testmask = 0x7F, testbits = 0; testmask != 0;
          testmask <<= 7, testbits += 7){
        if (subid & testmask){  /* if any bits set */
          mask = testmask;
          bits = testbits;
        }
      }
      /* mask can't be zero here */
      for(;mask != 0x7F; mask >>= 7, bits -= 7){
        /* fix a mask that got truncated above */
        if (mask == 0x1E00000)
          mask = 0xFE00000;
        *bp++ = (u_char)(((subid & mask) >> bits) | ASN_BIT8);
      }
      *bp++ = (u_char)(subid & mask);
    }
  }
  asnlength = bp - buf;
  data = asn1::build_header(data, datalength, type, asnlength);
  if (data == 0)
    return 0;
  if (*datalength < asnlength)
    return 0;
  // fixed
  ACE_OS::memcpy((char *)data, (char *)buf,  asnlength);
  *datalength -= asnlength;
  return data + asnlength;
}

/*
 * 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 0 on any error.
 */
u_char *asn1::parse_null(u_char *data,
                                int *datalength,
                                u_char *type)
{
  ACE_TRACE("asn1::parse_null");
  /*
   * ASN.1 null ::= 0x05 0x00
   */
  u_char   *bufp = data;
  u_long            asn_length;

  *type = *bufp++;
  bufp = asn1::parse_length(bufp, &asn_length);
  if (bufp == 0)
    return 0;
  if (asn_length != 0){
    ASNERROR("Malformed 0");
    return 0;
  }
  *datalength -= (bufp - data);
  return bufp + asn_length;
}


/*
 * 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 0 on any error.
 */
u_char *asn1::build_null( u_char *data,
                              int *datalength,
                              u_char type)
{
  ACE_TRACE("asn1::build_null");
  /*
   * ASN.1 null ::= 0x05 0x00
   */
  return asn1::build_header(data, datalength, type, 0);
}

/*
 * 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 0 on any error.
 */
u_char *asn1::parse_bitstring( u_char *data,
                                   int *datalength,
                                   u_char *type,
                                   u_char *string,
                                   int *strlength)
{
  ACE_TRACE("asn1::parse_bitstring");
  /*
   * bitstring ::= 0x03 asnlength unused {byte}*
   */
  u_char *bufp = data;
  u_long            asn_length;

  *type = *bufp++;
  bufp = asn1::parse_length(bufp, &asn_length);
  if (bufp == 0)
    return 0;
  if ((int)(asn_length + (bufp - data)) > *datalength){
    ASNERROR("overflow of message");
    return 0;
  }
  if ((int) asn_length > *strlength){
    ASNERROR("I don't support such long bitstrings");
    return 0;
  }
  if (asn_length < 1){
    ASNERROR("Invalid bitstring");
    return 0;
  }
  if (*bufp > 7){
    ASNERROR("Invalid bitstring");
    return 0;
  }
  // fixed
  ACE_OS::memcpy((char *)string,(char *)bufp,  (int)asn_length);
  *strlength = (int)asn_length;
  *datalength -= (int)asn_length + (bufp - data);
  return bufp + asn_length;
}


/*
 * 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 0 on any error.
 */
u_char *asn1::build_bitstring( u_char *data,
                                   int *datalength,
                                   u_char type,
                                   u_char *string,
                                   int strlength)
{
  ACE_TRACE("asn1::build_bitstring");
  /*
   * ASN.1 bit string ::= 0x03 asnlength unused {byte}*
   */
  if (strlength < 1 || *string || *string > 7){
    ASNERROR("Building invalid bitstring");
    return 0;
  }
  data = asn1::build_header(data, datalength, type, strlength);
  if (data == 0)
    return 0;
  if (*datalength < strlength)
    return 0;
  // fixed
  ACE_OS::memcpy((char *)data,(char *)string, strlength);
  *datalength -= strlength;
  return data + strlength;
}


/*
 * parse_unsigned_int64 - pulls a 64 bit u_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 0 on any error.
 */
u_char * asn1::parse_unsigned_int64(u_char *data,
                                         int *datalength,
                                         u_char *type,
                                         struct counter64 *cp,
                                         int countersize)
{
  ACE_TRACE("asn1::parse_unsigned_int64");
  /*
   * ASN.1 integer ::= 0x02 asnlength byte {byte}*
   */
  u_char *bufp = data;
  u_long            asn_length;
  u_long low = 0, high = 0;
  int intsize = 4;

  if (countersize != sizeof(struct counter64)){
    ASNERROR("not right size");
    return 0;
  }
  *type = *bufp++;
  bufp = asn1::parse_length(bufp, &asn_length);
  if (bufp == 0){
    ASNERROR("bad length");
    return 0;
  }
  if ((int)(asn_length + (bufp - data)) > *datalength){
    ASNERROR("overflow of message");
    return 0;
  }
  if (((int)asn_length > (intsize * 2 + 1)) ||
      (((int)asn_length == (intsize * 2) + 1) && *bufp != 0x00)){
    ASNERROR("I don't support such large integers");
    return 0;
  }
  *datalength -= (int)asn_length + (bufp - data);
  if (*bufp & 0x80){
    low = (u_long) -1; // integer is negative
    high = (u_long) -1;
  }
  while(asn_length--){
    high = (high << 8) | ((low & 0xFF000000) >> 24);
    low = (low << 8) | *bufp++;
  }
  cp->low = low;
  cp->high = high;
  return bufp;
}


/*
 * 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 0 on any error.
 */
u_char * asn1::build_unsigned_int64( u_char *data,
                                         int *datalength,
                                         u_char type,
                                         struct counter64 *cp,
                                         int countersize)
{
  ACE_TRACE("asn1::build_unsigned_int64");
  /*
   * ASN.1 integer ::= 0x02 asnlength byte {byte}*
   */

  u_long low, high;
  u_long mask, mask2;
  int add_null_byte = 0;
  int intsize;

  if (countersize != sizeof (struct counter64))
    return 0;
  intsize = 8;
  low = cp->low;
  high = cp->high;
  mask = u_long (0xFF) << (8 * (sizeof(u_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++;
  }
  /*
   * 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(u_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;
  }
  data = asn1::build_header(data, datalength, type, intsize);
  if (data == 0)
    return 0;
  if (*datalength < intsize)
    return 0;
  *datalength -= intsize;
  if (add_null_byte == 1){
    *data++ = '\0';
    intsize--;
  }
  while(intsize--){
    *data++ = (u_char)((high & mask) >> (8 * (sizeof(long) - 1)));
    high = (high << 8)
      | ((low & mask) >> (8 * (sizeof(long) - 1)));
    low <<= 8;

  }
  return data;
}


// create a pdu
struct snmp_pdu * cmu_snmp::pdu_create( int command)
{
  ACE_TRACE("cmu_snmp::snmp_pdu_create");
  struct snmp_pdu *pdu;

  ACE_NEW_RETURN(pdu, snmp_pdu, 0);
  ACE_OS::memset((char *)pdu, 0,sizeof(struct snmp_pdu));
  pdu->command = command;
  pdu->errstat = 0;
  pdu->errindex = 0;
  pdu->enterprise = 0;
  pdu->enterprise_length = 0;
  pdu->variables = 0;
  return pdu;
}

// release a pdu from memory
void cmu_snmp::free_pdu( struct snmp_pdu *pdu)
{
  ACE_TRACE("cmu_snmp::free_pdu");
  struct variable_list *vp, *ovp;

  vp = pdu->variables;
  while(vp){
    // release the oid part
    if (vp->name)
      delete [] vp->name;
    // if deep data, then release as well
    if (vp->val.string)
      delete [] vp->val.string;
    ovp = vp;
    // go to the next one
    vp = vp->next_variable;
    // release up vb itself
    delete ovp;
  }
  // if enterprise release it up
  if (pdu->enterprise)
    delete [] pdu->enterprise;
  // release up pdu itself
  delete pdu;
}


// add a null var to a pdu
void cmu_snmp::add_var(struct snmp_pdu *pdu,
                  oid *name,
                  int name_length,
                  SmiVALUE *smival)
{
  ACE_TRACE("cmu_snmp::add_var");

  struct variable_list *vars = 0;

  // if we don't have a vb list ,create one
  if (pdu->variables == 0) {
    ACE_NEW(pdu->variables, variable_list);
    vars = pdu->variables;
  }
  else
    { // we have one, find the end
      for(vars = pdu->variables; vars->next_variable; vars = vars->next_variable);
      // create one
      ACE_NEW(vars->next_variable, variable_list);
      // bump ptr
      vars = vars->next_variable;
    }

  // add the oid with no data
  vars->next_variable = 0;

  // hook in the Oid portion
  ACE_NEW(vars->name, oid[(name_length)]);

  // fixed
  ACE_OS::memcpy((char *)vars->name,(char *)name, name_length * sizeof(oid));
  vars->name_length = name_length;

  // hook in the SMI value
  switch( smival->syntax)
    {
      // null , do nothing
    case sNMP_SYNTAX_NULL:
    case sNMP_SYNTAX_NOSUCHOBJECT:
    case sNMP_SYNTAX_NOSUCHINSTANCE:
    case sNMP_SYNTAX_ENDOFMIBVIEW:
      {
        vars->type = (u_char) smival->syntax;
        vars->val.string = 0;
        vars->val_len = 0;
      }
      break;

      // octects
    case sNMP_SYNTAX_OCTETS:
    case sNMP_SYNTAX_OPAQUE:
    case sNMP_SYNTAX_IPADDR:
      {
        vars->type = (u_char) smival->syntax;
        ACE_NEW(vars->val.string,
                u_char[(unsigned)smival->value.string.len]);
        vars->val_len = (int) smival->value.string.len;
        ACE_OS::memcpy( (u_char *) vars->val.string,
                       (u_char *) smival->value.string.ptr,
                       (unsigned) smival->value.string.len);
      }
      break;

      // oid
    case sNMP_SYNTAX_OID:
      {
        vars->type = (u_char) smival->syntax;
        vars->val_len = (int) smival->value.oid.len * sizeof(oid);
        ACE_NEW(vars->val.objid, oid[(unsigned)vars->val_len]);
        ACE_OS::memcpy((u_long *)vars->val.objid,
                       (u_long *)smival->value.oid.ptr,
                       (unsigned) vars->val_len);
      }
      break;



    case sNMP_SYNTAX_TIMETICKS:
    case sNMP_SYNTAX_CNTR32:
    case sNMP_SYNTAX_GAUGE32:
    case sNMP_SYNTAX_UINT32:
      {
        long templong;
        vars->type = (u_char) smival->syntax;
        ACE_NEW(vars->val.integer, long);
        vars->val_len = sizeof(long);
        templong = (long) smival->value.uNumber;
        ACE_OS::memcpy( (long*) vars->val.integer,
                       (long*) &templong,
                       sizeof(long));
      }
      break;

    case sNMP_SYNTAX_INT32:
      {
        long templong;
        vars->type = (u_char) smival->syntax;
        ACE_NEW(vars->val.integer, long);
        vars->val_len = sizeof(long);
        templong = (long) smival->value.sNumber;
        ACE_OS::memcpy( (long*) vars->val.integer,
                       (long*) &templong,
                       sizeof(long));
      }
      break;

      // 64 bit counter
    case sNMP_SYNTAX_CNTR64:
      {
        vars->type = ( u_char) smival->syntax;
        ACE_NEW(vars->val.counter64, counter64);
        vars->val_len = sizeof(struct counter64);
        ACE_OS::memcpy( (struct counter64*) vars->val.counter64,
                       (SmiLPCNTR64) &(smival->value.hNumber),
                       sizeof( SmiCNTR64));
      }
      break;

    } // end switch

}

// build the authentication
// works for v1 or v2c
u_char *cmu_snmp::auth_build( u_char *data,
                               int *length,
                               long int version,
                               u_char *community,
                               int community_len,
                               int messagelen)
{
  ACE_TRACE("cmu_snmp::auth_build");
  u_char *params;
  int     plen;

  params = community;
  plen = community_len;

  data = asn1::build_sequence(data,
                            length,
                            (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR),
                            messagelen + plen + 5);
  if (data == 0){
    ASNERROR("buildheader");
    return 0;
  }
  data = asn1::build_int(data,
                       length,
                       (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
                       (long *)&version,
                       sizeof(version));
  if (data == 0){
    ASNERROR("buildint");
    return 0;
  }

  data = asn1::build_string(data,
                          length,
                          (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
                          params,
                          plen );
  if (data == 0){
    ASNERROR("buildstring");
    return 0;
  }

  return (u_char *)data;
}


// build a variable binding
u_char * cmu_snmp::build_var_op(u_char *data, oid * var_name,
                                  int *var_name_len,
                                  u_char var_val_type,
                                  int var_val_len, u_char *var_val,
                                  int *listlength)

{
  ACE_TRACE("cmu_snmp::build_var_op");
  int dummyLen, headerLen;
  u_char *dataPtr;

  dummyLen = *listlength;
  dataPtr = data;

  data += 4;
  dummyLen -=4;
  if (dummyLen < 0)
    return 0;

  headerLen = data - dataPtr;
  *listlength -= headerLen;
  data = asn1::build_objid( data, listlength,
                 (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
                         var_name, *var_name_len);
  if (data == 0) {
    ASNERROR("");
    return 0;
  }

  // based on the type...
  switch(var_val_type) {
  case ASN_INTEGER:
    data = asn1::build_int( data, listlength, var_val_type, (long *)var_val,
                         var_val_len);
    break;

  case SMI_GAUGE:
  case SMI_COUNTER:
  case SMI_TIMETICKS:
  case SMI_UINTEGER:
    data = asn1::build_unsigned_int( data,
                                  listlength,
                                  var_val_type,
                                  (u_long *)var_val,
                                  var_val_len);
    break;

  case SMI_COUNTER64:
    data = asn1::build_unsigned_int64(data,
                                    listlength,
                                    var_val_type,
                                    (struct counter64 *)var_val,
                                    var_val_len);
    break;

  case ASN_OCTET_STR:
  case SMI_IPADDRESS:
  case SMI_OPAQUE:
  case SMI_NSAP:
    data = asn1::build_string(data, listlength, var_val_type,
                            var_val, var_val_len);
    break;

  case ASN_OBJECT_ID:
    data = asn1::build_objid(data, listlength, var_val_type,
                           (oid *)var_val, var_val_len / sizeof(oid));
    break;

  case ASN_NULL:
    data = asn1::build_null(data, listlength, var_val_type);
    break;

  case ASN_BIT_STR:
    data = asn1::build_bitstring(data, listlength, var_val_type,
                               var_val, var_val_len);
    break;

  case SNMP_NOSUCHOBJECT:
  case SNMP_NOSUCHINSTANCE:
  case SNMP_ENDOFMIBVIEW:
    data = asn1::build_null(data, listlength, var_val_type);
    break;

  default:
    ASNERROR("wrong type");
    return 0;
  }
  if (data == 0) {
    ASNERROR("");
    return 0;
  }
  dummyLen = (data - dataPtr) - headerLen;

  asn1::build_sequence(dataPtr, &dummyLen,
                     (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR),
                     dummyLen);
  return data;
}


// serialize the pdu
int cmu_snmp::build( struct snmp_pdu *pdu, u_char *packet,
               int *out_length, long version,
               u_char* community, int community_len)
{
  ACE_TRACE("cmu_snmp::build");
  u_char buf[SNMP_MSG_LENGTH];
  u_char  *cp;
  struct variable_list *vp;
  int length;
  int totallength;

  length = *out_length;
  cp = packet;
  for(vp = pdu->variables; vp; vp = vp->next_variable) {
    cp = cmu_snmp::build_var_op( cp, vp->name,
                           &vp->name_length, vp->type,
                           vp->val_len, (u_char *)vp->val.string,
                           &length);
    if (cp == 0)
      return -1;
  }
  totallength = cp - packet;

  length = SNMP_MSG_LENGTH;

  // encode the total len
  cp = asn1::build_header( buf, &length,
                        (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR),
                        totallength);
  if (cp == 0)
    return -1;
  ACE_OS::memcpy( (char *)cp, (char *)packet,totallength);
  totallength += cp - buf;

  length = *out_length;
  if (pdu->command != TRP_REQ_MSG) {

    // request id
    cp = asn1::build_int( packet,
                       &length,
               (u_char )(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
                       (long *)&pdu->reqid,
                       sizeof(pdu->reqid));
    if (cp == 0)
      return -1;

    // error status
    cp = asn1::build_int(cp,
                       &length,
               (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
                       (long *)&pdu->errstat, sizeof(pdu->errstat));
    if (cp == 0)
      return -1;

    // error index
    cp = asn1::build_int(cp,
                       &length,
               (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
                       (long *)&pdu->errindex, sizeof(pdu->errindex));
    if (cp == 0)
      return -1;
  }
  else {        // this is a trap message

    // enterprise
    cp = asn1::build_objid( packet,
                         &length,
                 (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID),
                         (oid *)pdu->enterprise,
                         pdu->enterprise_length);
    if (cp == 0)
      return -1;

    // agent-addr
    cp = asn1::build_string(cp,
                            &length,
                            // HDN Fixed to use correct tag
                            (u_char)SMI_IPADDRESS,
                            //(u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR),
                          (u_char *)&pdu->agent_addr.sin_addr.s_addr,
                          sizeof(pdu->agent_addr.sin_addr.s_addr));
    if (cp == 0)
      return -1;

    // generic trap
    cp = asn1::build_int(cp,
                       &length,
               (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
                       (long *)&pdu->trap_type,
                       sizeof(pdu->trap_type));
    if (cp == 0)
      return -1;

    // specific trap
    cp = asn1::build_int( cp,
                       &length,
               (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
                       (long *)&pdu->specific_type,
                       sizeof(pdu->specific_type));
    if (cp == 0)
      return -1;

    // timestamp
    cp = asn1::build_int(cp,
                       &length,
                         // HDN Fixed to use correct tag
                         (u_char)SMI_TIMETICKS,
                         //(u_char )(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
                       (long *)&pdu->time,
                       sizeof(pdu->time));
    if (cp == 0)
      return -1;
  }

  if (length < totallength)
    return -1;
  // fixed
  ACE_OS::memcpy((char *)cp, (char *)buf, totallength);
  totallength += cp - packet;

  length = SNMP_MSG_LENGTH;
  cp = asn1::build_header(buf,
                        &length,
                        (u_char)pdu->command,
                        totallength);
  if (cp == 0)
    return -1;
  if (length < totallength)
    return -1;
  // fixed
  ACE_OS::memcpy((char *)cp, (char *)packet, totallength);
  totallength += cp - buf;

  length = *out_length;

  cp = cmu_snmp::auth_build( packet,
                       &length,
                       version,
                       community,
                       community_len,
                       totallength );
  if (cp == 0)
    return -1;
  if ((*out_length - (cp - packet)) < totallength)
    return -1;
  // fixed
  ACE_OS::memcpy((char *)cp, (char *)buf, totallength);
  totallength += cp - packet;
  *out_length = totallength;

  return 0;
}

// parse the authentication header
u_char *cmu_snmp::auth_parse(u_char *data,
                               int *length, u_char *sid,
                               int *slen, long  *version)
{
  ACE_TRACE("cmu_snmp::auth_parse");
  u_char type;

  // get the type
  data = asn1::parse_header( data, length, &type);
  if (data == 0){
    ASNERROR("bad header");
    return 0;
  }

  if (type != (ASN_SEQUENCE | ASN_CONSTRUCTOR)) {
    ASNERROR("wrong auth header type");
    return 0;
  }

  // get the version
  data = asn1::parse_int(data, length, &type, version, sizeof(*version));
  if (data == 0) {
    ASNERROR("bad parse of version");
    return 0;
  }

  // get the community name
  data = asn1::parse_string(data, length, &type, sid, slen);
  if (data == 0) {
    ASNERROR("bad parse of community");
    return 0;
  }

  return (u_char *)data;
}

/* u_char *data,  // IN - pointer to the start of object
  oid       *var_name,      // OUT - object id of variable
  int       *var_name_len,  // IN/OUT - length of variable name
  u_char  *var_val_type, // OUT - type of variable
                                 (int or octet string) (one byte)
  int       *var_val_len,     // OUT - length of variable
  u_char  **var_val,  // OUT - pointer to ASN1 encoded value of variable
*/

u_char *
cmu_snmp::parse_var_op( u_char *data, oid *var_name,
                       int *var_name_len,  u_char  *var_val_type,
                       int *var_val_len, u_char  **var_val,
                       int  *listlength)
{
  ACE_TRACE("cmu_snmp::parse_var_op");
  u_char var_op_type;
  int   var_op_len = *listlength;
  u_char *var_op_start = data;

  data = asn1::parse_header(data, &var_op_len, &var_op_type);
  if (data == 0){
    ASNERROR("");
    return 0;
  }
  if (var_op_type != (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR))
    return 0;
  data = asn1::parse_objid(data, &var_op_len, &var_op_type, var_name, var_name_len);
  if (data == 0) {
    ASNERROR("");
    return 0;
  }
  if (var_op_type != (u_char)
                     (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID))
    return 0;
  *var_val = data;      /* save pointer to this object */
  /* find out what type of object this is */
  data = asn1::parse_header(data, &var_op_len, var_val_type);
  if (data == 0) {
    ASNERROR("");
    return 0;
  }
  *var_val_len = var_op_len;
  data += var_op_len;
  *listlength -= (int)(data - var_op_start);
  return data;
}



// build a pdu from a data and length
int cmu_snmp::parse( struct snmp_pdu *pdu,
               u_char  *data,
               u_char *community_name,
               u_long &community_len,
               snmp_version &spp_version,
               int length)
{
  ACE_TRACE("cmu_snmp::parse");
  u_char  msg_type;
  u_char  type;
  u_char  *var_val;
  long    version;
  int       len, four;
  u_char community[256];
  int community_length = 256;
  struct variable_list *vp = 0;
  oid       objid[MAX_NAME_LEN], *op;

  // authenticates message and returns length if valid
  data = cmu_snmp::auth_parse(data,
                         &length,
                         community,
                         &community_length,
                         &version);
  if (data == 0)
    return -1;

  // copy the returned community name
  ACE_OS::memcpy( (u_char *) community_name,
                 (u_char *) community,
                 community_length);
  community_len = (long) community_length;

  if( version != SNMP_VERSION_1 && version != SNMP_VERSION_2C ) {
    ASNERROR("Wrong version");
    return -1;
  }

  spp_version = (snmp_version) version;

  data = asn1::parse_header(data,
                          &length,
                          &msg_type);
  if (data == 0)
    return -1;
  pdu->command = msg_type;

  if (pdu->command != TRP_REQ_MSG){
    // get the rid
    data = asn1::parse_int(data,
                         &length, &type,
                         (long *)&pdu->reqid,
                         sizeof(pdu->reqid));
    if (data == 0)
      return -1;
    // get the error status
    data = asn1::parse_int(data,
                         &length,
                         &type,
                         (long *)&pdu->errstat,
                         sizeof(pdu->errstat));
    if (data == 0)
      return -1;
    // get the error index
    data = asn1::parse_int(data,
                         &length,
                         &type,
                         (long *)&pdu->errindex,
                         sizeof(pdu->errindex));
    if (data == 0)
      return -1;
  }
  else {  // is a trap

    // get the enterprise
    pdu->enterprise_length = MAX_NAME_LEN;
    data = asn1::parse_objid(data,
                           &length,
                           &type,
                           objid,
                           &pdu->enterprise_length);
    if (data == 0)
      return -1;

    ACE_NEW_RETURN(pdu->enterprise,
                   oid[pdu->enterprise_length*sizeof(oid)],-1);

    // fixed
    ACE_OS::memcpy((char *)pdu->enterprise,(char *)objid,
                   pdu->enterprise_length * sizeof(oid));

    // get source address
    four = 4;
    data = asn1::parse_string(data, &length, &type,
                            (u_char *)&pdu->agent_addr.sin_addr.s_addr,
                            &four);
    if (data == 0)
      return -1;

    // get trap type
    data = asn1::parse_int(data, &length, &type, (long *)&pdu->trap_type,
                         sizeof(pdu->trap_type));
    if (data == 0)
      return -1;

    // trap type
    data = asn1::parse_int(data, &length, &type, (long *)&pdu->specific_type,
                         sizeof(pdu->specific_type));
    if (data == 0)
      return -1;

    // timestamp
    data = asn1::parse_int(data, &length, &type, (long *)&pdu->time,
                            sizeof(pdu->time));
    if (data == 0)
      return -1;
  }

  // get the vb list
  data = asn1::parse_header(data, &length, &type);
  if (data == 0)
    return -1;

  if (type != (u_char)(ASN_SEQUENCE | ASN_CONSTRUCTOR))
    return -1;

  while((int)length > 0) {
    if (pdu->variables == 0) {
      ACE_NEW_RETURN(pdu->variables, variable_list, -1);
      vp = pdu->variables;
    } else {
      ACE_NEW_RETURN(vp->next_variable, variable_list, -1);
      vp = vp->next_variable;
    }
    vp->next_variable = 0;
    vp->val.string = 0;
    vp->name = 0;
    vp->name_length = MAX_NAME_LEN;
    data = cmu_snmp::parse_var_op( data, objid,
                             &vp->name_length, &vp->type,
                             &vp->val_len, &var_val,
                             (int *)&length);
     if (data == 0)
      return -1;

    ACE_NEW_RETURN(op, oid[(unsigned)vp->name_length * sizeof(oid)], -1);

    // fixed
    ACE_OS::memcpy((char *)op, (char *)objid, vp->name_length * sizeof(oid));
    vp->name = op;

    len = SNMP_MSG_LENGTH;
    switch((short)vp->type) {
    case ASN_INTEGER:
    case SMI_COUNTER:
    case SMI_GAUGE:
    case SMI_TIMETICKS:
    case SMI_UINTEGER:
      ACE_NEW_RETURN(vp->val.integer,long, -1);
      vp->val_len = sizeof(long);
      asn1::parse_int(var_val, &len, &vp->type, (long *)vp->val.integer, sizeof(vp->val.integer));
      break;
    case SMI_COUNTER64:
      ACE_NEW_RETURN(vp->val.counter64, counter64, -1);
      vp->val_len = sizeof(struct counter64);
      asn1::parse_unsigned_int64(var_val, &len, &vp->type,
                               (struct counter64 *)vp->val.counter64,
                               sizeof(*vp->val.counter64));
      break;

    case ASN_OCTET_STR:
    case SMI_IPADDRESS:
    case SMI_OPAQUE:
    case SMI_NSAP:
      ACE_NEW_RETURN(vp->val.string, u_char[(unsigned)vp->val_len + 1], -1);
      asn1::parse_string(var_val, &len, &vp->type, vp->val.string,
                         &vp->val_len);
      break;

    case ASN_OBJECT_ID:
      vp->val_len = MAX_NAME_LEN;
      asn1::parse_objid(var_val, &len, &vp->type, objid, &vp->val_len);
      //vp->val_len *= sizeof(oid);

      ACE_NEW_RETURN(vp->val.objid, oid[(unsigned)vp->val_len*sizeof(oid)], -1);

      // fixed
      ACE_OS::memcpy((char *)vp->val.objid,
                     (char *)objid,
                     vp->val_len * sizeof(oid));
      break;

    case SNMP_NOSUCHOBJECT:
    case SNMP_NOSUCHINSTANCE:
    case SNMP_ENDOFMIBVIEW:
    case ASN_NULL:
      break;
    default:
      ASNERROR("bad type returned ");
      break;
    }
  }
  return 0;
}
