/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*	(c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*	1995-2005, All Rights Reserved					*/
/*									*/
/* MODULE NAME : acse2dec.c						*/
/* PRODUCT(S)  :							*/
/*									*/
/* MODULE DESCRIPTION :							*/
/*	ACSE Decode Functions.						*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev     Comments					*/
/* --------  ---  ------   -------------------------------------------  */
/* 02/26/09  JRB    12     Fix to handle PDV len=0 or user_info len=0.	*/
/* 04/28/08  GLB    59     Removed CALLED_ONLY and CALLING_ONLY         */
/* 01/30/06  GLB    58     Integrated porting changes for VMS           */
/* 07/06/05  EJV    57     Log "IP ADDR:" on the same line as the ip.	*/
/* 05/19/05  JRB    56     Chg "unrecognized tag" logs from ERR to DEC.	*/
/* 03/31/05  EJV    55     a_get_rem_ip_addr: added defined(linux)	*/
/* 09/20/04  JRB    54     u_copp_con_cnf: don't access acse_conn after	*/
/*			   copp_u_abort_req (it may have been freed).	*/
/* 08/06/04  EJV    53     _a_dec_authentication: chg param to ST_UCHAR.*/
/* 03/15/04  GLB    51     Delete "iso_mms_is_ASO_context_name"         */
/* 03/04/04  JRB    51     Use ASN1 decode log macros (ALOG_DEC*)	*/
/*			   Del logging of A-DATA.ind (use MVL or COPP)	*/
/* 02/06/04  EJV    50	   Use MACE_MECH_ID define.			*/
/* 01/08/04  EJV    49     Ported funcs to sun.				*/
/* 12/12/03  JRB    48     Fix asn1r_get_bitstr calls, init data=0 first*/
/* 10/24/03  JRB    47     Chg #if ..TP0.. to avoid compile errors.	*/
/* 07/08/03  EJV    46     Del old password auth (OBSOLETE_ACSE_AUTH);	*/
/*			   ACSE_AUTH_ENABLED code wo/define.		*/
/*			   Fix the ACSE Auth decode.			*/
/* 03/31/03  JRB    45     asn1r_get_bitstr: add max_bits arg.		*/
/* 11/08/02  JRB    44     asn1_skip_elmnt ret error if indef len.	*/
/* 10/29/02  JRB    43     Fix a_auth_password_dec ret if pwd too long.	*/
/* 10/22/02  ASK    42	   Wrap ifdef TP0_ENABLED around inet_ntoa calls*/
/* 10/14/02  ASK    41     Added decode of abort diagnostic.		*/
/* 10/03/02  ASK    40     Now send abort when AARQ fails to decode	*/
/* 09/27/02  ASK    39     Added ACSE auth decode to AARE, moved 	*/
/*			   bitstring defines into acse2.h		*/	
/* 06/26/02  JRB    38     con_ind: Log calling/called address.		*/
/* 06/20/02  JRB    37     con_ind: Copy calling/called PADDR to AARQ.	*/
/* 01/10/02  EJV    36     Converted to use ASN1R for TRIM7		*/
/* 01/02/02  JRB    35     Converted to use ASN1R (re-entrant ASN1)	*/
/* 10/09/01  JRB    34     Log if unrecognized tag received.		*/
/* 06/22/01  JRB    33     Fix u_copp_rel_cnf_pos & tp4_data_ind	*/
/*			   handling of STATE_RESP_COLL			*/
/*			   state per ISO 8650 (was kluged before).	*/
/* 03/15/01  JRB    32     Use new SMEM allocation functions.		*/
/* 01/02/01  EJV    31     Ported to _AIX				*/
/* 04/12/00  JRB    30     Lint cleanup.				*/
/* 09/13/99  MDE    29     Added SD_CONST modifiers			*/
/* 07/14/99  JRB    28     Add ACSE Password Authentication.		*/
/* 07/14/99  JRB    27     Add functions to get remote NSAP or IP Addr.	*/
/* 08/13/98  JRB    26     Lint cleanup.				*/
/* 08/11/98  JRB    25     BUG_FIX: MOSI: only save user_info if PCI=MMS*/
/* 03/18/98  JRB    24     Use defines (not variables) for ASN1 errors.	*/
/* 11/07/97  JRB    23     Can't use // for comments.			*/
/* 07/13/97  EJV    22     Commented out call to exit() in tp4_except.	*/
/* 05/27/97  JRB   7.00    MMSEASE 7.0 release.				*/
/* 03/19/97  JRB    21     Add MOSI "CALLING" support.			*/
/* 03/19/97  JRB    20     tp4_connect_cnf send AARQ (already encoded).	*/
/*			   Use acse_free_conn to free conn ctrl struct.	*/
/*			   Improve logging.				*/
/*			   Improve release collision handling.		*/
/* 03/07/97  JRB    19     Only pass abort to user if not in IDLE state.*/
/* 03/07/97  JRB    18     Don't call acse_decode unless class==APP.	*/
/* 03/07/97  JRB    17     Use new asn1_skip_elmnt funct instead of	*/
/*			   inline code and "parse_next" calls.		*/
/* 03/07/97  JRB    16     Added MOSI support.				*/
/* 08/12/96  JRB    15     Handle decode error gracefully in		*/
/*			   tp4_data_ind_aarqind & tp4_data_ind_aarqcnf.	*/
/* 07/15/96  JRB    14     Only free acse_conn->aarq_apdu.user_info.ptr	*/
/*			   if len != 0 (wasn't alloc'd otherwise).	*/
/* 07/08/96  JRB    13     Add tp4_except for LEAN_T.			*/
/* 06/25/96  JRB    12     Fix return codes for tp4_data_ind_aarq*	*/
/* 05/02/96  JRB    11     Add STATE_TP4CONN_WAIT.			*/
/* 04/25/96  JRB    10     Set acse_conn->initiator = SD_TRUE in		*/
/*			   tp4_data_ind_aarqcnf (for release collisions)*/
/* 04/18/96  JRB    09     if (acse_conn->state == STATE_AARE_ABANDON)	*/
/*			   in tp4_connect_cnf or tp4_data_ind, call	*/
/*			   tp4_disconnect and free acse_conn.		*/
/* 04/16/96  JRB    08     Copy rcv_data and rcv_data_len to local	*/
/*			   variables in tp4_data_ind for safe freeing.	*/
/* 03/26/96  MDE    07     Added REDUCED_STACK support			*/
/* 03/11/96  JRB    06     Move acse_msgsize to acse2enc.c		*/
/* 03/08/96  JRB    05     Define thisFileName even if !DEBUG_MMS.	*/
/* 03/07/96  JRB    04     Set default for acse_msgsize in case		*/
/*			   a_set_msgsize not called by user.		*/
/* 02/16/96  JRB    03     Clean up asn1_set_dec_err codes.			*/
/* 12/29/95  JRB    02     Broke up tp4_data_ind into subfunctions.	*/
/* 10/30/95  JRB    01     Created					*/
/************************************************************************/
static char *thisFileName = __FILE__;

#include "glbtypes.h"
#include "sysincs.h"
#include "mem_chk.h"
#include "asn1defs.h"
#include "asn1log.h"	/* for ASN.1 decode logging	*/
#include "acseauth.h"
#include "acse2.h"
#if defined (MOSI)
#include "copp_usr.h"
#endif	/* MOSI	*/
#include "tp4api.h"
#include "tp4.h"	/* need MIN_TP0_CONN_ID	definition	*/

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

static MMS_OBJ_ID *my_obj_id;
static ST_INT32 *my_i32;
static ST_INT16 *my_i16;

static AARQ_APDU aarq_apdu;
static AARE_APDU aare_apdu;
static RLRQ_APDU rlrq_apdu;
static RLRE_APDU rlre_apdu;
static ABRT_APDU abrt_apdu;

static ST_VOID decode_aarq (ASN1_DEC_CTXT *aCtx);
static ST_VOID decode_aarq_main (ASN1_DEC_CTXT *aCtx, ST_UINT16 id_code);
static ST_VOID decode_aarq_done (ASN1_DEC_CTXT *aCtx);

static ST_VOID decode_aare (ASN1_DEC_CTXT *aCtx);
static ST_VOID decode_aare_main (ASN1_DEC_CTXT *aCtx, ST_UINT16 id_code);
static ST_VOID decode_aare_done (ASN1_DEC_CTXT *aCtx);

static ST_VOID decode_rlrq (ASN1_DEC_CTXT *aCtx);
static ST_VOID decode_rlrq_main (ASN1_DEC_CTXT *aCtx, ST_UINT16 id_code);
static ST_VOID decode_rlrq_done (ASN1_DEC_CTXT *aCtx);

static ST_VOID decode_rlre (ASN1_DEC_CTXT *aCtx);
static ST_VOID decode_rlre_main (ASN1_DEC_CTXT *aCtx, ST_UINT16 id_code);
static ST_VOID decode_rlre_done (ASN1_DEC_CTXT *aCtx);

static ST_VOID decode_abrt (ASN1_DEC_CTXT *aCtx);
static ST_VOID decode_abrt_main (ASN1_DEC_CTXT *aCtx, ST_UINT16 id_code);
static ST_VOID decode_abrt_done (ASN1_DEC_CTXT *aCtx);

static ST_VOID decode_obj_id (ASN1_DEC_CTXT *aCtx);
static ST_VOID decode_i32 (ASN1_DEC_CTXT *aCtx);
static ST_VOID decode_i16 (ASN1_DEC_CTXT *aCtx);
static ST_VOID decode_result_source1 (ASN1_DEC_CTXT *aCtx);
static ST_VOID decode_result_source2 (ASN1_DEC_CTXT *aCtx);
static ST_VOID decode_acse_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID decode_constr_done (ASN1_DEC_CTXT *aCtx);
/* DEBUG: all calls to tp4_reldata could be eliminated if tp4 quits	*/
/* allocating data buffers. This would save a malloc and a memcpy in tp4.*/

static ST_CHAR *a_auth_password_dec (ST_UCHAR *asn1_ptr);
static ST_RET _a_dec_authentication (ACSE_AUTH_INFO *authInfo,		/* out	*/
                                     ST_UCHAR       *auth_value,	/* in	*/
                                     ST_INT          auth_value_len);	/* in	*/


#if defined (MOSI)
ACSE_CONN *decode_acse_conn;	/* connection we're doing decode for.	*/

static ST_VOID END_user_info (ASN1_DEC_CTXT *aCtx);

/************************************************************************/
/*			u_copp_con_cnf					*/
/* P-CONNECT.cnf (Connect Confirm).					*/
/* Parameters:								*/
/*	acse_conn	Pointer to connection info			*/
/*	result		P_CON_RESULT_ACCEPT, P_CON_RESULT_USER_REJ or	*/
/*			P_CON_RESULT_PROVIDER_REJ			*/
/*	reason		Provider-reason (if result=P_CON_RESULT_PROVIDER_REJ)*/
/************************************************************************/
ST_VOID u_copp_con_cnf (ACSE_CONN *acse_conn, ST_INT result, ST_INT reason)
  {
ASN1_DEC_CTXT localDecCtx;	/* For readability, use "aCtx" to access this.*/
ASN1_DEC_CTXT *aCtx = &localDecCtx;

  memset (aCtx, 0, sizeof (ASN1_DEC_CTXT));	/* CRITICAL: start clean	*/

  if (acse_conn->state != STATE_AARE_WAIT)
    {
    ACSELOG_ERR0 ("ACSE-ERROR: Ignoring unexpected P-CONNECT.cnf");
    return;
    }
  if (result == P_CON_RESULT_PROVIDER_REJ)
    {	/* No AARE to decode.	*/
    ACSELOG_DEC2 ("A-ASSOCIATE.cnf-: acse_conn_id =0x%08X  user_conn_id =%4ld",
      acse_conn, acse_conn->user_conn_id);
    ACSELOG_DEC1C ("Rejected by PS-provider. Provider-reason = %d. No P-user-data.",
      reason);

    /* ACSE spec says to pass "A-ASSOCIATE.cnf-" primitive to		*/
    /* ACSE-user, but ACSE-user may not handle it with no AARE info, so	*/
    /* pass "A-P-ABORT.ind" primitive instead.  The result is the same.	*/
    abrt_apdu.source = ACSE_SERVICE_PROVIDER;
    abrt_apdu.user_info.len = 0;
    u_a_abort_ind (acse_conn->user_conn_id, &abrt_apdu);
    acse_conn->state = STATE_IDLE;
    /* DON'T free acse_conn. COSP will free it when T-DISCONNECT done.	*/
    return;
    }

  /* result != P_CON_RESULT_PROVIDER_REJ.  Decode AARE.			*/
  aCtx->asn1r_decode_method = ASN1_TAG_METHOD;			/* Use asn1_tag method for decoding. */
  aCtx->asn1r_decode_done_fun = asn1r_done_err;	/* done function = error fun 	*/
  aCtx->asn1r_err_fun = NULL;			/* set up error detected fun 	*/
  decode_acse_conn = acse_conn;	/* Save in global so asn1r_decode_asn1 (aCtx,	*/
				/* can fill it in.			*/
  
  /* CRITICAL: Init global PDV ptr & len. Changed only if PDV found in msg.*/
  pdv_data_ptr = NULL;
  pdv_data_len = 0;

  ASN1R_TAG_ADD (aCtx, APP|CONSTR,TAG_AARE,decode_aare);
  asn1r_decode_asn1 (aCtx, acse_conn->apdu_ptr,acse_conn->apdu_len); /* decode APDU	*/

  if (aCtx->asn1r_pdu_dec_err == NO_DECODE_ERR)
    {						/* Handle AARE.		*/
    if (aare_apdu.result == 0)
      {
      acse_conn->initiator = SD_TRUE;
      acse_conn->state = STATE_ASSOCIATED;
      ACSELOG_DEC2 ("A-ASSOCIATE.cnf+: acse_conn_id =0x%08X  user_conn_id =%4ld",
        acse_conn, acse_conn->user_conn_id);
      }
    else
      {
      acse_conn->state = STATE_IDLE;
      ACSELOG_DEC2 ("A-ASSOCIATE.cnf-: acse_conn_id =0x%08X  user_conn_id =%4ld",
        acse_conn, acse_conn->user_conn_id);
      }

    /* save partner's security info to the ACSE_CONN for user's anytime examination */
    acse_conn->part_auth_info = aare_apdu.auth_info;
    if (aare_apdu.auth_info.mech_type == ACSE_AUTH_MECH_OTHER)
      {
      /* allocate buffer for the Authentication Value */
      acse_conn->part_auth_info.u.other_auth.auth_value.buf = acse_conn->part_auth_info.u.other_auth.auth_value.ptr =
           (ST_UCHAR *) chk_calloc (aare_apdu.auth_info.u.other_auth.auth_value.len, sizeof(ST_CHAR));
      memcpy (acse_conn->part_auth_info.u.other_auth.auth_value.ptr, aare_apdu.auth_info.u.other_auth.auth_value.ptr,
              aare_apdu.auth_info.u.other_auth.auth_value.len);
      }
    aare_apdu.encrypt_ctrl = acse_conn->encrypt_ctrl;

    u_a_associate_cnf ((ST_LONG) acse_conn, (ST_LONG) acse_conn->user_conn_id,
             &aare_apdu);
    }
  else
    {					/* Handle ASN1 decode error.	*/
    ACSELOG_ERR0 ("ACSE-ERROR: AARE decode failed. Aborting.");
    /* Pass up A-P-ABORT.ind	*/
    abrt_apdu.source = ACSE_SERVICE_PROVIDER;
    abrt_apdu.user_info.len = 0;
    u_a_abort_ind (acse_conn->user_conn_id, &abrt_apdu);

    acse_conn->state = STATE_IDLE;

    /* NOTE: Send copp_u_abort_req ONLY IF COPP accepted conn.		*/
    if (result == P_CON_RESULT_ACCEPT)
      copp_u_abort_req (acse_conn);
    /* CRITICAL: DO NOT access acse_conn after abort. It may have been	*/
    /*     freed by cosp_u_abort_req (called from copp_u_abort_req).	*/
    }
  return;
  }

/************************************************************************/
/*			u_copp_rel_cnf_pos				*/
/* P-RELEASE.cnf+ (POSITIVE Release Confirm).				*/
/* Must be RLRE APDU.  Decode it and call u_a_release_cnf.		*/
/************************************************************************/
ST_VOID u_copp_rel_cnf_pos (ACSE_CONN *acse_conn)
  {
ASN1_DEC_CTXT localDecCtx;	/* For readability, use "aCtx" to access this.*/
ASN1_DEC_CTXT *aCtx = &localDecCtx;

  memset (aCtx, 0, sizeof (ASN1_DEC_CTXT));	/* CRITICAL: start clean	*/

  aCtx->asn1r_decode_method = ASN1_TAG_METHOD;			/* Use asn1_tag method for decoding. */
  aCtx->asn1r_decode_done_fun = asn1r_done_err;	/* done function = error fun 	*/
  aCtx->asn1r_err_fun = NULL;			/* set up error detected fun 	*/
  decode_acse_conn = acse_conn;	/* Save in global so asn1r_decode_asn1 (aCtx,	*/
				/* can fill it in.			*/

  /* CRITICAL: Init global PDV ptr & len. Changed only if PDV found in msg.*/
  pdv_data_ptr = NULL;
  pdv_data_len = 0;

  ASN1R_TAG_ADD (aCtx, APP|CONSTR,TAG_RLRE,decode_rlre);
  asn1r_decode_asn1 (aCtx, acse_conn->apdu_ptr,acse_conn->apdu_len); /* decode APDU	*/

  if (aCtx->asn1r_pdu_dec_err == NO_DECODE_ERR)
    {
    /* Handle RLRE.		*/
    switch (acse_conn->state)
      {
      case STATE_RLRE_WAIT:
      acse_conn->state = STATE_IDLE;
      ACSELOG_DEC2 ("A-RELEASE.cnf+:   acse_conn_id =0x%08X  user_conn_id =%4ld",
        acse_conn, acse_conn->user_conn_id);
      u_a_release_cnf (acse_conn->user_conn_id, &rlre_apdu);
      break;

      case STATE_RESP_COLL:
      acse_conn->state = STATE_ARLSRSP_WAIT;
      ACSELOG_DEC2 ("A-RELEASE.cnf+:   acse_conn_id =0x%08X  user_conn_id =%4ld",
        acse_conn, acse_conn->user_conn_id);
      u_a_release_cnf (acse_conn->user_conn_id, &rlre_apdu);
      break;

      default:
      u_a_except (acse_conn->user_conn_id, EX_ACSE_INVALID_STATE, 
              thisFileName, __LINE__);
      break;
      }	/* end "switch"	*/
    }
  else
    {
    /* Handle ASN1 decode error.	*/
    ACSELOG_ERR0 ("ACSE-ERROR: RLRE decode failed. Aborting.");
    /* Pass up A-P-ABORT.ind	*/
    abrt_apdu.source = ACSE_SERVICE_PROVIDER;
    abrt_apdu.user_info.len = 0;
    u_a_abort_ind (acse_conn->user_conn_id, &abrt_apdu);

    acse_conn->state = STATE_IDLE;
    }
  }


/************************************************************************/
/* u_copp_con_ind							*/
/* P-Connect Indication.						*/
/* Must be AARQ APDU.  Decode it and call u_a_associate_ind.		*/
/************************************************************************/
ST_VOID u_copp_con_ind (ST_LONG user_bind_id, ACSE_CONN *acse_conn)
  {
ASN1_DEC_CTXT localDecCtx;	/* For readability, use "aCtx" to access this.*/
ASN1_DEC_CTXT *aCtx = &localDecCtx;
ST_INT j;

  memset (aCtx, 0, sizeof (ASN1_DEC_CTXT));	/* CRITICAL: start clean	*/

  aCtx->asn1r_decode_method = ASN1_TAG_METHOD;			/* Use asn1_tag method for decoding. */
  aCtx->asn1r_decode_done_fun = asn1r_done_err;	/* done function = error fun 	*/
  aCtx->asn1r_err_fun = NULL;			/* set up error detected fun 	*/
  decode_acse_conn = acse_conn;	/* Save in global so asn1r_decode_asn1 (aCtx,	*/
				/* can fill it in.			*/
  
  /* CRITICAL: Init global PDV ptr & len. Changed only if PDV found in msg.*/
  pdv_data_ptr = NULL;
  pdv_data_len = 0;

  ASN1R_TAG_ADD (aCtx, APP|CONSTR,TAG_AARQ,decode_aarq);
  asn1r_decode_asn1 (aCtx, acse_conn->apdu_ptr,acse_conn->apdu_len); /* decode APDU	*/

  if (aCtx->asn1r_pdu_dec_err == NO_DECODE_ERR)
    {
    /* Handle AARQ.		*/
    acse_conn->state = STATE_AASCRSP_WAIT;

    /* Copy "calling" (remote) address to aarq_apdu.			*/
    if ((aarq_apdu.calling_paddr.psel_len = acse_conn->rem_psel[0])!=0)
      memcpy (aarq_apdu.calling_paddr.psel, &acse_conn->rem_psel[1],
              acse_conn->rem_psel[0]);
    if ((aarq_apdu.calling_paddr.ssel_len = acse_conn->rem_ssel[0])!=0)
      memcpy (aarq_apdu.calling_paddr.ssel, &acse_conn->rem_ssel[1],
              acse_conn->rem_ssel[0]);
    if ((aarq_apdu.calling_paddr.tsel_len = acse_conn->rem_tsel[0])!=0)
      memcpy (aarq_apdu.calling_paddr.tsel, &acse_conn->rem_tsel[1],
              acse_conn->rem_tsel[0]);

    if (acse_conn->tp4_conn_id >= MIN_TP0_CONN_ID)	/* This is TP0 conn. Save IP addr*/
      {
      aarq_apdu.calling_paddr.tp_type = TP_TYPE_TCP;
      aarq_apdu.calling_paddr.netAddr.ip = acse_conn->remNetAddr.ip;
      }
    else				/* This is TP4 conn. Save NSAP	*/
      {
      aarq_apdu.calling_paddr.tp_type = TP_TYPE_TP4;
      if ((aarq_apdu.calling_paddr.nsap_len = acse_conn->remNetAddr.nsap[0])!=0)
        memcpy (aarq_apdu.calling_paddr.netAddr.nsap, &acse_conn->remNetAddr.nsap[1],
                acse_conn->remNetAddr.nsap[0]);
      }

    /* Copy "called" (local) address (except NSAP) to aarq_apdu.	*/
    /* NOTE: "called" tp_type must be same as "calling".	*/
    aarq_apdu.called_paddr.tp_type = aarq_apdu.calling_paddr.tp_type;
    if ((aarq_apdu.called_paddr.psel_len = acse_conn->loc_psel[0])!=0)
      memcpy (aarq_apdu.called_paddr.psel, &acse_conn->loc_psel[1],
              acse_conn->loc_psel[0]);
    if ((aarq_apdu.called_paddr.ssel_len = acse_conn->loc_ssel[0])!=0)
      memcpy (aarq_apdu.called_paddr.ssel, &acse_conn->loc_ssel[1],
              acse_conn->loc_ssel[0]);
    if ((aarq_apdu.called_paddr.tsel_len = acse_conn->loc_tsel[0])!=0)
      memcpy (aarq_apdu.called_paddr.tsel, &acse_conn->loc_tsel[1],
              acse_conn->loc_tsel[0]);

    ACSELOG_DEC1 ("A-ASSOCIATE.ind:  acse_conn_id =0x%08X", acse_conn);
    ACSELOG_DEC0C ("DST (CALLED) ADDRESS:");
    if (aarq_apdu.called_ae_title.AP_title_pres)
      {
      for (j = 0; j < aarq_apdu.called_ae_title.AP_title.num_comps; ++j)
        {			/* Verbose but gets the job done.	*/
        ACSELOG_DEC2C ("  AP_title.comps[%d] = %d",
          j, aarq_apdu.called_ae_title.AP_title.comps[j]);
        }
      }
    if (aarq_apdu.called_ae_title.AE_qual_pres)
      {
      ACSELOG_DEC1C ("  AE_qual = %ld", aarq_apdu.called_ae_title.AE_qual);
      }
    ACSELOG_DEC0C ("  PSEL:");
    ACSELOG_DECH  (aarq_apdu.called_paddr.psel_len, aarq_apdu.called_paddr.psel);
    ACSELOG_DEC0C ("  SSEL:");
    ACSELOG_DECH  (aarq_apdu.called_paddr.ssel_len, aarq_apdu.called_paddr.ssel);
    ACSELOG_DEC0C ("  TSEL:");
    ACSELOG_DECH  (aarq_apdu.called_paddr.tsel_len, aarq_apdu.called_paddr.tsel);

    ACSELOG_DEC0C ("SRC (CALLING) ADDRESS:");
    if (aarq_apdu.calling_ae_title.AP_title_pres)
      {
      for (j = 0; j < aarq_apdu.calling_ae_title.AP_title.num_comps; ++j)
        {			/* Verbose but gets the job done.	*/
        ACSELOG_DEC2C ("  AP_title.comps[%d] = %d",
          j, aarq_apdu.calling_ae_title.AP_title.comps[j]);
        }
      }
    if (aarq_apdu.calling_ae_title.AE_qual_pres)
      {
      ACSELOG_DEC1C ("  AE_qual = %ld", aarq_apdu.calling_ae_title.AE_qual);
      }
    ACSELOG_DEC0C ("  PSEL:");
    ACSELOG_DECH  (aarq_apdu.calling_paddr.psel_len, aarq_apdu.calling_paddr.psel);
    ACSELOG_DEC0C ("  SSEL:");
    ACSELOG_DECH  (aarq_apdu.calling_paddr.ssel_len, aarq_apdu.calling_paddr.ssel);
    ACSELOG_DEC0C ("  TSEL:");
    ACSELOG_DECH  (aarq_apdu.calling_paddr.tsel_len, aarq_apdu.calling_paddr.tsel);
    if (aarq_apdu.calling_paddr.tp_type == TP_TYPE_TCP)
      {
#if defined(TP0_ENABLED)
      struct in_addr sin_addr;	/* inet_ntoa needs this addr format*/
      sin_addr.s_addr = aarq_apdu.calling_paddr.netAddr.ip;
      ACSELOG_DEC1C ("  IP ADDR:  %s", inet_ntoa (sin_addr));
#else
      ACSELOG_ERR0 ("Received TCP conn ind. TCP not supported");
#endif
      }
    else
      {
      ACSELOG_DEC0C ("  NSAP:");
      ACSELOG_DECH  (aarq_apdu.calling_paddr.nsap_len, aarq_apdu.calling_paddr.netAddr.nsap);
      }

    /* save partner's security info to the ACSE_CONN for user's anytime examination */
    acse_conn->part_auth_info = aarq_apdu.auth_info;
    if (aarq_apdu.auth_info.mech_type == ACSE_AUTH_MECH_OTHER)
      {
      /* allocate buffer for the Authentication Value */
      acse_conn->part_auth_info.u.other_auth.auth_value.buf = acse_conn->part_auth_info.u.other_auth.auth_value.ptr =
           (ST_UCHAR *) chk_calloc (aarq_apdu.auth_info.u.other_auth.auth_value.len, sizeof(ST_CHAR));
      memcpy (acse_conn->part_auth_info.u.other_auth.auth_value.ptr, aarq_apdu.auth_info.u.other_auth.auth_value.ptr,
              aarq_apdu.auth_info.u.other_auth.auth_value.len);
      }
    aarq_apdu.encrypt_ctrl = acse_conn->encrypt_ctrl;

    u_a_associate_ind (user_bind_id, (ST_LONG) acse_conn, &aarq_apdu);
    }
  else
    {
    /* Handle ASN1 decode error.  					*/
    /* According to the ACSE spec section 7.3.3.4 (pg 20) we should 	*/
    /* be sending an Abort PDU upon protocol error.			*/
    ACSELOG_ERR0 ("ACSE-ERROR: AARQ decode failed");
    copp_u_abort_req (acse_conn);
    acse_conn->state = STATE_IDLE;
    }
  }

/************************************************************************/
/* u_copp_rel_ind							*/
/* P-Release Indication.						*/
/* Must be RLRQ APDU.  Decode it and call u_a_release_ind.		*/
/************************************************************************/
ST_VOID u_copp_rel_ind (ACSE_CONN *acse_conn)
  {
ASN1_DEC_CTXT localDecCtx;	/* For readability, use "aCtx" to access this.*/
ASN1_DEC_CTXT *aCtx = &localDecCtx;

  memset (aCtx, 0, sizeof (ASN1_DEC_CTXT));	/* CRITICAL: start clean	*/

  aCtx->asn1r_decode_method = ASN1_TAG_METHOD;			/* Use asn1_tag method for decoding. */
  aCtx->asn1r_decode_done_fun = asn1r_done_err;	/* done function = error fun 	*/
  aCtx->asn1r_err_fun = NULL;			/* set up error detected fun 	*/
  decode_acse_conn = acse_conn;	/* Save in global so asn1r_decode_asn1 (aCtx,	*/
				/* can fill it in.			*/

  /* CRITICAL: Init global PDV ptr & len. Changed only if PDV found in msg.*/
  pdv_data_ptr = NULL;
  pdv_data_len = 0;

  ASN1R_TAG_ADD (aCtx, APP|CONSTR,TAG_RLRQ,decode_rlrq);
  asn1r_decode_asn1 (aCtx, acse_conn->apdu_ptr,acse_conn->apdu_len); /* decode APDU	*/

  if (aCtx->asn1r_pdu_dec_err == NO_DECODE_ERR)
    {
    /* Handle RLRQ.		*/
    switch (acse_conn->state)
      {
      case STATE_ASSOCIATED:
      acse_conn->state = STATE_ARLSRSP_WAIT;
      ACSELOG_DEC2 ("A-RELEASE.ind:    acse_conn_id =0x%08X  user_conn_id =%4ld",
        acse_conn, acse_conn->user_conn_id);
      u_a_release_ind (acse_conn->user_conn_id, &rlrq_apdu);
      break;

      case STATE_RLRE_WAIT:
      if (acse_conn->initiator)
        acse_conn->state = STATE_INIT_COLL;
      else
        acse_conn->state = STATE_RESP_COLL;
      ACSELOG_DEC2 ("A-RELEASE.ind:    acse_conn_id =0x%08X  user_conn_id =%4ld",
        acse_conn, acse_conn->user_conn_id);
      u_a_release_ind (acse_conn->user_conn_id, &rlrq_apdu);
      break;

      default:
      u_a_except (acse_conn->user_conn_id, EX_ACSE_INVALID_STATE, 
              thisFileName, __LINE__);
      break;
      }	/* end "switch"	*/
    }
  else
    {
    /* Handle ASN1 decode error.	*/
    ACSELOG_ERR0 ("ACSE-ERROR: RLRQ decode failed");
    }
  }

/************************************************************************/
/* u_copp_p_abort_ind							*/
/* P-P-Abort Indication.						*/
/* ACSE doesn't need to decode anything.  Just pass up A-P-ABORT.ind	*/
/************************************************************************/
ST_VOID u_copp_p_abort_ind (ACSE_CONN *acse_conn, ST_INT reason)
  {
  acse_conn->state = STATE_IDLE;
  ACSELOG_DEC2 ("A-P-ABORT.ind:    acse_conn_id =0x%08X  user_conn_id =%4ld",
    acse_conn, acse_conn->user_conn_id);
  ACSELOG_DEC1C ("Provider-reason = %d", reason);
  /* Pass up A-P-ABORT.ind	*/
  abrt_apdu.source = ACSE_SERVICE_PROVIDER;
  abrt_apdu.user_info.len = 0;
  u_a_abort_ind (acse_conn->user_conn_id, &abrt_apdu);
  }

/************************************************************************/
/* u_copp_u_abort_ind							*/
/* P-U-Abort Indication.						*/
/* Must be ABRT APDU.  Decode it and call u_a_abort_ind.		*/
/************************************************************************/
ST_VOID u_copp_u_abort_ind (ACSE_CONN *acse_conn)
  {
ASN1_DEC_CTXT localDecCtx;	/* For readability, use "aCtx" to access this.*/
ASN1_DEC_CTXT *aCtx = &localDecCtx;

  memset (aCtx, 0, sizeof (ASN1_DEC_CTXT));	/* CRITICAL: start clean	*/

  /* This must be ABRT. Perform ASN1 Decode of ABRT.			*/
  aCtx->asn1r_decode_method = ASN1_TAG_METHOD;			/* Use asn1_tag method for decoding. */
  aCtx->asn1r_decode_done_fun = asn1r_done_err;	/* done function = error fun 	*/
  aCtx->asn1r_err_fun = NULL;			/* set up error detected fun 	*/
  decode_acse_conn = acse_conn;	/* Save in global so asn1r_decode_asn1 (aCtx,	*/
				/* can fill it in.			*/

  /* CRITICAL: Init global PDV ptr & len. Changed only if PDV found in msg.*/
  pdv_data_ptr = NULL;
  pdv_data_len = 0;

  ASN1R_TAG_ADD (aCtx, APP|CONSTR,TAG_ABRT,decode_abrt);
  asn1r_decode_asn1 (aCtx, acse_conn->apdu_ptr,acse_conn->apdu_len); /* decode APDU	*/

  /* "asn1r_decode_asn1 (aCtx," has filled in Global "abrt_apdu" structure.		*/ 

  if (aCtx->asn1r_pdu_dec_err != NO_DECODE_ERR)
    {
    ACSELOG_ERR1 ("ACSE-ERROR: ABRT decode failed (code = %d). Call u_a_abort_ind anyway.",
                  aCtx->asn1r_pdu_dec_err);
    /* Fill "abrt_apdu" with defaults (possibly corrupted during decode).*/
    abrt_apdu.source = ACSE_SERVICE_USER;
    abrt_apdu.user_info.len = 0;
    }

  acse_conn->state = STATE_IDLE;
  ACSELOG_DEC2 ("A-ABORT.ind:      acse_conn_id =0x%08X  user_conn_id =%4ld",
    acse_conn, acse_conn->user_conn_id);
  u_a_abort_ind (acse_conn->user_conn_id, &abrt_apdu);
  }

/************************************************************************/
/* u_copp_data_ind							*/
/* P-Data Indication.							*/
/* ACSE doesn't need to decode anything.  Just call u_a_data_ind.	*/
/************************************************************************/
ST_VOID u_copp_data_ind (ACSE_CONN *acse_conn)
  {
  u_a_data_ind (acse_conn->user_conn_id, acse_conn->apdu_len, acse_conn->apdu_ptr);
  }

#else	/* !MOSI	*/

static ST_INT acse_tag;

static ST_BOOLEAN tp4_data_ind_aarqind (ACSE_CONN *acse_conn, ST_UINT len);
static ST_BOOLEAN tp4_data_ind_aarqcnf (ACSE_CONN *acse_conn, ST_UINT len);
static ST_RET acse_decode (ASN1_DEC_CTXT *aCtx, ST_UCHAR *msg_ptr, ST_UINT len);

/************************************************************************/
/*                      tp4_session_timer_expired			*/
/*----------------------------------------------------------------------*/
/* This function is called by TP4 when the session_timer expires.	*/
/* TRIM-7 does not use any Session timers, so this function will	*/
/* never be called. It's only purpose is to resolve link with LEAN_T.	*/
/* For MOSI, the "tp4_session_timer_expired" function is in COSP????.C	*/
/************************************************************************/
ST_VOID tp4_session_timer_expired (ST_LONG user_conn_id)
  {
  }

/************************************************************************/
/* tp4_bind_cnf								*/
/************************************************************************/
ST_VOID tp4_bind_cnf (ST_LONG user_bind_id, ST_LONG copp_bind_id, ST_RET result)
  {
  ACSELOG_DEC2 ("COPP-Bind   Cnf.  user_bind_id =%9ld copp_bind_id =%9ld",
          user_bind_id, copp_bind_id);
  u_copp_bind_cnf (user_bind_id, copp_bind_id, result);
  }

/************************************************************************/
/* tp4_unbind_cnf							*/
/************************************************************************/
ST_VOID tp4_unbind_cnf (ST_LONG user_bind_id)
  {
  ACSELOG_DEC1 ("COPP-Unbind Cnf.  user_bind_id =%9ld", user_bind_id);
  u_copp_unbind_cnf (user_bind_id);
  }

/************************************************************************/
/* tp4_connect_ind							*/
/* This function is called by the Transport provider when an ACSE	*/
/* Associate request is received.					*/
/************************************************************************/
ST_VOID tp4_connect_ind (
	ST_LONG user_bind_id,
	ST_LONG tp4_conn_id,	/* TP4's connection ID			*/
	TP4_ADDR *rem_tp4_addr,
	ST_INT data_len,
	char *data)
  {
  ACSE_CONN * acse_conn;
  /* This function uses global "aarq_apdu" - Decoded Associate Req Data.*/

  tp4_reldata (data);			/* I never use this.	*/

  acse_conn = (ACSE_CONN *) M_CALLOC (MSMEM_ACSE_CONN, 1, sizeof (ACSE_CONN));

  /* Save TP4's connection ID. Use it in calls to TP4 functions.	*/
  acse_conn->tp4_conn_id = tp4_conn_id;
  /* Save user_bind_id for later passing to u_a_associate_ind		*/
  acse_conn->user_bind_id = user_bind_id;

  tp4_accept (tp4_conn_id, (ST_LONG) acse_conn, 15, "SISCO Transport");
  return;
  }    

/************************************************************************/
/* tp4_connect_cnf							*/
/* This function is called by the Transport provider when a POSITIVE	*/
/* T-CONNECT confirm is received.					*/
/* Now send the AARQ APDU (save_buf) that was built by a_associate_req.	*/
/************************************************************************/
ST_VOID tp4_connect_cnf (
	ST_LONG acse_conn_id,
	ST_LONG tp4_conn_id,	/* TP4's connection ID			*/
	ST_INT data_len,
	char *data)
  {
  ACSE_CONN * acse_conn;
  /* This function uses global "aare_apdu" - Decoded Associate Rsp Data.	*/

  tp4_reldata (data);			/* I never use this.	*/

  acse_conn = (ACSE_CONN *) acse_conn_id;

  if (acse_conn->state == STATE_AARE_ABANDON)
    {
    /* User already aborted. Couldn't wait for cnf (impatient!!)	*/
    tp4_disconnect (tp4_conn_id, 0, NULL);
    acse_free_con (acse_conn);
    return;
    }

  if (acse_conn->state != STATE_TP4CONN_WAIT)
    {
    u_a_except (acse_conn->user_conn_id, EX_ACSE_INVALID_STATE,
                thisFileName, __LINE__);
    acse_free_con (acse_conn);	/* Dump conn tracking struct		*/
    return;
    }
    
  /* Save TP4's connection ID. Use it in calls to TP4 functions.	*/
  acse_conn->tp4_conn_id = tp4_conn_id;

  /* Send saved AARQ APDU (save_buf) via Transport Data request.	*/
  if (tp4_data (acse_conn->tp4_conn_id, 1, (ST_UINT) acse_conn->save_buf.len,
      (ST_CHAR *) acse_conn->save_buf.ptr) != SD_SUCCESS)
    {		/* This should never happen.	*/
    ACSELOG_ERR0 ("ACSE-ERROR: AARQ APDU could not be sent. Unrecoverable");
    }
  /* Free encoded AARQ buffer now. Reset len so know not to free later.	*/
  M_FREE (MSMEM_ACSE_DATA, acse_conn->save_buf.ptr);
  acse_conn->save_buf.len = 0;	/* This indicates "ptr" has been freed.	*/

  acse_conn->state = STATE_AARE_WAIT;	/* Waiting for AARE-apdu	*/
  }    

/************************************************************************/
/* tp4_disconnect_ind							*/
/************************************************************************/
ST_VOID tp4_disconnect_ind (
	ST_LONG acse_conn_id,
	ST_INT reason,
	ST_INT data_len,
	char *data)
  {
  ACSE_CONN * acse_conn;

  tp4_reldata (data);			/* I never use this.	*/

  acse_conn = (ACSE_CONN *) acse_conn_id;

  /* Only pass up abort to user if not already in idle state.	*/
  if (acse_conn->state != STATE_IDLE)
    {
    if (reason == 0)
      {
      ACSELOG_DEC2 ("A-ABORT.ind:      acse_conn_id =0x%08X  user_conn_id =%4ld",
        acse_conn, acse_conn->user_conn_id);
      abrt_apdu.source = ACSE_SERVICE_USER;
      }
    else	
      {
      ACSELOG_DEC2 ("A-P-ABORT.ind:    acse_conn_id =0x%08X  user_conn_id =%4ld",
        acse_conn, acse_conn->user_conn_id);
      abrt_apdu.source = ACSE_SERVICE_PROVIDER;
      }

    abrt_apdu.user_info.len = 0;
    u_a_abort_ind (acse_conn->user_conn_id, &abrt_apdu);
    }
  
  /* In any case, we're done with this connection.			*/
  acse_free_con (acse_conn);	/* Dump conn tracking struct	*/
  return;
  }    

/************************************************************************/
/* tp4_data_ind								*/
/* This function is called by the Transport provider when a data packet	*/
/* is received on a previously established connection.			*/
/* It could be one of the following PDUs:				*/
/*	A-RELEASE.ind							*/
/*	A-RELEASE.cnf							*/
/* DEBUG: MUST call tp4_reldata before returning from this function.	*/
/************************************************************************/
ST_VOID tp4_data_ind (
	ST_LONG acse_conn_id,
	ST_INT eot,
	ST_UINT data_len,
	char *data)
  {
  /* This function uses globals "rlrq_apdu", "rlre_apdu", etc.		*/
  ACSE_CONN * acse_conn;
  ST_BOOLEAN free_flag = SD_FALSE;	/* Flag: does acse_conn need to be freed?*/
  ST_UCHAR *rcv_data;		/* Local copy of data ptr.		*/
  ST_UINT rcv_data_len;		/* Local copy of data len.		*/
  ASN1_DEC_CTXT aLocalCtx;
  ASN1_DEC_CTXT *aCtx = &aLocalCtx;

  acse_conn = (ACSE_CONN *) acse_conn_id;

  if (acse_conn->state == STATE_AARE_ABANDON)
    {
    /* User already aborted. Couldn't wait for cnf (impatient!!)	*/
    tp4_disconnect (acse_conn->tp4_conn_id, 0, NULL);
    acse_free_con (acse_conn);
    return;
    }

  if (eot == 0)
    {	/* NOT complete PDU, so just save data and return.	*/
    if (acse_conn->rcv_data_len == 0)
      acse_conn->rcv_data = (ST_UCHAR *) M_CALLOC (MSMEM_ACSE_DATA, 1, acse_msgsize);
    if (acse_conn->rcv_data_len + data_len > acse_msgsize)
      {
      u_a_except (acse_conn->user_conn_id, EX_ACSE_DECODE, 
                  thisFileName, __LINE__);
      acse_free_con (acse_conn);	/* Dump conn tracking struct		*/
      tp4_reldata (data);
      return;
      }
    memcpy (acse_conn->rcv_data + acse_conn->rcv_data_len, data, data_len);
    acse_conn->rcv_data_len += data_len;
    tp4_reldata (data);
    return;
    }
  else
    {	/* eot != 0	*/
    /* Complete PDU or Last piece of assembled PDU.	*/
    if (acse_conn->rcv_data_len == 0)
      {				/* This is complete PDU		*/
      /* DO NOT ALLOCATE a rcv buffer & DO NOT COPY DATA.	*/
      /* Just set "acse_conn->rcv_data" to point to data.		*/
      /* At end of function, if (acse_conn->rcv_data_len == data_len),	*/
      /* DO NOT FREE "acse_conn->rcv_data".				*/
      acse_conn->rcv_data = (ST_UCHAR *) data;
      }
    else
      {				/* This is last piece of assembled PDU	*/
      memcpy (acse_conn->rcv_data + acse_conn->rcv_data_len, data, data_len);
      }
    acse_conn->rcv_data_len += data_len;	
    }

  /* Save ptr in local variable so it can be freed later.		*/
  rcv_data = acse_conn->rcv_data;

  /* Save len in local variable and reset it so ready for next indication.*/
  rcv_data_len = acse_conn->rcv_data_len;
  acse_conn->rcv_data_len = 0;
  /* WARNING: acse_conn->rcv_data_len must not be used by any functions	*/
  /*          called after this point because it's already been reset.	*/

  /******* HAVE A COMPLETE PDU NOW.  HANDLE ALL POSSIBLE PDUs.	*********/

  if (acse_conn->state == STATE_IDLE)
    {	/* Handle A-Associate indication.				*/
    free_flag = tp4_data_ind_aarqind (acse_conn, rcv_data_len);
    }
  else if (acse_conn->state == STATE_AARE_WAIT)
    {	/* Handle A-Associate confirm.					*/
    free_flag = tp4_data_ind_aarqcnf (acse_conn, rcv_data_len);
    }
  else if ((acse_conn->rcv_data[0] & 0xC0) != APP)	/* chk ASN.1 class*/
    {	/* Handle NON-ACSE PDU.						*/
    /* Pass up to user (i.e. MMS).	*/
    ACSELOG_DEC2 ("A-DATA.ind:       acse_conn_id =0x%08X  user_conn_id =%4ld",
      acse_conn, acse_conn->user_conn_id);
    ACSELOG_DEC1C ("User data length: %d", rcv_data_len);
    u_a_data_ind (acse_conn->user_conn_id, rcv_data_len, acse_conn->rcv_data);
    }
  else if (acse_decode (aCtx, acse_conn->rcv_data, rcv_data_len) == SD_SUCCESS)
    {	/* Handle all other ACSE PDUs.	*/
    switch (acse_tag)		/* acse_tag set by acse_decode above.	*/
      {
      case TAG_RLRQ:
      switch (acse_conn->state)
        {
        case STATE_ASSOCIATED:
        acse_conn->state = STATE_ARLSRSP_WAIT;
        ACSELOG_DEC2 ("A-RELEASE.ind:    acse_conn_id =0x%08X  user_conn_id =%4ld",
          acse_conn, acse_conn->user_conn_id);
        u_a_release_ind (acse_conn->user_conn_id, &rlrq_apdu);
        break;

        case STATE_RLRE_WAIT:
        if (acse_conn->initiator)
          acse_conn->state = STATE_INIT_COLL;
        else
          acse_conn->state = STATE_RESP_COLL;
        u_a_release_ind (acse_conn->user_conn_id, &rlrq_apdu);
        break;

        default:
        u_a_except (acse_conn->user_conn_id, EX_ACSE_INVALID_STATE, 
                thisFileName, __LINE__);
        break;
        }	/* end inner "switch"	*/
      break;

      case TAG_RLRE:
      switch (acse_conn->state)
        {
        case STATE_RLRE_WAIT:
        acse_conn->state = STATE_IDLE;
        ACSELOG_DEC2 ("A-RELEASE.cnf:    acse_conn_id =0x%08X  user_conn_id =%4ld",
          acse_conn, acse_conn->user_conn_id);
        u_a_release_cnf (acse_conn->user_conn_id, &rlre_apdu);
        tp4_disconnect (acse_conn->tp4_conn_id, 0, NULL);
        free_flag = SD_TRUE;	/* conn tracking struct must be freed	*/
        break;

        case STATE_RESP_COLL:
        acse_conn->state = STATE_ARLSRSP_WAIT;
        ACSELOG_DEC2 ("A-RELEASE.cnf:    acse_conn_id =0x%08X  user_conn_id =%4ld",
          acse_conn, acse_conn->user_conn_id);
        u_a_release_cnf (acse_conn->user_conn_id, &rlre_apdu);
        break;

        default:
        u_a_except (acse_conn->user_conn_id, EX_ACSE_INVALID_STATE, 
                 thisFileName, __LINE__);
        break;
        }	/* end inner "switch"	*/
      break;

      default:
      /* Illegal from this ACSE state.					*/
      u_a_except (acse_conn->user_conn_id, EX_ACSE_INVALID_STATE, 
               thisFileName, __LINE__);
      break;
      }	/* end outer "switch"	*/
    }	/* end big "else"	*/

  if (rcv_data_len != data_len)
    M_FREE (MSMEM_ACSE_DATA, rcv_data);
  if (free_flag)		/* Does acse_conn need to be freed ??	*/
    acse_free_con (acse_conn);
  tp4_reldata (data);
  return;
  }  

/************************************************************************/
/* tp4_data_ind_aarqind							*/
/* Subfunction for "tp4_data_ind" to handle AARQ.ind			*/
/* RETURNS:	SD_TRUE (means acse_conn needs to be freed)		*/
/*		SD_FALSE (means acse_conn does NOT need to be freed)	*/
/************************************************************************/
static ST_BOOLEAN tp4_data_ind_aarqind (ACSE_CONN *acse_conn,
	ST_UINT rcv_data_len)
  {
ST_UCHAR *msg_ptr;
ST_UINT len;
ASN1_DEC_CTXT aLocalCtx;
ASN1_DEC_CTXT *aCtx = &aLocalCtx;

  /* Strip off Fastbyte Session and Presentation bytes.			*/
#if !defined(REDUCED_STACK)
  if ((ST_UCHAR) acse_conn->rcv_data [0] == 0xE8  &&
      acse_conn->rcv_data [1] == 0x01)
    {
    msg_ptr = &acse_conn->rcv_data [2];
    len = rcv_data_len-2;
#else
    {
    msg_ptr = acse_conn->rcv_data;
    len = rcv_data_len;
#endif
    /* Decode the AARQ APDU and fill in the AARQ_APDU structure.	*/
    if (acse_decode (aCtx, msg_ptr, len) == SD_SUCCESS)
      {
      if (acse_tag == TAG_AARQ)
        {
        acse_conn->state = STATE_AASCRSP_WAIT;
        ACSELOG_DEC1 ("A-ASSOCIATE.ind:  acse_conn_id =0x%08X", acse_conn);
        u_a_associate_ind (acse_conn->user_bind_id, (ST_LONG) acse_conn, &aarq_apdu);
        return (SD_FALSE);
        }
      }
    }

  ACSELOG_ERR0 ("ACSE-ERROR: AARQ decode failed");
  tp4_disconnect (acse_conn->tp4_conn_id, 0, NULL);
  return (SD_TRUE);
  }

/************************************************************************/
/* tp4_data_ind_aarqcnf							*/
/* Subfunction for "tp4_data_ind" to handle AARQ.cnf			*/
/* RETURNS:	SD_TRUE (means acse_conn needs to be freed)		*/
/*		SD_FALSE (means acse_conn does NOT need to be freed)	*/
/************************************************************************/
static ST_BOOLEAN tp4_data_ind_aarqcnf (ACSE_CONN *acse_conn,
	ST_UINT rcv_data_len)
  {
ST_UCHAR *msg_ptr;
ST_UINT len;
ASN1_DEC_CTXT aLocalCtx;
ASN1_DEC_CTXT *aCtx = &aLocalCtx;

  /* Strip off Fastbyte Session byte.	*/
#if !defined(REDUCED_STACK)
  if ((ST_UCHAR) acse_conn->rcv_data [0] == 0xF0  ||	/* Short-SAC	*/
      (ST_UCHAR) acse_conn->rcv_data [0] == 0xE3  ||	/* Short-SRF, permanent	*/
      (ST_UCHAR) acse_conn->rcv_data [0] == 0xE2)	/* Short-SRF, transient	*/
    {
    /* Strip off Fastbyte Presentation byte.	*/
    if (acse_conn->rcv_data [1] == 0x01)	/* Short-CP	*/
      {
      msg_ptr = &acse_conn->rcv_data [2];
      len = rcv_data_len-2;
#else
    {
      {
      msg_ptr = acse_conn->rcv_data;
      len = rcv_data_len;
#endif
      /* Decode the AARE APDU and fill in the AARE_APDU structure.	*/
      if (acse_decode (aCtx, msg_ptr, len) == SD_SUCCESS)
        {
        if (acse_tag == TAG_AARE)
          {
          ACSELOG_DEC2 ("A-ASSOCIATE.cnf:  acse_conn_id =0x%08X  user_conn_id =%4ld",
            acse_conn, acse_conn->user_conn_id);
          u_a_associate_cnf ((ST_LONG) acse_conn, (ST_LONG) acse_conn->user_conn_id,
             &aare_apdu);
          if (aare_apdu.result == 0)
            {
            acse_conn->initiator = SD_TRUE;
            acse_conn->state = STATE_ASSOCIATED;
            return (SD_FALSE);
            }
          else
            {
            tp4_disconnect (acse_conn->tp4_conn_id, 0, NULL);
            return (SD_TRUE);	/* Only so don't call u_a_except.	*/
            }
          }
        }
      }
    }

  ACSELOG_ERR0 ("ACSE-ERROR: AARE decode failed");
  tp4_disconnect (acse_conn->tp4_conn_id, 0, NULL);
  /* Pass up a Provider Abort Ind.	*/
  abrt_apdu.source = ACSE_SERVICE_PROVIDER;
  abrt_apdu.user_info.len = 0;
  ACSELOG_DEC2 ("A-ABORT.ind:      acse_conn_id =0x%08X  user_conn_id =%4ld",
    acse_conn, acse_conn->user_conn_id);
  u_a_abort_ind (acse_conn->user_conn_id, &abrt_apdu);
  return (SD_TRUE);
  }

/************************************************************************/
/* tp4_expdata_ind								*/
/************************************************************************/
ST_VOID tp4_expdata_ind (
	ST_LONG acse_conn_id,
	ST_UINT data_len,
	char *data)
  {
  /* Treat like normal data.  Assume "eot" = 1.		*/
  tp4_data_ind (acse_conn_id, 1, data_len, data);
  }

/************************************************************************/
/*			acse_decode					*/
/* Function to setup and initiate MMS PDU decode			*/
/* Only fourteen context specific codes are legal at start		*/
/* Input parameters : pointer to message start, length of message,	*/
/* pointer to result buffer.						*/
/************************************************************************/

static ST_RET acse_decode (ASN1_DEC_CTXT *aCtx, ST_UCHAR *msg_ptr, ST_UINT len)
  {
  aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. 	*/
  ASN1R_TAG_ADD (aCtx, APP|CONSTR,TAG_AARQ,decode_aarq);
  ASN1R_TAG_ADD (aCtx, APP|CONSTR,TAG_AARE,decode_aare);
  ASN1R_TAG_ADD (aCtx, APP|CONSTR,TAG_RLRQ,decode_rlrq);
  ASN1R_TAG_ADD (aCtx, APP|CONSTR,TAG_RLRE,decode_rlre);
  ASN1R_TAG_ADD (aCtx, APP|CONSTR,TAG_ABRT,decode_abrt);

  aCtx->asn1r_decode_done_fun = asn1r_done_err; /* done function = error fun 	*/
  aCtx->asn1r_err_fun = NULL;		   /* set up error detected fun 	*/

/* Call 'asn1r_decode_asn1 (aCtx,' to parse the message.                             */

  asn1r_decode_asn1 (aCtx, msg_ptr,len);		/* do message decode		*/
  if (aCtx->asn1r_pdu_dec_err == NO_DECODE_ERR)
    return (SD_SUCCESS);
  ACSELOG_ERR0 ("ACSE-ERROR: decode of APDU failed");
  return (SD_FAILURE);
  }

#endif	/* !MOSI	*/

/************************************************************************/
/* decode_aarq								*/
/************************************************************************/
static ST_VOID decode_aarq (ASN1_DEC_CTXT *aCtx)
  {
  aCtx->asn1r_decode_method = ASN1_CLASS_METHOD;	/* Use ASN1_CLASS_METHOD method to decode.		*/
  aCtx->asn1r_c_id_fun = decode_aarq_main;
  aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_aarq_done;
  aCtx->asn1r_u_id_fun = asn1r_class_err; 	/* no universals to start		*/
  aCtx->asn1r_a_id_fun = asn1r_class_err; 	/* no applications to start		*/
  aCtx->asn1r_p_id_fun = asn1r_class_err; 	/* no privates to start 		*/

  /* Clean out old stuff from structure.				*/
  memset (&aarq_apdu, 0, sizeof (aarq_apdu));
  }

/************************************************************************/
/* decode_aarq_main								*/
/************************************************************************/
static ST_VOID decode_aarq_main (ASN1_DEC_CTXT *aCtx, ST_UINT16 id_code)
  {
ST_RET ret;
ST_UCHAR bit_str;
MMS_OBJ_ID mechanism_name;
MMS_OBJ_ID maceMechId = MACE_MECH_ID;

  switch (id_code)
    {
    case AARQ_protocol_version:
      if (aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* constructor not allowed	*/
        return;
        }
      /* CRITICAL: asn1r_get_bitstr doesn't set unused bits, so init all bits=0*/
      bit_str = 0;
      asn1r_get_bitstr (aCtx, &bit_str, 8);
      if ((bit_str & BIT_VERSION1) == 0)		/* NOT Version1	*/
        {
        asn1r_set_dec_err (aCtx, ASN1E_INVALID_BITSTR);	/* Illegal ACSE version		*/
        return;
        }
      break;

    case AARQ_ASO_context_name:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      aarq_apdu.ASO_context_name_pres = SD_TRUE;
      my_obj_id = &aarq_apdu.ASO_context_name;
      ASN1R_TAG_ADD (aCtx, UNI,OBJ_ID_CODE,decode_obj_id);
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_constr_done;
      break;

    case AARQ_CALLED_AP_title:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      aarq_apdu.called_ae_title.AP_title_pres = SD_TRUE;
      my_obj_id = &aarq_apdu.called_ae_title.AP_title;
      ASN1R_TAG_ADD (aCtx, UNI,OBJ_ID_CODE,decode_obj_id);
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_constr_done;
      break;

    case AARQ_CALLED_AE_qual:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      aarq_apdu.called_ae_title.AE_qual_pres = SD_TRUE;
      my_i32 = &aarq_apdu.called_ae_title.AE_qual;
      ASN1R_TAG_ADD (aCtx, UNI,INT_CODE,decode_i32);
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_constr_done;
      break;

    case AARQ_CALLED_AP_inv_id:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      aarq_apdu.called_ae_title.AP_inv_id_pres = SD_TRUE;
      my_i32 = &aarq_apdu.called_ae_title.AP_inv_id;
      ASN1R_TAG_ADD (aCtx, UNI,INT_CODE,decode_i32);
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_constr_done;
      break;

    case AARQ_CALLED_AE_inv_id:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      aarq_apdu.called_ae_title.AE_inv_id_pres = SD_TRUE;
      my_i32 = &aarq_apdu.called_ae_title.AE_inv_id;
      ASN1R_TAG_ADD (aCtx, UNI,INT_CODE,decode_i32);
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_constr_done;
      break;

    case AARQ_CALLING_AP_title:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      aarq_apdu.calling_ae_title.AP_title_pres = SD_TRUE;
      my_obj_id = &aarq_apdu.calling_ae_title.AP_title;
      ASN1R_TAG_ADD (aCtx, UNI,OBJ_ID_CODE,decode_obj_id);
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_constr_done;
      break;

    case AARQ_CALLING_AE_qual:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      aarq_apdu.calling_ae_title.AE_qual_pres = SD_TRUE;
      my_i32 = &aarq_apdu.calling_ae_title.AE_qual;
      ASN1R_TAG_ADD (aCtx, UNI,INT_CODE,decode_i32);
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_constr_done;
      break;

    case AARQ_CALLING_AP_inv_id:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      aarq_apdu.calling_ae_title.AP_inv_id_pres = SD_TRUE;
      my_i32 = &aarq_apdu.calling_ae_title.AP_inv_id;
      ASN1R_TAG_ADD (aCtx, UNI,INT_CODE,decode_i32);
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_constr_done;
      break;

    case AARQ_CALLING_AE_inv_id:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      aarq_apdu.calling_ae_title.AE_inv_id_pres = SD_TRUE;
      my_i32 = &aarq_apdu.calling_ae_title.AE_inv_id;
      ASN1R_TAG_ADD (aCtx, UNI,INT_CODE,decode_i32);
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_constr_done;
      break;

    case AARQ_acse_requirements:
      if (aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must NOT be constr*/
        return;
        }
      /* CRITICAL: asn1r_get_bitstr doesn't set unused bits, so init all bits=0*/
      bit_str = 0;
      asn1r_get_bitstr (aCtx, &bit_str, 8);
      /* DEBUG: some stacks incorrectly set the low bit (0x01) in the	*/
      /* bitstring. To be nice, we will let them get away with it.	*/
      if ((bit_str & BIT_AUTHENTICATION) || (bit_str & 0x01))
        aarq_apdu.auth_info.auth_pres = SD_TRUE;
      /* Don't support AP context negotiation, so ignore Bit 1.		*/
      /* We will never set Bit 1 in the response.			*/
      break;

      
    case AARQ_mechanism_name:
      if (aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must NOT be constr*/
        return;
        }
      if (asn1r_get_objid (aCtx, mechanism_name.comps,
                     &mechanism_name.num_comps))
        {
        asn1r_set_dec_err (aCtx, ASN1E_INVALID_BOOLEAN); 
        return;                          
        }
      
      /* Save the mechanism id for the user */
      if (asn1_objidcmp (&mechanism_name, &a_auth_password_obj_id) == 0)
        aarq_apdu.auth_info.mech_type = ACSE_AUTH_MECH_PASSWORD;
      else  
        {
        if (asn1_objidcmp (&mechanism_name, &maceMechId) == 0)
          aarq_apdu.auth_info.mech_type = ACSE_AUTH_MECH_MACE_CERT;
	else
          aarq_apdu.auth_info.mech_type = ACSE_AUTH_MECH_OTHER;
      	memcpy(&aarq_apdu.auth_info.u.other_auth.mech_id, &mechanism_name, sizeof(MMS_OBJ_ID));
      	}
      
      break;

    case AARQ_CALLING_auth_value:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
      
      if (aarq_apdu.auth_info.mech_type == ACSE_AUTH_MECH_OTHER ||
          aarq_apdu.auth_info.mech_type == ACSE_AUTH_MECH_MACE_CERT)
	{
        ret = _a_dec_authentication (&aarq_apdu.auth_info,	/* out	*/
                                     aCtx->asn1r_field_ptr,	/* in	*/
                                     aCtx->asn1r_elmnt_len);	/* in	*/
        if (ret != SD_SUCCESS)
          return;
	}
      else
        strncpy(aarq_apdu.auth_info.u.pw_auth.password, 
        	a_auth_password_dec(aCtx->asn1r_field_ptr), ACSE_MAX_LEN_PASSWORD);
      
      asn1_skip_elmnt (aCtx);	/* Skip over this ASN.1 element.	*/
      break;

    case AARQ_user_info:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
#if defined (MOSI)
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      /* Set the PCI to be expected in PDV-list decode.			*/
      expected_pdv_pci = decode_acse_conn->pcimms;
      ASN1R_TAG_ADD (aCtx, UNI|CONSTR,EXTERN_CODE,START_pdv_list);	/* see copp_dec.c*/
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = END_user_info;
#else	/* !MOSI	*/
      /* This is MMS user data. Just pass up for MMS user to decode.	*/
      aarq_apdu.user_info.ptr = aCtx->asn1r_field_ptr;
      aarq_apdu.user_info.len = aCtx->asn1r_elmnt_len;
      asn1_skip_elmnt (aCtx);	/* Skip over this ASN.1 element.	*/
#endif	/* !MOSI	*/
      break;
      
    default:				/* Don't know how to decode.	*/
      ACSELOG_DEC1 ("AARQ unrecognized tag %d. Ignored.", id_code);
      asn1_skip_elmnt (aCtx);	/* Skip over this ASN.1 element.	*/
      break;
    }	/* End "switch (id_code)"	*/
  }

/************************************************************************/
/* decode_aarq_done							*/
/************************************************************************/
static ST_VOID decode_aarq_done (ASN1_DEC_CTXT *aCtx)
  {
#if defined (MOSI)
  if (expected_pdv_pci == decode_acse_conn->pcimms)
    {		/* only set these if this is MMS (not ACSE) data	*/
    aarq_apdu.user_info.ptr = pdv_data_ptr;
    aarq_apdu.user_info.len = pdv_data_len;
    }
#else	/* !MOSI	*/
  acse_tag = TAG_AARQ;
#endif	/* !MOSI	*/
  aCtx->asn1r_decode_done_fun = decode_acse_done;
  }

/************************************************************************/
/* decode_aare								*/
/************************************************************************/
static ST_VOID decode_aare (ASN1_DEC_CTXT *aCtx)
  {
  aCtx->asn1r_decode_method = ASN1_CLASS_METHOD;	/* Use ASN1_CLASS_METHOD method to decode.		*/
  aCtx->asn1r_c_id_fun = decode_aare_main;
  aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_aare_done;
  aCtx->asn1r_u_id_fun = asn1r_class_err; 	/* no universals to start		*/
  aCtx->asn1r_a_id_fun = asn1r_class_err; 	/* no applications to start		*/
  aCtx->asn1r_p_id_fun = asn1r_class_err; 	/* no privates to start 		*/

  /* Clean out old stuff from structure.				*/
  memset (&aare_apdu, 0, sizeof (aare_apdu));
  }

/************************************************************************/
/* decode_aare_main								*/
/************************************************************************/
static ST_VOID decode_aare_main (ASN1_DEC_CTXT *aCtx, ST_UINT16 id_code)
  {
ST_RET ret;
ST_UCHAR bit_str;
MMS_OBJ_ID mechanism_name;
MMS_OBJ_ID maceMechId = MACE_MECH_ID;

  switch (id_code)
    {
    case AARE_protocol_version:
      if (aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* constructor not allowed	*/
        return;
        }
      /* CRITICAL: asn1r_get_bitstr doesn't set unused bits, so init all bits=0*/
      bit_str = 0;
      asn1r_get_bitstr (aCtx, &bit_str, 8);
      if ((bit_str & BIT_VERSION1)==0)		/* NOT Version1	*/
        {
        asn1r_set_dec_err (aCtx, ASN1E_INVALID_BITSTR);	/* Illegal ACSE version		*/
        return;
        }
      break;

    case AARE_ASO_context_name:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      aare_apdu.ASO_context_name_pres = SD_TRUE;
      my_obj_id = &aare_apdu.ASO_context_name;
      ASN1R_TAG_ADD (aCtx, UNI,OBJ_ID_CODE,decode_obj_id);
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_constr_done;
      break;

    case AARE_result:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      my_i16 = &aare_apdu.result;
      ASN1R_TAG_ADD (aCtx, UNI,INT_CODE,decode_i16);
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_constr_done;
      break;

    case AARE_result_source_diagnostic:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      aare_apdu.result_source_diag_pres = SD_TRUE;
      ASN1R_TAG_ADD (aCtx, CTX|CONSTR,1,decode_result_source1);
      ASN1R_TAG_ADD (aCtx, CTX|CONSTR,2,decode_result_source2);
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_constr_done;
      break;

    case AARE_RESPONDING_AP_title:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      aare_apdu.responding_ae_title.AP_title_pres = SD_TRUE;
      my_obj_id = &aare_apdu.responding_ae_title.AP_title;
      ASN1R_TAG_ADD (aCtx, UNI,OBJ_ID_CODE,decode_obj_id);
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_constr_done;
      break;

    case AARE_RESPONDING_AE_qual:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      aare_apdu.responding_ae_title.AE_qual_pres = SD_TRUE;
      my_i32 = &aare_apdu.responding_ae_title.AE_qual;
      ASN1R_TAG_ADD (aCtx, UNI,INT_CODE,decode_i32);
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_constr_done;
      break;

    case AARE_RESPONDING_AP_inv_id:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      aare_apdu.responding_ae_title.AP_inv_id_pres = SD_TRUE;
      my_i32 = &aare_apdu.responding_ae_title.AP_inv_id;
      ASN1R_TAG_ADD (aCtx, UNI,INT_CODE,decode_i32);
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_constr_done;
      break;

    case AARE_RESPONDING_AE_inv_id:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      aare_apdu.responding_ae_title.AE_inv_id_pres = SD_TRUE;
      my_i32 = &aare_apdu.responding_ae_title.AE_inv_id;
      ASN1R_TAG_ADD (aCtx, UNI,INT_CODE,decode_i32);
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_constr_done;
      break;

    
    case AARE_RESPONDER_acse_requirements:
      if (aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must NOT be constr*/
        return;
        }
      /* CRITICAL: asn1r_get_bitstr doesn't set unused bits, so init all bits=0*/
      bit_str = 0;
      asn1r_get_bitstr (aCtx, &bit_str, 8);
      /* DEBUG: some stacks incorrectly set the low bit (0x01) in the	*/
      /* bitstring. To be nice, we will let them get away with it.	*/
      if ((bit_str & BIT_AUTHENTICATION) || (bit_str & 0x01))
        aare_apdu.auth_info.auth_pres = SD_TRUE;
      /* Don't support AP context negotiation, so ignore Bit 1.		*/
      /* We will never set Bit 1 in the response.			*/
      break;
    
    case AARE_mechanism_name:
      if (aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must NOT be constr*/
        return;
        }
      /* NOTE: this is IMPLICIT objid, so decoding simpler than normal.	*/
      if (asn1r_get_objid (aCtx, mechanism_name.comps, &mechanism_name.num_comps))
        {
        asn1r_set_dec_err (aCtx, ASN1E_INVALID_BOOLEAN); 
        return;                          
        }
      
      /* Save the mechanism id for the user */
      if (asn1_objidcmp (&mechanism_name, &a_auth_password_obj_id) == 0)
        aare_apdu.auth_info.mech_type = ACSE_AUTH_MECH_PASSWORD;
      else  
        {
        if (asn1_objidcmp (&mechanism_name, &maceMechId) == 0)
          aare_apdu.auth_info.mech_type = ACSE_AUTH_MECH_MACE_CERT;
	else
          aare_apdu.auth_info.mech_type = ACSE_AUTH_MECH_OTHER;
      	memcpy(&aare_apdu.auth_info.u.other_auth.mech_id, &mechanism_name, sizeof(MMS_OBJ_ID));
      	}
      
      break;

    case AARE_RESPONDING_auth_value:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
      
      if (aare_apdu.auth_info.mech_type == ACSE_AUTH_MECH_OTHER ||
          aare_apdu.auth_info.mech_type == ACSE_AUTH_MECH_MACE_CERT)
	{
        ret = _a_dec_authentication (&aare_apdu.auth_info,	/* out	*/
                                     aCtx->asn1r_field_ptr,	/* in	*/
                                     aCtx->asn1r_elmnt_len);	/* in	*/
        if (ret != SD_SUCCESS)
          return;
	}
      else
        strncpy(aare_apdu.auth_info.u.pw_auth.password, 
               a_auth_password_dec(aCtx->asn1r_field_ptr), ACSE_MAX_LEN_PASSWORD);
      
      asn1_skip_elmnt (aCtx);	/* Skip over this ASN.1 element.	*/
      break;

    case AARE_user_info:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
#if defined (MOSI)
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      /* Set the PCI to be expected in PDV-list decode.			*/
      expected_pdv_pci = decode_acse_conn->pcimms;
      ASN1R_TAG_ADD (aCtx, UNI|CONSTR,EXTERN_CODE,START_pdv_list);	/* see copp_dec.c*/
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = END_user_info;
#else	/* !MOSI	*/
      /* This is MMS user data. Just pass up for MMS user to decode.	*/
      aare_apdu.user_info.ptr = aCtx->asn1r_field_ptr;
      aare_apdu.user_info.len = aCtx->asn1r_elmnt_len;
      asn1_skip_elmnt (aCtx);	/* Skip over this ASN.1 element.	*/
#endif	/* !MOSI	*/
      break;
      
    default:				/* Don't know how to decode.	*/
      ACSELOG_DEC1 ("AARE unrecognized tag %d. Ignored.", id_code);
      asn1_skip_elmnt (aCtx);	/* Skip over this ASN.1 element.	*/
      break;
    }	/* End "switch (id_code)"	*/
  }

/************************************************************************/
/* decode_aare_done							*/
/************************************************************************/
static ST_VOID decode_aare_done (ASN1_DEC_CTXT *aCtx)
  {
#if defined (MOSI)
  if (expected_pdv_pci == decode_acse_conn->pcimms)
    {		/* only set these if this is MMS (not ACSE) data	*/
    aare_apdu.user_info.ptr = pdv_data_ptr;
    aare_apdu.user_info.len = pdv_data_len;
    }
#else	/* !MOSI	*/
  acse_tag = TAG_AARE;
#endif	/* !MOSI	*/
  aCtx->asn1r_decode_done_fun = decode_acse_done;
  }

/************************************************************************/
/* decode_rlrq								*/
/************************************************************************/
static ST_VOID decode_rlrq (ASN1_DEC_CTXT *aCtx)
  {
  aCtx->asn1r_decode_method = ASN1_CLASS_METHOD;	/* Use ASN1_CLASS_METHOD method to decode.		*/
  aCtx->asn1r_c_id_fun = decode_rlrq_main;
  aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_rlrq_done;
  aCtx->asn1r_u_id_fun = asn1r_class_err; 	/* no universals to start		*/
  aCtx->asn1r_a_id_fun = asn1r_class_err; 	/* no applications to start		*/
  aCtx->asn1r_p_id_fun = asn1r_class_err; 	/* no privates to start 		*/

  /* Clean out old stuff from structure.				*/
  memset (&rlrq_apdu, 0, sizeof (rlrq_apdu));
  }

/************************************************************************/
/* decode_rlrq_main								*/
/************************************************************************/
static ST_VOID decode_rlrq_main (ASN1_DEC_CTXT *aCtx, ST_UINT16 id_code)
  {
  switch (id_code)
    {
    case RLRQ_reason:
      if (aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be primitive	*/
        return;
        }
      rlrq_apdu.reason_pres = SD_TRUE;
      if (asn1r_get_i16 (aCtx, &rlrq_apdu.reason))
        {
        asn1r_set_dec_err (aCtx, ASN1E_INVALID_BOOLEAN); 
        return;                          
        }
      break;

    case RLRQ_user_info:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
#if defined (MOSI)
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      /* Set the PCI to be expected in PDV-list decode.			*/
      expected_pdv_pci = decode_acse_conn->pcimms;
      ASN1R_TAG_ADD (aCtx, UNI|CONSTR,EXTERN_CODE,START_pdv_list);	/* see copp_dec.c*/
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = END_user_info;
#else	/* !MOSI	*/
      /* This is MMS user data. Just pass up for MMS user to decode.	*/
      rlrq_apdu.user_info.ptr = aCtx->asn1r_field_ptr;
      rlrq_apdu.user_info.len = aCtx->asn1r_elmnt_len;
      asn1_skip_elmnt (aCtx);	/* Skip over this ASN.1 element.	*/
#endif	/* !MOSI	*/
      break;
      
    default:				/* Don't know how to decode.	*/
      ACSELOG_DEC1 ("RLRQ unrecognized tag %d. Ignored.", id_code);
      asn1_skip_elmnt (aCtx);	/* Skip over this ASN.1 element.	*/
      break;
    }	/* End "switch (id_code)"	*/
  }

/************************************************************************/
/* decode_rlrq_done							*/
/************************************************************************/
static ST_VOID decode_rlrq_done (ASN1_DEC_CTXT *aCtx)
  {
#if defined (MOSI)
  if (expected_pdv_pci == decode_acse_conn->pcimms)
    {		/* only set these if this is MMS (not ACSE) data	*/
    rlrq_apdu.user_info.ptr = pdv_data_ptr;
    rlrq_apdu.user_info.len = pdv_data_len;
    }
#else	/* !MOSI	*/
  acse_tag = TAG_RLRQ;
#endif	/* !MOSI	*/
  aCtx->asn1r_decode_done_fun = decode_acse_done;
  }

/************************************************************************/
/* decode_rlre								*/
/************************************************************************/
static ST_VOID decode_rlre (ASN1_DEC_CTXT *aCtx)
  {
  aCtx->asn1r_decode_method = ASN1_CLASS_METHOD;	/* Use ASN1_CLASS_METHOD method to decode.		*/
  aCtx->asn1r_c_id_fun = decode_rlre_main;
  aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_rlre_done;
  aCtx->asn1r_u_id_fun = asn1r_class_err; 	/* no universals to start		*/
  aCtx->asn1r_a_id_fun = asn1r_class_err; 	/* no applications to start		*/
  aCtx->asn1r_p_id_fun = asn1r_class_err; 	/* no privates to start 		*/

  /* Clean out old stuff from structure.				*/
  memset (&rlre_apdu, 0, sizeof (rlre_apdu));
  }

/************************************************************************/
/* decode_rlre_main								*/
/************************************************************************/
static ST_VOID decode_rlre_main (ASN1_DEC_CTXT *aCtx, ST_UINT16 id_code)
  {
  switch (id_code)
    {
    case RLRE_reason:
      if (aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be primitive	*/
        return;
        }
      rlre_apdu.reason_pres = SD_TRUE;
      if (asn1r_get_i16 (aCtx, &rlre_apdu.reason))
        {
        asn1r_set_dec_err (aCtx, ASN1E_INVALID_BOOLEAN); 
        return;                          
        }
      break;

    case RLRE_user_info:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
#if defined (MOSI)
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      /* Set the PCI to be expected in PDV-list decode.			*/
      expected_pdv_pci = decode_acse_conn->pcimms;
      ASN1R_TAG_ADD (aCtx, UNI|CONSTR,EXTERN_CODE,START_pdv_list);	/* see copp_dec.c*/
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = END_user_info;
#else	/* !MOSI	*/
      /* This is MMS user data. Just pass up for MMS user to decode.	*/
      rlre_apdu.user_info.ptr = aCtx->asn1r_field_ptr;
      rlre_apdu.user_info.len = aCtx->asn1r_elmnt_len;
      asn1_skip_elmnt (aCtx);	/* Skip over this ASN.1 element.	*/
#endif	/* !MOSI	*/
      break;
      
    default:				/* Don't know how to decode.	*/
      ACSELOG_DEC1 ("RLRE unrecognized tag %d. Ignored.", id_code);
      asn1_skip_elmnt (aCtx);	/* Skip over this ASN.1 element.	*/
      break;
    }	/* End "switch (id_code)"	*/
  }

/************************************************************************/
/* decode_rlre_done							*/
/************************************************************************/
static ST_VOID decode_rlre_done (ASN1_DEC_CTXT *aCtx)
  {
#if defined (MOSI)
  if (expected_pdv_pci == decode_acse_conn->pcimms)
    {		/* only set these if this is MMS (not ACSE) data	*/
    rlre_apdu.user_info.ptr = pdv_data_ptr;
    rlre_apdu.user_info.len = pdv_data_len;
    }
#else	/* !MOSI	*/
  acse_tag = TAG_RLRE;
#endif	/* !MOSI	*/
  aCtx->asn1r_decode_done_fun = decode_acse_done;
  }

/************************************************************************/
/* decode_abrt								*/
/************************************************************************/
static ST_VOID decode_abrt (ASN1_DEC_CTXT *aCtx)
  {
  aCtx->asn1r_decode_method = ASN1_CLASS_METHOD;	/* Use ASN1_CLASS_METHOD method to decode.		*/
  aCtx->asn1r_c_id_fun = decode_abrt_main;
  aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_abrt_done;
  aCtx->asn1r_u_id_fun = asn1r_class_err; 	/* no universals to start		*/
  aCtx->asn1r_a_id_fun = asn1r_class_err; 	/* no applications to start		*/
  aCtx->asn1r_p_id_fun = asn1r_class_err; 	/* no privates to start 		*/

  /* Clean out old stuff from structure.				*/
  memset (&abrt_apdu, 0, sizeof (abrt_apdu));
  abrt_apdu.source = -1;	/* So can verify that gets set by decode*/
  }

/************************************************************************/
/* decode_abrt_main								*/
/************************************************************************/
static ST_VOID decode_abrt_main (ASN1_DEC_CTXT *aCtx, ST_UINT16 id_code)
  {
  switch (id_code)
    {
    case ABRT_source:
      if (aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be primitive	*/
        return;
        }
      if (asn1r_get_i16 (aCtx, &abrt_apdu.source))
        {
        asn1r_set_dec_err (aCtx, ASN1E_INVALID_BOOLEAN); 
        return;                          
        }
      break;

    case ABRT_diagnostic:
      if (aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be primitive	*/
        return;
        }
      if (asn1r_get_i16 (aCtx, &abrt_apdu.diagnostic))
        {
        asn1r_set_dec_err (aCtx, ASN1E_INVALID_BOOLEAN); 
        return;                          
        }
      else
        abrt_apdu.diagnostic_pres = SD_TRUE;
      break;

    case ABRT_user_info:
      if (!aCtx->asn1r_constr_elmnt)
        {
        asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM);	/* Must be constructor	*/
        return;
        }
#if defined (MOSI)
      aCtx->asn1r_decode_method = ASN1_TAG_METHOD;		/* Use asn1_tag method for decoding. */
      /* Set the PCI to be expected in PDV-list decode.			*/
      expected_pdv_pci = decode_acse_conn->pcimms;
      ASN1R_TAG_ADD (aCtx, UNI|CONSTR,EXTERN_CODE,START_pdv_list);	/* see copp_dec.c*/
      aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = END_user_info;
#else	/* !MOSI	*/
      /* This is MMS user data. Just pass up for MMS user to decode.	*/
      abrt_apdu.user_info.ptr = aCtx->asn1r_field_ptr;
      abrt_apdu.user_info.len = aCtx->asn1r_elmnt_len;
      asn1_skip_elmnt (aCtx);	/* Skip over this ASN.1 element.	*/
#endif	/* !MOSI	*/
      break;
      
    default:				/* Don't know how to decode.	*/
      ACSELOG_DEC1 ("ABRT unrecognized tag %d. Ignored.", id_code);
      asn1_skip_elmnt (aCtx);	/* Skip over this ASN.1 element.	*/
      break;
    }	/* End "switch (id_code)"	*/
  }

/************************************************************************/
/* decode_abrt_done							*/
/************************************************************************/
static ST_VOID decode_abrt_done (ASN1_DEC_CTXT *aCtx)
  {
  if (abrt_apdu.source == -1)
    {
    asn1r_set_dec_err (aCtx, ASN1E_END_OF_MESSAGE);	/* Did not contain MANDATORY "source".	*/
    return;
    }
#if defined (MOSI)
  if (expected_pdv_pci == decode_acse_conn->pcimms)
    {		/* only set these if this is MMS (not ACSE) data	*/
    abrt_apdu.user_info.ptr = pdv_data_ptr;
    abrt_apdu.user_info.len = pdv_data_len;
    }
#else	/* !MOSI	*/
  acse_tag = TAG_ABRT;
#endif	/* !MOSI	*/
  aCtx->asn1r_decode_done_fun = decode_acse_done;
  }

/************************************************************************/
/* decode_obj_id								*/
/************************************************************************/
static ST_VOID decode_obj_id (ASN1_DEC_CTXT *aCtx)
  {
  if (asn1r_get_objid (aCtx, my_obj_id->comps,&my_obj_id->num_comps))
    {
    asn1r_set_dec_err (aCtx, ASN1E_INVALID_BOOLEAN); 
    return;                          
    }
  /* Go back to ASN1_CLASS_METHOD mode. "aCtx->asn1r_c_id_fun" should still = "decode_????_main"*/
  aCtx->asn1r_decode_method = ASN1_CLASS_METHOD;
  return;
  }
  
/************************************************************************/
/* decode_i32								*/
/************************************************************************/
static ST_VOID decode_i32 (ASN1_DEC_CTXT *aCtx)
  {
  if (asn1r_get_i32 (aCtx, my_i32))
    {
    asn1r_set_dec_err (aCtx, ASN1E_INVALID_BOOLEAN); 
    return;                          
    }
  /* Go back to ASN1_CLASS_METHOD mode. "aCtx->asn1r_c_id_fun" should still = "decode_????_main"*/
  aCtx->asn1r_decode_method = ASN1_CLASS_METHOD;
  return;
  }
  
/************************************************************************/
/* decode_i16								*/
/************************************************************************/
static ST_VOID decode_i16 (ASN1_DEC_CTXT *aCtx)
  {
  if (asn1r_get_i16 (aCtx, my_i16))
    {
    asn1r_set_dec_err (aCtx, ASN1E_INVALID_BOOLEAN); 
    return;                          
    }
  /* Go back to ASN1_CLASS_METHOD mode. "aCtx->asn1r_c_id_fun" should still = "decode_????_main"*/
  aCtx->asn1r_decode_method = ASN1_CLASS_METHOD;
  return;
  }
  
/************************************************************************/
/* decode_result_source1						*/
/************************************************************************/
static ST_VOID decode_result_source1 (ASN1_DEC_CTXT *aCtx)
  {
  aare_apdu.result_source = 1;

  my_i16 = &aare_apdu.result_diag;
  ASN1R_TAG_ADD (aCtx, UNI,INT_CODE,decode_i16);
  aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_constr_done;
  
  return;
  }
  
/************************************************************************/
/* decode_result_source2						*/
/************************************************************************/
static ST_VOID decode_result_source2 (ASN1_DEC_CTXT *aCtx)
  {
  aare_apdu.result_source = 2;

  my_i16 = &aare_apdu.result_diag;
  ASN1R_TAG_ADD (aCtx, UNI,INT_CODE,decode_i16);
  aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = decode_constr_done;
  
  return;
  }
  
/************************************************************************/
/* decode_acse_done							*/
/************************************************************************/
static ST_VOID decode_acse_done (ASN1_DEC_CTXT *aCtx)
  {
  return;
  }

/************************************************************************/
/* decode_constr_done							*/
/************************************************************************/
static ST_VOID decode_constr_done (ASN1_DEC_CTXT *aCtx)
  {
  return;
  }

/************************************************************************/
/*			asn1_skip_elmnt					*/
/* Skip over current ASN.1 element without decoding.			*/
/************************************************************************/
ST_VOID asn1_skip_elmnt (ASN1_DEC_CTXT *aCtx)
  {
  /* This function can't handle indefinite length encoding.		*/
  /* Only possible for constr, but let's chk here to be extra paranoid.	*/
  if (aCtx->asn1r_indef_flag)
    {
    /* NOTE: if this ever happens, chg code to use asn1r_parse_next.	*/
    ACSELOG_ERR0 ("asn1_skip_elmnt: indefinite length encoding not supported");
    asn1r_set_dec_err (aCtx, ASN1E_UNEXPECTED_FORM); 
    return;                          
    }
  if (aCtx->asn1r_constr_elmnt)
    {
    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;
  }

#if defined (MOSI)
/************************************************************************/
/* END_user_info						*/
/************************************************************************/
static ST_VOID END_user_info (ASN1_DEC_CTXT *aCtx)
  {
  }
#endif	/* MOSI	*/

#ifdef LEAN_T
/************************************************************************/
/* tp4_except								*/
/************************************************************************/
ST_VOID tp4_except (
	ST_CHAR *filename,		/* Source code file name		*/
	ST_INT line)		/* source code line number		*/
  {
  /* DEBUG: need better way to deal with all exceptions.		*/
  /* How about one exception function, pass component name and errcode	*/
  ACSELOG_ERR2 ("ERROR: TP4 exception:  File = %s, Line = %d", filename, line);
  /* DEBUG: create new errcode, or SKIP IT and use only filename/line??	*/
  u_a_except (-1L, EX_ACSE_DECODE, filename, line);
  }
#endif	/* LEAN_T	*/


/************************************************************************/
/*			a_auth_password_dec				*/
/* Authentication password decode function.				*/
/* RETURN: pointer to static NULL terminated string containing password.*/
/*         NULL if decode fails.					*/
/************************************************************************/
static ST_CHAR *a_auth_password_dec (ST_UCHAR *asn1_ptr)
  {
static ST_CHAR password [ACSE_MAX_LEN_PASSWORD + 1];
ST_UINT password_len;
ST_CHAR *ret_ptr = NULL;	/* assume error	*/

  if (asn1_ptr [0] == (CTX|0))	/* Tag 0 - GraphicString	*/
    {
    password_len = asn1_ptr[1];
    if (password_len <= ACSE_MAX_LEN_PASSWORD)
      {
      memcpy (password, &asn1_ptr [2], password_len);
      password [password_len] = 0;	/* NULL terminate	*/
      ret_ptr = password;	/* only valid return value	*/
      }
    }
  return (ret_ptr);
  }

#if defined (TP4_ENABLED)
/************************************************************************/
/*			a_get_rem_nsap					*/
/* RETURN: pointer to NSAP. First byte is length.			*/
/************************************************************************/
#include "tp4.h"			/* Need internal TP4 defines	*/
ST_UCHAR *a_get_rem_nsap (ST_LONG acse_conn_id)
  {
ACSE_CONN *acse_conn;
ST_LONG tp_conn_id;
TP_CONN *tp_conn;

  acse_conn = (ACSE_CONN *) acse_conn_id;
  tp_conn_id = acse_conn->tp4_conn_id;
  if (tp_conn_id < 0  ||  tp_conn_id >= tp_cfg.max_num_conns)
    return (NULL);	/* Invalid conn_id (could be TP0 conn id	*/
			/* which starts at MIN_TP0_CONN_ID).		*/
  tp_conn = &tp_conn_arr [tp_conn_id];
  return (tp_conn->rem_nsap);
  }
#endif	/* defined (TP4_ENABLED)	*/

#if defined (TP0_ENABLED)
/************************************************************************/
/*			a_get_rem_ip_addr				*/
/* This function returns the remote IP Address as an unsigned long in	*/
/* "network byte order", just like the "standard sockets" function	*/
/* "inet_addr" does. If there is an error, it returns			*/
/* "htonl (INADDR_NONE)", just like "inet_addr" does.			*/
/*   The return value can be stored in the appropriate union member	*/
/* in the structure "in_addr", which can be passed to "inet_ntoa".	*/
/************************************************************************/
#include "tp4.h"			/* Need internal TP4 defines	*/
#include "tp0_sock.h"			/* Need "sockets" defines	*/
ST_ULONG a_get_rem_ip_addr (ST_LONG acse_conn_id)
  {
ACSE_CONN *acse_conn;
ST_LONG tp_conn_id;
TP0_CONN *tp0_conn;
ST_INT ret;
SOCKADDR_IN sockaddr_in;
SOCK_ADDRLEN addr_len;

  acse_conn = (ACSE_CONN *) acse_conn_id;
  tp_conn_id = acse_conn->tp4_conn_id;
  if (tp_conn_id >= MIN_TP0_CONN_ID  &&
      tp_conn_id < tp0_cfg.max_num_conns + MIN_TP0_CONN_ID)
    {				/* Conn id is a legal TP0 conn id	*/
    tp0_conn = &tp0_conn_arr [tp_conn_id - MIN_TP0_CONN_ID];

    addr_len = sizeof (SOCKADDR_IN);	/* CRITICAL: set to expected len.*/
    ret = getpeername (tp0_conn->sock_info->hSock, (SOCKADDR *) &sockaddr_in, &addr_len); 
    if (ret == 0)
      return (sockaddr_in.sin_addr.s_addr);
    }
   
  return (htonl (INADDR_NONE));		/* something failed	*/
  }
#endif	/* defined (TP0_ENABLED)	*/


	/************************************************************************/
	/*	NOTE: Copied from marb_rx.c, if any changes are required	*/
	/*	      consider making them also in marb_rx.c file!		*/
	/************************************************************************/

static ST_VOID _a_dec_auth_value_external (ASN1_DEC_CTXT *aCtx);
static ST_VOID _a_dec_external_singleAsn1Type (ASN1_DEC_CTXT *aCtx);
static ST_VOID _a_dec_external_singleAsn1Type_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID _a_dec_auth_value_external_done (ASN1_DEC_CTXT *aCtx);
static ST_VOID _a_dec_auth_value_graphicString (ASN1_DEC_CTXT *aCtx);
static ST_VOID _a_dec_auth_done (ASN1_DEC_CTXT *aCtx);

/********************************************************************************/
/*			_a_dec_authentication					*/
/*------------------------------------------------------------------------------*/
/* Decodes Authentication info. The buffer needed to hold authentication info	*/
/* is allocated in this	function and should be released after connection	*/
/* terminates.									*/
/* Parameters:									*/
/*   ACSE_AUTH_INFO    *authInfo	pointer to the decoded authInfo		*/
/*   ST_CHAR	       *auth_value	ptr to encoded authentication value	*/
/*   ST_INT             auth_value_len	length of the authentication encoding	*/
/* Return:									*/
/*   ST_RET		SD_SUCCESS or SD_FAILURE				*/
/********************************************************************************/

static ST_RET _a_dec_authentication (ACSE_AUTH_INFO *authInfo,		/* out	*/
                                     ST_UCHAR        *auth_value,	/* in	*/
                                     ST_INT          auth_value_len)	/* in	*/

{
ASN1_DEC_CTXT   localDecCtx;	/* For readability, use "aCtx" to access this.*/
ASN1_DEC_CTXT  *aCtx = &localDecCtx;

  ALOG_DEC0 ("Decoding Authentication");
  memset (aCtx, 0, sizeof (ASN1_DEC_CTXT));	/* CRITICAL: start clean*/

  /* Get ptr to association indication information.	     		*/
  authInfo->auth_pres = SD_FALSE;	/* set in case we fail		*/

  if (auth_value_len <= 0)
    {
    ACSELOG_ERR1 ("_a_dec_authentication: invalid authentication len=%d", auth_value_len);
    return (SD_FAILURE);
    }

  if (authInfo->mech_type == ACSE_AUTH_MECH_PASSWORD)
    ASN1R_TAG_ADD (aCtx, CTX, 0, _a_dec_auth_value_graphicString);
  else
    ASN1R_TAG_ADD (aCtx, CTX|CONSTR, 2, _a_dec_auth_value_external);

  /* decode the authentication value */
  aCtx->asn1r_decode_method = ASN1_TAG_METHOD;	/* Use asn1_tag method for decoding. 	*/

  aCtx->usr_info[0] = authInfo;		/* need to be set by dec funcs	*/

  aCtx->asn1r_decode_done_fun = asn1r_done_err; /* done function = error fun 	*/
  aCtx->asn1r_err_fun = NULL;		   /* set up error detected fun 	*/

  /* Call function to parse the message	*/
  asn1r_decode_asn1 (aCtx, auth_value, auth_value_len);

  if (aCtx->asn1r_pdu_dec_err == NO_DECODE_ERR)
    {
    /* decoding of authentication successful */
    authInfo->auth_pres = SD_TRUE;
    return (SD_SUCCESS);
    }
    
  return (SD_FAILURE);
  }

/************************************************************************/
/*		_a_dec_auth_value_external				*/
/************************************************************************/

static ST_VOID _a_dec_auth_value_external (ASN1_DEC_CTXT *aCtx)
  {
  ALOG_CDEC0 ("_a_dec_auth_value_external");

/* We need to see the Single Asn1 Type constructor */
  ASN1R_TAG_ADD (aCtx, CTX|CONSTR, 0, _a_dec_external_singleAsn1Type);
  }


/************************************************************************/
/*		_a_dec_external_singleAsn1Type				*/
/************************************************************************/

static ST_VOID _a_dec_external_singleAsn1Type (ASN1_DEC_CTXT *aCtx)
  {
  ALOG_CDEC0 ("_a_dec_external_singleAsn1Type");

/* OK, we are at the start of the data ... just record the position 	*/
  aCtx->usr_info[1] = ASN1_DEC_PTR(aCtx);

  asn1r_parse_next (aCtx, _a_dec_external_singleAsn1Type_done); /* parse entire element */
  }


/************************************************************************/
/* 		_a_dec_external_singleAsn1Type_done			*/
/************************************************************************/

static ST_VOID _a_dec_external_singleAsn1Type_done (ASN1_DEC_CTXT *aCtx)
  {
ACSE_AUTH_INFO *authInfo = (ACSE_AUTH_INFO *) aCtx->usr_info[0];
ST_UCHAR *data_start;
ST_INT data_len;

  ALOG_CDEC0 ("_a_dec_external_singleAsn1Type_done");

  data_start = (ST_UCHAR *) aCtx->usr_info[1];
  data_len = aCtx->asn1r_octetcount;

  authInfo->u.other_auth.auth_value.len = data_len;
  authInfo->u.other_auth.auth_value.buf = authInfo->u.other_auth.auth_value.ptr =
                 (ST_UCHAR *) chk_calloc (data_len, sizeof(ST_CHAR));
  memcpy (authInfo->u.other_auth.auth_value.ptr, data_start, data_len);

  aCtx->asn1r_c_done_fun [aCtx->asn1r_msg_level] = _a_dec_auth_value_external_done;
  }

/************************************************************************/
/* 		_a_dec_auth_value_external_done				*/
/************************************************************************/

static ST_VOID _a_dec_auth_value_external_done (ASN1_DEC_CTXT *aCtx)
  {
  ALOG_CDEC0 ("_a_dec_auth_value_external_done");
  aCtx->asn1r_decode_done_fun = _a_dec_auth_done;
  aCtx->asn1r_decode_done = SD_TRUE;
  }


/************************************************************************/
/* 		_a_dec_auth_value_graphicString				*/
/************************************************************************/

static ST_VOID _a_dec_auth_value_graphicString (ASN1_DEC_CTXT *aCtx)
  {
ACSE_AUTH_INFO *authInfo = (ACSE_AUTH_INFO *) aCtx->usr_info[0];
ST_INT          password_len;

  ALOG_CDEC0 ("_a_dec_auth_value_graphicString");

/* OK, here we have the data we need */
  password_len = ASN1_DEC_LEN(aCtx);
  if (password_len <= ACSE_MAX_LEN_PASSWORD)
    {
    memcpy (authInfo->u.pw_auth.password, ASN1_DEC_PTR(aCtx), password_len);
    authInfo->u.pw_auth.password[password_len] = 0; /* NULL terminate */
    aCtx->asn1r_decode_done_fun = _a_dec_auth_done;
    }
  else
    {
    ACSELOG_ERR1 ("_a_dec_auth_value: password too long (%d bytes)", password_len);
    authInfo->u.pw_auth.password[0] = 0;	/* NULL password	*/
    asn1r_set_dec_err (aCtx, ASN1E_INVALID_LENGTH);
    }
  aCtx->asn1r_decode_done = SD_TRUE;
  }

/************************************************************************/
/*			_a_dec_auth_done				*/
/************************************************************************/

static ST_VOID _a_dec_auth_done (ASN1_DEC_CTXT *aCtx)
  {
  ALOG_CDEC0 ("_a_dec_auth_done");
  }

