/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*      	  1986 - 2009, All Rights Reserved.		        */
/*									*/
/*		    PROPRIETARY AND CONFIDENTIAL			*/
/*									*/
/* MODULE NAME : asn1r.c 						*/
/* PRODUCT(S)  : ASN1DE 						*/
/*									*/
/* MODULE DESCRIPTION : 						*/
/*	This module contains the main ASN.1 decode and encode tools.	*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev			Comments			*/
/* --------  ---  ------   -------------------------------------------	*/
/* 07/21/09  JRB     32    Cleanup new asn1r_get_tag.			*/
/* 05/29/09  JRB     31    Add asn1r_decode_init, asn1r_get_tag functs.	*/
/*			   For indefinite len constr, increase initial	*/
/*			   byte_count (couldn't handle big constr).	*/
/*			   Add asn1r_strt_constr_indef (FOR TESTING).	*/
/* 05/28/09  JRB     30	   On decode, don't log constr content(redundant)*/
/* 01/15/09  MDE     29    Added asn1_peek,made asn1r_head_decode static*/
/* 11/12/08  MDE     28    Added overreach checks to  asn1r_head_decode	*/
/* 11/12/08  MDE     27    Removed assert to improve robustness		*/
/* 10/03/08  JRB     28    Prevent crashes while decoding by improving	*/
/*			   len checks & doing it BEFORE logging.	*/
/* 10/04/07  MDE     27    Tweaked LOGCFG_VALUE_GROUP/LOGCFGX_VALUE_MAP	*/
/* 12/20/06  JRB     26    Add bstrcmp.					*/
/* 03/17/06  JRB     25    Add asn1r_anytag_fun, ASN1_ANYTAG_METHOD.	*/
/* 03/15/06  JRB     24    Add asn1r_skip_elmnt.			*/
/* 02/28/06  EJV     23    asn1_convert_...: added typecast from time_t */
/* 12/19/05  JRB     22    Chg cr to cr_asn1, del static to fix warning.*/
/* 05/23/05  EJV     21    Add asn1LogMaskMapCtrl for parsing logcfg.xml*/
/* 07/08/04  JRB     20    Use define TIME_T_1984_JAN_1 instead of mktime*/
/*			   Use define SECONDS_PER_DAY from asn1r.h	*/
/*			   Add asn1_convert_timet_to_btime6.		*/
/*			   Use 0x01000000 (2**24) in fraction computations*/
/* 01/14/03  JRB     19    Avoid redundant logging of encode overrun.	*/
/*			   fin_constr decrement msg_level on encode overrun*/
/* 11/05/03  EJV     18    Fixed logging.				*/
/* 10/13/03  JRB     17    asn1r_decode_asn1: Chg assert to err return.	*/
/* 04/28/03  JRB     16    Add assert to asn1r_decode_asn1.		*/
/* 04/02/03  JRB     15    On unexpected tag error, log the tag.	*/
/* 02/07/03  JRB     14    Add bstrcpy, bvstrcpy.			*/
/* 10/29/02  JRB     13    Use new asn1r_magic in ASN1_ENC_CTXT.	*/
/* 07/03/02  EJV     12    MMS_UTC_TIME: chg name usec to fraction.	*/
/* 03/05/02  JRB     11    Eliminate compiler warnings.			*/
/* 01/25/02  JRB     10    Move INT_MAX chk to #if, avoids unreachable	*/
/*			   code. Make SD_CONST match header on *logstr.	*/
/* 01/22/02  JRB     09    asn1r_strt_asn1_bld set new "asn1r_buf_end".	*/
/*			   Chg asn1r_end_of_buffer to asn1r_buf_start	*/
/*			   & asn1r_field_start to asn1r_field_end	*/
/*			   & _asn1_constr_start to asn1r_constr_end	*/
/*			   in ENC_CTX (start means start, end means end)*/
/* 01/07/02  EJV     08    Added asn1_ prefix to convert_... functions.	*/
/* 01/04/02  EJV     07    Add convert_btod_to_utc, convert_utc_to_btod	*/
/*			   Cleanup few function header comments.	*/
/*			   Updated cr[] to 2002.			*/
/* 12/20/01  JRB     06    Chg ASN1_CTXT to ASN1_DEC_CTXT, ASN1_ENC_CTXT*/
/*			   Delete #define USE_WITH_ASN1.		*/
/*			   Del asn1_old_err_fun & *_old_id_fun.		*/
/*			   asn1r_objidcmp chged to asn1_objidcmp (same	*/
/*			   as function in asn1.c).			*/
/*			   Del unused strt_asn1r, asn1r_end_asn_bld.	*/
/* 11/28/01  EJV     05    fin_prim: corrected len for logging.		*/
/* 11/12/01  EJV     04    asn1r_decode_asn1: removed static variables	*/
/* 09/13/99  MDE     03    Added SD_CONST modifiers			*/
/* 07/29/99  MDE     02    Added TABLE method				*/
/* 07/26/99  MDE     01    New module, derived from asn1.c		*/
/************************************************************************/

#include "glbtypes.h"
#include "sysincs.h"
#include <limits.h>
#include "asn1r.h"
#include "asn1log.h"
#include "mem_chk.h"

/************************************************************************/

const ST_CHAR cr_asn1[] =
 "(c) COPYRIGHT SYSTEMS INTEGRATION SPECIALISTS COMPANY INC., 1986 - 2005.  All Rights Reserved.";

/************************************************************************/
/* For debug version, use a static pointer to avoid duplication of 	*/
/* __FILE__ strings.							*/

#ifdef DEBUG_SISCO
SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__;
#endif

#if !defined(NO_GLB_VAR_INIT)
ST_UINT asn1_debug_sel = ASN1_LOG_ERR;
#else
ST_UINT asn1_debug_sel;
#endif

#ifdef DEBUG_SISCO
SD_CONST ST_CHAR *SD_CONST _asn1_log_dec_logstr = "ASN1_LOG_DEC";
SD_CONST ST_CHAR *SD_CONST _asn1_log_enc_logstr = "ASN1_LOG_ENC";
SD_CONST ST_CHAR *SD_CONST _asn1_log_err_logstr = "ASN1_LOG_ERR";
SD_CONST ST_CHAR *SD_CONST _asn1_log_nerr_logstr = "ASN1_LOG_NERR";

LOGCFGX_VALUE_MAP asn1LogMaskMaps[] =
  {
    {"ASN1_LOG_ERR",	ASN1_LOG_ERR,	&asn1_debug_sel, _LOGCFG_DATATYPE_UINT_MASK, NULL, "Error"},
    {"ASN1_LOG_NERR",	ASN1_LOG_NERR,	&asn1_debug_sel, _LOGCFG_DATATYPE_UINT_MASK, NULL, "Notice"},
    {"ASN1_LOG_DEC",	ASN1_LOG_DEC,	&asn1_debug_sel, _LOGCFG_DATATYPE_UINT_MASK, NULL, "Decode"},
    {"ASN1_LOG_ENC",	ASN1_LOG_ENC,	&asn1_debug_sel, _LOGCFG_DATATYPE_UINT_MASK, NULL, "Encode"}
  };

LOGCFG_VALUE_GROUP asn1LogMaskMapCtrl =
  {
  {NULL,NULL},
  "Asn1LogMasks",      /* Parent Tag */
  sizeof(asn1LogMaskMaps)/sizeof(LOGCFGX_VALUE_MAP),
  asn1LogMaskMaps
  };
#endif /* DEBUG_SISCO */

/************************************************************************/
/* General variables and pointers used by the ASN1 system.		*/
/************************************************************************/
/************************************************************************/
/* VMS Float Format select						*/

/* VMS has different forms of float format; select the default here	*/
#if !defined(NO_GLB_VAR_INIT) && defined(VAXC)
ST_INT	asn1_vax_double_type	= A_D_FLOAT;
#endif
#if !defined(NO_GLB_VAR_INIT) && defined( __ALPHA )
ST_INT	asn1_vax_double_type	= A_G_FLOAT;
#endif
#if defined(NO_GLB_VAR_INIT) && (defined( VAXC ) || defined( __ALPHA ))
ST_INT	asn1_vax_double_type;
#endif

/************************************************************************/
/* Static functions in this module					*/

static ST_RET     _asn1r_head_decode (ASN1_DEC_CTXT *ac);
static ST_VOID    _do_decode_asn1 (ASN1_DEC_CTXT *ac, ST_UCHAR *ptr, ST_INT len);
static ST_BOOLEAN _indef_eoc (ASN1_DEC_CTXT *ac);
static ST_VOID    _check_level_done (ASN1_DEC_CTXT *ac);
static ST_VOID    _call_user (ASN1_DEC_CTXT *ac);
static ST_VOID    _wr_asn1_len (ASN1_ENC_CTXT *ac, ST_INT len);
static ST_VOID    _wr_ident (ASN1_ENC_CTXT *ac, ST_UINT16 id_code, ST_UINT16 tag);
#ifdef DEBUG_SISCO
static ST_VOID    _list_elmnt (ASN1_DEC_CTXT *ac);
#endif

/************************************************************************/
/************************************************************************/
/* ASN.1 MESSAGE DECODE							*/
/************************************************************************/
/*			    asn1r_decode_asn1_seq			*/
/* This function is used to decode a ASN.1 data element when the outer	*/
/* constructor has been stripped.					*/
/************************************************************************/

