Skip to content

TLS 1.3 NewSessionTicket breaks read ("Failed reading HTTP status line") #592

Description

@jvnipers

Connecting to a TLS 1.3 wss server (e.g. behind Cloudflare/nginx) fails on the first response read with Failed reading HTTP status line. Windows/mbedtls backend, mbedtls 3.6+ (TLS 1.3 on by default).

Cause: In TLS 1.3 the server sends NewSessionTicket after the handshake. mbedtls_ssl_read then returns MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET (-0x7B00), a non-fatal "read again" code.
SocketMbedTLS::recv treats it as fatal and readByte aborts. Same for MBEDTLS_ERR_SSL_RECEIVED_EARLY_DATA.

Secondary (Windows only): on WANT_READ, recv/send set the CRT errno, but Socket::getErrno() reads WSAGetLastError().
The value is invisible, so isWaitNeeded() aborts a legitimate would-block.

Fix? (IXSocketMbedTLS.cpp, recv, the while(true) already allows the retry):

if (res > 0) return res;

// TLS 1.3: swallow post-handshake NewSessionTicket / early-data (non-fatal).
#if defined(MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
if (res == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) continue;
#endif
#if defined(MBEDTLS_ERR_SSL_RECEIVED_EARLY_DATA)
if (res == MBEDTLS_ERR_SSL_RECEIVED_EARLY_DATA) continue;
#endif

if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE) {
    errno = EWOULDBLOCK;
#ifdef _WIN32
    WSASetLastError(WSAEWOULDBLOCK);   // getErrno() reads WSAGetLastError() on Windows
#endif
}
return -1;

Maybe also WSASetLastError(WSAEWOULDBLOCK) to send's want-read/write branch

See: Mbed-TLS/mbedtls#9223, Mbed-TLS/mbedtls#8749.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions