From: Matthieu Nottale Subject: PATCH] ws2_32: Allow user to enable IP dual stack. Message-Id: Date: Mon, 04 Jul 2016 11:35:34 +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. Signed-off-by: Matthieu Nottale --- dlls/ws2_32/socket.c | 22 ++++++++-------------- dlls/ws2_32/tests/sock.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index b0af3d7..c29bd2b 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; @@ -7163,6 +7149,14 @@ 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) + { + /* Winsock documentation stipulates that IPV6_V6ONLY is enabled by default*/ + int enable = 1; + WS_setsockopt(ret, WS_IPPROTO_IPV6, WS_IPV6_V6ONLY, &enable, sizeof(enable)); + } +#endif return ret; } diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 6853279..7c8d834 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -6114,6 +6114,46 @@ static void test_ipv6only(void) ok(!ret, "Could not bind IPv4 address (LastError: %d; %d expected if IPv6 binds to IPv4 as well).\n", WSAGetLastError(), WSAEADDRINUSE); + if (v4 != INVALID_SOCKET) + closesocket(v4); + if (v6 != INVALID_SOCKET) + closesocket(v6); + + /* Test again, this time disabling V6ONLY*/ + 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); + goto end; + } + DWORD enabled = 0; + int len = sizeof(enabled); + ret = setsockopt(v6, IPPROTO_IPV6, IPV6_V6ONLY, &enabled, len); + if (ret) { + ok(!ret, "Failed to disable IPV6_V6ONLY (LastError: %d).\n", WSAGetLastError()); + goto end; + } + ret = bind(v6, (struct sockaddr*)&sin6, sizeof(sin6)); + if (ret) { + skip("Could not bind IPv6 address (LastError: %d).\n", + WSAGetLastError()); + goto end; + } + ret = getsockopt(v6, IPPROTO_IPV6, IPV6_V6ONLY, &enabled, &len); + if (ret) { + ok(!ret, "Failed to check IPV6_V6ONLY (LastError: %d).\n", WSAGetLastError()); + goto end; + } + ok(!enabled, "IPV6_V6ONLY is enabled after bind\n"); + v4 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (v4 == INVALID_SOCKET) { + skip("Could not create IPv4 socket (LastError: %d).\n", + WSAGetLastError()); + goto end; + } + ret = bind(v4, (struct sockaddr*)&sin4, sizeof(sin4)); + ok(ret, "Could bind IPv4 address when it should have failed (LastError: %d; %d expected if IPv6 binds to IPv4 as well).\n", + WSAGetLastError(), WSAEADDRINUSE); end: if (v4 != INVALID_SOCKET) closesocket(v4); -- 2.5.0 Attached is a test program that shows the behaviour difference between wine and Windows. Here is its output: WINE $ ./st default v6only 0 v6only after bind: 1 $ ./st set default v6only 0 vs6only disabled: 0 v6only after bind: 1 WINE WITH PATCH and NATIVE $ ./st default v6only 1 v6only after bind: 1 $ ./st set default v6only 1 vs6only disabled: 0 v6only after bind: 0 #include #include #include int main(int argc, char** argv) { WSADATA wsaData; int err = WSAStartup(MAKEWORD(2, 2), &wsaData); if (err) { printf("WSAStartup failed with error: %d\n", err); return 1; } SOCKET s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); if (s == INVALID_SOCKET) { printf("socket() failed\n"); return 1; } DWORD enabled = 12; int len = sizeof(enabled); getsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &enabled, &len); printf("default v6only %d\n", enabled); if (argc > 1) { enabled = 0; len = sizeof(enabled); err = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &enabled, len); if (err) printf("setsockopt failed with %d\n", WSAGetLastError()); len = sizeof(enabled); getsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &enabled, &len); printf("vs6only disabled: %d\n", enabled); } struct sockaddr_in6 service; memset(&service, 0, sizeof(service)); service.sin6_family = AF_INET6; service.sin6_port = 0; service.sin6_addr = in6addr_any; if (bind(s, &service, sizeof(service)) == SOCKET_ERROR) { printf("bind failed: %d\n", WSAGetLastError()); return 1; } len = sizeof(enabled); getsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &enabled, &len); printf("v6only after bind: %d\n", enabled); closesocket(s); WSACleanup(); return 0; }