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

1938 lines
70 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/************************************************************************/
/* SISCO SOFTWARE MODULE HEADER *****************************************/
/************************************************************************/
/* (c) Copyright Systems Integration Specialists Company, Inc., */
/* 2003-2008, All Rights Reserved */
/* */
/* MODULE NAME : tp0_socks.c */
/* PRODUCT(S) : Lean-T Stack */
/* */
/* MODULE DESCRIPTION : */
/* TP0 functions for dealing with secured sockets interface (SSL). */
/* Uses "gensock2" as intermediate interface to sockets. */
/* */
/* GLOBAL FUNCTIONS DEFINED IN THIS MODULE : */
/* */
/* MODIFICATION LOG : */
/* Date Who Rev Comments */
/* -------- --- ------ ------------------------------------------- */
/* 10/10/08 JRB 38 _uSockHunt: make sure len is legal. */
/* Ignore empty msg (len=RFC1006_HEAD_LEN). */
/* 02/21/08 EJV 37 _uSockHunt: corr lenOut for 0x8000-0xFFFF len*/
/* 09/10/07 MDE 36 Updated to use new SOCK_LOG_ macros */
/* 02/16/07 JRB 35 Use new sockEventPut, sockEventGet functions */
/* and new "uSockConnectInd/Conf" funct ptrs. */
/* 01/30/07 JRB 34 readyToFree flag was deleted so don't use it.*/
/* 01/15/07 JRB 33 Del poll_mode arg to sockStart. */
/* 01/15/07 EJV 32 Rpl TP0_SOCKS_MUTEX_* with S_LOCK_UTIL_RESOURCES */
/* due to mutex changes in gensock2.c. */
/* TP0_SOCK_CTX: rem listsMutex create/destroy. */
/* 12/11/06 EJV 31 rfc1006_listener_connect: chg nonblock_on */
/* from ST_LONG to int. */
/* Needed on 64-bit UNIX, Linux systems. */
/* 11/29/06 EJV 30 recvwait_fd: chg __hpux to use the SCM_RIGHTS*/
/* 11/17/06 EJV 29 socket funs: added (int) cast, the HP-UX */
/* ssize_t is long. */
/* recvwait_fd: impl timeout for recvmsg loop */
/* 11/07/06 EJV 28 MMSEASE_MOSI:init tp0Ctx.tcpEventWakeupPort=0*/
/* 07/10/06 EJV 27 MMSEASE_MOSI: finished GEN_SOCK_CTXT work. */
/* Use sysincs.h for system includes. */
/* Changed sun to s_un (err on Sun Solaris) */
/* 01/30/06 GLB 26 Integrated porting changes for VMS */
/* 12/15/05 EJV 24 _processDisconnectInd: call list_find_node */
/* 12/07/05 EJV 23 _processDisconnectInd: unlink if on the list */
/* _processConnectDone: moved secEnable code to */
/* case for SOCK_STATE_CONNECTED state. */
/* 10/04/05 EJV 22 Implemented GEN_SOCK_CTXT and other changes: */
/* Renamed orginal SOCK_CTX to TP0_SOCK_CTX, */
/* sockCtx to tp0Ctx. */
/* _uSockDisconnectInd: mk errptr more readable */
/* _addSockInd: chg to ST_RET from ST_VOID */
/* _sockClose: moved code to _sockCloseAllListen*/
/* np_end: added new call to _sockCloseAllListen*/
/* Rpl GSOCK_MUTEX_* with TP0_SOCKS_MUTEX_*. */
/* Folded some glb vars into the TP0_SOCK_CTX. */
/* Reversed Rev 21 change, not needed anymore. */
/* 08/03/05 EJV 20 Reworked rekeyTime to be per connection. */
/* Use sockCtx glb (instead saving it in usr1). */
/* 07/19/05 EJV 19 _processConnectInd: set usr2 back to NULL. */
/* _processConnectDone: if secEnable don't free */
/* sock_info. Check state. */
/* np_disconnect_req:check other states, add log*/
/* 07/01/05 EJV 18 _uSockWritable CORR: removed state chg. */
/* 06/24/05 EJV 17 Added code to prevent queues buildup. */
/* 05/02/05 JRB 16 Add RFC1006_LISTENER task code (MMSEASE_MOSI)*/
/* EJV Added _sockSetDefaults. */
/* 05/05/05 EJV 15 Use rfc1006_listen_port if configured */
/* 04/21/05 JRB 14 Fix logging of sin_port (use ntohs). */
/* Del unused vars. */
/* 04/13/05 EJV 13 Corr EADDRINUSE to SOCK_EADDRINUSE */
/* 03/22/05 EJV 12 LINUX (MMSEASE_MOSI): implemented events. */
/* _sockInitListen: allow to SO_REUSEADDR. */
/* Added indTypeStr. Added/chg logging. */
/* 03/16/05 JRB 11 _uSockHunt: allow any val for 2nd byte & */
/* ret ..HUNT_DISCONNECT on err to cause disconn*/
/* For speed, set ptr to strings instead of strcpy.*/
/* Use RFC1006 defines from tp0_sock.h. */
/* 03/07/05 EJV 10 Fixed queuing bug where connectInd sometimes */
/* processed AFTER disconnectInd by moving */
/* sec cleanup from _uSockDisconnectInd to */
/* _processDisconnectInd. */
/* Moved sec code from _uSockConnectDone to */
/* _processConnectDone,for consistency w/con ind*/
/* Use secEnable (in place of other sec fields) */
/* 02/19/04 EJV 09 np_end: chg sleep from 1000 to 100 ms */
/* 02/18/04 JRB 08 _uSockDisconnectInd: DON'T chk state, only */
/* safe from main thread (_processDisconnectInd)*/
/* np_end: allow disconnects to finish. */
/* 02/06/04 JRB 07 np_disconnect_req:Chg assert to log & ret err*/
/* 01/28/04 EJV 06 _handleRekeying:chk if connected before rekey*/
/* 01/23/04 EJV 05 np_data_req: sockTxMsg will free sockData. */
/* 01/21/04 JRB 04 Do most disconnect processing in callback */
/* _uSockDisconnectInd. */
/* EJV Change test for rekey to '>=' */
/* 01/14/04 EJV 03 np_data_req: added eot param. */
/* Free buff if sockTxMsg fails. */
/* Added np_get_tx_queue_cnt func. */
/* 10/16/03 JRB 02 Port to LINUX. Chg SOCKET to (GEN_SOCK *). */
/* Compare (GEN_SOCK *) to NULL, not INVALID_...*/
/* 07/29/03 EJV 01 New. Replacement for tp0_sock.c using */
/* gensock2 to interface to sockets. */
/* Used tp0_sock2.c and snapmain.c */
/************************************************************************/
#include "glbtypes.h"
#include "sysincs.h"
#include "mem_chk.h"
#include "ssec.h"
#include "ssec_int.h"
#include "tp4api.h" /* User definitions for tp4 */
#include "tp4.h" /* Internal definitions for tp4 */
#include "tp4_log.h"
#include "tp0_sock.h"
#include "sock_log.h"
#if defined(MMSEASE_MOSI)
#include "gensock2.h"
#endif
#ifdef MMS_LITE
#include "mvl_acse.h" /* Need "mvl_num_called". */
#endif
#ifdef DEBUG_SISCO
SD_CONST static ST_CHAR *SD_CONST thisFileName = __FILE__;
#endif
/* This many messages can accumulate in the receiving ind queue */
/* for given socket before we stop receiving, start receiving after */
/* we reach low mark. */
#define SOCK_RX_QUE_HIGH_MARK 10 /* >= stop receiving */
#define SOCK_RX_QUE_LOW_MARK 1 /* <= start receiving */
ST_CHAR *eventTypeStr[5] =
{
"UNKNOWN",
"CONNECT_IND",
"CONNECT_CONF",
"DISCONNECT",
"RXDATA"
};
/* Similar to sock_info_alloc but uses GEN_SOCK. */
SOCK_INFO *sock_info_alloc2 (GEN_SOCK *genSock, ST_INT state, ST_LONG user_conn_id);
/* Functions to process indications/events. */
static ST_RET _setSecInfo (GEN_SOCK *pSock, S_SEC_ENCRYPT_CTRL *encrypt_ctrl);
static ST_VOID _processConnectInd (GEN_SOCK *pSock);
static ST_VOID _processConnectDone (GEN_SOCK *pSock);
static ST_VOID _processDisconnectInd (GEN_SOCK *pSock);
static ST_VOID _processRxInd (GEN_SOCK *pSock, GEN_SOCK_DATA *sockData);
/* gensock2 callback functions */
static ST_RET _uSockConnectDone (GEN_SOCK *pSock);
static ST_RET _uSockConnectInd (GEN_SOCK *pSock);
static ST_VOID _uSockDisconnectInd (GEN_SOCK *pSock);
static ST_VOID _uSockRxInd (GEN_SOCK *pSock, GEN_SOCK_DATA *sockData);
static ST_VOID _uSockWritable (GEN_SOCK *pSock);
static ST_VOID _uSockHunt (GEN_SOCK *pSock, ST_INT *huntStateIo,
ST_CHAR *buf, ST_INT bufCount, ST_INT *lenOut);
static ST_VOID _uSockDataAlloc (GEN_SOCK *pSock, ST_INT dataLen,
GEN_SOCK_DATA **sockDataOut);
static ST_VOID _uSockDataFree (GEN_SOCK *pSock, GEN_SOCK_DATA *sockData);
/* misc functions */
static ST_RET _addSockInd (GEN_SOCK *pSock, ST_INT indType,
GEN_SOCK_DATA *sockData);
static ST_RET _handleRekeying (ST_VOID);
#define SOCK_REKEY_TIME 1000 /* check every 1000 ms if we need to rekey */
/************************************************************************/
/* Global variables. */
/************************************************************************/
int pipe_to_main; /* Linker needs this, but it is never used. */
#if defined(_WIN32)
ST_EVENT_SEM hTcpEvent; /* to notify user about network events */
#endif
#if defined(MMSEASE_MOSI)
/*----------------------------------------------------------------------*/
/* NOTES: */
/* 1. We set SO_KEEPALIVE opt on TCP sockets (in/out connections). */
/* 2. We set SO_REUSEADDR on TCP sockets (in connections). */
/* 3. The domain socket and TCP sockets are all non-blocking. */
/* 4. How many fds we can FD_SET (for TCP connections in gensock2) ? */
/* From the Linux: in the /usr/include/linux/posix_types.h the */
/* __FD_SETSIZE is 1024 (FD_SETSIZE) */
/* In gensock2.c there will be one service thread for every */
/* GS_MAX_SOCK_PER_SERVICE defined to be (FD_SETSIZE - 1). */
/*----------------------------------------------------------------------*/
#if !defined(GENSOCK_THREAD_SUPPORT)
#error GENSOCK_THREAD_SUPPORT must be defined for MMSEASE_MOSI
#endif
/* NOTE: These two sockets below need to be added to user's application */
/* select() statement. */
/* The mms_event_fd must always be added since it indicates events on */
/* all the TCP connections. */
/* The domsock_listener need to be added only if the flag */
/* use_rfc1006_listener_task=SD_TRUE. */
SOCKET mms_event_fd = INVALID_SOCKET; /* wakeup socket, indicating TCP Events */
SOCKET domsock_listener = INVALID_SOCKET; /* domain socket connected to the */
/* RFC1006_LISTENER task. */
ST_BOOLEAN use_rfc1006_listener_task = SD_TRUE; /*DEBUG:: add to tp0_cfg struct? */
#else /* !defined(MMSEASE_MOSI) */
ST_BOOLEAN use_rfc1006_listener_task = SD_FALSE;
#endif /* !defined(MMSEASE_MOSI) */
/* use macro to simplify the code */
#define CLOSE_THIS_SOCKET(hSock) \
{ \
if (hSock != INVALID_SOCKET) \
{ \
CLOSE_SOCKET (hSock); \
hSock = INVALID_SOCKET; \
} \
}
/* listening socket types */
#define SOCK_LISTEN_NON_SSL 1
#define SOCK_LISTEN_SSL 2
/* Ctx state */
#define TP0_SOCK_CTX_STATE_IDLE 0
#define TP0_SOCK_CTX_STATE_ACTIVE 1
#define TP0_SOCK_CTX_STATE_TERMINATING 2
/* struct to hold configuration and communication info for secured and */
/* non-secured connections. */
typedef struct tag_tp0_sock_ctx
{
ST_UINT state; /* TP0_SOCK_CTX_STATE_xxx */
S_SEC_CONFIG *secCfg; /* ptr to global configuration */
ST_UINT16 rfc1006Port; /* save the listen port used for RFC1006 connections */
GEN_SOCK *remListenSock; /* listen socket for non-secured cons from remotes */
GEN_SOCK *remListenSockSSL[S_SSL_MAX_LISTEN_PORTS]; /* tbl of secure listen sockets for */
/* connections from remotes */
SOCK_INFO *secureSockList; /* list of secured sockets */
#if defined(MMSEASE_MOSI)
ST_UINT16 tcpEventWakeupPortBase; /* base listen port */
ST_UINT tcpEventWakeupPortRange; /* range to search for free port */
ST_UINT16 tcpEventWakeupPort; /* actual wakeup port */
SOCKET hTcpEventSender; /* signaled by gensock2 service thread */
ST_CHAR listenerIPCDir[256]; /* IPC directory (where to find domsock file); */
/* set by calling setListenerIPCDir fun. If it */
/* is not set by user appl the default dir */
/* RFC1006_LISTENER_IPC_PATH will be used. */
#endif /* defined(MMSEASE_MOSI) */
GEN_SOCK_CTXT *sockCtx; /* this is gensock2 context for tp0_socks.c */
} TP0_SOCK_CTX;
TP0_SOCK_CTX tp0Ctx; /* must be global for !GENSOCK_THREAD_SUPPORT in event2.c */
static ST_RET _sockSetDefaults (GEN_SOCK_CONFIG *sockCfg);
static ST_RET _sockInitListen (ST_UINT type, ST_UINT idx, ST_INT max_num_conns);
static ST_RET _sockCloseAllListen (ST_VOID);
static ST_RET _sockClean (ST_VOID);
/*----------------------------------------------*/
/* MMSEASE_MOSI - RFC1006 Listener funcs */
/*----------------------------------------------*/
#if defined(MMSEASE_MOSI)
/************************************************************************/
/* setListenerIPCDir */
/* This function would be called by user application to set the */
/* RFC1006_LISTENER daemon IPC directory where UNIX domain socket */
/* should be created. If this function is not called the default path */
/* RFC1006_LISTENER_IPC_PATH will be used. */
/************************************************************************/
ST_RET setListenerIPCDir (ST_CHAR *dirPath)
{
ST_RET rtn = SD_FAILURE;
if (dirPath && strlen (dirPath) > 0)
{
strcpy (tp0Ctx.listenerIPCDir, dirPath); /* path to domsock file */
rtn = SD_SUCCESS;
}
return (rtn);
}
/* This section copied exactly from tp0_unix.c */
/************************************************************************/
/* This a special recv function only for receiving the socket handle */
/* from the "rfc1006_listener" task. It waits as long as the error is */
/* SOCK_WOULDBLOCK. On any other error, it stops waiting and returns. */
/* This function needs to take in account that the sender may fail in */
/* the sendmsg() by implementing timeout when waiting for the socket fd.*/
/************************************************************************/
ssize_t recvwait_fd (int fd, void *ptr, size_t nbytes, int *recvfd)
{
struct msghdr msg;
struct iovec iov[1];
ssize_t ret;
int err;
ST_DOUBLE recvmsg_start;
#define RECVMSG_TIMEOUT 10000 /* in milliseconds */
#if defined (OLD_WAY)
/* NOTE: On HP-UX to use the msg_accrights fields compile without */
/* defining the -D_XOPEN_SOURCE_EXTENDED, -D_OPEN_SOURCE and */
/* without linking the -lxnet library). */
#else
/* NOTE: On HP-UX to use the SCM_RIGHTS msg type compile with the */
/* -D_XOPEN_SOURCE_EXTENDED, -D_OPEN_SOURCE and link with the */
/* -lxnet library. Without linking the libxnet.* library the */
/* sendmsg() fails with EBADF error. */
/* See Richard Stevens "UNIX Network Programming" book. */
/* this just helps get alignment right */
union
{
struct cmsghdr cm;
char control [CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
#endif
SOCK_LOG_FLOW1 ("domsock %d: in recvwait_fd()", fd);
*recvfd = -1; /* init to invalid handle just in case the receive fails*/
#if defined (OLD_WAY)
msg.msg_accrights = (caddr_t) recvfd;
msg.msg_accrightslen = sizeof (int); /* must be int */
#else
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof (control_un.control);
#endif
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = ptr;
iov[0].iov_len = nbytes;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
/* Wait for data as long as error is SOCK_WOULDBLOCK. Stop on any other error.*/
/* NOTE: at this time there is only 1 byte of dummy data sent with this */
/* special message, but if this is changed to more bytes then the */
/* non-blocking recvmsg function may return with partial bytes. */
recvmsg_start = sGetMsTime();
while ((ret = recvmsg(fd, &msg, 0)) < 0)
{
err = SOCKET_ERRORNO;
if (!(err == SOCK_WOULDBLOCK || err == SOCK_INTR ||
err == SOCK_TIMEDOUT || err == SOCK_INPROGRESS))
break; /* some non-recoverable socket error */
/* check is we want to still wait longer for the msg */
if ((sGetMsTime() - recvmsg_start) >= RECVMSG_TIMEOUT)
{
SOCK_LOG_NERR3 ("domsock %d: timed out in recvmsg(), errno = %d, timeout=%d", fd, err, RECVMSG_TIMEOUT);
return (ret); /* timeout, other side failed to send the socket handle */
}
sMsSleep(20); /* sleep for a moment while waiting for the msg */
}
if (ret <= 0)
{
SOCK_LOG_NERR2 ("domsock %d: recvmsg() failed, errno = %d", fd, err);
return (ret); /* error we can't handle */
}
#if defined (OLD_WAY)
#else
if ((cmptr = CMSG_FIRSTHDR(&msg)) != NULL &&
cmptr->cmsg_len == CMSG_LEN(sizeof(int)))
{
if (cmptr->cmsg_level != SOL_SOCKET)
{
SOCK_LOG_NERR1 ("domsock %d: cmsg_level != SOL_SOCKET", fd);
*recvfd = -1; /* DEBUG: error. Book uses err_quit.*/
}
else if (cmptr->cmsg_type != SCM_RIGHTS)
{
SOCK_LOG_NERR1 ("domsock %d: cmsg_type != SCM_RIGHTS", fd);
*recvfd = -1; /* DEBUG: error. Book uses err_quit. */
}
else
*recvfd = *((int *) CMSG_DATA(cmptr));
}
else
*recvfd = -1; /* descriptor was not passed */
#endif
if (*recvfd == -1)
SOCK_LOG_NERR1 ("domsock %d: DID NOT receive socket handle from RFC1006_LISTENER task", fd);
else
SOCK_LOG_FLOW2 ("domsock %d: received socket handle = %d from RFC1006_LISTENER task",
fd, *recvfd);
return (ret);
}
/************************************************************************/
/* check_domsock */
/* This function checks if the there are any events pending from */
/* RFC1006_LISTENER task. */
/* Note that this function does not non-blocking select. */
/************************************************************************/
ST_RET check_domsock (int domsock)
{
fd_set rset, allset;
int nready;
int maxfd = 0;
struct timeval stTimeVal;
if (domsock == INVALID_SOCKET)
return (SD_FALSE);
FD_ZERO (&allset);
FD_SET (domsock, &allset);
maxfd = max (maxfd, domsock);
rset = allset;
/* 0 timeout on select. Don't want to wait here. */
stTimeVal.tv_sec = 0;
stTimeVal.tv_usec = 0;
nready = select (maxfd + 1, &rset, NULL, NULL, &stTimeVal);
if (nready > 0 && FD_ISSET (domsock, &rset))
return (SD_TRUE);
return (SD_FALSE);
}
/************************************************************************/
/* rfc1006_listener_connect */
/* Connects to the UNIX domain socket of the RFC1006 Listener task. */
/* Stores the connected socket handle at the address pointed to by */
/* domsock. */
/************************************************************************/
ST_RET rfc1006_listener_connect (SOCKET *domsock)
{
struct sockaddr_un s_un;
#if defined(__hpux) || defined(_AIX) || defined(sun) || defined(linux)
int nonblock_on=1; /* CRITICAL: must be non-zero to enable non-blocking*/
#else
/* _WIN32 */
ST_ULONG nonblock_on=1; /* CRITICAL: must be non-zero to enable non-blocking*/
#endif
int retcode;
*domsock = socket (AF_LOCAL, SOCK_STREAM, 0); /*DEBUG: why does some code use AF_UNIX */
if (*domsock == INVALID_SOCKET)
{
SOCK_LOG_ERR1 ("Cannot create UNIX domain socket(), errno=%d", SOCKET_ERRORNO);
return (SD_FAILURE);
}
s_un.sun_family = AF_LOCAL;
if (strlen (tp0Ctx.listenerIPCDir) == 0)
sprintf (s_un.sun_path, "%s/%s", RFC1006_LISTENER_IPC_PATH, RFC1006_LISTENER_DOMSOCK);
else
/* use the IPC directory that user set with call to setListenerIPCDir */
sprintf (s_un.sun_path, "%s/%s", tp0Ctx.listenerIPCDir, RFC1006_LISTENER_DOMSOCK);
retcode = connect (*domsock, (struct sockaddr *) &s_un, sizeof (s_un));
if (retcode == 0)
{
SOCK_LOG_FLOW2 ("domsock %d: connected to RFC1006_LISTENER task (DomSockFile=%s)",
*domsock, s_un.sun_path);
/* make the socket non-blocking */
if (ioctlsocket (*domsock, FIONBIO, &nonblock_on) == -1)
SOCK_LOG_ERR2 ("domsock %d: cannot set socket to non-blocking mode, errno = %d.",
*domsock, SOCKET_ERRORNO);
}
else
{
SOCK_LOG_ERR3 ("domsock %d: cannot connect() to RFC1006_LISTENER task (DomSockFile=%s), errno=%d, closing socket.",
*domsock, s_un.sun_path, SOCKET_ERRORNO);
CLOSE_SOCKET (*domsock);
*domsock = INVALID_SOCKET;
}
return (retcode);
}
/************************************************************************/
/* sockAcceptFromListenerTask */
/************************************************************************/
ST_RET sockAcceptFromListenerTask (SOCKET hNewSock,
char *tpkt_buf, /* ptr to TP0 connect.ind received from rfc1006_listener */
int tpkt_len)
{
ST_RET rc;
GEN_SOCK_CONFIG sockCfg;
GEN_SOCK *pSock;
#if !defined(_WIN32)
if (hNewSock >= FD_SETSIZE)
{ /* Can't use this socket because illegal to use in "select" call*/
SOCK_LOG_ERR1 ("sockAcceptFromListenerTask: socket num %d > FD_SETSIZE", hNewSock);
CLOSE_SOCKET (hNewSock);
return (SD_FAILURE);
}
#endif
/* Usually get sockCfg from pListenSock, but don't have that here, so just init local var*/
rc = _sockSetDefaults (&sockCfg);
/* OK, we have a go ... */
pSock = _sockAllocSock (tp0Ctx.sockCtx, GS_ROLE_CALLED, GS_STATE_CONNECTED, hNewSock, &sockCfg);
pSock->listenSocket = NULL; /* "listenSocket" not used with rfc1006_listener task */
SOCK_LOG_FLOW2 ("%s: new TCP connection passed from RFC1006_LISTENER, socket handle = %d",
pSock->sockIdStr, hNewSock);
/* Set callingAddr and callingAddrLen. */
pSock->callingAddrLen = sizeof (SOCKADDR_IN);
if (sockGetRemAddrInfo (pSock, &pSock->callingAddr, NULL, NULL) != SD_SUCCESS)
{
SOCK_LOG_ERR1 ("%s: error getting remote address info", pSock->sockIdStr);
}
/* only non-secured TCP connections */
rc = (*pSock->sockCfg.uSockConnectInd)(pSock);
if (rc == SD_SUCCESS)
_sockAddSock (pSock); /* Add to a service list */
else
{
CLOSE_SOCKET (hNewSock);
chk_free (pSock);
}
SOCK_LOG_FLOW2 ("%s: first packet received len = %d", pSock->sockIdStr, tpkt_len);
/* Allocate pSock->sockData and copy packet data to it. */
(*pSock->sockCfg.uSockRxBufAlloc) (pSock, tpkt_len, &pSock->sockData);
memcpy (pSock->sockData->data, tpkt_buf, tpkt_len);
pSock->sockData->dataLen = tpkt_len;
pSock->sockData->result = SD_SUCCESS;
/* Pass packet up to user just like normal packet. */
(*pSock->sockCfg.uSockRx)(pSock, pSock->sockData);
return (SD_SUCCESS);
}
/************************************************************************/
/* rfc1006_listener_event */
/************************************************************************/
ST_BOOLEAN rfc1006_listener_event (ST_VOID)
{
char tpkt_buf [1024]; /* this buffer is big enough for TP0 CR (per JRB) */
int tpkt_len;
int recv_ret;
ST_RET retcode;
SOCKET hSockConnected;
RFC1006_IPC_MSG ipc_msg;
char dummy_byte; /* one byte msg to read with recvwait_fd */
ST_BOOLEAN activityFlag = SD_FALSE; /* assume no events processed */
if (check_domsock (domsock_listener))
{
/* received msg on domain socket. Process it completely here. */
/* receiwait & recvwait_fd read TP0 connect.ind pdu from the listener task.
* It also gets the socket handle in hSockConnected.
*/
SOCK_LOG_FLOW1 ("domsock %d: receiving msg from RFC1006_LISTENER...", domsock_listener);
recv_ret = recvwait (domsock_listener, &ipc_msg, sizeof (RFC1006_IPC_MSG), 0);
if (recv_ret == sizeof (RFC1006_IPC_MSG))
{ /* NOTE: other opcodes maybe implemeted later */
if (ipc_msg.opcode != RFC1006_IPC_OP_CONNECT)
{
SOCK_LOG_ERR2 ("domsock %d: received invalid opcode=%u",
domsock_listener, (ST_UINT) ipc_msg.opcode);
return (activityFlag);
}
/* recvwait_fd waits for the socket handle in hSockConnected. */
recv_ret = (int) recvwait_fd (domsock_listener, &dummy_byte, 1, &hSockConnected);
if (recv_ret > 0)
{
assert (recv_ret == 1); /* must ALWAYS be this size */
/* This recvwait reads TP0 connect.ind pdu from the listener task.*/
/* NOTE: read (data_len-1) because 1 byte already read with recvwait_fd.*/
assert (ipc_msg.data_len-1 <= sizeof (tpkt_buf));
tpkt_len = recv_ret = recvwait (domsock_listener, tpkt_buf, ipc_msg.data_len-1, 0);
if (tpkt_len > 0 && hSockConnected >= 0)
{
/* rfc1006_listener task already did the "accept". */
/* This funct should get the whole machine running. */
retcode = sockAcceptFromListenerTask (hSockConnected, tpkt_buf, tpkt_len);
activityFlag = SD_TRUE;
}
}
}
/* DEBUG: In the future we could add code to reconnect to the RFC1006_LISTENER task */
/* and bind our selectors. For now just let the application to continue working.*/
if (recv_ret == 0)
{
SOCK_LOG_ERR1 ("domsock %d: recvwait() detected RFC1006_LISTENER socket disconnect", domsock_listener);
CLOSE_THIS_SOCKET(domsock_listener);
}
else if (recv_ret < 0)
{
SOCK_LOG_ERR2 ("domsock %d: recvwait() detected error (errno=%d), closing RFC1006_LISTENER socket",
domsock_listener, SOCKET_ERRORNO);
CLOSE_THIS_SOCKET(domsock_listener);
}
}
return (activityFlag);
}
#endif /* defined(MMSEASE_MOSI) */
/*----------------------------------------------*/
/* Initialization / Cleanup */
/*----------------------------------------------*/
/************************************************************************/
/* np_init */
/* NOTE: max_num_conns arg used only by listen sockets. */
/************************************************************************/
ST_RET np_init (ST_INT max_num_conns)
{
ST_RET rc;
ST_INT i;
#if defined(MMSEASE_MOSI)
tp0Ctx.tcpEventWakeupPortBase = 55051; /* base listen port */
tp0Ctx.tcpEventWakeupPortRange = 100; /* range to search for free port*/
tp0Ctx.tcpEventWakeupPort = 0; /* actual wakeup port */
tp0Ctx.hTcpEventSender = INVALID_SOCKET; /* set by gensock2 service thread*/
#endif
if (tp0Ctx.state != TP0_SOCK_CTX_STATE_IDLE)
{
SOCK_LOG_NERR0 (tp0Ctx.sockCtx, "np_init: TP0 Context State not IDLE.");
return (SD_FAILURE);
}
SOCK_LOG_FLOW1 (tp0Ctx.sockCtx, "np_init: initializing Network layer ... TCP max_num_conns = %d.", max_num_conns);
/* Create "TCP" Event Semaphore. */
#if defined(_WIN32)
if (!(hTcpEvent = gs_get_event_sem (SD_FALSE)))
{
SOCK_LOG_ERR0 (tp0Ctx.sockCtx, "np_init: error creating TCP Event Semaphore.");
return (SD_FAILURE);
}
#endif
/* initialize gensock2 before calling any of the socket functions */
rc = sockStart ("TP0_SOCKS", &tp0Ctx.sockCtx);
if (rc != SD_SUCCESS)
{
SOCK_LOG_ERR1 (tp0Ctx.sockCtx, "np_init: sockStart() failed, rc=%d", rc);
#if defined(_WIN32)
gs_free_event_sem (hTcpEvent);
#endif
return (rc);
}
#if defined(MMSEASE_MOSI)
if ((rc = sockCreateWakeupSockets ( tp0Ctx.sockCtx,
tp0Ctx.tcpEventWakeupPortBase, tp0Ctx.tcpEventWakeupPortRange,
&tp0Ctx.tcpEventWakeupPort,
&tp0Ctx.hTcpEventSender, &mms_event_fd)) != SD_SUCCESS)
{
sockEnd (tp0Ctx.sockCtx);
tp0Ctx.sockCtx = NULL;
return (rc);
}
#endif /* defined(MMSEASE_MOSI) */
/* start the security components */
rc = sSecStart (&tp0Ctx.secCfg);
if (rc != SD_SUCCESS)
{
SOCK_LOG_ERR1 (tp0Ctx.sockCtx, "np_init: sSecStart() failed, rc=%d", rc);
_sockClean ();
return (rc);
}
#if defined(MMSEASE_MOSI)
/* MMSEASE_MOSI uses the RFC1006_LISTENER task to listen for TCP connections, */
/* but it is not required to do so. Instead the application could listen for */
/* TCP connections itself if it is the only RFC1006 port listener in the system.*/
/* This is an undocumented option that maybe exposed in the future to */
/* selected customers when need arises. */
if (use_rfc1006_listener_task)
{
/* NOTE: if RFC1006_LITENER task is used then the UNIX domain socket will */
/* handle only non-secure connections. */
rc = rfc1006_listener_connect (&domsock_listener);
if (rc != SD_SUCCESS)
_sockClean ();
return (rc);
}
#endif /* defined(MMSEASE_MOSI) */
/* If !use_rfc1006_listener_task, continue with default code. */
#if !defined(MMSEASE_MOSI)
/* only MMS_LITE configures num_called */
if (mvl_cfg_info->num_called)
#endif
{
/* start listening for non-secured connections from remotes */
if (!tp0Ctx.secCfg->secureModeEnabled || tp0Ctx.secCfg->encryptReqCalled == SD_FALSE)
{
rc = _sockInitListen (SOCK_LISTEN_NON_SSL, 0, max_num_conns);
if (rc != SD_SUCCESS)
{
_sockClean ();
return (rc);
}
}
/* start listening for secured connections from remotes (multiple ports maybe configured) */
if (tp0Ctx.secCfg->secureModeEnabled)
{
for (i=0; i<tp0Ctx.secCfg->numSslListenPorts; ++i)
{
rc = _sockInitListen (SOCK_LISTEN_SSL, i, max_num_conns);
if (rc != SD_SUCCESS)
{
rc = _sockClean ();
return (SD_FAILURE);
}
}
}
}
if (rc == SD_SUCCESS)
{
tp0Ctx.state = TP0_SOCK_CTX_STATE_ACTIVE;
SOCK_LOG_FLOW0 (tp0Ctx.sockCtx, "np_init: initialization successful.");
}
return (rc);
}
/************************************************************************/
/* _sockSetDefaults */
/*----------------------------------------------------------------------*/
/* Initialize socket cfg params (used for secure/non-secure connections)*/
/************************************************************************/
static ST_RET _sockSetDefaults (GEN_SOCK_CONFIG *sockCfg)
{
ST_RET rc = SD_SUCCESS;
SOCK_LOG_FLOW0 (tp0Ctx.sockCtx, "in _sockSetDefaults()");
memset (sockCfg, 0, sizeof (GEN_SOCK_CONFIG)); /* start clean */
/* set socket parameters */
sockCfg->hdrSize = RFC1006_HEAD_LEN; /* RFC1006 header len */
/* set sockopt parameters */
sockCfg->setSockOpts = SD_TRUE;
sockCfg->noDelay = SD_TRUE;
sockCfg->keepAlive = SD_TRUE;
sockCfg->reuseAddr = SD_TRUE; /* SO_REUSEADDR for quick restart */
sockCfg->rcvBufSize = 0; /* use default socket buffer size */
sockCfg->sndBufSize = 0; /* use default socket buffer size */
sockCfg->pauseRecv = SD_FALSE; /* used to apply back pressure */
sockCfg->listenBacklog = 0; /* if 0, SOMAXCONN used */
/* set callback funcs for non-secured connections from remotes */
sockCfg->uSockConnectInd = &_uSockConnectInd;
sockCfg->uSockConnectConf= &_uSockConnectDone;
sockCfg->uSockDisconnect = &_uSockDisconnectInd;
sockCfg->uSockRx = &_uSockRxInd;
sockCfg->uSockWritable = &_uSockWritable;
sockCfg->uSockHunt = &_uSockHunt;
sockCfg->uSockRxBufAlloc = &_uSockDataAlloc;
sockCfg->uSockTxBufFree = &_uSockDataFree;
/* sockCfg->recvEvent = not used */
/* sockCfg->usr1 = not used */
return (rc);
}
/************************************************************************/
/* _sockInitListen */
/*----------------------------------------------------------------------*/
/* Initialize listen socket. */
/************************************************************************/
int cfg_61850_tcp_port=102;
static ST_RET _sockInitListen (ST_UINT type,
ST_UINT idx,
ST_INT max_num_conns)
{
ST_RET rc;
GEN_SOCK_CONFIG sockCfg;
ST_UINT16 listenPort;
ST_UINT maxActive;
GEN_SOCK **pListenSock;
SOCK_LOG_FLOW1 (tp0Ctx.sockCtx, "%s: in _sockInitListen()", tp0Ctx.sockCtx->ctxName);
/* set socket parameters */
rc = _sockSetDefaults (&sockCfg);
if (type == SOCK_LISTEN_SSL)
{
/* set callback functions for secure connections from remotes */
setGenSockSSL (&sockCfg); /* sets sockCfg.secEnable=SD_TRUE & secure fun ptrs */
listenPort = tp0Ctx.secCfg->sslListenPorts[idx];
maxActive = max_num_conns;
pListenSock = &tp0Ctx.remListenSockSSL[idx];
}
else
{
/* SOCK_LISTEN_NON_SSL */
if (tp0_cfg.rfc1006_listen_port)
listenPort = tp0_cfg.rfc1006_listen_port;
else
{
listenPort = IPPORT_RFC1006; /* use the default port */
if(cfg_61850_tcp_port)
listenPort = cfg_61850_tcp_port;
}
tp0Ctx.rfc1006Port = listenPort; /* save the port used */
maxActive = max_num_conns;
pListenSock = &tp0Ctx.remListenSock;
}
/* init the listening socket */
rc = sockInitListen (tp0Ctx.sockCtx, &sockCfg, listenPort, maxActive, pListenSock);
if (rc != SD_SUCCESS)
{
SOCK_LOG_ERR4 (tp0Ctx.sockCtx, "%s: sockInitListen() failed for port=%u, rc=%d %s",
tp0Ctx.sockCtx->ctxName, (ST_UINT) listenPort, rc,
(rc== SOCK_EADDRINUSE) ? "(port already used)" : "");
*pListenSock = NULL; /* need to be set if bind fails */
return (SD_FAILURE);
}
SOCK_LOG_FLOW2 (tp0Ctx.sockCtx, "%s: listening on port=%u", (*pListenSock)->sockIdStr, (ST_UINT) listenPort);
return (SD_SUCCESS);
}
/************************************************************************/
/* _sockCloseAllListen */
/* Closes all listen sockets. Currently the only return is SD_SUCCESS. */
/************************************************************************/
static ST_RET _sockCloseAllListen (ST_VOID)
{
ST_INT i;
#if defined(MMSEASE_MOSI)
/* disconnect domain socket to avoid incoming connections */
if (use_rfc1006_listener_task)
CLOSE_THIS_SOCKET(domsock_listener);
#endif
/* Close all listen sockets to avoid handling incoming connections */
if (tp0Ctx.secCfg)
for (i=0; i<tp0Ctx.secCfg->numSslListenPorts; ++i)
if (tp0Ctx.remListenSockSSL[i])
{
SOCK_LOG_FLOW2 (tp0Ctx.sockCtx, "%s: closing secure listening port=%d",
tp0Ctx.remListenSockSSL[i]->sockIdStr, tp0Ctx.secCfg->sslListenPorts[i]);
sockClose (tp0Ctx.remListenSockSSL[i]);
}
if (tp0Ctx.remListenSock)
{
SOCK_LOG_FLOW2 (tp0Ctx.sockCtx, "%s: closing listening port=%d",
tp0Ctx.remListenSock->sockIdStr, tp0Ctx.rfc1006Port);
sockClose (tp0Ctx.remListenSock);
}
return SD_SUCCESS;
}
/************************************************************************/
/* _sockClean */
/************************************************************************/
static ST_RET _sockClean (ST_VOID)
{
ST_RET rc;
SOCK_LOG_FLOW1 (tp0Ctx.sockCtx, "%s: in _sockClean()", tp0Ctx.sockCtx->ctxName);
/* CRITICAL: release resources in reverse order. */
/* close all listen sockets to avoid handling incoming connections */
_sockCloseAllListen ();
/* terminate Security Manager */
rc = sSecEnd ();
/* cleanup gensock2 resources, terminate threads,... */
rc = sockEnd (tp0Ctx.sockCtx);
tp0Ctx.sockCtx = NULL;
/* free events and mutexes */
#if defined(_WIN32)
gs_free_event_sem (hTcpEvent);
#endif
#if defined(MMSEASE_MOSI)
CLOSE_THIS_SOCKET(domsock_listener);
CLOSE_THIS_SOCKET(tp0Ctx.hTcpEventSender);
CLOSE_THIS_SOCKET(mms_event_fd);
#endif
return (SD_SUCCESS);
}
/************************************************************************/
/* np_end */
/* This function will terminate the operation of the Network layer */
/* */
/* Parameters: */
/* ST_VOID none */
/* */
/* Return: */
/* SD_SUCCESS (0) if termination successful */
/* error code otherwise */
/************************************************************************/
ST_RET np_end (ST_VOID)
{
ST_RET rc;
GEN_SOCK_EVENT *sdi;
SOCK_LOG_FLOW1 (tp0Ctx.sockCtx, "%s: in np_end()", tp0Ctx.sockCtx->ctxName);
tp0Ctx.state = TP0_SOCK_CTX_STATE_TERMINATING;
/* close all listen sockets to avoid handling incoming connections */
_sockCloseAllListen ();
/* Finish processing of disconnect events. Ignore all other events. */
sMsSleep (100); /* let other threads finish disconnects */
/* Get all events off list and process them. */
while ((sdi = sockEventGet (tp0Ctx.sockCtx)) != NULL)
{
if (sdi->eventType == GS_EVENT_DISCONNECT)
{
SOCK_LOG_FLOW1 (tp0Ctx.sockCtx, "%s: np_end: processing Event Indication IndType=DISCONNECT.",
sdi->pSock->sockIdStr);
_processDisconnectInd (sdi->pSock);
}
else if (sdi->eventType == GS_EVENT_CONNECT_IND)
{
SOCK_LOG_FLOW1 (tp0Ctx.sockCtx, "%s: np_end: IGNORING Event Indication IndType=CONNECT (closing socket).",
sdi->pSock->sockIdStr);
sockClose (sdi->pSock);
}
else if (sdi->eventType == GS_EVENT_DATA_IND)
{
SOCK_LOG_FLOW1 (tp0Ctx.sockCtx, "%s: np_end: IGNORING Event Indication IndType=RXDATA (closing socket).",
sdi->pSock->sockIdStr);
_uSockDataFree (sdi->pSock, sdi->sockData);
}
else
SOCK_LOG_FLOW3 (tp0Ctx.sockCtx, "%s: np_end: IGNORING Event Indication IndType=%d (%s).",
sdi->pSock->sockIdStr, sdi->eventType,
(sdi->eventType < sizeof(eventTypeStr)) ? eventTypeStr[sdi->eventType] : "UNKNOWN");
chk_free (sdi);
} /* end loop processing all events */
rc = _sockClean ();
tp0Ctx.state = TP0_SOCK_CTX_STATE_IDLE;
return (rc);
}
/*----------------------------------------------*/
/* Event/Indication Handling */
/*----------------------------------------------*/
#if defined(MMSEASE_MOSI)
ST_VOID drain_wakeup_sock (ST_VOID)
{
ST_CHAR dataBucket[256];
ST_INT recvRet;
ST_INT err;
/* Non-blocking receive, dump wakeup data */
if (mms_event_fd != INVALID_SOCKET)
{
recvRet = (ST_INT) recv (mms_event_fd, dataBucket, sizeof (dataBucket), 0);
if (recvRet > 0)
{
SOCK_LOG_FLOW3 ("%s: XSocket received %d wakeup bytes, byte[0]=%d",
tp0Ctx.sockCtx->ctxName, recvRet, (ST_UINT) ((ST_UCHAR) dataBucket[0]));
}
else if (recvRet == 0)
{
SOCK_LOG_ERR1 ("%s: XSocket disconnected", tp0Ctx.sockCtx->ctxName);
CLOSE_THIS_SOCKET(mms_event_fd);
}
else
{
err = SOCKET_ERRORNO;
if (err == SOCK_WOULDBLOCK || err == SOCK_INTR ||
err == SOCK_TIMEDOUT || err == SOCK_INPROGRESS)
{
/* recoverable error or nothing received */
}
else
{
SOCK_LOG_ERR3 ("%s: XSocket error, recv() returned %d, errno = %d",
tp0Ctx.sockCtx->ctxName, recvRet, err);
CLOSE_THIS_SOCKET(mms_event_fd);
}
}
}
}
#endif /* defined(MMSEASE_MOSI) */
/************************************************************************/
/* np_event */
/* This function is called to process network events. */
/************************************************************************/
ST_BOOLEAN np_event (ST_VOID)
{
GEN_SOCK_EVENT *sdi;
ST_BOOLEAN activityFlag = SD_FALSE; /* assume no events processed */
#if defined(MMSEASE_MOSI)
if (use_rfc1006_listener_task)
rfc1006_listener_event ();
#endif /* defined(MMSEASE_MOSI) */
#if !defined(GENSOCK_THREAD_SUPPORT)
/* Service all sockets. */
/* NOTE: this may add "many" entries to the linked list. */
/* User should have already waited for event, so use timeout=0 here. */
sockServiceAll (tp0Ctx.sockCtx, 0); /* arg is timeout in milliseconds*/
#endif /* !GENSOCK_THREAD_SUPPORT */
#if defined(MMSEASE_MOSI)
drain_wakeup_sock ();
#endif
/* CRITICAL: Get one and only one entry off linked list. If we get more,*/
/* MVL queue fills up, and user can't process other events, including TP4.*/
if ((sdi = sockEventGet (tp0Ctx.sockCtx)) != NULL)
{
activityFlag = SD_TRUE; /* 1 event processed */
S_LOCK_UTIL_RESOURCES ();
/* check if we need to enable reception of data (we have flow control to */
/* prevent excessive memory allocations when data messages are received */
/* faster than the application can process) */
if (sdi->eventType == GS_EVENT_DATA_IND)
{
SOCK_INFO *sock_info = (SOCK_INFO *) sdi->pSock->sockCfg.usr2; /* get (SOCK_INFO *) from GEN_SOCK */
if (sock_info->recvCnt > 0)
--sock_info->recvCnt;
if (sock_info->recvCnt <= SOCK_RX_QUE_LOW_MARK && sdi->pSock->sockCfg.pauseRecv == SD_TRUE)
{
sdi->pSock->sockCfg.pauseRecv = SD_FALSE;
SOCK_LOG_FLOW2 (tp0Ctx.sockCtx, "%s: recvCnt=%u setting pauseRecv=SD_FALSE", sdi->pSock->sockIdStr, sock_info->recvCnt);
sockServiceWakeAll (tp0Ctx.sockCtx);
}
}
S_UNLOCK_UTIL_RESOURCES ();
SOCK_LOG_FLOW3 (tp0Ctx.sockCtx, "%s: processing Event Indication IndType=%d (%s).",
sdi->pSock->sockIdStr, sdi->eventType,
(sdi->eventType < sizeof(eventTypeStr)) ? eventTypeStr[sdi->eventType] : "UNKNOWN");
switch (sdi->eventType)
{
case GS_EVENT_CONNECT_IND:
//logprint("Sokect_GetConnect_rec(IP:%s)",inet_ntoa(sdi->pSock->callingAddr.sin_addr));
_processConnectInd (sdi->pSock);
break;
case GS_EVENT_CONNECT_CONF:
_processConnectDone (sdi->pSock);
break;
case GS_EVENT_DISCONNECT:
//logprint("Sokect_DisConnect_rec(IP:%s)",inet_ntoa(sdi->pSock->callingAddr.sin_addr));
_processDisconnectInd (sdi->pSock);
break;
case GS_EVENT_DATA_IND:
_processRxInd (sdi->pSock, sdi->sockData);
break;
default:
assert (0); /* Unknown eventType */
}
chk_free (sdi);
}
/* we have to take care of rekeying every once a while, since this */
/* function is called periodically (at least once every 1 sec) we */
/* use it to do the job */
_handleRekeying ();
return (activityFlag);
}
/************************************************************************/
/* _setSecInfo */
/************************************************************************/
static ST_RET _setSecInfo (GEN_SOCK *pSock, S_SEC_ENCRYPT_CTRL *encrypt_ctrl)
{
ST_RET rc;
rc = sSecGetCipherSuite (pSock, &encrypt_ctrl->u.ssl.cipherSuite);
if (rc != SD_SUCCESS)
{
SOCK_LOG_NERR2 (tp0Ctx.sockCtx, "%s: failed to obtain Cipher Suite (rc=%d)", pSock->sockIdStr, rc);
return (rc);
}
rc = sSecGetCertCtrl (pSock, &encrypt_ctrl->u.ssl.sslCert);
if (rc == SD_SUCCESS)
{
/* SD_SUCCESS means that this is a secured connection */
encrypt_ctrl->encryptMode = S_SEC_ENCRYPT_SSL;
/* if the remote certificate is not config the function above may return */
/* NULL for the sslCert pointer */
if (encrypt_ctrl->u.ssl.sslCert)
encrypt_ctrl->u.ssl.sslCertMatched = SD_TRUE;
else
encrypt_ctrl->u.ssl.sslCertMatched = SD_FALSE;
}
else
SOCK_LOG_NERR2 (tp0Ctx.sockCtx, "%s: failed to obtain Certificate Ctrl (rtn=%d)", pSock->sockIdStr, rc);
return (rc);
}
/************************************************************************/
/* _processConnectInd */
/* Accepted new socket connection, let user know. */
/************************************************************************/
static ST_VOID _processConnectInd (GEN_SOCK *pSock)
{
ST_RET rc;
SOCK_INFO *sock_info;
S_SEC_ENCRYPT_CTRL *encrypt_ctrl;
/* For a listening socket we will attach the GEN_SOCK to our user ctrl*/
/* Pass (GEN_SOCK *) instead of SOCKET as first arg. */
/* This saves (GEN_SOCK *) in SOCK_INFO. */
sock_info = sock_info_alloc2 (pSock, SOCK_STATE_ACCEPTED,
INVALID_CONN_ID);
sock_info->ip_addr = pSock->callingAddr.sin_addr.s_addr; /* Save remote IP addr */
pSock->sockCfg.usr2 = sock_info; /* Save (SOCK_INFO *) in GEN_SOCK*/
encrypt_ctrl = &sock_info->encrypt_ctrl;
/* if secured connection extract encryption info */
if (pSock->sockCfg.secEnable)
{
rc = _setSecInfo (pSock, encrypt_ctrl);
if (rc != SD_SUCCESS)
{
pSock->sockCfg.usr2 = NULL; /* to prevent use of invalid sock_info */
sock_info_free (sock_info);
sockClose (pSock);
return;
}
sock_info->rekeyTime = sGetMsTime() + SOCK_REKEY_TIME;
/* add sock_info to list of secured sockets */
S_LOCK_UTIL_RESOURCES ();
list_add_last (&tp0Ctx.secureSockList, sock_info);
S_UNLOCK_UTIL_RESOURCES ();
}
handle_accepted_conn (sock_info);
}
/************************************************************************/
/* handle_accepted_conn */
/************************************************************************/
ST_VOID handle_accepted_conn (SOCK_INFO *sock_info)
{
/* User only cares about T-CONNECT.ind. Just log this. */
SOCK_LOG_RX1 (tp0Ctx.sockCtx, "N-CONNECT.ind: sock_info = 0x%X", sock_info);
sock_info->state = SOCK_STATE_CONNECTED;
}
/************************************************************************/
/* _processConnectDone */
/* Socket connection established, let user know. */
/************************************************************************/
static ST_VOID _processConnectDone (GEN_SOCK *pSock)
{
ST_RET rc;
SOCK_INFO *sock_info = (SOCK_INFO *) pSock->sockCfg.usr2; /* get (SOCK_INFO *) from GEN_SOCK */
S_SEC_ENCRYPT_CTRL *encrypt_ctrl;
assert (sock_info);
sock_info->genSock = pSock; /* CRITICAL: save pSock now */
/* it is possible that if the connection took long time then the application */
/* called already np_disconnect_req */
if (sock_info->state == SOCK_STATE_CONNECT_CANCELLED)
sockClose (pSock); /* couldn't call this function from np_disconnect_req */
else if (sock_info->state == SOCK_STATE_CONNECTING)
{
/* if secured connection extract encryption info */
if (pSock->sockCfg.secEnable)
{
encrypt_ctrl = &sock_info->encrypt_ctrl;
rc = _setSecInfo (pSock, encrypt_ctrl);
if (rc != SD_SUCCESS)
{
sockClose (pSock);
return;
}
sock_info->rekeyTime = sGetMsTime() + SOCK_REKEY_TIME;
/* add sock_info to list of secured sockets */
S_LOCK_UTIL_RESOURCES ();
list_add_last (&tp0Ctx.secureSockList, sock_info);
S_UNLOCK_UTIL_RESOURCES ();
}
handle_connect_success (sock_info);
}
else
SOCK_LOG_ERR2 (tp0Ctx.sockCtx, "%s: _processConnectDone invalid state=%d", pSock->sockIdStr, sock_info->state);
}
/************************************************************************/
/* handle_connect_success */
/************************************************************************/
ST_VOID handle_connect_success (SOCK_INFO *sock_info)
{
/* Pass up N-CONNECT.cnf+ to user. */
SOCK_LOG_RX1 (tp0Ctx.sockCtx, "N-CONNECT.cnf+ (pos): user_conn=%d", sock_info->user_conn_id);
np_connect_cnf_pos (sock_info, sock_info->user_conn_id);
sock_info->state = SOCK_STATE_CONNECTED;
}
/************************************************************************/
/* _processDisconnectInd */
/************************************************************************/
static ST_VOID _processDisconnectInd (GEN_SOCK *pSock)
{
SOCK_INFO *sock_info = (SOCK_INFO *) pSock->sockCfg.usr2;
if (pSock->sockCfg.secEnable)
{
if (sock_info)
{
S_LOCK_UTIL_RESOURCES ();
if (list_find_node (tp0Ctx.secureSockList, sock_info) == SD_SUCCESS)
/* del sock_info from list of secured sockets */
list_unlink (&tp0Ctx.secureSockList, sock_info);
S_UNLOCK_UTIL_RESOURCES ();
}
else
{
/* in this case a remote node attempted to connect on a secured port */
/* but for some SSL reason the socket connection need to be terminated */
sockClose (pSock);
sockFree (pSock);
return; /* can't do anything else */
}
}
if (sock_info)
{
/* If we disconnected, don't call handle_disconnect. */
if (!(sock_info->state == SOCK_STATE_DISCONNECTING ||
sock_info->state == SOCK_STATE_CONNECT_CANCELLED))
handle_disconnect (sock_info);
sock_info_free (sock_info);
}
else
SOCK_LOG_ERR1 (tp0Ctx.sockCtx, "%s: _processDisconnectInd sock_info=NULL", pSock->sockIdStr);
sockFree (pSock); /* Totally done with this socket now. */
}
/************************************************************************/
/* handle_disconnect */
/************************************************************************/
ST_VOID handle_disconnect (SOCK_INFO *sock_info)
{
SOCK_LOG_RX2 (tp0Ctx.sockCtx, "N-DISCONNECT.ind: sock_info = 0x%X, user_conn = %d",
sock_info, sock_info->user_conn_id);
np_disconnect_ind (sock_info);
}
/************************************************************************/
/* _processRxInd */
/************************************************************************/
static ST_VOID _processRxInd (GEN_SOCK *pSock, GEN_SOCK_DATA *sockData)
{
/* data is only valid if result==SD_SUCCESS */
if (sockData->result == SD_SUCCESS)
{
/* rxData->data includes 4-byte RFC-1006 header. That's what handle_data wants.*/
assert (sockData->dataLen <= 65535); /* RFC-1006 limit */
handle_data ((SOCK_INFO *)pSock->sockCfg.usr2, sockData->data,
(ST_UINT16)sockData->dataLen);
}
_uSockDataFree (pSock, sockData);
}
/************************************************************************/
/* handle_data */
/* Parameters: */
/* sock_info ptr to socket tracking struct */
/* tpkt_ptr ptr to RFC1006 TPKT. */
/* tpkt_len len of RFC1006 TPKT. */
/************************************************************************/
ST_VOID handle_data (SOCK_INFO *sock_info, ST_UCHAR *tpkt_ptr, ST_UINT16 tpkt_len)
{
SOCK_LOG_RX3 (tp0Ctx.sockCtx, "N-DATA.ind: sock_info = 0x%X, user_conn = %d tpkt_len = %d",
sock_info, sock_info->user_conn_id, tpkt_len);
SOCK_LOG_RXH (tp0Ctx.sockCtx, tpkt_len, tpkt_ptr);
np_data_ind (sock_info, tpkt_ptr, tpkt_len);
}
/*----------------------------------------------*/
/* Connection Request / Indication */
/*----------------------------------------------*/
/************************************************************************/
/* np_connect_req */
/* RETURNS: */
/* SOCK_INFO * ptr to socket info for new socket, OR */
/* NULL if connect fails immediately. */
/************************************************************************/
int win_61850_tcp_port=102;
SOCK_INFO *np_connect_req (ST_LONG user_conn_id, ST_ULONG ipAddr, ST_UINT16 rem_port,
S_SEC_ENCRYPT_CTRL *encrypt_ctrl)
{
ST_RET rc;
SOCK_INFO *sock_info; /* new tracking struct for this conn. */
struct in_addr sin_addr; /* inet_ntoa needs this addr format */
ST_CHAR *tmp_ptr;
ST_CHAR addr_string [32]; /* local copy of addr */
GEN_SOCK_CONFIG sockCfg = {0};
ST_UINT16 port;
GEN_SOCK *pGenSock;
SOCK_LOG_TX1 (tp0Ctx.sockCtx, "N-CONNECT.req: user_conn=%d", user_conn_id);
/* MMS Lite stores IP Addr as ULONG. Must convert back to string. */
sin_addr.s_addr = ipAddr;
tmp_ptr = inet_ntoa (sin_addr);
assert (strlen (tmp_ptr) < sizeof (addr_string));
strcpy (addr_string, tmp_ptr);
/* Must do this first, because connect ind callback may be called
* before returning from sockInitCalling!
*/
sock_info = sock_info_alloc2 (NULL, SOCK_STATE_CONNECTING, user_conn_id);
/* setup rem socket params and connect to the rem IP Addr, Port */
_sockSetDefaults (&sockCfg);
sockCfg.reuseAddr = SD_FALSE; /* Override setting from _sockSetDefaults.*/
/* save our control ptr (used in callback functions) */
sockCfg.usr2 = (ST_VOID *) sock_info;
if (encrypt_ctrl->encryptMode == S_SEC_ENCRYPT_SSL)
{
/* secure connection requested, set the security functions hooks */
setGenSockSSL (&sockCfg); /* sets sockCfg.secEnable=SD_TRUE & secure fun ptrs */
port = encrypt_ctrl->u.ssl.port;
}
else
{
/* non-secured connection */
if (rem_port)
port = rem_port; /* use configured remote port */
else
port = IPPORT_RFC1006;
}
#ifdef WIN32
port = win_61850_tcp_port;
#endif
/* connect to Remote */
/* NOTE: The callback may be called before
* sockInitCalling returns, so just save the (GEN_SOCK *) to "sock_info->genSock"
* in the callback function (_uSockConnectInd).
*/
SOCK_LOG_TX4 (tp0Ctx.sockCtx, "N-CONNECT.req: sock_info=0x%X, connection pending to IP Address='%s' Port=%u %s",
sock_info, addr_string, port,
(encrypt_ctrl->encryptMode==S_SEC_ENCRYPT_SSL) ? "(SSL)" : "");
rc = sockInitCalling (tp0Ctx.sockCtx, &sockCfg, port, addr_string, &pGenSock);
/* DEBUG Note: the pGenSock may need to be in the SOCK_INFO struct */
if (rc != SD_SUCCESS)
{ /* failed, clean up */
sock_info_free (sock_info);
sock_info = NULL;
}
return (sock_info);
}
/************************************************************************/
/* _uSockConnectDone */
/* This is gensock2 callback function called when socket connection */
/* to the remote has been established. */
/************************************************************************/
static ST_RET _uSockConnectDone (GEN_SOCK *pSock)
{
ST_RET rtn;
SOCK_LOG_FLOW1 (tp0Ctx.sockCtx, "%s: in _uSockConnectDone()", pSock->sockIdStr);
rtn = _addSockInd (pSock, GS_EVENT_CONNECT_CONF, NULL);
return (rtn);
}
/************************************************************************/
/* _uSockConnectInd */
/* This is gensock2 callback function when a socket connection has been */
/* accepted from remote. */
/************************************************************************/
static ST_RET _uSockConnectInd (GEN_SOCK *pSock)
{
ST_RET rtn;
ST_CHAR *SSLstr;
if (pSock->sockCfg.secEnable)
SSLstr = "SSL";
else
SSLstr = "";
SOCK_LOG_FLOW4 (tp0Ctx.sockCtx, "%s: in _uSockConnectInd(), received %s connection from"
" IP Address=%s Port=%u", pSock->sockIdStr, SSLstr,
inet_ntoa (pSock->callingAddr.sin_addr), (ST_UINT) ntohs (pSock->callingAddr.sin_port));
rtn = _addSockInd (pSock, GS_EVENT_CONNECT_IND, NULL);
return (rtn);
}
/*----------------------------------------------*/
/* Disconnection Request / Indication */
/*----------------------------------------------*/
/************************************************************************/
/* np_disconnect_req */
/* This function is called by the user to break connection */
/************************************************************************/
ST_RET np_disconnect_req (SOCK_INFO *sock_info)
{
ST_RET retCode;
SOCK_LOG_TX2 (tp0Ctx.sockCtx, "N-DISCONNECT.req: sock_info = 0x%X, user_conn = %d",
sock_info, sock_info->user_conn_id);
if (sock_info->genSock == NULL)
{
/* this is the case when application issues connect request but calls disconnect */
/* before the socket connection gets established (for example because of timeout). */
SOCK_LOG_NERR3 (tp0Ctx.sockCtx, "N-DISCONNECT.req: disconnect delayed (connect pending), \n"
" sock_info = 0x%X, user_conn = %d, state = %d",
sock_info, sock_info->user_conn_id, sock_info->state);
/* change the state to avoid calling any transport functions from this point on */
sock_info->state = SOCK_STATE_CONNECT_CANCELLED;
retCode = SD_FAILURE;
}
else
{
/* change the state to avoid calling any transport functions from this point on */
sock_info->state = SOCK_STATE_DISCONNECTING;
retCode = sockClose (sock_info->genSock);
}
return (retCode);
/* sock_info is not freed until _processDisconnectInd. */
}
/************************************************************************/
/* _uSockDisconnectInd */
/* This is gensock2 callback function when socket gets disconnected or */
/* we call sockClose. */
/************************************************************************/
static ST_VOID _uSockDisconnectInd (GEN_SOCK *pSock)
{
ST_RET rtn;
ST_CHAR *errptr;
ST_INT i;
switch (pSock->disconnectReason)
{
case GS_DISCONNECT_CONNECT_FAILED: errptr= "CONNECT_FAILED"; break;
case GS_DISCONNECT_USR_REFUSED: errptr= "USER_REFUSED"; break;
case GS_DISCONNECT_SEND_FAILURE: errptr= "SEND_FAILURE"; break;
case GS_DISCONNECT_RECV_FAILED: errptr= "RECEIVE_FAILED"; break;
case GS_DISCONNECT_ACCEPT_FAILED: errptr= "ACCEPT_FAILED"; break;
case GS_DISCONNECT_CLOSED: errptr= "SOCKET_CLOSED"; break;
case GS_DISCONNECT_TERMINATING: errptr= "TERMINATING"; break;
case GS_DISCONNECT_RESOURCES_ERROR: errptr= "RESOURCES_ERROR"; break;
case GS_DISCONNECT_INTERNAL_ERROR: errptr= "INTERNAL_ERROR"; break;
case GS_DISCONNECT_UNKNOWN:
default: errptr= "UNKNOWN"; break;
}
SOCK_LOG_FLOW2 (tp0Ctx.sockCtx, "%s: in _uSockDisconnectInd() reason=%s", pSock->sockIdStr, errptr);
/* CRITICAL: deal with GS_ROLE_LISTENING now. This happens when server is
* exiting. _processDisconnectInd may not be called again.
*/
if (pSock->role == GS_ROLE_LISTENING)
{
if (pSock == tp0Ctx.remListenSock)
{
SOCK_LOG_FLOWC1 (tp0Ctx.sockCtx, " for non-secured listening port=%d", tp0Ctx.rfc1006Port);
tp0Ctx.remListenSock = NULL;
}
else
{
for (i=0; i<S_SSL_MAX_LISTEN_PORTS; ++i)
{
if (pSock == tp0Ctx.remListenSockSSL[i])
{
SOCK_LOG_FLOWC1 (tp0Ctx.sockCtx, " for secured listening port=%d", tp0Ctx.secCfg->sslListenPorts[i]);
tp0Ctx.remListenSockSSL[i] = NULL;
break;
}
}
}
sockFree (pSock);
return;
}
rtn = _addSockInd (pSock, GS_EVENT_DISCONNECT, NULL);
if (rtn != SD_SUCCESS && tp0Ctx.state == TP0_SOCK_CTX_STATE_TERMINATING)
{
SOCK_INFO *sock_info = pSock->sockCfg.usr2;
/* since we will not pass the disconnect ind to the main thread clean the */
/* sock_info here */
if (sock_info)
sock_info_free (sock_info);
sockFree (pSock);
}
}
/*----------------------------------------------*/
/* Data Request / Indication */
/*----------------------------------------------*/
/************************************************************************/
/* np_data_req */
/* Parameters: */
/* eot SD_TRUE if last TPDU msg in a SPDU. */
/* RETURN CODES: */
/* SD_SUCCESS or SD_FAILURE */
/************************************************************************/
/*renxiaobao <20><><EFBFBD>ķ<EFBFBD><C4B7><EFBFBD>*/
ST_RET np_data_req (SOCK_INFO *sock_info, ST_INT tpkt_len, ST_UCHAR *tpkt_ptr,
ST_BOOLEAN eot)
{
GEN_SOCK_DATA *sockData;
ST_RET rc;
/*//static int qqq=0;
//assert (sock_info->genSock != NULL);
*/ if(sock_info->genSock == NULL)
{
printf("sock_info->genSock == NULL\n");
return (SD_FAILURE);
}
/*// if(qqq++>500)
//{
// qqq=0;
// sockClose (sock_info->genSock);
// sockFree (sock_info->genSock);
// return (SD_FAILURE);
//}
*/ /* check if queueing limit has been reached to prevent the excessive */
/* growth of the outbound queue in gensock2.c */
if (sockTxQueueGroupCntGet (sock_info->genSock) >= tp0_cfg.max_spdu_outst)
{
SOCK_LOG_ERR2 (tp0Ctx.sockCtx, "N-DATA.req: sock_info = 0x%X, error sending (queue limit = %u reached)",
sock_info, tp0_cfg.max_spdu_outst);
/*renxiaobao <20><><EFBFBD>ķ<EFBFBD><C4B7><EFBFBD>ʧ<EFBFBD><CAA7>
printf(tp0Ctx.sockCtx, "N-DATA.req: sock_info = 0x%X, error sending (queue limit = %u reached)",
sock_info, tp0_cfg.max_spdu_outst);*/
return (SD_FAILURE);
}
/* _uSockDataAlloc allocs "sockData" (plus space for data) and initializes it.*/
_uSockDataAlloc (sock_info->genSock, tpkt_len, &sockData);
memcpy (sockData->data, tpkt_ptr, tpkt_len); /* copy the data */
sockData->eot = eot;
rc = sockTxMsg (sock_info->genSock, sockData);
if (rc == SD_SUCCESS)
{
SOCK_LOG_TX3 (tp0Ctx.sockCtx, "N-DATA.req: sock_info = 0x%X, user_conn = %d tpkt_len = %d",
sock_info, sock_info->user_conn_id, tpkt_len);
SOCK_LOG_TXH (tp0Ctx.sockCtx, tpkt_len, tpkt_ptr);
}
else
{
SOCK_LOG_ERR1 (tp0Ctx.sockCtx, "N-DATA.req: sock_info = 0x%X, error sending", sock_info);
/*printf (tp0Ctx.sockCtx, "N-DATA.req: sock_info = 0x%X, error sending", sock_info);*/
}
return (rc);
}
/************************************************************************/
/* np_get_tx_queue_cnt */
/* Returns number of queued SPDUs. */
/************************************************************************/
ST_UINT np_get_tx_queue_cnt (SOCK_INFO *sock_info)
{
assert (sock_info->genSock != NULL);
return (sockTxQueueGroupCntGet (sock_info->genSock));
}
/************************************************************************/
/* _uSockWritable */
/* This function is called by gensock2 when the socket becomes writable.*/
/************************************************************************/
static ST_VOID _uSockWritable (GEN_SOCK *pSock)
{
ST_RET rc;
SOCK_LOG_FLOW1 (tp0Ctx.sockCtx, "%s: in _uSockWritable()", pSock->sockIdStr);
/* the socket is writable, so send something from the transmit queue. */
rc = sockTxQueueProc (pSock);
if (rc != SD_SUCCESS)
{
SOCK_LOG_ERR1 (tp0Ctx.sockCtx, "%s: sockTxQueueProc failed, closing socket", pSock->sockIdStr);
/* DEBUG: this change of state will cause problem, */
/* app will not be notified about disconnect */
/* sock_info->state = SOCK_STATE_DISCONNECTING; */
sockClose (pSock);
}
}
/************************************************************************/
/* _uSockRxInd */
/************************************************************************/
static ST_VOID _uSockRxInd (GEN_SOCK *pSock, GEN_SOCK_DATA *sockData)
{
ST_RET rtn;
if (pSock && sockData)
SOCK_LOG_FLOW3 (tp0Ctx.sockCtx, "%s: in _uSockRxInd() result=%s, bytes=%d", pSock->sockIdStr,
(sockData->result == SD_SUCCESS) ? "SD_SUCCESS" : "SD_FAILURE", sockData->dataLen);
else
{
SOCK_LOG_ERR2 (tp0Ctx.sockCtx, "%s: in _uSockRxInd() sockData=%08lx", pSock->sockIdStr, sockData);
if (sockData)
_uSockDataFree (pSock, sockData);
return;
}
if (sockData->result != SD_SUCCESS)
{
/* socket is disconnecting, any received data buffers need to be free */
_uSockDataFree (pSock, sockData);
return;
}
/* If no data (len=RFC1006_HEAD_LEN) ignore msg. */
if (sockData->dataLen <= RFC1006_HEAD_LEN)
{
/* Should NEVER be '<' because _uSockHunt checks for illegal len, but just in case...*/
if (sockData->dataLen < RFC1006_HEAD_LEN)
SOCK_LOG_ERR1 (tp0Ctx.sockCtx, "Illegal TPKT len = %d", sockData->dataLen);
/* May be '='. NOT normal, but some bad apps could send just header.*/
/* Either way, do NOT put on queue. */
_uSockDataFree (pSock, sockData);
return;
}
/* received data on the socket, queue indication for user */
rtn = _addSockInd (pSock, GS_EVENT_DATA_IND, sockData);
if (rtn != SD_SUCCESS)
_uSockDataFree (pSock, sockData);
}
/************************************************************************/
/* uSockHunt */
/* This function will get the length of received message on a socket. */
/* We expect the size field to be RFC1006_HEAD_LEN bytes. */
/************************************************************************/
static ST_VOID _uSockHunt (GEN_SOCK *pSock, ST_INT *huntStateIo,
ST_CHAR *buf, ST_INT bufCount, ST_INT *lenOut)
{
ST_UINT16 u16len;
/* Better get our 4 byte RFC-1006 header ... */
if (bufCount != RFC1006_HEAD_LEN)
{
/* This should NEVER happen. gensock2 state machine must be corrupted.*/
SOCK_LOG_ERR1 (tp0Ctx.sockCtx, "%s: Socket Hunt for start of message failed.\n"
" Error could be unrecoverable.", pSock->sockIdStr);
*huntStateIo = GENSOCK_HUNT_DISCONNECT;
return;
}
if (buf[0] != RFC1006_VERSION)
{
SOCK_LOG_ERR1 (tp0Ctx.sockCtx, "%s: Received RFC 1006 header with invalid version (first byte). Header:",
pSock->sockIdStr);
SOCK_LOG_ERRH (tp0Ctx.sockCtx, 4, buf);
*huntStateIo = GENSOCK_HUNT_DISCONNECT;
return;
}
/* Ignore 2nd byte (reserved). */
/* len is in 3rd/4th bytes, in network byte order, and includes len of header.
* Convert to host byte order, & subtract header len.
* CRITICAL: cast to ST_UCHAR before ST_UINT16 to avoid sign extension.
*/
u16len = (((ST_UINT16)(ST_UCHAR) buf[2]) << 8) | (ST_UINT16)(ST_UCHAR) buf[3];
/* CRITICAL: make sure len is at least RFC1006_HEAD_LEN so subtracting*/
/* does not create negative number. */
if (u16len < RFC1006_HEAD_LEN)
{
SOCK_LOG_ERR2 (tp0Ctx.sockCtx, "%s: Received RFC 1006 header with invalid length (%u). Header:",
pSock->sockIdStr, u16len);
SOCK_LOG_ERRH (tp0Ctx.sockCtx, RFC1006_HEAD_LEN, buf);
*huntStateIo = GENSOCK_HUNT_DISCONNECT;
return;
}
u16len -= RFC1006_HEAD_LEN; /* subtract header len */
/* NOTE: cast to ST_UINT before ST_INT to avoid sign extension. */
*lenOut = (ST_INT)(ST_UINT)u16len;
*huntStateIo = GENSOCK_HUNT_DONE;
}
/*----------------------------------------------*/
/* Alloc / Free */
/*----------------------------------------------*/
/************************************************************************/
/* sock_info_alloc2 */
/* Allocate a SOCK_INFO struct and fill it in. */
/************************************************************************/
SOCK_INFO *sock_info_alloc2 (GEN_SOCK *genSock, ST_INT state, ST_LONG user_conn_id)
{
SOCK_INFO *sock_info;
/* Allocate SOCK_INFO struct. */
sock_info = (SOCK_INFO *) M_CALLOC (MSMEM_SOCK_INFO, 1, sizeof (SOCK_INFO));
/* Fill in SOCK_INFO struct. */
/* NOTE: sock_info->hSock is NOT used. sock_info->genSock is used instead.*/
sock_info->genSock = genSock;
sock_info->state = state;
sock_info->user_conn_id = user_conn_id;
sock_info->encrypt_ctrl.encryptMode = S_SEC_ENCRYPT_NONE;
sock_info->recvCnt = 0;
return (sock_info);
}
/************************************************************************/
/* sock_info_free */
/* Free a SOCK_INFO struct. */
/************************************************************************/
ST_VOID sock_info_free (SOCK_INFO *sock_info)
{
/* NOTE: gensock2 handles closing of the socket. */
M_FREE (MSMEM_SOCK_INFO, sock_info);
}
/************************************************************************/
/* _uSockDataAlloc */
/************************************************************************/
static ST_VOID _uSockDataAlloc (GEN_SOCK *pSock, ST_INT dataLen,
GEN_SOCK_DATA **sockDataOut)
{
GEN_SOCK_DATA *sockData;
sockData = (GEN_SOCK_DATA *) chk_malloc (sizeof (GEN_SOCK_DATA) + dataLen);
sockData->data = (ST_UCHAR *) (sockData + 1);
sockData->dataLen = dataLen;
/* We don't use sockData->usrBufBase, sockData->usrBufLen */
*sockDataOut = sockData;
}
/************************************************************************/
/* _uSockDataFree */
/************************************************************************/
static ST_VOID _uSockDataFree (GEN_SOCK *pSock, GEN_SOCK_DATA *sockData)
{
/* Allocated in _uSockDataAlloc using "chk_malloc", so use "chk_free".*/
chk_free (sockData);
}
/*----------------------------------------------*/
/* Misc */
/*----------------------------------------------*/
/************************************************************************/
/* _addSockInd */
/************************************************************************/
static ST_RET _addSockInd (GEN_SOCK *pSock, ST_INT eventType, GEN_SOCK_DATA *sockData)
{
GEN_SOCK_EVENT *sdi;
#if defined(MMSEASE_MOSI)
static ST_UCHAR tcpEventWakeupData = 1;
ST_INT numSent = 0;
#endif
if (tp0Ctx.state == TP0_SOCK_CTX_STATE_TERMINATING)
{
SOCK_LOG_FLOW1 (tp0Ctx.sockCtx, "%s: in _addSockInd failed - context terminating", pSock->sockIdStr);
return (SD_FAILURE);
}
else
SOCK_LOG_FLOW1 (tp0Ctx.sockCtx, "%s: in _addSockInd", pSock->sockIdStr);
sdi = (GEN_SOCK_EVENT *) chk_calloc (1, sizeof (GEN_SOCK_EVENT));
sdi->eventType = eventType;
sdi->pSock = pSock;
sdi->sockData = sockData;
sockEventPut (tp0Ctx.sockCtx, sdi); /* let gensock save on list*/
S_LOCK_UTIL_RESOURCES ();
/* if application can't process incoming data msgs as fast as they are*/
/* received by the socket then the best way to prevent uncontrolled */
/* growth of the receiving queue is to apply back pressure to the */
/* sending socket. */
if (eventType == GS_EVENT_DATA_IND)
{
SOCK_INFO *sock_info = (SOCK_INFO *) pSock->sockCfg.usr2;
/* this is a bit tricky, if we accept connection from rfc1006listener */
/* then the first packet will be queued before the sock_info is allocated */
/* so we must check here if the ptr is NULL */
if (sock_info)
{
++sock_info->recvCnt;
if (sock_info->recvCnt >= SOCK_RX_QUE_HIGH_MARK && pSock->sockCfg.pauseRecv == SD_FALSE)
{
pSock->sockCfg.pauseRecv = SD_TRUE;
SOCK_LOG_FLOWC2 (tp0Ctx.sockCtx, "%s: recvCnt=%u, setting pauseRecv=SD_TRUE",
pSock->sockIdStr, sock_info->recvCnt);
}
}
}
S_UNLOCK_UTIL_RESOURCES ();
/* wake up main thread */
#if defined(_WIN32)
gs_signal_event_sem (hTcpEvent); /* wake up main thread */
SOCK_LOG_FLOW2 (tp0Ctx.sockCtx, "%s: in _addSockInd() signaled hTcpEvent=%lu", pSock->sockIdStr, (ST_ULONG) hTcpEvent);
#endif
#if defined(MMSEASE_MOSI)
if (tp0Ctx.hTcpEventSender != INVALID_SOCKET)
{
numSent = (ST_INT) send (tp0Ctx.hTcpEventSender, &tcpEventWakeupData, sizeof(tcpEventWakeupData), 0);
if (numSent == sizeof (tcpEventWakeupData))
{
SOCK_LOG_FLOW3 ("%s: XSocket sent %d Wakeup data (value=%u)",
tp0Ctx.sockCtx->ctxName, numSent, (ST_UINT) tcpEventWakeupData);
}
else
{
/* when a lot of indications are received in short time, EWOULDBLOCK and EAGAIN */
/* errors could happen often with this 1-byte packets */
SOCK_LOG_FLOW3 ("%s: XSocket Wakeup data send() error (numSent=%d), errno=%d",
tp0Ctx.sockCtx->ctxName, numSent, SOCKET_ERRORNO);
/* ignore any errors */
}
++tcpEventWakeupData;
}
#endif /* defined(MMSEASE_MOSI) */
return (SD_SUCCESS);
}
/************************************************************************/
/* _handleRekeying */
/* Call function driving security rekeying on every secured socket. */
/************************************************************************/
static ST_RET _handleRekeying (ST_VOID)
{
ST_RET rc;
S_SEC_CONFIG *pSecCfg;
rc = secManAccessCfg (&pSecCfg);
if (rc != SD_SUCCESS)
return (rc);
/* we have to drive the checking for periodic rekeying, to do that call */
/* the TX func with no data to send */
if (pSecCfg->secureModeEnabled)
{
SOCK_INFO *sock_info;
S_LOCK_UTIL_RESOURCES ();
sock_info = tp0Ctx.secureSockList;
while (sock_info)
{
if (sGetMsTime () >= sock_info->rekeyTime &&
sock_info->genSock != NULL && sock_info->genSock->sockState == GS_STATE_CONNECTED)
{
GEN_SOCK_DATA sockData={{0}}; /* first in struct is struct, so need double braces */
ST_INT numSent;
sockTx (sock_info->genSock, &sockData, &numSent);
/* set the time for next check */
sock_info->rekeyTime = sGetMsTime() + SOCK_REKEY_TIME;
}
sock_info = list_get_next (tp0Ctx.secureSockList, sock_info);
}
S_UNLOCK_UTIL_RESOURCES ();
}
secManReleaseCfg();
return (rc);
}