/*
 * 
 * (c) Copyright 1990 OPEN SOFTWARE FOUNDATION, INC.
 * (c) Copyright 1990 HEWLETT-PACKARD COMPANY
 * (c) Copyright 1990 DIGITAL EQUIPMENT CORPORATION
 * To anyone who acknowledges that this file is provided "AS IS"
 * without any express or implied warranty:
 *                 permission to use, copy, modify, and distribute this
 * file for any purpose is hereby granted without fee, provided that
 * the above copyright notices and this notice appears in all source
 * code copies, and that none of the names of Open Software
 * Foundation, Inc., Hewlett-Packard Company, or Digital Equipment
 * Corporation be used in advertising or publicity pertaining to
 * distribution of the software without specific, written prior
 * permission.  Neither Open Software Foundation, Inc., Hewlett-
 * Packard Company, nor Digital Equipment Corporation makes any
 * representations about the suitability of this software for any
 * purpose.
 * 
 */
/*
 */
/*
**
**  NAME
**
**      cncclsm.c
**
**  FACILITY:
**
**      Remote Procedure Call (RPC) 
**
**  ABSTRACT:
**
**  Client call State Machine for the Connection-based RPC runtime.
**
**
*/


#include <commonp.h>    /* Common declarations for all RPC runtime */
#include <com.h>        /* Common communications services */
#include <comprot.h>    /* Common protocol services */
#include <cnp.h>        /* NCA Connection private declarations */
#include <cnfbuf.h>     /* NCA Connection fragment buffer declarations */
#include <cnpkt.h>      /* NCA Connection protocol header */
#include <cnassoc.h>    /* NCA Connection association services */
#include <cnxfer.h>     /* NCA Connection buffered data transfer routines */
#include <cnsm.h>       /* NCA Connection state machine declarations */
#include <cncall.h>     /* NCA connection call service */
#include <cnclsm.h>


/******************************************************************************/
/*
 * Global Definitions
 */
#ifdef DEBUG
GLOBAL char     *rpc_g_cn_call_client_events [] =
{
    "TRANSMIT_REQ     ",
    "CONFIRM          ",
    "FAULT_DNE        ",
    "FAULT            ",
    "LOCAL_ALERT      ",
    "END              ",
    "ASSOC_ALLOC_ACK  ",
    "ASSOC_ALLOC_NAK  ",
    "START            ",
    "LAST_TRANSMIT_REQ",
    "LOCAL_ERROR      ",
    "ALERT_TIMEOUT    "
};

GLOBAL char     *rpc_g_cn_call_client_states [] =
{
    "INIT             ",
    "ASSOC_ALLOC_WAIT ",
    "STUB_WAIT        ",
    "REQUEST          ",
    "RESPONSE         ",
    "CALL_COMPLETED   ",
    "CALL_FAILED_DNE  ",
    "CALL_FAILED      "
};
#endif /* DEBUG */


/***********************************************************************/
/*
** C L I E N T   C A L L   P R E D I C A T E   T A B L E
**/
/*  
 * The predicates.
 * As a performance enhancement,
 * we have revamped many predicate routines as macros and have absorbed
 * the predicates into the actions.  Thus, there is no longer a need
 * for the predicate table;  the predicate declarations too, are
 * modified. 
 */
/* 
#define MAYBE_SEMANTICS_PRED    0
#define LAST_RECV_FRAG_PRED     1
*/ 

/***********************************************************************/
/*
** C L I E N T   C A L L   A C T I O N   T A B L E
**/

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

/*  
 * The actions.
 *
 * The QueueAlertTimeout action routine in the NCA CN arch spec
 * is not listed here since cancel timeouts are handled outside the
 * state machine. See the routine header of forward_alert_action_rtn
 * for more details.
 *
 * The VerifySecurity action routine in the NCA CN arch spec is not
 * listed here since this was embedded into existing action routines,
 * where appropriate in this implementation.
 */
#define TRANSMIT_REQ            0
#define HANDLE_RECV_FRAG        1
#define RAISE_FAULT             2
#define FORWARD_ALERT           3
#define ALLOCATE_ASSOC          4

/* abort send = send_orphaned + deallocate assoc + raise fault */
#define ABORT_SEND              5

#define ABORT_RECV              6
#define SEND_LAST_FRAG          7
#define PROTOCOL_ERROR          8

/*  
 * The Action routine prototypes.
 */
INTERNAL unsigned32     transmit_req_action_rtn( 
        pointer_t /*spc_struct*/, 
        pointer_t /*event_param*/,
        pointer_t /*sm*/
    );
INTERNAL unsigned32     handle_recv_frag_action_rtn( 
        pointer_t /*spc_struct*/, 
        pointer_t /*event_param*/,
        pointer_t /*sm*/
    );
INTERNAL unsigned32     raise_fault_action_rtn(
        pointer_t /*spc_struct*/, 
        pointer_t /*event_param*/,
        pointer_t /*sm*/
    );
INTERNAL unsigned32     forward_alert_action_rtn(  
        pointer_t /*spc_struct*/, 
        pointer_t /*event_param*/,
        pointer_t /*sm*/
    );
INTERNAL unsigned32     allocate_assoc_action_rtn( 
        pointer_t /*spc_struct*/, 
        pointer_t /*event_param*/,
        pointer_t /*sm*/
    );
INTERNAL unsigned32     abort_send_action_rtn(
        pointer_t /*spc_struct*/, 
        pointer_t /*event_param*/,
        pointer_t /*sm*/
    );
INTERNAL unsigned32     abort_recv_action_rtn(
        pointer_t /*spc_struct*/, 
        pointer_t /*event_param*/,
        pointer_t /*sm*/
    );
INTERNAL unsigned32     send_last_frag_action_rtn( 
        pointer_t /*spc_struct*/, 
        pointer_t /*event_param*/,
        pointer_t /*sm*/
    );

/*  
 * The action table itself.
 */
GLOBAL rpc_cn_sm_action_fn_t  rpc_g_cn_client_call_action_tbl [] =
{
    transmit_req_action_rtn,
    handle_recv_frag_action_rtn,
    raise_fault_action_rtn,
    forward_alert_action_rtn,
    allocate_assoc_action_rtn,
    abort_send_action_rtn,
    abort_recv_action_rtn,
    send_last_frag_action_rtn,
    rpc__cn_call_sm_protocol_error 
};

/***********************************************************************/
/*
** C L I E N T   C A L L   S T A T E   T A B L E
**/

INTERNAL rpc_cn_sm_state_tbl_entry_t init_state =

    /* state 0 - init */
    {
        ILLEGAL_TRANSITION,                 /* event 0 */
        ILLEGAL_TRANSITION,                 /* event 1 */
        ILLEGAL_TRANSITION,                 /* event 2 */
        ILLEGAL_TRANSITION,                 /* event 3 */
        ILLEGAL_TRANSITION,                 /* event 4 */
        { RPC_C_CLIENT_CALL_CFDNE },	    /* event 5 - call_end */
        ILLEGAL_TRANSITION,                 /* event 6 */
        ILLEGAL_TRANSITION,                 /* event 7 */
        { ALLOCATE_ASSOC },  		    /* event 8 - start_call */
        ILLEGAL_TRANSITION,                 /* event 9 */
        ILLEGAL_TRANSITION,                 /* event 10 */
        ILLEGAL_TRANSITION                  /* event 11 */
    };

    /* state 1 - assoc_alloc_wait */
