/*
 * tnmAsn1.c --
 *
 *	This is the implementation of the ASN1/BER encoding and 
 *	decoding functions. This file also includes the functions
 *	to handle ASN1 object identifier.
 *
 * Copyright (c) 1994-1996 Technical University of Braunschweig.
 * Copyright (c) 1996-1997 University of Twente.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tnmSnmp.h"

#include <math.h>

/*
 * The static error string is modified whenever we have found an error.
 * TnmBerError() can be used to retrieve the error message from other
 * modules.
 */

static char error[256];

char*
TnmBerError()
{
    return error;
}

/*
 * The following tables are used to convert SNMP types and SNMP
 * exceptions (or syntax tags in ASN.1 speak) to internally used
 * token.
 */

TnmTable tnmSnmpTypeTable[] =
{
    { ASN1_OTHER,		"OTHER" },
    { ASN1_BOOLEAN,		"BOOLEAN" },
    { ASN1_INTEGER,		"INTEGER" },
    { ASN1_INTEGER,		"Integer32" },
    { ASN1_BIT_STRING,		"BIT STRING" },
    { ASN1_OCTET_STRING,	"OCTET STRING" },
    { ASN1_NULL,		"NULL" },
    { ASN1_OBJECT_IDENTIFIER,	"OBJECT IDENTIFIER" },
    { ASN1_SEQUENCE,		"SEQUENCE" },
    { ASN1_SEQUENCE_OF,		"SEQUENCE OF" },
    { ASN1_COUNTER32,		"Counter32" },
    { ASN1_COUNTER32,		"Counter" },
    { ASN1_GAUGE32,		"Unsigned32" },
    { ASN1_GAUGE32,		"Gauge32", },
    { ASN1_GAUGE32,		"Gauge" },
    { ASN1_IPADDRESS,		"IpAddress" },
    { ASN1_TIMETICKS,		"TimeTicks" },
    { ASN1_OPAQUE,		"Opaque" },
    { ASN1_NSAPADDRESS,		"NsapAddress" },
    { ASN1_COUNTER64,		"Counter64" },
    { NetworkAddress,		"NetworkAddress" },
    { 0, NULL }
};

TnmTable tnmSnmpExceptionTable[] =
{
    { ASN1_NO_SUCH_OBJECT,	"noSuchObject" },
    { ASN1_NO_SUCH_INSTANCE,	"noSuchInstance" },
    { ASN1_END_OF_MIB_VIEW,	"endOfMibView" },
    { 0, NULL }
};


/*
 *----------------------------------------------------------------------
 *
 * TnmOidToStr --
 *
 *	This procedure converts an object identifier into string
 *	in dotted notation.
 *
 * Results:
 *	Returns the pointer to the string in static memory.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

char*
TnmOidToStr(oid, oidLen)
    Tnm_Oid *oid;
    int oidLen;
{
    int i;
    static char buf[TNM_OID_MAX_SIZE * 8];
    char *cp;

    if (oid == NULL) return NULL;

    buf[0] = '\0';
    
    for (cp = buf, i = 0; i < oidLen; i++) {
        if (oid[i] < 10) {
	    *cp++ = '0' + oid[i];
	} else {
	    u_int t=10;
	    char c = '0'+ (oid[i] % 10);
	    u_int u = oid[i] / 10;
	    while (u / t) t *= 10;
	    while (t /= 10) *cp++ = '0'+ (u / t) % 10;
	    *cp++ = c;
	}
	*cp++ = '.';
    }
    if (cp > buf) {
	*--cp = '\0';
    }
    
    return buf;
}

/*
 *----------------------------------------------------------------------
 *
 * TnmStrToOid --
 *
 *	This procedure converts a string with an object identifier
 *	in dotted representation into an object identifier vector.
 *
 * Results:
 *	Returns the pointer to the vector in static memory or a
 *	NULL pointer if the string contains illegal characters or
 *	exceeds the maximum length of an object identifier.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

Tnm_Oid*
TnmStrToOid(str, len)
    char *str;
    int *len;
{
    static Tnm_Oid oid[TNM_OID_MAX_SIZE];

    if (str == NULL) return NULL;
    if (*str == '.') str++;

    memset((char *) oid, 0, sizeof(oid));

    if (! *str) {
	*len = 0;
	return oid;
    }

    for (*len = 0; *str; str++) {
	if (isdigit(*str)) {
	    oid[*len] = 10 * oid[*len] + *str - '0';
	} else if (*str == '.' && *len < (TNM_OID_MAX_SIZE - 1)) {
	    *len += 1;
	} else {
	    return NULL;
	}
    }

    *len += 1;
    return oid;
}

/*
 *----------------------------------------------------------------------
 *
 * TnmBerEncLength --
 *
 *	This procedure sets the length field of any BER encoded
 *	ASN.1 type. If length is > 0x7F the array is shifted to get
 *	enough space to hold the value.
 *
 * Results:
 *	A pointer to the end of the BER encode byte stream or NULL.
 *
 * Side effects:
 *	The BER encoded octets might be moved.
 *
 *----------------------------------------------------------------------
 */