ST_VOID asn1r_decode_asn1_seq (ASN1_DEC_CTXT *ac, ST_UCHAR *ptr, ST_INT len)
  {
  ac->_asn1_indef_track[0] = SD_FALSE;	/* level 0 is always definite		*/
  ac->_asn1_indef_track[1] = SD_FALSE;	/* so is level 1 in this case		*/

  ac->_asn1_byte_count[0] = 0;		/* assumes definite			*/
  ac->_asn1_byte_count[1] = len;		/* contents of missing outer cstr	*/

  ac->asn1r_field_ptr = ptr;		/* point to first element 		*/
  ac->asn1r_msg_level = 1;		/* start at message level 1		*/
  ac->asn1r_c_done_fun[ac->asn1r_msg_level] = NULL; 
	      
  _do_decode_asn1 (ac, ptr,len);
  }

/************************************************************************/
/*			    asn1r_decode_asn1 				*/
/* Function to drive the decoding of an ASN.1 encoded message.		*/
/* The user must set up the class or tag function ptrs, and the error	*/
/* and done function ptrs before calling this function. Pass the length */
/* and location of the message as inputs.				*/
/************************************************************************/

ST_VOID asn1r_decode_asn1 (ASN1_DEC_CTXT *ac, ST_UCHAR *ptr, ST_INT len)
  {
#if !defined (MMS_LITE)
ST_UCHAR *save_asn1r_ptr = NULL;
#endif

/* The caller MUST clear ASN1_DEC_CTXT struct before calling this funct.*/
/* After clearing, caller may set a few params, so chk a param that	*/
/* caller would NEVER set.						*/
  if (ac->asn1r_msg_level != 0)
    {
    ALOG_ERR0 ("ASN.1 decode context not initialized");
    ac->asn1r_pdu_dec_err = ASN1E_DECODE_OTHER;	/* set error code	*/
    return;
    }

/* initialize flags, counters, pointers for decode			*/

  ac->asn1r_indef_flag = SD_FALSE;	/* clear flag				*/
  ac->_asn1_indef_track[0] = SD_FALSE;	/* level 0 is always definite		*/
  ac->_asn1_byte_count[0] = len;	/* set # bytes left in level 0		*/
  ac->asn1r_msg_level = 0;		/* start at message level 0		*/
  ac->asn1r_c_done_fun[0] = NULL;

#if !defined (MMS_LITE)
  /* save the data in case we need to log it if decode fails		*/
  if ( (asn1_debug_sel & ASN1_LOG_NERR) &&
      !(asn1_debug_sel & ASN1_LOG_DEC))
    {
    save_asn1r_ptr = chk_malloc (len);
    memcpy (save_asn1r_ptr, ptr, len);
    }
#endif  

  _do_decode_asn1 (ac, ptr,len);

#if !defined (MMS_LITE)
  if (ac->asn1r_pdu_dec_err != NO_DECODE_ERR) /* if an error was present	*/
    {
    if (save_asn1r_ptr)
      {
      ALOG_NERR0 ("ASN.1 decode: badly formed PDU:");
      ALOG_NERRH (len, save_asn1r_ptr);
      }
    }
  if (save_asn1r_ptr)
    chk_free (save_asn1r_ptr);
#endif  
  }

/************************************************************************/
/*			_do_decode_asn1					*/
/************************************************************************/

static ST_VOID _do_decode_asn1 (ASN1_DEC_CTXT *ac, ST_UCHAR *ptr, ST_INT len)
  {
ST_INT  log_len;
ST_INT  gone_len;
ST_INT i;
ST_RET rc;

  ac->asn1r_pdu_dec_err = NO_DECODE_ERR;    /* reset error flag			*/
  ac->asn1r_decode_done = SD_FALSE;	    /* initialize flag			*/
  ac->asn1r_field_ptr = ptr;		    /* initialize asn1_field_ptr 	*/
  ac->asn1r_done_ptr = ac->asn1r_field_ptr + len;

/* Validate length to avoid possible problems */
  if (len <= 0)
    {
    ALOG_NERR0 ("ASN.1 decode: Length zero or negative");
    asn1r_set_dec_err(ac, ASN1E_END_O_BUFFER);
    if (ac->asn1r_err_fun != NULL)
      (*ac->asn1r_err_fun)(ac, ac->asn1r_pdu_dec_err); /* call error function	*/
    ac->_asn1r_ntag = 0;			/* set # tags = 0 for next time	*/
    return;
    }

  ALOG_DECLF ();
  ALOG_DEC1 ("ASN.1 MESSAGE TO DECODE : len = %d",len);
  ALOG_DECH (len,ptr);

/* Enter the message parse loop.					*/
/* Parse the message until all done or asn1_decode_done flag is set. 	*/
/* asn1_field_ptr points to the start of the data element to be decoded.	*/

  while (ac->asn1r_field_ptr < ac->asn1r_done_ptr && !ac->asn1r_decode_done)
    {
    ALOG_DEC1 ("Current decode offset: 0x%05x", ac->asn1r_field_ptr - ptr);
    ac->asn1r_field_start = ac->asn1r_field_ptr;	/* save field start address		*/
    rc = _asn1r_head_decode (ac); /* decodes the data element header	*/
    if (rc != SD_SUCCESS)
      {
      ALOG_NERR0 ("ASN.1 decode: header decode error");
      asn1r_set_dec_err(ac, rc);
      break;
      }

  /* OK, the header looks good and has been validated */
    log_len = ac->asn1r_field_ptr - ac->asn1r_field_start; 
    /* For primitive, log the whole element	*/
    if (!ac->asn1r_constr_elmnt)
      log_len += ac->asn1r_elmnt_len;

    ALOG_DECH (log_len, ac->asn1r_field_start);
    ALOG_PAUSEDEC ("");

#ifdef DEBUG_SISCO
    _list_elmnt (ac);		/* print element attributes		*/
#endif

    if (_indef_eoc (ac)) 	/* check for indefinite level EOC elmnt */
      { 			/* EOC is a primitive			*/
      _check_level_done (ac);	/* primitive complete			*/
      continue; 		/* service next field if not done	*/
      }
				/* not indefinite length EOC		*/
    if (ac->asn1r_decode_done)	/* if errors detected in decode 	*/
      break;

    if (ac->asn1r_constr_elmnt)		/* if constructor, bump asn1_msg_level	*/
      {
      if (++ac->asn1r_msg_level >= ASN1_MAX_LEVEL)
	{
        ALOG_ERR0 ("Error : ASN.1 decode exceeded nesting level");
	asn1r_set_dec_err(ac, ASN1E_NEST_TOO_DEEP);
	break;
	}
      ac->asn1r_c_done_fun[ac->asn1r_msg_level] = asn1r_cstr_done_err;  
      }

/* Call the user function specified for the given class or tag.  The	*/
/* user function is expected to parse the contents of this data element */
/* (when it's primitive) and move asn1_field_ptr to the start of the next    */
/* data element.							*/
    _call_user (ac);

/* The user function has been invoked, must set the asn1_decode_done flag	*/
/* if the message is complete or in error. The user function must move	*/
/* asn1_field_ptr to the start of the next data element.			*/

    gone_len = ac->asn1r_field_ptr - ac->asn1r_field_start;/* number of bytes gone */
						 /* adjust count 	 */

/* For primitive data elements, decrement _asn1_byte_count for current	*/
/* message level and jump directly down to _check_level_done at the end	*/
/* of the while loop.							*/
    if (!ac->asn1r_constr_elmnt)
      {
      if (ac->_asn1_byte_count[ac->asn1r_msg_level] < gone_len)
	{
        ALOG_NERR0 ("ASN.1 decode: invalid constructor length");
	asn1r_set_dec_err (ac, ASN1E_CSTR_INVALID_LEN);	/* call error function		*/
	break;				/* exit parse loop		*/
	}
      ac->_asn1_byte_count[ac->asn1r_msg_level] -= gone_len;
      }

/* Constructors are much more involved. First decrement the _asn1_byte_count	*/
/* for the previous message level (the one containing the new constr.), */
/* then set _asn1_byte_count for this message level.				*/
    else
      {
      if (ac->_asn1_byte_count[ac->asn1r_msg_level-1] < gone_len)
	{
        ALOG_NERR0 ("ASN.1 decode: invalid constructor length");
	asn1r_set_dec_err (ac, ASN1E_CSTR_INVALID_LEN);	/* call error function		*/
	break;				/* exit parse loop		*/
	}
      ac->_asn1_byte_count[ac->asn1r_msg_level-1] -= gone_len;

						/* save length type	*/
      if ((ac->_asn1_indef_track[ac->asn1r_msg_level] = ac->asn1r_indef_flag)
          != SD_FALSE)				/* if indefinite	*/
	{					/* (INDEF = 0xFF)	*/
	ac->_asn1_constr_start[ac->asn1r_msg_level] = ac->asn1r_field_ptr;	/* save start pointer	*/
	ac->_asn1_byte_count[ac->asn1r_msg_level] = INT_MAX;
	ac->asn1r_indef_flag  = SD_FALSE; 		/* clear flag		*/
	}
      else				/* definite length constructor	*/
	{				/* byte count in element known	*/
	ac->_asn1_byte_count[ac->asn1r_msg_level] = ac->asn1r_elmnt_len;
	if (ac->_asn1_byte_count[ac->asn1r_msg_level-1] < (ST_INT) ac->asn1r_elmnt_len)
	  {
          ALOG_NERR0 ("ASN.1 decode: invalid constructor length");
	  asn1r_set_dec_err (ac, ASN1E_CSTR_INVALID_LEN); /* call error function		*/
	  break;			/* exit parse loop		*/
	  }
	ac->_asn1_byte_count[ac->asn1r_msg_level-1] -= ac->asn1r_elmnt_len;
	}
      }

/* After decrementing _asn1_byte_count, check to see if this message level is */
/* done (and if so, check the next msg level, etc...).			*/
    _check_level_done (ac);
    }				/* Now return to top of parse loop.	*/

/* End of outer while loop.  Message parse is complete. 		*/
/* At this point the asn1_field_ptr should match the done_ptr, and the */
/* asn1_pdu_dec_err variable should be = NO_DECODE_ERR, and asn1_msg_level  == -1.*/
/* If any of these conditions are not true, the user error function	*/
/* will be executed (typically will return error message).  If no	*/
/* decode error, the user function pointed to by (*asn1_decode_done_fun)	*/
/* is executed. 							*/

  ALOG_CDEC0 ("Message parse complete");
  ALOG_PAUSEDEC ("");
					/* don't overwrite previous err */
  if (ac->asn1r_pdu_dec_err == NO_DECODE_ERR && 
      (ac->asn1r_field_ptr != ac->asn1r_done_ptr || ac->asn1r_msg_level != -1))
    {  					/* Check for indef cstr done	*/
    for (i = ac->asn1r_msg_level; i >= 0; --i)
      {
      if (ac->_asn1_indef_track[i] == SD_TRUE)	/* if indef length cstr		*/
        {				/* check for cstr done (00 00)	*/
	if ((ac->asn1r_field_ptr+2 > ac->asn1r_done_ptr) ||
	    *(ac->asn1r_field_ptr++) || *(ac->asn1r_field_ptr++))
          {
          ALOG_NERR0 ("ASN.1 decode: indefinite length constructor not terminated");
	  asn1r_set_dec_err (ac, ASN1E_END_O_BUFFER);   	/* call error function		*/
	  break;
	  }
	}
      }
    if (ac->asn1r_field_ptr != ac->asn1r_done_ptr)
      {
      ALOG_NERR0 ("ASN.1 decode: PDU decode terminated before complete");
      asn1r_set_dec_err (ac, ASN1E_END_O_BUFFER);       	/* call error function		*/
      }
    }

  if (ac->asn1r_pdu_dec_err != NO_DECODE_ERR) /* if an error was present	*/
    {
    if (ac->asn1r_err_fun != NULL)
      (*ac->asn1r_err_fun)(ac, ac->asn1r_pdu_dec_err); /* call error function		*/
    }
  else					 /* if not -			*/
    {
    if (ac->asn1r_decode_done_fun != NULL)
      (*ac->asn1r_decode_done_fun)(ac); /* invoke the done function	*/
    }

  ac->_asn1r_ntag = 0;			/* set # tags = 0 for next time	*/
  }


