/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*	(c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*	1995-2008, All Rights Reserved					*/
/*									*/
/* MODULE NAME : acse2enc.c						*/
/* PRODUCT(S)  :							*/
/*									*/
/* MODULE DESCRIPTION :							*/
/*	ACSE Encode Functions.						*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev     Comments					*/
/* --------  ---  ------   -------------------------------------------  */
/* 02/27/08  EJV    35     a_associate_rsp: do not free acse_conn.	*/
/* 11/07/06  EJV    34     MMSEASE_MOSI:elim HPUX warn when logging PSEL*/
/* 07/06/05  EJV    33     a_associate_req: log "IP ADDR: ip" same line,*/
/*			     MMSEASE_MOSI: log local PSEL,SSEL,TSEL.	*/
/* 05/06/05  EJV    32     Log PORT if configured.			*/
/* 01/24/05  JRB    31     Move acse_debug_sel & "*_logstr" to lean_var.c*/
/* 09/17/04  JRB    30     a_abort_req: don't access acse_conn after	*/
/*			   copp_u_abort_req (it may have been freed).	*/
/* 08/06/04  EJV    29     Added typecast (ST_UCHAR *) ...password.	*/
/* 03/04/04  JRB    28     Mov thisFil.. after incs, they may define DEBUG_..*/
/*			   Del logging of A-DATA.req (use MVL or COPP)	*/
/* 12/18/03  JRB    27	   If MOSI, use cosp_buf, DON't use acse_buf.	*/
/*			   Use ASN1_ENC_PTR, ASN1_ENC_LEN macros.	*/
/* 10/21/03  JRB    26     Add a_get_event_handles_unix.		*/
/* 08/20/03  EJV    25	   Chg cosp_msgsize to fit larger User Data	*/
/*  			   ACSE_AUTH_ENABLED code wo/define.		*/
/*			   Added security functions.			*/
/*			   Added param to tp4_connect .			*/
/*			   Fix the ACSE Auth encode.			*/
/* 06/17/03  EJV    24	   Added _acse_dib_logstr.			*/
/* 10/22/02  ASK    23	   Wrap ifdef TP0_ENABLED around inet_ntoa calls*/
/* 10/14/02  ASK    22     Added encoding for abort diagnostic.		*/
/* 08/26/02  ASK    21	   Added password mechanism encoding in		*/ 
/*			   a_associate_req and a_associate rsp.		*/
/* 07/16/02  JRB    20     Add tp0_sock.h for sockets/IP defs.		*/
/* 06/26/02  JRB    19     associate_req: Log calling/called address.	*/
/* 01/22/02  JRB    18     Add args to copp_initialize (same as MAP30..)*/
/*			   Del a_set_msgsize (no longer needed).	*/
/* 01/02/02  JRB    17     Converted to use ASN1R (re-entrant ASN1)	*/
/* 06/22/01  JRB    16     Fix a_release_rsp handling of STATE_RESP_COLL*/
/*			   state per ISO 8650 (was kluged before).	*/
/* 03/15/01  JRB    15     Use new SMEM allocation functions.		*/
/* 01/04/01  EJV    14     a_get_event_handles used only on _WIN32	*/
/* 09/24/99  JRB    13     a_get_event_handles not used on		*/
/*                         DOS, QNX or VXWORKS				*/
/* 09/13/99  MDE    12     Added SD_CONST modifiers			*/
/* 08/05/99  JRB    11     Add "a_get_event_handles" function for MOSI.	*/
/* 10/08/98  MDE    10     Migrated to updated SLOG interface		*/
/* 08/13/98  JRB    09     Lint cleanup.				*/
/* 07/10/98  JRB    08     Del *_obj_id, use acse2var.c			*/
/* 03/23/98  NAV    07     Free buffers on termination			*/
/* 02/16/98  JRB    06     Del or "#if out" unused locals.		*/
/* 11/10/97  JRB    05     Chg cosp_buf to (ST_UCHAR *).		*/
/* 09/22/97  JRB    04     acse_msgsize MUST be set before copp_init.	*/
/*			   a_data_req (MOSI): check for buffer overflow	*/
/* 09/04/97  JRB    03     Pass SPDU msgsize arg to tp4_initialize.	*/ 
/* 07/31/97  JRB    02     copp_bind & a_associate_req set "tp_type"	*/
/*			   in tp4_addr. New TP4 needs it.		*/
/* 06/09/97  JRB    01     If !MOSI, on A-ASSOCIATE.rsp-, just set	*/
/*			   state=STATE_IDLE. Don't free as did on 3/7/97*/
/* 05/27/97  JRB   7.00    MMSEASE 7.0 release.				*/
/* 05/02/97  JRB    24     wr_* functs now ret void, so don't check.	*/
/* 04/09/97  JRB    23     a_associate_req return (0L) for any error.	*/
/* 03/19/97  JRB    22     Add MOSI "CALLING" support.			*/
/* 03/19/97  JRB    21     Encode AARQ in a_associate_req: need for MOSI*/
/*			   In a_release_req, chg handling if send fails.*/
/*			   Use acse_free_conn to free conn ctrl struct.	*/
/*			   Improve logging.				*/
/*			   Improve release collision handling.		*/
/* 03/07/97  JRB    20     Always send result-source-diag on AARE.	*/
/*			   Rechecked spec, and this is MANDATORY.	*/
/* 03/07/97  JRB    19     If !MOSI, free acse_conn on negative		*/
/*			   A-Associate.rsp (MOSI frees in COSP).	*/
/* 03/07/97  JRB    18     Use new "acse_free_con" to free acse_conn.	*/
/* 03/07/97  JRB    17     Added MOSI support.				*/
/* 02/11/97  JRB    16     In a_release_rsp, if send fails, don't free	*/
/*			   acse_conn and don't change state.		*/
/* 01/23/97  JRB    15     Init acse_debug_sel in copp_initialize.	*/
/* 11/18/96  JRB    14     Del acse_dummyfun call. Funct is gone now.	*/
/* 11/15/96  JRB    13     Don't initialize globals.			*/
/*			   Move acse_debug_sel global here.		*/
/* 08/19/96  MDE    12     Add dummy a_buffers_avail for Reduced Stack	*/
/* 08/14/96  JRB    11     Add a_buffers_avail function.		*/
/* 07/19/96  JRB    10     Change DEBUG_MMS to DEBUG_SISCO.		*/
/* 07/15/96  JRB    09     Only free acse_conn->aarq_apdu.user_info.ptr	*/
/*			   if len != 0 (wasn't alloc'd otherwise).	*/
/* 05/02/96  JRB    08     Add STATE_TP4CONN_WAIT.			*/
/* 04/23/96  MDE    07     Fixed minor warning              		*/
/* 04/18/96  JRB    06     a_associate_req return acse_conn_id or NULL	*/
/*			   on error, instead of just SD_SUCCESS or error.	*/
/*			   if (acse_conn->state == STATE_AARE_WAIT)	*/
/*			   in a_abort_req, set to STATE_AARE_ABANDON.	*/
/* 03/26/96  MDE    05     Added REDUCED_STACK support			*/
/* 03/11/96  JRB    04     Move acse_msgsize here & use for acse_buf.	*/
/* 03/07/96  JRB    03     Handle aarq_apdu->user_info.len = 0.		*/
/* 01/19/96  JRB    02     Cast constant chars to ST_UCHAR.		*/
/* 10/30/95  JRB    01     Created					*/
/************************************************************************/
#include "glbtypes.h"
#include "sysincs.h"
#include "mem_chk.h"
#include "asn1defs.h"
#include "acse2.h"
#if defined (MOSI)
#include "cosp_usr.h"
#include "copp_usr.h"
#endif	/* MOSI	*/
#if defined (MMSEASE_MOSI)
#include "cosp.h"			/* for cosp_only_sel		*/
#include "copp.h"			/* for only_loc_psel		*/
#include "tp4.h"			/* for tp0_check_bind_id	*/
#endif
#include "tp4api.h"
#include "tp0_sock.h"

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

ST_UINT acse_msgsize;


/* Buffer for building init, release, and abort reqests.	*/
#if defined (MOSI)
ST_UINT cosp_msgsize;
ST_UCHAR *cosp_buf;	/* Buffer containing "acse_buf" + COSP&COPP headers.*/
static ST_VOID acse_encode_user_info (ASN1_ENC_CTXT *aCtx, ACSE_CONN *acse_conn, BUFFER *user_info);
#else	/* !MOSI	*/
static ST_UCHAR *acse_buf;
#endif	/* !MOSI	*/

#define TP4_EOT		1	/* for tp4_data calls.		*/

/************************************************************************/
/* copp_initialize							*/
/* Save "msg_size". Pass "max_conns" to tp4_initialize.			*/
/************************************************************************/
ST_RET copp_initialize (ST_INT max_conns, ST_UINT msg_size)
  {
ST_RET ret;




  acse_msgsize = msg_size;	/* Save msg size in global.		*/

  /* Always enable error logging.	*/
#if defined(DEBUG_SISCO)
  acse_debug_sel |= (ACSE_LOG_ERR | COPP_LOG_ERR | COSP_LOG_ERR);
#endif

  if (acse_msgsize == 0)
    {			/* ACSE user must call a_set_msgsize first.	*/
    ACSELOG_ERR0 ("ACSE-ERROR: msg size not set");
    return (E_ACSE_INVALID_PARAM);	/* Invalid Parameter		*/
    }
#if defined(MOSI)
  cosp_msgsize = max(acse_msgsize, COSP_MAX_UDATA_CON) + MAX_COSP_COPP_HEADER;
  cosp_buf = (ST_UCHAR *) M_MALLOC (MSMEM_STARTUP, cosp_msgsize);

  ret = tp4_initialize (max_conns, cosp_msgsize);
#else	/* !MOSI	*/
  acse_buf = (ST_UCHAR *) M_MALLOC (MSMEM_STARTUP, acse_msgsize);
  ret = tp4_initialize (max_conns, acse_msgsize);
#endif	/* !MOSI	*/
  return (ret);
  }

/************************************************************************/
/* copp_terminate							*/
/************************************************************************/
ST_RET copp_terminate (ST_VOID)
  {

#ifdef MOSI
  if (cosp_buf)
    M_FREE (MSMEM_STARTUP, cosp_buf);
#else
  if(acse_buf)
    M_FREE (MSMEM_STARTUP, acse_buf);
#endif 

  return (tp4_terminate ());
  }

/************************************************************************/
/* copp_event								*/
/************************************************************************/
ST_BOOLEAN copp_event (ST_VOID)
  {
  return (tp4_event ());
  }

#if !defined (MOSI)	/* see copp_enc.c for MOSI bind functs.		*/
/************************************************************************/
/* copp_bind								*/
/************************************************************************/
ST_RET copp_bind (ST_LONG user_bind_id, PRES_ADDR *pres_addr, 
	ST_INT sharable, ST_INT max_conns)
  {
TP4_ADDR tp4_addr;

  ACSELOG_ENC1 ("COPP-Bind   Req.  user_bind_id =%9ld", user_bind_id);

#if !defined(REDUCED_STACK)
  /* Copy tsel and nsap from pres_addr to tp4_addr struct	*/
  tp4_addr.tp_type = pres_addr->tp_type;
  tp4_addr.tp4_sel_len = pres_addr->tsel_len;
  memcpy (tp4_addr.tp4_sel, pres_addr->tsel, pres_addr->tsel_len);
  tp4_addr.net_addr_len = pres_addr->nsap_len;
  memcpy (tp4_addr.net_addr, pres_addr->nsap, pres_addr->nsap_len);
#else
  tp4_addr.adlcAddr = pres_addr->adlcAddr;
#endif

  /* Call tp4_bind.	*/
  return (tp4_bind (user_bind_id, &tp4_addr, sharable, max_conns));
  }

/************************************************************************/
/* copp_unbind								*/
/************************************************************************/
ST_RET copp_unbind (ST_LONG copp_bind_id)
  {
  ACSELOG_ENC1 ("COPP-Unbind Req.                          copp_bind_id =%9ld", copp_bind_id);
  return (tp4_unbind (copp_bind_id));
  }
#endif	/* !MOSI	*/

/************************************************************************/
/* a_associate_req							*/
/************************************************************************/
ST_LONG a_associate_req (
	ST_LONG copp_bind_id,	/* ACSE's bind ID received in u_copp_bind_cnf*/
	ST_LONG user_conn_id,	/* USER's connection ID			*/
	AARQ_APDU *aarq_apdu)	/* AARQ_APDU info			*/
  {
#if !defined (MOSI)
TP4_ADDR tp4_addr;
#endif	/* !MOSI	*/
ACSE_CONN *acse_conn;
AE_TITLE *ae_title;
ST_UCHAR version1_bit_str = BIT_VERSION1;	/* Bit 0 of bitstr (high bit) set	*/
ST_UCHAR auth_bit_str = BIT_AUTHENTICATION;		

ST_UCHAR *msg_ptr;
ST_UINT msg_len;
ASN1_ENC_CTXT localEncCtx;	/* For readability, use "aCtx" to access this.*/
ASN1_ENC_CTXT *aCtx = &localEncCtx;
ST_INT j;

  /* Allocate connection tracking structure "acse_conn".		*/
  acse_conn = (ACSE_CONN *) M_CALLOC (MSMEM_ACSE_CONN, 1, sizeof (ACSE_CONN));

  /* Save USER's connection ID. Use it in calls to user functions.	*/
  acse_conn->user_conn_id = user_conn_id;

  /* save the encryption info */
  acse_conn->encrypt_ctrl = aarq_apdu->encrypt_ctrl;

  ACSELOG_ENC2 ("A-ASSOCIATE.req:  acse_conn_id =0x%08X  user_conn_id =%4ld",
    acse_conn, acse_conn->user_conn_id);
  ACSELOG_ENC0C ("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_ENC2C ("  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_ENC1C ("  AE_qual = %ld", aarq_apdu->called_ae_title.AE_qual);
    }
  ACSELOG_ENC0C ("  PSEL:");
  ACSELOG_ENCH  (aarq_apdu->called_paddr.psel_len, aarq_apdu->called_paddr.psel);
  ACSELOG_ENC0C ("  SSEL:");
  ACSELOG_ENCH  (aarq_apdu->called_paddr.ssel_len, aarq_apdu->called_paddr.ssel);
  ACSELOG_ENC0C ("  TSEL:");
  ACSELOG_ENCH  (aarq_apdu->called_paddr.tsel_len, aarq_apdu->called_paddr.tsel);
  if (aarq_apdu->called_paddr.tp_type == TP_TYPE_TCP)
    {
    struct in_addr sin_addr;	/* inet_ntoa needs this addr format*/
    sin_addr.s_addr = aarq_apdu->called_paddr.netAddr.ip;
#if defined(TP0_ENABLED)
    ACSELOG_ENC1C ("  IP ADDR:  %s", inet_ntoa (sin_addr));
    if (!aarq_apdu->encrypt_ctrl.encryptMode == S_SEC_ENCRYPT_SSL &&
        aarq_apdu->called_paddr.port)
      ACSELOG_ENC1C ("  PORT:  %u", (ST_UINT) aarq_apdu->called_paddr.port);
#endif
    if (aarq_apdu->encrypt_ctrl.encryptMode == S_SEC_ENCRYPT_SSL)
      ACSELOG_ENC1C ("  Encryption: SSL, port=%d", aarq_apdu->encrypt_ctrl.u.ssl.port);
    else
      ACSELOG_ENC0C ("  Encryption: None");
    if (aarq_apdu->auth_info.auth_pres)
      {
      if (aarq_apdu->auth_info.mech_type == ACSE_AUTH_MECH_MACE_CERT)
        ACSELOG_ENC0C ("  Authentication: MACE");
      else if (aarq_apdu->auth_info.mech_type == ACSE_AUTH_MECH_PASSWORD)
        ACSELOG_ENC0C ("  Authentication: password");
      else
        ACSELOG_ENC0C ("  Authentication: other");
      }
    else
      ACSELOG_ENC0C ("  Authentication: None");
    }
  else
    {
    ACSELOG_ENC0C ("  NSAP:");
    ACSELOG_ENCH  (aarq_apdu->called_paddr.nsap_len, aarq_apdu->called_paddr.netAddr.nsap);
    }

  ACSELOG_ENC0C ("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_ENC2C ("  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_ENC1C ("  AE_qual = %ld", aarq_apdu->calling_ae_title.AE_qual);
    }
#if defined (MMSEASE_MOSI)
  {
  ST_UCHAR *tp0_loc_tsap;
  ACSELOG_ENC0C ("  PSEL:");
  if (only_loc_psel[0])
    ACSELOG_ENCH  ((ST_UINT) only_loc_psel[0], &only_loc_psel[1]);
  ACSELOG_ENC0C ("  SSEL:");
  if (cosp_only_ssel[0])
    ACSELOG_ENCH  ((ST_UINT) cosp_only_ssel[0], &cosp_only_ssel[1]);
  ACSELOG_ENC0C ("  TSEL:");
  if (tp0_check_bind_id (copp_bind_id, &tp0_loc_tsap) == SD_SUCCESS)
    ACSELOG_ENCH  ((ST_UINT) tp0_loc_tsap[0], &tp0_loc_tsap[1]);
  }
#else
  /* NOTE: aarq_apdu->calling_paddr is NOT logged because caller may	*/
  /*       not set it, and even if they do, it is not used. The address	*/
  /*       passed to "copp_bind" is used as the calling_paddr.		*/
#endif

  /* Begin encoding request.						*/
#if defined (MOSI)
  asn1r_strt_asn1_bld (aCtx, cosp_buf,cosp_msgsize);
#else
  asn1r_strt_asn1_bld (aCtx, acse_buf,acse_msgsize);
#endif
    
  asn1r_strt_constr (aCtx);			/* Start AARQ APDU constructor	*/
  /* Write user_info.							*/
  if (aarq_apdu->user_info.len)
    {
    asn1r_strt_constr (aCtx);
#if defined (MOSI)
    /* PCI's must be set before any encoding.				*/
    acse_conn->pciacse = 1;
    acse_conn->pcimms = 3;
    acse_encode_user_info (aCtx, acse_conn, &aarq_apdu->user_info);
#else	/* !MOSI	*/
    asn1r_wr_octstr (aCtx, aarq_apdu->user_info.ptr, aarq_apdu->user_info.len);
    /* No P-Contexts in this implementation, so skip transfer-syntax	*/
    /* and p-context-id.						*/
#endif	/* !MOSI	*/
    asn1r_fin_constr (aCtx, AARQ_user_info,CTX,DEF);
    }
  
  if(aarq_apdu->auth_info.auth_pres == SD_TRUE)
    {
    if(aarq_apdu->auth_info.mech_type == ACSE_AUTH_MECH_PASSWORD)
      {
      ACSELOG_ENC0C ("  Authentication: password");
      /* Write the password value */
      asn1r_strt_constr (aCtx);	  
      asn1r_wr_octstr (aCtx, (ST_UCHAR *) aarq_apdu->auth_info.u.pw_auth.password, 
      		     strlen(aarq_apdu->auth_info.u.pw_auth.password)); /* Strip off the NULL */
      asn1r_fin_prim (aCtx, 0,CTX); 
      asn1r_fin_constr (aCtx, AARQ_CALLING_auth_value,CTX,DEF);
  
      /* Write the mechanism name (password in this case) */
      asn1r_wr_objid (aCtx, a_auth_password_obj_id.comps, a_auth_password_obj_id.num_comps);
      asn1r_fin_prim (aCtx, AARQ_mechanism_name, CTX);
      }
    else
      {
      if (aarq_apdu->auth_info.mech_type == ACSE_AUTH_MECH_MACE_CERT)
        ACSELOG_ENC0C ("Authentication: MACE");
      else
        ACSELOG_ENC0C ("Authentication: other");
      asn1r_strt_constr (aCtx);	  
      asn1r_strt_constr (aCtx);				/* Authentication-value	*/
      asn1r_strt_constr (aCtx);				/* Single ASN.1 Type 	*/
      asn1r_wr_octstr (aCtx, aarq_apdu->auth_info.u.other_auth.auth_value.ptr, 
      		     	     aarq_apdu->auth_info.u.other_auth.auth_value.len);
      asn1r_fin_constr (aCtx, 0, CTX, SD_FALSE); 	/* Single ASN.1 Type 	*/
      asn1r_fin_constr (aCtx, 2, CTX, SD_FALSE); 	/* Authentication-value	*/
      asn1r_fin_constr (aCtx, AARQ_CALLING_auth_value,CTX,DEF);
  
      /* Write the mechanism name */
      asn1r_wr_objid (aCtx, aarq_apdu->auth_info.u.other_auth.mech_id.comps, aarq_apdu->auth_info.u.other_auth.mech_id.num_comps);
      asn1r_fin_prim (aCtx, AARQ_mechanism_name, CTX);
      }
  
    /* Let's encode the authentication present bit to enable security */
    asn1r_wr_bitstr (aCtx, &auth_bit_str, 1);
    asn1r_fin_prim (aCtx, AARQ_acse_requirements, CTX);
    } 

  /* Write CALLING AE Title (i.e. AP_title, AE_qual, AP_inv_id, AE_inv_id).*/
  ae_title = &aarq_apdu->calling_ae_title;

  if (ae_title->AE_inv_id_pres)
    {
    asn1r_strt_constr (aCtx);
    asn1r_wr_i32 (aCtx, ae_title->AE_inv_id);
    asn1r_fin_prim (aCtx, INT_CODE,UNI);
    asn1r_fin_constr (aCtx, AARQ_CALLING_AE_inv_id,CTX,DEF);
    }
    
  if (ae_title->AP_inv_id_pres)
    {
    asn1r_strt_constr (aCtx);
    asn1r_wr_i32 (aCtx, ae_title->AP_inv_id);
    asn1r_fin_prim (aCtx, INT_CODE,UNI);
    asn1r_fin_constr (aCtx, AARQ_CALLING_AP_inv_id,CTX,DEF);
    }
    
  if (ae_title->AE_qual_pres)
    {
    asn1r_strt_constr (aCtx);
    asn1r_wr_i32 (aCtx, ae_title->AE_qual);
    asn1r_fin_prim (aCtx, INT_CODE,UNI);
    asn1r_fin_constr (aCtx, AARQ_CALLING_AE_qual,CTX,DEF);
    }
    
  if (ae_title->AP_title_pres)
    {
    asn1r_strt_constr (aCtx);
    asn1r_wr_objid (aCtx, ae_title->AP_title.comps, ae_title->AP_title.num_comps);
    asn1r_fin_prim (aCtx, OBJ_ID_CODE,UNI);
    asn1r_fin_constr (aCtx, AARQ_CALLING_AP_title,CTX,DEF);
    }


  /* Write CALLED AE Title (i.e. AP_title, AE_qual, AP_inv_id, AE_inv_id).*/
  ae_title = &aarq_apdu->called_ae_title;

  if (ae_title->AE_inv_id_pres)
    {
    asn1r_strt_constr (aCtx);
    asn1r_wr_i32 (aCtx, ae_title->AE_inv_id);
    asn1r_fin_prim (aCtx, INT_CODE,UNI);
    asn1r_fin_constr (aCtx, AARQ_CALLED_AE_inv_id,CTX,DEF);
    }
    
  if (ae_title->AP_inv_id_pres)
    {
    asn1r_strt_constr (aCtx);
    asn1r_wr_i32 (aCtx, ae_title->AP_inv_id);
    asn1r_fin_prim (aCtx, INT_CODE,UNI);
    asn1r_fin_constr (aCtx, AARQ_CALLED_AP_inv_id,CTX,DEF);
    }
    
  if (ae_title->AE_qual_pres)
    {
    asn1r_strt_constr (aCtx);
    asn1r_wr_i32 (aCtx, ae_title->AE_qual);
    asn1r_fin_prim (aCtx, INT_CODE,UNI);
    asn1r_fin_constr (aCtx, AARQ_CALLED_AE_qual,CTX,DEF);
    }
    
  if (ae_title->AP_title_pres)
    {
    asn1r_strt_constr (aCtx);
    asn1r_wr_objid (aCtx, ae_title->AP_title.comps, ae_title->AP_title.num_comps);
    asn1r_fin_prim (aCtx, OBJ_ID_CODE,UNI);
    asn1r_fin_constr (aCtx, AARQ_CALLED_AP_title,CTX,DEF);
    }

  /* Write ASO Context Name.						*/
  if (aarq_apdu->ASO_context_name_pres)
    {
    asn1r_strt_constr (aCtx);
    asn1r_wr_objid (aCtx, aarq_apdu->ASO_context_name.comps, aarq_apdu->ASO_context_name.num_comps);
    asn1r_fin_prim (aCtx, OBJ_ID_CODE,UNI);
    asn1r_fin_constr (aCtx, AARQ_ASO_context_name,CTX,DEF);
    }

  /* Write protocol_version.						*/
  asn1r_wr_bitstr (aCtx, &version1_bit_str, 1);
  asn1r_fin_prim (aCtx, AARQ_protocol_version, CTX);

  asn1r_fin_constr (aCtx, TAG_AARQ,APP,DEF);	/* Finish AARQ APDU	*/

  msg_ptr = ASN1_ENC_PTR (aCtx);	/* point to beginning of PDU.	*/
  msg_len = ASN1_ENC_LEN (aCtx);

#if ! (defined(REDUCED_STACK) || defined(MOSI) )
  /* Fastbyte Presentation and Session (2 bytes).			*/
  *(--msg_ptr) = 0x01;		/* Short-CP mode = BER			*/
  *(--msg_ptr) = (ST_UCHAR) 0xE8;	/* Short-SCN, SI = 11101, 0 params	*/
  msg_len+=2;	/* adjust len	*/
#endif	/* ! (defined(REDUCED_STACK) || defined(MOSI) )	*/
  if (msg_len > acse_msgsize)
    return (0L);

#if defined (MOSI)
  acse_conn->apdu_ptr = msg_ptr;
  acse_conn->apdu_len = msg_len;
  /* Send AARQ APDU via P-CONNECT.req					*/
  if (copp_con_req (copp_bind_id, acse_conn, &aarq_apdu->called_paddr) != SD_SUCCESS)
    {
    acse_free_con (acse_conn);		/* Dump conn tracking struct	*/
    return (0L);
    }
  acse_conn->state = STATE_AARE_WAIT;	/* Waiting for AARE-apdu	*/
#else	/* !MOSI	*/
  /* Save AARQ APDU (in save_buf) to send when tp4 connection		*/
  /* established (see tp4_connect_cnf).					*/
  acse_conn->save_buf.len = msg_len;
  acse_conn->save_buf.ptr = (ST_UCHAR *) M_MALLOC (MSMEM_ACSE_DATA, msg_len);
  memcpy (acse_conn->save_buf.ptr, msg_ptr, msg_len);

  /* Send Transport connect request.					*/
  /* The connection ID is just a pointer to "acse_conn" cast to a ST_LONG.	*/
  /* Copy tsel and nsap from aarq_apdu->called_paddr to tp4_addr	*/
#if !defined(REDUCED_STACK)
  tp4_addr.tp_type = aarq_apdu->called_paddr.tp_type;
  tp4_addr.tp4_sel_len = aarq_apdu->called_paddr.tsel_len;
  memcpy (tp4_addr.tp4_sel, aarq_apdu->called_paddr.tsel,
          aarq_apdu->called_paddr.tsel_len);
  tp4_addr.net_addr_len = aarq_apdu->called_paddr.nsap_len;
  memcpy (tp4_addr.net_addr, aarq_apdu->called_paddr.nsap,
          aarq_apdu->called_paddr.nsap_len);
#else
  tp4_addr.adlcAddr = aarq_apdu->called_paddr.adlcAddr;
#endif

  if (tp4_connect (copp_bind_id, (ST_LONG) acse_conn,
                   &tp4_addr, 0, NULL, NULL) != SD_SUCCESS)
    {
    acse_free_con (acse_conn);		/* Dump conn tracking struct	*/
    return (0L);
    }
  acse_conn->state = STATE_TP4CONN_WAIT;	/* Waiting for TP4 connection*/
#endif	/* !MOSI	*/
  return ((ST_LONG)acse_conn);
  }

/************************************************************************/
/* a_associate_rsp							*/
/************************************************************************/
ST_RET a_associate_rsp (
	ST_LONG acse_conn_id,	/* ACSE's connection ID			*/
	ST_LONG user_conn_id,	/* USER's connection ID			*/
	AARE_APDU *info)	/* AARE_APDU info			*/
  {
AE_TITLE *ae_title;
ST_UCHAR version1_bit_str = BIT_VERSION1;	/* Bit 0 of bitstr (high bit) set	*/
ST_UCHAR auth_bit_str = BIT_AUTHENTICATION;		

ST_UCHAR *msg_ptr;
ST_UINT msg_len;
ACSE_CONN *acse_conn;
ST_RET retcode;
ASN1_ENC_CTXT localEncCtx;	/* For readability, use "aCtx" to access this.*/
ASN1_ENC_CTXT *aCtx = &localEncCtx;

  ACSELOG_ENC2 ("A-ASSOCIATE.rsp:  acse_conn_id =0x%08X  user_conn_id =%4ld",
    acse_conn_id, user_conn_id);

  acse_conn = (ACSE_CONN *) acse_conn_id;

  if (acse_conn == NULL)
    return (E_ACSE_INVALID_CONN_ID);	/* Invalid acse_conn_id		*/

  if (acse_conn->state != STATE_AASCRSP_WAIT)
    return (E_ACSE_INVALID_STATE);	/* Invalid state for this operation*/

  /* Save USER's connection ID. Use it in calls to user functions.	*/
  acse_conn->user_conn_id = user_conn_id;

  /* Everything looks OK. Go ahead and encode response and send it.	*/
#if defined (MOSI)
  asn1r_strt_asn1_bld (aCtx, cosp_buf,cosp_msgsize);
#else
  asn1r_strt_asn1_bld (aCtx, acse_buf,acse_msgsize);
#endif
    
  asn1r_strt_constr (aCtx);			/* Start AARE APDU constructor	*/
  /* Write user_info.							*/
  if (info->user_info.len)
    {
    asn1r_strt_constr (aCtx);
#if defined (MOSI)
    acse_encode_user_info (aCtx, acse_conn, &info->user_info);
#else	/* !MOSI	*/
    asn1r_wr_octstr (aCtx, info->user_info.ptr, info->user_info.len);
    /* No P-Contexts in this implementation, so skip transfer-syntax	*/
    /* and p-context-id.						*/
#endif	/* !MOSI	*/
    asn1r_fin_constr (aCtx, AARE_user_info,CTX,DEF);
    }

  if(info->auth_info.auth_pres == SD_TRUE)
    {
    if(info->auth_info.mech_type == ACSE_AUTH_MECH_PASSWORD)
      {
      /* Write the password value */
      asn1r_strt_constr (aCtx);	  
      asn1r_wr_octstr (aCtx, (ST_UCHAR *) info->auth_info.u.pw_auth.password, 
      		     strlen(info->auth_info.u.pw_auth.password)); /* Strip off the NULL */
      asn1r_fin_prim (aCtx, 0,CTX); 
      asn1r_fin_constr (aCtx, AARE_RESPONDING_auth_value,CTX,DEF);
  
      /* Write the mechanism name (password in this case) */
      asn1r_wr_objid (aCtx, a_auth_password_obj_id.comps, a_auth_password_obj_id.num_comps);
      asn1r_fin_prim (aCtx, AARE_mechanism_name, CTX);
      }
    else
      {
      if (info->auth_info.mech_type == ACSE_AUTH_MECH_MACE_CERT)
        ACSELOG_ENC0C ("Authentication: MACE");
      else
        ACSELOG_ENC0C ("Authentication: other");
      asn1r_strt_constr (aCtx);	  
      asn1r_strt_constr (aCtx);				/* Authentication-value	*/
      asn1r_strt_constr (aCtx);				/* Single ASN.1 Type 	*/
      asn1r_wr_octstr (aCtx, info->auth_info.u.other_auth.auth_value.ptr, 
      		     	     info->auth_info.u.other_auth.auth_value.len);
      asn1r_fin_constr (aCtx, 0, CTX, SD_FALSE); 	/* Single ASN.1 Type 	*/
      asn1r_fin_constr (aCtx, 2, CTX, SD_FALSE); 	/* Authentication-value	*/
      asn1r_fin_constr (aCtx, AARE_RESPONDING_auth_value,CTX,DEF);
  
      /* Write the mechanism name */
      asn1r_wr_objid (aCtx, info->auth_info.u.other_auth.mech_id.comps, 
      			    info->auth_info.u.other_auth.mech_id.num_comps);
      asn1r_fin_prim (aCtx, AARE_mechanism_name, CTX);
      }
  
    /* Let's encode the authentication present bit to enable security */
    asn1r_wr_bitstr (aCtx, &auth_bit_str, 1);
    asn1r_fin_prim (aCtx, AARE_RESPONDER_acse_requirements, CTX);
    } 

  /* Write RESP AE Title (i.e. AP_title, AE_qual, AP_inv_id, AE_inv_id)	*/
  /* fields of AARE APDU.						*/
  ae_title = &info->responding_ae_title;

  if (ae_title->AE_inv_id_pres)
    {
    asn1r_strt_constr (aCtx);
    asn1r_wr_i32 (aCtx, ae_title->AE_inv_id);
    asn1r_fin_prim (aCtx, INT_CODE,UNI);
    asn1r_fin_constr (aCtx, AARE_RESPONDING_AE_inv_id,CTX,DEF);
    }
    
  if (ae_title->AP_inv_id_pres)
    {
    asn1r_strt_constr (aCtx);
    asn1r_wr_i32 (aCtx, ae_title->AP_inv_id);
    asn1r_fin_prim (aCtx, INT_CODE,UNI);
    asn1r_fin_constr (aCtx, AARE_RESPONDING_AP_inv_id,CTX,DEF);
    }
    
  if (ae_title->AE_qual_pres)
    {
    asn1r_strt_constr (aCtx);
    asn1r_wr_i32 (aCtx, ae_title->AE_qual);
    asn1r_fin_prim (aCtx, INT_CODE,UNI);
    asn1r_fin_constr (aCtx, AARE_RESPONDING_AE_qual,CTX,DEF);
    }
    
  if (ae_title->AP_title_pres)
    {
    asn1r_strt_constr (aCtx);
    asn1r_wr_objid (aCtx, ae_title->AP_title.comps, ae_title->AP_title.num_comps);
    asn1r_fin_prim (aCtx, OBJ_ID_CODE,UNI);
    asn1r_fin_constr (aCtx, AARE_RESPONDING_AP_title,CTX,DEF);
    }

  /* Write result-source-diagnostic.					*/
  /* DEBUG: If user required to set "result_source" and "result_diag",	*/
  /* "result_source_diag_pres" would no longer be needed.		*/
  if (info->result_source_diag_pres == 0)
    {		/* User probably didn't set values, so set to default.	*/
    info->result_source = 1;	/* Source = acse-service-user.	*/
    info->result_diag = 0;	/* Diag = null.			*/
    }
  asn1r_strt_constr (aCtx);
  asn1r_strt_constr (aCtx);
  asn1r_wr_i16 (aCtx, info->result_diag);
  asn1r_fin_prim (aCtx, INT_CODE,UNI);
  if (info->result_source != 1  &&  info->result_source != 2)
    return (E_ACSE_ENC_ERR);		/* Make sure source legal.	*/
  asn1r_fin_constr (aCtx, info->result_source,CTX,DEF);
  asn1r_fin_constr (aCtx, AARE_result_source_diagnostic,CTX,DEF);

  /* Write result							*/
  asn1r_strt_constr (aCtx);
  asn1r_wr_i16 (aCtx, info->result);
  asn1r_fin_prim (aCtx, INT_CODE,UNI);
  asn1r_fin_constr (aCtx, AARE_result,CTX,DEF);

  /* Write ASO Context Name.						*/
  if (info->ASO_context_name_pres)
    {
    asn1r_strt_constr (aCtx);
    asn1r_wr_objid (aCtx, info->ASO_context_name.comps, info->ASO_context_name.num_comps);
    asn1r_fin_prim (aCtx, OBJ_ID_CODE,UNI);
    asn1r_fin_constr (aCtx, AARE_ASO_context_name,CTX,DEF);
    }

  /* Write protocol_version.						*/
  asn1r_wr_bitstr (aCtx, &version1_bit_str, 1);
  asn1r_fin_prim (aCtx, AARE_protocol_version, CTX);

  asn1r_fin_constr (aCtx, TAG_AARE,APP,DEF);	/* Finish AARE APDU	*/

  msg_ptr = ASN1_ENC_PTR (aCtx);	/* point to beginning of PDU.	*/
  msg_len = ASN1_ENC_LEN (aCtx);

#if !(defined(REDUCED_STACK) || defined(MOSI))
  /* Fastbyte Presentation and Session (2 bytes).			*/
  *(--msg_ptr) = 0x01;		/* Short-CP mode = BER			*/
  switch (info->result)
    {
    case 0:
      *(--msg_ptr)=(ST_UCHAR)0xF0;	/* Short-SAC (SI=11110) 0 params	*/
      break;
    case 1:
      *(--msg_ptr)=(ST_UCHAR)0xE3;	/* Short-SRF (SI=11100) 0 params,	*/
                                /* transport conn released, permanent	*/
      break;
    case 2:
      *(--msg_ptr)=(ST_UCHAR)0xE2;	/* Short-SRF (SI=11100) 0 params,	*/
                                /* transport conn released, transient	*/
      break;
    default:
      return (E_ACSE_INVALID_PARAM);	/* Invalid Parameter		*/
    }
  msg_len+=2;	/* adjust len	*/
#endif	/* !(defined(REDUCED_STACK) || defined(MOSI))	*/

  if (msg_len > acse_msgsize)
    return (E_ACSE_BUFFER_OVERFLOW);

#if defined(MOSI)
  acse_conn->apdu_ptr = msg_ptr;
  acse_conn->apdu_len = msg_len;
  if (info->result)
    /* NOTE:  COSP will free "acse_conn" after T-disconnect.	*/
    retcode = copp_con_rsp_neg (acse_conn, -1);	/* -1 means "from user"	*/
  else
    {
    retcode = copp_con_rsp_pos (acse_conn);
    if (retcode == SD_SUCCESS)
      acse_conn->state = STATE_ASSOCIATED;
    }
  if (retcode != SD_SUCCESS)
    {
    #if 0  
    /* EJV 2/27/2008: if copp_con_rsp_* fails then the most probable cause is	*/
    /*                that the socket was disconencted. In this case the	*/
    /*		      the tp0_disconnect_ind will free the acse_conn.		*/
    /*		      If it was other error the application will issue an abort	*/
    /*		      to cleanup the connection.				*/
    acse_free_con (acse_conn);		/* Dump conn tracking struct	*/
    #endif  /* 0 */ 
    ACSELOG_ENC0 ("ACSE-ERROR: failed to send A-ASSOCIATE.rsp (disconnected socket or other error).");
    return (E_ACSE_SEND_ERR);
    }
#else	/* !MOSI	*/
  /* Send via tp4_data.		*/
  /* The "user_conn_id" is just a pointer to "acse_conn" cast to a ST_LONG.*/
  retcode = tp4_data (acse_conn->tp4_conn_id, TP4_EOT, msg_len, (ST_CHAR *) msg_ptr);
  if (retcode != SD_SUCCESS)
    {
    acse_free_con (acse_conn);		/* Dump conn tracking struct	*/
    return (E_ACSE_SEND_ERR);
    }

  if (info->result == 0)
    acse_conn->state = STATE_ASSOCIATED;
  else
    acse_conn->state = STATE_IDLE;
#endif	/* !MOSI	*/

  return (SD_SUCCESS);
  }

/************************************************************************/
/* a_release_req							*/
/************************************************************************/
ST_RET a_release_req (
	ST_LONG acse_conn_id,	/* ACSE's connection ID			*/
	RLRQ_APDU *info)	/* RLRQ_APDU info			*/
  {
ST_UCHAR *msg_ptr;
ST_UINT msg_len;
ACSE_CONN *acse_conn;
ASN1_ENC_CTXT localEncCtx;	/* For readability, use "aCtx" to access this.*/
ASN1_ENC_CTXT *aCtx = &localEncCtx;

  acse_conn = (ACSE_CONN *)acse_conn_id;

  ACSELOG_ENC2 ("A-RELEASE.req:    acse_conn_id =0x%08X  user_conn_id =%4ld",
    acse_conn, acse_conn->user_conn_id);

  if (acse_conn == NULL)
    return (E_ACSE_INVALID_CONN_ID);	/* Invalid acse_conn_id		*/

  if (acse_conn->state != STATE_ASSOCIATED)
    return (E_ACSE_INVALID_STATE);	/* Invalid state for this operation*/

  /* Everything looks OK. Go ahead and encode request and send it.	*/
#if defined (MOSI)
  asn1r_strt_asn1_bld (aCtx, cosp_buf,cosp_msgsize);
#else
  asn1r_strt_asn1_bld (aCtx, acse_buf,acse_msgsize);
#endif
    
  asn1r_strt_constr (aCtx);		/* Start RLRQ APDU constructor		*/
  /* Write user_info.							*/
  if (info->user_info.len)
    {
    asn1r_strt_constr (aCtx);
#if defined (MOSI)
    acse_encode_user_info (aCtx, acse_conn, &info->user_info);
#else	/* !MOSI	*/
    asn1r_wr_octstr (aCtx, info->user_info.ptr, info->user_info.len);
    /* No P-Contexts in this implementation, so skip transfer-syntax	*/
    /* and p-context-id.						*/
#endif	/* !MOSI	*/
    asn1r_fin_constr (aCtx, RLRQ_user_info,CTX,DEF);
    }
  
  /* Write Release Reason.						*/
  if (info->reason_pres)
    {
    asn1r_wr_i16 (aCtx, info->reason);
    asn1r_fin_prim (aCtx, RLRQ_reason,CTX);
    }
    
  asn1r_fin_constr (aCtx, TAG_RLRQ,APP,DEF);	/* Finish RLRQ APDU	*/
  msg_ptr = ASN1_ENC_PTR (aCtx);	/* point to beginning of PDU.	*/
  msg_len = ASN1_ENC_LEN (aCtx);
  if (msg_len > acse_msgsize)
    return (E_ACSE_BUFFER_OVERFLOW);

#if defined(MOSI)
  acse_conn->apdu_ptr = msg_ptr;
  acse_conn->apdu_len = msg_len;
  /* Send RLRQ APDU via P-RELEASE.req					*/
  if (copp_rel_req (acse_conn) != SD_SUCCESS)
    return (E_ACSE_SEND_ERR);
#else	/* !MOSI	*/
  /* Send RLRQ APDU via T-DATA.req					*/
  if (tp4_data (acse_conn->tp4_conn_id, TP4_EOT, msg_len, (ST_CHAR *) msg_ptr) != SD_SUCCESS)
    return (E_ACSE_SEND_ERR);
#endif	/* !MOSI	*/
  acse_conn->state = STATE_RLRE_WAIT;
  
  return (SD_SUCCESS);
  }

/************************************************************************/
/* a_release_rsp							*/
/************************************************************************/
ST_RET a_release_rsp (
	ST_LONG acse_conn_id,	/* ACSE's connection ID			*/
	RLRE_APDU *info)	/* RLRE_APDU info			*/
  {
ST_UCHAR *msg_ptr;
ST_UINT msg_len;
ACSE_CONN *acse_conn;
ST_RET retcode;
ASN1_ENC_CTXT localEncCtx;	/* For readability, use "aCtx" to access this.*/
ASN1_ENC_CTXT *aCtx = &localEncCtx;

  acse_conn = (ACSE_CONN *)acse_conn_id;

  ACSELOG_ENC2 ("A-RELEASE.rsp:    acse_conn_id =0x%08X  user_conn_id =%4ld",
    acse_conn, acse_conn->user_conn_id);

  if (acse_conn == 0)
    return (E_ACSE_INVALID_CONN_ID);	/* Invalid acse_conn_id		*/

  /* This state may happen, but ACSE state machine says do nothing.	*/
  if (acse_conn->state == STATE_RESP_COLL)
    {
    ACSELOG_ENC0C ("A-RELEASE.rsp not sent because this is Responder side of RELEASE collision");
    return (SD_SUCCESS);
    }

  if (acse_conn->state != STATE_ARLSRSP_WAIT  &&
      acse_conn->state != STATE_INIT_COLL)
    return (E_ACSE_INVALID_STATE);	/* Invalid state for this operation*/

  /* Everything looks OK. Go ahead and encode response and send it.	*/
#if defined (MOSI)
  asn1r_strt_asn1_bld (aCtx, cosp_buf,cosp_msgsize);
#else
  asn1r_strt_asn1_bld (aCtx, acse_buf,acse_msgsize);
#endif
    
  asn1r_strt_constr (aCtx);		/* Start RLRE APDU constructor		*/
  /* Write user_info.							*/
  if (info->user_info.len)
    {
    asn1r_strt_constr (aCtx);
#if defined (MOSI)
    acse_encode_user_info (aCtx, acse_conn, &info->user_info);
#else	/* !MOSI	*/
    asn1r_wr_octstr (aCtx, info->user_info.ptr, info->user_info.len);
    /* No P-Contexts in this implementation, so skip transfer-syntax	*/
    /* and p-context-id.						*/
#endif	/* !MOSI	*/
    asn1r_fin_constr (aCtx, RLRE_user_info,CTX,DEF);
    }
  
  /* Write Release Reason.						*/
  if (info->reason_pres)
    {
    asn1r_wr_i16 (aCtx, info->reason);
    asn1r_fin_prim (aCtx, RLRE_reason,CTX);
    }
    
  asn1r_fin_constr (aCtx, TAG_RLRE,APP,DEF);	/* Finish RLRE APDU	*/
  msg_ptr = ASN1_ENC_PTR (aCtx);	/* point to beginning of PDU.	*/
  msg_len = ASN1_ENC_LEN (aCtx);
  if (msg_len > acse_msgsize)
    return (E_ACSE_BUFFER_OVERFLOW);

#if defined(MOSI)
  acse_conn->apdu_ptr = msg_ptr;
  acse_conn->apdu_len = msg_len;
  /* NOTE: Only "affirmative" allowed because "Negotiated Release" not	*/
  /* supported by Session.						*/
  retcode = copp_rel_rsp_pos (acse_conn);
#else	/* !MOSI	*/
  /* Send Transport Data request.					*/
  retcode = tp4_data (acse_conn->tp4_conn_id, TP4_EOT, msg_len, (ST_CHAR *) msg_ptr);
#endif	/* !MOSI	*/
  if (retcode != SD_SUCCESS)
    return (E_ACSE_SEND_ERR);
  
  if (acse_conn->state == STATE_ARLSRSP_WAIT)
    acse_conn->state = STATE_IDLE;
  else					/* must be STATE_INIT_COLL	*/
    acse_conn->state = STATE_RLRE_WAIT;

  return (SD_SUCCESS);
  }

/************************************************************************/
/* a_abort_req								*/
/************************************************************************/
ST_RET a_abort_req (
	ST_LONG acse_conn_id,	/* ACSE's connection ID			*/
	ABRT_APDU *info)	/* ABRT_APDU info			*/
  {
ST_UCHAR *msg_ptr;
ST_UINT msg_len;
ACSE_CONN *acse_conn;
ASN1_ENC_CTXT localEncCtx;	/* For readability, use "aCtx" to access this.*/
ASN1_ENC_CTXT *aCtx = &localEncCtx;




  acse_conn = (ACSE_CONN *)acse_conn_id;

  ACSELOG_ENC2 ("A-ABORT.req:      acse_conn_id =0x%08X  user_conn_id =%4ld",
    acse_conn, acse_conn->user_conn_id);

  if (acse_conn == NULL)
    return (E_ACSE_INVALID_CONN_ID);	/* Invalid acse_conn_id		*/

  if (acse_conn->state == STATE_IDLE)	/* Waiting for T-DISCONNECT	*/
    return (E_ACSE_INVALID_STATE);	/* Invalid state for this operation*/

#ifndef MOSI
  if (acse_conn->state == STATE_TP4CONN_WAIT)
    {
    /* Don't free struct now. When get response, don't pass up to user	*/
    /* and free acse_conn then.						*/
    acse_conn->state = STATE_AARE_ABANDON;
    return (SD_SUCCESS);
    }

  if (acse_conn->state == STATE_AARE_WAIT)
    {
    /* Don't free struct now. When get response, don't pass up to user	*/
    /* and free acse_conn then.						*/
    acse_conn->state = STATE_AARE_ABANDON;
    return (SD_SUCCESS);
    }
#endif	/* !MOSI	*/

  /* Everything looks OK. Go ahead and encode request and send it.	*/
#if defined (MOSI)
  asn1r_strt_asn1_bld (aCtx, cosp_buf,cosp_msgsize);
#else
  asn1r_strt_asn1_bld (aCtx, acse_buf,acse_msgsize);
#endif
    
  asn1r_strt_constr (aCtx);		/* Start ABRT APDU constructor		*/
  /* Write user_info.							*/
  if (info->user_info.len)
    {
    asn1r_strt_constr (aCtx);
#if defined (MOSI)
    acse_encode_user_info (aCtx, acse_conn, &info->user_info);
#else	/* !MOSI	*/
    asn1r_wr_octstr (aCtx, info->user_info.ptr, info->user_info.len);
    /* No P-Contexts in this implementation, so skip transfer-syntax	*/
    /* and p-context-id.						*/
#endif	/* !MOSI	*/
    asn1r_fin_constr (aCtx, ABRT_user_info,CTX,DEF);
    }
  
  /* Write Abort Diagnostic.						*/
  if(info->diagnostic_pres == SD_TRUE)
    {
    asn1r_wr_i16 (aCtx, info->diagnostic);
    asn1r_fin_prim (aCtx, ABRT_diagnostic,CTX);
    }

  /* Write Abort Source.						*/
  asn1r_wr_i16 (aCtx, info->source);
  asn1r_fin_prim (aCtx, ABRT_source,CTX);
    
  asn1r_fin_constr (aCtx, TAG_ABRT,APP,DEF);	/* Finish ABRT APDU	*/
  msg_ptr = ASN1_ENC_PTR (aCtx);	/* point to beginning of PDU.	*/
  msg_len = ASN1_ENC_LEN (aCtx);
  if (msg_len > acse_msgsize)
    return (E_ACSE_BUFFER_OVERFLOW);




#if defined(MOSI)
  /* Send P-U-Abort Request.						*/
  acse_conn->apdu_ptr = msg_ptr;
  acse_conn->apdu_len = msg_len;
  acse_conn->state = STATE_IDLE;	/* COSP will free acse_conn.	*/
  if (copp_u_abort_req (acse_conn) != SD_SUCCESS)
    return (E_ACSE_SEND_ERR);
  /* CRITICAL: DO NOT access acse_conn after abort. It may have been	*/
  /*     freed by cosp_u_abort_req (called from copp_u_abort_req).	*/
#else	/* !MOSI	*/
  /* Send Transport Disconnect request.					*/
  tp4_disconnect (acse_conn->tp4_conn_id, msg_len, (ST_CHAR *) msg_ptr);
  acse_free_con (acse_conn);		/* Dump conn tracking struct	*/
#endif	/* !MOSI	*/
  
  return (SD_SUCCESS);
  }

/************************************************************************/
/* a_data_req - Send A-Data req.					*/
/************************************************************************/
ST_RET a_data_req (
	ST_LONG acse_conn_id,	/* ACSE's ID for this connection	*/
	ST_UINT data_len,
	ST_UCHAR *data)
  {
ACSE_CONN *acse_conn;

  acse_conn = (ACSE_CONN *)acse_conn_id;
  
#if defined(MOSI)
  if (data_len > acse_msgsize)
    {		/* Should never happen if ACSE user code is correct.	*/
    ACSELOG_ERR2 ("ACSE-ERROR: A-DATA.req: data len (%d) > max (%d)",
      data_len, acse_msgsize);
    return (E_ACSE_BUFFER_OVERFLOW);
    }
  /* Just copy data to end of "cosp_buf" & set apdu_ptr, apdu_len.	*/
  /* copp_data_req will add COPP encoding in front of this data.	*/
  acse_conn->apdu_ptr = cosp_buf + cosp_msgsize - data_len;
  acse_conn->apdu_len = data_len;
  memcpy (acse_conn->apdu_ptr, data, data_len);

  return (copp_data_req (acse_conn));
#else	/* !MOSI	*/
  return (tp4_data (acse_conn->tp4_conn_id, TP4_EOT, data_len,(ST_CHAR *)data));
#endif	/* !MOSI	*/
  }

#if defined (MOSI)
/************************************************************************/
/* acse_encode_user_info						*/
/************************************************************************/
static ST_VOID acse_encode_user_info (ASN1_ENC_CTXT *aCtx, ACSE_CONN *acse_conn, BUFFER *user_info)
  {
  asn1r_strt_constr (aCtx);			/* start EXTERNAL		*/
  asn1r_strt_constr (aCtx);			/* start single-ASN1-type	*/
  asn1r_wr_octstr (aCtx, user_info->ptr, user_info->len);
  asn1r_fin_constr (aCtx, 0,CTX,DEF);		/* finish single-ASN1-type	*/
  asn1r_wr_i8 (aCtx, acse_conn->pcimms);		/* PCI for MMS		*/
  asn1r_fin_prim (aCtx, INT_CODE,UNI);

  /* NOTE:  Transfer-syntax-name (OPTIONAL) not sent. Saves few bytes.	*/

  asn1r_fin_constr (aCtx, EXTERN_CODE,UNI,DEF);	/* finish EXTERNAL		*/
  return;
  }

#endif	/* MOSI	*/

#if !defined (MOSI)
/************************************************************************/
/*                      acse_free_con					*/
/*----------------------------------------------------------------------*/
/* This function will free the pointer to connection info.		*/
/* For MOSI, a slightly different "acse_free_con" function is		*/
/* defined in COSP????.c						*/
/************************************************************************/
ST_VOID acse_free_con (ACSE_CONN *acse_conn)
  {
  if (acse_conn->save_buf.len)
    M_FREE (MSMEM_ACSE_DATA, acse_conn->save_buf.ptr);
  M_FREE (MSMEM_ACSE_CONN, acse_conn);
  }
#endif	/* !MOSI	*/

/************************************************************************/
/* a_buffers_avail - Buffers available for sending A-Data req.		*/
/* This function returns the number of buffers available for sending	*/
/* A-Data.  This may be used, for example, when sending Information	*/
/* Reports, to make sure there are always enough buffers for sending	*/
/* responses to incoming requests.					*/
/*									*/
/* Parameters:								*/
/*	ST_LONG acse_conn_id	ACSE Connection ID			*/
/*									*/
/* Return:								*/
/*	ST_INT			# of ACSE transmit buffers available	*/
/************************************************************************/

#if defined(LEAN_T) ||  defined(REDUCED_STACK)

ST_INT a_buffers_avail (
	ST_LONG acse_conn_id)	/* ACSE's ID for this connection	*/
  {
#if defined(LEAN_T)
ACSE_CONN *acse_conn;

  acse_conn = (ACSE_CONN *)acse_conn_id;
  
  return (tp4_buffers_avail (acse_conn->tp4_conn_id));
#endif

#if defined(REDUCED_STACK)
  return (2);
#endif
  }

#endif

#if defined (MOSI)			/* Only MOSI uses events.	*/
#if defined(_WIN32)
/* Not used for DOS, QNX, or VXWORKS	*/
/************************************************************************/
/*			a_get_event_handles				*/
/* Returns a pointer to an array of (ST_EVENT_SEM) and sets		*/
/* (*pNumEntries) equal to the number of entries.			*/
/************************************************************************/
ST_EVENT_SEM a_get_event_handles (ST_INT *pNumEntries)
  {
static ST_EVENT_SEM EventArray [2];
ST_INT NumEntries = 0;
  
#if defined (TP4_ENABLED)
  /* If "hMainEvent" initialized, use it instead of default "hMMSEvent"*/
  if (hMainEvent)
    EventArray [NumEntries++] = hMainEvent;	/* CLNP thread event.	*/
  else
    EventArray [NumEntries++] = hMMSEvent;	/* driver event		*/
#endif
#if defined (TP0_ENABLED)
  EventArray [NumEntries++] = hTcpEvent;	/* TCP/IP sockets event	*/
#endif

  *pNumEntries = NumEntries;
  return (EventArray);
  }
#else /* !defined(_WIN32) */
/************************************************************************/
/*			a_get_event_handles_unix			*/
/* Returns a pointer to an array of handles and sets			*/
/* (*pNumEntries) equal to the number of entries.			*/
/* NOTE: The TP4_ENABLED option currently only works on "linux".	*/
/************************************************************************/
#if defined(TP4_ENABLED)
#if defined(linux)
extern int hPktSock;
#else
#error TP4 not supported on this platform
#endif
#endif
#if defined(TP0_ENABLED)
extern int pipe_to_main;	/* DEBUG: move to include	*/
#endif
int *a_get_event_handles_unix (ST_INT *pNumEntries)
  {
static int fd_array [2];
ST_INT NumEntries = 0;

#if defined(TP4_ENABLED)
  fd_array[NumEntries++] = hPktSock;
#endif
#if defined(TP0_ENABLED)
  fd_array[NumEntries++] = pipe_to_main;
#endif
  *pNumEntries = NumEntries;
  return (fd_array);
  }
#endif  /* !defined(_WIN32) */
#endif	/* MOSI	*/


	/*------------------------------------------------------*/
	/* 		Security functions 			*/
	/*------------------------------------------------------*/

/********************************************************************************/
/*			a_get_security_info					*/
/*------------------------------------------------------------------------------*/
/* Returns pointers to encryption control and partner's authentication info	*/
/* from the specified channel info. This functions should be called after	*/
/* connection indication was received to retrieve the security informations.	*/
/* After the connection terminates call the s_free_part_security_info() to free	*/
/* the allocated auth_value buf and cleanup the channel info.			*/
/* Parameters:									*/
/*   ST_LONG		  acse_conn_id	indirect ptr to access security info	*/
/*   S_SEC_PARAMS        *secParams	pointer where to  ret security info.	*/
/* Return:									*/
/*   ST_RET		SD_SUCCESS	if get operation succeeded, otherwise	*/
/*			SD_FAILURE	or other error.				*/
/********************************************************************************/
ST_RET a_get_security_info (ST_LONG acse_conn_id, S_SEC_SPARAMS *secParams)
{
ST_RET     ret = SD_SUCCESS;
ACSE_CONN *acse_conn;

  secParams->authInfo = NULL;		/* local auth info not stored		*/
  secParams->partAuthInfo = NULL;	/* partner auth info			*/
  secParams->encryptCtrl = NULL;	/* connection encryption info		*/

  if (acse_conn_id)
    {
    acse_conn= (ACSE_CONN *) acse_conn_id;
    if (acse_conn)
      {
      secParams->partAuthInfo = &acse_conn->part_auth_info;
      secParams->encryptCtrl = &acse_conn->encrypt_ctrl;
      }
    }
  else
    ret = SD_FAILURE;			/* cc should be valid in this call	*/

  return (ret);  
}

/********************************************************************************/
/*			a_free_security_info					*/
/*------------------------------------------------------------------------------*/
/* Releases authentication info resources.					*/
/* Parameters:									*/
/*   ACSE_AUTH_INFO	*authInfo	ptr to free the security resources	*/
/* Return:									*/
/*   ST_RET		SD_SUCCESS	if free operation succeeded		*/
/*			SD_FAILURE      otherwise				*/
/********************************************************************************/
ST_RET a_free_security_info (ACSE_AUTH_INFO *authInfo)
{
ST_RET ret = SD_SUCCESS;

  if (authInfo)
    {
    /* clear authentication info */
    /* if there is an existing auth_value buf pointer then we have to free it	*/
    if (authInfo->auth_pres && authInfo->mech_type != ACSE_AUTH_MECH_PASSWORD)
      if (authInfo->u.other_auth.auth_value.buf)
        {
        chk_free (authInfo->u.other_auth.auth_value.buf);
        authInfo->u.other_auth.auth_value.buf = NULL;
        authInfo->u.other_auth.auth_value.ptr = NULL;
        authInfo->u.other_auth.auth_value.len = 0;
        }
    authInfo->auth_pres = SD_FALSE;
    }
  
  return (ret);  
}


/********************************************************************************/
/*			a_free_part_security_info				*/
/*------------------------------------------------------------------------------*/
/* Releases partner authentication resources and resets the field in ACSE_CONN.	*/
/* Parameters:									*/
/*   ST_LONG		acse_conn_id	indirect ptr to access security info	*/
/* Return:									*/
/*   ST_RET		SD_SUCCESS	if free operation succeeded		*/
/*			SD_FAILURE      otherwise				*/
/********************************************************************************/
ST_RET a_free_part_security_info (ST_LONG acse_conn_id)
{
ST_RET     ret = SD_SUCCESS;
ACSE_CONN *acse_conn;

  if (acse_conn_id)
    {
    acse_conn= (ACSE_CONN *) acse_conn_id;
    if (acse_conn)
      ret = a_free_security_info (&acse_conn->part_auth_info);
    }
  return (ret);  
}