u_char*
TnmBerEncLength(packet, packetlen, len_fld, length)
    u_char *packet;
    int *packetlen;
    u_char *len_fld;
    int length;
{
    int i;

    if (! packet) {
	return packet;
    }

    if (length <= 0x7F) {

	*len_fld = length;
	
    } else if (length <= 0xff) {

        for (i = packet - len_fld - 1; i > 0; i--) {
	    len_fld[i + 1] = len_fld[i];
	}
	packet     += 1;
	*packetlen += 1;
	*len_fld++ = 0x81;
	*len_fld++ = length;

    } else if (length <= 0xffff) {

	for (i = packet - len_fld - 1; i > 0; i--) {
	    len_fld[i + 2] = len_fld[i];
        }
	packet     += 2;
	*packetlen += 2;
	*len_fld++ = 0x82;
	*len_fld++ = ( ( length >> 8 ) & 0xff );
	*len_fld++ = ( length & 0xff );

    } else {

	strcpy(error, "failed to encode very long ASN1 length");
	return NULL;
    }

    return packet;
}

/*
 *----------------------------------------------------------------------
 *
 * TnmBerDecLength --
 *
 *	This procedure decodes the length field of any ASN1 encoded 
 *	type. If length field is in indefinite length form or longer
 *	than the size of an unsigned int, an error is reported.
 *
 * Results:
 *	A pointer to the end of the BER decoded byte stream or NULL.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

u_char*
TnmBerDecLength(packet, packetlen, length)
    u_char *packet;
    int *packetlen;
    u_int *length;
{
    if (! packet) {
        return packet;
    }
    
    /*
     * Check if length field is longer than one byte.
     */
    
    if (*packet & 0x80) {

	u_char *cp = packet;
	*packet &= 0x7F;
	if (*packet == 0) {
	    strcpy(error, "indefinite length format not supported");
	    return NULL;
	}
	if (*packet > sizeof(int)) {
	    strcpy(error, "data lengths of this size not supported");
	    return NULL;
	}
	memcpy((char *) length, (char *) packet + 1, (int) *packet);
	*length = ntohl(*length);
	*length >>= ( 8 * ( (sizeof(*length)) - (int) *packet));
	
	*packetlen += ( 1 + *packet );
	packet     += ( 1 + *packet );
	*cp |= 0x80;

    } else {

	*length    = (int) *packet++;
	*packetlen += 1;
    }

    return packet;
}

/*
 *----------------------------------------------------------------------
 *
 * TnmBerEncSequenceStart --
 *
 *	This procedure marks the start of a BER encoded SEQUENCE or
 *	SEQUENCE OF. A pointer to the start of the sequence is
 *	initialized which must be presented when calling the 
 *	procedure to close a SEQUENCE or SEQUENCE OF.
 *
 * Results:
 *	A pointer to the end of the BER encode byte stream or NULL.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

u_char*
TnmBerEncSequenceStart(packet, packetlen, token)
    u_char *packet;
    int *packetlen;
    u_char **token;
{
    if (! packet) {
        return packet;
    }

    *packet++ = (ASN1_UNIVERSAL | ASN1_CONSTRUCTED | ASN1_SEQUENCE);
    *token = packet++;
    *packetlen += 2;
    return packet;
}

/*
 *----------------------------------------------------------------------
 *
 * TnmBerEncSequenceEnd --
 *
 *	This procedure closes a previously opened SEQUENCE or
 *	SEQUENCE OF encoding. The token passed to the procedure
 *	must be obtained from a previous call to the procedure
 *	which starts a SEQUENCE or SEQUENCE OF.
 *
 * Results:
 *	A pointer to the end of the BER encode byte stream or NULL.
 *
 * Side effects:
 *	The BER encoded octets might be moved.
 *
 *----------------------------------------------------------------------
 */

