=?UTF-8?Q?Roman=20Pi=C5=A1l=20?=: ws2_32: Fix the default behavior of IPV6_V6ONLY.

Alexandre Julliard julliard at winehq.org
Mon Oct 31 14:41:10 CDT 2016


Module: wine
Branch: master
Commit: 96eb36e4bbdb21da18aa2dd843b60f91fca5c0c4
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=96eb36e4bbdb21da18aa2dd843b60f91fca5c0c4

Author: Roman Pišl <rpisl at seznam.cz>
Date:   Sat Oct 29 22:26:43 2016 -0200

ws2_32: Fix the default behavior of IPV6_V6ONLY.

Signed-off-by: Roman Pišl <rpisl at seznam.cz>
Signed-off-by: Bruno Jesus <00cpxxx at gmail.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/ws2_32/socket.c     | 29 +++++++--------
 dlls/ws2_32/tests/sock.c | 91 ++++++++++++++++++++++++++++++++++++++----------
 2 files changed, 88 insertions(+), 32 deletions(-)

diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c
index 3d9a99b..8510034 100644
--- a/dlls/ws2_32/socket.c
+++ b/dlls/ws2_32/socket.c
@@ -3240,20 +3240,6 @@ int WINAPI WS_bind(SOCKET s, const struct WS_sockaddr* name, int namelen)
             }
             else
             {
-#ifdef IPV6_V6ONLY
-                const struct sockaddr_in6 *in6 = (const struct sockaddr_in6*) &uaddr;
-                if (name->sa_family == WS_AF_INET6 &&
-                    !memcmp(&in6->sin6_addr, &in6addr_any, sizeof(struct in6_addr)))
-                {
-                    int enable = 1;
-                    if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &enable, sizeof(enable)) == -1)
-                    {
-                        release_sock_fd( s, fd );
-                        SetLastError(WSAEAFNOSUPPORT);
-                        return SOCKET_ERROR;
-                    }
-                }
-#endif
                 if (name->sa_family == WS_AF_INET)
                 {
                     struct sockaddr_in *in4 = (struct sockaddr_in*) &uaddr;
@@ -7241,6 +7227,21 @@ SOCKET WINAPI WSASocketW(int af, int type, int protocol,
         TRACE("\tcreated %04lx\n", ret );
         if (ipxptype > 0)
             set_ipx_packettype(ret, ipxptype);
+
+#ifdef IPV6_V6ONLY
+        if (unixaf == AF_INET6)
+        {
+            int fd = get_sock_fd(ret, 0, NULL);
+            if (fd != -1)
+            {
+                /* IPV6_V6ONLY is set by default on Windows */
+                int enable = 1;
+                if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &enable, sizeof(enable)))
+                    WARN("\tsetting IPV6_V6ONLY failed - errno = %i\n", errno);
+                release_sock_fd(ret, fd);
+            }
+        }
+#endif
        return ret;
     }
 
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c
index a144bd3..80425c9 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -1732,7 +1732,7 @@ static void test_so_reuseaddr(void)
     DWORD err;
 
     saddr.sin_family      = AF_INET;
-    saddr.sin_port        = htons(9375);
+    saddr.sin_port        = htons(SERVERPORT+1);
     saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
 
     s1=socket(AF_INET, SOCK_STREAM, 0);
