Commit 1c26ae1e1d for openssl.org
commit 1c26ae1e1dd1159e785c75ea3ed6f1c72557cc56
Author: Matt Caswell <matt@openssl.foundation>
Date: Tue Aug 11 15:41:03 2026 +0100
DTLS 1.3 Support SSL_set_blocking_mode() on a DTLS listener
A DTLS listener cannot take its blocking behaviour from its network BIO
the way an ordinary DTLS object does. It demultiplexes one socket to
many connections, so a read for one of them must not be allowed to
block and stall the others, and the connections it hands out have no BIO
of their own to configure - they read from a queue the listener fills.
Blocking was therefore unavailable to them, SSL_set_blocking_mode()
being QUIC only.
Extend it to a DTLS listener and to the connections created from one,
with the same semantics QUIC has. Blocking is the default, a connection
follows its listener unless given a setting of its own, and asking for
blocking fails where it cannot be delivered, which for a listener means
a network BIO with no poll descriptor to wait on.
SSL_accept_connection() waits only where the caller did not pass
SSL_ACCEPT_CONNECTION_NO_BLOCK and the listener is in blocking mode,
matching what QUIC does with the same flag.
Only the accept path acts on the mode so far; reads and writes follow.
Note that this does not extend to a DTLS object which did not come from
a listener. Such an object has its own BIO and already offers both modes
through it, so unlike QUIC - where the socket is always nonblocking and
nothing works without emulation - there is nothing missing to provide.
While documenting the above, correct the existing description of which
QUIC objects these functions apply to. It said connection objects only,
but they have always been accepted for QUIC stream and listener objects
too.
Assisted-by: Claude Code:claude-opus-5
Reviewed-by: Ryan Hooper <ryanh@openssl.foundation>
Reviewed-by: Jakub Zelenka <jakub.zelenka@openssl.foundation>
Merge-date: Mon Aug 17 08:29:54 2026
Merged-from: https://github.com/openssl/openssl/pull/32324
diff --git a/doc/man3/SSL_new_listener.pod b/doc/man3/SSL_new_listener.pod
index fc56ac70df..6ec9aad8ae 100644
--- a/doc/man3/SSL_new_listener.pod
+++ b/doc/man3/SSL_new_listener.pod
@@ -289,6 +289,15 @@ generate and verify cookies based on the client's address, providing basic
amplification attack protection without requiring application-specific
callback implementations.
+A DTLS listener demultiplexes a single network socket to many connections, so it
+cannot allow one connection's read to block and thereby stall the others. Its
+network BIO is configured for nonblocking operation when it is set, and blocking
+behaviour is provided by waiting for readiness of that socket instead. A DTLS
+listener is blocking by default, and the connections it returns inherit that;
+use L<SSL_set_blocking_mode(3)> to change it. A listener whose BIO cannot provide
+a poll descriptor, such as one using a memory BIO, has nothing to wait on and is
+nonblocking whatever is requested.
+
An example DTLS server using the listener API in single threaded mode which only
accepts one connection at a time. A typical implementation would utilize L<SSL_poll(3)>
and can accept multiple connections concurrently.
diff --git a/doc/man3/SSL_set_bio.pod b/doc/man3/SSL_set_bio.pod
index bf1ca60734..cedf057189 100644
--- a/doc/man3/SSL_set_bio.pod
+++ b/doc/man3/SSL_set_bio.pod
@@ -101,6 +101,11 @@ BIO is subsequently set on the SSL object which can support blocking mode,
blocking mode will not be automatically re-enabled. For more information, see
L<SSL_set_blocking_mode(3)>.
+When B<ssl> is a DTLS listener object, the BIO is configured for nonblocking
+operation, since a listener demultiplexes one socket to many connections and so
+cannot allow a read for one of them to block. Blocking behaviour is provided by
+waiting for readiness of the socket instead; see L<SSL_set_blocking_mode(3)>.
+
When B<ssl> is a DTLS listener object, these functions must not be called
concurrently with any other operations on the listener or its connections.
This includes L<SSL_accept_connection(3)>, L<SSL_poll(3)>,
diff --git a/doc/man3/SSL_set_blocking_mode.pod b/doc/man3/SSL_set_blocking_mode.pod
index 4cf45fea29..3cf490f15b 100644
--- a/doc/man3/SSL_set_blocking_mode.pod
+++ b/doc/man3/SSL_set_blocking_mode.pod
@@ -3,7 +3,7 @@
=head1 NAME
SSL_set_blocking_mode, SSL_get_blocking_mode - configure blocking mode for a
-QUIC SSL object
+QUIC or DTLS listener SSL object
=head1 SYNOPSIS
@@ -14,11 +14,14 @@ QUIC SSL object
=head1 DESCRIPTION
-SSL_set_blocking_mode() can be used to enable or disable blocking mode on a QUIC
-connection SSL object. By default, blocking is enabled, unless the SSL object is
-configured to use an underlying read or write BIO which cannot provide a poll
-descriptor (see L<BIO_get_rpoll_descriptor(3)>), as blocking mode cannot be
-supported in this case.
+SSL_set_blocking_mode() can be used to enable or disable blocking mode on an SSL
+object which has a blocking mode of its own. For QUIC that means a connection,
+stream or listener SSL object. For DTLS it means a listener SSL object created
+with L<SSL_new_listener(3)>, or a connection SSL object returned from one by
+L<SSL_accept_connection(3)>. By default, blocking is enabled, unless the SSL
+object is configured to use an underlying read or write BIO which cannot provide
+a poll descriptor (see L<BIO_get_rpoll_descriptor(3)>), as blocking mode cannot
+be supported in this case.
To enable blocking mode, call SSL_set_blocking_mode() with I<blocking> set to 1;
to disable it, call SSL_set_blocking_mode() with I<blocking> set to 0.
@@ -30,10 +33,13 @@ until the requested operation can be performed. In nonblocking mode, these
calls will fail if the requested operation cannot be performed immediately; see
L<SSL_get_error(3)>.
-These functions are only applicable to QUIC connection SSL objects. Other kinds
-of SSL object, such as those for TLS, automatically function in blocking or
-nonblocking mode based on whether the underlying network read and write BIOs
-provided to the SSL object are themselves configured in nonblocking mode.
+Other kinds of SSL object, such as those for TLS or a DTLS object which did not
+come from a listener, automatically function in blocking or nonblocking mode
+based on whether the underlying network read and write BIOs provided to the SSL
+object are themselves configured in nonblocking mode, and so have no separate
+blocking mode to configure.
+
+=head2 QUIC connections
Where a QUIC connection SSL object is used in nonblocking mode, an application
is responsible for ensuring that the SSL object is ticked regularly; see
@@ -44,24 +50,45 @@ connection SSL object with a network BIO which cannot support blocking mode. To
re-enable blocking mode in this case, an application must set a network BIO
which can support blocking mode and explicitly call SSL_set_blocking_mode().
+=head2 DTLS listeners
+
+A DTLS listener demultiplexes a single network socket to many connections, so it
+cannot allow a read for one connection to block and thereby stall the others.
+Its network BIO is therefore configured for nonblocking operation when it is set,
+and blocking mode is provided by waiting for readiness of that socket instead,
+as it is for QUIC. This applies to L<SSL_accept_connection(3)> on the listener as
+well as to reads and writes on the connections it returns.
+
+A connection SSL object returned by L<SSL_accept_connection(3)> inherits the
+blocking mode of the listener it came from. Calling SSL_set_blocking_mode() on
+such a connection overrides that inheritance for that connection only, and there
+is no way to return it to inheriting afterwards. The blocking mode of a listener
+is normally configured once, before it is used.
+
+A DTLS listener whose network BIO cannot provide a poll descriptor, such as one
+using a memory BIO, has nothing to wait on and is therefore nonblocking
+whatever has been requested.
+
=head1 RETURN VALUES
SSL_set_blocking_mode() returns 1 on success and 0 on failure. The function
-fails if called on an SSL object which does not represent a QUIC connection,
-or if blocking mode cannot be used for the given connection.
+fails if called on an SSL object which has no blocking mode of its own, or if
+blocking mode was requested and cannot be used for the given object.
-SSL_get_blocking_mode() returns 1 if blocking is currently enabled. It returns
--1 if called on an unsupported SSL object.
+SSL_get_blocking_mode() returns 1 if blocking is currently enabled and 0 if it
+is not. It returns -1 if called on an SSL object which has no blocking mode of
+its own.
=head1 SEE ALSO
-L<SSL_handle_events(3)>, L<SSL_poll(3)>, L<openssl-quic(7)>,
-L<openssl-quic-concurrency(7)>, L<ssl(7)>
+L<SSL_handle_events(3)>, L<SSL_poll(3)>, L<SSL_new_listener(3)>,
+L<openssl-quic(7)>, L<openssl-quic-concurrency(7)>, L<ssl(7)>
=head1 HISTORY
The SSL_set_blocking_mode() and SSL_get_blocking_mode() functions were added in
-OpenSSL 3.2.
+OpenSSL 3.2. Support for DTLS listeners and the connections created from them
+was added in OpenSSL 4.1.
=head1 COPYRIGHT
diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c
index 0e8f1741f8..5e6b558391 100644
--- a/ssl/d1_lib.c
+++ b/ssl/d1_lib.c
@@ -336,6 +336,7 @@ int dtls1_clear(SSL *ssl)
DTLS_RX *rx = s->d1->rx;
SSL *listener = s->d1->listener;
OSSL_TIME created_at = s->d1->created_at;
+ unsigned int req_blocking_mode = s->d1->req_blocking_mode;
#endif
mtu = s->d1->mtu;
@@ -361,6 +362,11 @@ int dtls1_clear(SSL *ssl)
#ifndef OPENSSL_NO_DTLS
s->d1->rx = rx;
s->d1->listener = listener;
+ /*
+ * The blocking mode is a property of the connection as the application
+ * configured it, not of the handshake, so it survives a clear.
+ */
+ s->d1->req_blocking_mode = req_blocking_mode;
s->d1->created_at = created_at;
#endif
@@ -2433,6 +2439,15 @@ SSL *ossl_dtls_accept_connection(SSL *ssl, uint64_t flags)
if (conn != NULL)
goto end;
+ /*
+ * Wait only if the caller has not asked us not to and the listener is in
+ * blocking mode. Note that the check for a network BIO below is deliberately
+ * left ahead of this, so that asking to wait on a listener which has none
+ * remains an error rather than silently returning nothing.
+ */
+ if (!no_block && !ossl_dtls_blocking(ssl) && dl->net_rbio != NULL)
+ no_block = 1;
+
if (no_block) {
/*
* Non-blocking: run one tick to drain any pending datagram, then
@@ -2971,6 +2986,125 @@ int ossl_dtls_set_value_uint(SSL *s, uint32_t class_, uint32_t id, uint64_t valu
return ret;
}
+/*
+ * Resolve the requested blocking mode of a DTLS listener, or of a connection
+ * created from one, following the inheritance chain.
+ *
+ * A connection set to INHERIT follows its listener; a listener set to INHERIT
+ * is blocking, there being nothing further to inherit from. Blocking is
+ * therefore the default unless the application asks otherwise.
+ *
+ * Returns 1 if blocking is wanted, which says nothing about whether it can be
+ * provided - see ossl_dtls_can_support_blocking().
+ */
+static int ossl_dtls_desires_blocking(const SSL *s)
+{
+ const SSL_CONNECTION *sc = SSL_CONNECTION_FROM_CONST_SSL_ONLY(s);
+ const DTLS_LISTENER *dl = NULL;
+
+ if (sc != NULL && sc->d1 != NULL) {
+ if (sc->d1->req_blocking_mode != DTLS_BLOCKING_MODE_INHERIT)
+ return sc->d1->req_blocking_mode == DTLS_BLOCKING_MODE_BLOCKING;
+
+ dl = (const DTLS_LISTENER *)sc->d1->listener;
+ } else if (IS_DTLS_LISTENER(s)) {
+ dl = (const DTLS_LISTENER *)s;
+ }
+
+ if (dl == NULL)
+ return 0;
+
+ return dl->req_blocking_mode != DTLS_BLOCKING_MODE_NONBLOCKING;
+}
+
+/*
+ * Report whether blocking mode can be provided for a DTLS listener or a
+ * connection created from one.
+ *
+ * Blocking is emulated by waiting for readiness of the listener's network
+ * socket, so it requires a BIO which can supply a poll descriptor to wait on.
+ * A memory BIO cannot, and such a listener is therefore non-blocking whatever
+ * was requested, as is the case for QUIC.
+ */
+static int ossl_dtls_can_support_blocking(const SSL *s)
+{
+ const SSL_CONNECTION *sc = SSL_CONNECTION_FROM_CONST_SSL_ONLY(s);
+ const SSL *listener = NULL;
+ BIO_POLL_DESCRIPTOR desc;
+ BIO *rbio;
+
+ if (sc != NULL && sc->d1 != NULL)
+ listener = sc->d1->listener;
+ else if (IS_DTLS_LISTENER(s))
+ listener = s;
+
+ if (listener == NULL)
+ return 0;
+
+ rbio = SSL_get_rbio(listener);
+ if (rbio == NULL)
+ return 0;
+
+ return BIO_get_rpoll_descriptor(rbio, &desc) != 0
+ && desc.type == BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD;
+}
+
+/*
+ * Report whether a call on this object should block, which is the case when
+ * blocking is both wanted and possible.
+ */
+int ossl_dtls_blocking(const SSL *s)
+{
+ return ossl_dtls_desires_blocking(s) && ossl_dtls_can_support_blocking(s);
+}
+
+int ossl_dtls_set_blocking_mode(SSL *s, int blocking)
+{
+ SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL_ONLY(s);
+ unsigned int mode = (blocking != 0)
+ ? DTLS_BLOCKING_MODE_BLOCKING
+ : DTLS_BLOCKING_MODE_NONBLOCKING;
+
+ /*
+ * Only a listener, or a connection created from one, has a blocking mode.
+ * Any other DTLS object takes its behaviour from its own BIO in the
+ * traditional way, so there is nothing here to configure.
+ */
+ if (!IS_DTLS_LISTENER(s)
+ && (sc == NULL || sc->d1 == NULL || sc->d1->listener == NULL)) {
+ ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+
+ /*
+ * Refuse to claim blocking we cannot deliver, as QUIC does. Checked before
+ * anything is written, so that a call which fails leaves the mode alone
+ * rather than reporting failure having already changed it.
+ */
+ if (blocking && !ossl_dtls_can_support_blocking(s)) {
+ ERR_raise(ERR_LIB_SSL, ERR_R_UNSUPPORTED);
+ return 0;
+ }
+
+ if (IS_DTLS_LISTENER(s))
+ ((DTLS_LISTENER *)s)->req_blocking_mode = mode;
+ else
+ sc->d1->req_blocking_mode = mode;
+
+ return 1;
+}
+
+int ossl_dtls_get_blocking_mode(const SSL *s)
+{
+ const SSL_CONNECTION *sc = SSL_CONNECTION_FROM_CONST_SSL_ONLY(s);
+
+ if (!IS_DTLS_LISTENER(s)
+ && (sc == NULL || sc->d1 == NULL || sc->d1->listener == NULL))
+ return -1;
+
+ return ossl_dtls_blocking(s);
+}
+
void ossl_dtls_listener_enter_blocking_section(SSL *s)
{
DTLS_LISTENER *dl;
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index fcb626f227..bccfdc3786 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -655,6 +655,7 @@ int ossl_ssl_connection_reset(SSL *s)
DTLS_RX *saved_rx = NULL;
SSL *saved_listener = NULL;
OSSL_TIME saved_created_at = ossl_time_zero();
+ unsigned int saved_req_blocking_mode = DTLS_BLOCKING_MODE_INHERIT;
int is_dtls_listener_conn = 0;
if (SSL_CONNECTION_IS_DTLS(sc) && sc->d1 != NULL
@@ -664,6 +665,7 @@ int ossl_ssl_connection_reset(SSL *s)
saved_rx = sc->d1->rx;
saved_listener = sc->d1->listener;
saved_created_at = sc->d1->created_at;
+ saved_req_blocking_mode = sc->d1->req_blocking_mode;
/*
* Prevent dtls1_free from freeing rx and releasing the listener
* reference - we'll restore them after ssl_init.
@@ -692,6 +694,11 @@ int ossl_ssl_connection_reset(SSL *s)
sc->d1->rx = saved_rx;
sc->d1->listener = saved_listener;
sc->d1->created_at = saved_created_at;
+ /*
+ * The blocking mode is how the application configured this
+ * connection, not handshake state, so it survives a clear.
+ */
+ sc->d1->req_blocking_mode = saved_req_blocking_mode;
}
#endif
} else {
@@ -8035,25 +8042,31 @@ int SSL_net_write_desired(SSL *s)
int SSL_set_blocking_mode(SSL *s, int blocking)
{
#ifndef OPENSSL_NO_QUIC
- if (!IS_QUIC(s))
- return 0;
+ if (IS_QUIC(s))
+ return ossl_quic_conn_set_blocking_mode(s, blocking);
+#endif
- return ossl_quic_conn_set_blocking_mode(s, blocking);
-#else
- return 0;
+#if !defined(OPENSSL_NO_DTLS) && !defined(OPENSSL_NO_SOCK)
+ if (IS_DTLS(s))
+ return ossl_dtls_set_blocking_mode(s, blocking);
#endif
+
+ return 0;
}
int SSL_get_blocking_mode(SSL *s)
{
#ifndef OPENSSL_NO_QUIC
- if (!IS_QUIC(s))
- return -1;
+ if (IS_QUIC(s))
+ return ossl_quic_conn_get_blocking_mode(s);
+#endif
- return ossl_quic_conn_get_blocking_mode(s);
-#else
- return -1;
+#if !defined(OPENSSL_NO_DTLS) && !defined(OPENSSL_NO_SOCK)
+ if (IS_DTLS(s))
+ return ossl_dtls_get_blocking_mode(s);
#endif
+
+ return -1;
}
int SSL_set1_initial_peer_addr(SSL *s, const BIO_ADDR *peer_addr)
diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h
index 3020f44ec5..da04e6ddad 100644
--- a/ssl/ssl_local.h
+++ b/ssl/ssl_local.h
@@ -2255,11 +2255,30 @@ typedef struct dtls1_state_st {
* the listener mutex.
*/
unsigned int being_driven : 1;
+
+ /*
+ * Blocking mode requested for this connection, as a DTLS_BLOCKING_MODE.
+ * Defaults to inheriting from the listener it came from.
+ */
+ unsigned int req_blocking_mode : 2;
#endif
} DTLS1_STATE;
#if !defined(OPENSSL_NO_DTLS) && !defined(OPENSSL_NO_SOCK)
+/*
+ * Blocking mode of a DTLS listener or of a connection created from one.
+ *
+ * A connection set to INHERIT follows its listener, and a listener set to
+ * INHERIT means blocking, so blocking is the default throughout unless an
+ * application asks otherwise. This mirrors QUIC_BLOCKING_MODE.
+ */
+enum {
+ DTLS_BLOCKING_MODE_INHERIT,
+ DTLS_BLOCKING_MODE_NONBLOCKING,
+ DTLS_BLOCKING_MODE_BLOCKING
+};
+
/*
* Define stack of SSL for DTLS listener incoming connections.
*/
@@ -2389,6 +2408,13 @@ typedef struct dtls_listener_st {
* Count of threads currently blocked waiting in poll().
*/
size_t cur_blocking_waiters;
+
+ /*
+ * Blocking mode requested for this listener, as a DTLS_BLOCKING_MODE.
+ * INHERIT here means blocking, there being nothing further to inherit
+ * from.
+ */
+ unsigned int req_blocking_mode : 2;
} DTLS_LISTENER;
#endif /* !OPENSSL_NO_DTLS && !OPENSSL_NO_SOCK */
@@ -3065,6 +3091,9 @@ int ossl_dtls_conn_poll_events(SSL *s, uint64_t events, int do_tick,
void ossl_dtls_listener_enter_blocking_section(SSL *s);
void ossl_dtls_listener_leave_blocking_section(SSL *s);
int ossl_dtls_block_until_ready(SSL *ssl, uint64_t events, OSSL_TIME deadline);
+int ossl_dtls_blocking(const SSL *s);
+int ossl_dtls_set_blocking_mode(SSL *s, int blocking);
+int ossl_dtls_get_blocking_mode(const SSL *s);
int ossl_dtls_tick(DTLS_LISTENER *dl);
/* DTLS Listener internal cookie callbacks */
diff --git a/test/dtlsssllistenertest.c b/test/dtlsssllistenertest.c
index 52ee3e8b2b..edbf8dbe71 100644
--- a/test/dtlsssllistenertest.c
+++ b/test/dtlsssllistenertest.c
@@ -5347,6 +5347,316 @@ end:
return testresult;
}
+/*
+ * Drive a client's ClientHello, and any cookie exchange, at the listener until
+ * a connection is sitting on its accept queue, without accepting it.
+ *
+ * Returns 1 on success, 0 on failure.
+ */
+static int drive_until_connection_queued(SSL *listener, SSL *clientssl)
+{
+ SSL_POLL_ITEM poll_item;
+ struct timeval poll_timeout;
+ size_t poll_result = 0;
+ int abortctr, retc, err_code;
+
+ poll_item.desc.type = BIO_POLL_DESCRIPTOR_TYPE_SSL;
+ poll_item.desc.value.ssl = listener;
+ poll_timeout.tv_sec = 0;
+ poll_timeout.tv_usec = 0;
+
+ SSL_set_connect_state(clientssl);
+
+ for (abortctr = 0; abortctr < 100; abortctr++) {
+ retc = SSL_connect(clientssl);
+ err_code = SSL_get_error(clientssl, retc);
+ if (retc <= 0
+ && err_code != SSL_ERROR_WANT_READ
+ && err_code != SSL_ERROR_WANT_WRITE) {
+ TEST_error("SSL_connect failed (err %d)", err_code);
+ return 0;
+ }
+
+ /* A zero timeout, so this ticks the listener without ever waiting. */
+ poll_item.events = SSL_POLL_EVENT_IC;
+ poll_item.revents = 0;
+
+ if (!TEST_true(SSL_poll(&poll_item, 1, sizeof(poll_item), &poll_timeout,
+ 0, &poll_result)))
+ return 0;
+
+ if ((poll_item.revents & SSL_POLL_EVENT_IC) != 0)
+ return 1;
+ }
+
+ TEST_error("cookie exchange loop did not converge");
+ return 0;
+}
+
+/*
+ * Test the blocking mode of a DTLS listener and of the connections it creates.
+ *
+ * Blocking is the default, as it is for QUIC: a listener which was never
+ * configured is blocking, and a connection follows its listener unless it was
+ * given a setting of its own.
+ *
+ * Blocking is emulated by waiting for readiness of the listener's socket, so it
+ * needs a BIO which can supply a poll descriptor to wait on. Where there is
+ * none the object is non-blocking whatever was asked for, and asking for
+ * blocking fails rather than claiming something which cannot be delivered.
+ */
+static int test_dtls_blocking_mode(void)
+{
+ SSL_CTX *sctx = NULL, *cctx = NULL;
+ SSL *listener = NULL, *clientssl = NULL, *serverssl = NULL;
+ SSL *memlistener = NULL, *memclient = NULL, *plainssl = NULL;
+ BIO_ADDR *server_addr = NULL, *client_addr = NULL;
+ int server_fd = -1, client_fd = -1;
+ int testresult = 0;
+
+ if (!TEST_true(create_ssl_ctx_pair(NULL, DTLS_server_method(),
+ DTLS_client_method(), DTLS1_VERSION, 0, &sctx, &cctx, cert,
+ privkey)))
+ goto end;
+
+ /* A socket BIO supplies a poll descriptor, so blocking is available. */
+ if (!TEST_true(create_dtls_listener(sctx,
+ SSL_LISTENER_FLAG_REQUIRE_HVR | SSL_LISTENER_FLAG_REQUIRE_HRR
+ | SSL_LISTENER_FLAG_SINGLE_THREAD,
+ &listener, &server_addr, &server_fd)))
+ goto end;
+
+ /* Blocking by default, having never been configured. */
+ if (!TEST_int_eq(SSL_get_blocking_mode(listener), 1))
+ goto end;
+
+ /* Get a connection object accepted from the listener to examine. */
+ if (!TEST_true(create_dtls_client_for_addr(cctx, server_addr, &clientssl,
+ &client_fd)))
+ goto end;
+
+ if (!drive_until_connection_queued(listener, clientssl)
+ || !TEST_ptr(serverssl = SSL_accept_connection(listener,
+ SSL_ACCEPT_CONNECTION_NO_BLOCK)))
+ goto end;
+
+ /* It inherits the listener's mode. */
+
+ if (!TEST_int_eq(SSL_get_blocking_mode(serverssl), 1))
+ goto end;
+
+ /* Setting the listener non-blocking is inherited by the connection. */
+ if (!TEST_true(SSL_set_blocking_mode(listener, 0))
+ || !TEST_int_eq(SSL_get_blocking_mode(listener), 0)
+ || !TEST_int_eq(SSL_get_blocking_mode(serverssl), 0))
+ goto end;
+
+ /* A setting on the connection overrides what it would inherit. */
+ if (!TEST_true(SSL_set_blocking_mode(serverssl, 1))
+ || !TEST_int_eq(SSL_get_blocking_mode(serverssl), 1)
+ || !TEST_int_eq(SSL_get_blocking_mode(listener), 0))
+ goto end;
+
+ /*
+ * A connection's own setting survives SSL_clear(). The mode is a property
+ * of the connection as the application configured it, not of the handshake,
+ * and dtls1_clear() memsets d1 and restores only selected fields.
+ *
+ * The listener and the connection must disagree for this to prove anything:
+ * were the connection's setting lost it would fall back to inheriting, and
+ * that is only visible if the listener says something different.
+ */
+ if (!TEST_true(SSL_set_blocking_mode(listener, 1))
+ || !TEST_true(SSL_set_blocking_mode(serverssl, 0))
+ || !TEST_int_eq(SSL_get_blocking_mode(listener), 1)
+ || !TEST_int_eq(SSL_get_blocking_mode(serverssl), 0))
+ goto end;
+
+ if (!TEST_true(SSL_clear(serverssl))
+ || !TEST_int_eq(SSL_get_blocking_mode(serverssl), 0))
+ goto end;
+
+ /*
+ * Clear again. SSL_clear() resets the method to the default one, so the
+ * first call above went through the ssl_deinit/ssl_init path that
+ * reallocates d1 while this one goes through dtls1_clear(). Both discard
+ * d1, so both have to be covered.
+ */
+ if (!TEST_true(SSL_clear(serverssl))
+ || !TEST_int_eq(SSL_get_blocking_mode(serverssl), 0))
+ goto end;
+
+ /* And back the other way round. */
+ if (!TEST_true(SSL_set_blocking_mode(listener, 1))
+ || !TEST_true(SSL_set_blocking_mode(serverssl, 0))
+ || !TEST_int_eq(SSL_get_blocking_mode(listener), 1)
+ || !TEST_int_eq(SSL_get_blocking_mode(serverssl), 0))
+ goto end;
+
+ /*
+ * A listener on BIOs which cannot supply a poll descriptor reports
+ * non-blocking however it was configured, and asking for blocking fails.
+ */
+ if (!TEST_true(create_dtls_listener_and_client_mem(sctx, cctx,
+ SSL_LISTENER_FLAG_SINGLE_THREAD, &memlistener, &memclient,
+ &client_addr)))
+ goto end;
+
+ if (!TEST_int_eq(SSL_get_blocking_mode(memlistener), 0))
+ goto end;
+
+ ERR_clear_error();
+ if (!TEST_false(SSL_set_blocking_mode(memlistener, 1))
+ || !TEST_int_eq((int)ERR_GET_REASON(ERR_peek_error()), ERR_R_UNSUPPORTED))
+ goto end;
+ ERR_clear_error();
+
+ /* Asking for non-blocking is fine, that being what it already is. */
+ if (!TEST_true(SSL_set_blocking_mode(memlistener, 0))
+ || !TEST_int_eq(SSL_get_blocking_mode(memlistener), 0))
+ goto end;
+
+ /*
+ * A DTLS object which did not come from a listener has no blocking mode:
+ * it takes its behaviour from its own BIO in the traditional way.
+ */
+ if (!TEST_ptr(plainssl = SSL_new(cctx)))
+ goto end;
+
+ if (!TEST_int_eq(SSL_get_blocking_mode(plainssl), -1)
+ || !TEST_false(SSL_set_blocking_mode(plainssl, 1))
+ || !TEST_false(SSL_set_blocking_mode(plainssl, 0)))
+ goto end;
+
+ testresult = 1;
+end:
+ ERR_clear_error();
+ SSL_free(plainssl);
+ SSL_free(memclient);
+ SSL_free(memlistener);
+ SSL_free(serverssl);
+ SSL_free(clientssl);
+ SSL_free(listener);
+ BIO_ADDR_free(client_addr);
+ BIO_ADDR_free(server_addr);
+ if (server_fd >= 0)
+ BIO_closesocket(server_fd);
+ if (client_fd >= 0)
+ BIO_closesocket(client_fd);
+ SSL_CTX_free(sctx);
+ SSL_CTX_free(cctx);
+ return testresult;
+}
+
+/*
+ * Test when SSL_accept_connection() waits and when it does not.
+ *
+ * It waits only if the caller did not pass SSL_ACCEPT_CONNECTION_NO_BLOCK and
+ * the listener is in blocking mode, which is the same rule QUIC applies. So
+ * either of the two saying not to wait is enough, and this checks both of those
+ * cases: no client exists, so anything which did wait would never return.
+ */
+static int test_dtls_accept_wait_requires_mode_and_flag(void)
+{
+ SSL_CTX *sctx = NULL;
+ SSL *listener = NULL;
+ BIO_ADDR *server_addr = NULL;
+ int server_fd = -1;
+ int testresult = 0;
+
+ if (!TEST_ptr(sctx = SSL_CTX_new(DTLS_server_method())))
+ goto end;
+
+ if (!TEST_true(create_dtls_listener(sctx, SSL_LISTENER_FLAG_SINGLE_THREAD,
+ &listener, &server_addr, &server_fd)))
+ goto end;
+
+ /* Non-blocking mode, and no flag: the mode alone stops it waiting. */
+ if (!TEST_true(SSL_set_blocking_mode(listener, 0))
+ || !TEST_ptr_null(SSL_accept_connection(listener, 0)))
+ goto end;
+
+ /* Blocking mode, but the flag overrides it. */
+ if (!TEST_true(SSL_set_blocking_mode(listener, 1))
+ || !TEST_int_eq(SSL_get_blocking_mode(listener), 1)
+ || !TEST_ptr_null(SSL_accept_connection(listener,
+ SSL_ACCEPT_CONNECTION_NO_BLOCK)))
+ goto end;
+
+ testresult = 1;
+end:
+ SSL_free(listener);
+ BIO_ADDR_free(server_addr);
+ if (server_fd >= 0)
+ BIO_closesocket(server_fd);
+ SSL_CTX_free(sctx);
+ return testresult;
+}
+
+/*
+ * Test that a rejected SSL_set_blocking_mode() leaves the mode alone.
+ *
+ * Whether blocking can be supported depends on the listener's BIO, so a
+ * request can be refused now and be perfectly deliverable later. The refusal
+ * must therefore not record the mode it refused to set: the effect only becomes
+ * visible once a BIO which can supply a poll descriptor is in place, at which
+ * point the listener would be found blocking on the strength of a call which
+ * failed.
+ */
+static int test_dtls_blocking_mode_failed_set_is_inert(void)
+{
+ SSL_CTX *sctx = NULL;
+ SSL *listener = NULL;
+ BIO_ADDR *server_addr = NULL;
+ BIO *rbio = NULL;
+ int server_fd = -1;
+ int testresult = 0;
+
+ if (!TEST_ptr(sctx = SSL_CTX_new(DTLS_server_method())))
+ goto end;
+
+ if (!TEST_true(create_dtls_listener(sctx, SSL_LISTENER_FLAG_SINGLE_THREAD,
+ &listener, &server_addr, &server_fd)))
+ goto end;
+
+ /* Ask for non-blocking explicitly, so the default cannot mask a change. */
+ if (!TEST_true(SSL_set_blocking_mode(listener, 0))
+ || !TEST_int_eq(SSL_get_blocking_mode(listener), 0))
+ goto end;
+
+ /* Keep the BIO, then take it away so blocking cannot be supported. */
+ if (!TEST_ptr(rbio = SSL_get_rbio(listener))
+ || !TEST_true(BIO_up_ref(rbio)))
+ goto end;
+
+ SSL_set0_rbio(listener, NULL);
+
+ ERR_clear_error();
+ if (!TEST_false(SSL_set_blocking_mode(listener, 1))
+ || !TEST_int_eq((int)ERR_GET_REASON(ERR_peek_error()),
+ ERR_R_UNSUPPORTED)) {
+ BIO_free(rbio);
+ goto end;
+ }
+ ERR_clear_error();
+
+ /* Give the BIO back, which makes blocking supportable once more. */
+ SSL_set0_rbio(listener, rbio);
+
+ /* The refused request must not have taken effect. */
+ if (!TEST_int_eq(SSL_get_blocking_mode(listener), 0))
+ goto end;
+
+ testresult = 1;
+end:
+ SSL_free(listener);
+ BIO_ADDR_free(server_addr);
+ if (server_fd >= 0)
+ BIO_closesocket(server_fd);
+ SSL_CTX_free(sctx);
+ return testresult;
+}
+
OPT_TEST_DECLARE_USAGE("certfile privkeyfile\n")
int setup_tests(void)
@@ -5428,6 +5738,11 @@ int setup_tests(void)
ADD_TEST(test_dtls_listener_pending_timeout_basic);
ADD_TEST(test_dtls_listener_pending_timeout_invalid);
+ /* Blocking mode tests */
+ ADD_TEST(test_dtls_blocking_mode);
+ ADD_TEST(test_dtls_blocking_mode_failed_set_is_inert);
+ ADD_TEST(test_dtls_accept_wait_requires_mode_and_flag);
+
/* SSL object ownership tests (run with ASAN to detect leaks/double-frees) */
ADD_TEST(test_ssl_ownership_pending_conn_leak);
ADD_TEST(test_ssl_ownership_incoming_conn_leak);