u_char*
TnmBerEncSequenceEnd(packet, packetlen, token)
    u_char *packet;
    int *packetlen;
    u_char *token;
{
    if (! packet) {
        return packet;
    }

    packet = TnmBerEncLength(packet, packetlen, token, packet - (token + 1));
    return packet;
}

/*
 *----------------------------------------------------------------------
 *
 * TnmBerEncInt --
 *
 *	This procedure encodes an ASN1 Integer value (means a int) 
 *	to an octet string by using the primitive, definite length 
 *	encoding method.
 *
 * Results:
 *	A pointer to the end of the BER encode byte stream or NULL.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

u_char*
TnmBerEncInt(packet, packetlen, tag, value)
    u_char *packet;
    int *packetlen;
    int tag;
    int value;
{
    int asnlen  = 0;
    int intsize = sizeof(int);
    int mask;
    u_char *length;

    if (! packet) {
	return packet;
    }

    /* 
     * Encode tag and reserve space for length.
     */

    *packet++ = tag & 0xff;
    *packetlen += 1;
    
    length = packet++;   
    *packetlen += 1;
    
    /* 
     * Set the leftmost 9 bits of mask to 1 and check if the 
     * leftmost bits of value are 0 or 1.
     */

    mask = 0x1FF << ( ( 8 * ( sizeof(int) - 1 ) ) - 1 );
    
    while ((((value & mask) == 0) 
	    || ((value & mask) == mask )) && intsize > 1) {
	intsize--;
	value <<= 8;
    }

    /*
     * Set the leftmost 8 bits of mask to 1 and build the 
     * two's complement of value.
     */

    mask = 0xff << ( 8 * ( sizeof(int) - 1 ) );
    while (intsize--) {
	*packet++ = (( value & mask ) >> ( 8 * ( sizeof(int) - 1 )));
	*packetlen += 1;
	value <<= 8;
	asnlen += 1;
    }

    /*
     * Encode length field and return.
     */

    packet = TnmBerEncLength(packet, packetlen, length, asnlen);
    return packet;
}

/*
 *----------------------------------------------------------------------
 *
 * TnmBerDecInt --
 *
 *	This procedure decodes an ASN.1 integer value. We return an
 *	error if an int is not large enough to hold the ASN.1 value.
 *
 * Results:
 *	A pointer to the end of the BER decoded byte stream or NULL.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

u_char*
TnmBerDecInt(packet, packetlen, tag, value)
    u_char *packet;
    int *packetlen;
    int tag;
    int *value;
{
    u_int asnlen = 0;
    int negative = 0;

    if (! packet) {
        return packet;
    }

    if (*packet++ != (tag & 0xff)) {
	sprintf(error, "invalid tag: 0x%.2x, expecting 0x%.2x",
		*--packet, tag);
	return NULL;
    }
    *packetlen += 1;

    /*
     * Handle invalid integer size.
     */

    packet = TnmBerDecLength(packet, packetlen, &asnlen);
    if (packet == NULL) return NULL;
    
    if (asnlen == 0) {
	*value = 0;
	return packet;
    }
    
    /*
     * Check for an overflow for normal 32 bit integer values.
     */
    
    if ((*packet != 0 && asnlen > sizeof(int))
	|| (*packet == 0 && asnlen-1 > sizeof(int))) {
	sprintf(error,
		"integer overflow: %d bytes received, %d bytes available",
		asnlen, (int) sizeof(int));
	return NULL;
    }
    
    /*
     * Check if it's a negative value and decode data.
     */

    if (((tag & 0xff) == ASN1_INTEGER) && (*packet & 0x80)) {
	*value = -1;
	negative = 1;
    } else {
	*value = 0;
	negative = 0;
    }

    while (asnlen-- > 0) {
	*value = (*value << 8) | (*packet++ & 0xff);
	*packetlen += 1;
    }

    /*
     * Negative values are only allowed for ASN1_INTEGER tags.
     * Return an error if we get something negative for an
     * unsigned type, e.g. a Counter.
     */

    if (negative && ((tag & 0xff) != ASN1_INTEGER)) {
	sprintf(error,
		"received signed integer %d for unsigned tag 0x%.2x",
		*value, (tag & 0xff));
	return NULL;
    }

    return packet;
}