/************************************************************************/
/*			_check_level_done				*/
/* primitive field service complete, check to see if this level is	*/
/* complete, and if so, what the next level to service is.		*/
/************************************************************************/

static ST_VOID _check_level_done (ASN1_DEC_CTXT *ac)
  {
ST_INT i;

  while (ac->asn1r_msg_level >= 0 && !ac->_asn1_byte_count[ac->asn1r_msg_level])
    {		/* Search for first lower level with bytes left -	*/
    ALOG_CDEC1 ("Level %d parse complete",ac->asn1r_msg_level);
    if (!ac->asn1r_decode_done)	/* if user has not terminated		*/
      {
      ac->_asn1r_ntag = 0; 		/* clear tags, and call user		*/
      i = ac->asn1r_msg_level--;
      if (ac->asn1r_c_done_fun[i] != NULL)
        (*ac->asn1r_c_done_fun[i])(ac); /* done fcn for this asn1_msg_level	*/
      }
    else			/* decode terminated, just dec. 	*/
      ac->asn1r_msg_level--;
    }

  if (ac->asn1r_msg_level < 0)		/* if no bytes left at any lvl, */
    ac->asn1r_decode_done = SD_TRUE;		/* stop decode			*/
  }


/************************************************************************/
/*			_indef_eoc					*/
/* Function to check for universal EOC type, and perform level fixup if */
/* so. The fix consists of determining the number of bytes that were in */
/* the element and subtracting this number from the previous level byte */
/* count.								*/
/* Returns 0 if NOT EOC, else != 0					*/
/************************************************************************/

static ST_BOOLEAN _indef_eoc (ASN1_DEC_CTXT *ac)
  {
ST_INT len;

  if (ac->asn1r_constr_elmnt)		/* can only be a primitive	*/
    return (SD_FALSE);

  if (ac->asn1r_elmnt_class != UNI)		/* must be universal		*/
    return (SD_FALSE);

  if (ac->asn1r_elmnt_id != 0)		/* EOC is id_code 0		*/
    return (SD_FALSE);

/* This looks like the EOC from a indefinite length constructor 	*/
  if (ac->_asn1_indef_track[ac->asn1r_msg_level] == SD_TRUE) /* if indef len	*/
    {
    ALOG_CDEC1 ("EOC detected for level %d",ac->asn1r_msg_level);
    len = ac->asn1r_field_ptr - ac->_asn1_constr_start[ac->asn1r_msg_level];
    if (ac->_asn1_byte_count[ac->asn1r_msg_level - 1] < len)
      {
      ALOG_NERR0 ("ASN.1 decode: misplaced end of indefinite length constructor");
      asn1r_set_dec_err(ac, ASN1E_CSTR_INVALID_LEN);	/* call error function		*/
      }
    ac->_asn1_byte_count[ac->asn1r_msg_level-1] -= len;
    ac->_asn1_byte_count[ac->asn1r_msg_level] = 0;		/* set this level count = 0	*/
    return (SD_TRUE); 			/* indicate EOC found		*/
    }
  else
    {
    ALOG_NERR0 ("ASN.1 decode: unexpected end of indefinite length constructor");
    asn1r_set_dec_err(ac, ASN1E_UNEXPECTED_TAG);		/* EOC not allowed here 	*/
    }
  return (SD_FALSE);
  }


/************************************************************************/
/*			asn1r_head_decode				*/
/* function to decode the ASN.1 data element identified and length	*/
/* components and put the result into the component variables.		*/
/* The character ptr asn1r_field_ptr points to the data element start,	*/
/* and is left pointing to the start of the contents.			*/
/************************************************************************/

#define _CHECK_ASN1_LEFT(x) {if((ac->asn1r_field_ptr+x) > ac->asn1r_done_ptr)return(ASN1E_END_O_BUFFER);}

static ST_RET _asn1r_head_decode (ASN1_DEC_CTXT *ac)
  {
ST_UCHAR c;
ST_UINT16 ui16;
ST_INT i, i2, i3;

/* We don't want to read past end of buffer */
  _CHECK_ASN1_LEFT(1);
  c = *(ac->asn1r_field_ptr++);	/* read the first byte of the element	*/
				/* pointer points to next byte		*/

  ac->asn1r_constr_elmnt = (ST_UCHAR) (c & CONSTR);/* isolate the constr bit	*/
  ac->asn1r_elmnt_class = (ST_UCHAR) (c & 0xC0);   /* get the class bits	*/

  if ((c &= 0x1F) == 0x1F)		/* if ID extender -		*/
    {
    _CHECK_ASN1_LEFT(1);
    if ((c = *(ac->asn1r_field_ptr++)) & 0x80) /* if next byte is not last -	*/
      {
      _CHECK_ASN1_LEFT(1);
      if (*ac->asn1r_field_ptr & 0x80)	/* if more than two extention	*/
	{				/* ID octets, reject		*/
        ALOG_NERR0 ("ASN.1 decode: element id too long");
	return (ASN1E_ID_TOO_BIG);
	}
      ui16 = (ST_UINT16) (c & 0x7F) << 7;  /* support 2 extention bytes */

      _CHECK_ASN1_LEFT(1);
      ui16 |= (*(ac->asn1r_field_ptr++) & 0x7F);/* merge the 14 ID bits (only*/
      ac->asn1r_elmnt_id = ui16;		   /* 13 usable in this impl'n).*/
      }
    else				/* one octet extended		*/
      ac->asn1r_elmnt_id = (ST_UINT16) c;	/* write masked ID code 	*/
    }
  else					/* not extended ID code 	*/
    ac->asn1r_elmnt_id = (ST_UINT16) c;	/* write masked ID code 	*/

/* asn1_field_ptr points to contents length field start.			*/
/* Support all length forms : short, long, and indefinite.		*/

  _CHECK_ASN1_LEFT(1);
  if ((c = *(ac->asn1r_field_ptr++)) & 0x80)	/* check for long,indef forms	*/
    {
    if (c &= 0x7F)			/* if long form 		*/
      {
      _CHECK_ASN1_LEFT((int) c);
      switch (c)			/* switch on # bytes in length	*/
	{
	case 1 :			/* one byte length		*/
	  ac->asn1r_elmnt_len = *(ac->asn1r_field_ptr++) & 0xFF;
	break;

	case 2 :			/* two byte length		*/
	  i =  (ST_INT) ((ST_UINT) *(ac->asn1r_field_ptr++) << 8);
	  ac->asn1r_elmnt_len = (ST_INT) 
	    (((ST_UINT) *(ac->asn1r_field_ptr++) & 0xFF) | i);
	break;

	case 3 :	 		/* three byte length		*/
	  i =  (ST_INT) ((ST_UINT) *(ac->asn1r_field_ptr++) << 16);
#if (INT_MAX <= 0x7FFF)
	  if (i != 0)
            {
            ALOG_NERR0 ("ASN.1 decode: element length too long");
            return(ASN1E_INVALID_LENGTH);	/* other lengths not supported	*/
            }
#endif
	  i2 =  (ST_INT) (((ST_UINT) *(ac->asn1r_field_ptr++) << 8) | i);

	  ac->asn1r_elmnt_len = (ST_INT) 
	    (((ST_UINT) *(ac->asn1r_field_ptr++) & 0xFF) | i2);
	break;

	case 4 :	 		/* four byte length		*/
	  i =  (ST_INT) ((ST_UINT) *(ac->asn1r_field_ptr++) << 24);
	  i2 =  (ST_INT) (((ST_UINT) *(ac->asn1r_field_ptr++) << 16) | i);
#if (INT_MAX <= 0x7FFF)
	  if (i != 0 || i2 != 0)
            {
            ALOG_NERR0 ("ASN.1 decode: element length too long");
            return (ASN1E_INVALID_LENGTH);	/* other lengths not supported	*/
            }
#endif
	  i3 =  (ST_INT) (((ST_UINT) *(ac->asn1r_field_ptr++) << 8) | i2);
	  ac->asn1r_elmnt_len = (ST_INT) 
	    (((ST_UINT) *(ac->asn1r_field_ptr++) & 0xFF) | i3);
	break;

	default :
           ALOG_NERR0 ("ASN.1 decode: length length > 4");
	   return (ASN1E_INVALID_LENGTH); /* others not supported	*/
	break;
	}
      }
    else				/* indefinite length		*/
      {
      if (ac->asn1r_constr_elmnt) 		/* valid only for constructors	*/
	ac->asn1r_indef_flag = SD_TRUE;	/* set 'indefinite len' flag    */
      else
        {
        ALOG_NERR0 ("ASN.1 decode: indefinite length primitive");
	return (ASN1E_INVALID_LENGTH);	/* Bad PDU structure		*/
        }
      }
    }
  else					/* short form length		*/
    ac->asn1r_elmnt_len = (ST_INT) c;  	/* write length 		*/

  /* If NOT indefinite length, make sure length is legal.	*/
  if (!ac->asn1r_indef_flag)
    {
    if ((ac->asn1r_elmnt_len < 0) || ((ac->asn1r_field_ptr+ac->asn1r_elmnt_len) > ac->asn1r_done_ptr))
      {
      ALOG_NERR2 ("ASN.1 decode element length error: ASN.1 length=%d, remaining ASN.1 buffer %d ",
                  ac->asn1r_elmnt_len, ac->asn1r_done_ptr - ac->asn1r_field_ptr);
      return (ASN1E_INVALID_LENGTH);
      }
    }

  return (SD_SUCCESS);
  }					/* data element decode complete */

