Files
microser/mmslib/ositcps/tp0main.c
2026-06-15 15:48:16 +08:00

1174 lines
42 KiB
C
Raw Blame History

/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/* (c) Copyright Systems Integration Specialists Company, Inc., */
/* 1997-2005 All Rights Reserved */
/* */
/* MODULE NAME : tp0main.c */
/* PRODUCT(S) : Lean-T Stack */
/* */
/* MODULE DESCRIPTION : */
/* Main processsing functions for TP0. */
/* */
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE : */
/* tp0_initialize () */
/* tp0_terminate () */
/* tp0_bind () */
/* tp0_unbind () */
/* tp0_session_timer () */
/* tp0_timer_tick () */
/* tp0_buffers_avail () */
/* tp0_event () */
/* tp0_data () */
/* tp0_disconnect () */
/* tp0_process_dt () */
/* tp0_process_dr () */
/* */
/* MODIFICATION LOG : */
/* Date Who Rev Comments */
/* -------- --- ------ ------------------------------------------- */
/* 07/30/08 JRB 27 Use DR reason codes valid for TP0. */
/* 06/23/06 JRB 26 Fix calc_loc_ref problem (conn_id wrong type)*/
/* 06/06/05 EJV 25 MMSEASE_MOSI: add rfc1006_listener_* funcs. */
/* 04/06/05 EJV 24 Added MMSEASE_MOSI support. */
/* tp0_terminate: check for valid tp0_conn_arr. */
/* 03/11/05 JRB 23 Ignore DT.ind or DR.ind if waiting for CR.ind*/
/* 02/22/05 JRB 22 tp0_data: fix return value. */
/* 01/24/05 JRB 21 Move tp4_debug_sel & "*_logstr" to lean_var.c*/
/* 09/17/04 JRB 20 tp0_session_timer: chk state only if starting*/
/* Use session_cfg.disconnect_timeout. */
/* 01/13/04 EJV 19 tp0_initialize: set tp0_cfg.max_spdu_outst. */
/* Changed tp0_send_... funcs to return value. */
/* np_data_req: added eot param. */
/* 04/07/03 JRB 18 Add SPDU_RX_NO_PREALLOCATE option. */
/* 07/10/02 JRB 17 tp0_buffers_avail return INT_MAX (no limit). */
/* 06/20/02 JRB 16 Add tp0_convert_ip function. */
/* 05/08/02 JRB 15 Add tp0_connect_outstanding_count function. */
/* 01/22/02 JRB 14 Add max_conns arg to tp0_initialize. */
/* 05/25/01 JRB 13 Fix thisFile.. warning. */
/* 03/15/00 JRB 12 Alloc tp_conn->spdu_rx.spdu_ptr once at */
/* startup for each conn. Free before exit. */
/* 03/15/00 JRB 11 If spdu_rx exceeds max, disconnect. */
/* 01/05/01 EJV 10 tp0_initialize: corrected logging. */
/* 03/14/00 JRB 09 tp0_terminate: fake DR.ind so stack cleans up*/
/* 09/13/99 MDE 08 Added SD_CONST modifiers */
/* 06/08/99 JRB 07 Chg to support 1024 connections. */
/* 08/13/98 JRB 06 Lint cleanup. */
/* 03/23/98 NAV 05 Free g_tpkt_enc_buf on termination */
/* 02/16/98 JRB 04 Cast args. */
/* 09/25/97 JRB 03 Don't generate exception for invalid state. */
/* 09/23/97 JRB 02 tp0_terminate: disconnect all TP0 conns. */
/* 08/01/97 JRB 01 Created (compatible with MMS-EASE 7.0). */
/************************************************************************/
#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"
#include "tp4_log.h"
#ifdef DEBUG_SISCO
SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__;
#endif
/************************************************************************/
/* Local function Prototypes */
/************************************************************************/
/************************************************************************/
/* Global Configuration Structure */
/* This structure must be filled in by user BEFORE tp0_initialize (). */
/* It MUST NOT change after tp0_initialize (). */
/* DEBUG: should tp0_initialize make it's own copy of this struct */
/* so that the user CAN NOT screw it up????? */
/************************************************************************/
TP0_CFG tp0_cfg;
/************************************************************************/
/* Global Variables initialized by tp0_initialize (). */
/* Not accessible to user. */
/************************************************************************/
TP0_CONN *tp0_conn_arr; /* ptr to array of "max_num_conns" structs */
ST_UCHAR *g_tpkt_enc_buf; /* Buffer for encoding TPKTs. */
static ST_UINT g_tp0_max_spdu_len; /* Max SPDU size to send or receive */
/************************************************************************/
/* Global Variables initialized by tp0_bind (). */
/* Not accessible to user. */
/************************************************************************/
#if defined(MMSEASE_MOSI)
/* multiple binds allowed, tp0_bind_id = index into the bind_tsel_tbl */
typedef struct tagBIND_TSEL
{
ST_LONG user_bind_id; /* = session bind id */
ST_UCHAR tsel [1+MAX_TSEL_LEN]; /* Local len & TSEL */
} BIND_TSEL;
ST_UINT bind_tsel_tbl_max; /* for now use max_conns */
BIND_TSEL *bind_tsel_tbl; /* entry 0 unused! */
#else /* !defined(MMSEASE_MOSI) */
/* DEBUG: should we check tp0_bind_called before any calls to user? */
static ST_INT tp0_bind_called; /* Flag to prevent multiple bind calls. */
/* CRITICAL: only_tp0_bind_id must be different from only_tp4_bind_id, */
/* because either may be passed up to user in tp4_bind_cnf. */
ST_LONG only_tp0_bind_id = 199; /* Only one tp0_bind_id. */
ST_LONG only_tp0_user_bind_id; /* Only one user_bind_id. */
ST_UCHAR only_tp0_loc_tsap [1+MAX_TSEL_LEN]; /* Local len & TSAP addr*/
#endif /* !defined(MMSEASE_MOSI) */
/*======================================================================*/
/*======================================================================*/
/* INITIALIZATION AND TERMINATION FUNCTIONS */
/*======================================================================*/
/*======================================================================*/
/************************************************************************/
/* tp0_initialize */
/* Initialize the TP0 service provider. */
/* NOTE: the configuration parameters in the "tp0_cfg" struct must be */
/* set BEFORE calling this function. */
/* WARNING: tp0_cfg MUST NOT be modified after this function called. */
/* NOTE: the value of tp0_cfg.max_num_conns is overwritten with the */
/* value passed in the argument max_conns. */
/* Return: */
/* SD_SUCCESS or */
/* error code (TP4E_* or error code returned from np_init) */
/************************************************************************/
ST_RET tp0_initialize (ST_UINT max_conns, ST_UINT max_spdu_len)
{
ST_RET errcode;
ST_UINT16 max_tpkt_len; /* Max len of RFC1006 TPKT. */
ST_UINT j;
if (max_conns > TP_MAX_NUM_CONNS)
{
TP_LOG_ERR1 ("TP-ERROR: number of requested TCP connections (%d) exceeds max",
max_conns);
return (TP4E_INVAL_NUM_CONNS);
}
tp0_cfg.max_num_conns = max_conns; /* overwrite configured value */
#if !defined (TP4_ENABLED) /* Otherwise, done in tp4_initialize */
#if defined(DEBUG_SISCO)
tp4_debug_sel |= TP4_LOG_ERR; /* Always enable error logging. */
#endif
tp4_init_timer ();
#endif /* !TP4_ENABLED */
/* Save arg in global for later use. */
g_tp0_max_spdu_len = max_spdu_len;
/* Check that all configured parameter values are legal. */
/* init the max number of SPDUs that can be queued in gensock2 */
if (tp0_cfg.max_spdu_outst == 0)
tp0_cfg.max_spdu_outst = 50;
/* Check max_tpdu_len. If legal, save binary encoding in global NOW. */
switch (tp0_cfg.max_tpdu_len)
{ /* code for PDU size */
case 128: tp0_cfg.max_tpdu_len_enc = TP_PDU_MAX_SIZE_128 ; break;
case 256: tp0_cfg.max_tpdu_len_enc = TP_PDU_MAX_SIZE_256 ; break;
case 512: tp0_cfg.max_tpdu_len_enc = TP_PDU_MAX_SIZE_512 ; break;
case 1024: tp0_cfg.max_tpdu_len_enc = TP_PDU_MAX_SIZE_1024 ; break;
case 2048: tp0_cfg.max_tpdu_len_enc = TP_PDU_MAX_SIZE_2048 ; break;
case 4096: tp0_cfg.max_tpdu_len_enc = TP_PDU_MAX_SIZE_4096 ; break;
case 8192: tp0_cfg.max_tpdu_len_enc = TP_PDU_MAX_SIZE_8192 ; break;
case 65531: tp0_cfg.max_tpdu_len_enc = TP_PDU_MAX_SIZE_65531; break;
default:
TP_LOG_ERR1 ("TP-ERROR: configured rfc1006_max_tpdu_len (%d) illegal",
tp0_cfg.max_tpdu_len);
return (TP4E_INVAL_TPDU_LEN);
}
/* Compute Max TPKT len & allocate TPKT encode buffer. */
max_tpkt_len = tp0_cfg.max_tpdu_len + RFC1006_HEAD_LEN;
g_tpkt_enc_buf = (ST_UCHAR *) M_MALLOC (MSMEM_STARTUP, max_tpkt_len);
/* Initialize the Network layer (i.e. RFC1006). */
if ((errcode = np_init (max_conns)) != SD_SUCCESS)
return (errcode);
tp0_conn_arr = (TP0_CONN *) M_CALLOC (MSMEM_STARTUP, max_conns, sizeof (TP0_CONN));
for (j=0; j<max_conns; j++)
{
#if !defined(SPDU_RX_NO_PREALLOCATE)
tp0_conn_arr [j].spdu_rx.spdu_ptr = (ST_CHAR *) M_MALLOC (MSMEM_STARTUP, g_tp0_max_spdu_len);
#endif
}
return (SD_SUCCESS);
}
/************************************************************************/
/* tp0_terminate */
/************************************************************************/
ST_RET tp0_terminate (ST_VOID)
{
ST_RET ret = SD_SUCCESS;
ST_UINT j;
if (tp0_conn_arr)
{
for (j = 0; j < tp0_cfg.max_num_conns; j++)
{
if (tp0_conn_arr [j].state != TP_STATE_CLOSED)
{
/* Fake disconnect indication so Upper Layers can clean up. */
tp4_disconnect_ind (tp0_conn_arr[j].user_conn_id, TP_DR_NO_REASON,
0, NULL);
/* Send disconnect request. */
tp0_disconnect (j + MIN_TP0_CONN_ID, 0, NULL);
}
#if !defined(SPDU_RX_NO_PREALLOCATE)
M_FREE (MSMEM_STARTUP, tp0_conn_arr [j].spdu_rx.spdu_ptr);
#endif
}
M_FREE (MSMEM_STARTUP, tp0_conn_arr);
tp0_conn_arr = NULL;
}
if (g_tpkt_enc_buf)
M_FREE (MSMEM_STARTUP, g_tpkt_enc_buf);
ret = np_end ();
return (ret);
}
#if defined(MMSEASE_MOSI)
/************************************************************************/
/* rfc1006_listener_bind */
/* Function to pass TSAP to rfc1006_listener task through UNIX domain */
/* socket. Waits for response if TSAP bind successful. */
/************************************************************************/
ST_RET rfc1006_listener_bind (ST_UCHAR *tsap)
{
ST_UINT tsap_len = (ST_UINT) ((ST_UINT8) tsap [0]+1);
RFC1006_IPC_MSG ipc_msg;
ST_INT count;
char resp;
ipc_msg.magic = RFC1006_IPC_MAGIC_NUM;
ipc_msg.opcode = RFC1006_IPC_OP_BIND;
ipc_msg.data_len = tsap_len;
/* send the BIND msg header */
count = sendwait (domsock_listener, &ipc_msg, sizeof (RFC1006_IPC_MSG), 0);
if (count != sizeof (RFC1006_IPC_MSG))
{
TP_LOG_ERR0 ("TP-ERROR: rfc1006_listener_bind: send BIND header failed.");
return (SD_FAILURE);
}
/* send the TSAP to bind */
count = sendwait (domsock_listener, tsap, tsap_len, 0);
if (count != tsap_len)
{
TP_LOG_ERR0 ("TP-ERROR: rfc1006_listener_bind: send BIND TSAP failed.");
return (SD_FAILURE);
}
/* Wait for BIND response, keep trying to read until success. */
count = recvwait (domsock_listener, &ipc_msg, sizeof (RFC1006_IPC_MSG), 0);
if (count != sizeof (RFC1006_IPC_MSG))
{
TP_LOG_ERR0 ("TP-ERROR: rfc1006_listener_bind: failed to receive BIND response")
return (SD_FAILURE);
}
if (ipc_msg.magic != RFC1006_IPC_MAGIC_NUM)
{
TP_LOG_ERR1 ("TP-ERROR: rfc1006_listener_bind: received invalid magic='%X'", ipc_msg.magic);
return (SD_FAILURE);
}
if (ipc_msg.opcode != RFC1006_IPC_OP_BIND)
{
TP_LOG_ERR1 ("TP-ERROR: rfc1006_listener_bind: received invalid opcode=%u", (ST_UINT) ipc_msg.opcode);
return (SD_FAILURE);
}
if (ipc_msg.data_len != 1)
{
TP_LOG_ERR1 ("TP-ERROR: rfc1006_listener_bind: received invalid data_len=%u (expecting 1 byte)",
(ST_UINT) ipc_msg.data_len);
return (SD_FAILURE);
}
count = recvwait (domsock_listener, &resp, 1, 0);
if (count != 1)
{
TP_LOG_ERR0 ("TP-ERROR: rfc1006_listener_bind: failed to receive expected 1 byte BIND response");
return (SD_FAILURE);
}
if (resp != SD_SUCCESS)
{
/* most probable cause of failure */
TP_LOG_ERR1 ("TP-ERROR: rfc1006_listener_bind: BIND failed resp=%d,\n TSEL may be already bound to another user.",
(ST_UINT) resp);
return (TP4E_TOO_MANY_BINDS);
}
return (SD_SUCCESS);
}
/************************************************************************/
/* tp0_bind */
/* This function substitutes for "tp4_bind" when TP0/RFC1006 is being */
/* used. See "tp4_bind" header for more info. */
/************************************************************************/
ST_RET tp0_bind (ST_LONG user_bind_id,
TP4_ADDR *tp4_addr,
ST_INT sharable,
ST_INT max_conns)
{
ST_RET ret;
ST_UINT idx;
BIND_TSEL *bind_tsel;
ST_LONG tp0_bind_id;
if (tp4_addr->tp_type != TP_TYPE_TCP)
{ /* Not for TP0 */
/* No other TP provider to pass to. This is error. */
TP_LOG_ERR0 ("TP-ERROR: tp0_bind: invalid transport type");
return (SD_FAILURE);
}
if (max_conns > tp0_cfg.max_num_conns)
{
TP_LOG_ERR2 ("TP-ERROR: tp0_bind: requested TP conns (%d) greater than number configured (%d)",
max_conns, tp0_cfg.max_num_conns);
return (TP4E_INVAL_NUM_CONNS);
}
/* DEBUG:: the tbl alloc may need to be moved to tp0_initialize, and free */
/* in tp0_terminate. */
if (!bind_tsel_tbl)
{
bind_tsel_tbl_max = tp0_cfg.max_num_conns + 1; /* entry 0 unused */
bind_tsel_tbl = (BIND_TSEL *) chk_calloc (bind_tsel_tbl_max, sizeof(BIND_TSEL));
}
/* Make sure Local TSAP len is legal before copying. */
if (tp4_addr->tp4_sel_len > 0 && tp4_addr->tp4_sel_len <= MAX_TSEL_LEN)
{
/* find a spot in the bind_tsel_tbl (entry 0 unused) */
for (idx=1; idx<bind_tsel_tbl_max; ++idx)
{
bind_tsel = &bind_tsel_tbl[idx];
if (bind_tsel->tsel[0] == 0) /* if available */
{
ST_UCHAR tmp_tsel[MAX_TSEL_LEN+1];
tmp_tsel[0] = (ST_UCHAR) tp4_addr->tp4_sel_len;
memcpy (&tmp_tsel[1], tp4_addr->tp4_sel, tp4_addr->tp4_sel_len);
if (tp0_check_tsel (tmp_tsel, NULL) == SD_SUCCESS)
{
TP_LOG_ERR0 ("TP-ERROR: tp0_bind: TSEL already bound");
TP_LOGH_ERR (tp4_addr->tp4_sel_len, tp4_addr->tp4_sel);
return (TP4E_INVAL_TSEL);
}
ret = rfc1006_listener_bind (tmp_tsel);
if (ret != SD_SUCCESS)
return (ret);
tp0_bind_id = idx;
bind_tsel->user_bind_id = user_bind_id;
memcpy (bind_tsel->tsel, tmp_tsel, tp4_addr->tp4_sel_len+1);
TP_LOG_REQ1 ("tp0_bind: Bound TSEL to tp0_bind_id=%d", tp0_bind_id);
TP_LOGH_REQ (tp4_addr->tp4_sel_len, tp4_addr->tp4_sel);
break;
}
}
if (idx == bind_tsel_tbl_max)
{
TP_LOG_ERR0 ("TP-ERROR: too many binds");
return (TP4E_TOO_MANY_BINDS);
}
}
else
{
TP_LOG_ERR2 ("TP-ERROR: tp0_bind: invalid TSEL length=%d octets (len>0 && len<=%d)",
tp4_addr->tp4_sel_len, MAX_TSEL_LEN);
return (TP4E_INVAL_TSEL);
}
tp4_bind_cnf (user_bind_id, tp0_bind_id, SD_SUCCESS);
return (SD_SUCCESS);
}
/************************************************************************/
/* rfc1006_listener_unbind */
/* Function to unbind TSAP in rfc1006_listener task. Passed through */
/* UNIX domain socket. Waits for the response, discarding any non */
/* RFC1006_IPC_OP_UNBIND messages. */
/************************************************************************/
ST_RET rfc1006_listener_unbind (ST_UCHAR *tsap)
{
ST_UINT tsap_len = (ST_UINT) ((ST_UINT8) tsap [0]+1);
RFC1006_IPC_MSG ipc_msg;
int count;
ST_CHAR *tmp_buf;
char resp;
ipc_msg.magic = RFC1006_IPC_MAGIC_NUM;
ipc_msg.opcode = RFC1006_IPC_OP_UNBIND;
ipc_msg.data_len = 0;
/* send the UNBIND msg header */
count = sendwait (domsock_listener, &ipc_msg, sizeof (RFC1006_IPC_MSG), 0);
if (count != sizeof (RFC1006_IPC_MSG))
{
TP_LOG_ERR0 ("TP-ERROR: rfc1006_listener_unbind: send UNBIND header failed.");
return (SD_FAILURE);
}
/* send the TSAP to unbind */
count = sendwait (domsock_listener, tsap, tsap_len, 0);
if (count != tsap_len)
{
TP_LOG_ERR0 ("TP-ERROR: rfc1006_listener_unbind: send UNBIND TSAP failed.");
return (SD_FAILURE);
}
/* Keep trying to read until RFC1006_IPC_OP_UNBIND response arrives. */
do
{ /* read and drop any other IPC_MSG */
count = recvwait (domsock_listener, &ipc_msg, sizeof (RFC1006_IPC_MSG), 0);
if (count != sizeof (RFC1006_IPC_MSG))
{
TP_LOG_ERR0 ("TP-ERROR: rfc1006_listener_unbind: failed to receive UNIBIND response.");
return (SD_FAILURE);
}
if (ipc_msg.magic != RFC1006_IPC_MAGIC_NUM)
{
TP_LOG_ERR1 ("TP-ERROR: rfc1006_listener_unbind: received invalid magic='%X'",
ipc_msg.magic);
return (SD_FAILURE);
}
if (ipc_msg.opcode != RFC1006_IPC_OP_UNBIND)
{
TP_LOG_IND2 ("rfc1006_listener_unbind: waiting for UNBIND response.\n Ignoring message with opcode=%d, len=%d",
ipc_msg.opcode, ipc_msg.data_len);
if (ipc_msg.data_len > 0) /* read and discard data if present */
{
tmp_buf = chk_malloc (ipc_msg.data_len);
count = read (domsock_listener, tmp_buf, ipc_msg.data_len);
chk_free (tmp_buf);
}
}
} while (ipc_msg.opcode != RFC1006_IPC_OP_UNBIND);
if (ipc_msg.data_len != 1)
{
TP_LOG_ERR1 ("TP-ERROR: rfc1006_listener_unbind: received invalid data_len=%u (expecting 1 byte)",
(ST_UINT) ipc_msg.data_len);
return (SD_FAILURE);
}
count = recvwait (domsock_listener, &resp, 1, 0);
if (count != 1)
{
TP_LOG_ERR0 ("TP-ERROR: rfc1006_listener_unbind: failed to receive expected 1 byte UNBIND response");
return (SD_FAILURE);
}
if (resp != SD_SUCCESS)
{
TP_LOG_ERR1 ("TP-ERROR: rfc1006_listener_unbind: UNBIND failed resp=%d.",
(ST_UINT) resp);
return (SD_FAILURE);
}
return (SD_SUCCESS);
}
/************************************************************************/
/* tp0_unbind */
/************************************************************************/
ST_RET tp0_unbind (ST_LONG tp0_bind_id)
{
ST_RET ret = SD_FAILURE;
BIND_TSEL *bind_tsel;
if (bind_tsel_tbl && tp0_bind_id > 0 && tp0_bind_id <= bind_tsel_tbl_max)
{
/* tp0_bind_id is the index into the bind table */
bind_tsel = &bind_tsel_tbl[tp0_bind_id];
if (bind_tsel->tsel[0] != 0)
{
ret = rfc1006_listener_unbind (bind_tsel->tsel);
TP_LOG_REQ1 ("tp0_unbind: tp0_bind_id=%d", tp0_bind_id);
tp4_unbind_cnf (bind_tsel->user_bind_id);
bind_tsel->tsel[0] = 0;
bind_tsel->user_bind_id = 0;
ret = SD_SUCCESS;
}
else
TP_LOG_ERR0 ("TP-ERROR: TP0 UNBIND failed (not bound).");
}
else
TP_LOG_ERR1 ("TP-ERROR: TP0 UNBIND failed (invalid tp0_bind_id=%ld).", tp0_bind_id);
return (ret);
}
/************************************************************************/
/* tp0_check_tsel */
/* Checks if given tsel was bound and sets the user_bind_id if tsel OK. */
/* Returns: */
/* SD_SUCCESS if tsel bound */
/* SD_FAILURE otherwise */
/************************************************************************/
ST_RET tp0_check_tsel (ST_UCHAR *tsel, ST_LONG *user_bind_id)
{
ST_RET ret = SD_FAILURE;
ST_UINT idx;
BIND_TSEL *bind_tsel;
/* find tsel (entry 0 unused) */
for (idx=1; idx<bind_tsel_tbl_max; ++idx)
{
bind_tsel = &bind_tsel_tbl[idx];
if (bind_tsel->tsel[0] > 0 &&
memcmp (tsel, bind_tsel->tsel, bind_tsel->tsel[0]+1) == 0)
{
if (user_bind_id)
*user_bind_id = bind_tsel->user_bind_id;
ret = SD_SUCCESS; /* tsel is bound */
break;
}
}
return (ret);
}
/************************************************************************/
/* tp0_check_bind_id */
/* Checks if given bind_id is valid and sets the tsel if bind_id OK. */
/* Returns: */
/* SD_SUCCESS if bind_id valid */
/* SD_FAILURE otherwise */
/************************************************************************/
ST_RET tp0_check_bind_id (ST_LONG bind_id, ST_UCHAR **tsel)
{
ST_RET ret = SD_FAILURE;
ST_UINT idx;
BIND_TSEL *bind_tsel;
/* check bind_id (entry 0 unused) */
for (idx=1; idx<bind_tsel_tbl_max; ++idx)
{
bind_tsel = &bind_tsel_tbl[idx];
if (bind_tsel->tsel[0] > 0 && (ST_LONG) idx == bind_id)
{
*tsel = bind_tsel->tsel;
ret = SD_SUCCESS; /* bind_id valid */
break;
}
}
return (ret);
}
#else /* !defined(MMSEASE_MOSI) */
/************************************************************************/
/* tp0_bind */
/* This function substitutes for "tp4_bind" when TP0/RFC1006 is being */
/* used. See "tp4_bind" header for more info. */
/************************************************************************/
ST_RET tp0_bind (ST_LONG user_bind_id,
TP4_ADDR *tp4_addr,
ST_INT sharable,
ST_INT max_conns)
{
if (tp4_addr->tp_type != TP_TYPE_TCP)
{ /* Not for TP0 */
/* No other TP provider to pass to. This is error. */
TP_LOG_ERR0 ("TP-ERROR: tp0_bind: invalid transport type");
return (SD_FAILURE);
}
if (tp0_bind_called)
{
TP_LOG_ERR0 ("TP-ERROR: too many binds");
return (TP4E_TOO_MANY_BINDS);
}
if (max_conns > tp0_cfg.max_num_conns)
{
TP_LOG_ERR2 ("TP-ERROR: requested TP conns (%d) greater than number configured (%d)",
max_conns, tp0_cfg.max_num_conns);
return (TP4E_INVAL_NUM_CONNS);
}
/* Save user_bind_id in Global. Use in all calls to user functions. */
only_tp0_user_bind_id = user_bind_id;
/* Make sure Local TSAP len is legal before copying. */
if (tp4_addr->tp4_sel_len > MAX_TSEL_LEN)
{
TP_LOG_ERR0 ("TP-ERROR: TSEL too long");
return (TP4E_INVAL_TSEL);
}
/* Save Local TSAP (only do memcpy if len != 0). */
if ((only_tp0_loc_tsap [0] = (ST_UCHAR) tp4_addr->tp4_sel_len) != 0)
memcpy (&only_tp0_loc_tsap [1], tp4_addr->tp4_sel, tp4_addr->tp4_sel_len);
tp4_bind_cnf (user_bind_id, only_tp0_bind_id, SD_SUCCESS);
tp0_bind_called = SD_TRUE; /* Only set if successful. */
return (SD_SUCCESS);
}
/************************************************************************/
/* tp0_unbind */
/************************************************************************/
ST_RET tp0_unbind (ST_LONG tp0_bind_id)
{
if (tp0_bind_id != only_tp0_bind_id)
return (SD_FAILURE); /* Invalid bind id. */
if (tp0_bind_called == SD_FALSE)
return (SD_FAILURE); /* Bind never made. Cannot unbind. */
tp0_bind_called = SD_FALSE; /* Clear flag so user can bind again. */
tp4_unbind_cnf (only_tp0_user_bind_id);
return (SD_SUCCESS);
}
#endif /* !defined(MMSEASE_MOSI) */
/*======================================================================*/
/*======================================================================*/
/* TIMER FUNCTIONS */
/*======================================================================*/
/*======================================================================*/
/************************************************************************/
/* tp0_session_timer */
/*----------------------------------------------------------------------*/
/* This function starts the session timer (for MOSI implementation). */
/* The user function tp4_session_timer_expired will be called if the */
/* timer expires. */
/* */
/* Parameters: */
/* ST_LONG tp_conn_id TP0 connection id for this connect. */
/* ST_BOOLEAN start SD_TRUE if timer should be started, */
/* SD_FALSE if timer should be stopped. */
/* */
/* Return: */
/* SD_SUCCESS (0) if successful */
/* error code if not successful */
/************************************************************************/
ST_RET tp0_session_timer (ST_LONG tp_conn_id, ST_BOOLEAN start)
{
TP0_CONN *tp_conn = &tp0_conn_arr [tp_conn_id-MIN_TP0_CONN_ID];
if (start)
{
if (tp_conn->state != TP_STATE_OPEN)
{
TP_LOG_ERR1 ("TP-ERROR: illegal connection state = %d", tp_conn->state);
return (TP4E_CONN_STATE);
}
/* If configured, use configured val. It not, use reasonable val.*/
if (session_cfg.disconnect_timeout)
tp_conn->session_timer = session_cfg.disconnect_timeout;
else
tp_conn->session_timer = 60;
}
else
tp_conn->session_timer = 0; /* stopping the session timer */
return (SD_SUCCESS);
}
/************************************************************************/
/* tp0_timer_tick */
/*----------------------------------------------------------------------*/
/* This function is called on 1 second intervals by the TP0-user */
/* When this function is called, TP0 decrements all of the timers */
/* for all outstanding transport connections. */
/* */
/* Parameters: */
/* ST_VOID none */
/* */
/* Return: */
/* ST_VOID none */
/************************************************************************/
ST_VOID tp0_timer_tick ()
{
ST_UINT conn_id;
TP0_CONN *tp_conn;
/* Because this function is called often and the entire tp0_conn_arr */
/* must be checked, it must be very efficient. Therefore, tp_conn */
/* is used to point to each element of array (instead of */
/* tp0_conn_arr[conn_id]). */
for (conn_id = 0, tp_conn = &tp0_conn_arr[0];
conn_id < tp0_cfg.max_num_conns;
conn_id++, tp_conn++)
{
/* Handle "session timer" (MOSI implementation only) */
/* Only decrement it if it's not 0 and state is legal. */
if (tp_conn->state == TP_STATE_OPEN &&
tp_conn->session_timer)
{
if (--tp_conn->session_timer == 0)
tp4_session_timer_expired (tp_conn->user_conn_id);
} /* End "session timer" handling */
} /* End "for" loop */
}
/************************************************************************/
/* tp0_buffers_avail */
/*----------------------------------------------------------------------*/
/* This function returns the number of buffers available for sending */
/* SPDUs. 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 tp_conn_id Transport Connection ID */
/* */
/* Return: */
/* ST_INT # of SPDU transmit buffers available */
/************************************************************************/
ST_INT tp0_buffers_avail (ST_LONG tp_conn_id)
{
TP0_CONN *tp_conn = &tp0_conn_arr [tp_conn_id-MIN_TP0_CONN_ID];
ST_UINT spdu_que_cnt;
spdu_que_cnt = np_get_tx_queue_cnt (tp_conn->sock_info);
return (tp0_cfg.max_spdu_outst - spdu_que_cnt);
}
/************************************************************************/
/* tp0_event */
/* Receive a n_unitdata NSDU, decode it, process it, and free it. */
/************************************************************************/
ST_BOOLEAN tp0_event ()
{
tp4_check_timer ();
return (np_event ());
}
/*======================================================================*/
/*======================================================================*/
/* FUNCTIONS BELOW CALLED BY USER TO ENCODE AND SEND TPDUs. */
/*======================================================================*/
/*======================================================================*/
/************************************************************************/
/* tp0_data */
/* RETURN CODES: */
/* SD_SUCCESS (if SPDU successfully queued for sending) */
/* TP4E_CONN_STATE (connection not in proper state for this) */
/* OR error returned from tp0_send_dt. */
/************************************************************************/
ST_RET tp0_data (ST_LONG tp_conn_id, ST_INT eot, ST_UINT spdu_len, ST_CHAR *spdu_ptr)
{
ST_RET ret = SD_SUCCESS;
TP0_CONN *tp_conn = &tp0_conn_arr [tp_conn_id-MIN_TP0_CONN_ID];
ST_UINT spdu_offset;
TPDU_DT tpdu_dt_tx; /* ptr to TPDU of interest. */
if (tp_conn->state != TP_STATE_OPEN)
{
TP_LOG_ERR1 ("TP-ERROR: illegal connection state = %d", tp_conn->state);
return (TP4E_CONN_STATE);
}
tpdu_dt_tx.dst_ref = tp_conn->rem_ref;
tpdu_dt_tx.sock_info = tp_conn->sock_info;
/* Set up TPDU user data by pointing into SPDU. */
for (spdu_offset = 0; spdu_offset < spdu_len; )
{
tpdu_dt_tx.udata_ptr = spdu_ptr + spdu_offset;
if (spdu_len - spdu_offset <= (ST_UINT)(tp_conn->max_tpdu_len - TP0_HEAD_LEN_DT))
{
tpdu_dt_tx.udata_len = spdu_len - spdu_offset;
tpdu_dt_tx.eot = SD_TRUE;
}
else
{
TP_LOG_REQ0 ("TP-INFO: SPDU being segmented. Should increase TPDU size.");
/* Send largest allowed TPDU, taking header size into account. */
tpdu_dt_tx.udata_len = (tp_conn->max_tpdu_len - TP0_HEAD_LEN_DT);
tpdu_dt_tx.eot = SD_FALSE;
}
spdu_offset += tpdu_dt_tx.udata_len;
ret = tp0_send_dt (&tpdu_dt_tx);
if (ret != SD_SUCCESS)
break;
} /* end "for" */
return (ret);
}
/************************************************************************/
/* tp0_disconnect */
/* This function is called by the user to break connection */
/************************************************************************/
ST_RET tp0_disconnect (ST_LONG tp0_conn_id,
ST_INT conndata_len,
char *conndata)
{
TP0_CONN *tp_conn; /* ptr to struct in "tp0_conn_arr" array. */
TPDU_DR tpdu_dr;
ST_UINT16 conn_id = (ST_UINT16) tp0_conn_id - MIN_TP0_CONN_ID;
if (conn_id >= tp0_cfg.max_num_conns)
return (TP4E_BADCONN);
tp_conn = &tp0_conn_arr [conn_id];
/* Fill in TPDU_DR struct in case DR needs to be sent. */
tpdu_dr.loc_ref = calc_loc_ref (conn_id, tp_conn->loc_ref_offset);
tpdu_dr.rem_ref = tp_conn->rem_ref;
tpdu_dr.sock_info = tp_conn->sock_info;
tpdu_dr.reason = TP_DR_NO_REASON;
switch (tp_conn->state)
{
case TP_STATE_OPEN:
case TP_STATE_WFCC:
case TP_STATE_WFNC:
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
break;
case TP_STATE_WFTRESP:
/* Overwrite assumed "loc_ref". Not valid in this state. */
tpdu_dr.loc_ref = 0;
tp0_send_dr (&tpdu_dr);
tp_conn->state = TP_STATE_CLOSED;
#if defined(SPDU_RX_NO_PREALLOCATE)
M_FREE (MSMEM_STARTUP, tp_conn->spdu_rx.spdu_ptr);
#endif
break;
default:
TP_LOG_ERR1 ("TP-ERROR: invalid state (%d) for sending DR. Not sent.",
tp_conn->state);
return (SD_FAILURE);
} /* end "switch" */
return (SD_SUCCESS);
}
/*======================================================================*/
/*======================================================================*/
/* FUNCTIONS BELOW CALLED BY TP0 TO PROCESS TPDUs RECEIVED */
/* (TPDUs ALREADY DECODED BY TP0). */
/*======================================================================*/
/*======================================================================*/
/************************************************************************/
/* tp0_process_dt */
/* Only one SPDU per connection is ever being processed. As soon as */
/* the EOT is received, the SPDU is passed up to the Transport-user. */
/************************************************************************/
ST_VOID tp0_process_dt (TPDU_DT *tpdu_dt_rx)
{
TP0_CONN *tp_conn;
if (tpdu_dt_rx->sock_info->user_conn_id == INVALID_CONN_ID)
{
TP_LOG_ERR0 ("TP-ERROR: unexpected TPDU-DT.ind ignored. Waiting for TPDU-CR.ind.");
return;
}
tp_conn = (TP0_CONN *) tpdu_dt_rx->sock_info->user_conn_id;
if (tp_conn - tp0_conn_arr > tp0_cfg.max_num_conns) /* check "conn_id"*/
{
TP_LOG_ERR0 ("TP-ERROR: illegal connection id");
return; /* conn_id is invalid. Ignore this TPDU. */
}
switch (tp_conn->state)
{
case TP_STATE_CLOSED:
return; /* Ignore this DT. */
case TP_STATE_OPEN:
break; /* Continue */
default:
TP_LOG_ERR1 ("TP-ERROR: invalid state (%d) for received DT. Ignored.",
tp_conn->state);
return;
}
/* If this is beginning of SPDU, initialize buffer for it. */
if (tp_conn->spdu_rx_start) /* init when CR rcvd or CR sent */
{ /* Last TPDU contained EOT. This TPDU starts new SPDU. */
/* We can freely overwrite "spdu_rx.spdu_ptr" because the user */
/* is now responsible for the last one. */
tp_conn->spdu_rx.spdu_len = 0;
}
if ((ST_UINT) (tp_conn->spdu_rx.spdu_len + tpdu_dt_rx->udata_len)
> g_tp0_max_spdu_len)
{
/* Would exceed max SPDU size. CAN'T DO memcpy. WON'T FIT IN BUFFER.*/
/* No good recovery, so just disconnect. */
/* NOTE: This is really a Session problem, but we handle it here. */
TP_LOG_ERR1 ("Received msg exceeds max_spdu_len (%d)", g_tp0_max_spdu_len);
/* Pass up disconnect indication. */
tp4_disconnect_ind (tp_conn->user_conn_id, TP_DR_NO_REASON,
0, NULL);
/* Send disconnect request (compute conn_id from tp_conn. */
tp0_disconnect (tp_conn - tp0_conn_arr + MIN_TP0_CONN_ID, 0, NULL);
return; /* DO NOT CONTINUE */
}
memcpy (tp_conn->spdu_rx.spdu_ptr + tp_conn->spdu_rx.spdu_len, tpdu_dt_rx->udata_ptr,
tpdu_dt_rx->udata_len);
tp_conn->spdu_rx.spdu_len += tpdu_dt_rx->udata_len;
if (tpdu_dt_rx->eot)
{ /* This TPDU contains EOT. This TPDU ends SPDU. */
tp_conn->spdu_rx_start = SD_TRUE; /* Next TPDU will begin a SPDU. */
/* Pass up SPDU to user. Note that we always pass up complete SPDU */
/* so eot is always SD_TRUE. */
/* "tp4_data_ind" must completely process the data at spdu_ptr. */
/* When it returns, the buffer may be reused. */
tp4_data_ind (tp_conn->user_conn_id, tpdu_dt_rx->eot, tp_conn->spdu_rx.spdu_len,
tp_conn->spdu_rx.spdu_ptr);
}
else
tp_conn->spdu_rx_start = SD_FALSE; /* Next TPDU is part of this SPDU*/
}
/************************************************************************/
/* tp0_process_dr */
/* Process a DR (Disconnect Request) TPDU. */
/************************************************************************/
ST_VOID tp0_process_dr (TPDU_DR *tpdu_dr)
{
TP0_CONN *tp_conn;
ST_INT reason;
if (tpdu_dr->sock_info->user_conn_id == INVALID_CONN_ID)
{
TP_LOG_ERR0 ("TP-ERROR: unexpected TPDU-DR.ind ignored. Waiting for TPDU-CR.ind.");
return;
}
/* tp_conn was saved in "sock_info" (as user_conn_id) when CR sent/rcvd*/
tp_conn = (TP0_CONN *) tpdu_dr->sock_info->user_conn_id;
switch (tp_conn->state)
{
case TP_STATE_CLOSED:
break;
case TP_STATE_WFCC:
reason = 0; /* Normal disconnection */
/* NOTE: To save memory, user data is never passed up to user. */
tp4_disconnect_ind (tp_conn->user_conn_id, reason,
0, NULL); /* No user data passed up. */
np_disconnect_req (tp_conn->sock_info); /* Disconnect network conn.*/
tp_conn->state = TP_STATE_CLOSED; /* Skip REFWAIT state. */
#if defined(SPDU_RX_NO_PREALLOCATE)
M_FREE (MSMEM_STARTUP, tp_conn->spdu_rx.spdu_ptr);
#endif
break;
case TP_STATE_OPEN:
TP_LOG_ERR0 ("TP-ERROR: Received TP0 DR in OPEN state. Ignoring.");
break;
default:
TP_LOG_ERR1 ("TP-ERROR: invalid state (%d) for received DR. Ignored.",
tp_conn->state);
break;
}
return;
}
/*======================================================================*/
/*======================================================================*/
/* INTERNAL TP0 FUNCTIONS */
/*======================================================================*/
/*======================================================================*/
/************************************************************************/
/* tp0_conn_clean */
/* Initialize connection struct for a new connection. */
/************************************************************************/
ST_VOID tp0_conn_clean (TP0_CONN *tp_conn)
{
tp_conn->spdu_rx_start = SD_TRUE; /* CRITICAL: Initialize flag. */
#if defined(SPDU_RX_NO_PREALLOCATE)
tp_conn->spdu_rx.spdu_ptr = (ST_CHAR *) M_MALLOC (MSMEM_STARTUP, g_tp0_max_spdu_len);
#endif
return;
}
/************************************************************************/
/* tp0_send_dt */
/* Send a DT (Data) TPDU. */
/************************************************************************/
ST_RET tp0_send_dt (TPDU_DT *tpdu_dt)
{
ST_RET ret = SD_FAILURE;
ST_UINT16 data_len;
tp_log_tsdu ((ST_VOID *) tpdu_dt, (ST_UCHAR) TP_PDU_TYPE_DT, TP_REQ);
/* Fill in g_tpkt_enc_buf with TPDU */
data_len = tp0_encode_dt (g_tpkt_enc_buf + RFC1006_HEAD_LEN, tpdu_dt);
if (data_len > 0)
{
/* Fill in g_tpkt_enc_buf HEADER */
data_len += RFC1006_HEAD_LEN; /* include header in len */
g_tpkt_enc_buf [0] = 3; /* vrsn */
g_tpkt_enc_buf [1] = 0; /* reserved */
g_tpkt_enc_buf [2] = (ST_UCHAR) (data_len >> 8); /* high byte */
g_tpkt_enc_buf [3] = (ST_UCHAR) (data_len & 0xff); /* low byte */
ret = np_data_req (tpdu_dt->sock_info, data_len, g_tpkt_enc_buf, tpdu_dt->eot);
}
return (ret);
}
/************************************************************************/
/* tp0_send_cr */
/* Send a CR (Connect Request) TPDU. */
/************************************************************************/
ST_RET tp0_send_cr (TPDU_CX *tpdu_cr)
{
ST_RET ret = SD_FAILURE;
ST_UINT16 data_len;
tp_log_tsdu ((ST_VOID *) tpdu_cr, (ST_UCHAR) TP_PDU_TYPE_CR, TP_REQ);
/* Fill in g_tpkt_enc_buf with TPDU */
data_len = tp_encode_cx (g_tpkt_enc_buf + RFC1006_HEAD_LEN, tpdu_cr,
TP_PDU_TYPE_CR);
if (data_len > 0)
{
/* Fill in g_tpkt_enc_buf HEADER */
data_len += RFC1006_HEAD_LEN; /* include header in len */
g_tpkt_enc_buf [0] = 3; /* vrsn */
g_tpkt_enc_buf [1] = 0; /* reserved */
g_tpkt_enc_buf [2] = (ST_UCHAR) (data_len >> 8); /* high byte */
g_tpkt_enc_buf [3] = (ST_UCHAR) (data_len & 0xff); /* low byte */
ret = np_data_req (tpdu_cr->sock_info, data_len, g_tpkt_enc_buf, SD_TRUE);
}
return (ret);
}
/************************************************************************/
/* tp0_send_cc */
/* Send a CC (Connect Confirm) TPDU. */
/************************************************************************/
ST_RET tp0_send_cc (TPDU_CX *tpdu_cc)
{
ST_RET ret = SD_FAILURE;
ST_UINT16 data_len;
tp_log_tsdu ((ST_VOID *) tpdu_cc, (ST_UCHAR) TP_PDU_TYPE_CC, TP_REQ);
/* Fill in g_tpkt_enc_buf with TPDU */
data_len = tp_encode_cx (g_tpkt_enc_buf + RFC1006_HEAD_LEN, tpdu_cc,
TP_PDU_TYPE_CC);
if (data_len > 0)
{
/* Fill in g_tpkt_enc_buf HEADER */
data_len += RFC1006_HEAD_LEN; /* include header in len */
g_tpkt_enc_buf [0] = 3; /* vrsn */
g_tpkt_enc_buf [1] = 0; /* reserved */
g_tpkt_enc_buf [2] = (ST_UCHAR) (data_len >> 8); /* high byte */
g_tpkt_enc_buf [3] = (ST_UCHAR) (data_len & 0xff); /* low byte */
ret = np_data_req (tpdu_cc->sock_info, data_len, g_tpkt_enc_buf, SD_TRUE);
}
return (ret);
}
/************************************************************************/
/* tp0_send_dr */
/* Send a DR (Disconnect Request) TPDU. */
/************************************************************************/
int cotp_test_mode=0;
ST_RET tp0_send_dr (TPDU_DR *tpdu_dr)
{
ST_RET ret = SD_FAILURE;
ST_UINT16 data_len;
if(cotp_test_mode) return SD_FAILURE; /*renxiaobao <20><>ȫ<EFBFBD><C8AB><EFBFBD><EFBFBD>*/
tp_log_tsdu ((ST_VOID *) tpdu_dr, TP_PDU_TYPE_DR, TP_REQ);
/* Fill in g_tpkt_enc_buf with TPDU */
data_len = tp_encode_dr (g_tpkt_enc_buf + RFC1006_HEAD_LEN, tpdu_dr);
if (data_len > 0)
{
/* Fill in g_tpkt_enc_buf HEADER */
data_len += RFC1006_HEAD_LEN; /* include header in len */
g_tpkt_enc_buf [0] = 3; /* vrsn */
g_tpkt_enc_buf [1] = 0; /* reserved */
g_tpkt_enc_buf [2] = (ST_UCHAR) (data_len >> 8); /* high byte */
g_tpkt_enc_buf [3] = (ST_UCHAR) (data_len & 0xff); /* low byte */
ret = np_data_req (tpdu_dr->sock_info, data_len, g_tpkt_enc_buf, SD_TRUE);
}
return (ret);
}
/************************************************************************/
/* tp0_connect_outstanding_count */
/* NOTE: can't presently use tp4Addr for anything, but in the future */
/* we may use it to check only for outstanding connections to the same */
/* IP Address. */
/************************************************************************/
ST_INT tp0_connect_outstanding_count (TP4_ADDR *tp4Addr)
{
ST_INT connectOutstandingCount = 0;
ST_INT j;
for (j=0; j<tp0_cfg.max_num_conns; j++)
{
/* Waiting for Network connect (i.e., socket connect) to complete? */
if (tp0_conn_arr [j].state == TP_STATE_WFNC)
connectOutstandingCount++; /* YES, increment count */
}
return (connectOutstandingCount);
}
/************************************************************************/
/* tp0_convert_ip */
/* RETURNS: IP Address in network byte order OR */
/* "htonl(INADDR_NONE)" if conversion fails. */
/************************************************************************/
ST_ULONG tp0_convert_ip (ST_CHAR *pHostName)
{
ST_ULONG ipAddr;
ipAddr = inet_addr(pHostName);
if (ipAddr == htonl(INADDR_NONE))
{
#if defined (VXWORKS) /* gethostbyname not supported */
TP_LOG_ERR1 ("Invalid IP address %s", pHostName);
#else /* !VXWORKS */
HOSTENT *pHostEnt; /* host database entry for remote host */
pHostEnt = gethostbyname(pHostName);
if (pHostEnt == NULL)
{
TP_LOG_ERR1 ("Can't get IP address for host %s", pHostName);
}
else
ipAddr = *(ST_UINT32 *)(pHostEnt->h_addr);
#endif /* !VXWORKS */
}
return (ipAddr);
}