INTERNAL rpc_cn_sm_state_tbl_entry_t assoc_alloc_wait_state =
    {
        ILLEGAL_TRANSITION,                 /* event 0 */
        ILLEGAL_TRANSITION,                 /* event 1 */
        ILLEGAL_TRANSITION,                 /* event 2 */
        ILLEGAL_TRANSITION,                 /* event 3 */
        ILLEGAL_TRANSITION,                 /* event 4 */
        { RPC_C_CLIENT_CALL_CFDNE }, 	    /* event 5 - call_end */
        { RPC_C_CLIENT_CALL_STUB_WAIT },    /* event 6 - alloc_assoc_ack */
       	{ RPC_C_CLIENT_CALL_CFDNE },        /* event 7 - alloc_assoc_nak */
        ILLEGAL_TRANSITION,                 /* event 8 */
        ILLEGAL_TRANSITION,                 /* event 9 */
        ILLEGAL_TRANSITION,                 /* event 10 */
        ILLEGAL_TRANSITION                  /* event 11 */
    };

    /* state 2 - stub_wait */
INTERNAL rpc_cn_sm_state_tbl_entry_t stub_wait_state =
    {
        { TRANSMIT_REQ },    	            /* event 0 - transmit_req */
        ILLEGAL_TRANSITION,                 /* event 1 */
        ILLEGAL_TRANSITION,                 /* event 2 */
        ILLEGAL_TRANSITION,                 /* event 3 */
        ILLEGAL_TRANSITION,                 /* event 4 */
        { RPC_C_CLIENT_CALL_CFDNE },        /* event 5 - call_end */
        ILLEGAL_TRANSITION,                 /* event 6 */
        ILLEGAL_TRANSITION,                 /* event 7 */
        ILLEGAL_TRANSITION,                 /* event 8 */
        { SEND_LAST_FRAG },  	            /* event 9 - last_transmit_req */
        { RPC_C_CLIENT_CALL_CFDNE },        /* event 10 - local_err */
        ILLEGAL_TRANSITION,                 /* event 10 */
        ILLEGAL_TRANSITION                  /* event 11 */
    };

    /* state 3 - call_request */
INTERNAL rpc_cn_sm_state_tbl_entry_t call_request_state =
    {
        { TRANSMIT_REQ },		    /* event 0 - transmit_req */
        ILLEGAL_TRANSITION,                 /* event 1 */
        { RAISE_FAULT }, 		    /* event 2 - fault_dne */
        { RAISE_FAULT }, 		    /* event 3 - fault */
        { FORWARD_ALERT }, 		    /* event 4 - local alert */
        { ABORT_SEND },  		    /* event 5 - call_end */
        ILLEGAL_TRANSITION,                 /* event 6 */
        ILLEGAL_TRANSITION,                 /* event 7 */
        ILLEGAL_TRANSITION,                 /* event 8 */
        { SEND_LAST_FRAG },  	            /* event 9 - last_transmit_req */
        { ABORT_SEND }, 		    /* event 10 - local_err */
        { ABORT_SEND }  		    /* event 11 - alert timeout */
    };

    /* state 4 - call response */
INTERNAL rpc_cn_sm_state_tbl_entry_t call_response_state =
    {
        ILLEGAL_TRANSITION,                 /* event 0 */
        { HANDLE_RECV_FRAG }, 	            /* event 1 - rpc_conf */
        { RAISE_FAULT },  		    /* event 2 - fault_dne */
        { RAISE_FAULT }, 		    /* event 3 - fault */
        { FORWARD_ALERT },  	            /* event 4 - local alert */
        { ABORT_SEND },  		    /* event 5 - call_end */
        ILLEGAL_TRANSITION,                 /* event 6 */
        ILLEGAL_TRANSITION,                 /* event 7 */
        ILLEGAL_TRANSITION,                 /* event 8 */
        ILLEGAL_TRANSITION,                 /* event 9 */
        { ABORT_SEND },          	    /* event 10 - local_err */
        { ABORT_SEND }                      /* event 11 - alert timeout */
    };

    /* state 5 - call_completed */
INTERNAL rpc_cn_sm_state_tbl_entry_t call_completed_state =
    {
        ILLEGAL_TRANSITION,                 /* event 0 */
        ILLEGAL_TRANSITION,                 /* event 1 */
        ILLEGAL_TRANSITION,                 /* event 2 */
        ILLEGAL_TRANSITION,                 /* event 3 */
        { RPC_C_CLIENT_CALL_CALL_COMPLETED }, /* event 4 - local alert */
        { RPC_C_CLIENT_CALL_CALL_COMPLETED }, /* event 5 - call_end */
        ILLEGAL_TRANSITION,                 /* event 6 */
        ILLEGAL_TRANSITION,                 /* event 7 */
        ILLEGAL_TRANSITION,                 /* event 8 */
        ILLEGAL_TRANSITION,                 /* event 9 */
        ILLEGAL_TRANSITION,                 /* event 10 */
        ILLEGAL_TRANSITION                  /* event 11 */
    };

    /* state 6 - cfdne (call failed, did not execute) */
INTERNAL rpc_cn_sm_state_tbl_entry_t cfdne_state =
    {
        { RPC_C_CLIENT_CALL_CFDNE },        /* event 0 */
        ILLEGAL_TRANSITION,                 /* event 1 */
        ILLEGAL_TRANSITION,                 /* event 2 */
        ILLEGAL_TRANSITION,                 /* event 3 */
        { RPC_C_CLIENT_CALL_CFDNE },        /* event 4 - local alert */
        { RPC_C_CLIENT_CALL_CFDNE },        /* event 5 - call_end */
        ILLEGAL_TRANSITION,                 /* event 6 */
        ILLEGAL_TRANSITION,                 /* event 7 */
        ILLEGAL_TRANSITION,                 /* event 8 */
        { RPC_C_CLIENT_CALL_CFDNE },        /* event 9 */
        ILLEGAL_TRANSITION,                 /* event 10 */
        ILLEGAL_TRANSITION                  /* event 11 */
    };

    /* state 7 - call_failed */