@@ -6105,11 +6105,10 @@ end:
 
 static void test_ipv6only(void)
 {
-    SOCKET v4 = INVALID_SOCKET,
-           v6 = INVALID_SOCKET;
+    SOCKET v4 = INVALID_SOCKET, v6;
     struct sockaddr_in sin4;
     struct sockaddr_in6 sin6;
-    int ret;
+    int ret, enabled, len = sizeof(enabled);
 
     memset(&sin4, 0, sizeof(sin4));
     sin4.sin_family = AF_INET;
@@ -6120,27 +6119,83 @@ static void test_ipv6only(void)
     sin6.sin6_port = htons(SERVERPORT);
 
     v6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
-    if (v6 == INVALID_SOCKET) {
-        skip("Could not create IPv6 socket (LastError: %d; %d expected if IPv6 not available).\n",
-            WSAGetLastError(), WSAEAFNOSUPPORT);
+    if (v6 == INVALID_SOCKET)
+    {
+        skip("Could not create IPv6 socket (LastError: %d)\n", WSAGetLastError());
         goto end;
     }
+
+    enabled = 2;
+    ret = getsockopt(v6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&enabled, &len);
+    ok(!ret, "getsockopt(IPV6_ONLY) failed (LastError: %d)\n", WSAGetLastError());
+    ok(enabled == 1, "expected 1, got %d\n", enabled);
+
     ret = bind(v6, (struct sockaddr*)&sin6, sizeof(sin6));
-    if (ret) {
-        skip("Could not bind IPv6 address (LastError: %d).\n",
-            WSAGetLastError());
+    if (ret)
+    {
+        skip("Could not bind IPv6 address (LastError: %d)\n", WSAGetLastError());
         goto end;
     }
 
     v4 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-    if (v4 == INVALID_SOCKET) {
-        skip("Could not create IPv4 socket (LastError: %d).\n",
-            WSAGetLastError());
-        goto end;
-    }
+    ok(v4 != INVALID_SOCKET, "Could not create IPv6 socket (LastError: %d)\n", WSAGetLastError());
+
+    /* bind on IPv4 socket should succeed - IPV6_V6ONLY is enabled by default */
+    ret = bind(v4, (struct sockaddr*)&sin4, sizeof(sin4));
+    ok(!ret, "Could not bind IPv4 address (LastError: %d)\n", WSAGetLastError());
+
+    closesocket(v4);
+    closesocket(v6);
+
+    /* Test again, this time disabling IPV6_V6ONLY. */
+    sin4.sin_port = htons(SERVERPORT+2);
+    sin6.sin6_port = htons(SERVERPORT+2);
+
+    v6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
+    ok(v6 != INVALID_SOCKET, "Could not create IPv6 socket (LastError: %d; %d expected if IPv6 not available).\n",
+        WSAGetLastError(), WSAEAFNOSUPPORT);
+
+    enabled = 0;
+    ret = setsockopt(v6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&enabled, len);
+    ok(!ret, "Could not disable IPV6_V6ONLY (LastError: %d).\n", WSAGetLastError());
+
+    enabled = 2;
+    ret = getsockopt(v6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&enabled, &len);
+    ok(!ret, "getsockopt(IPV6_ONLY) failed (LastError: %d)\n", WSAGetLastError());
+    ok(!enabled, "expected 0, got %d\n", enabled);
+
+    /*
+        Observaition:
+        On Windows, bind on both IPv4 and IPv6 with IPV6_V6ONLY disabled succeeds by default.
+        Application must set SO_EXCLUSIVEADDRUSE on first socket to disallow another successful bind.
+        In general, a standard application should not use SO_REUSEADDR.
+        Setting both SO_EXCLUSIVEADDRUSE and SO_REUSEADDR on the same socket is not possible in
+        either order, the later setsockopt call always fails.
+    */
+    enabled = 1;
+    ret = setsockopt(v6, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&enabled, len);
+    ok(!ret, "Could not set SO_EXCLUSIVEADDRUSE on IPv6 socket (LastError: %d)\n", WSAGetLastError());
+
+    ret = bind(v6, (struct sockaddr*)&sin6, sizeof(sin6));
+    ok(!ret, "Could not bind IPv6 address (LastError: %d)\n", WSAGetLastError());
+
+    enabled = 2;
+    len = sizeof(enabled);
+    getsockopt(v6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&enabled, &len);
+    ok(!ret, "getsockopt(IPV6_ONLY) failed (LastError: %d)\n", WSAGetLastError());
+    ok(!enabled, "IPV6_V6ONLY is enabled after bind\n");
+
+    v4 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ok(v4 != INVALID_SOCKET, "Could not create IPv4 socket (LastError: %d)\n", WSAGetLastError());
+
+    enabled = 1;
+    ret = setsockopt(v4, SOL_SOCKET, SO_REUSEADDR, (char*)&enabled, len);
+    ok(!ret, "Could not set SO_REUSEADDR on IPv4 socket (LastError: %d)\n", WSAGetLastError());
+
+    WSASetLastError(0xdeadbeef);
     ret = bind(v4, (struct sockaddr*)&sin4, sizeof(sin4));
-    ok(!ret, "Could not bind IPv4 address (LastError: %d; %d expected if IPv6 binds to IPv4 as well).\n",
-        WSAGetLastError(), WSAEADDRINUSE);
+    ok(ret, "bind succeeded unexpectedly for the IPv4 socket\n");
+    ok(WSAGetLastError() == WSAEACCES, "Expected 10013, got %d\n", WSAGetLastError());
 
 end:
     if (v4 != INVALID_SOCKET)
@@ -8024,7 +8079,7 @@ static void test_TransmitFile(void)
     /* Setup a properly connected socket for transfers */
     memset(&bindAddress, 0, sizeof(bindAddress));
     bindAddress.sin_family = AF_INET;
-    bindAddress.sin_port = htons(9375);
+    bindAddress.sin_port = htons(SERVERPORT+1);
     bindAddress.sin_addr.s_addr = inet_addr("127.0.0.1");
     iret = bind(server, (struct sockaddr*)&bindAddress, sizeof(bindAddress));
     if (iret != 0)




More information about the wine-cvs mailing list