Commit bcbecdcaa5 for openssl.org

commit bcbecdcaa5c0d7ae11ddf2de58b65ae1b97ff0c4
Author: Jakub Zelenka <jakub.zelenka@openssl.foundation>
Date:   Thu May 14 18:39:18 2026 +0200

    Add BIO bio_addr test

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

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

diff --git a/test/unit/build.info b/test/unit/build.info
index 1ada01a0a5..befcc67279 100644
--- a/test/unit/build.info
+++ b/test/unit/build.info
@@ -1,5 +1,11 @@
 # Make sure all tests have WRAP otherwise -lcmocka is not added.

+PROGRAMS{noinst}=crypto/bio/test_bio_addr
+SOURCE[crypto/bio/test_bio_addr]=crypto/bio/test_bio_addr.c
+INCLUDE[crypto/bio/test_bio_addr]=../../include ../../crypto/bio
+DEPEND[crypto/bio/test_bio_addr]=../../libcrypto.a
+WRAP[crypto/bio/test_bio_addr]=BIO_sock_init getnameinfo freeaddrinfo
+
 PROGRAMS{noinst}=crypto/bio/test_bss_fd
 SOURCE[crypto/bio/test_bss_fd]=crypto/bio/test_bss_fd.c
 INCLUDE[crypto/bio/test_bss_fd]=../../include ../../crypto/bio
