Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- The same option apparently causes both the hop limit and the time-to-live to be received. --- include/ws2ipdef.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+)
diff --git a/include/ws2ipdef.h b/include/ws2ipdef.h index d5a4dab6ce0..3025a9c6fe9 100644 --- a/include/ws2ipdef.h +++ b/include/ws2ipdef.h @@ -83,6 +83,7 @@ typedef struct _INTERFACE_INFO #define IP_UNBLOCK_SOURCE 18 #define IP_PKTINFO 19 #define IP_HOPLIMIT 21 +#define IP_RECVTTL 21 #define IP_RECEIVE_BROADCAST 22 #define IP_RECVIF 24 #define IP_RECVDSTADDR 25 @@ -91,7 +92,21 @@ typedef struct _INTERFACE_INFO #define IP_DEL_IFLIST 30 #define IP_UNICAST_IF 31 #define IP_RTHDR 32 +#define IP_GET_IFLIST 33 #define IP_RECVRTHDR 38 +#define IP_TCLASS 39 +#define IP_RECVTCLASS 40 +#define IP_RECVTOS 40 +#define IP_ORIGINAL_ARRIVAL_IF 47 +#define IP_ECN 50 +#define IP_PKTINFO_EX 51 +#define IP_WFP_REDIRECT_RECORDS 60 +#define IP_WFP_REDIRECT_CONTEXT 70 +#define IP_MTU_DISCOVER 71 +#define IP_MTU 73 +#define IP_NRT_INTERFACE 74 +#define IP_RECVERR 75 +#define IP_USER_MTU 76 #else #define WS_IP_OPTIONS 1 #define WS_IP_HDRINCL 2 @@ -109,6 +124,7 @@ typedef struct _INTERFACE_INFO #define WS_IP_UNBLOCK_SOURCE 18 #define WS_IP_PKTINFO 19 #define WS_IP_HOPLIMIT 21 +#define WS_IP_RECVTTL 21 #define WS_IP_RECEIVE_BROADCAST 22 #define WS_IP_RECVIF 24 #define WS_IP_RECVDSTADDR 25 @@ -117,7 +133,21 @@ typedef struct _INTERFACE_INFO #define WS_IP_DEL_IFLIST 30 #define WS_IP_UNICAST_IF 31 #define WS_IP_RTHDR 32 +#define WS_IP_GET_IFLIST 33 #define WS_IP_RECVRTHDR 38 +#define WS_IP_TCLASS 39 +#define WS_IP_RECVTCLASS 40 +#define WS_IP_RECVTOS 40 +#define WS_IP_ORIGINAL_ARRIVAL_IF 47 +#define WS_IP_ECN 50 +#define WS_IP_PKTINFO_EX 51 +#define WS_IP_WFP_REDIRECT_RECORDS 60 +#define WS_IP_WFP_REDIRECT_CONTEXT 70 +#define WS_IP_MTU_DISCOVER 71 +#define WS_IP_MTU 73 +#define WS_IP_NRT_INTERFACE 74 +#define WS_IP_RECVERR 75 +#define WS_IP_USER_MTU 76 #endif /* USE_WS_PREFIX */
typedef struct WS(sockaddr_in6)
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- I'm not sure if IPV6_OPTIONS was ever in an official SDK, but we can just leave it as a synonym for now. --- include/ws2ipdef.h | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/include/ws2ipdef.h b/include/ws2ipdef.h index 3025a9c6fe9..0344a193dc1 100644 --- a/include/ws2ipdef.h +++ b/include/ws2ipdef.h @@ -214,6 +214,7 @@ typedef struct WS(in6_pktinfo) {
#ifndef USE_WS_PREFIX #define IPV6_OPTIONS 1 +#define IPV6_HOPOPTS 1 #define IPV6_HDRINCL 2 #define IPV6_UNICAST_HOPS 4 #define IPV6_MULTICAST_IF 9 @@ -236,6 +237,7 @@ typedef struct WS(in6_pktinfo) { #define IPV6_DEL_IFLIST 30 #define IPV6_UNICAST_IF 31 #define IPV6_RTHDR 32 +#define IPV6_GET_IFLIST 33 #define IPV6_RECVRTHDR 38 #define IPV6_TCLASS 39 #define IPV6_RECVTCLASS 40 @@ -250,6 +252,7 @@ typedef struct WS(in6_pktinfo) { #define IPV6_USER_MTU 76 #else #define WS_IPV6_OPTIONS 1 +#define WS_IPV6_HOPOPTS 1 #define WS_IPV6_HDRINCL 2 #define WS_IPV6_UNICAST_HOPS 4 #define WS_IPV6_MULTICAST_IF 9 @@ -271,6 +274,7 @@ typedef struct WS(in6_pktinfo) { #define WS_IPV6_DEL_IFLIST 30 #define WS_IPV6_UNICAST_IF 31 #define WS_IPV6_RTHDR 32 +#define WS_IPV6_GET_IFLIST 33 #define WS_IPV6_RECVRTHDR 38 #define WS_IPV6_TCLASS 39 #define WS_IPV6_RECVTCLASS 40
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- The idea here is to not have to write new tests every time we hook up a new option. --- dlls/ws2_32/tests/sock.c | 297 +++++++++++++++++++++++++++++++++++---- 1 file changed, 268 insertions(+), 29 deletions(-)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index dd8c83374d0..408afdceb88 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -11413,47 +11413,286 @@ static void test_so_debug(void) closesocket(s); }
-static void test_set_only_options(void) +enum sockopt_validity_flags { - unsigned int i; - int ret, len; - int value; - SOCKET s; + BROKEN = 1, + TODO_OPT = 2, + TODO_ERR = 4 +};
- static const struct - { - int level; - int option; - } - tests[] = - { - {IPPROTO_IP, IP_ADD_MEMBERSHIP}, - {IPPROTO_IP, IP_DROP_MEMBERSHIP}, - {IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP}, - {IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP}, - }; +struct sockopt_validity_test +{ + int opt; + int get_error; + enum sockopt_validity_flags get_flags; + int set_error; + enum sockopt_validity_flags set_flags; +};
- for (i = 0; i < ARRAY_SIZE(tests); ++i) +static void do_sockopt_validity_tests(const char *type, SOCKET sock, int level, + const struct sockopt_validity_test *tests) +{ + static const struct sockopt_validity_test default_test = {0, WSAENOPROTOOPT, 0, WSAENOPROTOOPT, TODO_ERR}; + const struct sockopt_validity_test *test; + char value[256]; + int opt, count, rc, expected_rc, i = 0; + + for (opt = 0; opt < 100; opt++) { - if (tests[i].level == IPPROTO_IPV6) + if (tests[i].opt && opt == tests[i].opt) { - s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); - if (s == INVALID_SOCKET) continue; + test = &tests[i]; + i++; } else { - s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + test = &default_test; }
- 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()); - - closesocket(s); + memset(value, 0, sizeof(value)); + count = sizeof(value); + + WSASetLastError(0); + rc = getsockopt(sock, level, opt, value, &count); + expected_rc = test->get_error ? SOCKET_ERROR : 0; +todo_wine_if(test->get_flags & TODO_OPT) + ok(rc == expected_rc || broken(rc == SOCKET_ERROR && test->get_flags & BROKEN), + "expected getting %s option %i to return %i, got %i\n", type, opt, expected_rc, rc); +todo_wine_if(test->get_flags & TODO_ERR) + ok(WSAGetLastError() == test->get_error || broken(rc == SOCKET_ERROR && test->get_flags & BROKEN), + "expected getting %s option %i to cause error %i, got %i\n", type, opt, test->get_error, WSAGetLastError()); + + WSASetLastError(0); + rc = setsockopt(sock, level, opt, value, count); + expected_rc = test->set_error ? SOCKET_ERROR : 0; +todo_wine_if(test->set_flags & TODO_OPT) + ok(rc == expected_rc || broken(rc == SOCKET_ERROR && test->set_flags & BROKEN), + "expected setting %s option %i to return %i, got %i\n", type, opt, expected_rc, rc); +todo_wine_if(test->set_flags & TODO_ERR) + ok(WSAGetLastError() == test->set_error || broken(rc == SOCKET_ERROR && test->set_flags & BROKEN), + "expected setting %s option %i to cause error %i, got %i\n", type, opt, test->set_error, WSAGetLastError()); } }
+static void test_sockopt_validity(void) +{ + static const struct sockopt_validity_test ipv4_tcp_tests[] = + { + { IP_OPTIONS }, + { 2, WSAEINVAL, TODO_OPT | TODO_ERR, WSAEINVAL, 0 }, + { IP_TOS }, + { IP_TTL }, + { IP_MULTICAST_IF, WSAEINVAL, TODO_OPT | TODO_ERR, WSAEINVAL, 0 }, + { IP_MULTICAST_TTL, WSAEINVAL, TODO_OPT | TODO_ERR, WSAEINVAL, 0 }, + { IP_MULTICAST_LOOP, WSAEINVAL, TODO_OPT | TODO_ERR, WSAEINVAL, TODO_OPT | TODO_ERR }, + { IP_ADD_MEMBERSHIP, WSAENOPROTOOPT, 0, WSAEINVAL, 0 }, + { IP_DROP_MEMBERSHIP, WSAENOPROTOOPT, 0, WSAEINVAL, 0 }, + { IP_DONTFRAGMENT }, + { IP_ADD_SOURCE_MEMBERSHIP, WSAENOPROTOOPT, 0, WSAEINVAL, 0 }, + { IP_DROP_SOURCE_MEMBERSHIP, WSAENOPROTOOPT, 0, WSAEINVAL, 0 }, + { IP_BLOCK_SOURCE, WSAENOPROTOOPT, 0, WSAEINVAL, 0 }, + { IP_UNBLOCK_SOURCE, WSAENOPROTOOPT, 0, WSAEINVAL, 0 }, + { IP_PKTINFO, WSAEINVAL, TODO_OPT | TODO_ERR, WSAEINVAL, TODO_OPT | TODO_ERR }, + { IP_RECVTTL, WSAEINVAL, TODO_ERR, WSAEINVAL, TODO_ERR }, + { IP_RECEIVE_BROADCAST, WSAEINVAL, TODO_ERR, WSAEINVAL, TODO_ERR }, + { 23, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IP_RECVIF, WSAEINVAL, TODO_ERR, WSAEINVAL, TODO_ERR }, + { IP_RECVDSTADDR, WSAEINVAL, TODO_ERR, WSAEINVAL, TODO_ERR }, + { IP_IFLIST, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IP_ADD_IFLIST, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { IP_DEL_IFLIST, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { IP_UNICAST_IF }, + { IP_RTHDR, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IP_GET_IFLIST, WSAEINVAL, BROKEN /* win8 */ | TODO_ERR, WSAENOPROTOOPT, TODO_ERR }, + { IP_RECVRTHDR, WSAEINVAL, TODO_ERR, WSAEINVAL, BROKEN /* win8 */ | TODO_ERR }, + { IP_RECVTCLASS, WSAEINVAL, TODO_ERR, WSAEINVAL, TODO_ERR }, + { 41, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { 42, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { 43, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { 44, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { 45, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { 46, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { IP_ORIGINAL_ARRIVAL_IF, WSAEINVAL, BROKEN /* vista */ | TODO_ERR, WSAEINVAL, BROKEN /* vista */ | TODO_ERR }, + { IP_ECN, WSAEINVAL, BROKEN /* win7 */ | TODO_ERR, WSAEINVAL, BROKEN /* win7 */ | TODO_ERR }, + { IP_PKTINFO_EX, WSAEINVAL, BROKEN /* win7 */ | TODO_ERR, WSAEINVAL, BROKEN /* win7 */ | TODO_ERR }, + { IP_WFP_REDIRECT_RECORDS, WSAEINVAL, BROKEN /* win7 */ | TODO_ERR, WSAEINVAL, BROKEN /* win7 */ | TODO_ERR }, + { IP_WFP_REDIRECT_CONTEXT, WSAEINVAL, BROKEN /* win7 */ | TODO_ERR, WSAEINVAL, BROKEN /* win7 */ | TODO_ERR }, + { IP_MTU_DISCOVER, 0, BROKEN /* win8 */ | TODO_OPT | TODO_ERR, WSAEINVAL, BROKEN /* win8 */ | TODO_ERR }, + { IP_MTU, WSAENOTCONN, BROKEN /* win8 */ | TODO_ERR, WSAENOPROTOOPT, TODO_ERR }, + { IP_NRT_INTERFACE, WSAENOPROTOOPT, 0, WSAEFAULT, BROKEN /* win8 */ | TODO_ERR }, + { IP_RECVERR, WSAEINVAL, BROKEN /* win8 */ | TODO_ERR, WSAEINVAL, BROKEN /* win8 */ | TODO_ERR }, + { IP_USER_MTU, 0, BROKEN /* win8 */ | TODO_OPT | TODO_ERR, 0, BROKEN /* win8 */ | TODO_OPT }, + {} + }; + static const struct sockopt_validity_test ipv4_udp_tests[] = + { + { IP_OPTIONS }, + { 2, WSAEINVAL, TODO_OPT | TODO_ERR, WSAEINVAL, 0 }, + { IP_TOS }, + { IP_TTL }, + { IP_MULTICAST_IF }, + { IP_MULTICAST_TTL }, + { IP_MULTICAST_LOOP }, + { IP_ADD_MEMBERSHIP, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { IP_DROP_MEMBERSHIP, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { IP_DONTFRAGMENT }, + { IP_ADD_SOURCE_MEMBERSHIP, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { IP_DROP_SOURCE_MEMBERSHIP, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { IP_BLOCK_SOURCE, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { IP_UNBLOCK_SOURCE, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { IP_PKTINFO }, + { IP_RECVTTL, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IP_RECEIVE_BROADCAST, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { 23, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IP_RECVIF, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IP_RECVDSTADDR, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IP_IFLIST, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IP_ADD_IFLIST, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { IP_DEL_IFLIST, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { IP_UNICAST_IF }, + { IP_RTHDR, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IP_GET_IFLIST, WSAEINVAL, BROKEN /* win8 */ | TODO_ERR, WSAENOPROTOOPT, TODO_ERR }, + { IP_RECVRTHDR, 0, TODO_OPT | TODO_ERR, 0, BROKEN /* win8 */ | TODO_OPT }, + { IP_RECVTCLASS, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { 41, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { 42, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { 43, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { 44, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { 45, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { 46, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { IP_ORIGINAL_ARRIVAL_IF, 0, BROKEN /* vista */ | TODO_OPT | TODO_ERR, 0, BROKEN /* vista */ | TODO_OPT }, + { IP_ECN, 0, BROKEN /* win7 */ | TODO_OPT | TODO_ERR, 0, BROKEN /* win7 */ | TODO_OPT }, + { IP_PKTINFO_EX, 0, BROKEN /* win7 */ | TODO_OPT | TODO_ERR, 0, BROKEN /* win7 */ | TODO_OPT }, + { IP_WFP_REDIRECT_RECORDS, 0, BROKEN /* win7 */ | TODO_OPT | TODO_ERR, 0, BROKEN /* win7 */ | TODO_OPT }, + { IP_WFP_REDIRECT_CONTEXT, 0, BROKEN /* win7 */ | TODO_OPT | TODO_ERR, 0, BROKEN /* win7 */ | TODO_OPT }, + { IP_MTU_DISCOVER, 0, BROKEN /* win8 */ | TODO_OPT | TODO_ERR, WSAEINVAL, BROKEN /* win8 */ | TODO_ERR }, + { IP_MTU, WSAENOTCONN, BROKEN /* win8 */ | TODO_ERR, WSAENOPROTOOPT, TODO_ERR }, + { IP_NRT_INTERFACE, WSAENOPROTOOPT, 0, WSAEFAULT, BROKEN /* win8 */ | TODO_ERR }, + { IP_RECVERR, 0, BROKEN /* win8 */ | TODO_OPT | TODO_ERR, 0, BROKEN /* win8 */ | TODO_OPT }, + { IP_USER_MTU, 0, BROKEN /* win8 */ | TODO_OPT | TODO_ERR, 0, BROKEN /* win8 */ | TODO_OPT }, + {} + }; + static const struct sockopt_validity_test ipv6_tcp_tests[] = + { + { IPV6_HOPOPTS, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { 2, WSAEINVAL, TODO_ERR, WSAEINVAL, TODO_ERR }, + { 3, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IPV6_UNICAST_HOPS }, + { IPV6_MULTICAST_IF, WSAEINVAL, TODO_OPT | TODO_ERR, WSAEINVAL, 0 }, + { IPV6_MULTICAST_HOPS, WSAEINVAL, TODO_OPT | TODO_ERR, WSAEINVAL, 0 }, + { IPV6_MULTICAST_LOOP, WSAEINVAL, TODO_OPT | TODO_ERR, WSAEINVAL, TODO_OPT | TODO_ERR }, + { IPV6_ADD_MEMBERSHIP, WSAENOPROTOOPT, 0, WSAEINVAL, 0 }, + { IPV6_DROP_MEMBERSHIP, WSAENOPROTOOPT, 0, WSAEINVAL, 0 }, + { IPV6_DONTFRAG }, + { 15, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { 16, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { 17, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { 18, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { IPV6_PKTINFO, WSAEINVAL, TODO_OPT | TODO_ERR, WSAEINVAL, TODO_OPT | TODO_ERR }, + { IPV6_HOPLIMIT, WSAEINVAL, TODO_OPT | TODO_ERR, WSAEINVAL, TODO_OPT | TODO_ERR }, + { 22, WSAEINVAL, TODO_ERR, WSAEINVAL, TODO_ERR }, + { IPV6_PROTECTION_LEVEL, 0, TODO_OPT | TODO_ERR, 0, 0 }, + { IPV6_RECVIF, WSAEINVAL, TODO_ERR, WSAEINVAL, TODO_ERR }, + { IPV6_RECVDSTADDR, WSAEINVAL, TODO_ERR, WSAEINVAL, TODO_ERR }, + { IPV6_V6ONLY }, + { IPV6_IFLIST, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IPV6_ADD_IFLIST, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { IPV6_DEL_IFLIST, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { IPV6_UNICAST_IF }, + { IPV6_RTHDR, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IPV6_GET_IFLIST, WSAEINVAL, BROKEN /* win8 */ | TODO_ERR, WSAENOPROTOOPT, TODO_ERR }, + { IPV6_RECVRTHDR, WSAEINVAL, TODO_ERR, WSAEINVAL, BROKEN /* win8 */ | TODO_ERR }, + { IPV6_RECVTCLASS, WSAEINVAL, TODO_ERR, WSAEINVAL, TODO_ERR }, + { 41, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { 42, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { 43, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { 44, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { 45, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { 46, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { 47, WSAEINVAL, BROKEN /* vista */ | TODO_ERR, WSAEINVAL, BROKEN /* vista */ | TODO_ERR }, + { IPV6_ECN, WSAEINVAL, BROKEN /* win7 */ | TODO_ERR, WSAEINVAL, BROKEN /* win7 */ | TODO_ERR }, + { IPV6_PKTINFO_EX, WSAEINVAL, BROKEN /* win7 */ | TODO_ERR, WSAEINVAL, BROKEN /* win7 */ | TODO_ERR }, + { IPV6_WFP_REDIRECT_RECORDS, WSAEINVAL, BROKEN /* win7 */ | TODO_ERR, WSAEINVAL, BROKEN /* win7 */ | TODO_ERR }, + { IPV6_WFP_REDIRECT_CONTEXT, WSAEINVAL, BROKEN /* win7 */ | TODO_ERR, WSAEINVAL, BROKEN /* win7 */ | TODO_ERR }, + { IPV6_MTU_DISCOVER, 0, BROKEN /* win8 */ | TODO_OPT | TODO_ERR, WSAEINVAL, BROKEN /* win8 */ | TODO_ERR }, + { IPV6_MTU, WSAENOTCONN, BROKEN /* win8 */ | TODO_ERR, WSAENOPROTOOPT, TODO_ERR }, + { IPV6_NRT_INTERFACE, WSAENOPROTOOPT, 0, WSAEFAULT, BROKEN /* win8 */ | TODO_ERR }, + { IPV6_RECVERR, WSAEINVAL, BROKEN /* win8 */ | TODO_ERR, WSAEINVAL, BROKEN /* win8 */ | TODO_ERR }, + { IPV6_USER_MTU, 0, BROKEN /* win8 */ | TODO_OPT | TODO_ERR, 0, BROKEN /* win8 */ | TODO_OPT }, + {} + }; + static const struct sockopt_validity_test ipv6_udp_tests[] = + { + { IPV6_HOPOPTS, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { 2, WSAEINVAL, TODO_ERR, WSAEINVAL, TODO_ERR }, + { 3, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IPV6_UNICAST_HOPS }, + { IPV6_MULTICAST_IF }, + { IPV6_MULTICAST_HOPS }, + { IPV6_MULTICAST_LOOP }, + { IPV6_ADD_MEMBERSHIP, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { IPV6_DROP_MEMBERSHIP, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { IPV6_DONTFRAG }, + { 15, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { 16, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { 17, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { 18, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { IPV6_PKTINFO }, + { IPV6_HOPLIMIT }, + { 22, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IPV6_PROTECTION_LEVEL, 0, TODO_OPT | TODO_ERR, 0, 0 }, + { IPV6_RECVIF, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IPV6_RECVDSTADDR, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IPV6_V6ONLY }, + { IPV6_IFLIST, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IPV6_ADD_IFLIST, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { IPV6_DEL_IFLIST, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, + { IPV6_UNICAST_IF }, + { IPV6_RTHDR, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IPV6_GET_IFLIST, WSAEINVAL, BROKEN /* win8 */ | TODO_ERR, WSAENOPROTOOPT, TODO_ERR }, + { IPV6_RECVRTHDR, 0, TODO_OPT | TODO_ERR, 0, BROKEN /* win8 */ | TODO_OPT }, + { IPV6_RECVTCLASS, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { 41, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { 42, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { 43, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { 44, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { 45, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { 46, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, + { 47, 0, BROKEN /* vista */ | TODO_OPT | TODO_ERR, 0, BROKEN /* vista */ | TODO_OPT }, + { IPV6_ECN, 0, BROKEN /* win7 */ | TODO_OPT | TODO_ERR, 0, BROKEN /* win7 */ | TODO_OPT }, + { IPV6_PKTINFO_EX, 0, BROKEN /* win7 */ | TODO_OPT | TODO_ERR, 0, BROKEN /* win7 */ | TODO_OPT }, + { IPV6_WFP_REDIRECT_RECORDS, 0, BROKEN /* win7 */ | TODO_OPT | TODO_ERR, 0, BROKEN /* win7 */ | TODO_OPT }, + { IPV6_WFP_REDIRECT_CONTEXT, 0, BROKEN /* win7 */ | TODO_OPT | TODO_ERR, 0, BROKEN /* win7 */ | TODO_OPT }, + { IPV6_MTU_DISCOVER, 0, BROKEN /* win8 */ | TODO_OPT | TODO_ERR, WSAEINVAL, BROKEN /* win8 */ | TODO_ERR }, + { IPV6_MTU, WSAENOTCONN, BROKEN /* win8 */ | TODO_ERR, WSAENOPROTOOPT, TODO_ERR }, + { IPV6_NRT_INTERFACE, WSAENOPROTOOPT, 0, WSAEFAULT, BROKEN /* win8 */ | TODO_ERR }, + { IPV6_RECVERR, 0, BROKEN /* win8 */ | TODO_OPT | TODO_ERR, 0, BROKEN /* win8 */ | TODO_OPT }, + { IPV6_USER_MTU, 0, BROKEN /* win8 */ | TODO_OPT | TODO_ERR, 0, BROKEN /* win8 */ | TODO_OPT }, + {} + }; + SOCKET sock; + + 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_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); + + 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); +} + START_TEST( sock ) { int i; @@ -11471,7 +11710,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]);
This is a good idea in principle, but a few things bother me about it:
(1) the tables are hard to read (could maybe be solved with indentation)?
(2) it would probably make sense to split get/set into separate tables, and not to split tcp/udp? I dunno, looks potentially simpler.
(3) It's a bit ambiguous in many cases whether an operation is supported or not. I.e. it'd be nice to have *successful* calls to most of these. At least WSAENOPROTOOPT is relatively unambiguous, but I can't tell if a given operation always returns WSAEINVAL (like, say, the hardcoded IPv4 option 41) or only returns WSAEINVAL because we're not using the right arguments.
Which is to say that maybe a table isn't that great after all :-/
(4) It's also kind of unclear what /* win7 */ means: broken on win7, or before win7, or up to and including win7, or what?
On Wed, Aug 11, 2021 at 9:18 AM Zebediah Figura (she/her) zfigura@codeweavers.com wrote:
This is a good idea in principle, but a few things bother me about it:
(1) the tables are hard to read (could maybe be solved with indentation)?
I'd be happy to indent the tables, but honestly they would be a lot cleaner if Wine always called SetLastError(WSAENOPROTOOPT) in setsockopt when an option is not recognized. (It already sets WSAENOPROTOOPT for SOL_SOCKET options, just not for network-level options.) Can I just send a patch to do that, or do the error code tests need to be added first?
(2) it would probably make sense to split get/set into separate tables, and not to split tcp/udp? I dunno, looks potentially simpler.
(3) It's a bit ambiguous in many cases whether an operation is supported or not. I.e. it'd be nice to have *successful* calls to most of these. At least WSAENOPROTOOPT is relatively unambiguous, but I can't tell if a given operation always returns WSAEINVAL (like, say, the hardcoded IPv4 option 41) or only returns WSAEINVAL because we're not using the right arguments.
Which is to say that maybe a table isn't that great after all :-/
In these tests, WSAEINVAL generally means that an option is supported on TCP but not UDP or vice-versa.
Since the setsockopt tests depend on getting a valid result from getsockopt, how about if we just skip calling setsockopt if we expect getsockopt to fail? That would be a little less comprehensive, but the tables would be a lot shorter.
(4) It's also kind of unclear what /* win7 */ means: broken on win7, or before win7, or up to and including win7, or what?
Like in most other Wine tests, the comment tells the most recent version of Windows where the option doesn't work correctly. For example, /* win7 */ means that the option does not work on Windows 7, but it works on Windows 8 and later.
By the way, the IPV6_RECVTCLASS patch has its own set of tests. Would you approve it if I resend it by itself while we're still working on the more comprehensive tabular tests?
Thanks for the feedback,
-Alex
On 8/11/21 1:02 PM, Alex Henrie wrote:
On Wed, Aug 11, 2021 at 9:18 AM Zebediah Figura (she/her) zfigura@codeweavers.com wrote:
This is a good idea in principle, but a few things bother me about it:
(1) the tables are hard to read (could maybe be solved with indentation)?
I'd be happy to indent the tables, but honestly they would be a lot cleaner if Wine always called SetLastError(WSAENOPROTOOPT) in setsockopt when an option is not recognized. (It already sets WSAENOPROTOOPT for SOL_SOCKET options, just not for network-level options.) Can I just send a patch to do that, or do the error code tests need to be added first?
I think either approach is reasonable; it'd be nice if it can be added to the series so that I can see what the end result looks like up front.
(2) it would probably make sense to split get/set into separate tables, and not to split tcp/udp? I dunno, looks potentially simpler.
(3) It's a bit ambiguous in many cases whether an operation is supported or not. I.e. it'd be nice to have *successful* calls to most of these. At least WSAENOPROTOOPT is relatively unambiguous, but I can't tell if a given operation always returns WSAEINVAL (like, say, the hardcoded IPv4 option 41) or only returns WSAEINVAL because we're not using the right arguments.
Which is to say that maybe a table isn't that great after all :-/
In these tests, WSAEINVAL generally means that an option is supported on TCP but not UDP or vice-versa.
Since the setsockopt tests depend on getting a valid result from getsockopt, how about if we just skip calling setsockopt if we expect getsockopt to fail? That would be a little less comprehensive, but the tables would be a lot shorter.
That seems reasonable to me, but this does break testing set-only options.
Actually now that I see how you're calling setsockopt(), I guess EINVAL is less unconvincing; we probably expect setsockopt() to work with the same data...
Hmm, do we need TODO_OPT? Can't we derive that from the error code?
(4) It's also kind of unclear what /* win7 */ means: broken on win7, or before win7, or up to and including win7, or what?
Like in most other Wine tests, the comment tells the most recent version of Windows where the option doesn't work correctly. For example, /* win7 */ means that the option does not work on Windows 7, but it works on Windows 8 and later.
By the way, the IPV6_RECVTCLASS patch has its own set of tests. Would you approve it if I resend it by itself while we're still working on the more comprehensive tabular tests?
Probably, yes.
Thanks for the feedback,
-Alex
On Thu, Aug 12, 2021 at 11:00 AM Zebediah Figura (she/her) zfigura@codeweavers.com wrote:
Hmm, do we need TODO_OPT? Can't we derive that from the error code?
If we make Wine's getsockopt correctly return success or failure for every option, then yes, we'd only need one todo flag. I've added patches to the series to accomplish that :-)
-Alex
Signed-off-by: Alex Henrie alexhenrie24@gmail.com ---
From the tests, it appears that Windows does not in fact implement the
complementary socket option IPV6_TCLASS (IPV6_TCLASS is used only as a constant in the control message header). --- dlls/ntdll/unix/socket.c | 18 ++++++++++++++++++ dlls/ws2_32/socket.c | 7 +++++++ dlls/ws2_32/tests/sock.c | 30 ++++++++++++++++++++++++++---- include/wine/afd.h | 2 ++ 4 files changed, 53 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 35c846eb7ae..4f0c83597df 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -462,6 +462,16 @@ static int convert_control_headers(struct msghdr *hdr, WSABUF *control) } #endif /* IPV6_PKTINFO */
+#if defined(IPV6_TCLASS) + case IPV6_TCLASS: + { + ptr = fill_control_message( WS_IPPROTO_IPV6, WS_IPV6_TCLASS, ptr, &ctlsize, + CMSG_DATA(cmsg_unix), sizeof(INT) ); + if (!ptr) goto error; + break; + } +#endif /* IPV6_TCLASS */ + default: FIXME("Unhandled IPPROTO_IPV6 message header type %d\n", cmsg_unix->cmsg_type); break; @@ -1916,6 +1926,14 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc return do_setsockopt( handle, io, IPPROTO_IPV6, IPV6_RECVPKTINFO, in_buffer, in_size ); #endif
+#ifdef IPV6_RECVTCLASS + case IOCTL_AFD_WINE_GET_IPV6_RECVTCLASS: + return do_getsockopt( handle, io, IPPROTO_IPV6, IPV6_RECVTCLASS, out_buffer, out_size ); + + case IOCTL_AFD_WINE_SET_IPV6_RECVTCLASS: + return do_setsockopt( handle, io, IPPROTO_IPV6, IPV6_RECVTCLASS, in_buffer, in_size ); +#endif + case IOCTL_AFD_WINE_GET_IPV6_UNICAST_HOPS: return do_getsockopt( handle, io, IPPROTO_IPV6, IPV6_UNICAST_HOPS, out_buffer, out_size );
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 933aff9a99c..611b11f5854 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -311,6 +311,7 @@ static inline const char *debugstr_sockopt(int level, int optname) DEBUG_SOCKOPT(IPV6_MULTICAST_HOPS); DEBUG_SOCKOPT(IPV6_MULTICAST_LOOP); DEBUG_SOCKOPT(IPV6_PKTINFO); + DEBUG_SOCKOPT(IPV6_RECVTCLASS); DEBUG_SOCKOPT(IPV6_UNICAST_HOPS); DEBUG_SOCKOPT(IPV6_V6ONLY); DEBUG_SOCKOPT(IPV6_UNICAST_IF); @@ -1733,6 +1734,9 @@ int WINAPI getsockopt( SOCKET s, int level, int optname, char *optval, int *optl case IPV6_PKTINFO: return server_getsockopt( s, IOCTL_AFD_WINE_GET_IPV6_RECVPKTINFO, optval, optlen );
+ case IPV6_RECVTCLASS: + return server_getsockopt( s, IOCTL_AFD_WINE_GET_IPV6_RECVTCLASS, optval, optlen ); + case IPV6_UNICAST_HOPS: return server_getsockopt( s, IOCTL_AFD_WINE_GET_IPV6_UNICAST_HOPS, optval, optlen );
@@ -2941,6 +2945,9 @@ int WINAPI setsockopt( SOCKET s, int level, int optname, const char *optval, int FIXME("IPV6_PROTECTION_LEVEL is ignored!\n"); return 0;
+ case IPV6_RECVTCLASS: + return server_setsockopt( s, IOCTL_AFD_WINE_SET_IPV6_RECVTCLASS, optval, optlen ); + case IPV6_UNICAST_HOPS: return server_setsockopt( s, IOCTL_AFD_WINE_SET_IPV6_UNICAST_HOPS, optval, optlen );
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 408afdceb88..0b58d29c68e 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -2002,7 +2002,7 @@ static void test_ipv6_cmsg(void) WSAMSG msg = {NULL, 0, &payload_buf, 1, {sizeof(control), control}, 0}; WSACMSGHDR *header = (WSACMSGHDR *)control; LPFN_WSARECVMSG pWSARecvMsg; - INT *hop_limit = (INT *)WSA_CMSG_DATA(header); + INT *int_data = (INT *)WSA_CMSG_DATA(header); IN6_PKTINFO *pkt_info = (IN6_PKTINFO *)WSA_CMSG_DATA(header); DWORD count, state; int rc; @@ -2043,7 +2043,7 @@ static void test_ipv6_cmsg(void) ok(header->cmsg_type == IPV6_HOPLIMIT, "expected IPV6_HOPLIMIT, got %i\n", header->cmsg_type); ok(header->cmsg_len == sizeof(*header) + sizeof(INT), "expected length %i, got %i\n", (INT)(sizeof(*header) + sizeof(INT)), (INT)header->cmsg_len); - ok(*hop_limit >= 32, "expected at least 32, got %i\n", *hop_limit); + ok(*int_data >= 32, "expected at least 32, got %i\n", *int_data); setsockopt(server, IPPROTO_IPV6, IPV6_HOPLIMIT, (const char *)&off, sizeof(off)); ok(!rc, "failed to clear IPV6_HOPLIMIT, error %u\n", WSAGetLastError());
@@ -2069,6 +2069,28 @@ static void test_ipv6_cmsg(void) rc = setsockopt(server, IPPROTO_IPV6, IPV6_PKTINFO, (const char *)&off, sizeof(off)); ok(!rc, "failed to clear IPV6_PKTINFO, error %u\n", WSAGetLastError());
+ memset(control, 0, sizeof(control)); + msg.Control.len = sizeof(control); + rc = setsockopt(server, IPPROTO_IPV6, IPV6_RECVTCLASS, (const char *)&on, sizeof(on)); + ok(!rc, "failed to set IPV6_RECVTCLASS, error %u\n", WSAGetLastError()); + state = 0; + count = sizeof(state); + rc = getsockopt(server, IPPROTO_IPV6, IPV6_RECVTCLASS, (char *)&state, (INT *)&count); + ok(!rc, "failed to get IPV6_RECVTCLASS, error %u\n", WSAGetLastError()); + ok(state == 1, "expected 1, got %u\n", state); + rc = send(client, payload, sizeof(payload), 0); + ok(rc == sizeof(payload), "send failed, error %u\n", WSAGetLastError()); + rc = pWSARecvMsg(server, &msg, &count, NULL, NULL); + ok(!rc, "WSARecvMsg failed, error %u\n", WSAGetLastError()); + ok(count == sizeof(payload), "expected length %i, got %i\n", (INT)sizeof(payload), count); + ok(header->cmsg_level == IPPROTO_IPV6, "expected IPPROTO_IPV6, got %i\n", header->cmsg_level); + ok(header->cmsg_type == IPV6_TCLASS, "expected IPV6_TCLASS, got %i\n", header->cmsg_type); + ok(header->cmsg_len == sizeof(*header) + sizeof(INT), + "expected length %i, got %i\n", (INT)(sizeof(*header) + sizeof(INT)), (INT)header->cmsg_len); + ok(*int_data == 0, "expected 0, got %i\n", *int_data); + rc = setsockopt(server, IPPROTO_IPV6, IPV6_RECVTCLASS, (const char *)&off, sizeof(off)); + ok(!rc, "failed to clear IPV6_RECVTCLASS, error %u\n", WSAGetLastError()); + closesocket(server); closesocket(client); } @@ -11602,7 +11624,7 @@ static void test_sockopt_validity(void) { IPV6_RTHDR, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, { IPV6_GET_IFLIST, WSAEINVAL, BROKEN /* win8 */ | TODO_ERR, WSAENOPROTOOPT, TODO_ERR }, { IPV6_RECVRTHDR, WSAEINVAL, TODO_ERR, WSAEINVAL, BROKEN /* win8 */ | TODO_ERR }, - { IPV6_RECVTCLASS, WSAEINVAL, TODO_ERR, WSAEINVAL, TODO_ERR }, + { IPV6_RECVTCLASS, WSAEINVAL, TODO_OPT | TODO_ERR, WSAEINVAL, TODO_OPT | TODO_ERR }, { 41, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, { 42, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, { 43, WSAENOPROTOOPT, 0, WSAEINVAL, TODO_ERR }, @@ -11651,7 +11673,7 @@ static void test_sockopt_validity(void) { IPV6_RTHDR, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, { IPV6_GET_IFLIST, WSAEINVAL, BROKEN /* win8 */ | TODO_ERR, WSAENOPROTOOPT, TODO_ERR }, { IPV6_RECVRTHDR, 0, TODO_OPT | TODO_ERR, 0, BROKEN /* win8 */ | TODO_OPT }, - { IPV6_RECVTCLASS, 0, TODO_OPT | TODO_ERR, 0, TODO_OPT }, + { IPV6_RECVTCLASS }, { 41, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, { 42, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, { 43, WSAENOPROTOOPT, 0, WSAEFAULT, TODO_ERR }, diff --git a/include/wine/afd.h b/include/wine/afd.h index caa86eabf83..3d84df9f735 100644 --- a/include/wine/afd.h +++ b/include/wine/afd.h @@ -232,6 +232,8 @@ struct afd_get_events_params #define IOCTL_AFD_WINE_SET_IPV6_RECVHOPLIMIT WINE_AFD_IOC(287) #define IOCTL_AFD_WINE_GET_IPV6_RECVPKTINFO WINE_AFD_IOC(288) #define IOCTL_AFD_WINE_SET_IPV6_RECVPKTINFO WINE_AFD_IOC(289) +#define IOCTL_AFD_WINE_GET_IPV6_RECVTCLASS WINE_AFD_IOC(290) +#define IOCTL_AFD_WINE_SET_IPV6_RECVTCLASS WINE_AFD_IOC(291)
struct afd_create_params {
August 8, 2021 10:45 PM, "Alex Henrie" alexhenrie24@gmail.com wrote:
The same option apparently causes both the hop limit and the time-to-live to be received.
Of course, because that's what time-to-live is--a hop limit. traceroute(8) works by starting with TTL=1, progressively increasing it and seeing where the packet stops each time.
Chip