[PATCH v2] ws2_32: hardcode optlen to 4 for setsockopt in the TCP_NODELAY case

Bastien Orivel eijebong at bananium.fr
Mon Nov 29 17:13:38 CST 2021


According to MSDN [1], `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]. Since any value seems to be working on windows (tried with
  1, 4, 8, 42, 1337), I hardcoded the length to be 4 on the wine side.

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-options
[2]: https://github.com/rust-lang/rust/blob/44593aeb1387b1be355aeaf0040d5927bd80f060/library/std/src/sys/windows/net.rs#L470
[3]: https://github.com/torvalds/linux/blob/d58071a8a76d779eedab38033ae4c821c30295a5/net/ipv4/tcp.c#L3419-L3420

Signed-off-by: Bastien Orivel <eijebong at bananium.fr>
---
 dlls/ws2_32/socket.c     |  3 ++-
 dlls/ws2_32/tests/sock.c | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c
index 2db441bee3c..b74276b5239 100644
--- a/dlls/ws2_32/socket.c
+++ b/dlls/ws2_32/socket.c
@@ -2868,7 +2868,8 @@ 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;
+            return server_setsockopt( s, IOCTL_AFD_WINE_SET_TCP_NODELAY, (char*)&nodelay, sizeof(nodelay) );
 
         default:
             FIXME("Unknown IPPROTO_TCP optname 0x%08x\n", optname);
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c
index 02713a7c625..87ced97c314 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -1259,6 +1259,41 @@ 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 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");
+    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");
+
+    err = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, 4);
+    ok (!err, "setsockopt TCP_NODELAY failed with optlen == 4\n");
+    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");
+
+    err = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, 42);
+    ok (!err, "setsockopt TCP_NODELAY failed with optlen == 42\n");
+    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");
+
+    err = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, -1);
+    ok (!err, "setsockopt TCP_NODELAY failed with optlen == -1\n");
+    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 = 0x100;
+    err = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&value, 4);
+    ok (!err, "setsockopt TCP_NODELAY failed with optlen == 4 and optvalue = 0x100\n");
+    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);
-- 
2.34.0




More information about the wine-devel mailing list