Commit 4a7d9705f3 for openssl.org
commit 4a7d9705f30842b402058324a6947938fe3486ec
Author: Alexandr Nedvedicky <sashan@openssl.org>
Date: Mon Jan 5 15:52:47 2026 +0100
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 <esyr@openssl.org>
Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Norbert Pocs <norbertp@openssl.org>
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 75ca85d7ed..9d5cf9e079 100644
--- a/doc/man7/openssl-quic.pod
+++ b/doc/man7/openssl-quic.pod
@@ -899,6 +899,20 @@ that a call to L<SSL_handle_events(3)> 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<WSAECONNRESET> 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<WSAIoctl()> calls fail. Robust application should set those
+options itself so it can handle error notifications from C<WSAIoctl()> properly.
+
=head1 SEE ALSO
L<SSL_handle_events(3)>, L<SSL_get_event_timeout(3)>,
diff --git a/ssl/quic/quic_reactor.c b/ssl/quic/quic_reactor.c
index f54fbcdd96..a754f285bb 100644
--- a/ssl/quic/quic_reactor.c
+++ b/ssl/quic/quic_reactor.c
@@ -11,6 +11,12 @@
#include "internal/thread_arch.h"
#include <assert.h>
+#if defined(OPENSSL_SYS_WINDOWS)
+#include <winsock2.h>
+#include <mstcpip.h>
+#include <mswsock.h>
+#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);
}