--- /dev/null
+/*
+ * Copyright 2023 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 <openssl/bio.h>
+#include "internal/e_os.h"
+#include "internal/sockets.h"
+#include "testutil.h"
+
+static int families[] = {
+ AF_INET,
+#if OPENSSL_USE_IPV6
+ AF_INET6,
+#endif
+#ifndef OPENSSL_NO_UNIX_SOCK
+ AF_UNIX
+#endif
+};
+
+static BIO_ADDR *make_dummy_addr(int family)
+{
+ BIO_ADDR *addr;
+ union {
+ struct sockaddr_in sin;
+#if OPENSSL_USE_IPV6
+ struct sockaddr_in6 sin6;
+#endif
+#ifndef OPENSSL_NO_UNIX_SOCK
+ struct sockaddr_un sun;
+#endif
+ } sa;
+ void *where;
+ size_t wherelen;
+
+ /* Fill with a dummy address */
+ switch(family) {
+ case AF_INET:
+ where = &(sa.sin.sin_addr);
+ wherelen = sizeof(sa.sin.sin_addr);
+ break;
+#if OPENSSL_USE_IPV6
+ case AF_INET6:
+ where = &(sa.sin6.sin6_addr);
+ wherelen = sizeof(sa.sin6.sin6_addr);
+ break;
+#endif
+#ifndef OPENSSL_NO_UNIX_SOCK
+ case AF_UNIX:
+ where = &(sa.sun.sun_path);
+ /* BIO_ADDR_rawmake needs an extra byte for a NUL-terminator*/
+ wherelen = sizeof(sa.sun.sun_path) - 1;
+ break;
+#endif
+ default:
+ TEST_error("Unsupported address family");
+ return 0;
+ }
+ /*
+ * Could be any data, but we make it printable because BIO_ADDR_rawmake
+ * expects the AF_UNIX address to be a string.
+ */
+ memset(where, 'a', wherelen);
+
+ addr = BIO_ADDR_new();
+ if (!TEST_ptr(addr))
+ return NULL;
+
+ if (!TEST_true(BIO_ADDR_rawmake(addr, family, where, wherelen, 1000))) {
+ BIO_ADDR_free(addr);
+ return NULL;
+ }
+
+ return addr;
+}
+
+static int bio_addr_is_eq(const BIO_ADDR *a, const BIO_ADDR *b)
+{
+ struct sockaddr_storage adata, bdata;
+ size_t alen, blen;
+
+ /* True even if a and b are NULL */
+ if (a == b)
+ return 1;
+
+ /* If one is NULL the other cannot be due to the test above */
+ if (a == NULL || b == NULL)
+ return 0;
+
+ if (BIO_ADDR_family(a) != BIO_ADDR_family(b))
+ return 0;
+
+ /* Works even with AF_UNIX/AF_UNSPEC which just returns 0 */
+ if (BIO_ADDR_rawport(a) != BIO_ADDR_rawport(b))
+ return 0;
+
+ if (!BIO_ADDR_rawaddress(a, NULL, &alen)
+ || alen > sizeof(adata)
+ || !BIO_ADDR_rawaddress(a, &adata, &alen))
+ return 0;
+
+ if (!BIO_ADDR_rawaddress(a, NULL, &blen)
+ || blen > sizeof(bdata)
+ || !BIO_ADDR_rawaddress(a, &bdata, &blen))
+ return 0;
+
+ if (alen != blen)
+ return 0;
+
+ if (alen == 0)
+ return 1;
+
+ return memcmp(&adata, &bdata, alen) == 0;
+}
+
+static int test_bio_addr_copy_dup(int idx)
+{
+ BIO_ADDR *src = NULL, *dst = NULL;
+ int ret = 0;
+ int docopy = idx & 1;
+
+ idx >>= 1;
+
+ src = make_dummy_addr(families[idx]);
+ if (!TEST_ptr(src))
+ return 0;
+
+ if (docopy) {
+ dst = BIO_ADDR_new();
+ if (!TEST_ptr(dst))
+ goto err;
+
+ if (!TEST_true(BIO_ADDR_copy(dst, src)))
+ goto err;
+ } else {
+ dst = BIO_ADDR_dup(src);
+ if (!TEST_ptr(dst))
+ goto err;
+ }
+
+ if (!TEST_true(bio_addr_is_eq(src, dst)))
+ goto err;
+
+ ret = 1;
+ err:
+ BIO_ADDR_free(src);
+ BIO_ADDR_free(dst);
+ return ret;
+}
+
+int setup_tests(void)
+{
+ if (!test_skip_common_options()) {
+ TEST_error("Error parsing test options\n");
+ return 0;
+ }
+
+ ADD_ALL_TESTS(test_bio_addr_copy_dup, OSSL_NELEM(families) * 2);
+ return 1;
+}