Module: wine Branch: master Commit: 96eb36e4bbdb21da18aa2dd843b60f91fca5c0c4 URL: http://source.winehq.org/git/wine.git/?a=commit;h=96eb36e4bbdb21da18aa2dd843...
Author: Roman Pišl rpisl@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@seznam.cz Signed-off-by: Bruno Jesus 00cpxxx@gmail.com Signed-off-by: Alexandre Julliard julliard@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)