/*
 *----------------------------------------------------------------------
 *
 * TnmBerEncCounter64 --
 *
 *	This procedure encodes an ASN1 Counter64 value into an
 *	octet string by using the primitive, definite length encoding
 *	method. Note, on 64 bit machines, TnmBerEncInt() should
 *	be used as it yields accurate results.
 *
 * Results:
 *	A pointer to the end of the BER encode byte stream or NULL.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

u_char*
TnmBerEncCounter64(packet, packetlen, value)
    u_char *packet;
    int *packetlen;
    double value;
{
    int i, asnlen = 0;
    u_char *length;
    double d;

    if (! packet) {
	return packet;
    }

    /* 
     * Encode type and reserve space for length.
     */

    *packet++  = ASN1_COUNTER64;
    *packetlen += 1;
    
    length = packet++;   
    *packetlen += 1;

    /*
     * Calculate the number of bytes needed to encode the ASN.1
     * integer.
     */

    for (d = value; d >= 1; asnlen++) {
	d /= 256.0;
    }

    /*
     * Now encode the bytes: We start at the end and move up
     * to the high byte.
     */

    for (i = asnlen - 1; i >= 0; i--) {
	d = value / 256.0;
	packet[i] = (int) (value - floor(d) * 256);
	value = d;
    }
    packet += asnlen;
    *packetlen += asnlen;
    
    /*
     * Encode length field and return.
     */

    packet = TnmBerEncLength(packet, packetlen, length, asnlen);
    return packet;
}

/*
 *----------------------------------------------------------------------
 *
 * TnmBerDecCounter64 --
 *
 *	This procedure decodes an ASN.1 Counter64 value. We return 
 *	the result as a double. Use TnmBerDecInt() on 64 bit machines
 *	to get an accurate result.
 *
 * Results:
 *	A pointer to the end of the BER decoded byte stream or NULL.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

u_char*
TnmBerDecCounter64(packet, packetlen, high, low)
    u_char *packet;
    int *packetlen;
    u_int *high;
    u_int *low;
{
    u_int asnlen = 0;

    if (! packet) {
        return packet;
    }

    if (*packet++ != ASN1_COUNTER64) {
	sprintf(error, "invalid tag: 0x%.2x, expecting 0x%.2x",
		*--packet, ASN1_COUNTER64);
	return NULL;
    }
    *packetlen += 1;

    packet = TnmBerDecLength(packet, packetlen, &asnlen);
    if (packet == NULL) return NULL;
    
    *high = 0, *low = 0;
    while (asnlen-- > 0) {
	*high = (*high << 8) | ((*low & 0xFF000000) >> 24);
	*low = (*low << 8) | (*packet++ & 0xff);
	*packetlen += 1;
    }

    return packet;
}

/*
 *----------------------------------------------------------------------
 *
 * TnmBerEncOID --
 *
 *	This procedure encodes an OBJECT IDENTIFIER to an octet string 
 *	by using the primitive, definite length encoding method. 
 *
 * Results:
 *	A pointer to the end of the BER encode byte stream or NULL.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

u_char*
TnmBerEncOID(packet, packetlen, oid, oidLen)
    u_char *packet;
    int *packetlen;
    Tnm_Oid *oid;
    int oidLen;
{
    int asnlen = 0;
#if (SIZEOF_LONG == 8)
    int mask, bits;
#else
    long mask, bits;
#endif
    Tnm_Oid *op = oid;
    u_char *length;
    
    if (! packet) {
	return packet;
    }

    /* 
     * Check for a valid length.
     */

    if (oidLen == 0) {
	strcpy(error, "OBJECT IDENTIFIER of length 0");
        return NULL;
    }

    /* 
     * Encode type and reserve space for length.
     */

    *packet++  = ASN1_OBJECT_IDENTIFIER;
    *packetlen += 1;

    length = packet++;   
    *packetlen += 1;

    if (oidLen == 1) {
      
        *packet++  = oid[0];
        *packetlen += 1;
        asnlen += 1;

    } else {

        /*
	 * Encode the first two components using the formula (X * 40) + Y
	 */
      
        *packet++  = oid[0] * 40 + oid[1];
	*packetlen += 1;
	asnlen += 1;
	oidLen -= 2;
	op     += 2;
	
	/*
	 * Encode the remaining subidentifier.
	 */
	
	while (oidLen-- > 0) {
	
	    /* are seven bits enough for this component */
	
	    if (*op <= 0x7F) {
	    
	        *packet++  = *op++;
	        *packetlen += 1;
	        asnlen += 1;
	      
	    } else {
	    
	        /* we need two or more octets for encoding */
	    
	        /* check nr of bits for this component */
	    
	        int n = sizeof(*op) * 8;		/* max bit of op */
	    
		mask = 1 << (n - 1);
		for (bits = n; bits > 0; bits--, mask >>= 1) {
		    if (*op & mask) break;
		}
	    
		/* round # of bits to multiple of 7: */
	    
		bits = ((bits + 6) / 7) * 7;

		/* Handle the first sequence of 7 bits if we have a
		   large number. */

		if (bits > n ) {
		    bits -= 7;
		    *packet++  = ((( *op >> bits ) & 0x7F) | 0x80 );
                    *packetlen += 1;
		    asnlen += 1;
		}

		mask = (1 << bits) - 1;
	    
		/* encode the mostleft 7 bits and shift right */
		
		for (; bits > 7; mask >>= 7 ) {
		    bits -= 7;
		    *packet++  = ((( *op & mask ) >> bits ) | 0x80 );
		    *packetlen += 1;
		    asnlen += 1;
	        }
	    
		*packet++ = ( *op++ & mask );
		*packetlen += 1;
		asnlen += 1;
	    }
	}
    }

    /* 
     * Finally, encode length field.
     */

    packet = TnmBerEncLength(packet, packetlen, length, asnlen);
    return packet;
}

/*
 *----------------------------------------------------------------------
 *
 * TnmBerDecOID --
 *
 *	This procedure decodes and object identifier. The caller of
 *	this function is responsible to provide enough memory to hold
 *	the object identifier.
 *
 * Results:
 *	A pointer to the end of the BER decoded byte stream or NULL.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

u_char*
TnmBerDecOID(packet, packetlen, oid, oidLen)
    u_char *packet;
    int *packetlen;
    Tnm_Oid *oid;
    int *oidLen;
{
    u_int asnlen;
    Tnm_Oid *op = oid;

    if (! packet) {
        return packet;
    }

    if (*packet++ != ASN1_OBJECT_IDENTIFIER) {
	sprintf(error, "invalid tag: 0x%.2x, expecting 0x%.2x",
		*--packet, ASN1_OBJECT_IDENTIFIER );
	return NULL;
    }
    *packetlen += 1;
    
    /*
     * Handle invalid length field.
     */
    
    packet = TnmBerDecLength(packet, packetlen, &asnlen);
    if (packet == NULL) return NULL;
    
    if (asnlen == 0) {
	strcpy(error, "OBJECT IDENTIFIER of length 0");
	return NULL;
    }

    if (asnlen > TNM_OID_MAX_SIZE) {
	sprintf(error, "OBJECT IDENTIFIER exceeds maximum length %d", 
		TNM_OID_MAX_SIZE);
	return NULL;
    }
    
    if (asnlen == 1 && (*packet % 40 == *packet)) {
	*oid       = *packet++;
	*oidLen    = 1;
	*packetlen += 1;
	return packet;
    }
    
    /*
     * Decode the first component to the first two subidentifiers.
     */
    
    oid[1] = (u_char)( *packet % 40 );
    oid[0] = (u_char)(( *packet++ - oid[1] ) / 40 );
    op         += 2;
    *oidLen    = 2;
    asnlen     -= 1;
    *packetlen += 1;
    
    /*
     * Decode the remaining subidentifer.
     */

    while (asnlen > 0) {
	memset((char *) op, 0, sizeof(oid));
	while (*packet > 0x7F) {
	    /* hansb@aie.nl (Hans Bayle) had problems with SCO. */
	    *op = ( *op << 7 ) + ( *packet++ & 0x7F );
	    asnlen     -= 1;
	    *packetlen += 1;
	}

	*op = ( *op << 7 ) + ( *packet++ );
	op         += 1;
	*oidLen    += 1;
	asnlen     -= 1;
	*packetlen += 1;
    }

    return packet;
}

