/************************************************************************/ /* 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; inumSslListenPorts; ++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; inumSslListenPorts; ++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; isslListenPorts[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 报文发送*/ 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 报文发送失败 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); }