/************************************************************************/
/*			_call_user					*/
/* Function to call the user function for the particular ID class.	*/
/* Passes the id code for the data element to the selected function	*/
/* For Universal class, also checks for EOC.				*/
/*									*/
/* The user functions are responsible for ensuring that the asn1_field_ptr	*/
/* is left pointing to the start of the next field.			*/
/************************************************************************/

static ST_VOID _call_user (ASN1_DEC_CTXT *ac)
 {
ST_UINT16 curtag;
ST_INT i;
ST_INT num_tags;
ASN1R_TAG_CTRL_1 *tag_ctrl;
ASN1R_TAG_PAIR *tags;

/* When parsing is being done on the basis of tag, compare the current	*/
/* tag with the list of allowable tags and call the corresponding fcn	*/
/* pointer. Error checking was done previously when tag_add was called. */

  if (ac->asn1r_decode_method == ASN1_TABLE_METHOD)
    {
    curtag =  (ST_UINT16) (ac->asn1r_elmnt_class | ac->asn1r_constr_elmnt) << 8;
    curtag |= ac->asn1r_elmnt_id;

    tag_ctrl = (ASN1R_TAG_CTRL_1 *) ac->asn1r_tag_table;
    tags = tag_ctrl->tags;
    num_tags = tag_ctrl->num_tags;
    for (i = 0; i < num_tags; ++i, ++tags)
     {
     if (curtag == tags->_asn1r_tag)
       {
       (*tags->_asn1r_tag_fun)(ac);		 /* Call user-defined function.	*/
       return;
       }
     }
    ALOG_NERR1 ("ASN.1 decode: unexpected tag 0x%X", (ST_UINT) curtag);
    asn1r_set_dec_err(ac, ASN1E_UNEXPECTED_TAG);
    return;
    }

  if (ac->asn1r_decode_method == ASN1_TAG_METHOD)
    {
    curtag =  (ST_UINT16) (ac->asn1r_elmnt_class | ac->asn1r_constr_elmnt) << 8;
    curtag |= ac->asn1r_elmnt_id;
    for (i = 0; i < ac->_asn1r_ntag; i++)
     {
      if (curtag == ac->_asn1r_valid_tags[i])
       {
       ac->_asn1r_old_ntag = ac->_asn1r_ntag;	 /* Tags are deleted once used,	*/
       ac->_asn1r_ntag = 0;			 /* but are recoverable. 	*/
       (*ac->_asn1r_tag_fun[i])(ac);		 /* Call user-defined function.	*/
       return;
       }
     }
    ALOG_NERR1 ("ASN.1 decode: unexpected tag 0x%X", (ST_UINT) curtag);
    asn1r_set_dec_err(ac, ASN1E_UNEXPECTED_TAG);
    return;
    }

  if (ac->asn1r_decode_method == ASN1_ANYTAG_METHOD)
    {
    curtag =  (ST_UINT16) (ac->asn1r_elmnt_class | ac->asn1r_constr_elmnt) << 8;
    curtag |= ac->asn1r_elmnt_id;
    (*ac->asn1r_anytag_fun)(ac, curtag);
    return;
    }
    
  ac->_asn1r_ntag = 0;				/* class method, no valid tags	*/

/* When parsing is being done on the basis of class, switch on the	*/
/* class of the current data element and call the corresponding fcn	*/
/* pointer. Do error checking where possible.				*/
  switch (ac->asn1r_elmnt_class)
    {
    case CTX : 			/* class = context specific	*/
      (*ac->asn1r_c_id_fun)(ac, ac->asn1r_elmnt_id);
    break;

    case UNI : 			/* class = universal		*/					/* Perform additional checks	*/
      switch (ac->asn1r_elmnt_id) 		/* for constr/prim allowed	*/
	{				/* check selected types 	*/
	case INT_CODE : 		/* integer must be primitive	*/
	   if (ac->asn1r_constr_elmnt)
             {
             ALOG_NERR0 ("ASN.1 decode: invalid INTEGER element");
	     asn1r_set_dec_err (ac, ASN1E_UNEXPECTED_FORM);
             }
	   else
	     {
             (*ac->asn1r_u_id_fun)(ac, ac->asn1r_elmnt_id);
	     }
	break;

	case SEQ_CODE : 		/* sequence valid in constr	*/
	   if (!ac->asn1r_constr_elmnt)
             {
             ALOG_NERR0 ("ASN.1 decode: invalid SEQ element");
	     asn1r_set_dec_err (ac, ASN1E_UNEXPECTED_FORM);
             }
	   else
	     {
             (*ac->asn1r_u_id_fun)(ac, ac->asn1r_elmnt_id);
	     }
	break;

	case SET_CODE : 		/* set valid in constructor	*/
	   if (!ac->asn1r_constr_elmnt)
             {
             ALOG_NERR0 ("ASN.1 decode: invalid SET element");
	     asn1r_set_dec_err (ac, ASN1E_UNEXPECTED_FORM);
             }
	   else
	     {
             (*ac->asn1r_u_id_fun)(ac, ac->asn1r_elmnt_id);
	     }
	break;

	case BOOL_CODE :		/* boolean must be primitive,	*/
	   if (ac->asn1r_constr_elmnt || ac->asn1r_elmnt_len != 1)	/* 1 octet		*/
             {
             ALOG_NERR0 ("ASN.1 decode: invalid BOOL element");
	     asn1r_set_dec_err (ac, ASN1E_INVALID_BOOLEAN);
             }
	   else
	     {
             (*ac->asn1r_u_id_fun)(ac, ac->asn1r_elmnt_id);
	     }
	break;

	case NULL_CODE :		/* null must be primitive	*/
	   if (ac->asn1r_constr_elmnt || ac->asn1r_elmnt_len)
             {
             ALOG_NERR0 ("ASN.1 decode: invalid NULL element");
	     asn1r_set_dec_err (ac, ASN1E_UNEXPECTED_TAG);
             }
	   else
	     {
             (*ac->asn1r_u_id_fun)(ac, ac->asn1r_elmnt_id);
	     }
	break;

	default :			/* all others can be either	*/
	  {
          (*ac->asn1r_u_id_fun)(ac, ac->asn1r_elmnt_id);
	  }
	break;
	}
    break;				/* end of universal		*/

    case APP : 				/* class = application wide	*/
      (*ac->asn1r_a_id_fun)(ac, ac->asn1r_elmnt_id);
    break;

    case PRV : 				/* class = private use		*/
      (*ac->asn1r_p_id_fun)(ac, ac->asn1r_elmnt_id);
    break;
    }
  }

/************************************************************************/
/*			asn1r_set_all_cstr_done				*/
/************************************************************************/

ST_VOID asn1r_set_all_cstr_done (ASN1_DEC_CTXT *ac)
 {
ST_INT i;

  for (i = 0; i < ASN1_MAX_LEVEL; ++i)
    ac->asn1r_c_done_fun[i] = NULL;
  }

