From: Alexandr Nedvedicky Date: Mon, 5 Jan 2026 14:52:47 +0000 (+0100) Subject: QUIC listener may fail with SSL_POLL_EVENT_EL on windows. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4a7d9705f30842b402058324a6947938fe3486ec;p=thirdparty%2Fopenssl.git QUIC listener may fail with SSL_POLL_EVENT_EL on windows. recvfrom() may return WSAECONNRESET when the destination port used in a previous sendto() call is no longer available. This causes QUIC PORT to swich from running state. This behavior is not desired for QUIC protocol. The trick is to disable SIO_UDP_CONNRESET flag on UDP port used by QUIC. The issue was kindly reported and root caused by goforit22123-netizen@ Fixes: #29530 Reviewed-by: Eugene Syromiatnikov Reviewed-by: Neil Horman Reviewed-by: Norbert Pocs MergeDate: Mon Jan 12 10:27:25 2026 (Merged from https://github.com/openssl/openssl/pull/29538) --- diff --git a/doc/man7/openssl-quic.pod b/doc/man7/openssl-quic.pod index 75ca85d7ed9..9d5cf9e0796 100644 --- a/doc/man7/openssl-quic.pod +++ b/doc/man7/openssl-quic.pod @@ -899,6 +899,20 @@ that a call to L is performed after the specified timeout =back +=head1 WINDOWS APPLICATION NOTES + +QUIC protocol uses UDP sockets. The recvfrom() function on Windows may fail +with C error causing OpenSSL QUIC stack to enter permanent +error, which prevents further communication over QUIC protocol. Applications +should disable SIO_UDP_CONNRESET and SIO_UDP_NETRESET error notification +on UDP sockets they pass to OpenSSL QUIC stack. More details can be found here: +https://learn.microsoft.com/en-us/windows/win32/winsock/winsock-ioctls#sio_udp_connreset-opcode-setting-i-t3 + +OpenSSL attempts to always disable SIO_UDP_CONNRESET and SIO_UDP_NETRESET +on UDP sockets it receives from application, but no error is reported back +if the respective C calls fail. Robust application should set those +options itself so it can handle error notifications from C properly. + =head1 SEE ALSO L, L, diff --git a/ssl/quic/quic_reactor.c b/ssl/quic/quic_reactor.c index f54fbcdd969..a754f285bbe 100644 --- a/ssl/quic/quic_reactor.c +++ b/ssl/quic/quic_reactor.c @@ -11,6 +11,12 @@ #include "internal/thread_arch.h" #include +#if defined(OPENSSL_SYS_WINDOWS) +#include +#include +#include +#endif + /* * Core I/O Reactor Framework * ========================== @@ -69,6 +75,28 @@ void ossl_quic_reactor_cleanup(QUIC_REACTOR *rtor) } } +#if defined(OPENSSL_SYS_WINDOWS) +/* + * On Windows recvfrom() may return WSAECONNRESET when destination port + * used in preceding call to sendto() is no longer reachable. The reset + * error received from UDP socket takes the whole port down. This behavior + * must be suppressed for QUIC protocol so QUIC applications may rely on + * QUIC protocol itself to detect network failures. + */ +static void rtor_configure_winsock(BIO_POLL_DESCRIPTOR *bpd) +{ + BOOL bNewBehavior = FALSE; + DWORD dwBytesReturned = 0; + + if (bpd->type == BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD) { + WSAIoctl(bpd->value.fd, SIO_UDP_CONNRESET, &bNewBehavior, + sizeof(bNewBehavior), NULL, 0, &dwBytesReturned, NULL, NULL); + WSAIoctl(bpd->value.fd, SIO_UDP_NETRESET, &bNewBehavior, + sizeof(bNewBehavior), NULL, 0, &dwBytesReturned, NULL, NULL); + } +} +#endif + void ossl_quic_reactor_set_poll_r(QUIC_REACTOR *rtor, const BIO_POLL_DESCRIPTOR *r) { if (r == NULL) @@ -76,6 +104,10 @@ void ossl_quic_reactor_set_poll_r(QUIC_REACTOR *rtor, const BIO_POLL_DESCRIPTOR else rtor->poll_r = *r; +#if defined(OPENSSL_SYS_WINDOWS) + rtor_configure_winsock(&rtor->poll_r); +#endif + rtor->can_poll_r = ossl_quic_reactor_can_support_poll_descriptor(rtor, &rtor->poll_r); } @@ -87,6 +119,10 @@ void ossl_quic_reactor_set_poll_w(QUIC_REACTOR *rtor, const BIO_POLL_DESCRIPTOR else rtor->poll_w = *w; +#if defined(OPENSSL_SYS_WINDOWS) + rtor_configure_winsock(&rtor->poll_w); +#endif + rtor->can_poll_w = ossl_quic_reactor_can_support_poll_descriptor(rtor, &rtor->poll_w); }