Windows supports none of these options on TCP. Linux supports all of them on TCP. Mac OS supports some of them on TCP, but sets EOPNOTSUPP instead of EINVAL for the ones that it doesn't support. So, Wine needs to do its own check for whether the option may be used with the socket.
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- v5: - Allow these options to work with raw sockets in addition to UDP sockets - Whether getting or setting the option, always do the check - Don't touch the IO status block --- dlls/ntdll/unix/socket.c | 99 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 1ac4365c012..2382794a830 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -1179,7 +1179,7 @@ static NTSTATUS do_getsockopt( HANDLE handle, IO_STATUS_BLOCK *io, int level, ret = getsockopt( fd, level, option, out_buffer, &len ); if (needs_close) close( fd ); if (ret) return sock_errno_to_status( errno ); - io->Information = len; + if (io) io->Information = len; return STATUS_SUCCESS; }
@@ -1200,6 +1200,15 @@ static NTSTATUS do_setsockopt( HANDLE handle, IO_STATUS_BLOCK *io, int level, }
+static int get_sock_type( HANDLE handle ) +{ + int sock_type; + if (do_getsockopt( handle, NULL, SOL_SOCKET, SO_TYPE, &sock_type, sizeof(sock_type) ) != STATUS_SUCCESS) + return -1; + return sock_type; +} + + NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, IO_STATUS_BLOCK *io, ULONG code, void *in_buffer, ULONG in_size, void *out_buffer, ULONG out_size ) { @@ -1767,22 +1776,46 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc #endif
case IOCTL_AFD_WINE_GET_IP_MULTICAST_IF: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_getsockopt( handle, io, IPPROTO_IP, IP_MULTICAST_IF, out_buffer, out_size ); + }
case IOCTL_AFD_WINE_SET_IP_MULTICAST_IF: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_setsockopt( handle, io, IPPROTO_IP, IP_MULTICAST_IF, in_buffer, in_size ); + }
case IOCTL_AFD_WINE_GET_IP_MULTICAST_LOOP: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_getsockopt( handle, io, IPPROTO_IP, IP_MULTICAST_LOOP, out_buffer, out_size ); + }
case IOCTL_AFD_WINE_SET_IP_MULTICAST_LOOP: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_setsockopt( handle, io, IPPROTO_IP, IP_MULTICAST_LOOP, in_buffer, in_size ); + }
case IOCTL_AFD_WINE_GET_IP_MULTICAST_TTL: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_getsockopt( handle, io, IPPROTO_IP, IP_MULTICAST_TTL, out_buffer, out_size ); + }
case IOCTL_AFD_WINE_SET_IP_MULTICAST_TTL: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_setsockopt( handle, io, IPPROTO_IP, IP_MULTICAST_TTL, in_buffer, in_size ); + }
case IOCTL_AFD_WINE_GET_IP_OPTIONS: return do_getsockopt( handle, io, IPPROTO_IP, IP_OPTIONS, out_buffer, out_size ); @@ -1792,16 +1825,32 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc
#ifdef IP_PKTINFO case IOCTL_AFD_WINE_GET_IP_PKTINFO: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_getsockopt( handle, io, IPPROTO_IP, IP_PKTINFO, out_buffer, out_size ); + }
case IOCTL_AFD_WINE_SET_IP_PKTINFO: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_setsockopt( handle, io, IPPROTO_IP, IP_PKTINFO, in_buffer, in_size ); + } #elif defined(IP_RECVDSTADDR) case IOCTL_AFD_WINE_GET_IP_PKTINFO: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_getsockopt( handle, io, IPPROTO_IP, IP_RECVDSTADDR, out_buffer, out_size ); + }
case IOCTL_AFD_WINE_SET_IP_PKTINFO: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_setsockopt( handle, io, IPPROTO_IP, IP_RECVDSTADDR, in_buffer, in_size ); + } #endif
case IOCTL_AFD_WINE_GET_IP_TOS: @@ -1890,45 +1939,93 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc #endif
case IOCTL_AFD_WINE_GET_IPV6_MULTICAST_HOPS: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_getsockopt( handle, io, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, out_buffer, out_size ); + }
case IOCTL_AFD_WINE_SET_IPV6_MULTICAST_HOPS: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_setsockopt( handle, io, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, in_buffer, in_size ); + }
case IOCTL_AFD_WINE_GET_IPV6_MULTICAST_IF: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_getsockopt( handle, io, IPPROTO_IPV6, IPV6_MULTICAST_IF, out_buffer, out_size ); + }
case IOCTL_AFD_WINE_SET_IPV6_MULTICAST_IF: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_setsockopt( handle, io, IPPROTO_IPV6, IPV6_MULTICAST_IF, in_buffer, in_size ); + }
case IOCTL_AFD_WINE_GET_IPV6_MULTICAST_LOOP: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_getsockopt( handle, io, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, out_buffer, out_size ); + }
case IOCTL_AFD_WINE_SET_IPV6_MULTICAST_LOOP: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_setsockopt( handle, io, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, in_buffer, in_size ); + }
#ifdef IPV6_RECVHOPLIMIT case IOCTL_AFD_WINE_GET_IPV6_RECVHOPLIMIT: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_getsockopt( handle, io, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, out_buffer, out_size ); + }
case IOCTL_AFD_WINE_SET_IPV6_RECVHOPLIMIT: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_setsockopt( handle, io, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, in_buffer, in_size ); + } #endif
#ifdef IPV6_RECVPKTINFO case IOCTL_AFD_WINE_GET_IPV6_RECVPKTINFO: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_getsockopt( handle, io, IPPROTO_IPV6, IPV6_RECVPKTINFO, out_buffer, out_size ); + }
case IOCTL_AFD_WINE_SET_IPV6_RECVPKTINFO: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_setsockopt( handle, io, IPPROTO_IPV6, IPV6_RECVPKTINFO, in_buffer, in_size ); + } #endif
#ifdef IPV6_RECVTCLASS case IOCTL_AFD_WINE_GET_IPV6_RECVTCLASS: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_getsockopt( handle, io, IPPROTO_IPV6, IPV6_RECVTCLASS, out_buffer, out_size ); + }
case IOCTL_AFD_WINE_SET_IPV6_RECVTCLASS: + { + int sock_type = get_sock_type( handle ); + if (sock_type != SOCK_DGRAM && sock_type != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_setsockopt( handle, io, IPPROTO_IPV6, IPV6_RECVTCLASS, in_buffer, in_size ); + } #endif
case IOCTL_AFD_WINE_GET_IPV6_UNICAST_HOPS:
This reverts most of commit e0013193044911140181ee69de4e7c721c6d0aa0.
My previous diagnosis was incorrect: Windows does support both getting and setting IP_HDRINCL, but only on raw sockets. Very old versions of Windows also accept it on UDP sockets.
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- The final patch in this series now includes tests that would have caught this mistake. Similar mistakes will not happen again.
We could support IP_HDRINCL on UDP sockets in Wine for perfect compatibility with Windows XP, but then we'd have to specially mark it as broken in the tests. Doesn't seem worth it. --- dlls/ntdll/unix/socket.c | 5 +++++ dlls/ws2_32/socket.c | 7 +++---- include/wine/afd.h | 1 + 3 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 2382794a830..8469def786a 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -1771,7 +1771,12 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc return do_setsockopt( handle, io, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, in_buffer, in_size );
#ifdef IP_HDRINCL + case IOCTL_AFD_WINE_GET_IP_HDRINCL: + if (get_sock_type( handle ) != SOCK_RAW) return STATUS_INVALID_PARAMETER; + return do_getsockopt( handle, io, IPPROTO_IP, IP_HDRINCL, out_buffer, out_size ); + case IOCTL_AFD_WINE_SET_IP_HDRINCL: + if (get_sock_type( handle ) != SOCK_RAW) return STATUS_INVALID_PARAMETER; return do_setsockopt( handle, io, IPPROTO_IP, IP_HDRINCL, in_buffer, in_size ); #endif
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index c991d212a07..9a45124cd9e 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -1677,6 +1677,9 @@ int WINAPI getsockopt( SOCKET s, int level, int optname, char *optval, int *optl case IP_DONTFRAGMENT: return server_getsockopt( s, IOCTL_AFD_WINE_GET_IP_DONTFRAGMENT, optval, optlen );
+ case IP_HDRINCL: + return server_getsockopt( s, IOCTL_AFD_WINE_GET_IP_HDRINCL, optval, optlen ); + case IP_MULTICAST_IF: return server_getsockopt( s, IOCTL_AFD_WINE_GET_IP_MULTICAST_IF, optval, optlen );
@@ -1701,10 +1704,6 @@ int WINAPI getsockopt( SOCKET s, int level, int optname, char *optval, int *optl case IP_UNICAST_IF: return server_getsockopt( s, IOCTL_AFD_WINE_GET_IP_UNICAST_IF, optval, optlen );
- case IP_HDRINCL: - SetLastError( WSAEINVAL ); - return -1; - default: FIXME( "unrecognized IP option %u\n", optname ); /* fall through */ diff --git a/include/wine/afd.h b/include/wine/afd.h index 43e140c91c6..49bbcccd3af 100644 --- a/include/wine/afd.h +++ b/include/wine/afd.h @@ -187,6 +187,7 @@ struct afd_get_events_params #define IOCTL_AFD_WINE_SET_IP_DONTFRAGMENT WINE_AFD_IOC(243) #define IOCTL_AFD_WINE_SET_IP_DROP_MEMBERSHIP WINE_AFD_IOC(244) #define IOCTL_AFD_WINE_SET_IP_DROP_SOURCE_MEMBERSHIP WINE_AFD_IOC(245) +#define IOCTL_AFD_WINE_GET_IP_HDRINCL WINE_AFD_IOC(246) #define IOCTL_AFD_WINE_SET_IP_HDRINCL WINE_AFD_IOC(247) #define IOCTL_AFD_WINE_GET_IP_MULTICAST_IF WINE_AFD_IOC(248) #define IOCTL_AFD_WINE_SET_IP_MULTICAST_IF WINE_AFD_IOC(249)
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=95813
Your paranoid android.
=== debiant2 (32 bit Chinese:China report) ===
ws2_32: sock.c:1135: Test failed: wait failed, error 258
Signed-off-by: Zebediah Figura zfigura@codeweavers.com
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- Since these constants are specific to Windows, they don't need the WS_ prefix. --- include/ws2ipdef.h | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/include/ws2ipdef.h b/include/ws2ipdef.h index 0344a193dc1..adc534dcb32 100644 --- a/include/ws2ipdef.h +++ b/include/ws2ipdef.h @@ -329,6 +329,11 @@ typedef struct WS(in6_pktinfo) { #define WS_TCP_DELAY_FIN_ACK 13 #endif /* USE_WS_PREFIX */
+#define PROTECTION_LEVEL_UNRESTRICTED 10 +#define PROTECTION_LEVEL_EDGERESTRICTED 20 +#define PROTECTION_LEVEL_RESTRICTED 30 +#define PROTECTION_LEVEL_DEFAULT ((UINT)-1) + #ifndef USE_WS_PREFIX #define INET_ADDRSTRLEN 22 #define INET6_ADDRSTRLEN 65
Signed-off-by: Zebediah Figura zfigura@codeweavers.com
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- dlls/ws2_32/socket.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 9a45124cd9e..83904f29626 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -1732,6 +1732,17 @@ int WINAPI getsockopt( SOCKET s, int level, int optname, char *optval, int *optl case IPV6_MULTICAST_LOOP: return server_getsockopt( s, IOCTL_AFD_WINE_GET_IPV6_MULTICAST_LOOP, optval, optlen );
+ case IPV6_PROTECTION_LEVEL: + if (!optlen || *optlen < sizeof(UINT) || !optval) + { + SetLastError( WSAEFAULT ); + return -1; + } + *optlen = sizeof(UINT); + *optval = PROTECTION_LEVEL_UNRESTRICTED; + FIXME("IPV6_PROTECTION_LEVEL is ignored!\n"); + return 0; + case IPV6_PKTINFO: return server_getsockopt( s, IOCTL_AFD_WINE_GET_IPV6_RECVPKTINFO, optval, optlen );
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=95815
Your paranoid android.
=== debiant2 (32 bit report) ===
ws2_32: sock.c:1135: Test failed: wait failed, error 258
Signed-off-by: Zebediah Figura zfigura@codeweavers.com
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- v5: - Add IP_ORIGINAL_ARRIVAL_IF to IPv6 UDP tests - Add raw socket tests - Use winetest_(push|pop)_context - Remove second (now redundant) todo flag --- dlls/ws2_32/tests/sock.c | 326 +++++++++++++++++++++++++++++++++++---- 1 file changed, 297 insertions(+), 29 deletions(-)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 51aa0e61282..a8d415c3cea 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -11429,44 +11429,312 @@ static void test_so_debug(void) closesocket(s); }
-static void test_set_only_options(void) +struct sockopt_validity_test { - unsigned int i; - int ret, len; - int value; - SOCKET s; + int opt; + int get_error; + int set_error; + BOOL todo; +};
- static const struct +static void do_sockopt_validity_tests(const char *type, SOCKET sock, int level, + const struct sockopt_validity_test *tests) +{ + char value[256]; + int count, rc, expected_rc, i; + + for (i = 0; tests[i].opt; i++) { - int level; - int option; + winetest_push_context("%s option %i", type, tests[i].opt); + memset(value, 0, sizeof(value)); + count = sizeof(value); + + WSASetLastError(0); + rc = getsockopt(sock, level, tests[i].opt, value, &count); + expected_rc = tests[i].get_error ? SOCKET_ERROR : 0; +todo_wine_if(!tests[i].get_error && tests[i].todo) + ok(rc == expected_rc || broken(rc == SOCKET_ERROR && WSAGetLastError() == WSAENOPROTOOPT), + "expected getsockopt to return %i, got %i\n", expected_rc, rc); +todo_wine_if(tests[i].todo) + ok(WSAGetLastError() == tests[i].get_error || broken(rc == SOCKET_ERROR && WSAGetLastError() == WSAENOPROTOOPT), + "expected getsockopt to set error %i, got %i\n", tests[i].get_error, WSAGetLastError()); + + if (tests[i].get_error) + { + winetest_pop_context(); + continue; + } + + WSASetLastError(0); + rc = setsockopt(sock, level, tests[i].opt, value, count); + expected_rc = tests[i].set_error ? SOCKET_ERROR : 0; +todo_wine_if(!tests[i].set_error && tests[i].todo) + ok(rc == expected_rc || broken(rc == SOCKET_ERROR && WSAGetLastError() == WSAENOPROTOOPT), + "expected setsockopt to return %i, got %i\n", expected_rc, rc); +todo_wine_if(tests[i].todo) + ok(WSAGetLastError() == tests[i].set_error || broken(rc == SOCKET_ERROR && WSAGetLastError() == WSAENOPROTOOPT), + "expected setsockopt to set error %i, got %i\n", tests[i].set_error, WSAGetLastError()); + + winetest_pop_context(); } - tests[] = +} + +static void test_sockopt_validity(void) +{ + static const struct sockopt_validity_test ipv4_tcp_tests[] = + { + { -1, WSAENOPROTOOPT }, + { IP_OPTIONS }, + { IP_HDRINCL, WSAEINVAL }, + { IP_TOS }, + { IP_TTL }, + { IP_MULTICAST_IF, WSAEINVAL }, + { IP_MULTICAST_TTL, WSAEINVAL }, + { IP_MULTICAST_LOOP, WSAEINVAL }, + { IP_ADD_MEMBERSHIP, WSAENOPROTOOPT }, + { IP_DROP_MEMBERSHIP, WSAENOPROTOOPT }, + { IP_DONTFRAGMENT }, + { IP_PKTINFO, WSAEINVAL }, + { IP_RECVTTL, WSAEINVAL, 0, TRUE }, + { IP_RECEIVE_BROADCAST, WSAEINVAL, 0, TRUE }, + { IP_RECVIF, WSAEINVAL, 0, TRUE }, + { IP_RECVDSTADDR, WSAEINVAL, 0, TRUE }, + { IP_IFLIST, 0, 0, TRUE }, + { IP_UNICAST_IF }, + { IP_RTHDR, 0, 0, TRUE }, + { IP_GET_IFLIST, WSAEINVAL, 0, TRUE }, + { IP_RECVRTHDR, WSAEINVAL, 0, TRUE }, + { IP_RECVTCLASS, WSAEINVAL, 0, TRUE }, + { IP_ORIGINAL_ARRIVAL_IF, WSAEINVAL, 0, TRUE }, + { IP_ECN, WSAEINVAL, 0, TRUE }, + { IP_PKTINFO_EX, WSAEINVAL, 0, TRUE }, + { IP_WFP_REDIRECT_RECORDS, WSAEINVAL, 0, TRUE }, + { IP_WFP_REDIRECT_CONTEXT, WSAEINVAL, 0, TRUE }, + { IP_MTU_DISCOVER, 0, WSAEINVAL, TRUE }, + { IP_MTU, WSAENOTCONN, 0, TRUE }, + { IP_RECVERR, WSAEINVAL, 0, TRUE }, + { IP_USER_MTU, 0, 0, TRUE }, + {} + }; + static const struct sockopt_validity_test ipv4_udp_tests[] = + { + { -1, WSAENOPROTOOPT }, + { IP_OPTIONS }, + { IP_HDRINCL, WSAEINVAL }, + { IP_TOS }, + { IP_TTL }, + { IP_MULTICAST_IF }, + { IP_MULTICAST_TTL }, + { IP_MULTICAST_LOOP }, + { IP_ADD_MEMBERSHIP, WSAENOPROTOOPT }, + { IP_DROP_MEMBERSHIP, WSAENOPROTOOPT }, + { IP_DONTFRAGMENT }, + { IP_PKTINFO }, + { IP_RECVTTL, 0, 0, TRUE }, + { IP_RECEIVE_BROADCAST, 0, 0, TRUE }, + { IP_RECVIF, 0, 0, TRUE }, + { IP_RECVDSTADDR, 0, 0, TRUE }, + { IP_IFLIST, 0, 0, TRUE }, + { IP_UNICAST_IF }, + { IP_RTHDR, 0, 0, TRUE }, + { IP_GET_IFLIST, WSAEINVAL, 0, TRUE }, + { IP_RECVRTHDR, 0, 0, TRUE }, + { IP_RECVTCLASS, 0, 0, TRUE }, + { IP_ORIGINAL_ARRIVAL_IF, 0, 0, TRUE }, + { IP_ECN, 0, 0, TRUE }, + { IP_PKTINFO_EX, 0, 0, TRUE }, + { IP_WFP_REDIRECT_RECORDS, 0, 0, TRUE }, + { IP_WFP_REDIRECT_CONTEXT, 0, 0, TRUE }, + { IP_MTU_DISCOVER, 0, WSAEINVAL, TRUE }, + { IP_MTU, WSAENOTCONN, 0, TRUE }, + { IP_RECVERR, 0, 0, TRUE }, + { IP_USER_MTU, 0, 0, TRUE }, + {} + }; + static const struct sockopt_validity_test ipv4_raw_tests[] = + { + { -1, WSAENOPROTOOPT }, + { IP_OPTIONS }, + { IP_HDRINCL, }, + { IP_TOS }, + { IP_TTL }, + { IP_MULTICAST_IF }, + { IP_MULTICAST_TTL }, + { IP_MULTICAST_LOOP }, + { IP_ADD_MEMBERSHIP, WSAENOPROTOOPT }, + { IP_DROP_MEMBERSHIP, WSAENOPROTOOPT }, + { IP_DONTFRAGMENT }, + { IP_PKTINFO }, + { IP_RECVTTL, 0, 0, TRUE }, + { IP_RECEIVE_BROADCAST, 0, 0, TRUE }, + { IP_RECVIF, 0, 0, TRUE }, + { IP_RECVDSTADDR, 0, 0, TRUE }, + { IP_IFLIST, 0, 0, TRUE }, + { IP_UNICAST_IF }, + { IP_RTHDR, 0, 0, TRUE }, + { IP_GET_IFLIST, WSAEINVAL, 0, TRUE }, + { IP_RECVRTHDR, 0, 0, TRUE }, + { IP_RECVTCLASS, 0, 0, TRUE }, + { IP_ORIGINAL_ARRIVAL_IF, 0, 0, TRUE }, + { IP_ECN, 0, 0, TRUE }, + { IP_PKTINFO_EX, 0, 0, TRUE }, + { IP_WFP_REDIRECT_RECORDS, 0, 0, TRUE }, + { IP_WFP_REDIRECT_CONTEXT, 0, 0, TRUE }, + { IP_MTU_DISCOVER, 0, WSAEINVAL, TRUE }, + { IP_MTU, WSAENOTCONN, 0, TRUE }, + { IP_RECVERR, WSAEINVAL, 0, TRUE }, + { IP_USER_MTU, 0, 0, TRUE }, + {} + }; + static const struct sockopt_validity_test ipv6_tcp_tests[] = { - {IPPROTO_IP, IP_ADD_MEMBERSHIP}, - {IPPROTO_IP, IP_DROP_MEMBERSHIP}, - {IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP}, - {IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP}, + { -1, WSAENOPROTOOPT }, + { IPV6_HOPOPTS, 0, 0, TRUE }, + { IPV6_HDRINCL, WSAEINVAL, 0, TRUE }, + { IPV6_UNICAST_HOPS }, + { IPV6_MULTICAST_IF, WSAEINVAL }, + { IPV6_MULTICAST_HOPS, WSAEINVAL }, + { IPV6_MULTICAST_LOOP, WSAEINVAL }, + { IPV6_ADD_MEMBERSHIP, WSAENOPROTOOPT }, + { IPV6_DROP_MEMBERSHIP, WSAENOPROTOOPT }, + { IPV6_DONTFRAG }, + { IPV6_PKTINFO, WSAEINVAL }, + { IPV6_HOPLIMIT, WSAEINVAL }, + { IPV6_PROTECTION_LEVEL }, + { IPV6_RECVIF, WSAEINVAL, 0, TRUE }, + { IPV6_RECVDSTADDR, WSAEINVAL, 0, TRUE }, + { IPV6_V6ONLY }, + { IPV6_IFLIST, 0, 0, TRUE }, + { IPV6_UNICAST_IF }, + { IPV6_RTHDR, 0, 0, TRUE }, + { IPV6_GET_IFLIST, WSAEINVAL, 0, TRUE }, + { IPV6_RECVRTHDR, WSAEINVAL, 0, TRUE }, + { IPV6_RECVTCLASS, WSAEINVAL }, + { IP_ORIGINAL_ARRIVAL_IF, WSAEINVAL, 0, TRUE }, + { IPV6_ECN, WSAEINVAL, 0, TRUE }, + { IPV6_PKTINFO_EX, WSAEINVAL, 0, TRUE }, + { IPV6_WFP_REDIRECT_RECORDS, WSAEINVAL, 0, TRUE }, + { IPV6_WFP_REDIRECT_CONTEXT, WSAEINVAL, 0, TRUE }, + { IPV6_MTU_DISCOVER, 0, WSAEINVAL, TRUE }, + { IPV6_MTU, WSAENOTCONN, 0, TRUE }, + { IPV6_RECVERR, WSAEINVAL, 0, TRUE }, + { IPV6_USER_MTU, 0, 0, TRUE }, + {} }; + static const struct sockopt_validity_test ipv6_udp_tests[] = + { + { -1, WSAENOPROTOOPT }, + { IPV6_HOPOPTS, 0, 0, TRUE }, + { IPV6_HDRINCL, WSAEINVAL, 0, TRUE }, + { IPV6_UNICAST_HOPS }, + { IPV6_MULTICAST_IF }, + { IPV6_MULTICAST_HOPS }, + { IPV6_MULTICAST_LOOP }, + { IPV6_ADD_MEMBERSHIP, WSAENOPROTOOPT }, + { IPV6_DROP_MEMBERSHIP, WSAENOPROTOOPT }, + { IPV6_DONTFRAG }, + { IPV6_PKTINFO }, + { IPV6_HOPLIMIT }, + { IPV6_PROTECTION_LEVEL }, + { IPV6_RECVIF, 0, 0, TRUE }, + { IPV6_RECVDSTADDR, 0, 0, TRUE }, + { IPV6_V6ONLY }, + { IPV6_IFLIST, 0, 0, TRUE }, + { IPV6_UNICAST_IF }, + { IPV6_RTHDR, 0, 0, TRUE }, + { IPV6_GET_IFLIST, WSAEINVAL, 0, TRUE }, + { IPV6_RECVRTHDR, 0, 0, TRUE }, + { IPV6_RECVTCLASS }, + { IP_ORIGINAL_ARRIVAL_IF, 0, 0, TRUE }, + { IPV6_ECN, 0, 0, TRUE }, + { IPV6_PKTINFO_EX, 0, 0, TRUE }, + { IPV6_WFP_REDIRECT_RECORDS, 0, 0, TRUE }, + { IPV6_WFP_REDIRECT_CONTEXT, 0, 0, TRUE }, + { IPV6_MTU_DISCOVER, 0, WSAEINVAL, TRUE }, + { IPV6_MTU, WSAENOTCONN, 0, TRUE }, + { IPV6_RECVERR, 0, 0, TRUE }, + { IPV6_USER_MTU, 0, 0, TRUE }, + {} + }; + static const struct sockopt_validity_test ipv6_raw_tests[] = + { + { -1, WSAENOPROTOOPT }, + { IPV6_HOPOPTS, 0, 0, TRUE }, + { IPV6_HDRINCL, 0, 0, TRUE }, + { IPV6_UNICAST_HOPS }, + { IPV6_MULTICAST_IF }, + { IPV6_MULTICAST_HOPS }, + { IPV6_MULTICAST_LOOP }, + { IPV6_ADD_MEMBERSHIP, WSAENOPROTOOPT }, + { IPV6_DROP_MEMBERSHIP, WSAENOPROTOOPT }, + { IPV6_DONTFRAG }, + { IPV6_PKTINFO }, + { IPV6_HOPLIMIT }, + { IPV6_PROTECTION_LEVEL }, + { IPV6_RECVIF, 0, 0, TRUE }, + { IPV6_RECVDSTADDR, 0, 0, TRUE }, + { IPV6_V6ONLY }, + { IPV6_IFLIST, 0, 0, TRUE }, + { IPV6_UNICAST_IF }, + { IPV6_RTHDR, 0, 0, TRUE }, + { IPV6_GET_IFLIST, WSAEINVAL, 0, TRUE }, + { IPV6_RECVRTHDR, 0, 0, TRUE }, + { IPV6_RECVTCLASS }, + { IP_ORIGINAL_ARRIVAL_IF, 0, 0, TRUE }, + { IPV6_ECN, 0, 0, TRUE }, + { IPV6_PKTINFO_EX, 0, 0, TRUE }, + { IPV6_WFP_REDIRECT_RECORDS, 0, 0, TRUE }, + { IPV6_WFP_REDIRECT_CONTEXT, 0, 0, TRUE }, + { IPV6_MTU_DISCOVER, 0, WSAEINVAL, TRUE }, + { IPV6_MTU, WSAENOTCONN, 0, TRUE }, + { IPV6_RECVERR, WSAEINVAL, 0, TRUE }, + { IPV6_USER_MTU, 0, 0, TRUE }, + {} + }; + SOCKET sock;
- for (i = 0; i < ARRAY_SIZE(tests); ++i) + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + ok(sock != INVALID_SOCKET, "failed to create socket, error %u\n", WSAGetLastError()); + do_sockopt_validity_tests("IPv4 TCP", sock, IPPROTO_IP, ipv4_tcp_tests); + closesocket(sock); + + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + ok(sock != INVALID_SOCKET, "failed to create socket, error %u\n", WSAGetLastError()); + do_sockopt_validity_tests("IPv4 UDP", sock, IPPROTO_IP, ipv4_udp_tests); + closesocket(sock); + + sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (sock == INVALID_SOCKET && WSAGetLastError() == WSAEACCES) { - if (tests[i].level == IPPROTO_IPV6) - { - s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); - if (s == INVALID_SOCKET) continue; - } - else - { - s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - } + skip("Raw IPv4 sockets are not available\n"); + } + else + { + ok(sock != INVALID_SOCKET, "failed to create socket, error %u\n", WSAGetLastError()); + do_sockopt_validity_tests("IPv4 raw", sock, IPPROTO_IP, ipv4_raw_tests); + closesocket(sock); + }
- len = sizeof(value); - ret = getsockopt(s, tests[i].level, tests[i].option, (char *)&value, &len); - ok(ret == -1, "expected failure\n"); - ok(WSAGetLastError() == WSAENOPROTOOPT, "got error %u\n", WSAGetLastError()); + sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + ok(sock != INVALID_SOCKET, "failed to create socket, error %u\n", WSAGetLastError()); + do_sockopt_validity_tests("IPv6 TCP", sock, IPPROTO_IPV6, ipv6_tcp_tests); + closesocket(sock);
- closesocket(s); + sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + ok(sock != INVALID_SOCKET, "failed to create socket, error %u\n", WSAGetLastError()); + do_sockopt_validity_tests("IPv6 UDP", sock, IPPROTO_IPV6, ipv6_udp_tests); + closesocket(sock); + + sock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); + if (sock == INVALID_SOCKET && WSAGetLastError() == WSAEACCES) + { + skip("Raw IPv6 sockets are not available\n"); + } + else + { + ok(sock != INVALID_SOCKET, "failed to create socket, error %u\n", WSAGetLastError()); + do_sockopt_validity_tests("IPv6 raw", sock, IPPROTO_IPV6, ipv6_raw_tests); + closesocket(sock); } }
@@ -11487,7 +11755,7 @@ START_TEST( sock ) test_ipv6_cmsg(); test_extendedSocketOptions(); test_so_debug(); - test_set_only_options(); + test_sockopt_validity();
for (i = 0; i < ARRAY_SIZE(tests); i++) do_test(&tests[i]);
Signed-off-by: Zebediah Figura zfigura@codeweavers.com