Module: wine
Branch: stable
Commit: 2a097acb0d3df6ebd459ec2f27f28ac043cf886e
URL: https://gitlab.winehq.org/wine/wine/-/commit/2a097acb0d3df6ebd459ec2f27f28a…
Author: Bastien Orivel <eijebong(a)bananium.fr>
Date: Thu Feb 3 11:28:27 2022 +0100
ws2_32: Fix setsockopt(TCP_NODELAY) when optlen is less than 4.
According to MSDN [1], the `TCP_NODELAY` parameter should be of type
`BOOL` which is 4 bytes. Due to a bug [2] in rustc passing a byte
instead of an int, any program written in rust that tries to set that
option on a socket will fail with a "Invalid parameter supplied" error.
Turns out that setsockopt on linux does not want optlen to be less than
4 bytes [3].
Windows' behavior is the following:
- For optlen <= 0, return SOCKET_ERROR and set last error to WSAEFAULT
- For optlen > 0, ignore the optlen value and set the TCP_NODELAY value
to one if the first byte of the given optvalue is not 0.
This will fix any rust program using the hyper library to do HTTP
requests.
[1]: https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-tcp-socket-o…
[2]: https://github.com/rust-lang/rust/blob/44593aeb1387b1be355aeaf0040d5927bd80…
[3]: https://github.com/torvalds/linux/blob/d58071a8a76d779eedab38033ae4c821c302…
Signed-off-by: Bastien Orivel <eijebong(a)bananium.fr>
Signed-off-by: Zebediah Figura <zfigura(a)codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard(a)winehq.org>
(cherry picked from commit d6ea38f32dfd3edbe107a255c37e9f7f3da06ae7)
Signed-off-by: Michael Stefaniuc <mstefani(a)winehq.org>
---
dlls/ws2_32/socket.c | 10 ++++++--
dlls/ws2_32/tests/sock.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 71 insertions(+), 2 deletions(-)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c
index a75aaee68be..4b3ca3c2507 100644
--- a/dlls/ws2_32/socket.c
+++ b/dlls/ws2_32/socket.c
@@ -2903,8 +2903,14 @@ int WINAPI setsockopt( SOCKET s, int level, int optname, const char *optval, int
switch(optname)
{
case TCP_NODELAY:
- return server_setsockopt( s, IOCTL_AFD_WINE_SET_TCP_NODELAY, optval, optlen );
-
+ {
+ INT nodelay = *optval;
+ if (optlen <= 0) {
+ SetLastError(WSAEFAULT);
+ return SOCKET_ERROR;
+ }
+ return server_setsockopt( s, IOCTL_AFD_WINE_SET_TCP_NODELAY, (char*)&nodelay, sizeof(nodelay) );
+ }
default:
FIXME("Unknown IPPROTO_TCP optname 0x%08x\n", optname);
SetLastError(WSAENOPROTOOPT);
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c
index 054e597b719..09ec6009c3f 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -1259,6 +1259,69 @@ static void test_set_getsockopt(void)
ok(err == SOCKET_ERROR && WSAGetLastError() == WSAEFAULT,
"got %d with %d (expected SOCKET_ERROR with WSAEFAULT)\n", err, WSAGetLastError());
+ /* TCP_NODELAY: optlen doesn't matter on windows, it should work with any positive value */
+ size = sizeof(value);
+
+ value = 1;
+ err = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, 1);
+ ok (!err, "setsockopt TCP_NODELAY failed with optlen == 1\n");
+ value = 0xff;
+ err = getsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, &size);
+ ok(!err, "getsockopt TCP_NODELAY failed\n");
+ ok(value == 1, "TCP_NODELAY should be 1\n");
+ value = 0;
+ err = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, sizeof(value));
+ ok(!err, "Failed to reset TCP_NODELAY to 0\n");
+
+ value = 1;
+ err = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, 4);
+ ok (!err, "setsockopt TCP_NODELAY failed with optlen == 4\n");
+ value = 0xff;
+ err = getsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, &size);
+ ok(!err, "getsockopt TCP_NODELAY failed\n");
+ ok(value == 1, "TCP_NODELAY should be 1\n");
+ value = 0;
+ err = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, sizeof(value));
+ ok(!err, "Failed to reset TCP_NODELAY to 0\n");
+
+ value = 1;
+ err = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, 42);
+ ok (!err, "setsockopt TCP_NODELAY failed with optlen == 42\n");
+ value = 0xff;
+ err = getsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, &size);
+ ok(!err, "getsockopt TCP_NODELAY failed\n");
+ ok(value == 1, "TCP_NODELAY should be 1\n");
+ value = 0;
+ err = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, sizeof(value));
+ ok(!err, "Failed to reset TCP_NODELAY to 0\n");
+
+ value = 1;
+ err = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, 0);
+ ok(err == SOCKET_ERROR && WSAGetLastError() == WSAEFAULT,
+ "got %d with %d (expected SOCKET_ERROR with WSAEFAULT)\n", err, WSAGetLastError());
+ value = 0xff;
+ err = getsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, &size);
+ ok(!err, "getsockopt TCP_NODELAY failed\n");
+ ok(!value, "TCP_NODELAY should be 0\n");
+
+ value = 1;
+ err = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, -1);
+ /* On win 10 pro, this sets the error to WSAENOBUFS instead of WSAEFAULT */
+ ok(err == SOCKET_ERROR && (WSAGetLastError() == WSAEFAULT || WSAGetLastError() == WSAENOBUFS),
+ "got %d with %d (expected SOCKET_ERROR with either WSAEFAULT or WSAENOBUFS)\n", err, WSAGetLastError());
+ value = 0xff;
+ err = getsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, &size);
+ ok(!err, "getsockopt TCP_NODELAY failed\n");
+ ok(!value, "TCP_NODELAY should be 0\n");
+
+ value = 0x100;
+ err = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, 4);
+ ok (!err, "setsockopt TCP_NODELAY failed with optlen == 4 and optvalue = 0x100\n");
+ value = 0xff;
+ err = getsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, &size);
+ ok(!err, "getsockopt TCP_NODELAY failed\n");
+ ok(!value, "TCP_NODELAY should be 0\n");
+
/* Test for erroneously passing a value instead of a pointer as optval */
size = sizeof(char);
err = setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)1, size);