Signed-off-by: Paul Gofman pgofman@codeweavers.com --- The aim of the patchset is to fix setting and getting 32bit sized socket options when the beaviour is different between Windows and Linux setsockopt() and getsockopt(). That behaviour partially regressed on the move from ws2_32 to ntdll: setsockopt() had the code which would adjust the size for option with the provided length less than 4 bytes (which helped, e. g., SO_REUSEADDR and SO_KEEPALIVE and Killing Floor 2 depended on that). ALthough, as my tests show, that was not universally correct across all the options and the details of getsockopt() were also different. I have another 16 patches on top of this series addressing the other options.
dlls/ws2_32/tests/sock.c | 86 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 6b6267dc153..0eece82bfb1 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -1155,8 +1155,27 @@ static const LINGER linger_testvals[] = {
static void test_set_getsockopt(void) { + static struct + { + int af; + int type; + int level; + int optname; + BOOL accepts_short_len; + unsigned int sizes[3]; + DWORD values[3]; + } + test_optsize[] = + { + {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_RCVTIMEO, FALSE, {1, 2, 4}}, + {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_SNDTIMEO, FALSE, {1, 2, 4}}, + {AF_INET, SOCK_DGRAM, IPPROTO_IP, IP_MULTICAST_LOOP, TRUE, {1, 1, 4}}, + {AF_INET, SOCK_DGRAM, IPPROTO_IP, IP_MULTICAST_TTL, TRUE, {1, 1, 4}}, + {AF_INET, SOCK_DGRAM, IPPROTO_IP, IP_TOS, TRUE, {1, 1, 4}}, + {AF_INET, SOCK_DGRAM, IPPROTO_IP, IP_TTL, TRUE, {1, 1, 4}}, + }; SOCKET s, s2; - int i, err, lasterr; + int i, j, err, lasterr; int timeout; LINGER lingval; int size; @@ -1375,6 +1394,71 @@ static void test_set_getsockopt(void) err, WSAGetLastError());
closesocket(s); + + /* Test option short length. */ + for (i = 0; i < ARRAY_SIZE(test_optsize); ++i) + { + s2 = socket( test_optsize[i].af, test_optsize[i].type, 0 ); + ok(s2 != INVALID_SOCKET, "socket() failed error %d\n", WSAGetLastError()); + for (j = 0; j < 3; ++j) + { + DWORD expected_last_error, expected_value, expected_size, save_value; + int expected_err; + + winetest_push_context("i %u, level %d, optname %d, len %u", + i, test_optsize[i].level, test_optsize[i].optname, 1 << j); + + size = sizeof(save_value); + err = getsockopt(s2, test_optsize[i].level, test_optsize[i].optname, (char*)&save_value, &size); + ok(!err, "Unexpected getsockopt result %d.\n", err); + + if (test_optsize[i].accepts_short_len || j == 2) + { + expected_err = 0; + expected_last_error = ERROR_SUCCESS; + expected_size = test_optsize[i].sizes[j] ? test_optsize[i].sizes[j] : 4; + if (test_optsize[i].values[j]) + expected_value = test_optsize[i].values[j]; + else if (expected_size == 4) + expected_value = 1; + else + expected_value = (0xdeadbeef & ~((1 << expected_size * 8) - 1)) | 1; + } + else + { + expected_err = -1; + expected_last_error = WSAEFAULT; + expected_size = test_optsize[i].sizes[j]; + if (test_optsize[i].values[j]) + expected_value = test_optsize[i].values[j]; + else + expected_value = 0xdeadbeef; + } + + value = 1; + SetLastError(0xdeadbeef); + err = setsockopt(s2, test_optsize[i].level, test_optsize[i].optname, (char*)&value, 1 << j); + ok(err == expected_err, "Unexpected setsockopt result %d.\n", err); + ok(WSAGetLastError() == expected_last_error, "Unexpected WSAGetLastError() %u.\n", WSAGetLastError()); + + value = 0xdeadbeef; + SetLastError(0xdeadbeef); + size = 1 << j; + err = getsockopt(s2, test_optsize[i].level, test_optsize[i].optname, (char*)&value, &size); + ok(err == expected_err, "Unexpected getsockopt result %d.\n", err); + ok(WSAGetLastError() == expected_last_error, "Unexpected WSAGetLastError() %u.\n", WSAGetLastError()); + ok(value == expected_value, "Got unexpected value %#x, expected %#x.\n", value, expected_value); + ok(size == expected_size, "Got unexpected size %u, expected %u.\n", size, expected_size); + + err = setsockopt(s2, test_optsize[i].level, test_optsize[i].optname, + (char*)&save_value, sizeof(save_value)); + ok(!err, "Unexpected getsockopt result %d.\n", err); + + winetest_pop_context(); + } + closesocket(s2); + } + /* Test with the closed socket */ SetLastError(0xdeadbeef); size = sizeof(i);
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/ws2_32/socket.c | 15 ++++++++++++--- dlls/ws2_32/tests/sock.c | 1 + 2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index e5e29107853..c9afbaa8eba 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -1412,7 +1412,9 @@ int WINAPI getsockopt( SOCKET s, int level, int optname, char *optval, int *optl return server_getsockopt( s, IOCTL_AFD_WINE_GET_SO_ACCEPTCONN, optval, optlen );
case SO_BROADCAST: - return server_getsockopt( s, IOCTL_AFD_WINE_GET_SO_BROADCAST, optval, optlen ); + ret = server_getsockopt( s, IOCTL_AFD_WINE_GET_SO_BROADCAST, optval, optlen ); + if (!ret && *optlen < sizeof(DWORD)) *optlen = 1; + return ret;
case SO_BSP_STATE: { @@ -2769,6 +2771,8 @@ static int server_setsockopt( SOCKET s, ULONG code, const char *optval, int optl */ int WINAPI setsockopt( SOCKET s, int level, int optname, const char *optval, int optlen ) { + DWORD value = 0; + TRACE( "socket %#Ix, %s, optval %s, optlen %d\n", s, debugstr_sockopt(level, optname), debugstr_optval(optval, optlen), optlen );
@@ -2785,8 +2789,13 @@ int WINAPI setsockopt( SOCKET s, int level, int optname, const char *optval, int switch(optname) { case SO_BROADCAST: - return server_setsockopt( s, IOCTL_AFD_WINE_SET_SO_BROADCAST, optval, optlen ); - + if (optlen <= 0) + { + SetLastError(WSAEFAULT); + return SOCKET_ERROR; + } + memcpy( &value, optval, min( optlen, sizeof(value) )); + return server_setsockopt( s, IOCTL_AFD_WINE_SET_SO_BROADCAST, (char *)&value, sizeof(value) ); case SO_DONTLINGER: { struct linger linger; diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 0eece82bfb1..56ef78ad040 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -1167,6 +1167,7 @@ static void test_set_getsockopt(void) } test_optsize[] = { + {AF_INET, SOCK_DGRAM, SOL_SOCKET, SO_BROADCAST, TRUE, {1, 1, 4}, {0, 0xdead0001, 0}}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_RCVTIMEO, FALSE, {1, 2, 4}}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_SNDTIMEO, FALSE, {1, 2, 4}}, {AF_INET, SOCK_DGRAM, IPPROTO_IP, IP_MULTICAST_LOOP, TRUE, {1, 1, 4}},
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/ws2_32/socket.c | 12 +++++++----- dlls/ws2_32/tests/sock.c | 1 + 2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index c9afbaa8eba..d862e0396d1 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -1481,11 +1481,12 @@ int WINAPI getsockopt( SOCKET s, int level, int optname, char *optval, int *optl
case SO_DONTLINGER: { - struct linger linger; + LINGER linger; int len = sizeof(linger); + BOOL value; int ret;
- if (!optlen || *optlen < sizeof(BOOL)|| !optval) + if (!optlen || *optlen < 1 || !optval) { SetLastError(WSAEFAULT); return SOCKET_ERROR; @@ -1493,8 +1494,9 @@ int WINAPI getsockopt( SOCKET s, int level, int optname, char *optval, int *optl
if (!(ret = getsockopt( s, SOL_SOCKET, SO_LINGER, (char *)&linger, &len ))) { - *(BOOL *)optval = !linger.l_onoff; - *optlen = sizeof(BOOL); + value = !linger.l_onoff; + memcpy( optval, &value, min( sizeof(BOOL), *optlen )); + *optlen = *optlen >= sizeof(BOOL) ? sizeof(BOOL) : 1; } return ret; } @@ -2798,7 +2800,7 @@ int WINAPI setsockopt( SOCKET s, int level, int optname, const char *optval, int return server_setsockopt( s, IOCTL_AFD_WINE_SET_SO_BROADCAST, (char *)&value, sizeof(value) ); case SO_DONTLINGER: { - struct linger linger; + LINGER linger;
if (!optval) { diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 56ef78ad040..5d8c1b169e1 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -1168,6 +1168,7 @@ static void test_set_getsockopt(void) test_optsize[] = { {AF_INET, SOCK_DGRAM, SOL_SOCKET, SO_BROADCAST, TRUE, {1, 1, 4}, {0, 0xdead0001, 0}}, + {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_DONTLINGER, TRUE, {1, 1, 4}, {0, 0xdead0001, 0}}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_RCVTIMEO, FALSE, {1, 2, 4}}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_SNDTIMEO, FALSE, {1, 2, 4}}, {AF_INET, SOCK_DGRAM, IPPROTO_IP, IP_MULTICAST_LOOP, TRUE, {1, 1, 4}},
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/ws2_32/socket.c | 8 +++++--- dlls/ws2_32/tests/sock.c | 1 + 2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index d862e0396d1..942a7a65176 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -1504,13 +1504,14 @@ int WINAPI getsockopt( SOCKET s, int level, int optname, char *optval, int *optl /* As mentioned in setsockopt, Windows ignores this, so we * always return true here */ case SO_DONTROUTE: - if (!optlen || *optlen < sizeof(BOOL) || !optval) + if (!optlen || *optlen < 1 || !optval) { SetLastError(WSAEFAULT); return SOCKET_ERROR; } - *(BOOL *)optval = TRUE; - *optlen = sizeof(BOOL); + *optval = TRUE; + *optlen = 1; + SetLastError( ERROR_SUCCESS ); return 0;
case SO_ERROR: @@ -2851,6 +2852,7 @@ int WINAPI setsockopt( SOCKET s, int level, int optname, const char *optval, int * socket. According to MSDN, this option is silently ignored.*/ case SO_DONTROUTE: TRACE("Ignoring SO_DONTROUTE\n"); + SetLastError( ERROR_SUCCESS ); return 0;
/* Stops two sockets from being bound to the same port. Always happens diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 5d8c1b169e1..bfc688a8422 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -1169,6 +1169,7 @@ static void test_set_getsockopt(void) { {AF_INET, SOCK_DGRAM, SOL_SOCKET, SO_BROADCAST, TRUE, {1, 1, 4}, {0, 0xdead0001, 0}}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_DONTLINGER, TRUE, {1, 1, 4}, {0, 0xdead0001, 0}}, + {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_DONTROUTE, TRUE, {1, 1, 1}}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_RCVTIMEO, FALSE, {1, 2, 4}}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_SNDTIMEO, FALSE, {1, 2, 4}}, {AF_INET, SOCK_DGRAM, IPPROTO_IP, IP_MULTICAST_LOOP, TRUE, {1, 1, 4}},
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/ws2_32/socket.c | 14 +++++++++++++- dlls/ws2_32/tests/sock.c | 1 + 2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 942a7a65176..696e337bfdd 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -1518,6 +1518,12 @@ int WINAPI getsockopt( SOCKET s, int level, int optname, char *optval, int *optl return server_getsockopt( s, IOCTL_AFD_WINE_GET_SO_ERROR, optval, optlen );
case SO_KEEPALIVE: + if (!optlen || *optlen < 1 || !optval) + { + SetLastError(WSAEFAULT); + return SOCKET_ERROR; + } + *optlen = 1; return server_getsockopt( s, IOCTL_AFD_WINE_GET_SO_KEEPALIVE, optval, optlen );
case SO_LINGER: @@ -2820,7 +2826,13 @@ int WINAPI setsockopt( SOCKET s, int level, int optname, const char *optval, int return -1;
case SO_KEEPALIVE: - return server_setsockopt( s, IOCTL_AFD_WINE_SET_SO_KEEPALIVE, optval, optlen ); + if (optlen <= 0) + { + SetLastError(WSAEFAULT); + return SOCKET_ERROR; + } + memcpy( &value, optval, min( optlen, sizeof(value) )); + return server_setsockopt( s, IOCTL_AFD_WINE_SET_SO_KEEPALIVE, (char *)&value, sizeof(value) );
case SO_LINGER: return server_setsockopt( s, IOCTL_AFD_WINE_SET_SO_LINGER, optval, optlen ); diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index bfc688a8422..5850bf38f1b 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -1169,6 +1169,7 @@ static void test_set_getsockopt(void) { {AF_INET, SOCK_DGRAM, SOL_SOCKET, SO_BROADCAST, TRUE, {1, 1, 4}, {0, 0xdead0001, 0}}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_DONTLINGER, TRUE, {1, 1, 4}, {0, 0xdead0001, 0}}, + {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_KEEPALIVE, TRUE, {1, 1, 1}}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_DONTROUTE, TRUE, {1, 1, 1}}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_RCVTIMEO, FALSE, {1, 2, 4}}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_SNDTIMEO, FALSE, {1, 2, 4}},
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/ws2_32/socket.c | 7 +++++++ dlls/ws2_32/tests/sock.c | 1 + 2 files changed, 8 insertions(+)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 696e337bfdd..32d06dc11b4 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -1534,6 +1534,8 @@ int WINAPI getsockopt( SOCKET s, int level, int optname, char *optval, int *optl /* struct linger and LINGER have different sizes */ if (!optlen || *optlen < sizeof(LINGER) || !optval) { + if (optlen && *optlen > 0 && optval) + memset( optval, 0, *optlen ); SetLastError(WSAEFAULT); return SOCKET_ERROR; } @@ -2835,6 +2837,11 @@ int WINAPI setsockopt( SOCKET s, int level, int optname, const char *optval, int return server_setsockopt( s, IOCTL_AFD_WINE_SET_SO_KEEPALIVE, (char *)&value, sizeof(value) );
case SO_LINGER: + if (!optlen || optlen < sizeof(LINGER) || !optval) + { + SetLastError(WSAEFAULT); + return SOCKET_ERROR; + } return server_setsockopt( s, IOCTL_AFD_WINE_SET_SO_LINGER, optval, optlen );
case SO_OOBINLINE: diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 5850bf38f1b..9a3e1994f49 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -1171,6 +1171,7 @@ static void test_set_getsockopt(void) {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_DONTLINGER, TRUE, {1, 1, 4}, {0, 0xdead0001, 0}}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_KEEPALIVE, TRUE, {1, 1, 1}}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_DONTROUTE, TRUE, {1, 1, 1}}, + {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_LINGER, FALSE, {1, 2, 4}, {0xdeadbe00, 0xdead0000}}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_RCVTIMEO, FALSE, {1, 2, 4}}, {AF_INET, SOCK_STREAM, SOL_SOCKET, SO_SNDTIMEO, FALSE, {1, 2, 4}}, {AF_INET, SOCK_DGRAM, IPPROTO_IP, IP_MULTICAST_LOOP, TRUE, {1, 1, 4}},