INTERNAL rpc_cn_sm_state_tbl_entry_t call_failed_state =
    {
        { RPC_C_CLIENT_CALL_CALL_FAILED },  /* event 0 */
        ILLEGAL_TRANSITION,                 /* event 1 */
        ILLEGAL_TRANSITION,                 /* event 2 */
        ILLEGAL_TRANSITION,                 /* event 3 */
        { RPC_C_CLIENT_CALL_CALL_FAILED },  /* event 4 - local alert */
        { RPC_C_CLIENT_CALL_CALL_FAILED },  /* event 5 - call_end */
        ILLEGAL_TRANSITION,                 /* event 6 */
        ILLEGAL_TRANSITION,                 /* event 7 */
        ILLEGAL_TRANSITION,                 /* event 8 */
        { RPC_C_CLIENT_CALL_CALL_FAILED },  /* event 9 */
        ILLEGAL_TRANSITION,                 /* event 10 */
        ILLEGAL_TRANSITION                  /* event 11 */
    };


GLOBAL rpc_cn_sm_state_entry_p_t rpc_g_cn_client_call_sm [] =
{
    init_state,                     /* state 0 - init */
    assoc_alloc_wait_state,         /* state 1 - assoc_alloc_wait */
    stub_wait_state,                /* state 2 - stub_wait */
    call_request_state,             /* state 3 - call_request */
    call_response_state,            /* state 4 - call_response */
    call_completed_state,           /* state 5 - call_completed */
    cfdne_state,                    /* state 6 - call failed, dne */
    call_failed_state               /* state 7 - call_failed */

};


/***********************************************************************/
/*
**
** C L I E N T   C A L L   P R E D I C A T E   R O U T I N E S
**
**/

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

/*
**++
**
**  MACRO NAME:		MAYBE_SEMANTICS_PRED        
**
**  SCOPE:              INTERNAL
**
**  DESCRIPTION:
**      
**  This is a macro version of maybe_semantics_pred_rtn, introduced  for
**  performance reasons.  The macro lets us avoid overhead associated with
**  calling the predicate routine from within the action routine.
**  Predicate macro is invoked from the Call Active State.
**
**  INPUTS:
**
**      spc_struct      The association group. Note that this is passed in as
**                      the special structure which is passed to the
**                      state machine event evaluation routine.
**
**      event_param     The special event related parameter which is
**                      passed to the state machine event evaluation
**                      routine.
**                      This input argument is ignored.
**
**	status		Instead of returning a value from the macro,
**			write the value calculated in the macro to
**			status.  Status' scope includes the routine
**			calling the macro.  Check status in the calling
**			routine to determine next state and in cases,
**			flow through the action routine. 
** 
**  INPUTS/OUTPUTS:     none
**
**  OUTPUTS:            
**
**	status		See explanation above.  
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     0 if MaybeSemantics is false
**                      1 if MaybeSemantics is true
**
**  SIDE EFFECTS:       none
**
**--
**/
#define MAYBE_SEMANTICS_PRED(spc_struct, event_param, status)	\
{\
    RPC_CN_DBG_RTN_PRINTF(CLIENT maybe_semantics_pred_macro);\
    header_p = (rpc_cn_packet_p_t) RPC_CN_CREP_SEND_HDR(\
        (rpc_cn_call_rep_p_t) spc_struct);\
    if ((RPC_CN_PKT_FLAGS (header_p) & RPC_C_CN_FLAGS_MAYBE) == 0)\
    {\
        status = 0;\
    }\
    else\
    {\
    	status = 1;\
    }\
}



/***********************************************************************/
/*
 * C L I E N T   C A L L   A C T I O N   R O U T I N E S
 */
/***********************************************************************/


/*
**++
**
**  ROUTINE NAME:       allocate_assoc_action_rtn
**
**  SCOPE:              INTERNAL
**
**  DESCRIPTION:
**      
**  Action routine to allocate an association from the current
**  association group.
**
**  INPUTS:
**
**      spc_struct      The call rep.  Note that this is passed in as
**                      the special structure which is passed to the
**                      state machine event evaluation routine.
**
**      event_param     The if_spec_rep.  This is passed in as the
**                      special event related parameter which was
**                      passed to the state machine evaluation routine.
**
**  INPUTS/OUTPUTS: 
**
**	sm              The control block from the event evaluation
**                      routine.  Input is the current state and
**                      event for the control block.  Output is the
**                      next state or updated current state, for the
**                      control block.
**
**  OUTPUTS:            none
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     completion status, one of:
**                      rpc_s_ok,
**
**  SIDE EFFECTS:       Either alloc_assoc_ack or alloc_assoc_nak
**                      event would be appended to the state
**                      machine event evaluation list.
**
**--
**/
INTERNAL unsigned32     allocate_assoc_action_rtn 
(
  pointer_t       spc_struct,
  pointer_t       event_param,
  pointer_t       sm
)
{
    rpc_cn_call_rep_p_t     call_rep_p;
    rpc_cn_sm_event_entry_t event_entry;
    unsigned32              status;
    rpc_cn_sm_ctlblk_t 	    *sm_p;
 
    RPC_CN_DBG_RTN_PRINTF(CLIENT allocate_assoc_action_rtn);

    call_rep_p = (rpc_cn_call_rep_p_t) spc_struct;

    /* 
     * Allocate the association.  Pass in the binding rep,
     * and interface spec rep and get back an association, it
     * negotiated transfer syntax and its context id.
     */
    if ((call_rep_p->assoc = rpc__cn_assoc_request 
         (call_rep_p,
          (rpc_cn_binding_rep_t *) call_rep_p->binding_rep,
          (rpc_if_rep_t *) event_param, 
          &call_rep_p->transfer_syntax,
          &call_rep_p->context_id, 
          &call_rep_p->sec,
          &status)) != NULL)
    {
        call_rep_p->max_seg_size = RPC_CN_ASSOC_MAX_XMIT_FRAG (call_rep_p->assoc);
        rpc__cn_assoc_push_call (call_rep_p->assoc, call_rep_p);

        event_entry.event_id = RPC_C_CALL_ALLOC_ASSOC_ACK;
        event_entry.event_param = (pointer_t) NULL;

        status = rpc_s_ok;
    }
    else
    {
        event_entry.event_id = RPC_C_CALL_ALLOC_ASSOC_NAK;
        event_entry.event_param = (pointer_t) NULL;

        /* 
         * We will return the status returned by assoc_request.
         * This status will be returned by the eval routine since
         * we will invoke no action routine when we transtion to
         * cfdne state.
         */
    }

    /* 
     * Insert the new event on the event queue for our state
     * machine.
     */
    rpc__cn_sm_insert_event (&event_entry, &(call_rep_p->call_state));
    sm_p = (rpc_cn_sm_ctlblk_t *)sm; 
    sm_p->cur_state = RPC_C_CLIENT_CALL_ASSOC_ALLOC_WAIT;
    return (status); 
}


/*
**++
**
**  ROUTINE NAME:       transmit_req_action_rtn
**
**  SCOPE:              INTERNAL
**
**  DESCRIPTION:
**      
**  Action routine to send the call request PDU(s) to the server.
**
**  INPUTS:
**
**      spc_struct      The call rep.  Note that this is passed in as
**                      the special structure which is passed to the
**                      state machine event evaluation routine.
**
**      event_param     The iovector describing the data to be sent.
**                      This is passed in as the special event related
**                      parameter passed to the state machine event
**                      evaluator.
**
**  INPUTS/OUTPUTS:     
**
**	sm              The control block from the event evaluation
**                      routine.  Input is the current state and
**                      event for the control block.  Output is the
**                      next state or updated current state, for the
**                      control block.
**
**  OUTPUTS:            none
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     rpc_s_ok if the send was completed successfully.
**
**  SIDE EFFECTS:       none
**
**--
**/

