diff --git a/doc/dox_comments/header_files/wolfio.h b/doc/dox_comments/header_files/wolfio.h index bc50477fe9..a809b74f41 100644 --- a/doc/dox_comments/header_files/wolfio.h +++ b/doc/dox_comments/header_files/wolfio.h @@ -459,7 +459,9 @@ void wolfSSL_SetIOWriteFlags(WOLFSSL* ssl, int flags); \param nxsocket a pointer to type NX_TCP_SOCKET that is set to the nxTcpSocket member of the nxCtx structure. \param waitoption a ULONG type that is set to the nxWait member of - the nxCtx structure. + the nxCtx structure. With NX_NO_WAIT, or any wait option that can expire, + the callbacks report WANT_READ/WANT_WRITE instead of a fatal error, so + the wolfSSL call can be retried. _Example_ \code @@ -496,6 +498,10 @@ void wolfSSL_SetIO_NetX(WOLFSSL* ssl, NX_TCP_SOCKET* nxsocket, \param nxdip the destination NXD_ADDRESS (passed by value; IPv4 or IPv6). \param nxport the destination UDP port number. \param waitoption a ULONG NetX wait option (e.g. NX_WAIT_FOREVER or ticks). + With NX_NO_WAIT, or any wait option that can expire, NetX_SendTo reports + WANT_WRITE instead of a fatal error. A receive that expires reports + WANT_READ when the session is set non blocking, and a timeout otherwise so + that DTLS can retransmit. _Example_ \code diff --git a/src/wolfio.c b/src/wolfio.c index 09d0b6818f..6d061722eb 100644 --- a/src/wolfio.c +++ b/src/wolfio.c @@ -2807,6 +2807,46 @@ void wolfSSL_CTX_SetIOSetPeer(WOLFSSL_CTX* ctx, CallbackSetPeer cb) #ifdef HAVE_NETX +/* Map a failing NetX status onto a wolfSSL CBIO error code. + * Transient conditions must not be reported as fatal, otherwise a non + * blocking (or short wait option) setup cannot retry the operation. */ +static int NetX_TranslateReturnCode(UINT status, int direction) +{ + int ret; + + switch (status) { + /* Receive queue empty, packet pool exhausted, peer receive window + * full or transmit queue at max depth. All clear on their own. */ + case NX_NO_PACKET: + case NX_WINDOW_OVERFLOW: + case NX_TX_QUEUE_DEPTH: + WOLFSSL_MSG("\tWould block"); + ret = (direction == SOCKET_SENDING) ? WOLFSSL_CBIO_ERR_WANT_WRITE + : WOLFSSL_CBIO_ERR_WANT_READ; + break; + + /* A suspended wait was aborted, treated like an interrupted call. */ + case NX_WAIT_ABORTED: + WOLFSSL_MSG("\tSocket interrupted"); + ret = WOLFSSL_CBIO_ERR_ISR; + break; + + /* NetX has no separate reset status, so a peer reset also lands + * here and is reported as a close. */ + case NX_NOT_CONNECTED: + WOLFSSL_MSG("\tConnection closed"); + ret = WOLFSSL_CBIO_ERR_CONN_CLOSE; + break; + + default: + WOLFSSL_MSG_EX("\tGeneral error: %u", (unsigned int)status); + ret = WOLFSSL_CBIO_ERR_GENERAL; + break; + } + + return ret; +} + /* The NetX receive callback for TLS * return : bytes read, or error */ @@ -2830,7 +2870,7 @@ int NetX_Receive(WOLFSSL *ssl, char *buf, int sz, void *ctx) nxCtx->nxWait); if (status != NX_SUCCESS) { WOLFSSL_MSG("NetX Recv receive error"); - return WOLFSSL_CBIO_ERR_GENERAL; + return NetX_TranslateReturnCode(status, SOCKET_RECEIVING); } } @@ -2885,21 +2925,21 @@ int NetX_Send(WOLFSSL* ssl, char *buf, int sz, void *ctx) nxCtx->nxWait); if (status != NX_SUCCESS) { WOLFSSL_MSG("NetX Send packet alloc error"); - return WOLFSSL_CBIO_ERR_GENERAL; + return NetX_TranslateReturnCode(status, SOCKET_SENDING); } status = nx_packet_data_append(packet, buf, sz, pool, nxCtx->nxWait); if (status != NX_SUCCESS) { nx_packet_release(packet); WOLFSSL_MSG("NetX Send data append error"); - return WOLFSSL_CBIO_ERR_GENERAL; + return NetX_TranslateReturnCode(status, SOCKET_SENDING); } status = nx_tcp_socket_send(nxCtx->nxTcpSocket, packet, nxCtx->nxWait); if (status != NX_SUCCESS) { nx_packet_release(packet); WOLFSSL_MSG("NetX Send socket send error"); - return WOLFSSL_CBIO_ERR_GENERAL; + return NetX_TranslateReturnCode(status, SOCKET_SENDING); } return sz; @@ -2930,13 +2970,25 @@ static int NetX_PeerAddrEqual(const NXD_ADDRESS* left, const NXD_ADDRESS* right) if (left->nxd_ip_version != right->nxd_ip_version) return 0; + /* NXD_ADDRESS only carries the union member for the families the NetX Duo + * build was configured with, so guard each access. NX_DISABLE_IPV4 and + * NX_DISABLE_IPV6 are set in nx_user.h, FEATURE_NX_IPV6 is derived from + * NX_DISABLE_IPV6 by nx_api.h. */ +#ifndef NX_DISABLE_IPV4 if (left->nxd_ip_version == NX_IP_VERSION_V4) return left->nxd_ip_address.v4 == right->nxd_ip_address.v4; +#endif +#ifdef FEATURE_NX_IPV6 + if (left->nxd_ip_version == NX_IP_VERSION_V6) { + return left->nxd_ip_address.v6[0] == right->nxd_ip_address.v6[0] && + left->nxd_ip_address.v6[1] == right->nxd_ip_address.v6[1] && + left->nxd_ip_address.v6[2] == right->nxd_ip_address.v6[2] && + left->nxd_ip_address.v6[3] == right->nxd_ip_address.v6[3]; + } +#endif - return left->nxd_ip_address.v6[0] == right->nxd_ip_address.v6[0] && - left->nxd_ip_address.v6[1] == right->nxd_ip_address.v6[1] && - left->nxd_ip_address.v6[2] == right->nxd_ip_address.v6[2] && - left->nxd_ip_address.v6[3] == right->nxd_ip_address.v6[3]; + /* Unknown or unsupported address family, do not treat it as our peer. */ + return 0; } /* The NetX receive callback for DTLS @@ -3019,7 +3071,7 @@ int NetX_ReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx) : WOLFSSL_CBIO_ERR_TIMEOUT; } WOLFSSL_MSG("NetX Recv receive error"); - return WOLFSSL_CBIO_ERR_GENERAL; + return NetX_TranslateReturnCode(status, SOCKET_RECEIVING); } status = nxd_udp_source_extract(nxCtx->nxPacket, &srcIp, &srcPort); @@ -3137,29 +3189,26 @@ int NetX_SendTo(WOLFSSL* ssl, char *buf, int sz, void *ctx) nxCtx->nxWait); if (status != NX_SUCCESS) { WOLFSSL_MSG("NetX Send packet alloc error"); - return WOLFSSL_CBIO_ERR_GENERAL; + return NetX_TranslateReturnCode(status, SOCKET_SENDING); } status = nx_packet_data_append(packet, buf, sz, pool, nxCtx->nxWait); if (status != NX_SUCCESS) { nx_packet_release(packet); WOLFSSL_MSG("NetX Send data append error"); - return WOLFSSL_CBIO_ERR_GENERAL; + return NetX_TranslateReturnCode(status, SOCKET_SENDING); } - if (nxCtx->nxdIp.nxd_ip_version == NX_IP_VERSION_V4) { - status = nx_udp_socket_send(nxCtx->nxUdpSocket, packet, - nxCtx->nxdIp.nxd_ip_address.v4, - (UINT)nxCtx->nxPort); - } - else { - status = nxd_udp_socket_send(nxCtx->nxUdpSocket, packet, - &nxCtx->nxdIp, (UINT)nxCtx->nxPort); - } + /* nxd_udp_socket_send() takes the NXD_ADDRESS itself and dispatches on + * nxd_ip_version, so it serves IPv4 and IPv6 without reaching into the + * nxd_ip_address union, which is only partly populated when the NetX Duo + * build disables a family. */ + status = nxd_udp_socket_send(nxCtx->nxUdpSocket, packet, + &nxCtx->nxdIp, (UINT)nxCtx->nxPort); if (status != NX_SUCCESS) { nx_packet_release(packet); WOLFSSL_MSG("NetX Send socket send error"); - return WOLFSSL_CBIO_ERR_GENERAL; + return NetX_TranslateReturnCode(status, SOCKET_SENDING); } return sz; diff --git a/wolfssl/wolfio.h b/wolfssl/wolfio.h index 2194c254c2..4d578d6118 100644 --- a/wolfssl/wolfio.h +++ b/wolfssl/wolfio.h @@ -411,10 +411,14 @@ #define SOCKET_ECONNREFUSED SOCKET_ERROR #define SOCKET_ECONNABORTED SOCKET_ERROR #elif defined(HAVE_NETX) - #define SOCKET_EWOULDBLOCK NX_NOT_CONNECTED - #define SOCKET_EAGAIN NX_NOT_CONNECTED + /* NetX has no errno, these map onto the closest nx_api.h status codes. + * A send can also block as NX_WINDOW_OVERFLOW or NX_TX_QUEUE_DEPTH, so + * use the WANT_READ/WANT_WRITE the callbacks return rather than testing + * a NetX status against these. */ + #define SOCKET_EWOULDBLOCK NX_NO_PACKET + #define SOCKET_EAGAIN NX_NO_PACKET #define SOCKET_ECONNRESET NX_NOT_CONNECTED - #define SOCKET_EINTR NX_NOT_CONNECTED + #define SOCKET_EINTR NX_WAIT_ABORTED #define SOCKET_EPIPE NX_NOT_CONNECTED #define SOCKET_ECONNREFUSED NX_NOT_CONNECTED #define SOCKET_ECONNABORTED NX_NOT_CONNECTED