diff --git a/cmake/config.in b/cmake/config.in index 295581bbdc2..209899181fd 100644 --- a/cmake/config.in +++ b/cmake/config.in @@ -19,6 +19,9 @@ /* Define to 1 if you have the `getaddrinfo' function. */ #cmakedefine HAVE_GETADDRINFO @HAVE_GETADDRINFO@ +/* Define to 1 if you have the `gettimeofday' function. */ +#cmakedefine HAVE_GETTIMEOFDAY @HAVE_GETTIMEOFDAY@ + /* Define to 1 if you have the `gmtime_r' function. */ #cmakedefine HAVE_GMTIME_R @HAVE_GMTIME_R@ diff --git a/src/dtls13.c b/src/dtls13.c index f1a9afd7f4d..0a1ebf89d12 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -782,6 +782,10 @@ int Dtls13RtxAddAck(WOLFSSL* ssl, w64wrapper epoch, w64wrapper seq) return MEMORY_E; } + #ifndef NO_ASN_TIME + if (ssl->dtls13Rtx.seenRecords == NULL) + ssl->dtls_ack_timer_start = DtlsTimerNowMs(); + #endif *prevNext = rn; rn->next = cur; ssl->dtls13Rtx.seenRecordsCount++; @@ -827,6 +831,7 @@ static void Dtls13RtxFlushAcks(WOLFSSL* ssl) ssl->dtls13Rtx.seenRecords = NULL; ssl->dtls13Rtx.seenRecordsCount = 0; + ssl->dtls13FastTimeout = 0; #ifdef WOLFSSL_RW_THREADED wc_UnLockMutex(&ssl->dtls13Rtx.mutex); #endif @@ -1039,8 +1044,10 @@ static int Dtls13SendOneFragmentRtx(WOLFSSL* ssl, handshakeType, hashOutput, Dtls13SendNow(ssl, handshakeType)); if (rtxRecord != NULL) { - if (ret == 0 || ret == WC_NO_ERR_TRACE(WANT_WRITE)) + if (ret == 0 || ret == WC_NO_ERR_TRACE(WANT_WRITE)) { Dtls13RtxAddRecord(&ssl->dtls13Rtx, rtxRecord); + DtlsTimerStart(ssl); + } else Dtls13FreeRtxBufferRecord(ssl, rtxRecord); } @@ -1628,8 +1635,20 @@ int Dtls13RecordRecvd(WOLFSSL* ssl) if (ssl->curRL.type != handshake) return 0; - if (!ssl->options.dtls13SendMoreAcks) + if (!ssl->options.dtls13SendMoreAcks) { + #ifdef WOLFSSL_RW_THREADED + if (wc_LockMutex(&ssl->dtls13Rtx.mutex) != 0) + return BAD_MUTEX_E; + #endif + #ifndef NO_ASN_TIME + if (!ssl->dtls13FastTimeout) + ssl->dtls_ack_timer_start = DtlsTimerNowMs(); + #endif ssl->dtls13FastTimeout = 1; + #ifdef WOLFSSL_RW_THREADED + wc_UnLockMutex(&ssl->dtls13Rtx.mutex); + #endif + } /* Acking happens in Dtls13RtxAddAckForCurRecord(). */ return 0; @@ -1655,26 +1674,18 @@ static int Dtls13RtxSendBuffered(WOLFSSL* ssl) byte* output; int isLast; int sendSz; -#ifndef NO_ASN_TIME -#ifdef WOLFSSL_32BIT_MILLI_TIME - word32 now; -#else - sword64 now; -#endif -#endif int ret; WOLFSSL_ENTER("Dtls13RtxSendBuffered"); #ifndef NO_ASN_TIME - now = TimeNowInMilliseconds(); - if (now - ssl->dtls13Rtx.lastRtx < DTLS13_MIN_RTX_INTERVAL) { + if (Dtls13RtxWaitMs(ssl) > 0) { #ifdef WOLFSSL_DEBUG_TLS WOLFSSL_MSG("Avoid too fast retransmission"); #endif /* WOLFSSL_DEBUG_TLS */ return 0; } - ssl->dtls13Rtx.lastRtx = now; + ssl->dtls13Rtx.lastRtx = TimeNowInMilliseconds(); #endif r = ssl->dtls13Rtx.rtxRecords; @@ -1731,6 +1742,7 @@ static int Dtls13RtxSendBuffered(WOLFSSL* ssl) /* this fragment will be sent eventually. Move it to the end of the list so next time we start with a new one. */ Dtls13RtxMoveToEndOfList(ssl, prevNext, r); + DtlsTimerStart(ssl); return ret; } @@ -1738,6 +1750,7 @@ static int Dtls13RtxSendBuffered(WOLFSSL* ssl) r = r->next; } + DtlsTimerStart(ssl); return 0; } @@ -3418,6 +3431,7 @@ int Dtls13DoScheduledWork(WOLFSSL* ssl) *tail = ssl->dtls13Rtx.seenRecords; ssl->dtls13Rtx.seenRecords = NULL; ssl->dtls13Rtx.seenRecordsCount = 0; + ssl->dtls13FastTimeout = 0; } else { /* Queue already at the cap: drop this batch. */ @@ -3456,39 +3470,89 @@ int Dtls13DoScheduledWork(WOLFSSL* ssl) return 0; } -/* Send ACKs when available after a timeout but only retransmit the last - * flight after a long timeout */ -int Dtls13RtxTimeout(WOLFSSL* ssl) +/* Return 1 when seen records wait for an ACK that may be sent now. */ +int Dtls13AckSendable(const WOLFSSL* ssl) +{ + return ssl->dtls13Rtx.seenRecords != NULL && + (ssl->options.serverState >= SERVER_HELLO_COMPLETE || + ssl->options.seenUnifiedHdr); +} + +/* Milliseconds until Dtls13RtxSendBuffered() retransmits again. */ +word32 Dtls13RtxWaitMs(WOLFSSL* ssl) +{ +#ifndef NO_ASN_TIME +#ifdef WOLFSSL_32BIT_MILLI_TIME + word32 since = TimeNowInMilliseconds() - ssl->dtls13Rtx.lastRtx; +#else + sword64 since = TimeNowInMilliseconds() - ssl->dtls13Rtx.lastRtx; + + if (since < 0) + return 0; +#endif + + if (since < DTLS13_MIN_RTX_INTERVAL) + return (word32)(DTLS13_MIN_RTX_INTERVAL - since); +#else + (void)ssl; +#endif + return 0; +} + +/* Return 1 once the buffered flight waited half its retransmission timeout. */ +static int Dtls13RtxTimerDue(WOLFSSL* ssl) +{ +#ifndef NO_ASN_TIME + if (ssl->dtls13Rtx.rtxRecords == NULL || ssl->dtls_timeout <= 0) + return 0; + return DtlsTimerElapsedMs(ssl->dtls_timer_start) >= + DtlsTimerTimeoutMs(ssl) / 2; +#else + (void)ssl; + return 0; +#endif +} + +/* Send ACKs after a timeout and retransmit the last flight only after a long + * timeout. expired is the DtlsTimer that expired, or 0 when not known. */ +int Dtls13RtxTimeout(WOLFSSL* ssl, int expired) { int ret = 0; + int ackOnly = (expired == DTLS_TIMER_ACK); + int fastTimeout; + int sendAck; - /* We don't want to send acks until we have done version - * negotiation in the SH or have received a unified header in the - * DTLS record. */ - if (ssl->dtls13Rtx.seenRecords != NULL && - (ssl->options.serverState >= SERVER_HELLO_COMPLETE || - ssl->options.seenUnifiedHdr)) { +#ifdef WOLFSSL_RW_THREADED + if (wc_LockMutex(&ssl->dtls13Rtx.mutex) != 0) + return BAD_MUTEX_E; +#endif + fastTimeout = ssl->dtls13FastTimeout; + ssl->dtls13FastTimeout = 0; + sendAck = Dtls13AckSendable(ssl); + if (sendAck) ssl->dtls13Rtx.sendAcks = 0; - /* reset fast timeout as we are sending ACKs */ - ssl->dtls13FastTimeout = 0; +#ifdef WOLFSSL_RW_THREADED + wc_UnLockMutex(&ssl->dtls13Rtx.mutex); +#endif + + if (expired == 0 && fastTimeout) + ackOnly = !Dtls13RtxTimerDue(ssl); + + if (sendAck) { ret = SendDtls13Ack(ssl); if (ret != 0) return ret; } - /* we have two timeouts, a shorter (dtls13FastTimeout = 1) and a longer - one. When the shorter expires we only send ACKs, as it normally means - that some messages we are waiting for don't arrive yet. But we - retransmit our buffered messages only if the longer timeout - expires. fastTimeout is 1/4 of the longer timeout */ - if (ssl->dtls13FastTimeout) { - ssl->dtls13FastTimeout = 0; + if (ackOnly) + return 0; + + if (Dtls13RtxWaitMs(ssl) > 0) return 0; - } /* Increase timeout on long timeout */ if (DtlsMsgPoolTimeout(ssl) != 0) - return WOLFSSL_FATAL_ERROR; + return SOCKET_ERROR_E; return Dtls13RtxSendBuffered(ssl); } @@ -3623,6 +3687,7 @@ int DoDtls13Ack(WOLFSSL* ssl, const byte* input, word32 inputSize, #ifndef WOLFSSL_RW_THREADED int ret; #endif + int hadRecords; int i; /* an epoch 0 ACK once encrypted only triggers retransmission */ @@ -3646,6 +3711,7 @@ int DoDtls13Ack(WOLFSSL* ssl, const byte* input, word32 inputSize, WOLFSSL_MSG("read ack records"); + hadRecords = ssl->dtls13Rtx.rtxRecords != NULL; ackMessage = input + OPAQUE16_LEN; for (i = 0; i < length; i += DTLS13_RN_SIZE) { ato64(ackMessage + i, &epoch); @@ -3670,6 +3736,9 @@ int DoDtls13Ack(WOLFSSL* ssl, const byte* input, word32 inputSize, #endif /* HAVE_WRITE_DUP */ } + if (hadRecords && ssl->dtls13Rtx.rtxRecords == NULL) + DtlsTimeoutReset(ssl); + /* last client flight was completely acknowledged by the server. Handshake is complete. */ if (ssl->options.side == WOLFSSL_CLIENT_END && diff --git a/src/internal.c b/src/internal.c index 81f89bd4c90..0e4bf6d496b 100644 --- a/src/internal.c +++ b/src/internal.c @@ -205,6 +205,12 @@ #include #endif +#if defined(__MACH__) || defined(__FreeBSD__) || \ + defined(__INCLUDE_NUTTX_CONFIG_H) || defined(WOLFSSL_RIOT_OS) +#include +#endif /* __MACH__ || __FreeBSD__ || + __INCLUDE_NUTTX_CONFIG_H || WOLFSSL_RIOT_OS */ + #define ERROR_OUT(err, eLabel) { ret = (int)(err); goto eLabel; } #define CHECK_RET(ret, exp, eLabel) do { if (((ret) = (exp)) != 0) goto eLabel;\ @@ -11928,6 +11934,7 @@ int DtlsMsgPoolSave(WOLFSSL* ssl, const byte* data, word32 dataSz, cur->next = item; } ssl->dtls_tx_msg_list_sz++; + DtlsTimerStart(ssl); } else ret = MEMORY_E; @@ -11944,12 +11951,111 @@ int DtlsMsgPoolTimeout(WOLFSSL* ssl) WOLFSSL_ENTER("DtlsMsgPoolTimeout"); if (ssl->dtls_timeout < ssl->dtls_timeout_max) { ssl->dtls_timeout *= DTLS_TIMEOUT_MULTIPLIER; + DtlsTimerStart(ssl); result = 0; } WOLFSSL_LEAVE("DtlsMsgPoolTimeout()", result); return result; } +#ifndef NO_ASN_TIME +/* Milliseconds since start, never more than really passed. */ +word32 DtlsTimerElapsedMs(word32 start) +{ + word32 elapsed = DtlsTimerNowMs() - start; + + /* Both readings were truncated to whole seconds. */ + if (DtlsTimerLowRes()) + elapsed = (elapsed >= 1000) ? elapsed - 1000 : 0; + return elapsed; +} + +/* Current retransmission timeout in milliseconds. */ +word32 DtlsTimerTimeoutMs(const WOLFSSL* ssl) +{ + return (word32)min(ssl->dtls_timeout, DTLS_TIMEOUT_MAX) * 1000; +} +#endif /* !NO_ASN_TIME */ + +/* Start or restart the retransmission timer. */ +void DtlsTimerStart(WOLFSSL* ssl) +{ +#ifndef NO_ASN_TIME + ssl->dtls_timer_start = DtlsTimerNowMs(); +#else + (void)ssl; +#endif +} + +/* Reset a backed off timeout and restart the timer with it. */ +void DtlsTimeoutReset(WOLFSSL* ssl) +{ + if (ssl->dtls_timeout != ssl->dtls_timeout_init) { + ssl->dtls_timeout = ssl->dtls_timeout_init; + DtlsTimerStart(ssl); + } +} + +/* Return 1 when the stored flight was sent and waits for the peer. */ +static int DtlsMsgPoolWaiting(WOLFSSL* ssl) +{ + if (ssl->options.side == WOLFSSL_SERVER_END) { + return ssl->options.acceptState == ACCEPT_BEGIN_RENEG || + ssl->options.acceptState == SERVER_HELLO_DONE || + ssl->options.acceptState == ACCEPT_FINISHED_DONE || + ssl->options.acceptState == ACCEPT_THIRD_REPLY_DONE; + } + if (ssl->options.side == WOLFSSL_CLIENT_END) { + return ssl->options.connectState == CLIENT_HELLO_SENT || + ssl->options.connectState == HELLO_AGAIN_REPLY || + ssl->options.connectState == FINISHED_DONE || + ssl->options.connectState == SECOND_REPLY_DONE; + } + return 1; +} + +/* Get the DTLS timers that run. + * + * @param [in] ssl SSL/TLS object. + * @param [out] ackStart Start of the DTLS 1.3 ack timer. May be NULL. + * @return DTLS_TIMER_RTX and DTLS_TIMER_ACK bits, 0 when none runs. + * @return BAD_MUTEX_E when locking fails. + */ +int DtlsTimersRunning(WOLFSSL* ssl, word32* ackStart) +{ + int running = 0; + + (void)ackStart; + +#ifdef WOLFSSL_DTLS13 + if (IsAtLeastTLSv1_3(ssl->version)) { + if (ssl->dtls13Rtx.rtxRecords != NULL) + running |= DTLS_TIMER_RTX; + #ifdef WOLFSSL_RW_THREADED + if (wc_LockMutex(&ssl->dtls13Rtx.mutex) != 0) + return BAD_MUTEX_E; + #endif + if (ssl->dtls13FastTimeout && Dtls13AckSendable(ssl)) + running |= DTLS_TIMER_ACK; + #ifndef NO_ASN_TIME + if (ackStart != NULL) + *ackStart = ssl->dtls_ack_timer_start; + #endif + #ifdef WOLFSSL_RW_THREADED + wc_UnLockMutex(&ssl->dtls13Rtx.mutex); + #endif + return running; + } +#endif /* WOLFSSL_DTLS13 */ + + if (ssl->options.handShakeState != HANDSHAKE_DONE && + (ssl->dtls_tx_msg_list != NULL || ssl->dtls_tx_msg != NULL) && + DtlsMsgPoolWaiting(ssl)) + running |= DTLS_TIMER_RTX; + + return running; +} + /* DtlsMsgPoolReset() deletes the stored transmit list. */ void DtlsMsgPoolReset(WOLFSSL* ssl) @@ -12041,17 +12147,7 @@ int DtlsMsgPoolSend(WOLFSSL* ssl, int sendOnlyFirstPacket) pool = ssl->dtls_tx_msg == NULL ? ssl->dtls_tx_msg_list : ssl->dtls_tx_msg; if (pool != NULL) { - if ((ssl->options.side == WOLFSSL_SERVER_END && - !(ssl->options.acceptState == ACCEPT_BEGIN_RENEG || - ssl->options.acceptState == SERVER_HELLO_DONE || - ssl->options.acceptState == ACCEPT_FINISHED_DONE || - ssl->options.acceptState == ACCEPT_THIRD_REPLY_DONE)) || - (ssl->options.side == WOLFSSL_CLIENT_END && - !(ssl->options.connectState == CLIENT_HELLO_SENT || - ssl->options.connectState == HELLO_AGAIN_REPLY || - ssl->options.connectState == FINISHED_DONE || - ssl->options.connectState == SECOND_REPLY_DONE))) { - + if (!DtlsMsgPoolWaiting(ssl)) { WOLFSSL_ERROR(DTLS_RETX_OVER_TX); ssl->error = DTLS_RETX_OVER_TX; return WOLFSSL_FATAL_ERROR; @@ -12160,6 +12256,8 @@ int DtlsMsgPoolSend(WOLFSSL* ssl, int sendOnlyFirstPacket) if (ret == 0 && ssl->options.groupMessages) ret = SendBuffered(ssl); + if (ret == 0 || ret == WC_NO_ERR_TRACE(WANT_WRITE)) + DtlsTimerStart(ssl); } WOLFSSL_LEAVE("DtlsMsgPoolSend()", ret); @@ -12475,6 +12573,273 @@ ProtocolVersion MakeDTLSv1_3(void) */ #endif /* !NO_ASN_TIME */ +/* TLS 1.3 tickets and PSK and DTLS 1.3 need a millisecond clock. */ +#if (defined(WOLFSSL_TLS13) && (defined(HAVE_SESSION_TICKET) || \ + !defined(NO_PSK))) || defined(WOLFSSL_DTLS13) + #define MS_TIME_NEEDED +#endif + +#if !defined(NO_TLS) && (defined(MS_TIME_NEEDED) || defined(WOLFSSL_DTLS)) +#ifndef NO_ASN_TIME +#ifdef WOLFSSL_32BIT_MILLI_TIME + #define MS_TIME word32 +#else + #define MS_TIME sword64 +#endif + +/* The time in milliseconds, for ticket ages and the DTLS timers. */ +#if defined(USER_TICKS) && !defined(MS_TIME_NEEDED) + #define MS_TIME_FALLBACK +#elif defined(USER_TICKS) + /* The application supplies TimeNowInMilliseconds(). */ +#elif defined(WOLFSSL_OPTEE) + MS_TIME TimeNowInMilliseconds(void) + { + TEE_Time now; + + /* Monotonic within the TA, and not the normal world's clock. */ + TEE_GetSystemTime(&now); + + return ((MS_TIME)now.seconds * 1000) + (MS_TIME)now.millis; + } + +#elif defined(TIME_OVERRIDES) + #define MS_TIME_LOW_RES +#ifndef NO_ASN + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME) wc_Time(0) * 1000; + } +#else + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME) XTIME(0) * 1000; + } +#endif + +#elif defined(XTIME_MS) + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME)XTIME_MS(0); + } + +#elif defined(USE_WINDOWS_API) + MS_TIME TimeNowInMilliseconds(void) + { + static int init = 0; + static LARGE_INTEGER freq; + LARGE_INTEGER count; + + if (!init) { + QueryPerformanceFrequency(&freq); + init = 1; + } + + QueryPerformanceCounter(&count); + + return (MS_TIME)(count.QuadPart / (freq.QuadPart / 1000)); + } + +#elif defined(HAVE_RTP_SYS) + #include "rtptime.h" + #define MS_TIME_LOW_RES + + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME)rtp_get_system_sec() * 1000; + } +#elif defined(WOLFSSL_DEOS) + #define MS_TIME_LOW_RES + MS_TIME TimeNowInMilliseconds(void) + { + const word32 systemTickTimeInHz = 1000000 / systemTickInMicroseconds(); + const volatile word32 *systemTickPtr = systemTickPointer(); + + return (MS_TIME) (*systemTickPtr/systemTickTimeInHz) * 1000; + } +#elif defined(MICRIUM) + #define MS_TIME_LOW_RES + MS_TIME TimeNowInMilliseconds(void) + { + OS_TICK ticks = 0; + OS_ERR err; + + ticks = OSTimeGet(&err); + + return (MS_TIME) (ticks / OSCfg_TickRate_Hz) * 1000; + } +#elif defined(MICROCHIP_TCPIP_V5) + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME) (TickGet() / (TICKS_PER_SECOND / 1000)); + } +#elif defined(MICROCHIP_TCPIP) + #if defined(MICROCHIP_MPLAB_HARMONY) + #include + + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME)SYS_TMR_TickCountGet() / + (SYS_TMR_TickCounterFrequencyGet() / 1000); + } + #else + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME)SYS_TICK_Get() / (SYS_TICK_TicksPerSecondGet() / 1000); + } + #endif + +#elif defined(FREESCALE_MQX) || defined(FREESCALE_KSDK_MQX) + #define MS_TIME_LOW_RES + MS_TIME TimeNowInMilliseconds(void) + { + TIME_STRUCT mqxTime; + + _time_get_elapsed(&mqxTime); + + return (MS_TIME) mqxTime.SECONDS * 1000; + } +#elif defined(FREESCALE_FREE_RTOS) || defined(FREESCALE_KSDK_FREERTOS) + #include "include/task.h" + + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME)((uint64_t)xTaskGetTickCount() * 1000 / + configTICK_RATE_HZ); + } +#elif defined(FREESCALE_KSDK_BM) + #include "lwip/sys.h" /* lwIP */ + + MS_TIME TimeNowInMilliseconds(void) + { + return sys_now(); + } + +#elif defined(WOLFSSL_CMSIS_RTOS) || defined(WOLFSSL_CMSIS_RTOSv2) + + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME)osKernelGetTickCount(); + } + +#elif defined(WOLFSSL_TIRTOS) + #define MS_TIME_LOW_RES + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME) Seconds_get() * 1000; + } +#elif defined(WOLFSSL_UTASKER) + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME)(uTaskerSystemTick / (TICK_RESOLUTION / 1000)); + } +#elif defined(WOLFSSL_LINUXKM) +#ifdef WOLFSSL_32BIT_MILLI_TIME + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME)wc_linuxkm_time_epoch_msecs(); + } +#else + MS_TIME TimeNowInMilliseconds(void) + { + s64 t; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) + struct timespec ts; + getnstimeofday(&ts); + t = ts.tv_sec * (s64)1000; + t += ts.tv_nsec / (s64)1000000; +#else + struct timespec64 ts; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) + ts = current_kernel_time64(); +#else + ktime_get_coarse_real_ts64(&ts); +#endif + t = ts.tv_sec * 1000L; + t += ts.tv_nsec / 1000000L; +#endif + return (MS_TIME)t; + } +#endif +#elif defined(WOLFSSL_QNX_CAAM) + MS_TIME TimeNowInMilliseconds(void) + { + struct timespec now; + clock_gettime(CLOCK_REALTIME, &now); + return (MS_TIME)(now.tv_sec * 1000 + now.tv_nsec / 1000000); + } +#elif defined(FUSION_RTOS) + MS_TIME TimeNowInMilliseconds(void) + { + struct timeval now; + if (FCL_GETTIMEOFDAY(&now, 0) < 0) + return 0; + + /* Convert to milliseconds number. */ + return (MS_TIME)((MS_TIME)now.tv_sec * 1000 + now.tv_usec / 1000); + } +#elif defined(WOLFSSL_ZEPHYR) + MS_TIME TimeNowInMilliseconds(void) + { + int64_t t; + #if defined(CONFIG_ARCH_POSIX) && !defined(CONFIG_BOARD_NATIVE_POSIX) \ + && !defined(CONFIG_BOARD_NATIVE_SIM) + k_cpu_idle(); + #endif + t = k_uptime_get(); /* returns current uptime in milliseconds */ + return (MS_TIME)t; + } +#elif defined(FREERTOS) + MS_TIME TimeNowInMilliseconds(void) + { + return (MS_TIME)((uint64_t)xTaskGetTickCount() * 1000 / + configTICK_RATE_HZ); + } +#elif !defined(HAVE_GETTIMEOFDAY) && !defined(MS_TIME_NEEDED) + #define MS_TIME_FALLBACK +#else + MS_TIME TimeNowInMilliseconds(void) + { + struct timeval now; + + if (gettimeofday(&now, 0) < 0) + return 0; + + /* Convert to milliseconds number. */ + return (MS_TIME)((MS_TIME)now.tv_sec * 1000 + now.tv_usec / 1000); + } +#endif + +#ifdef WOLFSSL_DTLS +/* Current time for the DTLS timers, in milliseconds. */ +word32 DtlsTimerNowMs(void) +{ +#ifdef MS_TIME_FALLBACK + return LowResTimer() * 1000; +#else + return (word32)TimeNowInMilliseconds(); +#endif +} + +/* Return 1 when the DTLS timer clock counts whole seconds. */ +int DtlsTimerLowRes(void) +{ +#if defined(MS_TIME_LOW_RES) || defined(MS_TIME_FALLBACK) + return 1; +#else + return 0; +#endif +} +#endif +#undef MS_TIME +#undef MS_TIME_LOW_RES +#undef MS_TIME_FALLBACK +#else + /* The application supplies TimeNowInMilliseconds(). */ +#endif /* !NO_ASN_TIME */ +#endif /* !NO_TLS && (MS_TIME_NEEDED || WOLFSSL_DTLS) */ +#undef MS_TIME_NEEDED + #ifndef NO_TLS #if !defined(WOLFSSL_NO_CLIENT_AUTH) && \ @@ -13086,7 +13451,7 @@ static int wolfSSLReceive(WOLFSSL* ssl, byte* buf, word32 sz) #ifdef WOLFSSL_DTLS #ifdef WOLFSSL_DTLS13 if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) { - int rtxRet = Dtls13RtxTimeout(ssl); + int rtxRet = Dtls13RtxTimeout(ssl, 0); if (rtxRet == WC_NO_ERR_TRACE(WANT_WRITE)) { /* Record that this ACK or retransmit still owes a * write, so the next flush sends it. */ @@ -27893,7 +28258,7 @@ static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) ) { /* Reset timeout as we have received a valid * DTLS handshake message */ - ssl->dtls_timeout = ssl->dtls_timeout_init; + DtlsTimeoutReset(ssl); } else if (DtlsShouldDropHandshake(ssl, ret)) { DropAndRestartProcessReply(ssl); @@ -27939,7 +28304,7 @@ static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) ) { /* Reset timeout as we have received a valid * DTLS handshake message */ - ssl->dtls_timeout = ssl->dtls_timeout_init; + DtlsTimeoutReset(ssl); } else if (DtlsShouldDropHandshake(ssl, ret)) { DropAndRestartProcessReply(ssl); diff --git a/src/ssl_api_dtls.c b/src/ssl_api_dtls.c index 174aa97ff7b..67a8d0e54c2 100644 --- a/src/ssl_api_dtls.c +++ b/src/ssl_api_dtls.c @@ -1195,36 +1195,163 @@ void wolfSSL_dtls13_set_send_more_acks(WOLFSSL* ssl, int value) } #endif /* WOLFSSL_DTLS13 */ -/* Get the time left until the next DTLS timeout. +#ifdef WOLFSSL_DTLS13 +/* Handle a DTLS 1.3 timeout. * - * @param [in] ssl SSL/TLS object. - * @param [out] timeleft Time left until the next timeout. - * @return 0 always. + * @param [in] ssl SSL/TLS object. + * @param [in] expired DtlsTimer that expired, or 0 when not known. + * @return WOLFSSL_SUCCESS on success. + * @return WOLFSSL_FATAL_ERROR on error. */ -int wolfSSL_DTLSv1_get_timeout(WOLFSSL* ssl, WOLFSSL_TIMEVAL* timeleft) +static int Dtls13GotTimeout(WOLFSSL* ssl, int expired) { - if (ssl && timeleft) { - XMEMSET(timeleft, 0, sizeof(WOLFSSL_TIMEVAL)); - timeleft->tv_sec = ssl->dtls_timeout; + int ret = Dtls13RtxTimeout(ssl, expired); + + if (ret < 0) { + if (ret == WC_NO_ERR_TRACE(WANT_WRITE)) + ssl->dtls13SendingAckOrRtx = 1; + ssl->error = ret; + WOLFSSL_ERROR(ret); + return WOLFSSL_FATAL_ERROR; } + + return WOLFSSL_SUCCESS; +} +#endif /* WOLFSSL_DTLS13 */ + +/* Get the time left on the DTLS timers. + * + * @param [in] ssl SSL/TLS object. + * @param [out] left Milliseconds until the first timer expires. + * @param [out] ackOnly 1 when only the DTLS 1.3 ack timer expired. + * @return 1 when a timer runs. + * @return 0 when no timer runs. + * @return BAD_MUTEX_E when locking fails. + */ +static int DtlsTimerLeft(WOLFSSL* ssl, word32* left, int* ackOnly) +{ +#ifndef NO_ASN_TIME + int running; + word32 timeout; + word32 elapsed; +#ifdef WOLFSSL_DTLS13 + word32 ackLeft; +#endif + word32 ackStart = 0; + + if (!IsDtlsNotSctpMode(ssl) || ssl->dtls_timeout <= 0 || + ssl->buffers.outputBuffer.length > 0) + return 0; + running = DtlsTimersRunning(ssl, &ackStart); +#ifdef WOLFSSL_HAVE_HS_SUSPEND + /* A suspended handshake has not finished sending its flight. */ + if (running > 0 && IsHsSuspendErr(ssl->error)) + running &= ~DTLS_TIMER_RTX; +#endif + if (running <= 0) + return running; + + timeout = DtlsTimerTimeoutMs(ssl); + *left = timeout; + *ackOnly = 1; + if (running & DTLS_TIMER_RTX) { + elapsed = DtlsTimerElapsedMs(ssl->dtls_timer_start); + *left = (elapsed < timeout) ? timeout - elapsed : 0; +#ifdef WOLFSSL_DTLS13 + if (IsAtLeastTLSv1_3(ssl->version)) + *left = max(*left, Dtls13RtxWaitMs(ssl)); +#endif + *ackOnly = (*left > 0); + } +#ifdef WOLFSSL_DTLS13 + if (running & DTLS_TIMER_ACK) { + elapsed = DtlsTimerNowMs() - ackStart; + ackLeft = (elapsed < timeout / 4) ? timeout / 4 - elapsed : 0; + if (ackLeft < *left || !(running & DTLS_TIMER_RTX)) + *left = ackLeft; + } +#endif + return 1; +#else + (void)ssl; + (void)left; + (void)ackOnly; return 0; +#endif } -#ifndef NO_WOLFSSL_STUB -/* Handle a DTLS timeout. +/* Get the time left until the next DTLS timer expires. * - * Not implemented - stub for OpenSSL compatibility. + * @param [in] ssl SSL/TLS object. + * @param [out] timeleft Time left, zero once a timer expired. + * @return 1 when the timer runs. + * @return 0 when no timer runs, or ssl or timeleft is NULL. + */ +int wolfSSL_DTLSv1_get_timeout(WOLFSSL* ssl, WOLFSSL_TIMEVAL* timeleft) +{ + word32 left = 0; + int ackOnly = 0; + int ret; + + if (ssl == NULL || timeleft == NULL) + return 0; + + XMEMSET(timeleft, 0, sizeof(WOLFSSL_TIMEVAL)); + ret = DtlsTimerLeft(ssl, &left, &ackOnly); + if (ret < 0) + WOLFSSL_MSG("Getting the DTLS timers failed"); + if (ret <= 0) + return 0; + + timeleft->tv_sec = left / 1000; + timeleft->tv_usec = (left % 1000) * 1000; + return 1; +} + +/* Handle an expired DTLS timer. + * + * Retransmits the last flight, or for an expired DTLS 1.3 ack timer only + * sends the ACKs. * * @param [in] ssl SSL/TLS object. - * @return 0 always. + * @return 1 when a timer expired and was handled. + * @return 0 when no timer runs or it has not expired yet. + * @return WOLFSSL_FATAL_ERROR when ssl is NULL or retransmitting failed. */ int wolfSSL_DTLSv1_handle_timeout(WOLFSSL* ssl) { - WOLFSSL_STUB("SSL_DTLSv1_handle_timeout"); - (void)ssl; - return 0; + word32 left = 0; + int ackOnly = 0; + int ret; + + if (ssl == NULL) + return WOLFSSL_FATAL_ERROR; + + ret = DtlsTimerLeft(ssl, &left, &ackOnly); + if (ret < 0) { + ssl->error = ret; + WOLFSSL_ERROR(ret); + return WOLFSSL_FATAL_ERROR; + } + if (ret == 0 || left > 0) + return 0; + +#ifdef WOLFSSL_DTLS13 + if (IsAtLeastTLSv1_3(ssl->version)) { + ret = Dtls13GotTimeout(ssl, ackOnly ? DTLS_TIMER_ACK : DTLS_TIMER_RTX); + } + else +#endif + { + ret = wolfSSL_dtls_got_timeout(ssl); + } + if (ret != WOLFSSL_SUCCESS) + return WOLFSSL_FATAL_ERROR; + + return 1; } +#ifndef NO_WOLFSSL_STUB /* Set the initial DTLS timeout duration. * * Not implemented - stub for OpenSSL compatibility. @@ -1304,18 +1431,8 @@ int wolfSSL_dtls_got_timeout(WOLFSSL* ssl) return WOLFSSL_FATAL_ERROR; #ifdef WOLFSSL_DTLS13 - if (IsAtLeastTLSv1_3(ssl->version)) { - result = Dtls13RtxTimeout(ssl); - if (result < 0) { - if (result == WC_NO_ERR_TRACE(WANT_WRITE)) - ssl->dtls13SendingAckOrRtx = 1; - ssl->error = result; - WOLFSSL_ERROR(result); - return WOLFSSL_FATAL_ERROR; - } - - return WOLFSSL_SUCCESS; - } + if (IsAtLeastTLSv1_3(ssl->version)) + return Dtls13GotTimeout(ssl, 0); #endif /* WOLFSSL_DTLS13 */ /* Do we have any 1.2 messages stored? */ diff --git a/src/tls13.c b/src/tls13.c index 1a4e34e73c6..524e364fb2b 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -124,12 +124,6 @@ #include #endif -#if defined(__MACH__) || defined(__FreeBSD__) || \ - defined(__INCLUDE_NUTTX_CONFIG_H) || defined(WOLFSSL_RIOT_OS) -#include -#endif /* __MACH__ || __FreeBSD__ || - __INCLUDE_NUTTX_CONFIG_H || WOLFSSL_RIOT_OS */ - #include #include @@ -2103,613 +2097,6 @@ int DeriveTls13Keys(WOLFSSL* ssl, int secret, int side, int store) return ret; } -#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) || defined(WOLFSSL_DTLS13) -#ifdef WOLFSSL_32BIT_MILLI_TIME -#ifndef NO_ASN_TIME -#if defined(USER_TICKS) -#if 0 - word32 TimeNowInMilliseconds(void) - { - /* - write your own clock tick function if don't want gettimeofday() - needs millisecond accuracy but doesn't have to correlated to EPOCH - */ - } -#endif - -#elif defined(WOLFSSL_OPTEE) - word32 TimeNowInMilliseconds(void) - { - TEE_Time now; - - /* Monotonic within the TA, and not the normal world's clock. */ - TEE_GetSystemTime(&now); - - return (word32)((now.seconds * 1000) + now.millis); - } - -#elif defined(TIME_OVERRIDES) -#if !defined(NO_ASN) && !defined(NO_ASN_TIME) - word32 TimeNowInMilliseconds(void) - { - return (word32) wc_Time(0) * 1000; - } -#else - #ifndef HAVE_TIME_T_TYPE - typedef long time_t; - #endif - extern time_t XTIME(time_t * timer); - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - return (word32) XTIME(0) * 1000; - } -#endif - -#elif defined(XTIME_MS) - word32 TimeNowInMilliseconds(void) - { - return (word32)XTIME_MS(0); - } - -#elif defined(USE_WINDOWS_API) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - static int init = 0; - static LARGE_INTEGER freq; - LARGE_INTEGER count; - - if (!init) { - QueryPerformanceFrequency(&freq); - init = 1; - } - - QueryPerformanceCounter(&count); - - return (word32)(count.QuadPart / (freq.QuadPart / 1000)); - } - -#elif defined(HAVE_RTP_SYS) - #include "rtptime.h" - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - return (word32)rtp_get_system_sec() * 1000; - } -#elif defined(WOLFSSL_DEOS) - word32 TimeNowInMilliseconds(void) - { - const word32 systemTickTimeInHz = 1000000 / systemTickInMicroseconds(); - word32 *systemTickPtr = systemTickPointer(); - - return (word32) (*systemTickPtr/systemTickTimeInHz) * 1000; - } -#elif defined(MICRIUM) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - OS_TICK ticks = 0; - OS_ERR err; - - ticks = OSTimeGet(&err); - - return (word32) (ticks / OSCfg_TickRate_Hz) * 1000; - } -#elif defined(MICROCHIP_TCPIP_V5) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - return (word32) (TickGet() / (TICKS_PER_SECOND / 1000)); - } -#elif defined(MICROCHIP_TCPIP) - #if defined(MICROCHIP_MPLAB_HARMONY) - #include - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - return (word32)(SYS_TMR_TickCountGet() / - (SYS_TMR_TickCounterFrequencyGet() / 1000)); - } - #else - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - return (word32)(SYS_TICK_Get() / (SYS_TICK_TicksPerSecondGet() / 1000)); - } - - #endif - -#elif defined(FREESCALE_MQX) || defined(FREESCALE_KSDK_MQX) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - TIME_STRUCT mqxTime; - - _time_get_elapsed(&mqxTime); - - return (word32) mqxTime.SECONDS * 1000; - } -#elif defined(FREESCALE_FREE_RTOS) || defined(FREESCALE_KSDK_FREERTOS) - #include "include/task.h" - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - return (unsigned int)(((float)xTaskGetTickCount()) / - (configTICK_RATE_HZ / 1000)); - } -#elif defined(FREESCALE_KSDK_BM) - #include "lwip/sys.h" /* lwIP */ - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - return sys_now(); - } - -#elif defined(WOLFSSL_CMSIS_RTOS) || defined(WOLFSSL_CMSIS_RTOSv2) - - word32 TimeNowInMilliseconds(void) - { - return (word32)osKernelGetTickCount(); - } - -#elif defined(WOLFSSL_TIRTOS) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - return (word32) Seconds_get() * 1000; - } -#elif defined(WOLFSSL_UTASKER) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - return (word32)(uTaskerSystemTick / (TICK_RESOLUTION / 1000)); - } -#elif defined(WOLFSSL_LINUXKM) - word32 TimeNowInMilliseconds(void) - { - return (word32)wc_linuxkm_time_epoch_msecs(); - } -#elif defined(WOLFSSL_QNX_CAAM) - word32 TimeNowInMilliseconds(void) - { - struct timespec now; - clock_gettime(CLOCK_REALTIME, &now); - return (word32)(now.tv_sec * 1000 + now.tv_nsec / 1000000); - } -#elif defined(FUSION_RTOS) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - struct timeval now; - if (FCL_GETTIMEOFDAY(&now, 0) < 0) - return 0; - - /* Convert to milliseconds number. */ - return (word32)(now.tv_sec * 1000 + now.tv_usec / 1000); - } -#elif defined(WOLFSSL_ZEPHYR) - word32 TimeNowInMilliseconds(void) - { - int64_t t; - #if defined(CONFIG_ARCH_POSIX) - k_cpu_idle(); - #endif - t = k_uptime_get(); /* returns current uptime in milliseconds */ - return (word32)t; - } -#elif defined(FREERTOS) - word32 TimeNowInMilliseconds(void) - { - return (word32)((uint64_t)(xTaskGetTickCount() * 1000) / - configTICK_RATE_HZ); - } -#else - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - word32 TimeNowInMilliseconds(void) - { - struct timeval now; - - if (gettimeofday(&now, 0) < 0) - return 0; - - /* Convert to milliseconds number. */ - return (word32)(now.tv_sec * 1000 + now.tv_usec / 1000); - } -#endif -#else - /* user must supply time in milliseconds function: - * word32 TimeNowInMilliseconds(void); - * The response is milliseconds elapsed - */ -#endif /* !NO_ASN_TIME */ -#else -#ifndef NO_ASN_TIME -#if defined(USER_TICKS) -#if 0 - sword64 TimeNowInMilliseconds(void) - { - /* - write your own clock tick function if don't want gettimeofday() - needs millisecond accuracy but doesn't have to correlated to EPOCH - */ - } -#endif - -#elif defined(WOLFSSL_OPTEE) - sword64 TimeNowInMilliseconds(void) - { - TEE_Time now; - - /* Monotonic within the TA, and not the normal world's clock. */ - TEE_GetSystemTime(&now); - - return ((sword64)now.seconds * 1000) + (sword64)now.millis; - } - -#elif defined(TIME_OVERRIDES) -#if !defined(NO_ASN) && !defined(NO_ASN_TIME) - sword64 TimeNowInMilliseconds(void) - { - return (sword64) wc_Time(0) * 1000; - } -#else - #ifndef HAVE_TIME_T_TYPE - typedef long time_t; - #endif - extern time_t XTIME(time_t * timer); - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 32-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - return (sword64) XTIME(0) * 1000; - } -#endif - -#elif defined(XTIME_MS) - sword64 TimeNowInMilliseconds(void) - { - return (sword64)XTIME_MS(0); - } - -#elif defined(USE_WINDOWS_API) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - static int init = 0; - static LARGE_INTEGER freq; - LARGE_INTEGER count; - - if (!init) { - QueryPerformanceFrequency(&freq); - init = 1; - } - - QueryPerformanceCounter(&count); - - return (sword64)(count.QuadPart / (freq.QuadPart / 1000)); - } - -#elif defined(HAVE_RTP_SYS) - #include "rtptime.h" - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - return (sword64)rtp_get_system_sec() * 1000; - } -#elif defined(WOLFSSL_DEOS) - sword64 TimeNowInMilliseconds(void) - { - const word32 systemTickTimeInHz = 1000000 / systemTickInMicroseconds(); - word32 *systemTickPtr = systemTickPointer(); - - return (sword64) (*systemTickPtr/systemTickTimeInHz) * 1000; - } -#elif defined(MICRIUM) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - OS_TICK ticks = 0; - OS_ERR err; - - ticks = OSTimeGet(&err); - - return (sword64) (ticks / OSCfg_TickRate_Hz) * 1000; - } -#elif defined(MICROCHIP_TCPIP_V5) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - return (sword64) (TickGet() / (TICKS_PER_SECOND / 1000)); - } -#elif defined(MICROCHIP_TCPIP) - #if defined(MICROCHIP_MPLAB_HARMONY) - #include - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - return (sword64)SYS_TMR_TickCountGet() / - (SYS_TMR_TickCounterFrequencyGet() / 1000); - } - #else - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - return (sword64)SYS_TICK_Get() / (SYS_TICK_TicksPerSecondGet() / 1000); - } - - #endif - -#elif defined(FREESCALE_MQX) || defined(FREESCALE_KSDK_MQX) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - TIME_STRUCT mqxTime; - - _time_get_elapsed(&mqxTime); - - return (sword64) mqxTime.SECONDS * 1000; - } -#elif defined(FREESCALE_FREE_RTOS) || defined(FREESCALE_KSDK_FREERTOS) - #include "include/task.h" - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - return (sword64)xTaskGetTickCount() / (configTICK_RATE_HZ / 1000); - } -#elif defined(FREESCALE_KSDK_BM) - #include "lwip/sys.h" /* lwIP */ - - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - return sys_now(); - } - -#elif defined(WOLFSSL_CMSIS_RTOS) || defined(WOLFSSL_CMSIS_RTOSv2) - - sword64 TimeNowInMilliseconds(void) - { - return (sword64)osKernelGetTickCount(); - } - -#elif defined(WOLFSSL_TIRTOS) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - return (sword64) Seconds_get() * 1000; - } -#elif defined(WOLFSSL_UTASKER) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - return (sword64)(uTaskerSystemTick / (TICK_RESOLUTION / 1000)); - } -#elif defined(WOLFSSL_LINUXKM) - sword64 TimeNowInMilliseconds(void) - { - s64 t; -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) - struct timespec ts; - getnstimeofday(&ts); - t = ts.tv_sec * (s64)1000; - t += ts.tv_nsec / (s64)1000000; -#else - struct timespec64 ts; -#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) - ts = current_kernel_time64(); -#else - ktime_get_coarse_real_ts64(&ts); -#endif - t = ts.tv_sec * 1000L; - t += ts.tv_nsec / 1000000L; -#endif - return (sword64)t; - } -#elif defined(WOLFSSL_QNX_CAAM) - sword64 TimeNowInMilliseconds(void) - { - struct timespec now; - clock_gettime(CLOCK_REALTIME, &now); - return (sword64)(now.tv_sec * 1000 + now.tv_nsec / 1000000); - } -#elif defined(FUSION_RTOS) - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - struct timeval now; - if (FCL_GETTIMEOFDAY(&now, 0) < 0) - return 0; - - /* Convert to milliseconds number. */ - return (sword64)now.tv_sec * 1000 + now.tv_usec / 1000; - } -#elif defined(WOLFSSL_ZEPHYR) - sword64 TimeNowInMilliseconds(void) - { - int64_t t; - #if defined(CONFIG_ARCH_POSIX) - k_cpu_idle(); - #endif - t = k_uptime_get(); /* returns current uptime in milliseconds */ - return (sword64)t; - } -#elif defined(FREERTOS) - sword64 TimeNowInMilliseconds(void) - { - return (sword64)((uint64_t)(xTaskGetTickCount() * 1000) / - configTICK_RATE_HZ); - } -#else - /* The time in milliseconds. - * Used for tickets to represent difference between when first seen and when - * sending. - * - * returns the time in milliseconds as a 64-bit value. - */ - sword64 TimeNowInMilliseconds(void) - { - struct timeval now; - - if (gettimeofday(&now, 0) < 0) - return 0; - - /* Convert to milliseconds number. */ - return (sword64)now.tv_sec * 1000 + now.tv_usec / 1000; - } -#endif -#else - /* user must supply time in milliseconds function: - * sword64 TimeNowInMilliseconds(void); - * The response is milliseconds elapsed - */ -#endif /* !NO_ASN_TIME */ -#endif /* WOLFSSL_32BIT_MILLI_TIME */ -#endif /* HAVE_SESSION_TICKET || !NO_PSK || WOLFSSL_DTLS13 */ - /* Add record layer header to message. * * output The buffer to write the record layer header into. diff --git a/src/wolfio.c b/src/wolfio.c index 3b44df7a569..08de336b19c 100644 --- a/src/wolfio.c +++ b/src/wolfio.c @@ -738,23 +738,10 @@ int EmbedReceiveFrom(WOLFSSL *ssl, char *buf, int sz, void *ctx) /* Don't use ssl->options.handShakeDone since it is true even if * we are in the process of renegotiation */ - doDtlsTimeout = ssl->options.handShakeState != HANDSHAKE_DONE; - -#ifdef WOLFSSL_DTLS13 - if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) { - doDtlsTimeout = doDtlsTimeout || ssl->dtls13Rtx.rtxRecords != NULL; -#ifdef WOLFSSL_RW_THREADED - ret = wc_LockMutex(&ssl->dtls13Rtx.mutex); - if (ret != 0) - return ret; -#endif - doDtlsTimeout = doDtlsTimeout || - (ssl->dtls13FastTimeout && ssl->dtls13Rtx.seenRecords != NULL); -#ifdef WOLFSSL_RW_THREADED - wc_UnLockMutex(&ssl->dtls13Rtx.mutex); -#endif - } -#endif /* WOLFSSL_DTLS13 */ + ret = DtlsTimersRunning(ssl, NULL); + if (ret < 0) + return ret; + doDtlsTimeout = ssl->options.handShakeState != HANDSHAKE_DONE || ret != 0; do { int ignore = 0; diff --git a/tests/api/test_dtls.c b/tests/api/test_dtls.c index 2fb4f10ef28..2c486535618 100644 --- a/tests/api/test_dtls.c +++ b/tests/api/test_dtls.c @@ -2910,7 +2910,6 @@ int test_dtls_drop_client_ack(void) /* trigger client timeout, finished should be rtx */ ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS); - ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS); /* this should have triggered a rtx */ ExpectIntGT(test_ctx.s_msg_count, 0); @@ -3108,6 +3107,9 @@ int test_dtls_timeout(void) ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); ExpectIntEQ(wolfSSL_dtls_get_current_timeout(ssl_c), 1); #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_DTLS13) + /* Don't wait DTLS13_MIN_RTX_INTERVAL since the last retransmission. */ + if (ssl_c != NULL) + ssl_c->dtls13Rtx.lastRtx = 0; /* will return 0 when not 1.3 */ if (wolfSSL_dtls13_use_quick_timeout(ssl_c)) ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS); @@ -13790,8 +13792,8 @@ int test_wolfSSL_DTLSv1_compat_timeouts(void) ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl, &tv), 0); /* NULL arguments are tolerated. */ ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(NULL, NULL), 0); -#ifndef NO_WOLFSSL_STUB ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl), 0); +#ifndef NO_WOLFSSL_STUB wolfSSL_DTLSv1_set_initial_timeout_duration(ssl, 1000); #endif @@ -13801,6 +13803,885 @@ int test_wolfSSL_DTLSv1_compat_timeouts(void) return EXPECT_RESULT(); } +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_DTLS) && !defined(NO_ASN_TIME) +/* Move the flight's send time back past the current timeout. */ +static void test_DTLSv1_timer_expire(WOLFSSL* ssl) +{ + if (ssl != NULL) + ssl->dtls_timer_start -= (word32)(ssl->dtls_timeout + 1) * 1000; +} + +#ifdef WOLFSSL_DTLS13 +static void test_DTLSv1_ack_timer_expire(WOLFSSL* ssl) +{ + if (ssl != NULL) + ssl->dtls_ack_timer_start -= (word32)(ssl->dtls_timeout + 1) * 1000; +} +#endif + +static int test_DTLSv1_timer_left(WOLFSSL* ssl) +{ + WOLFSSL_TIMEVAL left; + + XMEMSET(&left, 0, sizeof(left)); + if (wolfSSL_DTLSv1_get_timeout(ssl, &left) != 1) + return -1; + return (int)(left.tv_sec * 1000 + left.tv_usec / 1000); +} + +static int test_DTLSv1_timer(method_provider method_c, + method_provider method_s) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_TIMEVAL left; + char buf[16]; + word32 now = 0; + int i; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + method_c, method_s), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_s, 1), WOLFSSL_SUCCESS); + + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_c, &left), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(test_ctx.s_msg_count, 1); + XMEMSET(&left, 0, sizeof(left)); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_c, &left), 1); + ExpectTrue(left.tv_sec > 0 || left.tv_usec > 0); + ExpectTrue(left.tv_sec == 0 || (left.tv_sec == 1 && left.tv_usec == 0)); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + ExpectIntEQ(test_ctx.s_msg_count, 1); + + if (DtlsTimerLowRes()) { + /* A start one tick ago may be under a second, so 1 s hasn't passed. */ + for (i = 0; i < 3 && EXPECT_SUCCESS(); i++) { + now = DtlsTimerNowMs(); + ssl_c->dtls_timer_start = now - 1000; + XMEMSET(&left, 0, sizeof(left)); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_c, &left), 1); + if (DtlsTimerNowMs() == now) + break; + } + ExpectTrue(left.tv_sec > 0 || left.tv_usec > 0); + } + else { + if (ssl_c != NULL) + ssl_c->dtls_timer_start -= 300; + ExpectIntGT(test_DTLSv1_timer_left(ssl_c), 0); + ExpectIntLE(test_DTLSv1_timer_left(ssl_c), 700); + } + + test_DTLSv1_timer_expire(ssl_c); + ExpectIntEQ(test_DTLSv1_timer_left(ssl_c), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 1); + ExpectIntEQ(test_ctx.s_msg_count, 2); + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->dtls_timeout, 2); + XMEMSET(&left, 0, sizeof(left)); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_c, &left), 1); + ExpectTrue(left.tv_sec >= 1 && left.tv_sec <= 2); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + ExpectIntEQ(test_ctx.s_msg_count, 2); + ExpectIntEQ(test_memio_drop_message(&test_ctx, 0, 1), 0); + + /* HelloVerifyRequest and HelloRetryRequest are not retransmitted. */ + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_s, &left), 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntGT(test_DTLSv1_timer_left(ssl_s), 0); + + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + /* Let both sides consume any post-handshake messages and acks. */ + ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + if (wolfSSL_version(ssl_c) == DTLS1_3_VERSION) + TEST_DTLS13_PUMP(ssl_c); + ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + if (wolfSSL_version(ssl_s) == DTLS1_3_VERSION) + TEST_DTLS13_PUMP(ssl_s); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_c, &left), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_s, &left), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_s), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +/* A zero timeout means no timeout, so no timer runs. */ +static int test_DTLSv1_timer_zero(method_provider method_c, + method_provider method_s) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_TIMEVAL left; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + method_c, method_s), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 0), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_c, &left), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + ExpectIntEQ(test_ctx.s_msg_count, 1); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +/* At the maximum timeout the retransmission fails. */ +static int test_DTLSv1_timer_max(method_provider method_c, + method_provider method_s) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + method_c, method_s), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_dtls_set_timeout_max(ssl_c, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + test_DTLSv1_timer_expire(ssl_c); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), WOLFSSL_FATAL_ERROR); + ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR), + WC_NO_ERR_TRACE(SOCKET_ERROR_E)); + ExpectIntEQ(test_ctx.s_msg_count, 1); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +/* Timeouts above DTLS_TIMEOUT_MAX are reported as DTLS_TIMEOUT_MAX. */ +static int test_DTLSv1_timer_large(method_provider method_c, + method_provider method_s) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_TIMEVAL left; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + method_c, method_s), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_max(ssl_c, 5000000), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 5000000), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + XMEMSET(&left, 0, sizeof(left)); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_c, &left), 1); + ExpectIntLE(left.tv_sec, DTLS_TIMEOUT_MAX); + ExpectIntGE(left.tv_sec, DTLS_TIMEOUT_MAX - 1); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +#ifndef WOLFSSL_NO_TLS12 +/* A valid handshake message resets the timeout and restarts the timer. */ +static int test_DTLSv1_timer_reset(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 2), WOLFSSL_SUCCESS); + + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + test_DTLSv1_timer_expire(ssl_c); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 1); + ExpectIntEQ(test_ctx.s_msg_count, 2); + ExpectIntEQ(test_memio_drop_message(&test_ctx, 0, 1), 0); + if (ssl_c != NULL) { + ExpectIntEQ(ssl_c->dtls_timeout, 4); + ssl_c->dtls_timer_start -= 3000; + } + ExpectIntGT(test_DTLSv1_timer_left(ssl_c), 0); + + /* Deliver only the ServerHello of the server's flight. */ + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntGT(test_ctx.c_msg_count, 1); + while (EXPECT_SUCCESS() && test_ctx.c_msg_count > 1) + ExpectIntEQ(test_memio_drop_message(&test_ctx, 1, 1), 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->dtls_timeout, 2); + ExpectIntGT(test_DTLSv1_timer_left(ssl_c), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + ExpectIntEQ(test_ctx.s_msg_count, 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} +#endif /* !WOLFSSL_NO_TLS12 */ + +#ifndef WOLFSSL_NO_TLS12 +/* A flight resent for a retransmitted peer flight restarts the timer. */ +static int test_DTLSv1_timer_peer_resend(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + word32 start = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntGT(test_ctx.c_msg_count, 0); + test_memio_clear_buffer(&test_ctx, 1); + + test_DTLSv1_timer_expire(ssl_c); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 1); + if (ssl_s != NULL) { + ssl_s->dtls_timer_start -= 2000; + start = ssl_s->dtls_timer_start; + } + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntGT(test_ctx.c_msg_count, 0); + if (ssl_s != NULL) + ExpectIntNE(ssl_s->dtls_timer_start, start); + ExpectIntGT(test_DTLSv1_timer_left(ssl_s), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} +#endif /* !WOLFSSL_NO_TLS12 */ + +/* No timer runs while our own write is blocked. */ +static int test_DTLSv1_timer_write_blocked(method_provider method_c, + method_provider method_s) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_TIMEVAL left; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + method_c, method_s), 0); + + test_memio_simulate_want_write(&test_ctx, 1, 1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_WRITE); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_c, &left), 0); + test_DTLSv1_timer_expire(ssl_c); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + test_memio_simulate_want_write(&test_ctx, 1, 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(test_ctx.s_msg_count, 1); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +#ifndef WOLFSSL_NO_TLS12 +/* No timer runs while a flight is still being written. */ +static int test_DTLSv1_timer_mid_flight(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_TIMEVAL left; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method), 0); + + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + test_memio_simulate_want_write(&test_ctx, 1, 1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_WRITE); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_c, &left), 0); + test_DTLSv1_timer_expire(ssl_c); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + test_memio_simulate_want_write(&test_ctx, 1, 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} +#endif /* !WOLFSSL_NO_TLS12 */ + +#if (!defined(WOLFSSL_NO_TLS12) && defined(HAVE_SECURE_RENEGOTIATION)) || \ + (defined(WOLFSSL_DTLS13) && defined(HAVE_SESSION_TICKET)) +/* A flight sent while an older one is kept gets a fresh timer. */ +static int test_DTLSv1_timer_new_flight(method_provider method_c, + method_provider method_s) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + int kept = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + method_c, method_s), 0); +#ifdef HAVE_SECURE_RENEGOTIATION + ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSecureRenegotiation(ssl_s), WOLFSSL_SUCCESS); +#endif + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectNotNull(ssl_s); + if (ssl_s != NULL) { + kept = ssl_s->dtls_tx_msg_list != NULL; +#ifdef WOLFSSL_DTLS13 + kept |= ssl_s->dtls13Rtx.rtxRecords != NULL; +#endif + } + ExpectIntEQ(kept, 1); + test_DTLSv1_timer_expire(ssl_s); + +#if defined(WOLFSSL_DTLS13) && defined(HAVE_SESSION_TICKET) + if (wolfSSL_version(ssl_s) == DTLS1_3_VERSION) + ExpectIntEQ(wolfSSL_update_keys(ssl_s), WOLFSSL_SUCCESS); +#endif +#if !defined(WOLFSSL_NO_TLS12) && defined(HAVE_SECURE_RENEGOTIATION) + if (wolfSSL_version(ssl_s) != DTLS1_3_VERSION) { + ExpectIntEQ(wolfSSL_Rehandshake(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + } +#endif + ExpectIntGT(test_DTLSv1_timer_left(ssl_s), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_s), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} +#endif + +#ifdef WOLFSSL_DTLS13 +/* Records of a partly received flight are acked after a quarter timeout, + * without retransmitting or backing off. */ +static int test_DTLSv1_timer_ack(int blockAck) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + char held[TEST_MEMIO_BUF_SZ]; + int heldSz = (int)sizeof(held); + int left = 0; + int n = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 2), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_s, 2), WOLFSSL_SUCCESS); + + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + /* Hold back one record and drop the last one. */ + ExpectIntGE(n = test_ctx.c_msg_count, 3); + ExpectIntEQ(test_memio_copy_message(&test_ctx, 1, held, &heldSz, n - 2), + 0); + ExpectIntEQ(test_memio_drop_message(&test_ctx, 1, n - 1), 0); + ExpectIntEQ(test_memio_drop_message(&test_ctx, 1, n - 2), 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(test_ctx.s_msg_count, 0); + ExpectIntGT(left = test_DTLSv1_timer_left(ssl_c), 0); + ExpectIntLE(left, 500); + + /* An ack timer that stopped with records left restarts with the next + * record. */ + if (ssl_c != NULL) + ssl_c->dtls13FastTimeout = 0; + test_DTLSv1_ack_timer_expire(ssl_c); + ExpectIntEQ(test_DTLSv1_timer_left(ssl_c), -1); + ExpectIntEQ(test_memio_inject_message(&test_ctx, 1, held, heldSz), 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntGT(left = test_DTLSv1_timer_left(ssl_c), 0); + ExpectIntLE(left, 500); + + test_DTLSv1_ack_timer_expire(ssl_c); + ExpectIntEQ(test_DTLSv1_timer_left(ssl_c), 0); + if (blockAck) { + /* A blocked ACK is reported and sent by the next call. */ + test_memio_simulate_want_write(&test_ctx, 1, 1); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), + WOLFSSL_FATAL_ERROR); + ExpectIntEQ(wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR), + WOLFSSL_ERROR_WANT_WRITE); + ExpectIntEQ(test_ctx.s_msg_count, 0); + test_memio_simulate_want_write(&test_ctx, 1, 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + } + else { + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 1); + } + ExpectIntEQ(test_ctx.s_msg_count, 1); + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->dtls_timeout, 2); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + ExpectIntEQ(test_ctx.s_msg_count, 1); + test_memio_clear_buffer(&test_ctx, 0); + + /* The server resends what wasn't acked and the handshake completes. */ + test_DTLSv1_timer_expire(ssl_s); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_s), 1); + ExpectIntGT(test_ctx.c_msg_count, 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +/* A retransmission right after another one is held back without backing + * off. */ +static int test_DTLSv1_timer_throttle(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + test_DTLSv1_timer_expire(ssl_c); + /* A left over quick timeout flag doesn't hold back the retransmission. */ + if (ssl_c != NULL) + ssl_c->dtls13FastTimeout = 1; + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 1); + ExpectIntEQ(test_ctx.s_msg_count, 2); + + test_DTLSv1_timer_expire(ssl_c); + ExpectIntGT(test_DTLSv1_timer_left(ssl_c), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 0); + ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(test_ctx.s_msg_count, 2); + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->dtls_timeout, 2); + + if (ssl_c != NULL) + ssl_c->dtls13Rtx.lastRtx -= 2000; + ExpectIntEQ(test_DTLSv1_timer_left(ssl_c), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 1); + ExpectIntEQ(test_ctx.s_msg_count, 3); + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->dtls_timeout, 4); + +#ifndef WOLFSSL_32BIT_MILLI_TIME + /* A clock that stepped back doesn't hold the retransmission back. */ + test_DTLSv1_timer_expire(ssl_c); + if (ssl_c != NULL) + ssl_c->dtls13Rtx.lastRtx += 3600 * 1000; + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 1); + ExpectIntEQ(test_ctx.s_msg_count, 4); +#endif + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +/* An ACK for the whole flight resets a backed off DTLS 1.3 timeout. */ +static int test_DTLSv1_timer_ack_reset(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + char buf[16]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_no_ticket_TLSv13(ssl_s), 0); + + /* ClientHello, HelloRetryRequest, ClientHello, server flight */ + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + /* Drop the server's ACK of the client Finished. */ + ExpectIntEQ(wolfSSL_accept(ssl_s), WOLFSSL_SUCCESS); + test_memio_clear_buffer(&test_ctx, 1); + + test_DTLSv1_timer_expire(ssl_c); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 1); + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->dtls_timeout, 2); + ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + TEST_DTLS13_PUMP(ssl_s); + ExpectIntEQ(wolfSSL_negotiate(ssl_c), WOLFSSL_SUCCESS); + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->dtls_timeout, 1); + ExpectIntEQ(test_DTLSv1_timer_left(ssl_c), -1); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +/* A flight resent for the peer's ACK restarts the timer. */ +static int test_DTLSv1_timer_ack_resend(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + word32 start = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 2), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_s, 2), WOLFSSL_SUCCESS); + + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntGE(test_ctx.c_msg_count, 2); + ExpectIntEQ(test_memio_drop_message(&test_ctx, 1, + test_ctx.c_msg_count - 1), 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + test_DTLSv1_ack_timer_expire(ssl_c); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_c), 1); + ExpectIntEQ(test_ctx.s_msg_count, 1); + + if (ssl_s != NULL) { + ssl_s->dtls_timer_start -= 1500; + start = ssl_s->dtls_timer_start; + } + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntGT(test_ctx.c_msg_count, 0); + if (ssl_s != NULL) + ExpectIntNE(ssl_s->dtls_timer_start, start); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +/* A new flight drops the quick timeout of the records it acks, so the next + * timeout retransmits it. */ +static int test_DTLSv1_timer_flight_acks(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + int sent = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + /* The client takes the whole server flight and sends its Finished. */ + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntGT(sent = test_ctx.s_msg_count, 0); + ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS); + ExpectIntGT(test_ctx.s_msg_count, sent); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +/* With the quick timeout flag set, got_timeout only retransmits and backs + * off once the flight waited half its timeout. */ +static int test_DTLSv1_timer_got_timeout_quick(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(test_ctx.s_msg_count, 1); + + if (ssl_c != NULL) + ssl_c->dtls13FastTimeout = 1; + ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(test_ctx.s_msg_count, 1); + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->dtls_timeout, 1); + + if (ssl_c != NULL) + ssl_c->dtls13FastTimeout = 1; + test_DTLSv1_timer_expire(ssl_c); + ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(test_ctx.s_msg_count, 2); + if (ssl_c != NULL) + ExpectIntEQ(ssl_c->dtls_timeout, 2); + + /* With no timeout a quick timeout only sends the ACKs. */ + ExpectIntEQ(wolfSSL_dtls_set_timeout_init(ssl_c, 0), WOLFSSL_SUCCESS); + if (ssl_c != NULL) { + ssl_c->dtls13FastTimeout = 1; + ssl_c->dtls13Rtx.lastRtx = 0; + } + ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_c), WOLFSSL_SUCCESS); + ExpectIntEQ(test_ctx.s_msg_count, 2); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} + +#if !defined(NO_RSA) && !defined(NO_FILESYSTEM) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) +/* A timeout with records waiting for an ack and no flight to resend only + * sends the ACK. */ +static int test_DTLSv1_timer_got_timeout_acks(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL), + WOLFSSL_SUCCESS); + wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER | + WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + /* The server only gets the first record of the client's flight. */ + ExpectIntGE(test_ctx.s_msg_count, 2); + while (EXPECT_SUCCESS() && test_ctx.s_msg_count > 1) + ExpectIntEQ(test_memio_drop_message(&test_ctx, 0, 1), 0); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(test_ctx.c_msg_count, 0); + ExpectIntEQ(wolfSSL_dtls13_use_quick_timeout(ssl_s), 1); + + ExpectIntEQ(wolfSSL_dtls_got_timeout(ssl_s), WOLFSSL_SUCCESS); + ExpectIntEQ(test_ctx.c_msg_count, 1); + if (ssl_s != NULL) + ExpectIntEQ(ssl_s->dtls_timeout, 1); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} +#endif + +#ifdef WOLFSSL_ASYNC_CRYPT_SW +/* No timer runs while crypto suspends the server part way through a flight. */ +static int test_DTLSv1_timer_suspended(void) +{ + EXPECT_DECLS; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + WOLFSSL_TIMEVAL left; + int devId = INVALID_DEVID; + int suspended = 0; + int i; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(wolfAsync_DevOpen(&devId), 0); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0); + ExpectIntEQ(wolfSSL_SetDevId(ssl_s, devId), WOLFSSL_SUCCESS); + + for (i = 0; i < 50 && EXPECT_SUCCESS() && !suspended; i++) { + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_accept(ssl_s), -1); + if (wolfSSL_get_error(ssl_s, -1) == WC_NO_ERR_TRACE(WC_PENDING_E)) { + suspended = ssl_s->dtls13Rtx.rtxRecords != NULL; + if (!suspended) { + ExpectIntGE(wolfSSL_AsyncPoll(ssl_s, WOLF_POLL_FLAG_CHECK_HW), + 0); + } + } + } + ExpectIntEQ(suspended, 1); + test_DTLSv1_timer_expire(ssl_s); + ExpectIntEQ(wolfSSL_DTLSv1_get_timeout(ssl_s, &left), 0); + ExpectIntEQ(wolfSSL_DTLSv1_handle_timeout(ssl_s), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 50, NULL), 0); + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + wolfAsync_DevClose(&devId); + return EXPECT_RESULT(); +} +#endif +#endif /* WOLFSSL_DTLS13 */ +#endif + +int test_wolfSSL_DTLSv1_timer(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_DTLS) && !defined(NO_ASN_TIME) +#ifndef WOLFSSL_NO_TLS12 + ExpectIntEQ(test_DTLSv1_timer(wolfDTLSv1_2_client_method, + wolfDTLSv1_2_server_method), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_zero(wolfDTLSv1_2_client_method, + wolfDTLSv1_2_server_method), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_max(wolfDTLSv1_2_client_method, + wolfDTLSv1_2_server_method), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_large(wolfDTLSv1_2_client_method, + wolfDTLSv1_2_server_method), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_reset(), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_peer_resend(), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_mid_flight(), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_write_blocked(wolfDTLSv1_2_client_method, + wolfDTLSv1_2_server_method), TEST_SUCCESS); +#ifdef HAVE_SECURE_RENEGOTIATION + ExpectIntEQ(test_DTLSv1_timer_new_flight(wolfDTLSv1_2_client_method, + wolfDTLSv1_2_server_method), TEST_SUCCESS); +#endif +#endif +#ifdef WOLFSSL_DTLS13 + ExpectIntEQ(test_DTLSv1_timer(wolfDTLSv1_3_client_method, + wolfDTLSv1_3_server_method), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_zero(wolfDTLSv1_3_client_method, + wolfDTLSv1_3_server_method), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_max(wolfDTLSv1_3_client_method, + wolfDTLSv1_3_server_method), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_large(wolfDTLSv1_3_client_method, + wolfDTLSv1_3_server_method), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_write_blocked(wolfDTLSv1_3_client_method, + wolfDTLSv1_3_server_method), TEST_SUCCESS); +#ifdef HAVE_SESSION_TICKET + ExpectIntEQ(test_DTLSv1_timer_new_flight(wolfDTLSv1_3_client_method, + wolfDTLSv1_3_server_method), TEST_SUCCESS); +#endif + ExpectIntEQ(test_DTLSv1_timer_ack(0), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_ack(1), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_throttle(), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_ack_resend(), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_ack_reset(), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_flight_acks(), TEST_SUCCESS); + ExpectIntEQ(test_DTLSv1_timer_got_timeout_quick(), TEST_SUCCESS); +#if !defined(NO_RSA) && !defined(NO_FILESYSTEM) && \ + !defined(WOLFSSL_NO_CLIENT_AUTH) + ExpectIntEQ(test_DTLSv1_timer_got_timeout_acks(), TEST_SUCCESS); +#endif +#ifdef WOLFSSL_ASYNC_CRYPT_SW + ExpectIntEQ(test_DTLSv1_timer_suspended(), TEST_SUCCESS); +#endif +#endif +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_dtls13_set_send_more_acks(void) { EXPECT_DECLS; diff --git a/tests/api/test_dtls.h b/tests/api/test_dtls.h index 2a96a9bb731..5f0d6d51e9d 100644 --- a/tests/api/test_dtls.h +++ b/tests/api/test_dtls.h @@ -112,6 +112,7 @@ int test_wolfSSL_dtls_get0_peer(void); int test_wolfSSL_dtls_set_timeout_init(void); int test_wolfSSL_dtls_retransmit(void); int test_wolfSSL_DTLSv1_compat_timeouts(void); +int test_wolfSSL_DTLSv1_timer(void); int test_wolfSSL_dtls13_set_send_more_acks(void); int test_wolfSSL_dtls_srtp_keying_material(void); int test_wolfSSL_mcast_peers(void); @@ -357,6 +358,7 @@ int test_dtls_no_cookie_ch_pause(WOLFSSL* ssl, void* ctx); TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_set_timeout_init), \ TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_retransmit), \ TEST_DECL_GROUP("dtls", test_wolfSSL_DTLSv1_compat_timeouts), \ + TEST_DECL_GROUP("dtls", test_wolfSSL_DTLSv1_timer), \ TEST_DECL_GROUP("dtls", test_wolfSSL_dtls13_set_send_more_acks), \ TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_srtp_keying_material), \ TEST_DECL_GROUP("dtls", test_wolfSSL_mcast_peers), \ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 3974ba87cbc..05c596cfe35 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -7234,6 +7234,10 @@ struct WOLFSSL { int dtls_timeout; /* current timeout value, changes */ #ifndef NO_ASN_TIME word32 dtls_start_timeout; + word32 dtls_timer_start; /* ms, when the flight was sent */ +#ifdef WOLFSSL_DTLS13 + word32 dtls_ack_timer_start; /* ms, first record to ack */ +#endif #endif /* !NO_ASN_TIME */ word32 dtls_tx_msg_list_sz; word32 dtls_rx_msg_list_sz; @@ -8102,6 +8106,8 @@ WOLFSSL_LOCAL int Tls13KeyUpdateLimitReached(WOLFSSL* ssl); #define DtlsMsgListNextReady wolfSSL_DtlsMsgListNextReady #define DtlsMsgFind wolfSSL_DtlsMsgFind #define DtlsMsgStore wolfSSL_DtlsMsgStore + #define DtlsTimerNowMs wolfSSL_DtlsTimerNowMs + #define DtlsTimerLowRes wolfSSL_DtlsTimerLowRes #endif /* WOLFSSL_API_PREFIX_MAP */ WOLFSSL_LOCAL DtlsMsg* DtlsMsgNew(word32 sz, byte tx, void* heap); WOLFSSL_LOCAL void DtlsMsgDelete(DtlsMsg* item, void* heap); @@ -8122,9 +8128,23 @@ WOLFSSL_LOCAL int Tls13KeyUpdateLimitReached(WOLFSSL* ssl); void* heap); WOLFSSL_LOCAL DtlsMsg* DtlsMsgInsert(DtlsMsg* head, DtlsMsg* item); + /* DTLS timers returned by DtlsTimersRunning(). */ + enum DtlsTimer { + DTLS_TIMER_RTX = 0x1, /* a flight waits for a reply */ + DTLS_TIMER_ACK = 0x2 /* DTLS 1.3 records wait for an ack */ + }; WOLFSSL_LOCAL int DtlsMsgPoolSave(WOLFSSL* ssl, const byte* data, word32 dataSz, enum HandShakeType type); WOLFSSL_LOCAL int DtlsMsgPoolTimeout(WOLFSSL* ssl); + WOLFSSL_LOCAL void DtlsTimerStart(WOLFSSL* ssl); + WOLFSSL_LOCAL void DtlsTimeoutReset(WOLFSSL* ssl); + WOLFSSL_LOCAL int DtlsTimersRunning(WOLFSSL* ssl, word32* ackStart); +#ifndef NO_ASN_TIME + WOLFSSL_TEST_VIS word32 DtlsTimerNowMs(void); + WOLFSSL_TEST_VIS int DtlsTimerLowRes(void); + WOLFSSL_LOCAL word32 DtlsTimerElapsedMs(word32 start); + WOLFSSL_LOCAL word32 DtlsTimerTimeoutMs(const WOLFSSL* ssl); +#endif WOLFSSL_LOCAL int VerifyForDtlsMsgPoolSend(WOLFSSL* ssl, byte type, word32 fragOffset); WOLFSSL_LOCAL int VerifyForTxDtlsMsgDelete(WOLFSSL* ssl, DtlsMsg* item); @@ -8170,8 +8190,8 @@ WOLFSSL_LOCAL int Tls13KeyUpdateLimitReached(WOLFSSL* ssl); WOLFSSL_LOCAL void WriteSEQ(WOLFSSL* ssl, int verifyOrder, byte* out); -#if defined(WOLFSSL_TLS13) && (defined(HAVE_SESSION_TICKET) || \ - !defined(NO_PSK) || defined(WOLFSSL_DTLS13)) +#if (defined(WOLFSSL_TLS13) && (defined(HAVE_SESSION_TICKET) || \ + !defined(NO_PSK))) || defined(WOLFSSL_DTLS) #ifdef WOLFSSL_32BIT_MILLI_TIME WOLFSSL_LOCAL word32 TimeNowInMilliseconds(void); #else @@ -8502,7 +8522,9 @@ WOLFSSL_LOCAL int Dtls13HashClientHello(const WOLFSSL* ssl, byte* hash, WOLFSSL_LOCAL void Dtls13FreeFsmResources(WOLFSSL* ssl); WOLFSSL_LOCAL void Dtls13RtxFlushBuffered(WOLFSSL* ssl, byte keepNewSessionTicket); -WOLFSSL_LOCAL int Dtls13RtxTimeout(WOLFSSL* ssl); +WOLFSSL_LOCAL int Dtls13RtxTimeout(WOLFSSL* ssl, int expired); +WOLFSSL_LOCAL word32 Dtls13RtxWaitMs(WOLFSSL* ssl); +WOLFSSL_LOCAL int Dtls13AckSendable(const WOLFSSL* ssl); WOLFSSL_LOCAL int Dtls13ProcessBufferedMessages(WOLFSSL* ssl); WOLFSSL_LOCAL int Dtls13CheckAEADFailLimit(WOLFSSL* ssl); WOLFSSL_LOCAL int Dtls13UpdateWindowRecordRecvd(WOLFSSL* ssl);