INTERNAL unsigned32     transmit_req_action_rtn 
(
  pointer_t       spc_struct,
  pointer_t       event_param,
  pointer_t       sm
)
{

    rpc_cn_call_rep_p_t     call_rep;
    rpc_iovector_p_t        stub_data_p;
    rpc_iovector_elt_p_t    iov_elt_p;
    unsigned32              i;
    unsigned32              status;
    boolean                 found_reusable;
    rpc_cn_sm_ctlblk_t	    *sm_p; 

    RPC_CN_DBG_RTN_PRINTF(CLIENT transmit_req_action_rtn);

    status = rpc_s_ok;

    call_rep = (rpc_cn_call_rep_p_t) spc_struct;
    stub_data_p = (rpc_iovector_p_t) event_param;
    sm_p = (rpc_cn_sm_ctlblk_t *)sm; 
    
    /*
     * We set call_executed to true at this point.
     * This is somewhat conservative; but it is correct.
     * We will reset call_executed if we get back a 
     * fault_dne.
     */
    call_rep->call_executed = true;

    /*
     * A call_transmit must have some stub data.  If this RPC
     * had no input arguments, then the first call should have
     * been a call_transceive with no stub data.
     */
#ifdef DEBUG
    if (stub_data_p->num_elt <= 0)
    {
        status = rpc_s_coding_error;
    }
    else
#endif
    {
        for (i = 0,
             found_reusable = false,
             iov_elt_p = stub_data_p->elt;   /* first iovector element */
             i < stub_data_p->num_elt;
             i++, iov_elt_p++)
        {
            /*
             * If the data_len is 0, just deallocate the iovector
             * element.
             */
            if (iov_elt_p->data_len == 0)
            {
                if (iov_elt_p->buff_dealloc != NULL)
                {
                    (iov_elt_p->buff_dealloc) (iov_elt_p->buff_addr);
                }
            }
            else 
            {
                /*
                 * If the number of bytes < our bcopy_lim,
                 * copy the data and deallocate the buffer.
                 * copy_buffer will automatically transfer the
                 * data if the accumulated byte count reaches
                 * the segment size.
                 */
                if (iov_elt_p->data_len <= RPC_C_CN_BCOPY_LIM)
                {
                    rpc__cn_copy_buffer (call_rep, iov_elt_p, &status);
                    if (iov_elt_p->buff_dealloc != NULL)
                    {
                        (iov_elt_p->buff_dealloc) (iov_elt_p->buff_addr);
                    }
                }
                else 
                {
                    /*
                     * If the buffer must be made immediately reusable, copy
                     * it also.
                     * Note that this can be optimized later so that we won't
                     * copy; just transmit the data; if certain criteria have
                     * been met.
                     */
                    if (iov_elt_p->flags & rpc_c_iovector_elt_reused)
                    {
                        rpc__cn_copy_buffer (call_rep, iov_elt_p, &status);
                        if (status != rpc_s_ok)
                        {
                            goto done;
                        }
                    }
                    else
                    {
#if 0
                        if (iov_elt_p->flags & rpc_c_iovector_elt_reused)
                        {
                            found_reusable = true;
                        }
#endif
                        /*
                         * Don't copy, add this buffer as a new iovector
                         * element.
                         * add_new_vector_elmt will automatically transfer the
                         * data if the accumulated byte count reaches
                         * the segment size.
                         */
                        rpc__cn_add_new_iovector_elmt (call_rep, iov_elt_p, &status);
                        if (status != rpc_s_ok)
                        {
                            goto done;
                        }
                    }
                }
            }
        }
    }

#if 0
    /*
     * Finally, if there is any buffered data on the call rep flush
     * any data that we'd have to copy if possible.
     */
    if (found_reusable)
    {
        rpc__cn_flush_buffers (call_rep, &status);
    }
#endif

done:
;
    sm_p->cur_state = RPC_C_CLIENT_CALL_REQUEST;
    return (status);
}


/*
**++
**
**  ROUTINE NAME:       send_last_frag_action_rtn
**
**  SCOPE:              INTERNAL
**
**  DESCRIPTION:
**      
**  Action routine to send the last call request fragment to the server.
**
**  INPUTS:
**
**      spc_struct      The call rep.  Note that this is passed in as
**                      the special structure which is passed to the
**                      state machine event evaluation routine.
**
**      event_param     The iovector describing the data to be sent.
**                      This is passed in as the special event related
**                      parameter passed to the state machine event
**                      evaluator.
**                      This parameter can be null for a transceive
**                      with no input arguments.
**
**  INPUTS/OUTPUTS:     
**
**	sm              The control block from the event evaluation
**                      routine.  Input is the current state and
**                      event for the control block.  Output is the
**                      next state or updated current state, for the
**                      control block.
**
**  OUTPUTS:            none
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     rpc_s_ok if the send was completed successfully.
**
**  SIDE EFFECTS:       none
**
**--
**/