diff --git a/test/unit/crypto/bio/test_bio_addr.c b/test/unit/crypto/bio/test_bio_addr.c
new file mode 100644
index 0000000000..de92d8a0f6
--- /dev/null
+++ b/test/unit/crypto/bio/test_bio_addr.c
@@ -0,0 +1,1155 @@
+/*
+ * 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"
+
+#ifdef OPENSSL_NO_SOCK
+
+int main(void)
+{
+    return 0;
+}
+
+#else
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <string.h>
+#include <cmocka.h>
+
+#include "bio_local.h"
+
+#include <openssl/bio.h>
+
+/* wraps */
+
+int __wrap_BIO_sock_init(void);
+#ifdef AI_PASSIVE
+int __wrap_getnameinfo(const struct sockaddr *sa, socklen_t salen,
+    char *host, socklen_t hostlen,
+    char *serv, socklen_t servlen, int flags);
+void __wrap_freeaddrinfo(struct addrinfo *res);
+#endif
+
+int __wrap_BIO_sock_init(void)
+{
+    function_called();
+    return mock_type(int);
+}
+
+#ifdef AI_PASSIVE
+int __wrap_getnameinfo(const struct sockaddr *sa, socklen_t salen,
+    char *host, socklen_t hostlen,
+    char *serv, socklen_t servlen, int flags)
+{
+    int rc;
+
+    function_called();
+    check_expected_ptr(sa);
+    check_expected(salen);
+    check_expected(flags);
+    rc = mock_type(int);
+    if (rc == 0) {
+        if (host != NULL)
+            strncpy(host, mock_ptr_type(const char *), hostlen - 1);
+        if (serv != NULL)
+            strncpy(serv, mock_ptr_type(const char *), servlen - 1);
+    }
+    return rc;
+}
+
+void __wrap_freeaddrinfo(struct addrinfo *res)
+{
+    function_called();
+    check_expected_ptr(res);
+}
+#endif /* AI_PASSIVE */
+
+/* expectations */
+
+static void expect_sock_init(int rc)
+{
+    expect_function_call(__wrap_BIO_sock_init);
+    will_return(__wrap_BIO_sock_init, rc);
+}
+
+#ifdef AI_PASSIVE
+static void expect_getnameinfo(const struct sockaddr *sa, socklen_t salen,
+    int flags, int rc,
+    const char *host_out, const char *serv_out)
+{
+    expect_function_call(__wrap_getnameinfo);
+    expect_value(__wrap_getnameinfo, sa, sa);
+    expect_value(__wrap_getnameinfo, salen, salen);
+    expect_value(__wrap_getnameinfo, flags, flags);
+    will_return(__wrap_getnameinfo, rc);
+    if (rc == 0) {
+        will_return(__wrap_getnameinfo, host_out);
+        will_return(__wrap_getnameinfo, serv_out);
+    }
+}
+
+static void expect_freeaddrinfo(const struct addrinfo *res)
+{
+    expect_function_call(__wrap_freeaddrinfo);
+    expect_value(__wrap_freeaddrinfo, res, res);
+}
+#endif /* AI_PASSIVE */
+
+/* BIO_ADDR_clear */
+
+static void test_addr_clear(void **state)
+{
+    BIO_ADDR ap;
+
+    (void)state;
+    memset(&ap, 0xFF, sizeof(ap));
+    BIO_ADDR_clear(&ap);
+    assert_int_equal(ap.sa.sa_family, AF_UNSPEC);
+    assert_int_equal(ap.s_in.sin_port, 0);
+    assert_int_equal(ap.s_in.sin_addr.s_addr, 0);
+}
+
+/* BIO_ADDR_make */
+
+static void test_addr_make_ipv4(void **state)
+{
+    struct sockaddr_in sa4;
+    BIO_ADDR ap;
+
+    (void)state;
+    memset(&sa4, 0, sizeof(sa4));
+    sa4.sin_family = AF_INET;
+    sa4.sin_port = htons(443);
+    sa4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+    assert_int_equal(BIO_ADDR_make(&ap, (const struct sockaddr *)&sa4), 1);
+    assert_int_equal(ap.s_in.sin_family, AF_INET);
+    assert_int_equal(ap.s_in.sin_port, htons(443));
+    assert_int_equal(ap.s_in.sin_addr.s_addr, htonl(INADDR_LOOPBACK));
+}
+
+#if OPENSSL_USE_IPV6
+static void test_addr_make_ipv6(void **state)
+{
+    struct sockaddr_in6 sa6;
+    BIO_ADDR ap;
+    struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT;
+
+    (void)state;
+    memset(&sa6, 0, sizeof(sa6));
+    sa6.sin6_family = AF_INET6;
+    sa6.sin6_port = htons(443);
+    sa6.sin6_addr = loopback;
+
+    assert_int_equal(BIO_ADDR_make(&ap, (const struct sockaddr *)&sa6), 1);
+    assert_int_equal(ap.s_in6.sin6_family, AF_INET6);
+    assert_int_equal(ap.s_in6.sin6_port, htons(443));
+    assert_memory_equal(&ap.s_in6.sin6_addr, &loopback, sizeof(loopback));
+}
+#endif
+
+#ifndef OPENSSL_NO_UNIX_SOCK
+static void test_addr_make_unix(void **state)
+{
+    struct sockaddr_un sau;
+    BIO_ADDR ap;
+
+    (void)state;
+    memset(&sau, 0, sizeof(sau));
+    sau.sun_family = AF_UNIX;
+    strncpy(sau.sun_path, "/tmp/test.sock", sizeof(sau.sun_path) - 1);
+
+    assert_int_equal(BIO_ADDR_make(&ap, (const struct sockaddr *)&sau), 1);
+    assert_int_equal(ap.s_un.sun_family, AF_UNIX);
+    assert_string_equal(ap.s_un.sun_path, "/tmp/test.sock");
+}
+#endif
+
+static void test_addr_make_unknown_family(void **state)
+{
+    struct sockaddr sa;
+    BIO_ADDR ap;
+
+    (void)state;
+    memset(&sa, 0, sizeof(sa));
+    sa.sa_family = AF_UNSPEC;
+    assert_int_equal(BIO_ADDR_make(&ap, &sa), 0);
+}
+
+/* BIO_ADDR_rawmake */
+
+static void test_rawmake_ipv4(void **state)
+{
+    struct in_addr addr4;
+    BIO_ADDR ap;
+
+    (void)state;
+    addr4.s_addr = htonl(INADDR_LOOPBACK);
+    assert_int_equal(
+        BIO_ADDR_rawmake(&ap, AF_INET, &addr4, sizeof(addr4), htons(80)), 1);
+    assert_int_equal(ap.s_in.sin_family, AF_INET);
+    assert_int_equal(ap.s_in.sin_port, htons(80));
+    assert_int_equal(ap.s_in.sin_addr.s_addr, htonl(INADDR_LOOPBACK));
+}
+
+static void test_rawmake_ipv4_wrong_len(void **state)
+{
+    struct in_addr addr4;
+    BIO_ADDR ap;
+
+    (void)state;
+    addr4.s_addr = htonl(INADDR_LOOPBACK);
+    assert_int_equal(
+        BIO_ADDR_rawmake(&ap, AF_INET, &addr4, sizeof(addr4) - 1, 0), 0);
+}
+
+#if OPENSSL_USE_IPV6
+static void test_rawmake_ipv6(void **state)
+{
+    struct in6_addr addr6 = IN6ADDR_LOOPBACK_INIT;
+    BIO_ADDR ap;
+
+    (void)state;
+    assert_int_equal(
+        BIO_ADDR_rawmake(&ap, AF_INET6, &addr6, sizeof(addr6), htons(443)), 1);
+    assert_int_equal(ap.s_in6.sin6_family, AF_INET6);
+    assert_int_equal(ap.s_in6.sin6_port, htons(443));
+    assert_memory_equal(&ap.s_in6.sin6_addr, &addr6, sizeof(addr6));
+}
+#endif
+
+#ifndef OPENSSL_NO_UNIX_SOCK
+static void test_rawmake_unix(void **state)
+{
+    const char *path = "/tmp/test.sock";
+    BIO_ADDR ap;
+
+    (void)state;
+    assert_int_equal(
+        BIO_ADDR_rawmake(&ap, AF_UNIX, path, strlen(path), 0), 1);
+    assert_int_equal(ap.s_un.sun_family, AF_UNIX);
+    assert_string_equal(ap.s_un.sun_path, path);
+}
+
+static void test_rawmake_unix_too_long(void **state)
+{
+    /* path longer than sun_path must be rejected */
+    char path[sizeof(((struct sockaddr_un *)0)->sun_path) + 2];
+    BIO_ADDR ap;
+
+    (void)state;
+    memset(path, 'x', sizeof(path) - 1);
+    path[sizeof(path) - 1] = '\0';
+    assert_int_equal(
+        BIO_ADDR_rawmake(&ap, AF_UNIX, path, strlen(path), 0), 0);
+}
+#endif
+
+static void test_rawmake_unknown_family(void **state)
+{
+    char data[4] = { 0 };
+    BIO_ADDR ap;
+
+    (void)state;
+    assert_int_equal(BIO_ADDR_rawmake(&ap, AF_UNSPEC, data, 0, 0), 0);
+}
+
+/* BIO_ADDR_family */
+
+static void test_addr_family(void **state)
+{
+    BIO_ADDR ap;
+
+    (void)state;
+    memset(&ap, 0, sizeof(ap));
+    ap.sa.sa_family = AF_UNSPEC;
+    assert_int_equal(BIO_ADDR_family(&ap), AF_UNSPEC);
+    ap.sa.sa_family = AF_INET;
+    assert_int_equal(BIO_ADDR_family(&ap), AF_INET);
+}
+
+/* BIO_ADDR_rawport */
+
+static void test_rawport_ipv4(void **state)
+{
+    BIO_ADDR ap;
+    struct in_addr addr4;
+
+    (void)state;
+    addr4.s_addr = htonl(INADDR_LOOPBACK);
+    BIO_ADDR_rawmake(&ap, AF_INET, &addr4, sizeof(addr4), htons(443));
+    assert_int_equal(BIO_ADDR_rawport(&ap), htons(443));
+}
+
+#if OPENSSL_USE_IPV6
+static void test_rawport_ipv6(void **state)
+{
+    BIO_ADDR ap;
+    struct in6_addr addr6 = IN6ADDR_LOOPBACK_INIT;
+
+    (void)state;
+    BIO_ADDR_rawmake(&ap, AF_INET6, &addr6, sizeof(addr6), htons(8080));
+    assert_int_equal(BIO_ADDR_rawport(&ap), htons(8080));
+}
+#endif
+
+static void test_rawport_no_port(void **state)
+{
+    BIO_ADDR ap;
+
+    (void)state;
+    memset(&ap, 0, sizeof(ap));
+    ap.sa.sa_family = AF_UNSPEC;
+    assert_int_equal(BIO_ADDR_rawport(&ap), 0);
+}
+
+/* BIO_ADDR_sockaddr and BIO_ADDR_sockaddr_noconst */
+
+static void test_sockaddr_pointers(void **state)
+{
+    BIO_ADDR ap = { 0 };
+
+    (void)state;
+    memset(&ap, 0, sizeof(ap));
+    assert_ptr_equal(BIO_ADDR_sockaddr(&ap), &ap.sa);
+    assert_ptr_equal(BIO_ADDR_sockaddr_noconst(&ap), &ap.sa);
+}
+
+/* BIO_ADDR_sockaddr_size */
+
+static void test_sockaddr_size_ipv4(void **state)
+{
+    BIO_ADDR ap;
+
+    (void)state;
+    memset(&ap, 0, sizeof(ap));
+    ap.sa.sa_family = AF_INET;
+    assert_int_equal(BIO_ADDR_sockaddr_size(&ap), sizeof(struct sockaddr_in));
+}
+
+#if OPENSSL_USE_IPV6
+static void test_sockaddr_size_ipv6(void **state)
+{
+    BIO_ADDR ap;
+
+    (void)state;
+    memset(&ap, 0, sizeof(ap));
+    ap.sa.sa_family = AF_INET6;
+    assert_int_equal(BIO_ADDR_sockaddr_size(&ap), sizeof(struct sockaddr_in6));
+}
+#endif
+
+#ifndef OPENSSL_NO_UNIX_SOCK
+static void test_sockaddr_size_unix(void **state)
+{
+    BIO_ADDR ap;
+
+    (void)state;
+    memset(&ap, 0, sizeof(ap));
+    ap.sa.sa_family = AF_UNIX;
+    assert_int_equal(BIO_ADDR_sockaddr_size(&ap), sizeof(struct sockaddr_un));
+}
+#endif
+
+static void test_sockaddr_size_default(void **state)
+{
+    BIO_ADDR ap;
+
+    (void)state;
+    memset(&ap, 0, sizeof(ap));
+    ap.sa.sa_family = AF_UNSPEC;
+    assert_int_equal(BIO_ADDR_sockaddr_size(&ap), sizeof(BIO_ADDR));
+}
+
+/* BIO_ADDR_rawaddress */
+
+static void test_rawaddress_unset(void **state)
+{
+    BIO_ADDR ap;
+    struct in_addr out;
+    size_t len;
+
+    (void)state;
+    memset(&ap, 0, sizeof(ap));
+    ap.sa.sa_family = AF_UNSPEC;
+    assert_int_equal(BIO_ADDR_rawaddress(&ap, &out, &len), 0);
+}
+
+static void test_rawaddress_ipv4(void **state)
+{
+    BIO_ADDR ap;
+    struct in_addr expected, out;
+    size_t len = 0;
+
+    (void)state;
+    expected.s_addr = htonl(INADDR_LOOPBACK);
+    BIO_ADDR_rawmake(&ap, AF_INET, &expected, sizeof(expected), htons(80));
+
+    assert_int_equal(BIO_ADDR_rawaddress(&ap, &out, &len), 1);
+    assert_int_equal(len, sizeof(struct in_addr));
+    assert_memory_equal(&out, &expected, sizeof(expected));
+}
+
+static void test_rawaddress_ipv4_len_only(void **state)
+{
+    BIO_ADDR ap;
+    struct in_addr addr4;
+    size_t len = 0;
+
+    (void)state;
+    addr4.s_addr = htonl(INADDR_LOOPBACK);
+    BIO_ADDR_rawmake(&ap, AF_INET, &addr4, sizeof(addr4), 0);
+
+    assert_int_equal(BIO_ADDR_rawaddress(&ap, NULL, &len), 1);
+    assert_int_equal(len, sizeof(struct in_addr));
+}
+
+static void test_rawaddress_ipv4_ptr_only(void **state)
+{
+    BIO_ADDR ap;
+    struct in_addr expected, out;
+
+    (void)state;
+    expected.s_addr = htonl(INADDR_LOOPBACK);
+    BIO_ADDR_rawmake(&ap, AF_INET, &expected, sizeof(expected), 0);
+
+    assert_int_equal(BIO_ADDR_rawaddress(&ap, &out, NULL), 1);
+    assert_memory_equal(&out, &expected, sizeof(expected));
+}
+
+#ifndef OPENSSL_NO_UNIX_SOCK
+static void test_rawaddress_unix(void **state)
+{
+    BIO_ADDR ap;
+    const char *path = "/tmp/unit.sock";
+    char out[64];
+    size_t len = 0;
+
+    (void)state;
+    BIO_ADDR_rawmake(&ap, AF_UNIX, path, strlen(path), 0);
+
+    assert_int_equal(BIO_ADDR_rawaddress(&ap, out, &len), 1);
+    assert_int_equal(len, strlen(path));
+    assert_memory_equal(out, path, strlen(path));
+}
+#endif
+
+/* BIO_ADDR_copy */
+
+static void test_addr_copy_null(void **state)
+{
+    BIO_ADDR ap;
+
+    (void)state;
+    memset(&ap, 0, sizeof(ap));
+    assert_int_equal(BIO_ADDR_copy(NULL, NULL), 0);
+    assert_int_equal(BIO_ADDR_copy(NULL, &ap), 0);
+    assert_int_equal(BIO_ADDR_copy(&ap, NULL), 0);
+}
+
+static void test_addr_copy_unspec(void **state)
+{
+    BIO_ADDR src, dst;
+
+    (void)state;
+    memset(&src, 0, sizeof(src));
+    src.sa.sa_family = AF_UNSPEC;
+    memset(&dst, 0xFF, sizeof(dst));
+
+    assert_int_equal(BIO_ADDR_copy(&dst, &src), 1);
+    assert_int_equal(dst.sa.sa_family, AF_UNSPEC);
+}
+
+static void test_addr_copy_ipv4(void **state)
+{
+    BIO_ADDR src, dst;
+    struct in_addr addr4;
+
+    (void)state;
+    addr4.s_addr = htonl(INADDR_LOOPBACK);
+    BIO_ADDR_rawmake(&src, AF_INET, &addr4, sizeof(addr4), htons(443));
+    memset(&dst, 0, sizeof(dst));
+
+    assert_int_equal(BIO_ADDR_copy(&dst, &src), 1);
+    assert_int_equal(dst.s_in.sin_family, AF_INET);
+    assert_int_equal(dst.s_in.sin_port, htons(443));
+    assert_int_equal(dst.s_in.sin_addr.s_addr, htonl(INADDR_LOOPBACK));
+}
+
+/* BIO_ADDR_new and BIO_ADDR_free */
+
+static void test_addr_new_sets_unspec(void **state)
+{
+    BIO_ADDR *ap;
+
+    (void)state;
+    ap = BIO_ADDR_new();
+    assert_non_null(ap);
+    assert_int_equal(BIO_ADDR_family(ap), AF_UNSPEC);
+    BIO_ADDR_free(ap);
+}
+
+/* BIO_ADDR_dup */
+
+static void test_addr_dup_null(void **state)
+{
+    (void)state;
+    assert_null(BIO_ADDR_dup(NULL));
+}
+
+static void test_addr_dup_ipv4(void **state)
+{
+    BIO_ADDR src, *dup;
+    struct in_addr addr4;
+
+    (void)state;
+    addr4.s_addr = htonl(INADDR_LOOPBACK);
+    BIO_ADDR_rawmake(&src, AF_INET, &addr4, sizeof(addr4), htons(8443));
+
+    dup = BIO_ADDR_dup(&src);
+    assert_non_null(dup);
+    assert_int_equal(dup->s_in.sin_family, AF_INET);
+    assert_int_equal(dup->s_in.sin_port, htons(8443));
+    assert_int_equal(dup->s_in.sin_addr.s_addr, htonl(INADDR_LOOPBACK));
+    BIO_ADDR_free(dup);
+}
+
+/* BIO_ADDR_path_string */
+
+#ifndef OPENSSL_NO_UNIX_SOCK
+static void test_path_string_unix(void **state)
+{
+    BIO_ADDR ap;
+    char *path;
+
+    (void)state;
+    BIO_ADDR_rawmake(&ap, AF_UNIX, "/run/unit.sock", 14, 0);
+
+    path = BIO_ADDR_path_string(&ap);
+    assert_non_null(path);
+    assert_string_equal(path, "/run/unit.sock");
+    OPENSSL_free(path);
+}
+#endif
+
+static void test_path_string_non_unix(void **state)
+{
+    BIO_ADDR ap;
+    struct in_addr addr4;
+
+    (void)state;
+    addr4.s_addr = htonl(INADDR_LOOPBACK);
+    BIO_ADDR_rawmake(&ap, AF_INET, &addr4, sizeof(addr4), htons(80));
+    assert_null(BIO_ADDR_path_string(&ap));
+}
+
+/* BIO_ADDRINFO accessors */
+
+static void test_addrinfo_next_null(void **state)
+{
+    (void)state;
+    assert_null(BIO_ADDRINFO_next(NULL));
+}
+
+static void test_addrinfo_next(void **state)
+{
+    BIO_ADDRINFO a, b;
+
+    (void)state;
+    memset(&a, 0, sizeof(a));
+    memset(&b, 0, sizeof(b));
+    a.bai_next = &b;
+    b.bai_next = NULL;
+    assert_ptr_equal(BIO_ADDRINFO_next(&a), &b);
+    assert_null(BIO_ADDRINFO_next(&b));
+}
+
+static void test_addrinfo_family(void **state)
+{
+    BIO_ADDRINFO bai;
+
+    (void)state;
+    assert_int_equal(BIO_ADDRINFO_family(NULL), 0);
+    memset(&bai, 0, sizeof(bai));
+    bai.bai_family = AF_INET;
+    assert_int_equal(BIO_ADDRINFO_family(&bai), AF_INET);
+}
+
+static void test_addrinfo_socktype(void **state)
+{
+    BIO_ADDRINFO bai;
+
+    (void)state;
+    assert_int_equal(BIO_ADDRINFO_socktype(NULL), 0);
+    memset(&bai, 0, sizeof(bai));
+    bai.bai_socktype = SOCK_STREAM;
+    assert_int_equal(BIO_ADDRINFO_socktype(&bai), SOCK_STREAM);
+}
+
+/* BIO_ADDRINFO_protocol */
+
+static void test_addrinfo_protocol_null(void **state)
+{
+    (void)state;
+    assert_int_equal(BIO_ADDRINFO_protocol(NULL), 0);
+}
+
+static void test_addrinfo_protocol_explicit(void **state)
+{
+    BIO_ADDRINFO bai;
+
+    (void)state;
+    memset(&bai, 0, sizeof(bai));
+    bai.bai_protocol = IPPROTO_SCTP;
+    assert_int_equal(BIO_ADDRINFO_protocol(&bai), IPPROTO_SCTP);
+}
+
+static void test_addrinfo_protocol_stream(void **state)
+{
+    BIO_ADDRINFO bai;
+
+    (void)state;
+    memset(&bai, 0, sizeof(bai));
+    bai.bai_family = AF_INET;
+    bai.bai_socktype = SOCK_STREAM;
+    assert_int_equal(BIO_ADDRINFO_protocol(&bai), IPPROTO_TCP);
+}
+
+static void test_addrinfo_protocol_dgram(void **state)
+{
+    BIO_ADDRINFO bai;
+
+    (void)state;
+    memset(&bai, 0, sizeof(bai));
+    bai.bai_family = AF_INET;
+    bai.bai_socktype = SOCK_DGRAM;
+    assert_int_equal(BIO_ADDRINFO_protocol(&bai), IPPROTO_UDP);
+}
+
+#ifndef OPENSSL_NO_UNIX_SOCK
+static void test_addrinfo_protocol_unix(void **state)
+{
+    BIO_ADDRINFO bai;
+
+    (void)state;
+    memset(&bai, 0, sizeof(bai));
+    bai.bai_family = AF_UNIX;
+    bai.bai_socktype = SOCK_STREAM;
+    /* AF_UNIX always returns 0, regardless of socktype */
+    assert_int_equal(BIO_ADDRINFO_protocol(&bai), 0);
+}
+#endif
+
+static void test_addrinfo_sockaddr_size(void **state)
+{
+    BIO_ADDRINFO bai;
+
+    (void)state;
+    assert_int_equal(BIO_ADDRINFO_sockaddr_size(NULL), 0);
+    memset(&bai, 0, sizeof(bai));
+    bai.bai_addrlen = 28;
+    assert_int_equal(BIO_ADDRINFO_sockaddr_size(&bai), 28);
+}
+
+static void test_addrinfo_sockaddr(void **state)
+{
+    BIO_ADDRINFO bai;
+    struct sockaddr sa;
+
+    (void)state;
+    assert_null(BIO_ADDRINFO_sockaddr(NULL));
+    memset(&bai, 0, sizeof(bai));
+    bai.bai_addr = &sa;
+    assert_ptr_equal(BIO_ADDRINFO_sockaddr(&bai), &sa);
+}
+
+static void test_addrinfo_address(void **state)
+{
+    BIO_ADDRINFO bai;
+    struct sockaddr sa;
+
+    (void)state;
+    assert_null(BIO_ADDRINFO_address(NULL));
+    memset(&bai, 0, sizeof(bai));
+    bai.bai_addr = &sa;
+    assert_ptr_equal(BIO_ADDRINFO_address(&bai), (BIO_ADDR *)&sa);
+}
+
+/* BIO_ADDRINFO_free */
+
+static void test_addrinfo_free_null(void **state)
+{
+    (void)state;
+    BIO_ADDRINFO_free(NULL); /* must not crash */
+}
+
+#ifdef AI_PASSIVE
+static void test_addrinfo_free_ip(void **state)
+{
+    /* AF_INET with AI_PASSIVE: freeaddrinfo is called, not manual free */
+    struct addrinfo bai;
+
+    (void)state;
+    memset(&bai, 0, sizeof(bai));
+    bai.ai_family = AF_INET;
+
+    expect_freeaddrinfo(&bai);
+    BIO_ADDRINFO_free(&bai);
+}
+#endif
+
+/* BIO_parse_hostserv */
+
+static void test_parse_host_and_service(void **state)
+{
+    char *host = NULL, *service = NULL;
+
+    (void)state;
+    assert_int_equal(
+        BIO_parse_hostserv("host.example:443", &host, &service,
+            BIO_PARSE_PRIO_HOST),
+        1);
+    assert_string_equal(host, "host.example");
+    assert_string_equal(service, "443");
+    OPENSSL_free(host);
+    OPENSSL_free(service);
+}
+
+static void test_parse_empty_host(void **state)
+{
+    char *host = NULL, *service = NULL;
+
+    (void)state;
+    assert_int_equal(
+        BIO_parse_hostserv(":80", &host, &service, BIO_PARSE_PRIO_HOST), 1);
+    assert_null(host);
+    assert_string_equal(service, "80");
+    OPENSSL_free(service);
+}
+
+static void test_parse_star_host(void **state)
+{
+    char *host = NULL, *service = NULL;
+
+    (void)state;
+    assert_int_equal(
+        BIO_parse_hostserv("*:80", &host, &service, BIO_PARSE_PRIO_HOST), 1);
+    assert_null(host);
+    assert_string_equal(service, "80");
+    OPENSSL_free(service);
+}
+
+static void test_parse_empty_service(void **state)
+{
+    char *host = NULL, *service = NULL;
+
+    (void)state;
+    assert_int_equal(
+        BIO_parse_hostserv("host:", &host, &service, BIO_PARSE_PRIO_HOST), 1);
+    assert_string_equal(host, "host");
+    assert_null(service);
+    OPENSSL_free(host);
+}
+
+static void test_parse_star_service(void **state)
+{
+    char *host = NULL, *service = NULL;
+
+    (void)state;
+    assert_int_equal(
+        BIO_parse_hostserv("host:*", &host, &service, BIO_PARSE_PRIO_HOST), 1);
+    assert_string_equal(host, "host");
+    assert_null(service);
+    OPENSSL_free(host);
+}
+
+static void test_parse_no_colon_host_prio(void **state)
+{
+    /* no colon + HOST prio: host set, service untouched */
+    char *host = NULL, *service = NULL;
+
+    (void)state;
+    assert_int_equal(
+        BIO_parse_hostserv("myhost", &host, &service, BIO_PARSE_PRIO_HOST), 1);
+    assert_string_equal(host, "myhost");
+    assert_null(service);
+    OPENSSL_free(host);
+}
+
+static void test_parse_no_colon_serv_prio(void **state)
+{
+    /* no colon + SERV prio: service set, host untouched */
+    char *host = NULL, *service = NULL;
+
+    (void)state;
+    assert_int_equal(
+        BIO_parse_hostserv("https", &host, &service, BIO_PARSE_PRIO_SERV), 1);
+    assert_null(host);
+    assert_string_equal(service, "https");
+    OPENSSL_free(service);
+}
+
+static void test_parse_bracket_host_and_service(void **state)
+{
+    char *host = NULL, *service = NULL;
+
+    (void)state;
+    assert_int_equal(
+        BIO_parse_hostserv("[::1]:443", &host, &service, BIO_PARSE_PRIO_HOST), 1);
+    assert_string_equal(host, "::1");
+    assert_string_equal(service, "443");
+    OPENSSL_free(host);
+    OPENSSL_free(service);
+}
+
+static void test_parse_bracket_no_service(void **state)
+{
+    /* "[host]" with no trailing colon: service left untouched */
+    char *host = NULL, *service = NULL;
+
+    (void)state;
+    assert_int_equal(
+        BIO_parse_hostserv("[::1]", &host, &service, BIO_PARSE_PRIO_HOST), 1);
+    assert_string_equal(host, "::1");
+    assert_null(service);
+    OPENSSL_free(host);
+}
+
+static void test_parse_bracket_unclosed(void **state)
+{
+    char *host = NULL, *service = NULL;
+
+    (void)state;
+    assert_int_equal(
+        BIO_parse_hostserv("[::1", &host, &service, BIO_PARSE_PRIO_HOST), 0);
+}
+
+static void test_parse_bracket_bad_suffix(void **state)
+{
+    /* ']' not followed by ':' or '\0' */
+    char *host = NULL, *service = NULL;
+
+    (void)state;
+    assert_int_equal(
+        BIO_parse_hostserv("[::1]X", &host, &service, BIO_PARSE_PRIO_HOST), 0);
+}
+
+static void test_parse_bracket_service_with_colon(void **state)
+{
+    /* service part contains a colon */
+    char *host = NULL, *service = NULL;
+
+    (void)state;
+    assert_int_equal(
+        BIO_parse_hostserv("[host]:a:b", &host, &service, BIO_PARSE_PRIO_HOST), 0);
+}
+
+static void test_parse_ambiguous(void **state)
+{
+    /* multiple colons without brackets */
+    char *host = NULL, *service = NULL;
+
+    (void)state;
+    assert_int_equal(
+        BIO_parse_hostserv("host:port:extra", &host, &service,
+            BIO_PARSE_PRIO_HOST),
+        0);
+}
+
+static void test_parse_null_host_param(void **state)
+{
+    /* host output param NULL */
+    char *service = NULL;
+
+    (void)state;
+    assert_int_equal(
+        BIO_parse_hostserv("host:443", NULL, &service, BIO_PARSE_PRIO_HOST), 1);
+    assert_string_equal(service, "443");
+    OPENSSL_free(service);
+}
+
+static void test_parse_null_service_param(void **state)
+{
+    /* service output param NULL */
+    char *host = NULL;
+
+    (void)state;
+    assert_int_equal(
+        BIO_parse_hostserv("host:443", &host, NULL, BIO_PARSE_PRIO_HOST), 1);
+    assert_string_equal(host, "host");
+    OPENSSL_free(host);
+}
+
+/* BIO_ADDR_hostname_string and BIO_ADDR_service_string (addr_strings) */
+
+static void make_ipv4_addr(BIO_ADDR *ap, unsigned short port)
+{
+    struct in_addr addr4;
+
+    addr4.s_addr = htonl(INADDR_LOOPBACK);
+    BIO_ADDR_rawmake(ap, AF_INET, &addr4, sizeof(addr4), htons(port));
+}
+
+static void test_hostname_string_sock_init_fail(void **state)
+{
+    BIO_ADDR ap;
+
+    (void)state;
+    make_ipv4_addr(&ap, 80);
+
+    expect_sock_init(0);
+    assert_null(BIO_ADDR_hostname_string(&ap, 1));
+}
+
+static void test_service_string_sock_init_fail(void **state)
+{
+    BIO_ADDR ap;
+
+    (void)state;
+    make_ipv4_addr(&ap, 443);
+
+    expect_sock_init(0);
+    assert_null(BIO_ADDR_service_string(&ap, 1));
+}
+
+#ifdef AI_PASSIVE
+static void test_hostname_string_getnameinfo_fail(void **state)
+{
+    BIO_ADDR ap;
+
+    (void)state;
+    make_ipv4_addr(&ap, 80);
+
+    expect_sock_init(1);
+    expect_getnameinfo(BIO_ADDR_sockaddr(&ap),
+        BIO_ADDR_sockaddr_size(&ap),
+        NI_NUMERICHOST | NI_NUMERICSERV,
+        EAI_AGAIN, NULL, NULL);
+    assert_null(BIO_ADDR_hostname_string(&ap, 1));
+}
+
+static void test_hostname_string_numeric(void **state)
+{
+    BIO_ADDR ap;
+    char *result;
+
+    (void)state;
+    make_ipv4_addr(&ap, 80);
+
+    expect_sock_init(1);
+    expect_getnameinfo(BIO_ADDR_sockaddr(&ap),
+        BIO_ADDR_sockaddr_size(&ap),
+        NI_NUMERICHOST | NI_NUMERICSERV,
+        0, "127.0.0.1", "80");
+    result = BIO_ADDR_hostname_string(&ap, 1);
+    assert_non_null(result);
+    assert_string_equal(result, "127.0.0.1");
+    OPENSSL_free(result);
+}
+
+static void test_hostname_string_non_numeric(void **state)
+{
+    BIO_ADDR ap;
+    char *result;
+
+    (void)state;
+    make_ipv4_addr(&ap, 80);
+
+    expect_sock_init(1);
+    expect_getnameinfo(BIO_ADDR_sockaddr(&ap),
+        BIO_ADDR_sockaddr_size(&ap),
+        0, /* flags = 0 for non-numeric lookup */
+        0, "localhost", "http");
+    result = BIO_ADDR_hostname_string(&ap, 0);
+    assert_non_null(result);
+    assert_string_equal(result, "localhost");
+    OPENSSL_free(result);
+}
+
+static void test_service_string_numeric(void **state)
+{
+    BIO_ADDR ap;
+    char *result;
+
+    (void)state;
+    make_ipv4_addr(&ap, 443);
+
+    expect_sock_init(1);
+    expect_getnameinfo(BIO_ADDR_sockaddr(&ap),
+        BIO_ADDR_sockaddr_size(&ap),
+        NI_NUMERICHOST | NI_NUMERICSERV,
+        0, "127.0.0.1", "443");
+    result = BIO_ADDR_service_string(&ap, 1);
+    assert_non_null(result);
+    assert_string_equal(result, "443");
+    OPENSSL_free(result);
+}
+#else
+
+/* inet_ntoa path: no getnameinfo, result is deterministic from the address */
+static void test_hostname_string_fallback(void **state)
+{
+    BIO_ADDR ap;
+    char *result;
+
+    (void)state;
+    make_ipv4_addr(&ap, 80);
+
+    expect_sock_init(1);
+    result = BIO_ADDR_hostname_string(&ap, 1);
+    assert_non_null(result);
+    assert_string_equal(result, "127.0.0.1");
+    OPENSSL_free(result);
+}
+
+static void test_service_string_fallback(void **state)
+{
+    BIO_ADDR ap;
+    char *result;
+
+    (void)state;
+    make_ipv4_addr(&ap, 443);
+
+    expect_sock_init(1);
+    result = BIO_ADDR_service_string(&ap, 1);
+    assert_non_null(result);
+    assert_string_equal(result, "443");
+    OPENSSL_free(result);
+}
+
+#endif /* AI_PASSIVE */
+
+/* main */
+
+#define ADDR_TEST(name) cmocka_unit_test(name)
+
+int main(void)
+{
+    const struct CMUnitTest tests[] = {
+        /* BIO_ADDR_clear */
+        ADDR_TEST(test_addr_clear),
+        /* BIO_ADDR_make */
+        ADDR_TEST(test_addr_make_ipv4),
+#if OPENSSL_USE_IPV6
+        ADDR_TEST(test_addr_make_ipv6),
+#endif
+#ifndef OPENSSL_NO_UNIX_SOCK
+        ADDR_TEST(test_addr_make_unix),
+#endif
+        ADDR_TEST(test_addr_make_unknown_family),
+        /* BIO_ADDR_rawmake */
+        ADDR_TEST(test_rawmake_ipv4),
+        ADDR_TEST(test_rawmake_ipv4_wrong_len),
+#if OPENSSL_USE_IPV6
+        ADDR_TEST(test_rawmake_ipv6),
+#endif
+#ifndef OPENSSL_NO_UNIX_SOCK
+        ADDR_TEST(test_rawmake_unix),
+        ADDR_TEST(test_rawmake_unix_too_long),
+#endif
+        ADDR_TEST(test_rawmake_unknown_family),
+        /* BIO_ADDR_family */
+        ADDR_TEST(test_addr_family),
+        /* BIO_ADDR_rawport */
+        ADDR_TEST(test_rawport_ipv4),
+#if OPENSSL_USE_IPV6
+        ADDR_TEST(test_rawport_ipv6),
+#endif
+        ADDR_TEST(test_rawport_no_port),
+        /* BIO_ADDR_sockaddr / BIO_ADDR_sockaddr_noconst */
+        ADDR_TEST(test_sockaddr_pointers),
+        /* BIO_ADDR_sockaddr_size */
+        ADDR_TEST(test_sockaddr_size_ipv4),
+#if OPENSSL_USE_IPV6
+        ADDR_TEST(test_sockaddr_size_ipv6),
+#endif
+#ifndef OPENSSL_NO_UNIX_SOCK
+        ADDR_TEST(test_sockaddr_size_unix),
+#endif
+        ADDR_TEST(test_sockaddr_size_default),
+        /* BIO_ADDR_rawaddress */
+        ADDR_TEST(test_rawaddress_unset),
+        ADDR_TEST(test_rawaddress_ipv4),
+        ADDR_TEST(test_rawaddress_ipv4_len_only),
+        ADDR_TEST(test_rawaddress_ipv4_ptr_only),
+#ifndef OPENSSL_NO_UNIX_SOCK
+        ADDR_TEST(test_rawaddress_unix),
+#endif
+        /* BIO_ADDR_copy */
+        ADDR_TEST(test_addr_copy_null),
+        ADDR_TEST(test_addr_copy_unspec),
+        ADDR_TEST(test_addr_copy_ipv4),
+        /* BIO_ADDR_new and BIO_ADDR_free */
+        ADDR_TEST(test_addr_new_sets_unspec),
+        /* BIO_ADDR_dup */
+        ADDR_TEST(test_addr_dup_null),
+        ADDR_TEST(test_addr_dup_ipv4),
+    /* BIO_ADDR_path_string */
+#ifndef OPENSSL_NO_UNIX_SOCK
+        ADDR_TEST(test_path_string_unix),
+#endif
+        ADDR_TEST(test_path_string_non_unix),
+        /* BIO_ADDRINFO accessors */
+        ADDR_TEST(test_addrinfo_next_null),
+        ADDR_TEST(test_addrinfo_next),
+        ADDR_TEST(test_addrinfo_family),
+        ADDR_TEST(test_addrinfo_socktype),
+        ADDR_TEST(test_addrinfo_protocol_null),
+        ADDR_TEST(test_addrinfo_protocol_explicit),
+        ADDR_TEST(test_addrinfo_protocol_stream),
+        ADDR_TEST(test_addrinfo_protocol_dgram),
+#ifndef OPENSSL_NO_UNIX_SOCK
+        ADDR_TEST(test_addrinfo_protocol_unix),
+#endif
+        ADDR_TEST(test_addrinfo_sockaddr_size),
+        ADDR_TEST(test_addrinfo_sockaddr),
+        ADDR_TEST(test_addrinfo_address),
+        /* BIO_ADDRINFO_free */
+        ADDR_TEST(test_addrinfo_free_null),
+#ifdef AI_PASSIVE
+        ADDR_TEST(test_addrinfo_free_ip),
+#endif
+        /* BIO_parse_hostserv */
+        ADDR_TEST(test_parse_host_and_service),
+        ADDR_TEST(test_parse_empty_host),
+        ADDR_TEST(test_parse_star_host),
+        ADDR_TEST(test_parse_empty_service),
+        ADDR_TEST(test_parse_star_service),
+        ADDR_TEST(test_parse_no_colon_host_prio),
+        ADDR_TEST(test_parse_no_colon_serv_prio),
+        ADDR_TEST(test_parse_bracket_host_and_service),
+        ADDR_TEST(test_parse_bracket_no_service),
+        ADDR_TEST(test_parse_bracket_unclosed),
+        ADDR_TEST(test_parse_bracket_bad_suffix),
+        ADDR_TEST(test_parse_bracket_service_with_colon),
+        ADDR_TEST(test_parse_ambiguous),
+        ADDR_TEST(test_parse_null_host_param),
+        ADDR_TEST(test_parse_null_service_param),
+        /* BIO_ADDR_hostname_string / BIO_ADDR_service_string */
+        ADDR_TEST(test_hostname_string_sock_init_fail),
+        ADDR_TEST(test_service_string_sock_init_fail),
+#ifdef AI_PASSIVE
+        ADDR_TEST(test_hostname_string_getnameinfo_fail),
+        ADDR_TEST(test_hostname_string_numeric),
+        ADDR_TEST(test_hostname_string_non_numeric),
+        ADDR_TEST(test_service_string_numeric),
+#else
+        ADDR_TEST(test_hostname_string_fallback),
+        ADDR_TEST(test_service_string_fallback),
+#endif
+    };
+
+    cmocka_set_message_output(CM_OUTPUT_TAP);
+
+    return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+#endif /* OPENSSL_NO_SOCK */