From: Roman Pišl Subject: [PATCH] ws2_32: Allow user to enable IP dual stack (v6). Message-Id: <1476988151-21081-1-git-send-email-rpisl@seznam.cz> Date: Thu, 20 Oct 2016 20:29:11 +0200 IP dual stack (v4+v6) should be disabled by default, but previous code was setting IPV6_V6ONLY in bind() which prevented user to override it. This patch moves setting IPV6_V6ONLY to socket creation time. V1..V3 - original version by Matthieu Nottale V4 - modified tests V5 - fixed failing test on Windows V6 - fixed setting of IPV6_V6ONLY in WSASocketW and formatting Signed-off-by: Roman Pišl --- dlls/ws2_32/socket.c | 31 +++++++++++--------- dlls/ws2_32/tests/sock.c | 75 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 88 insertions(+), 18 deletions(-) diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 3d9a99b..cdebe41 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,23 @@ 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)) != 0) + { + WARN("\tsetting IPV6_V6ONLY failure - 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..49d4e13 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); @@ -6110,6 +6110,8 @@ static void test_ipv6only(void) struct sockaddr_in sin4; struct sockaddr_in6 sin6; int ret; + int enabled; + int len; memset(&sin4, 0, sizeof(sin4)); sin4.sin_family = AF_INET; @@ -6138,9 +6140,74 @@ static void test_ipv6only(void) WSAGetLastError()); goto end; } + + /* 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; %d expected if IPv6 binds to IPv4 as well).\n", - WSAGetLastError(), WSAEADDRINUSE); + ok(!ret, "Could not bind IPv4 address (LastError: %d).\n", + WSAGetLastError()); + + closesocket(v4); + closesocket(v6); + v4 = INVALID_SOCKET; + v6 = INVALID_SOCKET; + + /* 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 = 2; + len = sizeof(enabled); + ret = getsockopt(v6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&enabled, &len); + if (ret) + { + skip("Could not check IPV6_V6ONLY (LastError: %d).\n", WSAGetLastError()); + goto end; + } + ok(enabled == 1, "IPV6_V6ONLY is not enabled by default.\n"); + + enabled = 0; + ret = setsockopt(v6, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&enabled, len); + ok(!ret, "Could not disable IPV6_V6ONLY (LastError: %d).\n", WSAGetLastError()); + + /* + 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(enabled == 0, "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); + bind(v4, (struct sockaddr*)&sin4, sizeof(sin4)); + + ok(WSAGetLastError() == WSAEACCES, "Failed to disable IPV6_V6ONLY (LastError: %d; %d expected if IPv6 binds to IPv4 as well).\n", + WSAGetLastError(), WSAEACCES); end: if (v4 != INVALID_SOCKET) @@ -8024,7 +8091,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) -- 2.7.4