INTERNAL unsigned32     send_last_frag_action_rtn 
(
  pointer_t       spc_struct,
  pointer_t       event_param,
  pointer_t       sm
)
{

    rpc_cn_call_rep_p_t     call_rep;
    rpc_iovector_p_t        stub_data_p;
    rpc_cn_packet_p_t       header_p;
    unsigned32              status;
    rpc_cn_sm_ctlblk_t	    *sm_p; 
    unsigned8		    n_state; 

    RPC_CN_DBG_RTN_PRINTF(CLIENT send_last_frag_action_rtn);

    sm_p = (rpc_cn_sm_ctlblk_t *)sm; 
    status = rpc_s_ok;
    call_rep = (rpc_cn_call_rep_p_t) spc_struct;
    header_p = (rpc_cn_packet_p_t) (RPC_CN_CREP_SEND_HDR (call_rep));
   
    /* 
     * Status contains the result of the macro.
     */  
    MAYBE_SEMANTICS_PRED(spc_struct, event_param, status);
    if (status == 0)  /* MaybeSemantics is false */  
    {
   	n_state = RPC_C_CLIENT_CALL_RESPONSE;  
    }
    else  /* MaybeSemantics is true */ 
    {
        n_state = RPC_C_CLIENT_CALL_CALL_COMPLETED;
    }

    stub_data_p = (rpc_iovector_p_t) event_param;

    /*
     * If there's stub data, we can process it just like a normal
     * call request.  This might leave data buffered.
     *
     * Note that the absence of stub data is indicated by either
     * a null iovector pointer or an iovector with 0 elements.
     */
    if ((stub_data_p != NULL) && (stub_data_p->num_elt > 0))
    {
   	/* 
	 * Note that since we are calling action routines from
	 * within action routines, we need to update state as
	 * a final step here.  Otherwise, the action routines
	 * would update sm->cur_state inappropriately for
	 * the calling routine.
	 */   
        status = 
		transmit_req_action_rtn (spc_struct, event_param, sm);
        if (status != rpc_s_ok)
        {
		sm_p->cur_state = n_state; 
		return (status);    
        }
    }

    /*
     * Set the last frag flag bit in the cached protocol header
     * and send it along with any buffered data.
     */
    RPC_CN_PKT_FLAGS (header_p) |= RPC_C_CN_FLAGS_LAST_FRAG;
    if (RPC_CN_CREP_ACC_BYTCNT (call_rep) >= RPC_CN_CREP_SIZEOF_HDR (call_rep))
    {
        rpc__cn_transmit_buffers (call_rep, &status);
        rpc__cn_dealloc_buffered_data (call_rep);

        /*
         * Set the length of the iov to 1.  We don't use the general
         * FREE_ALL_EXCEPT_PROT_HEADER macro since we won't be
         * using the iov again.
         */
        RPC_CN_CREP_IOVLEN (call_rep) = 1;
        if (status != rpc_s_ok)
        {
		sm_p->cur_state = n_state; 
		return (status);    
        }
    }
    else
    {
        /*
         * If the accumulated bytecount field is less than at
         * least that of the request header, something is really
         * off.
         */
	/*
	 * rpc_m_invalid_accbytcnt
	 * "(%s) Inconsistency in ACC_BYTCNT field"
	 */
	RPC_DCE_SVC_PRINTF ((
	    DCE_SVC(RPC__SVC_HANDLE, "%s"),
	    rpc_svc_cn_errors,
	    svc_c_sev_fatal | svc_c_action_abort,
	    rpc_m_invalid_accbytcnt,
	    "send_last_frag_action_rtn" ));
    }

    sm_p->cur_state = n_state; 
    return (rpc_s_ok);    

}


/*
**++
**
**  ROUTINE NAME:       handle_recv_frag_action_rtn
**
**  SCOPE:              INTERNAL
**
**  DESCRIPTION:
**      
**  Action routine to make the (received) fragment data available
**  to the stub for unmarshalling.
**
**  INPUTS:
**
**      spc_struct      The call rep.  Note that this is passed in as
**                      the special structure which is passed to the
**                      state machine event evaluation routine.
**
**      event_param     The fragment buffer containing the response
**                      message.  This is passed in as the special 
**                      event related parameter which was passed to
**                      the state machine event evaluation routine.
**
**  INPUTS/OUTPUTS:     
**
**	sm              The control block from the event evaluation
**                      routine.  Input is the current state and
**                      event for the control block.  Output is the
**                      next state or updated current state, for the
**                      control block.
**
**  OUTPUTS:            none
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     rpc_s_ok
**
**  SIDE EFFECTS:       Either a fault or fault_dne event may be
**                      appended to the state machine event list.
**
**--
**/
INTERNAL unsigned32     handle_recv_frag_action_rtn 
(
  pointer_t       spc_struct,
  pointer_t       event_param,
  pointer_t       sm
)
{
    rpc_cn_fragbuf_p_t      fragbuf;
    rpc_cn_packet_p_t       header_p;
    rpc_cn_call_rep_p_t     call_rep;
    rpc_cn_sm_ctlblk_t	    *sm_p;
    unsigned8		    n_state = 0;  
    
    RPC_CN_DBG_RTN_PRINTF(CLIENT handle_recv_frag_action_rtn);
    
    call_rep = (rpc_cn_call_rep_p_t) spc_struct;
    fragbuf = (rpc_cn_fragbuf_p_t) event_param;
    sm_p = (rpc_cn_sm_ctlblk_t *)sm; 
    header_p = (rpc_cn_packet_p_t) fragbuf->data_p;

    /*
     * We've got a proper response.  Adjust data_size to describe 
     * the stub data.
     * Note that we do not need to adjust data_p since that will
     * be done by rpc__cn_call_receive. 
     */
    fragbuf->data_size = RPC_CN_PKT_FRAG_LEN (header_p) -
                         RPC_CN_PKT_AUTH_TLR_LEN (header_p) -
                         RPC_CN_PKT_SIZEOF_RESP_HDR;


    /*
     * Determine whether this is the last response fragment.
     */
    if (RPC_CN_PKT_FLAGS (header_p) & RPC_C_CN_FLAGS_LAST_FRAG)
    {
        
        /*
	 * If RPC_CN_PKT_FLAGS & rpc_c_cn_flags_lastfrag
         * are 0, then set state to rpc_c_client_call
	 * response, else set state to rpc_c_client_call_
         * call_complete.
         */ 
        n_state = RPC_C_CLIENT_CALL_CALL_COMPLETED;
        /*
         * If there is a timer running stop it since we've heard from
         * the server.
         */
        rpc__cn_call_stop_cancel_timer (call_rep);
        
        /*
         * Record whether the server finished with a pending alert. Note
         * that the alert count in the packet does not include the
         * alert forwarded by setting the PFC_PENDING_ALERT bit in the
         * first fragment of the request.
         */
        if ((call_rep->u.client.cancel.server_count > RPC_CN_PKT_ALERT_COUNT (header_p)) ||
            (RPC_CN_PKT_FLAGS (header_p) & RPC_C_CN_FLAGS_ALERT_PENDING))
        {
            /*
             * Either the number of alerts forwarded by us is
             * greater than the number of alerts posted to the call
             * executor thread on the server OR the there was still an alert
             * pending in the call executor thread when the server
             * stub returned. In either case set the
             * server_had_pending flag in the call_rep to indicate an
             * alert should be posted to the client caller thread
             * before returning to the client stub.
             */
            call_rep->u.client.cancel.server_had_pending = true;
#ifdef DEBUG
            if (RPC_CN_PKT_FLAGS (header_p) & RPC_C_CN_FLAGS_ALERT_PENDING)
            {
                RPC_DBG_PRINTF (rpc_e_dbg_cancel, RPC_C_CN_DBG_CANCEL,
                                ("(handle_recv_frag_action_rtn) call_rep->%x alert pending flag is set in header\n", call_rep));
            }
            else
            {
                RPC_DBG_PRINTF (rpc_e_dbg_cancel, RPC_C_CN_DBG_CANCEL,
                ("(handle_recv_frag_action_rtn) call_rep->%x number alerts forwarded (%d) > alert count in header (%d)\n", 
                 call_rep,
                 call_rep->u.client.cancel.server_count,
                 RPC_CN_PKT_ALERT_COUNT (header_p)));
            }
#endif
        }
    }
    else
    {
	n_state = RPC_C_CLIENT_CALL_RESPONSE; 
    } 
    /*
     * We are currently executing in the receiver thread.
     *
     * If there is stub data, queue it on the association so that
     * the client call thread can get it.
     * If there is no stub data (e.g., no out arguments), just
     * deallocate the fragment buffer.
     * We make an exception for the first fragment; it is always
     * queued since the client call thread may already be blocked
     * on the condition variable.
     */
    if (fragbuf->data_size ||
        (RPC_CN_PKT_FLAGS (header_p) & RPC_C_CN_FLAGS_FIRST_FRAG))
    {
        rpc__cn_assoc_queue_frag (call_rep->assoc, fragbuf, true);
    }
    else
    {
        (* fragbuf->fragbuf_dealloc) (fragbuf);
    }

    /*
     * Increment num_pkts in the call rep. This will be used in
     * determining when to check for pending cancels.
     */
    call_rep->num_pkts++;
    
    sm_p->cur_state = n_state;
    return (rpc_s_ok);
}



