From 1178a9dd3ae1afdba0c2bb309a04b8a33056ef92 Mon Sep 17 00:00:00 2001
From: Paul Gofman <pgofman@codeweavers.com>
Date: Mon, 31 Oct 2022 12:20:47 -0600
Subject: [PATCH] ws2_32/tests: Add tests for SO_EXCLUSIVEADDRUSE.

---
 dlls/ws2_32/tests/sock.c | 208 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 204 insertions(+), 4 deletions(-)

diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c
index 5460bc621d3..852cdd88dc7 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -2013,9 +2013,10 @@ static void test_set_getsockopt(void)
     }
 }
 
-static void test_so_reuseaddr(void)
+static void test_reuseaddr(void)
 {
     static struct sockaddr_in6 saddr_in6_any, saddr_in6_loopback;
+    static struct sockaddr_in6 saddr_in6_any_v4mapped, saddr_in6_loopback_v4mapped;
     static struct sockaddr_in saddr_in_any, saddr_in_loopback;
 
     static const struct
@@ -2030,10 +2031,132 @@ static void test_so_reuseaddr(void)
         { AF_INET, (struct sockaddr *)&saddr_in_any, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_any) },
         { AF_INET6, (struct sockaddr *)&saddr_in6_any, (struct sockaddr *)&saddr_in6_loopback, sizeof(saddr_in6_any) },
     };
-    unsigned int rc, reuse;
+    static const struct
+    {
+        struct
+        {
+            int domain;
+            struct sockaddr *addr;
+            socklen_t addrlen;
+            BOOL exclusive;
+        }
+        s[2];
+        int error;
+        BOOL todo;
+    }
+    tests_exclusive[] =
+    {
+        {
+            {{ AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), TRUE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), FALSE, }},
+            WSAEACCES, TRUE,
+        },
+        {
+            {{ AF_INET6, (struct sockaddr *)&saddr_in6_any, sizeof(saddr_in6_any), TRUE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), FALSE, }},
+            WSAEACCES, TRUE,
+        },
+        {
+            {{ AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), FALSE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), TRUE, }},
+            NOERROR,
+        },
+        {
+            {{ AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), TRUE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), TRUE, }},
+            WSAEACCES, TRUE,
+        },
+        {
+            {{ AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), TRUE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), FALSE, }},
+            NOERROR,
+        },
+        {
+            {{ AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), FALSE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), TRUE, }},
+            WSAEADDRINUSE, TRUE,
+        },
+        {
+            {{ AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), FALSE, },
+            { AF_INET6, (struct sockaddr *)&saddr_in6_any, sizeof(saddr_in6_any), TRUE, }},
+            WSAEADDRINUSE, TRUE,
+        },
+        {
+            {{ AF_INET6, (struct sockaddr *)&saddr_in6_loopback, sizeof(saddr_in6_loopback), FALSE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), TRUE, }},
+            NOERROR,
+        },
+        {
+            {{ AF_INET6, (struct sockaddr *)&saddr_in6_loopback, sizeof(saddr_in6_loopback), TRUE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), TRUE, }},
+            NOERROR,
+        },
+        {
+            {{ AF_INET6, (struct sockaddr *)&saddr_in6_loopback_v4mapped, sizeof(saddr_in6_loopback_v4mapped), FALSE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), TRUE, }},
+            WSAEADDRINUSE, TRUE,
+        },
+        {
+            {{ AF_INET6, (struct sockaddr *)&saddr_in6_any, sizeof(saddr_in6_any), TRUE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), FALSE, }},
+            WSAEACCES, TRUE,
+        },
+        {
+            {{ AF_INET6, (struct sockaddr *)&saddr_in6_any, sizeof(saddr_in6_any), FALSE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), TRUE, }},
+            NOERROR, TRUE,
+        },
+        {
+            {{ AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), FALSE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), TRUE, }},
+            WSAEADDRINUSE,
+        },
+        {
+            {{ AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), FALSE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), FALSE, }},
+            WSAEADDRINUSE,
+        },
+        {
+            {{ AF_INET6, (struct sockaddr *)&saddr_in6_any, sizeof(saddr_in6_any), FALSE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), FALSE, }},
+            NOERROR, TRUE,
+        },
+        {
+            {{ AF_INET6, (struct sockaddr *)&saddr_in6_any_v4mapped, sizeof(saddr_in6_any_v4mapped), FALSE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_any, sizeof(saddr_in_any), FALSE, }},
+            WSAEADDRINUSE,
+        },
+        {
+            {{ AF_INET6, (struct sockaddr *)&saddr_in6_loopback_v4mapped, sizeof(saddr_in6_loopback_v4mapped), FALSE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), FALSE, }},
+            WSAEADDRINUSE,
+        },
+        {
+            {{ AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), FALSE, },
+            { AF_INET6, (struct sockaddr *)&saddr_in6_loopback_v4mapped, sizeof(saddr_in6_loopback_v4mapped), FALSE, }},
+            WSAEADDRINUSE,
+        },
+        {
+            {{ AF_INET6, (struct sockaddr *)&saddr_in6_loopback, sizeof(saddr_in6_loopback), TRUE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), TRUE, }},
+            NOERROR,
+        },
+        {
+            {{ AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), TRUE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), FALSE, }},
+            WSAEADDRINUSE,
+        },
+        {
+            {{ AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), FALSE, },
+            { AF_INET, (struct sockaddr *)&saddr_in_loopback, sizeof(saddr_in_loopback), TRUE, }},
+            WSAEADDRINUSE,
+        },
+    };
+
+    unsigned int rc, reuse, value;
     struct sockaddr saddr;
     SOCKET s1, s2, s3, s4;
