/******************************************************************************* File Name: socket.c Summary: WINC3400 BSD Compatible Socket Interface Description: WINC3400 BSD Compatible Socket Interface *******************************************************************************/ //DOM-IGNORE-BEGIN /******************************************************************************* * Copyright (C) 2021 Microchip Technology Inc. and its subsidiaries. * * Subject to your compliance with these terms, you may use Microchip software * and any derivatives exclusively with Microchip products. It is your * responsibility to comply with third party license terms applicable to your * use of third party software (including open source software) that may * accompany Microchip software. * * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER * EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A * PARTICULAR PURPOSE. * * IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, * INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND * WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS * BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE * FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN * ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. *******************************************************************************/ /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* INCLUDES *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ #include "nm_bsp.h" #include "socket.h" #include "m2m_hif.h" #include "m2m_socket_host_if.h" /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* MACROS *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ #define TLS_RECORD_HEADER_LENGTH (5) #define ETHERNET_HEADER_OFFSET (34) #define ETHERNET_HEADER_LENGTH (14) #define TCP_IP_HEADER_LENGTH (40) #define UDP_IP_HEADER_LENGTH (28) #define IP_PACKET_OFFSET (ETHERNET_HEADER_LENGTH + ETHERNET_HEADER_OFFSET - M2M_HIF_HDR_OFFSET) #define TCP_TX_PACKET_OFFSET (IP_PACKET_OFFSET + TCP_IP_HEADER_LENGTH) #define UDP_TX_PACKET_OFFSET (IP_PACKET_OFFSET + UDP_IP_HEADER_LENGTH) #define SSL_TX_PACKET_OFFSET (TCP_TX_PACKET_OFFSET + TLS_RECORD_HEADER_LENGTH) #define SOCKET_REQUEST(reqID, reqArgs, reqSize, reqPayload, reqPayloadSize, reqPayloadOffset) \ hif_send(M2M_REQ_GROUP_IP, reqID, reqArgs, reqSize, reqPayload, reqPayloadSize, reqPayloadOffset) #define SSL_FLAGS_ACTIVE NBIT0 #define SSL_FLAGS_BYPASS_X509 NBIT1 #define SSL_FLAGS_CACHE_SESSION NBIT4 #define SSL_FLAGS_CHECK_CERTNAME NBIT6 #define SSL_FLAGS_DELAY NBIT7 /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* PRIVATE DATA TYPES *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ /*! * @brief */ typedef struct { SOCKET sock; uint8_t u8Dummy; uint16_t u16SessionID; } tstrCloseCmd; /*! * @brief */ typedef struct { uint8_t *pu8UserBuffer; uint16_t u16UserBufferSize; uint16_t u16SessionID; uint16_t u16DataOffset; uint8_t bIsUsed; uint8_t u8SSLFlags; uint8_t bIsRecvPending; uint8_t u8AlpnStatus; uint8_t u8ErrSource; uint8_t u8ErrCode; } tstrSocket; /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* GLOBALS *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ volatile tstrSocket gastrSockets[MAX_SOCKET]; volatile uint16_t gu16SessionID = 0; volatile tpfAppSocketCb gpfAppSocketCb; volatile tpfAppResolveCb gpfAppResolveCb; volatile uint8_t gbSocketInit = 0; static tpfPingCb gfpPingCb = NULL; static uint32_t gu32PingId = 0; /********************************************************************* Function Socket_ReadSocketData Description Callback function used by the WINC3400 driver to deliver messages for socket layer. Return None. *********************************************************************/ static void Socket_ReadSocketData(SOCKET sock, tstrSocketRecvMsg *pstrRecv, uint8_t u8SocketMsg, uint32_t u32StartAddress, uint16_t u16ReadCount) { uint32_t u32Address = u32StartAddress; uint16_t u16Read; int16_t s16Diff; pstrRecv->u16RemainingSize = u16ReadCount; if((u16ReadCount > 0) && (gastrSockets[sock].pu8UserBuffer != NULL) && (gastrSockets[sock].u16UserBufferSize > 0) && (gastrSockets[sock].bIsUsed == 1)) { u16Read = u16ReadCount; s16Diff = u16Read - gastrSockets[sock].u16UserBufferSize; if(s16Diff > 0) { /* We don't expect to be here. Firmware only sends data to the driver according to the application's buffer size. * But it is worth keeping this check, eg in case the application calls recv again with a smaller buffer size, or in case of HIF hacking. */ u16Read = gastrSockets[sock].u16UserBufferSize; } if(hif_receive(u32Address, gastrSockets[sock].pu8UserBuffer, u16Read, 1) == M2M_SUCCESS) { pstrRecv->pu8Buffer = gastrSockets[sock].pu8UserBuffer; pstrRecv->s16BufferSize = u16Read; pstrRecv->u16RemainingSize -= u16Read; gastrSockets[sock].u16UserBufferSize = 0; gastrSockets[sock].pu8UserBuffer = NULL; if(gpfAppSocketCb) gpfAppSocketCb(sock, u8SocketMsg, pstrRecv); } else { M2M_ERR("Current <%d>\r\n", u16ReadCount); } } } /********************************************************************* Function m2m_ip_cb Description Callback function used by the NMC1000 driver to deliver messages for socket layer. Return None. *********************************************************************/ static void m2m_ip_cb(uint8_t u8OpCode, uint16_t u16BufferSize, uint32_t u32Address) { if(u8OpCode == SOCKET_CMD_BIND) { tstrBindReply strBindReply; tstrSocketBindMsg strBind; if(hif_receive(u32Address, (uint8_t*)&strBindReply, sizeof(tstrBindReply), 0) == M2M_SUCCESS) { strBind.status = strBindReply.s8Status; if(gpfAppSocketCb) gpfAppSocketCb(strBindReply.sock, SOCKET_MSG_BIND, &strBind); } } else if(u8OpCode == SOCKET_CMD_LISTEN) { tstrListenReply strListenReply; tstrSocketListenMsg strListen; if(hif_receive(u32Address, (uint8_t*)&strListenReply, sizeof(tstrListenReply), 0) == M2M_SUCCESS) { strListen.status = strListenReply.s8Status; if(gpfAppSocketCb) gpfAppSocketCb(strListenReply.sock, SOCKET_MSG_LISTEN, &strListen); } } else if(u8OpCode == SOCKET_CMD_ACCEPT) { tstrAcceptReply strAcceptReply; tstrSocketAcceptMsg strAccept; if(hif_receive(u32Address, (uint8_t*)&strAcceptReply, sizeof(tstrAcceptReply), 0) == M2M_SUCCESS) { if((strAcceptReply.sConnectedSock >= 0) && (strAcceptReply.sConnectedSock < MAX_SOCKET)) { gastrSockets[strAcceptReply.sConnectedSock].u8SSLFlags = 0; gastrSockets[strAcceptReply.sConnectedSock].bIsUsed = 1; /* The session ID is used to distinguish different socket connections by comparing the assigned session ID to the one reported by the firmware*/ ++gu16SessionID; if(gu16SessionID == 0) ++gu16SessionID; gastrSockets[strAcceptReply.sConnectedSock].u16SessionID = gu16SessionID; M2M_DBG("Socket %d session ID = %d\r\n", strAcceptReply.sConnectedSock, gu16SessionID); } strAccept.sock = strAcceptReply.sConnectedSock; strAccept.strAddr.sin_family = AF_INET; strAccept.strAddr.sin_port = strAcceptReply.strAddr.u16Port; strAccept.strAddr.sin_addr.s_addr = strAcceptReply.strAddr.u32IPAddr; if(gpfAppSocketCb) gpfAppSocketCb(strAcceptReply.sListenSock, SOCKET_MSG_ACCEPT, &strAccept); } } else if((u8OpCode == SOCKET_CMD_CONNECT) || (u8OpCode == SOCKET_CMD_SSL_CONNECT) || (u8OpCode == SOCKET_CMD_SSL_CONNECT_ALPN)) { /* Note that for successful connections the fw always sends SOCKET_CMD_CONNECT, even for SSL connections. */ tstrConnectAlpnReply strConnectAlpnReply = {{0}}; tstrSocketConnectMsg strConnMsg; uint16_t u16HifSz = sizeof(tstrConnectAlpnReply); if(u8OpCode != SOCKET_CMD_SSL_CONNECT_ALPN) u16HifSz = sizeof(tstrConnectReply); if(hif_receive(u32Address, (uint8_t*)&strConnectAlpnReply, u16HifSz, 0) == M2M_SUCCESS) { if((strConnectAlpnReply.strConnReply.sock >= 0) && (strConnectAlpnReply.strConnReply.sock < MAX_SOCKET)) { uint8_t u8Msg = SOCKET_MSG_CONNECT; strConnMsg.sock = strConnectAlpnReply.strConnReply.sock; strConnMsg.s8Error = strConnectAlpnReply.strConnReply.s8Error; /* If the SOCKET_CMD_SSL_CONNECT op code is received and the socket was already connected, then the callback corresponds to an attempt to make the socket secure. */ if(0 != gastrSockets[strConnMsg.sock].u16DataOffset) { u8Msg = SOCKET_MSG_SECURE; } if(strConnectAlpnReply.strConnReply.s8Error == SOCK_ERR_NO_ERROR) { gastrSockets[strConnMsg.sock].u16DataOffset = strConnectAlpnReply.strConnReply.u16AppDataOffset - M2M_HIF_HDR_OFFSET; gastrSockets[strConnMsg.sock].u8AlpnStatus = strConnectAlpnReply.u8AppProtocolIdx; } else { gastrSockets[strConnMsg.sock].u8ErrSource = strConnectAlpnReply.strConnReply.u8ErrSource; gastrSockets[strConnMsg.sock].u8ErrCode = strConnectAlpnReply.strConnReply.u8ErrCode; } if(gpfAppSocketCb) gpfAppSocketCb(strConnMsg.sock, u8Msg, &strConnMsg); } } } else if(u8OpCode == SOCKET_CMD_DNS_RESOLVE) { tstrDnsReply strDnsReply; if(hif_receive(u32Address, (uint8_t*)&strDnsReply, sizeof(tstrDnsReply), 0) == M2M_SUCCESS) { strDnsReply.u32HostIP = strDnsReply.u32HostIP; if(gpfAppResolveCb) gpfAppResolveCb((uint8_t*)strDnsReply.acHostName, strDnsReply.u32HostIP); } } else if((u8OpCode == SOCKET_CMD_RECV) || (u8OpCode == SOCKET_CMD_RECVFROM) || (u8OpCode == SOCKET_CMD_SSL_RECV)) { SOCKET sock; int16_t s16RecvStatus; tstrRecvReply strRecvReply; uint16_t u16ReadSize; tstrSocketRecvMsg strRecvMsg; uint8_t u8CallbackMsgID = SOCKET_MSG_RECV; uint16_t u16DataOffset; if(u8OpCode == SOCKET_CMD_RECVFROM) u8CallbackMsgID = SOCKET_MSG_RECVFROM; /* Read RECV REPLY data structure. */ u16ReadSize = sizeof(tstrRecvReply); if(hif_receive(u32Address, (uint8_t*)&strRecvReply, u16ReadSize, 0) == M2M_SUCCESS) { if((strRecvReply.sock >= 0) && (strRecvReply.sock < MAX_SOCKET)) { uint16_t u16SessionID = 0; sock = strRecvReply.sock; u16SessionID = strRecvReply.u16SessionID; M2M_DBG("recv callback session ID = %d\r\n", u16SessionID); /* Reset the Socket RX Pending Flag. */ gastrSockets[sock].bIsRecvPending = 0; s16RecvStatus = NM_BSP_B_L_16(strRecvReply.s16RecvStatus); u16DataOffset = NM_BSP_B_L_16(strRecvReply.u16DataOffset); strRecvMsg.strRemoteAddr.sin_port = strRecvReply.strRemoteAddr.u16Port; strRecvMsg.strRemoteAddr.sin_addr.s_addr = strRecvReply.strRemoteAddr.u32IPAddr; if(u16SessionID == gastrSockets[sock].u16SessionID) { if((s16RecvStatus > 0) && (s16RecvStatus < u16BufferSize)) { /* Skip incoming bytes until reaching the Start of Application Data. */ u32Address += u16DataOffset; /* Read the Application data and deliver it to the application callback in the given application buffer. Firmware only sends data up to the size of the application buffer. For TCP, a new call to recv is needed in order to retrieve any outstanding data from firmware. */ u16ReadSize = (uint16_t)s16RecvStatus; Socket_ReadSocketData(sock, &strRecvMsg, u8CallbackMsgID, u32Address, u16ReadSize); } else { /* Don't tidy up here. Application must call shutdown(). */ strRecvMsg.s16BufferSize = s16RecvStatus; strRecvMsg.pu8Buffer = NULL; if(gpfAppSocketCb) gpfAppSocketCb(sock, u8CallbackMsgID, &strRecvMsg); } } else { M2M_DBG("Discard recv callback %d %d\r\n", u16SessionID, gastrSockets[sock].u16SessionID); if(u16ReadSize < u16BufferSize) hif_receive(0, NULL, 0, 1); } } } } else if((u8OpCode == SOCKET_CMD_SEND) || (u8OpCode == SOCKET_CMD_SENDTO) || (u8OpCode == SOCKET_CMD_SSL_SEND)) { SOCKET sock; int16_t s16Rcvd; tstrSendReply strReply; uint8_t u8CallbackMsgID = SOCKET_MSG_SEND; if(u8OpCode == SOCKET_CMD_SENDTO) u8CallbackMsgID = SOCKET_MSG_SENDTO; if(hif_receive(u32Address, (uint8_t*)&strReply, sizeof(tstrSendReply), 0) == M2M_SUCCESS) { if((strReply.sock >=0) && (strReply.sock < MAX_SOCKET)) { uint16_t u16SessionID = 0; sock = strReply.sock; u16SessionID = strReply.u16SessionID; M2M_DBG("send callback session ID = %d\r\n", u16SessionID); s16Rcvd = NM_BSP_B_L_16(strReply.s16SentBytes); if(u16SessionID == gastrSockets[sock].u16SessionID) { if(gpfAppSocketCb) gpfAppSocketCb(sock, u8CallbackMsgID, &s16Rcvd); } else { M2M_DBG("Discard send callback %d %d\r\n", u16SessionID, gastrSockets[sock].u16SessionID); } } } } else if(u8OpCode == SOCKET_CMD_PING) { tstrPingReply strPingReply; if(hif_receive(u32Address, (uint8_t*)&strPingReply, sizeof(tstrPingReply), 1) == M2M_SUCCESS) { if((gu32PingId == strPingReply.u32CmdPrivate) && (gfpPingCb != NULL)) { gfpPingCb(strPingReply.u32IPAddr, strPingReply.u32RTT, strPingReply.u8ErrorCode); } } } } /********************************************************************* Function socketInit Description Return None. *********************************************************************/ void socketInit(void) { if(gbSocketInit==0) { memset((uint8_t*)gastrSockets, 0, MAX_SOCKET * sizeof(tstrSocket)); hif_register_cb(M2M_REQ_GROUP_IP, m2m_ip_cb); gbSocketInit=1; gu16SessionID = 0; } } /********************************************************************* Function socketDeinit Description Return None. *********************************************************************/ void socketDeinit(void) { memset((uint8_t*)gastrSockets, 0, MAX_SOCKET * sizeof(tstrSocket)); hif_register_cb(M2M_REQ_GROUP_IP, NULL); gpfAppSocketCb = NULL; gpfAppResolveCb = NULL; gbSocketInit = 0; } /********************************************************************* Function registerSocketCallback Description Return None. *********************************************************************/ void registerSocketCallback(tpfAppSocketCb pfAppSocketCb, tpfAppResolveCb pfAppResolveCb) { gpfAppSocketCb = pfAppSocketCb; gpfAppResolveCb = pfAppResolveCb; } void registerSocketEventCallback(tpfAppSocketCb pfAppSocketCb) { gpfAppSocketCb = pfAppSocketCb; } void registerSocketResolveCallback(tpfAppResolveCb pfAppResolveCb) { gpfAppResolveCb = pfAppResolveCb; } /********************************************************************* Function socket Description Creates a socket. Return - Negative value for error. - ZERO or positive value as a socket ID if successful. *********************************************************************/ SOCKET socket(uint16_t u16Domain, uint8_t u8Type, uint8_t u8Config) { SOCKET sock = -1; uint8_t u8SockID; uint8_t u8Count; volatile tstrSocket *pstrSock = NULL; static volatile uint8_t u8NextTcpSock = 0; static volatile uint8_t u8NextUdpSock = 0; /* The only supported family is the AF_INET for UDP and TCP transport layer protocols. */ if(u16Domain == AF_INET) { if(u8Type == SOCK_STREAM) { for(u8Count = 0; u8Count < TCP_SOCK_MAX; u8Count ++) { u8SockID = u8NextTcpSock; pstrSock = &gastrSockets[u8NextTcpSock]; u8NextTcpSock = (u8NextTcpSock + 1) % TCP_SOCK_MAX; if(!pstrSock->bIsUsed) { sock = (SOCKET)u8SockID; memset((uint8_t*)pstrSock, 0, sizeof(tstrSocket)); /* In the current implementation, SSL flags only have meaning for the TCP socket case. */ if(u8Config != SOCKET_CONFIG_SSL_OFF) { tstrSSLSocketCreateCmd strSSLCreate; strSSLCreate.sslSock = sock; SOCKET_REQUEST(SOCKET_CMD_SSL_CREATE, (uint8_t*)&strSSLCreate, sizeof(tstrSSLSocketCreateCmd), 0, 0, 0); pstrSock->u8SSLFlags = SSL_FLAGS_ACTIVE; if(u8Config == SOCKET_CONFIG_SSL_DELAY) pstrSock->u8SSLFlags |= SSL_FLAGS_DELAY; } break; } } } else if(u8Type == SOCK_DGRAM) { volatile tstrSocket *pastrUDPSockets = &gastrSockets[TCP_SOCK_MAX]; for(u8Count = 0; u8Count < UDP_SOCK_MAX; u8Count ++) { u8SockID = u8NextUdpSock; pstrSock = &pastrUDPSockets[u8NextUdpSock]; u8NextUdpSock = (u8NextUdpSock + 1) % UDP_SOCK_MAX; if(!pstrSock->bIsUsed) { sock = (SOCKET)(u8SockID + TCP_SOCK_MAX); memset((uint8_t*)pstrSock, 0, sizeof(tstrSocket)); break; } } } else if(u8Type == SOCK_RAW) { /* Only raw IP packets are supported */ if(u8Config == SOCKET_CONFIG_IPPROTO_RAW) { pstrSock = &gastrSockets[RAW_SOCK_ID]; if(!pstrSock->bIsUsed) { /* Socket identified by RAW_SOCK_ID is reserved */ sock = (SOCKET)RAW_SOCK_ID; memset((uint8_t*)pstrSock, 0, sizeof(tstrSocket)); } } } if(sock >= 0) { pstrSock->bIsUsed = 1; /* The session ID is used to distinguish different socket connections by comparing the assigned session ID to the one reported by the firmware*/ ++gu16SessionID; if(gu16SessionID == 0) ++gu16SessionID; pstrSock->u16SessionID = gu16SessionID; M2M_INFO("Socket %d session ID = %d\r\n", sock, gu16SessionID); } } return sock; } /********************************************************************* Function bind Description Request to bind a socket on a local address. Return *********************************************************************/ int8_t bind(SOCKET sock, struct sockaddr *pstrAddr, uint8_t u8AddrLen) { int8_t s8Ret = SOCK_ERR_INVALID_ARG; if((pstrAddr != NULL) && (sock >= 0) && (sock < MAX_SOCKET) && (gastrSockets[sock].bIsUsed == 1) && (u8AddrLen != 0)) { tstrBindCmd strBind; /* Build the bind request. */ strBind.sock = sock; memcpy((uint8_t *)&strBind.strAddr, (uint8_t *)pstrAddr, sizeof(tstrSockAddr)); strBind.strAddr.u16Family = strBind.strAddr.u16Family; strBind.strAddr.u16Port = strBind.strAddr.u16Port; strBind.strAddr.u32IPAddr = strBind.strAddr.u32IPAddr; strBind.u16SessionID = gastrSockets[sock].u16SessionID; /* Send the request. */ s8Ret = SOCKET_REQUEST(SOCKET_CMD_BIND, (uint8_t*)&strBind, sizeof(tstrBindCmd), NULL, 0, 0); if(s8Ret != SOCK_ERR_NO_ERROR) { s8Ret = SOCK_ERR_INVALID; } } return s8Ret; } /********************************************************************* Function listen Description Return *********************************************************************/ int8_t listen(SOCKET sock, uint8_t backlog) { int8_t s8Ret = SOCK_ERR_INVALID_ARG; if((sock >= 0) && (sock < MAX_SOCKET) && (gastrSockets[sock].bIsUsed == 1)) { tstrListenCmd strListen; strListen.sock = sock; strListen.u8BackLog = backlog; strListen.u16SessionID = gastrSockets[sock].u16SessionID; s8Ret = SOCKET_REQUEST(SOCKET_CMD_LISTEN, (uint8_t*)&strListen, sizeof(tstrListenCmd), NULL, 0, 0); if(s8Ret != SOCK_ERR_NO_ERROR) { s8Ret = SOCK_ERR_INVALID; } } return s8Ret; } /********************************************************************* Function accept Description Return *********************************************************************/ int8_t accept(SOCKET sock, struct sockaddr *addr, uint8_t *addrlen) { int8_t s8Ret = SOCK_ERR_INVALID_ARG; if((sock >= 0) && (sock < MAX_SOCKET) && (gastrSockets[sock].bIsUsed == 1)) { s8Ret = SOCK_ERR_NO_ERROR; } return s8Ret; } /********************************************************************* Function connect Description Connect to a remote TCP Server. Return *********************************************************************/ int8_t connect(SOCKET sock, struct sockaddr *pstrAddr, uint8_t u8AddrLen) { int8_t s8Ret = SOCK_ERR_INVALID_ARG; if((sock >= 0) && (sock < MAX_SOCKET) && (pstrAddr != NULL) && (gastrSockets[sock].bIsUsed == 1) && (u8AddrLen != 0)) { tstrConnectCmd strConnect; uint8_t u8Cmd = SOCKET_CMD_CONNECT; if((gastrSockets[sock].u8SSLFlags) & SSL_FLAGS_ACTIVE) { u8Cmd = SOCKET_CMD_SSL_CONNECT; strConnect.u8SslFlags = gastrSockets[sock].u8SSLFlags; } strConnect.sock = sock; memcpy((uint8_t *)&strConnect.strAddr, (uint8_t *)pstrAddr, sizeof(tstrSockAddr)); strConnect.u16SessionID = gastrSockets[sock].u16SessionID; s8Ret = SOCKET_REQUEST(u8Cmd, (uint8_t*)&strConnect, sizeof(tstrConnectCmd), NULL, 0, 0); if(s8Ret != SOCK_ERR_NO_ERROR) { s8Ret = SOCK_ERR_INVALID; } } return s8Ret; } /********************************************************************* Function secure Description Make secure (TLS) an open TCP client connection. Return *********************************************************************/ int8_t secure(SOCKET sock) { int8_t s8Ret = SOCK_ERR_INVALID_ARG; if((sock >= 0) && (sock < MAX_SOCKET) && (gastrSockets[sock].bIsUsed == 1)) { if( (gastrSockets[sock].u8SSLFlags & SSL_FLAGS_ACTIVE) && (gastrSockets[sock].u8SSLFlags & SSL_FLAGS_DELAY) && (gastrSockets[sock].u16DataOffset != 0) ) { tstrConnectCmd strConnect = {0}; gastrSockets[sock].u8SSLFlags &= ~SSL_FLAGS_DELAY; strConnect.u8SslFlags = gastrSockets[sock].u8SSLFlags; strConnect.sock = sock; strConnect.u16SessionID = gastrSockets[sock].u16SessionID; s8Ret = SOCKET_REQUEST(SOCKET_CMD_SECURE, (uint8_t*)&strConnect, sizeof(tstrConnectCmd), NULL, 0, 0); if(s8Ret != SOCK_ERR_NO_ERROR) { s8Ret = SOCK_ERR_INVALID; } } } return s8Ret; } /********************************************************************* Function send Description Return *********************************************************************/ int16_t send(SOCKET sock, void *pvSendBuffer, uint16_t u16SendLength, uint16_t flags) { int16_t s16Ret = SOCK_ERR_INVALID_ARG; if((sock >= 0) && (sock < MAX_SOCKET) && (pvSendBuffer != NULL) && (u16SendLength <= SOCKET_BUFFER_MAX_LENGTH) && (gastrSockets[sock].bIsUsed == 1)) { uint16_t u16DataOffset; tstrSendCmd strSend; uint8_t u8Cmd; u8Cmd = SOCKET_CMD_SEND; u16DataOffset = TCP_TX_PACKET_OFFSET; strSend.sock = sock; strSend.u16DataSize = NM_BSP_B_L_16(u16SendLength); strSend.u16SessionID = gastrSockets[sock].u16SessionID; if(sock >= TCP_SOCK_MAX) { u16DataOffset = UDP_TX_PACKET_OFFSET; } if( (gastrSockets[sock].u8SSLFlags & SSL_FLAGS_ACTIVE) && (!(gastrSockets[sock].u8SSLFlags & SSL_FLAGS_DELAY)) ) { u8Cmd = SOCKET_CMD_SSL_SEND; u16DataOffset = gastrSockets[sock].u16DataOffset; } s16Ret = SOCKET_REQUEST(u8Cmd|M2M_REQ_DATA_PKT, (uint8_t*)&strSend, sizeof(tstrSendCmd), pvSendBuffer, u16SendLength, u16DataOffset); if(s16Ret != SOCK_ERR_NO_ERROR) { s16Ret = SOCK_ERR_BUFFER_FULL; } } return s16Ret; } /********************************************************************* Function sendto Description Return *********************************************************************/ int16_t sendto(SOCKET sock, void *pvSendBuffer, uint16_t u16SendLength, uint16_t flags, struct sockaddr *pstrDestAddr, uint8_t u8AddrLen) { int16_t s16Ret = SOCK_ERR_INVALID_ARG; uint16_t u16MaxLength = (sock == RAW_SOCK_ID) ? (M2M_HIF_MAX_PACKET_SIZE - M2M_HIF_HDR_OFFSET - sizeof(tstrSendCmd)) : SOCKET_BUFFER_MAX_LENGTH; if((sock >= 0) && (sock < MAX_SOCKET) && (pvSendBuffer != NULL) && (u16SendLength <= u16MaxLength) && (gastrSockets[sock].bIsUsed == 1)) { tstrSendCmd strSendTo; memset((uint8_t*)&strSendTo, 0, sizeof(tstrSendCmd)); strSendTo.sock = sock; strSendTo.u16DataSize = NM_BSP_B_L_16(u16SendLength); strSendTo.u16SessionID = gastrSockets[sock].u16SessionID; if(pstrDestAddr != NULL) { struct sockaddr_in *pstrAddr; pstrAddr = (void*)pstrDestAddr; strSendTo.strAddr.u16Family = pstrAddr->sin_family; strSendTo.strAddr.u16Port = pstrAddr->sin_port; strSendTo.strAddr.u32IPAddr = pstrAddr->sin_addr.s_addr; } s16Ret = SOCKET_REQUEST(SOCKET_CMD_SENDTO|M2M_REQ_DATA_PKT, (uint8_t*)&strSendTo, sizeof(tstrSendCmd), pvSendBuffer, u16SendLength, (sock == RAW_SOCK_ID) ? IP_PACKET_OFFSET: UDP_TX_PACKET_OFFSET); if(s16Ret != SOCK_ERR_NO_ERROR) { s16Ret = SOCK_ERR_BUFFER_FULL; } } return s16Ret; } /********************************************************************* Function recv Description Return *********************************************************************/ int16_t recv(SOCKET sock, void *pvRecvBuf, uint16_t u16BufLen, uint32_t u32Timeoutmsec) { int16_t s16Ret = SOCK_ERR_INVALID_ARG; if((sock >= 0) && (sock < MAX_SOCKET) && (pvRecvBuf != NULL) && (u16BufLen != 0) && (gastrSockets[sock].bIsUsed == 1)) { s16Ret = SOCK_ERR_NO_ERROR; gastrSockets[sock].pu8UserBuffer = (uint8_t*)pvRecvBuf; gastrSockets[sock].u16UserBufferSize = u16BufLen; if(!gastrSockets[sock].bIsRecvPending) { tstrRecvCmd strRecv; uint8_t u8Cmd = SOCKET_CMD_RECV; gastrSockets[sock].bIsRecvPending = 1; if( (gastrSockets[sock].u8SSLFlags & SSL_FLAGS_ACTIVE) && (!(gastrSockets[sock].u8SSLFlags & SSL_FLAGS_DELAY)) ) { u8Cmd = SOCKET_CMD_SSL_RECV; } /* Check the timeout value. */ if(u32Timeoutmsec == 0) strRecv.u32Timeoutmsec = 0xFFFFFFFF; else strRecv.u32Timeoutmsec = NM_BSP_B_L_32(u32Timeoutmsec); strRecv.sock = sock; strRecv.u16SessionID = gastrSockets[sock].u16SessionID; strRecv.u16BufLen = u16BufLen; s16Ret = SOCKET_REQUEST(u8Cmd, (uint8_t*)&strRecv, sizeof(tstrRecvCmd), NULL, 0, 0); if(s16Ret != SOCK_ERR_NO_ERROR) { s16Ret = SOCK_ERR_BUFFER_FULL; } } } return s16Ret; } /********************************************************************* Function shutdown Description Return None. *********************************************************************/ int8_t shutdown(SOCKET sock) { int8_t s8Ret = SOCK_ERR_INVALID_ARG; M2M_INFO("Sock to delete <%d> (used %u)\r\n", sock, gastrSockets[sock].bIsUsed); if((sock >= 0) && (sock < MAX_SOCKET) && (gastrSockets[sock].bIsUsed == 1)) { uint8_t u8Cmd = SOCKET_CMD_CLOSE; tstrCloseCmd strclose; strclose.sock = sock; strclose.u16SessionID = gastrSockets[sock].u16SessionID; if(gastrSockets[sock].u8SSLFlags & SSL_FLAGS_ACTIVE) { u8Cmd = SOCKET_CMD_SSL_CLOSE; } s8Ret = SOCKET_REQUEST(u8Cmd, (uint8_t*)&strclose, sizeof(tstrCloseCmd), NULL, 0, 0); if(s8Ret != SOCK_ERR_NO_ERROR) { s8Ret = SOCK_ERR_INVALID; } memset((uint8_t*)&gastrSockets[sock], 0, sizeof(tstrSocket)); } return s8Ret; } /********************************************************************* Function recvfrom Description Return *********************************************************************/ int16_t recvfrom(SOCKET sock, void *pvRecvBuf, uint16_t u16BufLen, uint32_t u32Timeoutmsec) { int16_t s16Ret = SOCK_ERR_NO_ERROR; if((sock >= 0) && (sock < MAX_SOCKET) && (pvRecvBuf != NULL) && (u16BufLen != 0) && (gastrSockets[sock].bIsUsed == 1)) { if(gastrSockets[sock].bIsUsed) { s16Ret = SOCK_ERR_NO_ERROR; gastrSockets[sock].pu8UserBuffer = (uint8_t*)pvRecvBuf; gastrSockets[sock].u16UserBufferSize = u16BufLen; if(!gastrSockets[sock].bIsRecvPending) { tstrRecvCmd strRecv; gastrSockets[sock].bIsRecvPending = 1; /* Check the timeout value. */ if(u32Timeoutmsec == 0) strRecv.u32Timeoutmsec = 0xFFFFFFFF; else strRecv.u32Timeoutmsec = NM_BSP_B_L_32(u32Timeoutmsec); strRecv.sock = sock; strRecv.u16SessionID = gastrSockets[sock].u16SessionID; strRecv.u16BufLen = u16BufLen; s16Ret = SOCKET_REQUEST(SOCKET_CMD_RECVFROM, (uint8_t*)&strRecv, sizeof(tstrRecvCmd), NULL, 0, 0); if(s16Ret != SOCK_ERR_NO_ERROR) { s16Ret = SOCK_ERR_BUFFER_FULL; } } } } else { s16Ret = SOCK_ERR_INVALID_ARG; } return s16Ret; } /********************************************************************* Function gethostbyname Description Return None. *********************************************************************/ int8_t gethostbyname(const char *pcHostName) { int8_t s8Err = SOCK_ERR_INVALID_ARG; uint8_t u8HostNameSize = (uint8_t)strlen(pcHostName); if(u8HostNameSize <= HOSTNAME_MAX_SIZE) { s8Err = SOCKET_REQUEST(SOCKET_CMD_DNS_RESOLVE|M2M_REQ_DATA_PKT, (uint8_t*)pcHostName, u8HostNameSize + 1, NULL, 0, 0); if(s8Err != SOCK_ERR_NO_ERROR) { s8Err = SOCK_ERR_INVALID; } } return s8Err; } /********************************************************************* Function rawSetSockOpt Description Return None. *********************************************************************/ static int8_t rawSetSockOpt(SOCKET sock, uint8_t u8Opt, const void *pvOptVal, uint16_t u16OptLen) { int8_t s8Ret = SOCK_ERR_INVALID_ARG; if(RAW_SOCK_ID == sock) { if(u16OptLen == sizeof(uint32_t)) { uint8_t u8Cmd = SOCKET_CMD_RAW_SET_SOCK_OPT; tstrSetSocketOptCmd strSetSockOpt; strSetSockOpt.u8Option = u8Opt; strSetSockOpt.sock = sock; strSetSockOpt.u32OptionValue = *(uint32_t*)pvOptVal; strSetSockOpt.u16SessionID = gastrSockets[sock].u16SessionID; s8Ret = SOCKET_REQUEST(u8Cmd, (uint8_t*)&strSetSockOpt, sizeof(tstrSetSocketOptCmd), NULL, 0, 0); if(s8Ret != SOCK_ERR_NO_ERROR) { s8Ret = SOCK_ERR_INVALID; } } } return s8Ret; } /********************************************************************* Function sslSetSockOpt Description Return None. *********************************************************************/ static int8_t sslSetSockOpt(SOCKET sock, uint8_t u8Opt, const void *pvOptVal, uint16_t u16OptLen) { int8_t s8Ret = SOCK_ERR_INVALID_ARG; if(sock < TCP_SOCK_MAX) { if(gastrSockets[sock].u8SSLFlags & SSL_FLAGS_ACTIVE) { uint8_t sslFlag = 0; s8Ret = SOCK_ERR_NO_ERROR; if(u16OptLen == sizeof(int)) { if(u8Opt == SO_SSL_BYPASS_X509_VERIF) { sslFlag = SSL_FLAGS_BYPASS_X509; } else if(u8Opt == SO_SSL_ENABLE_SESSION_CACHING) { sslFlag = SSL_FLAGS_CACHE_SESSION; } else if(u8Opt == SO_SSL_ENABLE_CERTNAME_VALIDATION) { sslFlag = SSL_FLAGS_CHECK_CERTNAME; } } if(sslFlag) { int optVal = *((int*)pvOptVal); if(optVal) { gastrSockets[sock].u8SSLFlags |= sslFlag; } else { gastrSockets[sock].u8SSLFlags &= ~sslFlag; } } else if( ((u8Opt == SO_SSL_SNI) && (u16OptLen < HOSTNAME_MAX_SIZE)) || ((u8Opt == SO_SSL_ALPN) && (u16OptLen <= ALPN_LIST_MAX_SIZE)) ) { tstrSSLSetSockOptCmd strCmd = {0}; strCmd.sock = sock; strCmd.u16SessionID = gastrSockets[sock].u16SessionID; strCmd.u8Option = u8Opt; strCmd.u32OptLen = u16OptLen; memcpy(strCmd.au8OptVal, (uint8_t*)pvOptVal, u16OptLen); s8Ret = SOCKET_REQUEST(SOCKET_CMD_SSL_SET_SOCK_OPT, (uint8_t*)&strCmd, sizeof(tstrSSLSetSockOptCmd), 0, 0, 0); if(s8Ret == M2M_ERR_MEM_ALLOC) { s8Ret = SOCKET_REQUEST(SOCKET_CMD_SSL_SET_SOCK_OPT | M2M_REQ_DATA_PKT, (uint8_t*)&strCmd, sizeof(tstrSSLSetSockOptCmd), 0, 0, 0); } } else { M2M_ERR("Unknown SSL Socket Option %d\r\n",u8Opt); s8Ret = SOCK_ERR_INVALID_ARG; } } else { M2M_ERR("Not SSL Socket\r\n"); } } return s8Ret; } /********************************************************************* Function setsockopt Description Return None. *********************************************************************/ int8_t setsockopt(SOCKET sock, uint8_t u8Level, uint8_t option_name, const void *option_value, uint16_t u16OptionLen) { int8_t s8Ret = SOCK_ERR_INVALID_ARG; if((sock >= 0) && (sock < MAX_SOCKET) && (option_value != NULL) && (gastrSockets[sock].bIsUsed == 1)) { if(u8Level == SOL_RAW) { s8Ret = rawSetSockOpt(sock, option_name, option_value, u16OptionLen); } else if(u8Level == SOL_SSL_SOCKET) { s8Ret = sslSetSockOpt(sock, option_name, option_value, u16OptionLen); } else if(u8Level == SOL_SOCKET) { if(u16OptionLen == sizeof(uint32_t)) { uint8_t u8Cmd = SOCKET_CMD_SET_SOCKET_OPTION; tstrSetSocketOptCmd strSetSockOpt; strSetSockOpt.u8Option=option_name; strSetSockOpt.sock = sock; strSetSockOpt.u32OptionValue = *(uint32_t*)option_value; strSetSockOpt.u16SessionID = gastrSockets[sock].u16SessionID; s8Ret = SOCKET_REQUEST(u8Cmd, (uint8_t*)&strSetSockOpt, sizeof(tstrSetSocketOptCmd), NULL, 0, 0); if(s8Ret != SOCK_ERR_NO_ERROR) { s8Ret = SOCK_ERR_INVALID; } } } } return s8Ret; } /********************************************************************* Function getsockopt Description Return None. *********************************************************************/ int8_t getsockopt(SOCKET sock, uint8_t u8Level, uint8_t u8OptName, const void *pvOptValue, uint8_t* pu8OptLen) { // This is not implemented so return a value that will cause failure should this be used. return SOCK_ERR_INVALID_ARG; } /********************************************************************* Function m2m_ping_req Description Send Ping request. Return *********************************************************************/ int8_t m2m_ping_req(uint32_t u32DstIP, uint8_t u8TTL, tpfPingCb fpPingCb) { int8_t s8Ret = M2M_ERR_INVALID_ARG; if((u32DstIP != 0) && (fpPingCb != NULL)) { tstrPingCmd strPingCmd; strPingCmd.u16PingCount = 1; strPingCmd.u32DestIPAddr = u32DstIP; strPingCmd.u32CmdPrivate = ++gu32PingId; strPingCmd.u8TTL = u8TTL; gfpPingCb = fpPingCb; s8Ret = SOCKET_REQUEST(SOCKET_CMD_PING, (uint8_t*)&strPingCmd, sizeof(tstrPingCmd), NULL, 0, 0); } return s8Ret; } /********************************************************************* Function set_alpn_protocol_list Description This function sets the protocol list used for application-layer protocol negotiation (ALPN). If used, it must be called after creating a SSL socket (using @ref socket) and before connecting/binding (using @ref connect or @ref bind). Return The function returns @ref M2M_SUCCESS for successful operations and a negative value otherwise. *********************************************************************/ int8_t set_alpn_list(SOCKET sock, const char *pcProtocolList) { int8_t s8Ret = SOCK_ERR_INVALID_ARG; if ((sock >= 0) && (sock < TCP_SOCK_MAX) && (pcProtocolList != NULL)) { uint8_t u8Length = strlen(pcProtocolList); if ((u8Length > 0) && (u8Length < ALPN_LIST_MAX_APP_LENGTH)) { /* ALPN socket option requires Alpn list in this format: 0 1 2 3 ... (bytes) +-------+-------+-------+ ... +-------+ ... +-------+ ... | Length L (BE) | len1 | name1... | len2 | name2... | len3 | name3... +-------+-------+-------+ ... +-------+ ... +-------+ ... Length fields do not include themselves. */ uint8_t au8AlpnList[ALPN_LIST_MAX_SIZE] = {0}; uint8_t *pu8Ptr = &au8AlpnList[3] + u8Length; uint8_t u8Len = 0; memcpy(&au8AlpnList[3], pcProtocolList, u8Length); u8Length++; au8AlpnList[1] = u8Length; au8AlpnList[2] = ' '; /* Convert space characters into length fields. */ while (u8Length--) { if (*--pu8Ptr == ' ') { if (u8Len == 0) goto ERR; *pu8Ptr = u8Len; u8Len = 0; } else u8Len++; } s8Ret = setsockopt(sock, SOL_SSL_SOCKET, SO_SSL_ALPN, au8AlpnList, sizeof(au8AlpnList)); } } ERR: return s8Ret; } /********************************************************************* Function get_alpn_protocol_index Description This function gets the protocol list used for application-layer protocol negotiation (ALPN). If used, it must be called after creating a SSL socket (using @ref socket) and before connecting/binding (using @ref connect or @ref bind). Return The function returns the index of the selected application-layer protocol. Special values: 0: no negotiation has occurred. <0: error. *********************************************************************/ int8_t get_alpn_index(SOCKET sock) { if (sock >= TCP_SOCK_MAX || sock < 0) return SOCK_ERR_INVALID_ARG; if (!(gastrSockets[sock].u8SSLFlags & SSL_FLAGS_ACTIVE) || !gastrSockets[sock].bIsUsed) return SOCK_ERR_INVALID_ARG; return gastrSockets[sock].u8AlpnStatus; } /********************************************************************* Function IsSocketReady Description Return None. *********************************************************************/ uint8_t IsSocketReady(void) { return gbSocketInit; } /********************************************************************* Function get_error_detail Description This function gets detail about a socket failure. The application can call this when notified of a socket failure via @ref SOCKET_MSG_CONNECT or @ref SOCKET_MSG_RECV. If used, it must be called before @ref shutdown. Return The function returns @ref SOCK_ERR_NO_ERROR if the request is successful and a negative value otherwise. *********************************************************************/ int8_t get_error_detail(SOCKET sock, tstrSockErr *pstrErr) { if ((sock >= TCP_SOCK_MAX) || (sock < 0) || (pstrErr == NULL)) return SOCK_ERR_INVALID_ARG; if (!gastrSockets[sock].bIsUsed) return SOCK_ERR_INVALID_ARG; pstrErr->enuErrSource = gastrSockets[sock].u8ErrSource; pstrErr->u8ErrCode = gastrSockets[sock].u8ErrCode; return SOCK_ERR_NO_ERROR; }