/*
**++
**
**  ROUTINE NAME:       raise_fault_action_rtn
**
**  SCOPE:              INTERNAL
**
**  DESCRIPTION:
**      
**  Action routine to deallocate the current association and raise
**  fault.  Operationally, this will store the address of the 
**  fragment buffer in the callrep (for later retrieval via the
**  rpc__receive_fault).
**
**  INPUTS:
**
**      spc_struct      The call rep.  Note that this is passed in as
**                      the special structure which is passed to the
**                      state machine event evaluation routine.
**
**      event_param     The fault packet.  This is passed in as the
**                      event specific structure.
** 
**  INPUTS/OUTPUTS:     
**
**	sm              The control block from the event evaluation
**                      routine.  Input is the current state and
**                      event for the control block.  Output is the
**                      next state or updated current state, for the
**                      control block.
**
**  OUTPUTS:            none
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     rpc_s_call_faulted
**
**  SIDE EFFECTS:       none
**
**--
**/
INTERNAL unsigned32     raise_fault_action_rtn 
(
  pointer_t       spc_struct,
  pointer_t       event_param,
  pointer_t       sm
)
{
    rpc_cn_fragbuf_p_t      fragbuf;
    rpc_cn_packet_p_t       header_p;
    rpc_cn_call_rep_p_t     call_rep;
    rpc_cn_sm_ctlblk_t	    *sm_p; 

    RPC_CN_DBG_RTN_PRINTF(CLIENT raise_fault_action_rtn);

    call_rep = (rpc_cn_call_rep_p_t) spc_struct;
    fragbuf = (rpc_cn_fragbuf_p_t) event_param;
    header_p = (rpc_cn_packet_p_t) fragbuf->data_p;
    sm_p = (rpc_cn_sm_ctlblk_t *)sm;

    /*
     * We've got a proper response.  Adjust data_size to describe 
     * the stub data.
     * Note that we do not need to adjust data_p since that will
     * be done by rpc__cn_call_receive.
     */
    fragbuf->data_size = RPC_CN_PKT_FRAG_LEN (header_p) -
                         RPC_CN_PKT_AUTH_TLR_LEN (header_p) -
                         RPC_CN_PKT_SIZEOF_FAULT_HDR;

    /*
     * If there is a timer running stop it since we've heard from
     * the server.
     */
    rpc__cn_call_stop_cancel_timer (call_rep);

    /*
     * Determine whether this is the last fault fragment.
     */
    if (RPC_CN_PKT_FLAGS (header_p) & RPC_C_CN_FLAGS_LAST_FRAG)
    {
        /*
         * Record whether the server finished with a pending alert. Note
         * that the alert count in the packet does not include the
         * alert forwarded by setting the PFC_PENDING_ALERT bit in the
         * first fragment of the request.
         */
        if ((call_rep->u.client.cancel.server_count > RPC_CN_PKT_ALERT_COUNT (header_p)) ||
            (RPC_CN_PKT_FLAGS (header_p) & RPC_C_CN_FLAGS_ALERT_PENDING))
        {
            /*
             * Either the number of alerts forwarded by us is
             * greater than the number of alerts posted to the call
             * executor thread on the server OR the there was still an alert
             * pending in the call executor thread when the server
             * stub returned. In either case set the
             * server_had_pending flag in the call_rep to indicate an
             * alert should be posted to the client caller thread
             * before returning to the client stub.
             */
            call_rep->u.client.cancel.server_had_pending = true;
#ifdef DEBUG
            if (RPC_CN_PKT_FLAGS (header_p) & RPC_C_CN_FLAGS_ALERT_PENDING)
            {
                RPC_DBG_PRINTF (rpc_e_dbg_cancel, RPC_C_CN_DBG_CANCEL,
                               ("(raise_fault_action_rtn) call_rep->%x alert pending flag is set in header\n", call_rep));
            }
            else
            {
                RPC_DBG_PRINTF (rpc_e_dbg_cancel, RPC_C_CN_DBG_CANCEL,
                               ("(raise_fault_action_rtn) call_rep->%x number alerts forwarded (%d) > alert count in header (%d)\n",
                                call_rep,
                                call_rep->u.client.cancel.server_count,
                                RPC_CN_PKT_ALERT_COUNT (header_p)));
            }
#endif
        }
    }

    /*
     * We are currently executing in the receiver thread.
     *
     * If there is stub data, queue it on the association so that
     * the client call thread can get it.
     * If there is no stub data (e.g., no out arguments), just
     * deallocate the fragment buffer.
     * We make an exception for the first fragment; it is always
     * queued since the client call thread may already be blocked
     * on the condition variable.
     */
    if (fragbuf->data_size ||
        (RPC_CN_PKT_FLAGS (header_p) & RPC_C_CN_FLAGS_FIRST_FRAG))
    {
        rpc__cn_assoc_queue_frag (call_rep->assoc, fragbuf, true);
    }
    else
    {
        (* fragbuf->fragbuf_dealloc) (fragbuf);
    }

    /* 
     * There is no predicate associated with this routine but the
     * new value of sm->cur_state is determined by the value of
     * sm->cur_event coming into the routine.  Note that 
     * 2+statebase is event fault_dns;  3+statebase is event
     * fault.
     */  
    if (sm_p->cur_event == (2 + RPC_C_CN_STATEBASE )) 	
    	sm_p->cur_state =  RPC_C_CLIENT_CALL_CFDNE;
    else if (sm_p->cur_event == (3 + RPC_C_CN_STATEBASE ))	
	sm_p->cur_state = RPC_C_CLIENT_CALL_CALL_FAILED; 
    return (rpc_s_ok);
}


