/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/*	(c) Copyright Systems Integration Specialists Company, Inc.,	*/
/*	1997-2005 All Rights Reserved					*/
/*									*/
/* MODULE NAME : tp0callg.c						*/
/* PRODUCT(S)  : Lean-T Stack						*/
/*									*/
/* MODULE DESCRIPTION :							*/
/*	TP0 functions for handling of "Calling" side of connections.	*/
/*									*/
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE :				*/
/*	tp0_connect							*/
/*	tp0_process_cc							*/
/*									*/
/* MODIFICATION LOG :							*/
/*  Date     Who   Rev     Comments					*/
/* --------  ---  ------   -------------------------------------------  */
/* 11/12/08  MDE    15     Added tpdu_cc->sock_info->user_conn_id check */
/* 07/30/08  JRB    14     Use DR reason codes valid for TP0.		*/
/* 05/06/05  EJV    13     Added port param to np_connect_req call.	*/
/* 03/29/05  EJV    12     Added MMSEASE_MOSI support.			*/
/* 09/17/04  JRB    11     Chg tp0_connect to return connection ID.	*/
/* 02/02/04  JRB    10     tp0_connect: fix tp_type, bind_id checks.	*/
/* 07/30/03  EJV    09     Added encrypt_ctrl to tp0_connect.		*/
/* 04/07/03  JRB    08     Add SPDU_RX_NO_PREALLOCATE option.		*/
/* 06/20/02  JRB    07     Pass ip addr as ulong to np_connect_req.	*/
/* 06/08/99  JRB    06     Chg to support 1024 connections.		*/
/* 08/13/98  JRB    05     Lint cleanup.				*/
/* 06/17/98  JRB    04     Increment loc_ref_offset before encoding CR	*/
/*			   to avoid sending duplicate CR.		*/
/* 12/04/97  JRB    03     Don't chk TSAPs on CC (may not be sent).	*/
/* 09/25/97  JRB    02     Don't generate exception for invalid state.	*/
/* 08/01/97  JRB    01     Created (compatible with MMS-EASE 7.0).	*/
/************************************************************************/
static char *thisFileName = __FILE__;	/* For TP4_EXCEPT and slog macros*/

#include "glbtypes.h"
#include "sysincs.h"
#include "mem_chk.h"
#include "tp4api.h"		/* User definitions for tp4	*/
#include "tp4.h"		/* Internal definitions for tp4	*/
#include "tp4_encd.h"		/* Encode/decode defs for tp4	*/
#include "tp4_log.h"

/************************************************************************/
/*			tp0_connect					*/
/* This function is called by the user to initiate a connect request to	*/
/* a remote node							*/
/* RETURNS: connection ID if successful, (-1) on error			*/
/* CRITICAL: connection ID = (conn_id+MIN_TP0_CONN_ID). Remember that	*/
/*	this is hoe we distinguish between TP0 and TP4 connections.	*/
/************************************************************************/

