 
            My understanding is that, in the absence of layered service providers, `SIO_BSP_HANDLE_SELECT` should behave like `SIO_BASE_HANDLE` (this is relied upon by https://github.com/python-trio/trio). Should I add tests to verify this? If so, should they basically just be the same as the existing `SIO_BASE_HANDLE` tests?
-- v2: ws2_32: Implement SIO_BSP_HANDLE_SELECT.
 
            From: Josh Steffen josh@joshsteffen.com
Wine does not support layered service providers, so just fall through to SIO_BASE_HANDLE. --- dlls/ws2_32/socket.c | 1 + dlls/ws2_32/tests/sock.c | 92 ++++++++++++++++++++++++++++++++++++++++ include/mswsock.h | 2 + 3 files changed, 95 insertions(+)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index d4223e93bd1..a267f65ab91 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -2701,6 +2701,7 @@ INT WINAPI WSAIoctl(SOCKET s, DWORD code, LPVOID in_buff, DWORD in_size, LPVOID return ret ? -1 : 0; }
+ case SIO_BSP_HANDLE_SELECT: case SIO_BASE_HANDLE: { NTSTATUS status; diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 77bc3beeabf..a8a48438a83 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -6253,6 +6253,97 @@ static void test_base_handle(void) } }
+static void test_bsp_handle_select(void) +{ + OVERLAPPED overlapped = {0}, *overlapped_ptr; + unsigned int i; + SOCKET s, base; + ULONG_PTR key; + HANDLE port; + DWORD size; + int ret; + + static const struct + { + int family, type, protocol; + } + tests[] = + { + {AF_INET, SOCK_STREAM, IPPROTO_TCP}, + {AF_INET, SOCK_DGRAM, IPPROTO_UDP}, + {AF_INET6, SOCK_STREAM, IPPROTO_TCP}, + {AF_INET6, SOCK_DGRAM, IPPROTO_UDP}, + }; + + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + s = socket(tests[i].family, tests[i].type, tests[i].protocol); + if (s == INVALID_SOCKET) continue; + port = CreateIoCompletionPort((HANDLE)s, NULL, 123, 0); + + WSASetLastError(0xdeadbeef); + ret = WSAIoctl(s, SIO_BSP_HANDLE_SELECT, NULL, 0, &base, sizeof(base), NULL, &overlapped, NULL); + ok(ret == -1, "expected failure\n"); + ok(WSAGetLastError() == WSAEFAULT, "got error %u\n", WSAGetLastError()); + + WSASetLastError(0xdeadbeef); + size = 0xdeadbeef; + base = 0xdeadbeef; + ret = WSAIoctl(s, SIO_BSP_HANDLE_SELECT, NULL, 0, &base, sizeof(base), &size, NULL, NULL); + ok(!ret, "expected success\n"); + ok(!WSAGetLastError(), "got error %u\n", WSAGetLastError()); + ok(size == sizeof(base), "got size %lu\n", size); + ok(base == s, "expected %#Ix, got %#Ix\n", s, base); + + WSASetLastError(0xdeadbeef); + size = 0xdeadbeef; + base = 0xdeadbeef; + overlapped.Internal = 0xdeadbeef; + overlapped.InternalHigh = 0xdeadbeef; + ret = WSAIoctl(s, SIO_BSP_HANDLE_SELECT, NULL, 0, &base, sizeof(base), &size, &overlapped, NULL); + ok(ret == -1, "expected failure\n"); + ok(WSAGetLastError() == ERROR_IO_PENDING, "got error %u\n", WSAGetLastError()); + ok(size == 0xdeadbeef, "got size %lu\n", size); + + ret = GetQueuedCompletionStatus(port, &size, &key, &overlapped_ptr, 0); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_NOT_SUPPORTED, "got error %lu\n", GetLastError()); + ok(!size, "got size %lu\n", size); + ok(key == 123, "got key %Iu\n", key); + ok(overlapped_ptr == &overlapped, "got overlapped %p\n", overlapped_ptr); + ok((NTSTATUS)overlapped.Internal == STATUS_NOT_SUPPORTED, "got status %#lx\n", (NTSTATUS)overlapped.Internal); + ok(!overlapped.InternalHigh, "got size %Iu\n", overlapped.InternalHigh); + ok(base == 0xdeadbeef, "expected %#Ix, got %#Ix\n", s, base); + + CloseHandle(port); + closesocket(s); + + s = socket(tests[i].family, tests[i].type, tests[i].protocol); + + ret = WSAIoctl(s, SIO_BSP_HANDLE_SELECT, NULL, 0, &base, sizeof(base), NULL, &overlapped, socket_apc); + ok(ret == -1, "expected failure\n"); + ok(WSAGetLastError() == WSAEFAULT, "got error %u\n", WSAGetLastError()); + + apc_count = 0; + size = 0xdeadbeef; + base = 0xdeadbeef; + ret = WSAIoctl(s, SIO_BSP_HANDLE_SELECT, NULL, 0, &base, sizeof(base), &size, &overlapped, socket_apc); + ok(ret == -1, "expected failure\n"); + ok(WSAGetLastError() == ERROR_IO_PENDING, "got error %u\n", WSAGetLastError()); + ok(size == 0xdeadbeef, "got size %lu\n", size); + + ret = SleepEx(0, TRUE); + ok(ret == WAIT_IO_COMPLETION, "got %d\n", ret); + ok(apc_count == 1, "APC was called %u times\n", apc_count); + ok(apc_error == WSAEOPNOTSUPP, "got APC error %lu\n", apc_error); + ok(!apc_size, "got APC size %lu\n", apc_size); + ok(apc_overlapped == &overlapped, "got APC overlapped %p\n", apc_overlapped); + ok(base == 0xdeadbeef, "expected %#Ix, got %#Ix\n", s, base); + + closesocket(s); + } +} + static void test_circular_queueing(void) { SOCKET s; @@ -14646,6 +14737,7 @@ START_TEST( sock ) test_sioRoutingInterfaceQuery(); test_sioAddressListChange(); test_base_handle(); + test_bsp_handle_select(); test_circular_queueing(); test_unsupported_ioctls();
diff --git a/include/mswsock.h b/include/mswsock.h index fa97d5bf04d..754b8ec3dd0 100644 --- a/include/mswsock.h +++ b/include/mswsock.h @@ -85,10 +85,12 @@ extern "C" { #ifndef USE_WS_PREFIX #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR, 12) #define SIO_SET_COMPATIBILITY_MODE _WSAIOW(IOC_VENDOR, 300) +#define SIO_BSP_HANDLE_SELECT _WSAIOR(IOC_WS2, 28) #define SIO_BASE_HANDLE _WSAIOR(IOC_WS2, 34) #else #define WS_SIO_UDP_CONNRESET _WSAIOW(WS_IOC_VENDOR, 12) #define WS_SIO_SET_COMPATIBILITY_MODE _WSAIOW(WS_IOC_VENDOR, 300) +#define WS_SIO_BSP_HANDLE_SELECT _WSAIOR(WS_IOC_WS2, 28) #define WS_SIO_BASE_HANDLE _WSAIOR(WS_IOC_WS2, 34) #endif
 
            On Mon Oct 6 20:31:41 2025 +0000, Elizabeth Figura wrote:
Should I add tests to verify this? If so, should they basically just
be the same as the existing `SIO_BASE_HANDLE` tests? Yes, please.
Done (I think... I'm a bit out of my depth here with overlapped IO and completions so I hope my find-and-replacing is okay)
 
            This merge request was approved by Elizabeth Figura.
 
            On Mon Oct 6 20:31:40 2025 +0000, Josh Steffen wrote:
Done (I think... I'm a bit out of my depth here with overlapped IO and completions so I hope my find-and-replacing is okay)
It is, thank you. Actually we could probably go as far as putting another loop in the same test with different ioctl codes.
 
            On Mon Oct 6 21:12:30 2025 +0000, Elizabeth Figura wrote:
It is, thank you. Actually we could probably go as far as putting another loop in the same test with different ioctl codes.
Oh good thinking, I can do that now if you'd like. Will the name `test_base_handle` need to be updated?
 
            Will the name `test_base_handle` need to be updated?
It's fine enough as-is I think.