/*
**++
**
**  ROUTINE NAME:       forward_alert_action_rtn
**
**  SCOPE:              INTERNAL
**
**  DESCRIPTION:
**      
**  Action routine to forward an alert. The first alert that is
**  forwarded will start the alert timer. This timer will run until
**  either the call is completed or a reponse is received from the
**  server. If the timer expires the call is orphaned. The alert
**  timer setting, clearing and expiration handling is all done outside
**  the state machine action routines, primarily in the
**  rpc__cn_call_forward_cancel, rpc__cn_call_[start,stop]_cancel_timer
**  and rpc__cn_call_cancel_timer. All cancellable operations made in
**  the CN runtime are encompassed in cancel exception handlers.
**
**  INPUTS:
**
**      spc_struct      The call rep.  Note that this is passed in as
**                      the special structure which is passed to the
**                      state machine event evaluation routine.
**
**      event_param     The error_status to return. This is passed in 
**                      as the special event related parameter which 
**                      was passed to the state machine event 
**                      evaluation routine.
**
**  INPUTS/OUTPUTS:     
**
**	sm              The control block from the event evaluation
**                      routine.  Input is the current state and
**                      event for the control block.  Output is the
**                      next state or updated current state, for the
**                      control block.
**
**  OUTPUTS:            none
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     rpc_s_call_faulted
**
**  SIDE EFFECTS:       none
**
**--
**/
INTERNAL unsigned32     forward_alert_action_rtn 
(
  pointer_t       spc_struct,
  pointer_t       event_param __attribute((unused)),
  pointer_t       sm
)
{
    rpc_cn_call_rep_p_t         call_rep;
    rpc_cn_packet_p_t           header_p;
    struct
    {
        rpc_iovector_t          iov;
        rpc_iovector_elt_t      elt_1;
    } pdu;
    unsigned32                  status;
    unsigned8                   prev_ptype;
    rpc_cn_sm_ctlblk_t		*sm_p; 

    RPC_CN_DBG_RTN_PRINTF(CLIENT forward_alert_action_rtn);

    call_rep = (rpc_cn_call_rep_p_t) spc_struct;
    sm_p = (rpc_cn_sm_ctlblk_t *)sm; 
    header_p = (rpc_cn_packet_p_t) RPC_CN_CREP_SEND_HDR (call_rep);

    /*
     * The remote alert indication packet uses only the
     * common fields of the header.  We can therefore just
     * use the current header.
     */
    prev_ptype = RPC_CN_PKT_PTYPE (header_p);
    RPC_CN_PKT_PTYPE (header_p) = RPC_C_CN_PKT_REMOTE_ALERT;
    RPC_CN_PKT_FLAGS (header_p) |= RPC_C_CN_FLAGS_ALERT_PENDING;

    /*
     * If security was requested on this call an authentication
     * trailer will have to be added to the alert PDU.
     */
    if (call_rep->sec == NULL)
    {
        RPC_CN_PKT_FRAG_LEN (header_p) = RPC_CN_PKT_SIZEOF_ALERT_HDR;
        pdu.iov.num_elt = 1;
    }
    else
    {
        RPC_CN_PKT_FRAG_LEN (header_p) = 
            RPC_CN_PKT_SIZEOF_ALERT_HDR + 
            call_rep->prot_tlr->data_size - 
            RPC_CN_CREP_SIZEOF_TLR_PAD (call_rep);
        pdu.iov.num_elt = 2;
        pdu.elt_1.buff_dealloc = NULL;
        pdu.elt_1.data_addr = (byte_p_t) call_rep->prot_tlr->data_p;
        pdu.elt_1.data_len = 
            call_rep->prot_tlr->data_size - 
            RPC_CN_CREP_SIZEOF_TLR_PAD (call_rep);
    }
    
    /* 
     * Send the packet over.
     */
    pdu.iov.elt[0].buff_dealloc = NULL;
    pdu.iov.elt[0].data_addr = (byte_p_t) header_p;
    pdu.iov.elt[0].data_len = RPC_CN_PKT_SIZEOF_ALERT_HDR;
    rpc__cn_assoc_send_frag (call_rep->assoc, &pdu.iov, call_rep->sec, &status);
    
    /*
     * Restore the previous packet type.
     */
    RPC_CN_PKT_PTYPE (header_p) = prev_ptype;

    /*
     * Increment the count of forwarded cancels.
     */
    call_rep->u.client.cancel.server_count++;
    RPC_DBG_PRINTF (rpc_e_dbg_cancel, RPC_C_CN_DBG_CANCEL,
                ("(forward_alert_action_rtn) call_rep->%x forwarding cancel total so far = %d\n", 
                 call_rep,
                 call_rep->u.client.cancel.server_count));

    /* 
     * There is no predicate associated with this routine but the
     * new value of sm->cur_state is determined by the value of
     * sm->cur_state  coming into the routine.  In otherwords,
     * this action routine is called from 2 different states
     * and the value of that state determines the new value
     * for sm->cur_state.  Note that 3+statebase is call_request;  
     * 4+statebase is call_response.  rpc_c_cn_statebase is
     * set to 100 to distinguish it from action routine
     * indexes used in the rpc__cn_sm_event_eval() routine. 
     */  
    if (sm_p->cur_state == (3 + RPC_C_CN_STATEBASE))	
	sm_p->cur_state = RPC_C_CLIENT_CALL_REQUEST;
    else if (sm_p->cur_state == (4 + RPC_C_CN_STATEBASE))	
	sm_p->cur_state = RPC_C_CLIENT_CALL_RESPONSE; 
    return (status);
}