ST_LONG tp0_connect (ST_LONG tp0_bind_id, ST_LONG user_conn_id,
		     TP4_ADDR *rem_tp4_addr,
		     ST_INT conndata_len,
		     char *conndata,
                     S_SEC_ENCRYPT_CTRL *encrypt_ctrl)
  {
ST_UINT16 conn_id;		/* index into "tp0_conn_arr" array.		*/
TP0_CONN *tp_conn;
TPDU_CX *tpdu_cr;
ST_UCHAR *tp0_loc_tsap;

  if (rem_tp4_addr->tp_type == TP_TYPE_TCP)
    {
#if defined(MMSEASE_MOSI)
    if (tp0_check_bind_id (tp0_bind_id, &tp0_loc_tsap) != SD_SUCCESS)
#else
    tp0_loc_tsap = only_tp0_loc_tsap;
    if (tp0_bind_id != only_tp0_bind_id)
#endif
      {
      TP_LOG_ERR1 ("tp0_connect: bind id %ld invalid. TCP must be enabled in config.", tp0_bind_id);
      return (-1);
      }
    }    
  else		/* Transport type not TP0, this is error.	*/
    {
    TP_LOG_ERR0 ("tp0_connect: invalid transport type");
    return (-1);
    }

  if (rem_tp4_addr->tp4_sel_len > MAX_TSEL_LEN)
    {
    TP4_EXCEPT ();	/* If this happens, user code is invalid.	*/
    return (-1);
    }

  /* Look for available connection.	*/
  tp_conn = &tp0_conn_arr [0];
  for (conn_id = 0;  conn_id < tp0_cfg.max_num_conns;  conn_id++, tp_conn++)
    {
    if (tp_conn->state == TP_STATE_CLOSED)
      break;		/* Found one!!  "tp_conn" points to it now.	*/
    }	/* end "for"	*/
  if (conn_id >= tp0_cfg.max_num_conns)
    {			/* Didn't find one!!	*/
    TP_LOG_ERR0 ("TP-ERROR: tp0_connect: all connections in use");
    return (-1);
    }
  /* "tp_conn" now points to a connection in TP_STATE_CLOSED state.	*/    
  tp0_conn_clean (tp_conn);	/* Good time to clean up conn struct.	*/

  /* Increment "loc_ref_offset" to avoid duplicate CR.			*/
  inc_loc_ref_offset (tp_conn->loc_ref_offset);
  
  /* Set "tpdu_cr" pointing to "tp_conn->tpdu_cx" and fill in.		*/
  /* Info stored in "tp_conn" in case retransmissions needed.		*/
  tpdu_cr = &tp_conn->tpdu_cx;
  memcpy (tpdu_cr->loc_tsap, tp0_loc_tsap, tp0_loc_tsap [0]+1);
  tpdu_cr->rem_tsap [0] = (ST_UCHAR) rem_tp4_addr->tp4_sel_len;
  memcpy (&tpdu_cr->rem_tsap [1], rem_tp4_addr->tp4_sel, rem_tp4_addr->tp4_sel_len);
  tpdu_cr->loc_ref   = calc_loc_ref (conn_id, tp_conn->loc_ref_offset);
  tpdu_cr->rem_ref   = 0;
  tpdu_cr->max_tpdu_len_enc = tp0_cfg.max_tpdu_len_enc;
  tpdu_cr->preferred_class = 0;
  tpdu_cr->cdt       = 0;
  tpdu_cr->udata_len = min ((ST_UINT16) conndata_len, TP_MAX_CONN_UDATA);
  memcpy (tpdu_cr->udata_buf, conndata, tpdu_cr->udata_len);

  /* Set state before calling np_connect_req (may complete immediately).*/
  tp_conn->user_conn_id = user_conn_id;	/* Save User's ID for confirm.	*/
  tp_conn->state = TP_STATE_WFNC;
  if ((tp_conn->sock_info = np_connect_req ((ST_LONG) tp_conn, rem_tp4_addr->netAddr.ip,
                                            rem_tp4_addr->port, encrypt_ctrl)) == NULL)
    {
    tp_conn->state = TP_STATE_CLOSED;	/* CRITICAL: so tp_conn can be reused.*/
#if defined(SPDU_RX_NO_PREALLOCATE)
    M_FREE (MSMEM_STARTUP, tp_conn->spdu_rx.spdu_ptr);
#endif
    return (-1);	/* Network connect failed immediately.	*/
    }

  /* CRITICAL: For TP0, connection ID = (conn_id+MIN_TP0_CONN_ID).	*/
  return (conn_id+MIN_TP0_CONN_ID);
  }