/************************************************************************/
/*			asn1r_tag_add 					*/
/************************************************************************/

ST_VOID asn1r_tag_add (ASN1_DEC_CTXT *ac, ST_UINT16 tag, 
		       ST_VOID (*fcn_ptr)(ASN1_DEC_CTXT *ac))
  {
#ifdef DEBUG_SISCO
  if (ac->_asn1r_ntag >= MAX_TAG_FUN)  	/* Array is MAX_TAG_FUN deep */
    {
    ALOG_ERR0 ("ASN.1 Decode Internal Error : Tag Table Buffer Overflow");
    return;
    }
#endif

  ac->_asn1r_valid_tags[ac->_asn1r_ntag] = tag;
  ac->_asn1r_tag_fun[ac->_asn1r_ntag] = (ST_VOID (*)(ASN1_DEC_CTXT *)) fcn_ptr;
  ac->_asn1r_ntag++;
  return;
  }

/************************************************************************/
/*			asn1r_tag_restore				*/
/* This fun restores the el'ts of the tag[] and _asn1_tag_fun[] arrays. */
/************************************************************************/

ST_VOID asn1r_tag_restore (ASN1_DEC_CTXT *ac)
 {
 ac->_asn1r_ntag = ac->_asn1r_old_ntag;
 }

/************************************************************************/
/************************************************************************/
/*				asn1_peek 				*/
/************************************************************************/

ST_RET asn1_peek (ASN1_DEC_CTXT *ac, ST_UCHAR *ptr, ST_INT len)
  {
ST_RET rc;

  ac->asn1r_pdu_dec_err = NO_DECODE_ERR;    /* reset error flag			*/
  ac->asn1r_field_ptr = ptr;		    /* initialize asn1_field_ptr 	*/
  ac->asn1r_done_ptr = ac->asn1r_field_ptr + len;

/* Validate length to avoid possible problems */
  if (len <= 0)
    {
    ALOG_NERR0 ("ASN.1 peek: Length zero or negative");
    asn1r_set_dec_err(ac, ASN1E_END_O_BUFFER);
    return (SD_FAILURE);
    }

  rc = _asn1r_head_decode (ac); /* decodes the data element header	*/
  if (rc != SD_SUCCESS)
    {
    ALOG_NERR0 ("ASN.1 decode: header decode error");
    return (rc);
    }
  return (SD_SUCCESS);
  }

/************************************************************************/
/************************************************************************/
/* ASN.1 message construct tools.					*/
/************************************************************************/
/************************************************************************/
/*			asn1r_strt_asn1_bld				*/
/* Function to initialize the ASN.1 build parameters. Should be called	*/
/* before starting construction of each message.			*/
/************************************************************************/

ST_VOID asn1r_strt_asn1_bld (ASN1_ENC_CTXT *ac, ST_UCHAR *bufptr, ST_INT buflen)
 {
  ALOG_ENC0 ("Starting ASN.1 message construct");

/* Initialize variables in the encode context structure.		*/

  ac->asn1r_encode_overrun = SD_FALSE;
  ac->asn1r_buf_start = bufptr;

  /* Initialize a bunch of variables to the "end" of buffer.		*/
  ac->asn1r_constr_end[0] = ac->asn1r_field_ptr = ac->asn1r_field_end =
    ac->asn1r_buf_end = bufptr + buflen - 1;

  ac->asn1r_msg_level = 0;			     /* init current level	*/
/* Chk this later to verify that "asn1r_strt_asn1_bld" was called.	*/
  ac->asn1r_magic = ASN1_ENC_MAGIC_NUMBER;
  }

/************************************************************************/
/*			asn1r_strt_constr				*/
/* Function to start a constructor build (from back).			*/
/* Simply increments the message level (if notmax) sets the start	*/
/* pointer for the level at the current position			*/
/************************************************************************/

ST_VOID asn1r_strt_constr (ASN1_ENC_CTXT *ac)
  {
  if (++ac->asn1r_msg_level >= ASN1_MAX_LEVEL) /* increment the level			*/
    {
    ALOG_ERR0 ("Error : ASN.1 encode exceeded nesting level");
    --ac->asn1r_msg_level;		/* if at top level			*/
    }

  ALOG_CENC1 ("Starting constructor @ level %d",ac->asn1r_msg_level);
  ac->asn1r_constr_end[ac->asn1r_msg_level] = ac->asn1r_field_ptr; /* save msg ptr	*/
  }

/************************************************************************/
/*			asn1r_strt_constr_indef				*/
/* FOR TEST APPLICATIONS ONLY.						*/
/* SISCO applications never encode Indefinite Length constructors (slow)*/
/* but this function may be needed by test applications to generate	*/
/* ASN.1 messages with Indefinite Length constructors.			*/
/************************************************************************/

ST_VOID asn1r_strt_constr_indef (ASN1_ENC_CTXT *ac)
  {
  if (++ac->asn1r_msg_level >= ASN1_MAX_LEVEL)	/* increment the level	*/
    {
    ALOG_ERR0 ("Error : ASN.1 encode exceeded nesting level");
    --ac->asn1r_msg_level;		/* if at top level		*/
    }

  ALOG_CENC1 ("Starting 'indefinite length' constructor @ level %d",ac->asn1r_msg_level);
  *ac->asn1r_field_ptr-- = 0;	/* write 2 zeros for 'end of constructor'*/
  *ac->asn1r_field_ptr-- = 0;
  ac->asn1r_constr_end[ac->asn1r_msg_level] = ac->asn1r_field_ptr;
  /* CRITICAL: set ..field_end so length of last prim can be computed later.*/
  ac->asn1r_field_end = ac->asn1r_field_ptr;
  }

/************************************************************************/
/*			asn1r_fin_constr				*/
/* Function to finish a constructor.					*/
/* Writes the constructor length, then the identifier as requested.	*/
/*									*/
/* Pass the required id_code, the tag bits (upper 3 bits), and flag to	*/
/* indicate when the indefinite length is required (indef != 0) 	*/
/************************************************************************/

ST_VOID asn1r_fin_constr (ASN1_ENC_CTXT *ac, ST_UINT16 id_code, ST_UINT16 el_tag, ST_BOOLEAN indef)
  {
ST_INT len;

  if (ac->asn1r_field_ptr - ASN1_MAX_ELEMENT_OVERHEAD < ac->asn1r_buf_start)
    {
    /* Do NOT encode anything, but decrement msg_level to avoid nesting	*/
    /* errors if user continues trying to encode.			*/
    --ac->asn1r_msg_level;
    ac->asn1r_encode_overrun = SD_TRUE;	/* May already be SD_TRUE.	*/
    return;
    }


  if (!indef)			/* write the length field (moves ptr)	*/
    {
    len = ac->asn1r_constr_end[ac->asn1r_msg_level] - ac->asn1r_field_ptr;
    _wr_asn1_len (ac, len);
    }
  else			  	/* if not indefinite length -		*/
    *(ac->asn1r_field_ptr--) = 0x80;

				/* write identifier (moves ptr)		*/
  _wr_ident (ac, id_code, (ST_UINT16) (el_tag | CONSTR)); 

#ifdef DEBUG_SISCO
  ALOG_CENC1 ("Finish level %d constructor",ac->asn1r_msg_level);
  if (indef)
    {
    ALOG_CENC0 ("Indefinite length");
    }
  else
    {
    ALOG_CENC1 ("Length = %d",ac->asn1r_constr_end[ac->asn1r_msg_level] - ac->asn1r_field_ptr);
    }
  ALOG_ENCH (ac->asn1r_constr_end[ac->asn1r_msg_level] - ac->asn1r_field_ptr, ac->asn1r_field_ptr + 1);
#endif

  if (--ac->asn1r_msg_level < 0)	/* if was level 0 already		*/
    ac->asn1r_msg_level = 0;		/* set = 0				*/

  ac->asn1r_field_end = ac->asn1r_field_ptr;	/* reset the field end pointer*/
  }

/************************************************************************/
/*			asn1r_fin_prim					*/
/* Function to finish writing a primitive data element. 		*/
/* Pass the constructor type (set,sequence) and data element type.	*/
/************************************************************************/

ST_VOID asn1r_fin_prim (ASN1_ENC_CTXT *ac, ST_UINT16 id_code, ST_UINT16 el_tag)
  {
ST_INT len;

  if (ac->asn1r_field_ptr - ASN1_MAX_ELEMENT_OVERHEAD < ac->asn1r_buf_start)
    {
    ac->asn1r_encode_overrun = SD_TRUE;	/* May already be SD_TRUE.	*/
    return;
    }

  len = ac->asn1r_field_end - ac->asn1r_field_ptr;

				/* write the length field (moves ptr)	*/
  _wr_asn1_len (ac, len);
  _wr_ident (ac, id_code, el_tag);	/* write the identifier (moves ptr)	*/

  len = ac->asn1r_field_end - ac->asn1r_field_ptr;

  ALOG_CENC0 ("Finish primitive");
  ALOG_ENCH (len, ac->asn1r_field_ptr + 1);

  ac->asn1r_field_end = ac->asn1r_field_ptr;	/* reset the field end pointer*/
  }

/************************************************************************/
/*			_wr_ident					*/
/* Function to write an ASN.1 identifier field. 			*/
/* Pass the id code (0 to 0x3FFF), and the tag (class and constr bits)	*/
/************************************************************************/

