=?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