/*
 *----------------------------------------------------------------------
 *
 * TnmBerEncOctetString --
 *
 *	This procedure encodes an octet string by using the primitive,
 *	definite length encoding method. The first byte is the tag of 
 *	this octet string followed by the length of the octet string 
 *	and the octets itself.
 *
 * Results:
 *	A pointer to the end of the BER encode byte stream or NULL.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

u_char*
TnmBerEncOctetString(packet, packetlen, tag, octets, len)
    u_char *packet;
    int *packetlen;
    int tag;
    char *octets;
    int len;
{
    int	i, asnlen = 0;
    u_char *length;
    char *op = octets;

    if (! packet) {
	return packet;
    }

    *packet++  = tag & 0xff;
    *packetlen += 1;
    length     = packet++;   
    *packetlen += 1;

    for (i = 0; i < len; i++) {
	*packet++ = *op++;
    }
    *packetlen += len;
    asnlen     += len;

    packet = TnmBerEncLength(packet, packetlen, length, asnlen);
    return packet;
}

/*
 *----------------------------------------------------------------------
 *
 * TnmBerDecOctetString --
 *
 *	This procedure decodes an octet string. It is up to the caller
 *	to copy the octets in private memory if the packet itself is
 *	cleared. The first byte in the packet must match the tag given
 *	by type followed by the length of the octet string and the 
 *	octets themself.
 *
 * Results:
 *	A pointer to the end of the BER decoded byte stream or NULL.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

u_char*
TnmBerDecOctetString(packet, packetlen, tag, octets, len)
    u_char *packet;
    int *packetlen;
    int tag;
    char **octets;
    int *len;
{
    u_int asnlen;

    if (! packet) {
        return packet;
    }

    if (*packet++ != (tag & 0xff)) {
	sprintf(error, "invalid tag: 0x%.2x, expecting 0x%.2x",
		*--packet, tag );
	return NULL;
    }
    *packetlen += 1;

    /*
     * Handle zero length octet string of the form 0x04 0x00.
     */

    packet = TnmBerDecLength(packet, packetlen, &asnlen);
    if (packet == NULL) return NULL;

    if (octets) {
        *octets = (char *) packet;
	*len = asnlen;
    }

    *packetlen += asnlen;
    return (packet + asnlen);
}

/*
 *----------------------------------------------------------------------
 *
 * TnmBerEncNull --
 *
 *	This procedure encodes an ASN.1 NULL value.
 *
 * Results:
 *	A pointer to the end of the BER encode byte stream or NULL.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

u_char*
TnmBerEncNull(packet, packetlen, tag)
    u_char *packet;
    int *packetlen;
    int tag;
{
    if (! packet) {
	return packet;
    }

    *packet++  = tag & 0xff;
    *packet++  = '\0';
    *packetlen += 2;
    return packet;
}

/*
 *----------------------------------------------------------------------
 *
 * TnmBerDecNull --
 *
 *	This procedure decodes an ASN.1 NULL value. Checks if the tag 
 *	matches the required tag given in tag.
 *
 * Results:
 *	A pointer to the end of the BER decoded byte stream or NULL.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

u_char*
TnmBerDecNull(packet, packetlen, tag)
    u_char *packet;
    int *packetlen;
    int tag;
{
    u_int asnlen;

    if (! packet) {
        return packet;
    }

    if (*packet++ != (tag & 0xff)) {
	sprintf(error, "invalid tag: 0x%.2x, expecting 0x%.2x",
		*--packet, tag);
	return NULL;
    }
    *packetlen += 1;

    packet = TnmBerDecLength(packet, packetlen, &asnlen);
    if (packet == NULL) return NULL;

    return (packet + asnlen);
}