static ST_VOID _wr_ident (ASN1_ENC_CTXT *ac, ST_UINT16 id_code, ST_UINT16 el_tag)
  {
  if (id_code < 0x1F)		/* if no extension octets required	*/
    *(ac->asn1r_field_ptr--) = (ST_UCHAR)(id_code | el_tag);	/* one byte identifier	*/
  else
    {
    if (id_code < 0x80) 	/* if just one extension byte - 	*/
      *(ac->asn1r_field_ptr--) = (ST_UCHAR)id_code;		/* write id_code	*/
    else
      { 			/* need two extension bytes		*/
      *(ac->asn1r_field_ptr--) = (ST_UCHAR)(id_code & 0x7F); /* write low 7 bits	*/
      *(ac->asn1r_field_ptr--) = (ST_UCHAR)(((id_code >> 7) & 0x7F) | 0x80);
      }
    *(ac->asn1r_field_ptr--) = (ST_UCHAR)(el_tag | 0x1F); /* indicte extnd'r	*/
    }
  }

/************************************************************************/
/*			_wr_asn1_len					*/
/* Function to write an ASN.1 length field				*/
/* Lengths from 0 to INT_MAX are supported.				*/
/************************************************************************/

static ST_VOID _wr_asn1_len (ASN1_ENC_CTXT *ac, ST_INT len)
  {
  if (len <= 0x7F)			/* see if short form will do	*/
    *(ac->asn1r_field_ptr--) = (ST_UCHAR)len;
  else
    {					/* need long form		*/
    if (len <= 0xFF)			/* if one byte will do		*/
      {
      *(ac->asn1r_field_ptr--) = (ST_UCHAR)len;   /* write length 		*/
      *(ac->asn1r_field_ptr--) = (ST_UCHAR) 0x81; /* write # bytes in len    */
      }
    else if (len <= 0xFFFF)
      {
      *(ac->asn1r_field_ptr--) = (ST_UCHAR)(len & 0xFF);    	   /* low byte	*/
      *(ac->asn1r_field_ptr--) = (ST_UCHAR)((len >> 8) & 0xFF); /* high byte	*/
      *(ac->asn1r_field_ptr--) = (ST_UCHAR) 0x82;               /* #len bytes*/
      }
    else if (len <= (ST_INT) 0xFFFFFF)
      {
      *(ac->asn1r_field_ptr--) = (ST_UCHAR)(len & 0xFF);    	   /* low byte	*/
      *(ac->asn1r_field_ptr--) = (ST_UCHAR)((len >> 8) & 0xFF); /* mid byte	*/
      *(ac->asn1r_field_ptr--) = (ST_UCHAR)((len >> 16) & 0xFF); /* high byte	*/
      *(ac->asn1r_field_ptr--) = (ST_UCHAR) 0x83;               /* #len bytes*/
      }
    else
      {
      *(ac->asn1r_field_ptr--) = (ST_UCHAR)(len & 0xFF);    	   /* low byte	*/
      *(ac->asn1r_field_ptr--) = (ST_UCHAR)((len >> 8) & 0xFF); /* mid log byte	*/
      *(ac->asn1r_field_ptr--) = (ST_UCHAR)((len >> 16) & 0xFF); /* mid high byte	*/
      *(ac->asn1r_field_ptr--) = (ST_UCHAR)((len >> 24) & 0xFF); /* high byte	*/
      *(ac->asn1r_field_ptr--) = (ST_UCHAR) 0x84;               /* #len bytes*/
      }
    }
  }				      



/************************************************************************/
/*			asn1r_set_dec_err					*/
/* Function called from anywhere when an error is detected. Sets the	*/
/* passed error code = asn1_pdu_dec_err, and sets asn1_decode_done.		*/
/************************************************************************/

ST_VOID asn1r_set_dec_err (ASN1_DEC_CTXT *ac, ST_RET err_code)
  {
  ALOG_NERR1 ("ASN.1 decode error : 0x%X", err_code);
  ac->asn1r_pdu_dec_err = err_code;	/* set the problem type 		*/
  ac->asn1r_decode_done = SD_TRUE;	/* quit parse				*/
  }

/************************************************************************/
/*			asn1r_done_err					*/
/* function to eliminate premature decode complete, allow release of	*/
/* allocated resources by invoking the error function.			*/
/* Use as a asn1_decode_done_fun.					*/
/************************************************************************/

ST_VOID asn1r_done_err (ASN1_DEC_CTXT *ac)
  {
  ALOG_NERR0 ("ASN1 decode: PDU ended before valid");
  asn1r_set_dec_err (ac, ASN1E_END_OF_MESSAGE); /* set the error			*/
  if (ac->asn1r_err_fun != NULL)
    (*ac->asn1r_err_fun)(ac, ASN1E_END_OF_MESSAGE); /* call the error function	*/
  }


/************************************************************************/
/*			asn1r_class_err					*/
/* This function is a nominal function to be invoked when a undesired	*/
/* class data element is found during the message parse.		*/
/* Just set an error code, stop parse.					*/
/************************************************************************/

ST_VOID asn1r_class_err (ASN1_DEC_CTXT *ac, ST_UINT16 id_code)
  {
  ALOG_NERR0 ("CLASS ERROR");
  asn1r_set_dec_err (ac, ASN1E_UNEXPECTED_CLASS); /* just set the error flag	*/
  }

/************************************************************************/
/*			asn1_cstr_done_err					*/
/* Function to prevent premature constructor completion.		*/
/************************************************************************/

ST_VOID asn1r_cstr_done_err (ASN1_DEC_CTXT *ac)
  {
  ALOG_NERR0 ("CONSTRUCTOR DONE ERROR");
  asn1r_set_dec_err(ac, ASN1E_END_OF_CSTR);
  }

/************************************************************************/
/*			asn1r_chk_getcstr_done				*/
/* General constructor done function to be used with various pieces	*/
/* of code to get information from a constructor inside a message.	*/
/* To use, the variables _asn1_save_method and _asn1r_fun_save MUST be	*/
/* setup and the ASN.1 function pointer cstr_done_fun must point	*/
/* to this function.							*/
/************************************************************************/

ST_VOID asn1r_chk_getcstr_done (ASN1_DEC_CTXT *ac)
  {
  ac->asn1r_decode_method = ac->asn1r_save_method; /* restore the decode method	*/
  ac->asn1r_decode_done_fun = ac->_asn1r_fun_save;  /* restore the decode done fun*/
  (*ac->_asn1r_cstr_done_save)(ac);		  /* call the user selected fun	*/
  }

/************************************************************************/
/*			asn1_objidcmp					*/
/* Function to compare to OBJECT IDENTIFIERs				*/
/* RETURN:	0 if same						*/
/*		1 if different						*/
/************************************************************************/

ST_BOOLEAN asn1_objidcmp (MMS_OBJ_ID *obj1, MMS_OBJ_ID *obj2)
  {
ST_INT i;

  if (obj1->num_comps != obj2->num_comps)
    return (1);

  for (i = 0; i < obj1->num_comps; i++)
    {
    if (obj1->comps[i] != obj2->comps[i])
      return (SD_TRUE);
    }

  return (SD_FALSE);
  }

/************************************************************************/
/*			asn1r_init_glb_vars				*/
/************************************************************************/

#if defined(NO_GLB_VAR_INIT)

ST_VOID asn1r_init_glb_vars (ST_VOID)
  {
  asn1_debug_sel = ASN1_LOG_ERR;
#if defined( __ALPHA )
  asn1_vax_double_type	= A_G_FLOAT;
#endif
#if defined( VAXC )
  asn1_vax_double_type	= A_D_FLOAT;
#endif
  }

#endif
/************************************************************************/
#ifdef DEBUG_SISCO
/************************************************************************/
/************************************************************************/
/*			_list_elmnt					*/
/* Function to print the attributes of a data element			*/
/************************************************************************/

static ST_VOID _list_elmnt (ASN1_DEC_CTXT *ac)
  {
ST_CHAR *ctxt;
ST_CHAR *pc;
ST_CHAR len[50];

  ctxt ="Bogus";
  if ((asn1_debug_sel & ASN1_LOG_DEC) == 0)
    return;

  switch (ac->asn1r_elmnt_class)
    {
    case (0x00) :
      ctxt = "Univ";
    break;
    case (0x40) :
      ctxt = "App ";
    break;
    case (0x80) :
      ctxt = "Ctxt";
    break;
    case (0xC0) :
      ctxt = "Priv";
    break;
     }
 
  if (ac->asn1r_constr_elmnt)
    pc = "Cstr";
  else
    pc = "Prim";

  if (!ac->asn1r_indef_flag)
    sprintf (len,"%u", (unsigned int) ac->asn1r_elmnt_len);
  else
    strcpy (len,"Indef");

  ALOG_DECLF ();
  ALOG_CDEC4 ("Element : %s, %s, ID = %d, Len = %s",
			ctxt, pc, (int) ac->asn1r_elmnt_id, len);
  }

/************************************************************************/
#endif	/* end ifdef DEBUG_SISCO						*/
/************************************************************************/


	/*------------------------------------------------------*/
	/* MMS_BTOD to/from MMS_UTC_TIME conversion functions	*/
	/*------------------------------------------------------*/

