Commit 234845aaab for openssl.org

commit 234845aaabbcfef5f58a00835f0383ceabe469b5
Author: Jakub Zelenka <jakub.zelenka@openssl.foundation>
Date:   Fri Jun 12 13:45:25 2026 +0200

    Add BIO bio_sock2 test

    Assisted-by: Claude:claude-opus-4-8

    Reviewed-by: Richard Levitte <levitte@openssl.org>
    Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
    MergeDate: Thu Jul  9 17:39:25 2026
    (Merged from https://github.com/openssl/openssl/pull/30788)

diff --git a/test/unit/build.info b/test/unit/build.info
index f215b70ec0..bc0465dc13 100644
--- a/test/unit/build.info
+++ b/test/unit/build.info
@@ -15,6 +15,13 @@ WRAP[crypto/bio/test_bio_sock]=getsockopt setsockopt getsockname ioctl poll \
     BIO_ADDRINFO_free BIO_accept_ex BIO_sock_should_retry \
     BIO_ADDR_hostname_string BIO_ADDR_service_string

+PROGRAMS{noinst}=crypto/bio/test_bio_sock2
+SOURCE[crypto/bio/test_bio_sock2]=crypto/bio/test_bio_sock2.c
+INCLUDE[crypto/bio/test_bio_sock2]=../../include ../../crypto/bio
+DEPEND[crypto/bio/test_bio_sock2]=../../libcrypto.a
+WRAP[crypto/bio/test_bio_sock2]=socket connect bind listen accept close \
+    getsockopt setsockopt BIO_socket_nbio BIO_sock_should_retry
+
 PROGRAMS{noinst}=crypto/bio/test_bss_acpt
 SOURCE[crypto/bio/test_bss_acpt]=crypto/bio/test_bss_acpt.c
 WRAP[crypto/bio/test_bss_acpt]=BIO_lookup BIO_socket BIO_listen \
diff --git a/test/unit/crypto/bio/test_bio_sock2.c b/test/unit/crypto/bio/test_bio_sock2.c
new file mode 100644
index 0000000000..96f63e59cf
--- /dev/null
+++ b/test/unit/crypto/bio/test_bio_sock2.c
@@ -0,0 +1,786 @@
+/*
+ * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/sockets.h"
+
+#ifndef OPENSSL_NO_SOCK
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "bio_local.h"
+#include "internal/bio_tfo.h"
+#include <openssl/bio.h>
+
+#define FAKE_SOCKET 42
+#define ACCEPTED_SOCKET 7
+
+/* prototypes for __wrap_* (required by -Wmissing-prototypes) */
+int __wrap_socket(int domain, int type, int protocol);
+int __wrap_connect(int fd, const struct sockaddr *addr, socklen_t addrlen);
+int __wrap_bind(int fd, const struct sockaddr *addr, socklen_t addrlen);
+int __wrap_listen(int fd, int backlog);
+int __wrap_accept(int fd, struct sockaddr *addr, socklen_t *addrlen);
+int __wrap_close(int fd);
+int __wrap_getsockopt(int fd, int level, int optname, void *optval,
+    socklen_t *optlen);
+int __wrap_setsockopt(int fd, int level, int optname, const void *optval,
+    socklen_t optlen);
+int __wrap_BIO_socket_nbio(int fd, int mode);
+int __wrap_BIO_sock_should_retry(int i);
+
+/* wraps */
+
+int __wrap_socket(int domain, int type, int protocol)
+{
+    function_called();
+    check_expected(domain);
+    check_expected(type);
+    check_expected(protocol);
+    return mock_type(int);
+}
+
+int __wrap_connect(int fd, const struct sockaddr *addr, socklen_t addrlen)
+{
+    function_called();
+    check_expected(fd);
+    (void)addr;
+    (void)addrlen;
+    return mock_type(int);
+}
+
+int __wrap_bind(int fd, const struct sockaddr *addr, socklen_t addrlen)
+{
+    function_called();
+    check_expected(fd);
+    (void)addr;
+    (void)addrlen;
+    return mock_type(int);
+}
+
+int __wrap_listen(int fd, int backlog)
+{
+    function_called();
+    check_expected(fd);
+    (void)backlog;
+    return mock_type(int);
+}
+
+int __wrap_accept(int fd, struct sockaddr *addr, socklen_t *addrlen)
+{
+    function_called();
+    check_expected(fd);
+    (void)addr;
+    (void)addrlen;
+    return mock_type(int);
+}
+
+int __wrap_close(int fd)
+{
+    function_called();
+    check_expected(fd);
+    return mock_type(int);
+}
+
+int __wrap_getsockopt(int fd, int level, int optname, void *optval,
+    socklen_t *optlen)
+{
+    int rc;
+
+    function_called();
+    check_expected(fd);
+    check_expected(level);
+    check_expected(optname);
+    rc = mock_type(int);
+    if (rc == 0) {
+        if (optval != NULL)
+            *(int *)optval = mock_type(int);
+        if (optlen != NULL)
+            *optlen = (socklen_t)mock_type(int);
+    }
+    return rc;
+}
+
+int __wrap_setsockopt(int fd, int level, int optname, const void *optval,
+    socklen_t optlen)
+{
+    int on = (optval != NULL) ? *(const int *)optval : 0;
+
+    function_called();
+    check_expected(fd);
+    check_expected(level);
+    check_expected(optname);
+    check_expected(on);
+    (void)optlen;
+    return mock_type(int);
+}
+
+int __wrap_BIO_socket_nbio(int fd, int mode)
+{
+    function_called();
+    check_expected(fd);
+    check_expected(mode);
+    return mock_type(int);
+}
+
+int __wrap_BIO_sock_should_retry(int i)
+{
+    function_called();
+    check_expected(i);
+    return mock_type(int);
+}
+
+/* expectations */
+
+static void expect_socket(int domain, int type, int protocol, int rc)
+{
+    expect_function_call(__wrap_socket);
+    expect_value(__wrap_socket, domain, domain);
+    expect_value(__wrap_socket, type, type);
+    expect_value(__wrap_socket, protocol, protocol);
+    will_return(__wrap_socket, rc);
+}
+
+static void expect_connect(int fd, int rc)
+{
+    expect_function_call(__wrap_connect);
+    expect_value(__wrap_connect, fd, fd);
+    will_return(__wrap_connect, rc);
+}
+
+static void expect_bind(int fd, int rc)
+{
+    expect_function_call(__wrap_bind);
+    expect_value(__wrap_bind, fd, fd);
+    will_return(__wrap_bind, rc);
+}
+
+static void expect_listen(int fd, int rc)
+{
+    expect_function_call(__wrap_listen);
+    expect_value(__wrap_listen, fd, fd);
+    will_return(__wrap_listen, rc);
+}
+
+static void expect_accept(int fd, int rc)
+{
+    expect_function_call(__wrap_accept);
+    expect_value(__wrap_accept, fd, fd);
+    will_return(__wrap_accept, rc);
+}
+
+static void expect_close(int fd, int rc)
+{
+    expect_function_call(__wrap_close);
+    expect_value(__wrap_close, fd, fd);
+    will_return(__wrap_close, rc);
+}
+
+static void expect_getsockopt(int fd, int level, int optname, int rc,
+    int value, socklen_t outlen)
+{
+    expect_function_call(__wrap_getsockopt);
+    expect_value(__wrap_getsockopt, fd, fd);
+    expect_value(__wrap_getsockopt, level, level);
+    expect_value(__wrap_getsockopt, optname, optname);
+    will_return(__wrap_getsockopt, rc);
+    if (rc == 0) {
+        will_return(__wrap_getsockopt, value);
+        will_return(__wrap_getsockopt, (int)outlen);
+    }
+}
+
+static void expect_setsockopt(int fd, int level, int optname, int on, int rc)
+{
+    expect_function_call(__wrap_setsockopt);
+    expect_value(__wrap_setsockopt, fd, fd);
+    expect_value(__wrap_setsockopt, level, level);
+    expect_value(__wrap_setsockopt, optname, optname);
+    expect_value(__wrap_setsockopt, on, on);
+    will_return(__wrap_setsockopt, rc);
+}
+
+static void expect_nbio(int fd, int mode, int rc)
+{
+    expect_function_call(__wrap_BIO_socket_nbio);
+    expect_value(__wrap_BIO_socket_nbio, fd, fd);
+    expect_value(__wrap_BIO_socket_nbio, mode, mode);
+    will_return(__wrap_BIO_socket_nbio, rc);
+}
+
+static void expect_should_retry(int i, int rc)
+{
+    expect_function_call(__wrap_BIO_sock_should_retry);
+    expect_value(__wrap_BIO_sock_should_retry, i, i);
+    will_return(__wrap_BIO_sock_should_retry, rc);
+}
+
+/* helpers */
+
+static void make_addr(BIO_ADDR *a, int family)
+{
+    memset(a, 0, sizeof(*a));
+    a->sa.sa_family = family;
+}
+
+/* BIO_socket */
+
+static void test_socket_success(void **state)
+{
+    (void)state;
+    expect_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, FAKE_SOCKET);
+    assert_int_equal(
+        BIO_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0), FAKE_SOCKET);
+}
+
+static void test_socket_fails(void **state)
+{
+    (void)state;
+    expect_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, -1);
+    assert_int_equal(
+        BIO_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0), (int)INVALID_SOCKET);
+}
+
+/* BIO_connect */
+
+static void test_connect_invalid_sock(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    assert_int_equal(BIO_connect(-1, &a, 0), 0);
+}
+
+static void test_connect_nbio_fails(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_nbio(FAKE_SOCKET, 0, 0);
+    assert_int_equal(BIO_connect(FAKE_SOCKET, &a, 0), 0);
+}
+
+static void test_connect_success(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_nbio(FAKE_SOCKET, 0, 1);
+    expect_connect(FAKE_SOCKET, 0);
+    assert_int_equal(BIO_connect(FAKE_SOCKET, &a, 0), 1);
+}
+
+static void test_connect_nonblock(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_nbio(FAKE_SOCKET, 1, 1);
+    expect_connect(FAKE_SOCKET, 0);
+    assert_int_equal(BIO_connect(FAKE_SOCKET, &a, BIO_SOCK_NONBLOCK), 1);
+}
+
+static void test_connect_keepalive(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_nbio(FAKE_SOCKET, 0, 1);
+    expect_setsockopt(FAKE_SOCKET, SOL_SOCKET, SO_KEEPALIVE, 1, 0);
+    expect_connect(FAKE_SOCKET, 0);
+    assert_int_equal(BIO_connect(FAKE_SOCKET, &a, BIO_SOCK_KEEPALIVE), 1);
+}
+
+static void test_connect_keepalive_fails(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_nbio(FAKE_SOCKET, 0, 1);
+    expect_setsockopt(FAKE_SOCKET, SOL_SOCKET, SO_KEEPALIVE, 1, -1);
+    assert_int_equal(BIO_connect(FAKE_SOCKET, &a, BIO_SOCK_KEEPALIVE), 0);
+}
+
+static void test_connect_nodelay(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_nbio(FAKE_SOCKET, 0, 1);
+    expect_setsockopt(FAKE_SOCKET, IPPROTO_TCP, TCP_NODELAY, 1, 0);
+    expect_connect(FAKE_SOCKET, 0);
+    assert_int_equal(BIO_connect(FAKE_SOCKET, &a, BIO_SOCK_NODELAY), 1);
+}
+
+static void test_connect_nodelay_fails(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_nbio(FAKE_SOCKET, 0, 1);
+    expect_setsockopt(FAKE_SOCKET, IPPROTO_TCP, TCP_NODELAY, 1, -1);
+    assert_int_equal(BIO_connect(FAKE_SOCKET, &a, BIO_SOCK_NODELAY), 0);
+}
+
+static void test_connect_fails_retry(void **state)
+{
+    /* connect() failing retryably returns 0 without raising a fatal error */
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_nbio(FAKE_SOCKET, 0, 1);
+    expect_connect(FAKE_SOCKET, -1);
+    expect_should_retry(-1, 1);
+    assert_int_equal(BIO_connect(FAKE_SOCKET, &a, 0), 0);
+}
+
+static void test_connect_fails_error(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_nbio(FAKE_SOCKET, 0, 1);
+    expect_connect(FAKE_SOCKET, -1);
+    expect_should_retry(-1, 0);
+    assert_int_equal(BIO_connect(FAKE_SOCKET, &a, 0), 0);
+}
+
+/* BIO_bind */
+
+static void test_bind_invalid_sock(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    assert_int_equal(BIO_bind(-1, &a, 0), 0);
+}
+
+static void test_bind_success(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_bind(FAKE_SOCKET, 0);
+    assert_int_equal(BIO_bind(FAKE_SOCKET, &a, 0), 1);
+}
+
+static void test_bind_reuseaddr(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_setsockopt(FAKE_SOCKET, SOL_SOCKET, SO_REUSEADDR, 1, 0);
+    expect_bind(FAKE_SOCKET, 0);
+    assert_int_equal(BIO_bind(FAKE_SOCKET, &a, BIO_SOCK_REUSEADDR), 1);
+}
+
+static void test_bind_reuseaddr_fails(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_setsockopt(FAKE_SOCKET, SOL_SOCKET, SO_REUSEADDR, 1, -1);
+    assert_int_equal(BIO_bind(FAKE_SOCKET, &a, BIO_SOCK_REUSEADDR), 0);
+}
+
+static void test_bind_bind_fails(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_bind(FAKE_SOCKET, -1);
+    assert_int_equal(BIO_bind(FAKE_SOCKET, &a, 0), 0);
+}
+
+/* BIO_listen */
+
+static void test_listen_success(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM,
+        sizeof(int));
+    expect_nbio(FAKE_SOCKET, 0, 1);
+    expect_bind(FAKE_SOCKET, 0);
+    expect_listen(FAKE_SOCKET, 0);
+    assert_int_equal(BIO_listen(FAKE_SOCKET, &a, 0), 1);
+}
+
+static void test_listen_invalid_sock(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    assert_int_equal(BIO_listen(-1, &a, 0), 0);
+}
+
+static void test_listen_getsockopt_fails(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, -1, 0, 0);
+    assert_int_equal(BIO_listen(FAKE_SOCKET, &a, 0), 0);
+}
+
+static void test_listen_socktype_len_mismatch(void **state)
+{
+    /* a short socktype_len is rejected even when getsockopt succeeds */
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM,
+        (socklen_t)(sizeof(int) - 1));
+    assert_int_equal(BIO_listen(FAKE_SOCKET, &a, 0), 0);
+}
+
+static void test_listen_nbio_fails(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM,
+        sizeof(int));
+    expect_nbio(FAKE_SOCKET, 0, 0);
+    assert_int_equal(BIO_listen(FAKE_SOCKET, &a, 0), 0);
+}
+
+static void test_listen_keepalive(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM,
+        sizeof(int));
+    expect_nbio(FAKE_SOCKET, 0, 1);
+    expect_setsockopt(FAKE_SOCKET, SOL_SOCKET, SO_KEEPALIVE, 1, 0);
+    expect_bind(FAKE_SOCKET, 0);
+    expect_listen(FAKE_SOCKET, 0);
+    assert_int_equal(BIO_listen(FAKE_SOCKET, &a, BIO_SOCK_KEEPALIVE), 1);
+}
+
+static void test_listen_keepalive_fails(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM,
+        sizeof(int));
+    expect_nbio(FAKE_SOCKET, 0, 1);
+    expect_setsockopt(FAKE_SOCKET, SOL_SOCKET, SO_KEEPALIVE, 1, -1);
+    assert_int_equal(BIO_listen(FAKE_SOCKET, &a, BIO_SOCK_KEEPALIVE), 0);
+}
+
+static void test_listen_nodelay(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM,
+        sizeof(int));
+    expect_nbio(FAKE_SOCKET, 0, 1);
+    expect_setsockopt(FAKE_SOCKET, IPPROTO_TCP, TCP_NODELAY, 1, 0);
+    expect_bind(FAKE_SOCKET, 0);
+    expect_listen(FAKE_SOCKET, 0);
+    assert_int_equal(BIO_listen(FAKE_SOCKET, &a, BIO_SOCK_NODELAY), 1);
+}
+
+static void test_listen_reuseaddr(void **state)
+{
+    /* REUSEADDR is honoured inside the real (unwrappable) BIO_bind */
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM,
+        sizeof(int));
+    expect_nbio(FAKE_SOCKET, 0, 1);
+    expect_setsockopt(FAKE_SOCKET, SOL_SOCKET, SO_REUSEADDR, 1, 0);
+    expect_bind(FAKE_SOCKET, 0);
+    expect_listen(FAKE_SOCKET, 0);
+    assert_int_equal(BIO_listen(FAKE_SOCKET, &a, BIO_SOCK_REUSEADDR), 1);
+}
+
+static void test_listen_bind_fails(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM,
+        sizeof(int));
+    expect_nbio(FAKE_SOCKET, 0, 1);
+    expect_bind(FAKE_SOCKET, -1);
+    assert_int_equal(BIO_listen(FAKE_SOCKET, &a, 0), 0);
+}
+
+static void test_listen_dgram(void **state)
+{
+    /* datagram sockets are bound but never put into listen() */
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_DGRAM,
+        sizeof(int));
+    expect_nbio(FAKE_SOCKET, 0, 1);
+    expect_bind(FAKE_SOCKET, 0);
+    assert_int_equal(BIO_listen(FAKE_SOCKET, &a, 0), 1);
+}
+
+static void test_listen_listen_fails(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET);
+    expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM,
+        sizeof(int));
+    expect_nbio(FAKE_SOCKET, 0, 1);
+    expect_bind(FAKE_SOCKET, 0);
+    expect_listen(FAKE_SOCKET, -1);
+    assert_int_equal(BIO_listen(FAKE_SOCKET, &a, 0), 0);
+}
+
+#if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
+static void test_listen_v6only(void **state)
+{
+    /* an AF_INET6 socket always gets IPV6_V6ONLY set, here to 1 */
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET6);
+    expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM,
+        sizeof(int));
+    expect_nbio(FAKE_SOCKET, 0, 1);
+    expect_setsockopt(FAKE_SOCKET, IPPROTO_IPV6, IPV6_V6ONLY, 1, 0);
+    expect_bind(FAKE_SOCKET, 0);
+    expect_listen(FAKE_SOCKET, 0);
+    assert_int_equal(BIO_listen(FAKE_SOCKET, &a, BIO_SOCK_V6_ONLY), 1);
+}
+
+static void test_listen_v6only_off(void **state)
+{
+    /* without BIO_SOCK_V6_ONLY the option is still set, to 0 */
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET6);
+    expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM,
+        sizeof(int));
+    expect_nbio(FAKE_SOCKET, 0, 1);
+    expect_setsockopt(FAKE_SOCKET, IPPROTO_IPV6, IPV6_V6ONLY, 0, 0);
+    expect_bind(FAKE_SOCKET, 0);
+    expect_listen(FAKE_SOCKET, 0);
+    assert_int_equal(BIO_listen(FAKE_SOCKET, &a, 0), 1);
+}
+
+static void test_listen_v6only_fails(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    make_addr(&a, AF_INET6);
+    expect_getsockopt(FAKE_SOCKET, SOL_SOCKET, SO_TYPE, 0, SOCK_STREAM,
+        sizeof(int));
+    expect_nbio(FAKE_SOCKET, 0, 1);
+    expect_setsockopt(FAKE_SOCKET, IPPROTO_IPV6, IPV6_V6ONLY, 1, -1);
+    assert_int_equal(BIO_listen(FAKE_SOCKET, &a, BIO_SOCK_V6_ONLY), 0);
+}
+#endif
+
+/* BIO_accept_ex */
+
+static void test_accept_ex_success(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    memset(&a, 0, sizeof(a));
+    expect_accept(FAKE_SOCKET, ACCEPTED_SOCKET);
+    expect_nbio(ACCEPTED_SOCKET, 0, 1);
+    assert_int_equal(BIO_accept_ex(FAKE_SOCKET, &a, 0), ACCEPTED_SOCKET);
+}
+
+static void test_accept_ex_null_addr(void **state)
+{
+    /* a NULL addr is accepted into an internal local BIO_ADDR */
+    (void)state;
+    expect_accept(FAKE_SOCKET, ACCEPTED_SOCKET);
+    expect_nbio(ACCEPTED_SOCKET, 0, 1);
+    assert_int_equal(BIO_accept_ex(FAKE_SOCKET, NULL, 0), ACCEPTED_SOCKET);
+}
+
+static void test_accept_ex_nonblock(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    memset(&a, 0, sizeof(a));
+    expect_accept(FAKE_SOCKET, ACCEPTED_SOCKET);
+    expect_nbio(ACCEPTED_SOCKET, 1, 1);
+    assert_int_equal(
+        BIO_accept_ex(FAKE_SOCKET, &a, BIO_SOCK_NONBLOCK), ACCEPTED_SOCKET);
+}
+
+static void test_accept_ex_retry(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    memset(&a, 0, sizeof(a));
+    expect_accept(FAKE_SOCKET, -1);
+    expect_should_retry(-1, 1);
+    assert_int_equal(BIO_accept_ex(FAKE_SOCKET, &a, 0), (int)INVALID_SOCKET);
+}
+
+static void test_accept_ex_error(void **state)
+{
+    BIO_ADDR a;
+
+    (void)state;
+    memset(&a, 0, sizeof(a));
+    expect_accept(FAKE_SOCKET, -1);
+    expect_should_retry(-1, 0);
+    assert_int_equal(BIO_accept_ex(FAKE_SOCKET, &a, 0), (int)INVALID_SOCKET);
+}
+
+static void test_accept_ex_nbio_fails(void **state)
+{
+    /* a non-blocking failure on the accepted socket closes it again */
+    BIO_ADDR a;
+
+    (void)state;
+    memset(&a, 0, sizeof(a));
+    expect_accept(FAKE_SOCKET, ACCEPTED_SOCKET);
+    expect_nbio(ACCEPTED_SOCKET, 0, 0);
+    expect_close(ACCEPTED_SOCKET, 0);
+    assert_int_equal(BIO_accept_ex(FAKE_SOCKET, &a, 0), (int)INVALID_SOCKET);
+}
+
+/* BIO_closesocket */
+
+static void test_closesocket_success(void **state)
+{
+    (void)state;
+    expect_close(FAKE_SOCKET, 0);
+    assert_int_equal(BIO_closesocket(FAKE_SOCKET), 1);
+}
+
+static void test_closesocket_negative(void **state)
+{
+    /* a negative fd short-circuits before any close() */
+    (void)state;
+    assert_int_equal(BIO_closesocket(-1), 0);
+}
+
+static void test_closesocket_fails(void **state)
+{
+    (void)state;
+    expect_close(FAKE_SOCKET, -1);
+    assert_int_equal(BIO_closesocket(FAKE_SOCKET), 0);
+}
+
+/* main */
+
+#define SOCK_TEST(name) cmocka_unit_test(name)
+
+int main(void)
+{
+    const struct CMUnitTest tests[] = {
+        /* BIO_socket */
+        SOCK_TEST(test_socket_success),
+        SOCK_TEST(test_socket_fails),
+        /* BIO_connect */
+        SOCK_TEST(test_connect_invalid_sock),
+        SOCK_TEST(test_connect_nbio_fails),
+        SOCK_TEST(test_connect_success),
+        SOCK_TEST(test_connect_nonblock),
+        SOCK_TEST(test_connect_keepalive),
+        SOCK_TEST(test_connect_keepalive_fails),
+        SOCK_TEST(test_connect_nodelay),
+        SOCK_TEST(test_connect_nodelay_fails),
+        SOCK_TEST(test_connect_fails_retry),
+        SOCK_TEST(test_connect_fails_error),
+        /* BIO_bind */
+        SOCK_TEST(test_bind_invalid_sock),
+        SOCK_TEST(test_bind_success),
+        SOCK_TEST(test_bind_reuseaddr),
+        SOCK_TEST(test_bind_reuseaddr_fails),
+        SOCK_TEST(test_bind_bind_fails),
+        /* BIO_listen */
+        SOCK_TEST(test_listen_success),
+        SOCK_TEST(test_listen_invalid_sock),
+        SOCK_TEST(test_listen_getsockopt_fails),
+        SOCK_TEST(test_listen_socktype_len_mismatch),
+        SOCK_TEST(test_listen_nbio_fails),
+        SOCK_TEST(test_listen_keepalive),
+        SOCK_TEST(test_listen_keepalive_fails),
+        SOCK_TEST(test_listen_nodelay),
+        SOCK_TEST(test_listen_reuseaddr),
+        SOCK_TEST(test_listen_bind_fails),
+        SOCK_TEST(test_listen_dgram),
+        SOCK_TEST(test_listen_listen_fails),
+#if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
+        SOCK_TEST(test_listen_v6only),
+        SOCK_TEST(test_listen_v6only_off),
+        SOCK_TEST(test_listen_v6only_fails),
+#endif
+        /* BIO_accept_ex */
+        SOCK_TEST(test_accept_ex_success),
+        SOCK_TEST(test_accept_ex_null_addr),
+        SOCK_TEST(test_accept_ex_nonblock),
+        SOCK_TEST(test_accept_ex_retry),
+        SOCK_TEST(test_accept_ex_error),
+        SOCK_TEST(test_accept_ex_nbio_fails),
+        /* BIO_closesocket */
+        SOCK_TEST(test_closesocket_success),
+        SOCK_TEST(test_closesocket_negative),
+        SOCK_TEST(test_closesocket_fails),
+    };
+
+    cmocka_set_message_output(CM_OUTPUT_TAP);
+
+    return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+#else
+
+int main(void)
+{
+    return 0;
+}
+
+#endif /* OPENSSL_NO_SOCK */