]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
QUIC listener may fail with SSL_POLL_EVENT_EL on windows.
authorAlexandr Nedvedicky <sashan@openssl.org>
Mon, 5 Jan 2026 14:52:47 +0000 (15:52 +0100)
committerAlexandr Nedvedicky <sashan@openssl.org>
Mon, 12 Jan 2026 10:27:22 +0000 (11:27 +0100)
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)

doc/man7/openssl-quic.pod
ssl/quic/quic_reactor.c

index 75ca85d7ed9b87adc7336d110f58aae4ac5fcd0a..9d5cf9e079672d023a711e3e441e8de8d1a0f7d1 100644 (file)
@@ -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)>,
index f54fbcdd96969a566c491b0cc9c95a8b58627faa..a754f285bbe2bebf0b9baf21d90759a2616b1e00 100644 (file)
 #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);
 }