/************************************************************************/
/* tp0_process_cc							*/
/************************************************************************/
ST_VOID tp0_process_cc (TPDU_CX *tpdu_cc)
  {
ST_ULONG conn_id;	/* index into "tp0_conn_arr" array (Unsigned so	*/
		/* no sign-extension on compare to tp0_cfg.max_num_conns).*/
TP0_CONN *tp_conn;	/* ptr to struct in "tp0_conn_arr" array.	*/
TPDU_DR tpdu_dr;
ST_INT unacceptable_cc = SD_FALSE;
ST_INT reason;


  /* Fill in TPDU_DR struct in case DR needs to be sent.		*/
  /* Do not fill in "reason" yet.					*/
  tpdu_dr.loc_ref = tpdu_cc->loc_ref;
  tpdu_dr.rem_ref = tpdu_cc->rem_ref;
  tpdu_dr.sock_info = tpdu_cc->sock_info;

  /* Check connection ID.	*/
  if (tpdu_cc->sock_info->user_conn_id == INVALID_CONN_ID)
    {
    /* Invalid ID means CR has not been sent, so this CC is unexpected.	*/
    /* CC most likely received from "abnormal" application.		*/
    TP_LOG_ERR0 ("TP-ERROR: tp0_process_cc: Invalid tp_conn");
    /* May be intentional attack, so disconnect immediately.		*/
    np_disconnect_req (tpdu_cc->sock_info);  
    return;  
    }

  /* Find the correct "tp_conn". It should have been saved in sock_info.*/
  tp_conn = (TP0_CONN *) tpdu_cc->sock_info->user_conn_id;


  conn_id = tp_conn - tp0_conn_arr;	/* conn_id = index into array	*/

  switch (tp_conn->state)
    {
    case TP_STATE_CLOSED:
      tpdu_dr.reason = TP_DR_NO_SESSION;	/* We're not trying to connect*/
      tp0_send_dr (&tpdu_dr);
      break;
    case TP_STATE_WFCC:
      /* Check for Transport Class 0.  Don't allow any other.		*/
      if (tpdu_cc->preferred_class != 0)
        {
        unacceptable_cc = SD_TRUE;
        }
      /* Save negotiated TPDU len in tp_conn->max_tpdu_len.	*/
      switch (tpdu_cc->max_tpdu_len_enc)
        {
        case TP_PDU_MAX_SIZE_128  :	tp_conn->max_tpdu_len =   128; break;
        case TP_PDU_MAX_SIZE_256  :	tp_conn->max_tpdu_len =   256; break;
        case TP_PDU_MAX_SIZE_512  :	tp_conn->max_tpdu_len =   512; break;
        case TP_PDU_MAX_SIZE_1024 :	tp_conn->max_tpdu_len =  1024; break;
        case TP_PDU_MAX_SIZE_2048 :	tp_conn->max_tpdu_len =  2048; break;
        case TP_PDU_MAX_SIZE_4096 :	tp_conn->max_tpdu_len =  4096; break;
        case TP_PDU_MAX_SIZE_8192 :	tp_conn->max_tpdu_len =  8192; break;
        case TP_PDU_MAX_SIZE_65531:	tp_conn->max_tpdu_len = 65531; break;
        /* Default to ILLEGAL value (i.e. 0)	*/
        default:			tp_conn->max_tpdu_len =     0; break;
        }	/* end "switch"	*/
      /* If max_tpdu_len ILLEGAL or ">" proposed, this is protocol error.*/
      if (tp_conn->max_tpdu_len == 0  ||		/* ILLEGAL	*/
          tp_conn->max_tpdu_len > tp0_cfg.max_tpdu_len)	/* ">" proposed	*/
        {
        tpdu_dr.reason = TP_DR_NO_REASON;
        unacceptable_cc = SD_TRUE;
        }

      if (unacceptable_cc)
        {	/* UN-ACCEPTABLE CLASS 0 CC TPDU.	*/
        reason = 0xff;			/* Local user error	*/
        tp4_disconnect_ind (tp_conn->user_conn_id, reason,
                            0, NULL);	/* No user data passed up.	*/
        np_disconnect_req (tp_conn->sock_info);
        tp_conn->state = TP_STATE_CLOSED;
#if defined(SPDU_RX_NO_PREALLOCATE)
        M_FREE (MSMEM_STARTUP, tp_conn->spdu_rx.spdu_ptr);
#endif
        }
      else
        {	/* ACCEPTABLE CLASS 0 CC TPDU.	*/
        /* Save "rem_ref" in "tp_conn".			*/
        tp_conn->rem_ref = tpdu_cc->rem_ref;

        /* CRITICAL: set state before tp4_connect_cnf because may	*/
        /* send first Transport data from within tp4_connect_cnf.	*/
        tp_conn->state = TP_STATE_OPEN;
        tp4_connect_cnf (tp_conn->user_conn_id, conn_id+MIN_TP0_CONN_ID, 
                        (ST_UINT) tpdu_cc->udata_len, tpdu_cc->udata_buf);
        tp_conn->state = TP_STATE_OPEN;
        }
      break;

    default:
      TP_LOG_ERR1 ("TP-ERROR: invalid state (%d) for received CC. Ignored.",
        tp_conn->state);
      break;
    }		/* end "switch"	*/
  return;
  }