/************************************************************************/
/*			asn1_convert_btod_to_utc			*/
/* This function converts MMS_BTOD (time relative to 1/1/1984) to the	*/
/* MMS_UTC_TIME (time relative to 1/1/1970).				*/
/* The qflags field in the MMS_UTC_TIME need to be set by the calling	*/
/* function.								*/
/* Only the MMS_BTOD6 form of the MMS_BTOD struct can be converted to	*/
/* the MMS_UTC_TIME.							*/ 
/* Parameters:								*/
/*	btod	pointer to MMS_BTOD struct that should be converted to	*/
/*		the MMS_UTC_TIME					*/
/*	utc	pointer to MMS_UTC_TIME struct where the result of the	*/
/*		conversion will be placed				*/
/* Return:								*/
/*	SD_SUCCESS	if function successful				*/
/*	SD_FAILURE	otherwise					*/
/************************************************************************/
ST_RET asn1_convert_btod_to_utc (MMS_BTOD *btod, MMS_UTC_TIME *utc)
  {
time_t tJan84 = TIME_T_1984_JAN_1;

  if (btod->form != MMS_BTOD6)
    {
    ALOG_NERR0 ("convert_btod_to_utc: MMS_BTOD4 can't be converted to UTC time");
    return (SD_FAILURE);
    }

  /* Now compute the MMS_UTC_TIME */
  utc->secs = (ST_UINT32) (tJan84 +
                           (time_t) (btod->day * SECONDS_PER_DAY) + (time_t) (btod->ms / 1000));
					/* num of seconds since Jan 1, 1970	*/
  /* NOTE: use 0x01000000 (2**24) in fraction computations.	*/
  utc->fraction = (ST_UINT32) ((ST_DOUBLE)(btod->ms % 1000) / 1000.0 * (ST_DOUBLE)0x01000000);
					/* fraction of a second	on 24-bits */

  return (SD_SUCCESS);
  }

/************************************************************************/
/*			asn1_convert_utc_to_btod			*/
/* This function converts MMS_UTC_TIME (time relative to 1/1/1970) to	*/
/* the MMS_BTOD (time relative to 1/1/1984).				*/
/* The form field in the MMS_BTOD is set to MMS_BTOD6 by this function.	*/
/* Parameters:								*/
/*	utc	pointer to MMS_UTC_TIME struct that should be converted	*/
/*		to the MMS_BTOD						*/
/*	btod	pointer to MMS_BTOD struct where the result of the	*/
/*		conversion will be placed				*/
/* Return:								*/
/*	SD_SUCCESS	if function successful				*/
/*	SD_FAILURE	otherwise					*/
/************************************************************************/
ST_RET asn1_convert_utc_to_btod (MMS_UTC_TIME *utc, MMS_BTOD *btod)
  {
time_t tJan84 = TIME_T_1984_JAN_1;

  /* Now compute the MMS_BTOD time	*/
  btod->day = (ST_INT32) (utc->secs - tJan84) / SECONDS_PER_DAY;	  /* num of days since 1/1/1984		*/
  btod->ms = (ST_INT32) ((utc->secs - tJan84) % SECONDS_PER_DAY) * 1000; /* num milliseconds since midnight	*/
  /* NOTE: use 0x01000000 (2**24) in fraction computations.	*/
  btod->ms += (ST_INT32) ((ST_DOUBLE) utc->fraction * 1000.0/(ST_DOUBLE)0x01000000);
							/* add the milliseconds left in a sec	*/
  btod->form = MMS_BTOD6;

  return (SD_SUCCESS);
  }

/************************************************************************/
/*			asn1_convert_timet_to_btime6			*/
/* Convert time_t value to MMS_BTIME6.					*/
/* Input "time_t" value is always based on UTC (GMT). It is compared	*/
/* to the define TIME_T_1984_JAN_1 which is also based on UTC (GMT).	*/
/* The difference is used to compute the number of seconds elapsed	*/
/* since 1984. Then seconds is converted to days & milliseconds.	*/
/************************************************************************/
ST_RET asn1_convert_timet_to_btime6 (time_t tThis, MMS_BTIME6 *btime6)
  {
time_t tJan84 = TIME_T_1984_JAN_1;
long elapsed;		/* elapsed time since 1984-Jan-1 (in seconds)	*/
ldiv_t divResult;
ST_RET retcode;

  /* Find the number of seconds since "1984-Jan-1" and convert to days & msec.*/
  /* NOTE: difftime returns "double" (don't know why), but ldiv needs "long".*/
  elapsed = (long) difftime (tThis, tJan84);

  if (elapsed < 0)
    {				/* "negative" elapsed time NOT ALLOWED	*/
    btime6->day = 0;		/* days since 1984-Jan-1		*/
    btime6->ms = 0;		/* milliseconds this day		*/
    retcode = SD_FAILURE;
    }
  else
    {
    divResult = ldiv (elapsed, SECONDS_PER_DAY);

    btime6->day = divResult.quot;		/* days since 1984		*/
    btime6->ms = (divResult.rem * 1000);	/* milliseconds this day	*/
    retcode = SD_SUCCESS;
    }

  return (retcode);
  }

/************************************************************************/
/*			bstrcpy						*/
/************************************************************************/
ST_VOID bstrcpy (ST_UCHAR *dstptr, ST_UCHAR *srcptr, ST_INT numbits)
  {
ST_INT num_bytes = numbits/8;	/* Number of complete bytes	*/
ST_INT extra_bits = numbits%8;	/* Number of extra bits		*/ 
ST_UCHAR src_mask;
ST_UCHAR dst_mask;

  if (num_bytes)
    memcpy (dstptr, srcptr, num_bytes);	
  
  if (extra_bits)
    {
    src_mask = 0xFF << (8-extra_bits);	/* Use high bits from src	*/
    dst_mask = 0xFF >> extra_bits;	/* Use low bits from dst	*/

    dstptr[num_bytes] &= dst_mask;
    dstptr[num_bytes] |= (srcptr[num_bytes] & src_mask);
    }
  }

/************************************************************************/
/*			bvstrcpy					*/
/* Copy one "variable length bitstring" to another.			*/
/************************************************************************/
ST_VOID bvstrcpy (MMS_BVSTRING *dstptr, MMS_BVSTRING *srcptr)
  {
  dstptr->len = srcptr->len;
  bstrcpy (dstptr->data, srcptr->data, srcptr->len);
  }

/************************************************************************/
/*			bstrcmp						*/
/* Compare bitstrings.							*/
/* RETURNS: 0 if bitstrings identical, 1 if different.			*/
/************************************************************************/
ST_INT bstrcmp (ST_UCHAR *dstptr, ST_UCHAR *srcptr, ST_INT numbits)
  {
ST_INT num_bytes = numbits/8;	/* Number of complete bytes	*/
ST_INT extra_bits = numbits%8;	/* Number of extra bits		*/ 
ST_UCHAR mask;
  
  if (num_bytes)
    {
    if (memcmp (dstptr, srcptr, num_bytes))
      return (1);	/* different	*/
    }
  
  if (extra_bits)
    {
    /* Mask off extra (low order) bits in last byte.	*/
    mask = 0xFF << (8-extra_bits);	/* Use high order bits	*/
    if ((dstptr[num_bytes] & mask) != (srcptr[num_bytes] & mask))
      return (1);	/* different	*/
    }
  return (0);	/* same	*/
  }

/************************************************************************/
/*			asn1r_skip_elmnt				*/
/* Skip over current ASN.1 element without decoding.			*/
/* Does not work for indefinite length encoding (returns SD_FAILURE).	*/
/* DEBUG: If indefinite len support needed, try using asn1r_parse_next.	*/
/************************************************************************/
ST_RET asn1r_skip_elmnt (ASN1_DEC_CTXT *aCtx)
  {
ST_RET retcode = SD_SUCCESS;

  if (aCtx->asn1r_constr_elmnt)
    {
    if (aCtx->asn1r_indef_flag)
      {
      ALOG_ERR0 ("asn1r_skip_elmnt: indefinite length encoding not supported");
      asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM); 
      retcode = SD_FAILURE;
      }
    else
      {
      aCtx->asn1r_constr_elmnt = 0;	/* Make ASN.1 treat it like primitive.	*/
      --aCtx->asn1r_msg_level;	/* Already incremented for constructor	*/
				/* so decrement to act like primitive.	*/
      aCtx->asn1r_field_ptr += aCtx->asn1r_elmnt_len;
      }
    }
  else
    {	/* primitive: just update field_ptr	*/
    aCtx->asn1r_field_ptr += aCtx->asn1r_elmnt_len;
    }
  return (retcode);
  }

/*======================================================================*/
/*======================================================================*/
/* The following functions are for a new decode method that does	*/
/* NOT use any function pointers:					*/
/*			asn1r_decode_init 				*/
/*			asn1r_byte_count_decrement (static)		*/
/*			asn1r_get_tag					*/
/*======================================================================*/
/*======================================================================*/