-    unsigned int i;
+    unsigned int i, j;
     int size;
 
     saddr_in_any.sin_family = AF_INET;
@@ -2048,6 +2171,14 @@ static void test_so_reuseaddr(void)
     saddr_in6_loopback = saddr_in6_any;
     inet_pton(AF_INET6, "::1", &saddr_in6_loopback.sin6_addr);
 
+    saddr_in6_loopback_v4mapped = saddr_in6_any;
+    rc = inet_pton(AF_INET6, "::ffff:127.0.0.1", &saddr_in6_loopback_v4mapped.sin6_addr);
+    ok(rc, "got error %d.\n", WSAGetLastError());
+
+    saddr_in6_any_v4mapped = saddr_in6_any;
+    rc = inet_pton(AF_INET6, "::ffff:0.0.0.0", &saddr_in6_any_v4mapped.sin6_addr);
+    ok(rc, "got error %d.\n", WSAGetLastError());
+
     for (i = 0; i < ARRAY_SIZE(tests); ++i)
     {
         winetest_push_context("test %u", i);
@@ -2210,6 +2341,75 @@ static void test_so_reuseaddr(void)
 
         winetest_pop_context();
     }
+
+    /* SO_REUSEADDR and SO_EXCLUSIVEADDRUSE are mutually exclusive. */
+    s1 = socket(AF_INET, SOCK_STREAM, 0);
+    ok(s1 != INVALID_SOCKET, "got error %d.\n", WSAGetLastError());
+
+    value = 1;
+    rc = setsockopt(s1, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(value));
+    ok(!rc, "got error %d.\n", WSAGetLastError());
+
+    value = 1;
+    rc = setsockopt(s1, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&value, sizeof(value));
+    todo_wine ok(rc == SOCKET_ERROR && WSAGetLastError() == WSAEINVAL, "got rc %d, error %d.\n", rc, WSAGetLastError());
+
+    value = 0;
+    rc = setsockopt(s1, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(value));
+
+    value = 1;
+    rc = setsockopt(s1, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&value, sizeof(value));
+    ok(!rc, "got error %d.\n", WSAGetLastError());
+
+    value = 1;
+    rc = setsockopt(s1, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(value));
+    todo_wine ok(rc == SOCKET_ERROR && WSAGetLastError() == WSAEINVAL, "got rc %d, error %d.\n", rc, WSAGetLastError());
+
+    closesocket(s1);
+
+    /* Test SO_EXCLUSIVEADDRUSE. */
+    for (i = 0; i < ARRAY_SIZE(tests_exclusive); ++i)
+    {
+        SOCKET s[2];
+
+        winetest_push_context("test %u", i);
+
+        for (j = 0; j < 2; ++j)
+        {
+            s[j] = socket(tests_exclusive[i].s[j].domain, SOCK_STREAM, 0);
+            ok(s[j] != INVALID_SOCKET, "got error %d.\n", WSAGetLastError());
+
+            if (tests_exclusive[i].s[j].exclusive)
+            {
+                value = 1;
+                rc = setsockopt(s[j], SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&value, sizeof(value));
+                ok(!rc, "got error %d.\n", WSAGetLastError());
+            }
+            if (tests_exclusive[i].s[j].domain == AF_INET6)
+            {
+                value = 0;
+                rc = setsockopt(s[j], IPPROTO_IPV6, IPV6_V6ONLY, (char*)&value, sizeof(value));
+                ok(!rc, "got error %d.\n", WSAGetLastError());
+            }
+        }
+        rc = bind(s[0], tests_exclusive[i].s[0].addr, tests_exclusive[i].s[0].addrlen);
+        ok(!rc, "got error %d.\n", WSAGetLastError());
+
+        rc = bind(s[1], tests_exclusive[i].s[1].addr, tests_exclusive[i].s[1].addrlen);
+
+        todo_wine_if(tests_exclusive[i].todo)
+        {
+        if (tests_exclusive[i].error)
+            ok(rc == SOCKET_ERROR && WSAGetLastError() == tests_exclusive[i].error,
+                    "got rc %d, error %d, expected error %d.\n", rc, WSAGetLastError(), tests_exclusive[i].error);
+        else
+            ok(!rc, "got error %d.\n", WSAGetLastError());
+        }
+
+        closesocket(s[0]);
+        closesocket(s[1]);
+        winetest_pop_context();
+    }
 }
 
 #define IP_PKTINFO_LEN (sizeof(WSACMSGHDR) + WSA_CMSG_ALIGN(sizeof(struct in_pktinfo)))
@@ -13389,7 +13589,7 @@ START_TEST( sock )
     Init();
 
     test_set_getsockopt();
-    test_so_reuseaddr();
+    test_reuseaddr();
     test_ip_pktinfo();
     test_ipv4_cmsg();
     test_ipv6_cmsg();
-- 
GitLab