/*
**++
**
**  ROUTINE NAME:       abort_send_action_rtn
**
**  SCOPE:              INTERNAL
**
**  DESCRIPTION:
**      
**  Action routine to abort a send.
**  It sends an orphaned message, and then raises a fault by 
**  returning the error status back to the caller.
**
**  INPUTS:
**
**      spc_struct      The call rep.  Note that this is passed in as
**                      the special structure which is passed to the
**                      state machine event evaluation routine.
**
**      event_param     This parameter is ignored.  It is passed in 
**                      as the special event related parameter which 
**                      was passed to the state machine event 
**                      evaluation routine.
**
**  INPUTS/OUTPUTS:     
**
**	sm              The control block from the event evaluation
**                      routine.  Input is the current state and
**                      event for the control block.  Output is the
**                      next state or updated current state, for the
**                      control block.
**
**  OUTPUTS:            rpc_s_call_faulted
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     error_status reflecting the fault
**
**  SIDE EFFECTS:       none
**
**--
**/
INTERNAL unsigned32     abort_send_action_rtn 
(
  pointer_t       spc_struct,
  pointer_t       event_param __attribute((unused)),
  pointer_t       sm
)
{
    rpc_cn_call_rep_p_t         call_rep;
    rpc_cn_packet_p_t           header_p;
    unsigned32                  status;
    rpc_cn_sm_ctlblk_t		*sm_p;

    RPC_CN_DBG_RTN_PRINTF(CLIENT abort_send_action_rtn);

    call_rep = (rpc_cn_call_rep_p_t) spc_struct;
    sm_p = (rpc_cn_sm_ctlblk_t *)sm;

    /*
     * The call is going to be orphaned. The stub data bufferred
     * on the call rep will not be sent and can be released.
     */
    rpc__cn_dealloc_buffered_data (call_rep);
    RPC_CN_FREE_ALL_EXCEPT_PROT_HDR (call_rep);

    /*
     * Now prepare to send an orphaned packet to the server.
     */
    header_p = (rpc_cn_packet_p_t) RPC_CN_CREP_SEND_HDR(call_rep);
    RPC_CN_PKT_PTYPE (header_p) = RPC_C_CN_PKT_ORPHANED;
    RPC_CN_PKT_FLAGS (header_p) |= RPC_C_CN_FLAGS_LAST_FRAG;
    RPC_DBG_PRINTF (rpc_e_dbg_orphan, RPC_C_CN_DBG_ORPHAN,
                    ("(abort_send_action_rtn) call_rep->%x sending orphan packet ... call id = %x\n", 
                     call_rep,
                     RPC_CN_PKT_CALL_ID (header_p)));

    /*
     * If security was requested on this call an authentication
     * trailer will have to be added to the orphan PDU.
     */
    if (call_rep->sec == NULL)
    {
        RPC_CN_PKT_FRAG_LEN (header_p) = RPC_CN_PKT_SIZEOF_ORPHANED_HDR;
        RPC_CN_CREP_IOVLEN (call_rep) = 1;
    }
    else
    {
        RPC_CN_PKT_FRAG_LEN (header_p) =
            RPC_CN_PKT_SIZEOF_ORPHANED_HDR +
            call_rep->prot_tlr->data_size - 
            RPC_CN_CREP_SIZEOF_TLR_PAD (call_rep);
        RPC_CN_CREP_IOVLEN (call_rep) = 2;
        RPC_CN_CREP_IOV (call_rep)[1].data_addr = (byte_p_t) call_rep->prot_tlr->data_p;
        RPC_CN_CREP_IOV (call_rep)[1].data_len =
            call_rep->prot_tlr->data_size - 
            RPC_CN_CREP_SIZEOF_TLR_PAD (call_rep);
        RPC_CN_CREP_IOV (call_rep)[1].buff_dealloc = NULL;
    }

    /* 
     * Send the packet over.  Note that the returned status is
     * ignored.
     */
    RPC_CN_CREP_IOV (call_rep)[0].data_addr = (byte_p_t) header_p;
    RPC_CN_CREP_IOV (call_rep)[0].data_len = RPC_CN_PKT_SIZEOF_ORPHANED_HDR;
    rpc__cn_assoc_send_frag (call_rep->assoc, 
                             &(call_rep->buffered_output.iov), 
                             call_rep->sec,
                             &status);
    /*
     * Now return to the caller (presumably rpc__cn_call_end) which
     * will deallocate the association on our end and clean up.
     */
    sm_p->cur_state = RPC_C_CLIENT_CALL_CALL_FAILED;
    return (rpc_s_ok);
}

/*
**++
**
**  ROUTINE NAME:       abort_recv_action_rtn
**
**  SCOPE:              INTERNAL
**
**  DESCRIPTION:
**      
**  Action routine to abort a receive.
**
**  INPUTS:
**
**      spc_struct      The call rep.  Note that this is passed in as
**                      the special structure which is passed to the
**                      state machine event evaluation routine.
**
**      event_param     The fault data.
**                      This is passed in as the special event related
**                      parameter which was passed to the state machine 
**                      event evaluation routine.
**
**  INPUTS/OUTPUTS:     
**
**	sm              The control block from the event evaluation
**                      routine.  Input is the current state and
**                      event for the control block.  Output is the
**                      next state or updated current state, for the
**                      control block.
**
**  OUTPUTS:            none
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     completion status
**                          rpc_s_call_faulted
**
**  SIDE EFFECTS:       none
**
**--
**/
INTERNAL unsigned32     abort_recv_action_rtn 
(
  pointer_t       spc_struct,
  pointer_t       event_param,
  pointer_t       sm
)
{
    unsigned32      status;
    rpc_cn_sm_ctlblk_t *sm_p;

    RPC_CN_DBG_RTN_PRINTF(CLIENT abort_recv_action_rtn);
    sm_p = (rpc_cn_sm_ctlblk_t *)sm; 
    /* 
     * Note that we are getting state from raise_fault_action_rtn().  Also
     * note that it does not seem that we are actually using abort_recv_
     * action_rtn in the state tables.
     *   
     * Note, we don't need to chase down our receiver thread.
     * We will shortly deallocate the association.  The receiver
     * thread will automatically discard fragments for non-existent
     * associations.
     *
     * We make this a separate action routine (instead of using
     * raise_fault_action_rtn) to leave room for future optimizations.
     */

    /*
     * Abort the association.
     */
    rpc__cn_assoc_abort (((rpc_cn_call_rep_p_t) spc_struct)->assoc, &status);

    /* 
     * Raise the fault.
     */
    return (raise_fault_action_rtn (spc_struct, event_param, sm ));
}


/*
**++
**
**  ROUTINE NAME:       rpc__cn_call_sm_protocol_error
**
**  SCOPE:              PRIVATE
**
**  DESCRIPTION:
**
**  Action routine invoked when an illegal transition is detected.
**  This routine writes an error message to stdout and DIEs.
**
**  INPUTS:
**
**      spc_struct      The special structure which is passed to the
**                      state machine event evaluation routine.
**                      This is assumed to be the call rep.
**
**      event_param     The event specific argument.
**
**  INPUTS/OUTPUTS:     none
**
**	sm		The control block from the event evaluation
**			routine.  Input is the current state and
**			event for the control block.  Output is the
**			next state or updated current state, for the 
**			control block.
**
**  OUTPUTS:            none
**
**  IMPLICIT INPUTS:    none
**
**  IMPLICIT OUTPUTS:   none
**
**  FUNCTION VALUE:     unsigned32
**
**  SIDE EFFECTS:       output is printed on stdout.
**
**--
**/
PRIVATE unsigned32     rpc__cn_call_sm_protocol_error 
(
  pointer_t       spc_struct,
  pointer_t       event_param __attribute((unused)),
  pointer_t       sm 
)
{
    rpc_cn_call_rep_p_t call_rep;
    rpc_cn_sm_ctlblk_t 	    *sm_p;

    RPC_CN_DBG_RTN_PRINTF(rpc__cn_call_sm_protocol_error);
    
    sm_p = (rpc_cn_sm_ctlblk_t *)sm; 
    sm_p->cur_state = RPC_C_SM_NO_NSTATE;
 
    call_rep = (rpc_cn_call_rep_p_t) spc_struct;
    
    /*
     * "Illegal state transition detected in CN {client|server} call state
     * machine [cur_state: %d, cur_event: %d, call_rep: %x]"
     */
    RPC_DCE_SVC_PRINTF ((
	DCE_SVC(RPC__SVC_HANDLE, "%d%d%x"),
	rpc_svc_cn_state,
	svc_c_sev_fatal | svc_c_action_abort,
        RPC_CALL_IS_SERVER( (rpc_call_rep_t *)call_rep ) ?
            rpc_m_cn_ill_state_trans_sr : rpc_m_cn_ill_state_trans_cr,
        call_rep->call_state.cur_state,
        call_rep->call_state.cur_event,
        call_rep ));

	return (0);
}