/************************************************************************/
/*			asn1r_decode_init 				*/
/* Initialize the ASN.1 decode context.					*/
/* IMPORTANT:  Use ONLY with "asn1r_get_tag".				*/
/* NOTE: This function does NOT use any of the function pointers in	*/
/*     ASN1_DEC_CTXT (_asn1r_tag_fun, asn1r_c_done_fun, etc.).		*/
/*     Also, several other parameters in ASN1_DEC_CTXT are NOT used.	*/
/* NOTE: To avoid a slow "memset" call, this function only sets the	*/
/*	necessary parameters in ASN1_DEC_CTXT (other parameters contain	*/
/*	random data).							*/
/************************************************************************/
ST_RET asn1r_decode_init (ASN1_DEC_CTXT *ac,
	ST_UCHAR *ptr,		/* ASN.1 message to decode	*/
	ST_INT len)		/* length of ASN.1 message	*/
  {
  /* Validate length to avoid possible problems	*/
  if (len <= 0)
    {
    ALOG_NERR0 ("ASN.1 decode: Length zero or negative");
    return (ASN1E_END_O_BUFFER);
    }

/* initialize flags, counters, pointers for decode			*/

  ac->asn1r_indef_flag = SD_FALSE;	/* clear flag			*/
  ac->_asn1_indef_track[0] = SD_FALSE;	/* level 0 is always definite	*/
  ac->_asn1_byte_count[0] = len;	/* set # bytes left in level 0	*/
  ac->asn1r_msg_level = 0;		/* start at message level 0	*/

  ac->asn1r_field_ptr = ptr;
  ac->asn1r_done_ptr  = ptr + len;
  ac->asn1r_msg_start = ptr;		/* save start ptr to compute	*/
					/* offset for logging.		*/

  ALOG_DEC1 ("ASN.1 MESSAGE TO DECODE : len = %d",len);
  ALOG_DECH (len,ptr);
  return (SD_SUCCESS);
  }

/************************************************************************/
/*			asn1r_byte_count_decrement			*/
/* If this is a "definite length" constructor, decrement its byte count.*/
/* NOTE: For "indefinite length" constructor, DO NOTHING.		*/
/************************************************************************/
static ST_RET asn1r_byte_count_decrement (ASN1_DEC_CTXT *ac,
	ST_INT msg_level,	/* constructor nesting level		*/
	ST_INT byte_count)	/* number of bytes processed		*/
  {
  if ( ! ac->_asn1_indef_track[msg_level])
    {
    /* This is "definite length" constructor. Decrement its byte count.	*/
    if (ac->_asn1_byte_count[msg_level] < byte_count)
      {
      if (msg_level == 0)
        ALOG_ERR0 ("ASN.1 decode: data exceeds message size");
      else 
        ALOG_ERR1 ("ASN.1 decode: data exceeds constructor length at message level %d", msg_level);
      return (ASN1E_CSTR_INVALID_LEN);
      }
    ac->_asn1_byte_count[msg_level] -= byte_count;
    }
  return (SD_SUCCESS);
  }

/************************************************************************/
/*			asn1r_get_tag					*/
/* Decode the next ASN.1 tag.						*/
/* IMPORTANT:  Use ONLY with "asn1r_decode_init".			*/
/* Must call 'asn1r_decode_init' once before any calls to this function.*/
/* NOTE: This function does NOT use any of the function pointers in	*/
/*     ASN1_DEC_CTXT (_asn1r_tag_fun, asn1r_c_done_fun, etc.).		*/
/*     Also does NOT use several other parameters in ASN1_DEC_CTXT.	*/
/************************************************************************/
ST_RET asn1r_get_tag (ASN1_DEC_CTXT *ac,
	ST_UINT16 *tag_out)	/* out: decoded tag		*/
  {
ST_INT head_len;	/* num bytes processed by asn1r_head_decode	*/
ST_RET rc;

  /* Before decoding the next tag, if current constructor is 		*/
  /* "definite length", check if it is finished.			*/
  /* NOTE: end of "indefinite length" constructor is a real tag, so	*/
  /*       it is processed AFTER the tag is decoded.			*/
  if (   ac->asn1r_msg_level > 0
      && ac->_asn1_indef_track[ac->asn1r_msg_level] == SD_FALSE	/* definite length*/
      && ac->_asn1_byte_count[ac->asn1r_msg_level] == 0)	/* constr done	*/
    {
    /* 0 bytes left at this constructor level (i.e. this constr done).	*/
    ALOG_CDEC1 ("Level %d parse complete",ac->asn1r_msg_level);
    ac->asn1r_msg_level--;
    *tag_out = 0;	/* 0 always indicates constructor done	*/
    return (SD_SUCCESS);
    }
    
  if (ac->asn1r_field_ptr >= ac->asn1r_done_ptr)
    {
    ALOG_ERR0 ("ASN.1 decode: past end of buffer");
    return (ASN1E_END_O_BUFFER);
    }

  ALOG_DEC1 ("Current decode offset: 0x%X",
             ac->asn1r_field_ptr - ac->asn1r_msg_start);
  ac->asn1r_field_start = ac->asn1r_field_ptr;	/* save field start address*/

  /* Clear old asn1r_indef_flag. May be set again by _asn1r_head_decode.*/
  ac->asn1r_indef_flag = SD_FALSE; 		/* clear flag	*/

  /* Decode the ASN.1 element header.	*/
  rc = _asn1r_head_decode (ac);
  if (rc != SD_SUCCESS)
    {
    ALOG_NERR0 ("ASN.1 decode: header decode error");
    return(rc);
    }

  /* Log this element (if enabled).	*/
#ifdef DEBUG_SISCO
  if (asn1_debug_sel & ASN1_LOG_DEC)
    {
    ST_INT log_len;
    log_len = ac->asn1r_field_ptr - ac->asn1r_field_start;
    /* For primitive, log the whole element	*/
    if (!ac->asn1r_constr_elmnt)
      log_len += ac->asn1r_elmnt_len;

    ALOG_DECH (log_len, ac->asn1r_field_start);	/* log bytes just processed*/

    _list_elmnt (ac);		/* log element attributes		*/
    }
#endif

  /* Generate tag to return to caller (*tag_out).			*/
  /* Our storage of tags in ST_UINT16 can only handle elmnt_id of	*/
  /* 13 bits (high 3 bits indicate Class and Constructor).		*/
  /* This should be good enough for anything we need to parse.		*/
  if (ac->asn1r_elmnt_id > 0x1fff)
    {
    ALOG_ERR1 ("ASN.1 element id = %u unsupported", ac->asn1r_elmnt_id);
    return (SD_FAILURE);		/* should NEVER get here	*/
    }
  *tag_out = ((ST_UINT16) (ac->asn1r_elmnt_class | ac->asn1r_constr_elmnt) << 8)
             | ac->asn1r_elmnt_id;

  /* Check for Indefinite Length End of Constructor (EOC)(i.e. tag==0)	*/
  if (*tag_out == 0)
    {
    /* EOC found. Was constr at this level "indefinite length"?		*/
    if (ac->_asn1_indef_track[ac->asn1r_msg_level])
      {
      ST_INT indef_len;	/* Length of this indefinite length constructor	*/
      ALOG_CDEC1 ("EOC detected for level %d",ac->asn1r_msg_level);
      indef_len = ac->asn1r_field_ptr - ac->_asn1_constr_start[ac->asn1r_msg_level];

      /* Decrement the byte count of the "higher level" constr.		*/
      rc = asn1r_byte_count_decrement (ac, ac->asn1r_msg_level - 1, indef_len);
      if (rc)
        return (rc);

      ALOG_CDEC1 ("Level %d parse complete",ac->asn1r_msg_level);
      ac->asn1r_msg_level--;
      }
    else
      {
      ALOG_NERR0 ("ASN.1 decode: unexpected end of indefinite length constructor");
      return (ASN1E_UNEXPECTED_TAG);	/* EOC not allowed here 	*/
      }
    /* ALWAYS return when EOC found.					*/
    /* NOTE: "*tag_out" already set to 0 to indicate constructor done.	*/
    return (SD_SUCCESS);
    }	/* end "if (*tag_out == 0)"	*/

  /* Compute number of bytes processed by asn1r_head_decode.		*/
  /* Need this to decrement constructor byte counts below.		*/
  head_len = ac->asn1r_field_ptr - ac->asn1r_field_start;

  if (ac->asn1r_constr_elmnt)
    {
    /* Process constructor.	*/
    if (++ac->asn1r_msg_level >= ASN1_MAX_LEVEL)
      {
      ALOG_ERR0 ("Error : ASN.1 decode exceeded nesting level");
      return (ASN1E_NEST_TOO_DEEP);
      }

    /* Save "indefinite length" flag for this constructor level.	*/
    ac->_asn1_indef_track[ac->asn1r_msg_level] = ac->asn1r_indef_flag;

    if (ac->asn1r_indef_flag)
      {
      /* Save start ptr for this constructor. Use to compute len later.	*/
      ac->_asn1_constr_start[ac->asn1r_msg_level] = ac->asn1r_field_ptr;
      /* _asn1_byte_count NOT USED for indef len constructor. Set to 0,	*/
      /* just so it's easier to see in the debugger.			*/
      ac->_asn1_byte_count[ac->asn1r_msg_level] = 0;	/* NOT USED for indef*/

      /* Decrement the byte count of the "higher level" constr.		*/
      /* Use "head_len". Adjust for "data len" later when EOC is found.	*/
      rc = asn1r_byte_count_decrement (ac, ac->asn1r_msg_level - 1, head_len);
      if (rc)
        return (rc);
      }
    else
      {
      /* definite length constructor. Initialize byte count for it.	*/
      ac->_asn1_byte_count[ac->asn1r_msg_level] = ac->asn1r_elmnt_len;
      /* Decrement the byte count of the "higher level" constr.		*/
      rc = asn1r_byte_count_decrement (ac, ac->asn1r_msg_level - 1,
                head_len+ac->asn1r_elmnt_len);
      if (rc)
        return (rc);
      }
    }	/* end constructor processing	*/
  else
    {
    /* This is a primitive.	*/
    /* Decrement the byte count of the "current" constr.		*/
    rc = asn1r_byte_count_decrement (ac, ac->asn1r_msg_level,
              head_len+ac->asn1r_elmnt_len);
    if (rc)
      return (rc);
    }

  return (SD_SUCCESS);
  }

