This MR adds an initial implementation of the winsock `WSALookupsService*` methods for performing Bluetooth device discovery (`LUP_CONTAINERS`).
Pending !7472, the code will also eventually support performing device inquiry scans.
-- v7: ws2_32: Implement WSALookupServiceNext for Bluetooth device discovery.
From: Vibhav Pant vibhavp@gmail.com
--- dlls/ws2_32/tests/protocol.c | 201 +++++++++++++++++++++++++++++++++++ include/winsock2.h | 4 + 2 files changed, 205 insertions(+)
diff --git a/dlls/ws2_32/tests/protocol.c b/dlls/ws2_32/tests/protocol.c index 09c56439ada..46efc85b281 100644 --- a/dlls/ws2_32/tests/protocol.c +++ b/dlls/ws2_32/tests/protocol.c @@ -536,6 +536,206 @@ static void test_WSALookupService(void) ok(!ret, "WSALookupServiceEnd failed unexpectedly\n"); }
+static DWORD get_nth_lookup_flags( SIZE_T n, const DWORD *flags, SIZE_T len ) +{ + SIZE_T i; + DWORD result = 0; + + if (!n) + return 0; + + for (i = 0; i < len; i++) + if (n & (1 << i)) + result |= flags[i]; + + return result; +} + +static void test_WSALookupServiceNext_devices( HANDLE *lookup_handle, DWORD flags, BOOL verbose ) +{ + WSAQUERYSETA *results; + DWORD buf_len, i = 0; + + buf_len = sizeof( *results ); + for (;;) + { + INT ret, err; + const BTH_DEVICE_INFO *info = NULL; + + results = calloc( 1, buf_len ); + results->dwSize = sizeof( *results ); + + SetLastError( 0xdeadbeef ); + ret = WSALookupServiceNextA( lookup_handle, flags, &buf_len, results ); + err = WSAGetLastError(); + + winetest_push_context( "device %lu", i); + if (ret) + { + ok( ret == -1, "%d != -1\n", ret ); + todo_wine ok( err == WSA_E_NO_MORE || err == WSAEFAULT, + "WSALookupServiceNextA failed: %d\n", WSAGetLastError() ); + if (err != WSAEFAULT) + { + free( results ); + winetest_pop_context(); + return; + } + ok( buf_len >= sizeof( *results ), "Expected %lu to be greater than %lu\n", buf_len, (DWORD)sizeof( *results ) ); + free( results ); + winetest_pop_context(); + continue; + } + i++; + + if (flags & LUP_RETURN_NAME) + { + todo_wine ok( !!results->lpszServiceInstanceName, "Expected lpszServiceInstanceName to not be NULL\n" ); + if (verbose) + trace( "Name: %s\n", debugstr_a( results->lpszServiceInstanceName ) ); + } + + if (flags & LUP_RETURN_TYPE) + { + todo_wine ok( !!results->lpServiceClassId, "Expected lpServiceClassId to not be NULL\n" ); + if (results->lpServiceClassId && verbose) + trace( "CoD: %#lx\n", results->lpServiceClassId->Data1 ); + } + + if (flags & LUP_RETURN_BLOB) + { + todo_wine ok( !!results->lpBlob, "Expected lpBlob to not be NULL\n" ); + if (results->lpBlob) + { + info = (BTH_DEVICE_INFO *)results->lpBlob->pBlobData; + ok( results->lpBlob->cbSize >= sizeof( *info ), "%lu should be at least %lu\n", + results->lpBlob->cbSize, (DWORD)sizeof( *info ) ); + ok( !!info, "Expected pBlobData to not be NULL\n" ); + if (info) + { + if (results->lpszServiceInstanceName) + ok( !strcmp( info->name, results->lpszServiceInstanceName ), "%s != %s", debugstr_a( info->name ), + debugstr_a( results->lpszServiceInstanceName )); + if (verbose) + trace( "Device Flags: %#lx\n", info->flags ); + if (results->lpServiceClassId) + ok( info->classOfDevice == results->lpServiceClassId->Data1, "%lu != %lu\n", info->classOfDevice, + results->lpServiceClassId->Data1 ); + } + } + } + + if (flags & LUP_RETURN_ADDR) + { + todo_wine ok( !!results->lpcsaBuffer, "Expected lpcsaBuffer to not be NULL\n" ); + if (results->lpcsaBuffer) + { + const SOCKET_ADDRESS *remote_sock_addr = &results->lpcsaBuffer->RemoteAddr; + const SOCKADDR_BTH *remote_bth_addr = (SOCKADDR_BTH *)remote_sock_addr->lpSockaddr; + const SOCKET_ADDRESS *local_sock_addr = &results->lpcsaBuffer->LocalAddr; + const SOCKADDR_BTH *local_bth_addr = (SOCKADDR_BTH *)local_sock_addr->lpSockaddr; + + ok( remote_sock_addr->iSockaddrLength == sizeof( *remote_bth_addr ), "%d != %lu\n", + remote_sock_addr->iSockaddrLength, (DWORD)sizeof( *remote_bth_addr ) ); + ok( !!remote_bth_addr, "Expected RemoteAddr to not be NULL\n" ); + if (remote_bth_addr && remote_sock_addr->iSockaddrLength == sizeof( *remote_bth_addr )) + { + ok( remote_bth_addr->addressFamily == AF_BTH, "%d != %d\n", remote_bth_addr->addressFamily, AF_BTH ); + if (info && info->flags & BDIF_ADDRESS) + ok( remote_bth_addr->btAddr == info->address, "%I64x != %I64x\n", remote_bth_addr->btAddr, info->address ); + } + + if (flags & LUP_RES_SERVICE) + { + ok( local_sock_addr->iSockaddrLength == sizeof( *local_bth_addr ), "%d != %lu\n", + local_sock_addr->iSockaddrLength, (DWORD)sizeof( *local_bth_addr )); + ok( !!local_bth_addr, "Expected LocalAddr to not be NULL\n" ); + if (local_bth_addr && local_sock_addr->iSockaddrLength == sizeof( *local_bth_addr )) + { + ok( local_bth_addr->addressFamily == AF_BTH, "%d != %d\n", local_bth_addr->addressFamily, AF_BTH ); + todo_wine ok( local_bth_addr->btAddr, "Expected btAddr to not be zero\n" ); + } + } + } + } + + winetest_pop_context(); + free( results ); + } +} + +static const DWORD device_lookup_flags[] = {LUP_RETURN_TYPE, LUP_RETURN_NAME, LUP_RETURN_BLOB, LUP_RETURN_ADDR, + LUP_RES_SERVICE}; + +static void test_WSALookupService_devices( void ) +{ + WSAQUERYSETA queryA = {0}, *results; + DWORD init_flags = LUP_CONTAINERS, i = 0, buf_len, orig_len; + DWORD all_flags = LUP_RETURN_TYPE | LUP_RETURN_NAME | LUP_RETURN_BLOB | LUP_RETURN_ADDR | LUP_RES_SERVICE; + HANDLE lookup_handle; + INT ret, err; + + queryA.dwSize = sizeof( queryA ); + queryA.dwNameSpace = NS_BTH; + if (winetest_interactive) + init_flags |= LUP_FLUSHCACHE; + + for (i = 1; i < 1 << ARRAY_SIZE( device_lookup_flags ); i++) + { + DWORD next_flags = get_nth_lookup_flags( i, device_lookup_flags, ARRAY_SIZE( device_lookup_flags )); + lookup_handle = NULL; + + winetest_push_context( "flags=%#lx", next_flags ); + SetLastError( 0xdeadbeef ); + ret = WSALookupServiceBeginA( &queryA, init_flags, &lookup_handle ); + err = WSAGetLastError(); + if (ret) + { + todo_wine ok( err == WSASERVICE_NOT_FOUND, "WSALookupServiceBeginA failed: %d\n", WSAGetLastError() ); + ok( !lookup_handle, "Handle should not have been filled\n"); + skip( "WSALookupServiceBeginA failed\n" ); + winetest_pop_context(); + continue; + } + + ok( !!lookup_handle, "Handle was not filled\n"); + test_WSALookupServiceNext_devices( lookup_handle, next_flags, next_flags == all_flags ); + + ok( !WSALookupServiceEnd( lookup_handle ), "WSALookupServiceEnd failed: %d\n", WSAGetLastError() ); + winetest_pop_context(); + } + + /* Test that WSALookupServiceNext does not modify the buffer partially when there's insufficient space for other + * params. */ + lookup_handle = NULL; + SetLastError( 0xdeadbeef ); + ret = WSALookupServiceBeginA( &queryA, init_flags, &lookup_handle ); + err = WSAGetLastError(); + if (ret) + { + todo_wine ok( err == WSASERVICE_NOT_FOUND, "WSALookupServiceBeginA failed: %d\n", err ); + ok( !lookup_handle, "Handle should not have been filled\n"); + skip( "WSALookupServiceBeginA failed\n" ); + return; + } + + todo_wine ok( !!lookup_handle, "Handle was not filled\n" ); + /* Missing space for a BTH_DEVICE_INFO */ + orig_len = buf_len = sizeof( *results ) + sizeof( GUID ) + sizeof( BLOB ); + results = calloc( 1, buf_len ); + results->dwSize = sizeof( *results ); + SetLastError( 0xdeadbeef ); + ret = WSALookupServiceNextA( lookup_handle, LUP_RETURN_TYPE | LUP_RETURN_BLOB, &buf_len, results ); + err = WSAGetLastError(); + ok( ret == -1, "Expected WSALookupServiceNextA to fail\n" ); + todo_wine ok( err == WSAEFAULT, "%d != %d", err, WSAEFAULT ); + todo_wine ok( buf_len > orig_len, "%ld should be greater than %ld\n", buf_len, orig_len ); + ok( !results->lpServiceClassId, "lpServiceClassId should not have been filled\n" ); + ok( !results->lpBlob, "lpBlob should not have been filled\n" ); + free( results ); + ok( !WSALookupServiceEnd( lookup_handle ), "WSALookupServiceEnd failed: %d\n", WSAGetLastError() ); +} + #define WM_ASYNCCOMPLETE (WM_USER + 100) static HWND create_async_message_window(void) { @@ -3154,6 +3354,7 @@ START_TEST( protocol )
test_getservbyname(); test_WSALookupService(); + test_WSALookupService_devices();
test_inet_ntoa(); test_inet_addr(); diff --git a/include/winsock2.h b/include/winsock2.h index f7b88322f49..3f7d334c81c 100644 --- a/include/winsock2.h +++ b/include/winsock2.h @@ -468,6 +468,7 @@ extern "C" {
/* Constants for WSALookupServiceBegin() */ #define LUP_DEEP 0x0001 +#define LUP_CONTAINERS 0x0002 #define LUP_RETURN_NAME 0x0010 #define LUP_RETURN_TYPE 0x0020 #define LUP_RETURN_VERSION 0x0040 @@ -476,6 +477,9 @@ extern "C" { #define LUP_RETURN_BLOB 0x0200 #define LUP_RETURN_ALIASES 0x0400 #define LUP_RETURN_QUERY_STRING 0x0800 +#define LUP_FLUSHCACHE 0x1000 +#define LUP_FLUSHPREVIOUS 0x2000 +#define LUP_RES_SERVICE 0x8000 #define LUP_RETURN_ALL (LUP_RETURN_ADDR|LUP_RETURN_BLOB|LUP_RETURN_ALIASES|LUP_RETURN_QUERY_STRING \ |LUP_RETURN_NAME|LUP_RETURN_TYPE|LUP_RETURN_VERSION|LUP_RETURN_COMMENT)
From: Vibhav Pant vibhavp@gmail.com
--- dlls/ws2_32/Makefile.in | 2 +- dlls/ws2_32/protocol.c | 306 +++++++++++++++++++++++++++++++++-- dlls/ws2_32/tests/protocol.c | 28 ++-- dlls/ws2_32/ws2_32_private.h | 2 + 4 files changed, 313 insertions(+), 25 deletions(-)
diff --git a/dlls/ws2_32/Makefile.in b/dlls/ws2_32/Makefile.in index e80def64881..10faa56759c 100644 --- a/dlls/ws2_32/Makefile.in +++ b/dlls/ws2_32/Makefile.in @@ -2,7 +2,7 @@ EXTRADEFS = -D_WS2_32_ MODULE = ws2_32.dll UNIXLIB = ws2_32.so IMPORTLIB = ws2_32 -DELAYIMPORTS = dnsapi advapi32 iphlpapi user32 +DELAYIMPORTS = dnsapi advapi32 iphlpapi user32 bluetoothapis UNIX_LIBS = $(PTHREAD_LIBS)
SOURCES = \ diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c index 2699593a2f3..8d67aa7c558 100644 --- a/dlls/ws2_32/protocol.c +++ b/dlls/ws2_32/protocol.c @@ -2073,35 +2073,319 @@ int WINAPI WSAGetServiceClassNameByClassIdW( GUID *class, WCHAR *service, DWORD return -1; }
+/* NS_BTH support */ +struct ws_bth_query_device +{ + BTH_DEVICE_INFO_LIST *list; + SIZE_T idx; +}; + +struct ws_bth_query_ctx +{ + unsigned int lookup_devices : 1; + union { + struct ws_bth_query_device device; + } data; +}; + +struct ws_lookup_service_ctx +{ + DWORD namespace; + union { + struct ws_bth_query_ctx bth; + } data; +}; + +static DWORD bth_for_all_radios( void (*callback)( HANDLE radio, void *data ), void *data ) +{ + BLUETOOTH_FIND_RADIO_PARAMS params = {0}; + HBLUETOOTH_RADIO_FIND find; + HANDLE radio; + DWORD ret; + + params.dwSize = sizeof( params ); + find = BluetoothFindFirstRadio( ¶ms, &radio ); + if (!find) + return GetLastError(); + do { + callback( radio, data ); + CloseHandle( radio ); + } while (BluetoothFindNextRadio( find, &radio )); + ret = GetLastError(); + BluetoothFindRadioClose( find ); + return ret == ERROR_NO_MORE_ITEMS ? 0 : ret; +} + +static void bth_radio_append_device_list( HANDLE radio, void *data ) +{ + ULONG num_devices = 1; + BTH_DEVICE_INFO_LIST *final_list = *(BTH_DEVICE_INFO_LIST **)data, *list, **ret = data; + + for (;;) + { + DWORD size, bytes; + + size = offsetof( BTH_DEVICE_INFO_LIST, deviceList[num_devices] ); + list = calloc( 1, size ); + if (!list) + return; + if (!DeviceIoControl( radio, IOCTL_BTH_GET_DEVICE_INFO, NULL, 0, list, size, &bytes, NULL ) + && GetLastError() != ERROR_INVALID_USER_BUFFER) + { + free( list ); + ERR( "Failed to get device list for radio %p: %lu\n", radio, GetLastError() ); + return; + } + if (!list->numOfDevices) + { + free( list ); + return; + } + if (num_devices == list->numOfDevices) + break; + + /* The buffer was not properly sized, try again. */ + num_devices = list->numOfDevices; + free( list ); + } + if (!final_list && !(final_list = calloc( 1, sizeof( *final_list )))) + { + free( list ); + return; + } + else if (final_list) + { + void *ptr; + + ptr = realloc( final_list, offsetof( BTH_DEVICE_INFO_LIST, + deviceList[final_list->numOfDevices + list->numOfDevices] ) ); + if (!ptr) + { + free( list ); + return; + } + final_list = ptr; + } + + memcpy( &final_list->deviceList[(final_list)->numOfDevices], list->deviceList, + sizeof( list->deviceList[0] ) * list->numOfDevices ); + final_list->numOfDevices += list->numOfDevices; + *ret = final_list; + free( list ); +} + +static INT bth_lookup_devices( const BLOB *blob, DWORD flags, struct ws_bth_query_device *query ) +{ + DWORD err; + + TRACE( "(%p, %#lx, %p)\n", blob, flags, query ); + + if (flags & LUP_FLUSHCACHE) + FIXME( "Device inquiry is not supported\n" ); + + query->idx = 0; + query->list = NULL; + err = bth_for_all_radios( bth_radio_append_device_list, &query->list ); + if (err) + { + if (query->list) + free( query->list ); + WSASetLastError( err == ERROR_NO_MORE_ITEMS ? WSASERVICE_NOT_FOUND : err ); + return -1; + } + WSASetLastError( query->list ? ERROR_SUCCESS : WSASERVICE_NOT_FOUND ); + return query->list ? 0 : -1; +} + +static INT ws_bth_lookup_service_begin( const BLOB *blob, DWORD flags, struct ws_bth_query_ctx *ctx ) +{ + TRACE( "(%p, %#lx, %p)\n", blob, flags, ctx ); + + ctx->lookup_devices = !!(flags & LUP_CONTAINERS); + + if (ctx->lookup_devices) + return bth_lookup_devices( blob, flags, &ctx->data.device ); + + FIXME( "Service inquiry is not supported\n" ); + WSASetLastError( WSA_NOT_ENOUGH_MEMORY ); + return -1; +} + +static void ws_bth_lookup_service_end( struct ws_bth_query_ctx *handle ); + +struct flags_info +{ + DWORD val; + const char *name; +}; + +/* Taken from dlls/dmusic/dmusic_main.c */ +static const char *debugstr_flags( DWORD flags, const struct flags_info* info, DWORD len ) +{ + char buffer[256], *ptr = buffer; + DWORD i, flags_left = flags, buf_left = sizeof( buffer ); + + if (!flags) + return wine_dbg_sprintf( "0" ); + + for (i = 0; i < len; i++) + { + if (flags_left & info[i].val) + { + int len = snprintf( ptr, buf_left, buf_left == sizeof( buffer ) ? "%s" : " | %s", info[i].name ); + if (len < 0 || len >= buf_left) + return wine_dbg_sprintf( "%#lx", flags ); + ptr += len; + buf_left -= len; + flags_left &= ~info[i].val; + } + } + + return flags_left ? wine_dbg_sprintf( "(%s | %#lx)", buffer, flags_left ) : wine_dbg_sprintf( "(%s)", buffer ); +} + +static const char *debugstr_WSALookupService_flags( DWORD flags ) +{ + const static struct flags_info lookup_flags[] = { + { LUP_DEEP, "LUP_DEEP" }, + { LUP_CONTAINERS, "LUP_CONTAINERS" }, + { LUP_RETURN_NAME, "LUP_RETURN_NAME" }, + { LUP_RETURN_TYPE, "LUP_RETURN_TYPE" }, + { LUP_RETURN_VERSION, "LUP_RETURN_VERSION" }, + { LUP_RETURN_COMMENT, "LUP_RETURN_COMMENT" }, + { LUP_RETURN_ADDR, "LUP_RETURN_ADDR" }, + { LUP_RETURN_BLOB, "LUP_RETURN_BLOB" }, + { LUP_RETURN_ALIASES, "LUP_RETURN_ALIASES" }, + { LUP_RETURN_QUERY_STRING, "LUP_RETURN_QUERY_STRING" }, + { LUP_FLUSHCACHE, "LUP_FLUSHCACHE" }, + { LUP_FLUSHPREVIOUS, "LUP_FLUSHPREVIOUS" }, + { LUP_RES_SERVICE, "LUP_RES_SERVICE" } + }; + + return flags == LUP_RETURN_ALL ? "(LUP_RETURN_ALL)" + : debugstr_flags( flags, lookup_flags, ARRAY_SIZE( lookup_flags ) ); +}
/*********************************************************************** * WSALookupServiceBeginA (ws2_32.@) */ -int WINAPI WSALookupServiceBeginA( WSAQUERYSETA *query, DWORD flags, HANDLE *lookup ) +int WINAPI WSALookupServiceBeginA( WSAQUERYSETA *query, DWORD flags, HANDLE *lookup_handle ) { - FIXME( "(%p %#lx %p) Stub!\n", query, flags, lookup ); - SetLastError( WSA_NOT_ENOUGH_MEMORY ); - return -1; -} + struct ws_lookup_service_ctx *ret, lookup = {0}; + + TRACE( "(%p %s %p)\n", query, debugstr_WSALookupService_flags( flags ), lookup_handle ); + + if (!query || !lookup_handle) + { + WSASetLastError( WSAEFAULT ); + return -1; + } + if (query->dwSize != sizeof( *query )) + { + WSASetLastError( WSAEINVAL ); + return -1; + } + switch (query->dwNameSpace) + { + case NS_BTH: + if (ws_bth_lookup_service_begin( query->lpBlob, flags, &lookup.data.bth )) + return -1; + break; + default: + FIXME( "Unsupported dwNameSpace: %lu\n", query->dwNameSpace ); + SetLastError( WSA_NOT_ENOUGH_MEMORY ); + return -1; + } + if (!(ret = calloc( 1, sizeof( *ret ) ))) + { + ws_bth_lookup_service_end( &lookup.data.bth ); + WSASetLastError( WSA_NOT_ENOUGH_MEMORY ); + return -1; + }
+ ret->namespace = query->dwNameSpace; + ret->data = lookup.data; + *lookup_handle = ret; + + return 0; +}
/*********************************************************************** * WSALookupServiceBeginW (ws2_32.@) */ -int WINAPI WSALookupServiceBeginW( WSAQUERYSETW *query, DWORD flags, HANDLE *lookup ) +int WINAPI WSALookupServiceBeginW( WSAQUERYSETW *query, DWORD flags, HANDLE *lookup_handle ) { - FIXME( "(%p %#lx %p) Stub!\n", query, flags, lookup ); - SetLastError( WSA_NOT_ENOUGH_MEMORY ); - return -1; + struct ws_lookup_service_ctx *ret, lookup = {0}; + + TRACE( "(%p %s %p)\n", query, debugstr_WSALookupService_flags( flags ), lookup_handle ); + + if (!query || !lookup_handle) + { + WSASetLastError( WSAEFAULT ); + return -1; + } + if (query->dwSize != sizeof( *query )) + { + WSASetLastError( WSAEINVAL ); + return -1; + } + switch (query->dwNameSpace) + { + case NS_BTH: + if (ws_bth_lookup_service_begin( query->lpBlob, flags, &lookup.data.bth )) + return -1; + break; + default: + FIXME( "Unsupported dwNameSpace: %lu\n", query->dwNameSpace ); + SetLastError( WSA_NOT_ENOUGH_MEMORY ); + return -1; + } + if (!(ret = calloc( 1, sizeof( *ret ) ))) + { + ws_bth_lookup_service_end( &lookup.data.bth ); + WSASetLastError( WSA_NOT_ENOUGH_MEMORY ); + return -1; + } + + ret->namespace = query->dwNameSpace; + ret->data = lookup.data; + *lookup_handle = ret; + + return 0; }
+static void ws_bth_lookup_service_end( struct ws_bth_query_ctx *handle ) +{ + if (handle->lookup_devices) + free( handle->data.device.list ); +}
/*********************************************************************** * WSALookupServiceEnd (ws2_32.@) */ -int WINAPI WSALookupServiceEnd( HANDLE lookup ) +int WINAPI WSALookupServiceEnd( HANDLE lookup_handle ) { - FIXME("(%p) Stub!\n", lookup ); + struct ws_lookup_service_ctx *lookup = lookup_handle; + + TRACE( "(%p)\n", lookup_handle ); + + if (!lookup_handle) + { + WSASetLastError( WSA_INVALID_HANDLE ); + return -1; + } + + switch (lookup->namespace) + { + case NS_BTH: + ws_bth_lookup_service_end( &lookup->data.bth ); + break; + default: + WSASetLastError( WSA_INVALID_HANDLE ); + return -1; + } + + free( lookup_handle ); return 0; }
diff --git a/dlls/ws2_32/tests/protocol.c b/dlls/ws2_32/tests/protocol.c index 46efc85b281..29ffaa9c96e 100644 --- a/dlls/ws2_32/tests/protocol.c +++ b/dlls/ws2_32/tests/protocol.c @@ -400,27 +400,23 @@ static void test_WSALookupService(void) ret = WSALookupServiceBeginW(NULL, 0, &handle); error = WSAGetLastError(); ok(ret == SOCKET_ERROR, "WSALookupServiceBeginW should have failed\n"); - todo_wine ok(error == WSAEFAULT, "expected 10014, got %ld\n", error);
ret = WSALookupServiceBeginW(qs, 0, NULL); error = WSAGetLastError(); ok(ret == SOCKET_ERROR, "WSALookupServiceBeginW should have failed\n"); - todo_wine ok(error == WSAEFAULT, "expected 10014, got %ld\n", error);
ret = WSALookupServiceBeginW(qs, 0, &handle); ok(ret == SOCKET_ERROR, "WSALookupServiceBeginW should have failed\n"); - todo_wine ok(WSAGetLastError() == WSAEINVAL - || broken(WSAGetLastError() == ERROR_INVALID_PARAMETER) - || broken(WSAGetLastError() == WSASERVICE_NOT_FOUND) /* win10 1809 */, - "got error %u\n", WSAGetLastError()); + ok(WSAGetLastError() == WSAEINVAL + || broken(WSAGetLastError() == ERROR_INVALID_PARAMETER) + || broken(WSAGetLastError() == WSASERVICE_NOT_FOUND) /* win10 1809 */, + "got error %u\n", WSAGetLastError());
ret = WSALookupServiceEnd(NULL); error = WSAGetLastError(); - todo_wine ok(ret == SOCKET_ERROR, "WSALookupServiceEnd should have failed\n"); - todo_wine ok(error == ERROR_INVALID_HANDLE, "expected 6, got %ld\n", error);
/* standard network list query */ @@ -439,6 +435,12 @@ static void test_WSALookupService(void) todo_wine ok(handle != (HANDLE)0xdeadbeef, "Handle was not filled\n");
+ if (ret) + { + skip( "WSALookupServiceBeginW failed, skipping.\n" ); + return; + } + offset = 0; do { @@ -573,8 +575,8 @@ static void test_WSALookupServiceNext_devices( HANDLE *lookup_handle, DWORD flag if (ret) { ok( ret == -1, "%d != -1\n", ret ); - todo_wine ok( err == WSA_E_NO_MORE || err == WSAEFAULT, - "WSALookupServiceNextA failed: %d\n", WSAGetLastError() ); + ok( err == WSA_E_NO_MORE || err == WSAEFAULT, + "WSALookupServiceNextA failed: %d\n", WSAGetLastError() ); if (err != WSAEFAULT) { free( results ); @@ -691,7 +693,7 @@ static void test_WSALookupService_devices( void ) err = WSAGetLastError(); if (ret) { - todo_wine ok( err == WSASERVICE_NOT_FOUND, "WSALookupServiceBeginA failed: %d\n", WSAGetLastError() ); + ok( err == WSASERVICE_NOT_FOUND, "WSALookupServiceBeginA failed: %d\n", WSAGetLastError() ); ok( !lookup_handle, "Handle should not have been filled\n"); skip( "WSALookupServiceBeginA failed\n" ); winetest_pop_context(); @@ -713,13 +715,13 @@ static void test_WSALookupService_devices( void ) err = WSAGetLastError(); if (ret) { - todo_wine ok( err == WSASERVICE_NOT_FOUND, "WSALookupServiceBeginA failed: %d\n", err ); + ok( err == WSASERVICE_NOT_FOUND, "WSALookupServiceBeginA failed: %d\n", err ); ok( !lookup_handle, "Handle should not have been filled\n"); skip( "WSALookupServiceBeginA failed\n" ); return; }
- todo_wine ok( !!lookup_handle, "Handle was not filled\n" ); + ok( !!lookup_handle, "Handle was not filled\n" ); /* Missing space for a BTH_DEVICE_INFO */ orig_len = buf_len = sizeof( *results ) + sizeof( GUID ) + sizeof( BLOB ); results = calloc( 1, buf_len ); diff --git a/dlls/ws2_32/ws2_32_private.h b/dlls/ws2_32/ws2_32_private.h index 9a4d0794151..179a8f02f40 100644 --- a/dlls/ws2_32/ws2_32_private.h +++ b/dlls/ws2_32/ws2_32_private.h @@ -38,6 +38,8 @@ #include "mswsock.h" #include "bthsdpdef.h" #include "bluetoothapis.h" +#include "bthdef.h" +#include "bthioctl.h" #include "ws2bth.h" #include "ws2tcpip.h" #include "ws2spi.h"
From: Vibhav Pant vibhavp@gmail.com
--- dlls/ws2_32/protocol.c | 223 +++++++++++++++++++++++++++++++++-- dlls/ws2_32/tests/protocol.c | 12 +- 2 files changed, 221 insertions(+), 14 deletions(-)
diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c index 8d67aa7c558..6736e83e061 100644 --- a/dlls/ws2_32/protocol.c +++ b/dlls/ws2_32/protocol.c @@ -2390,25 +2390,232 @@ int WINAPI WSALookupServiceEnd( HANDLE lookup_handle ) }
+/* Known flags for device discovery: + * + * LUP_FLUSHPREVIOUS: Skip to the next device in the list. + * LUP_RETURN_TYPE: Return the class of device code inside the Data1 field of lpServiceClassId (yes, really). + * LUP_RETURN_NAME: Return the device name in lpszServiceInstanceName. + * LUP_RETURN_ADDR: Return the device address in lpcsaBuffer.LocalAddr as a SOCKADDR_BTH value. + * LUP_RES_SERVICE: If LUP_RETURN_ADDR is set, return the local address in lpcsaBuffer.RemoteAddr similarly as well. + * LUP_RETURN_BLOB: Return the BTH_DEVICE_INFO value associated with the device in lpBlob. + */ +static INT bth_devices_next( struct ws_bth_query_device *devices, DWORD flags, DWORD *buf_len, void *queryset, BOOL unicode ) +{ + union { + WSAQUERYSETA resultsA; + WSAQUERYSETW resultsW; + } *results = queryset; + DWORD queryset_size = (unicode ? sizeof( results->resultsW ) : sizeof( results->resultsA )); + char *buf_start = (char *)queryset + queryset_size; + DWORD buf_required = queryset_size; + const BTH_DEVICE_INFO *device_info; + + if (devices->list->numOfDevices == devices->idx) + { + SetLastError( WSA_E_NO_MORE ); + return -1; + } + + if (flags & LUP_RETURN_TYPE) + buf_required += sizeof( GUID ); + if (flags & LUP_RETURN_NAME) + buf_required += unicode ? MultiByteToWideChar( CP_ACP, 0, device_info->name, -1, NULL, 0 ) * sizeof( WCHAR ) + : strlen( device_info->name ) + 1; + if (flags & LUP_RETURN_ADDR) + { + buf_required += sizeof( CSADDR_INFO ) + sizeof( SOCKADDR_BTH ); + if (flags & LUP_RES_SERVICE) + buf_required += sizeof( SOCKADDR_BTH ); + } + if (flags & LUP_RETURN_BLOB) + buf_required += sizeof( BLOB ) + sizeof( BTH_DEVICE_INFO ); + + if (*buf_len < buf_required) + { + *buf_len = buf_required; + WSASetLastError( WSAEFAULT ); + return -1; + } + + if (flags & LUP_FLUSHPREVIOUS) + { + devices->idx++; + return bth_devices_next( devices, flags & ~LUP_FLUSHPREVIOUS, buf_len, queryset, unicode ); + } + device_info = &devices->list->deviceList[devices->idx++]; + + if (flags & LUP_RETURN_TYPE) + { + GUID *cod = (GUID *)buf_start; + + memset( cod, 0, sizeof( *cod )); + cod->Data1 = device_info->classOfDevice; + buf_start += sizeof( *cod ); + if (unicode) + results->resultsW.lpServiceClassId = cod; + else + results->resultsA.lpServiceClassId = cod; + } + if (flags & LUP_RETURN_NAME) + { + if (unicode) + { + DWORD lenW = MultiByteToWideChar( CP_ACP, 0, device_info->name, -1, NULL, 0 ); + results->resultsW.lpszServiceInstanceName = (WCHAR *)buf_start; + buf_start += lenW * sizeof( WCHAR ); + MultiByteToWideChar( CP_ACP, 0, device_info->name, -1, results->resultsW.lpszServiceInstanceName, + lenW ); + } + else + { + DWORD lenA = strlen( device_info->name ) + 1; + results->resultsA.lpszServiceInstanceName = buf_start; + buf_start += lenA; + memcpy( results->resultsA.lpszServiceInstanceName, device_info->name, lenA + 1 ); + } + } + if (flags & LUP_RETURN_ADDR) + { + CSADDR_INFO *addr_info; + SOCKADDR_BTH *bth_addr; + + addr_info = (CSADDR_INFO *)buf_start; + buf_start += sizeof( *addr_info ); + bth_addr = (SOCKADDR_BTH *)buf_start; + buf_start += sizeof( *bth_addr ); + + addr_info->RemoteAddr.iSockaddrLength = sizeof( *bth_addr ); + addr_info->RemoteAddr.lpSockaddr = (SOCKADDR *)bth_addr; + memset( bth_addr, 0, sizeof( *bth_addr )); + bth_addr->addressFamily = AF_BTH; + bth_addr->btAddr = device_info->address; + if (flags & LUP_RES_SERVICE) + { + FIXME( "LUP_RES_SERVICE: semi-stub!\n" ); + bth_addr = (SOCKADDR_BTH *)buf_start; + buf_start += sizeof( *bth_addr ); + memset( bth_addr, 0, sizeof( *bth_addr )); + bth_addr->addressFamily = AF_BTH; + addr_info->LocalAddr.iSockaddrLength = sizeof( *bth_addr ); + addr_info->LocalAddr.lpSockaddr = (SOCKADDR *)bth_addr; + } + if (unicode) + results->resultsW.lpcsaBuffer = addr_info; + else + results->resultsA.lpcsaBuffer = addr_info; + } + if (flags & LUP_RETURN_BLOB) + { + BLOB *blob; + BTH_DEVICE_INFO *info; + + blob = (BLOB *)buf_start; + buf_start += sizeof( *blob ); + info = (BTH_DEVICE_INFO *)buf_start; + buf_start += sizeof( *info ); + + blob->cbSize = sizeof( *device_info ); + blob->pBlobData = (BYTE *)info; + info->address = device_info->address; + info->classOfDevice = device_info->classOfDevice; + info->flags = device_info->flags; + if (flags & LUP_RETURN_NAME) + lstrcpynA( info->name, device_info->name, sizeof( info->name ) ); + + if (unicode) + results->resultsW.lpBlob = blob; + else + results->resultsA.lpBlob = blob; + } + + WSASetLastError( ERROR_SUCCESS ); + return 0; +} + +static INT ws_bth_lookup_service_next( struct ws_bth_query_ctx *handle, DWORD flags, DWORD *buf_len, void *results, + BOOL unicode ) +{ + TRACE( "(%p, %#lx, %p, %p)\n", handle, flags, buf_len, results ); + + if (handle->lookup_devices) + return bth_devices_next( &handle->data.device, flags, buf_len, results, unicode ); + + WSASetLastError( WSA_INVALID_HANDLE ); + return -1; +} + /*********************************************************************** * WSALookupServiceNextA (ws2_32.@) */ -int WINAPI WSALookupServiceNextA( HANDLE lookup, DWORD flags, DWORD *len, WSAQUERYSETA *results ) +int WINAPI WSALookupServiceNextA( HANDLE lookup_handle, DWORD flags, DWORD *len, WSAQUERYSETA *results ) { - FIXME( "(%p %#lx %p %p) Stub!\n", lookup, flags, len, results ); - SetLastError( WSA_E_NO_MORE ); - return -1; + struct ws_lookup_service_ctx *lookup = lookup_handle; + + TRACE( "(%p %s %p %p)\n", lookup_handle, debugstr_WSALookupService_flags( flags ), len, results ); + + if (!len || !results || results->dwSize != sizeof( *results )) + { + WSASetLastError( WSAEINVAL ); + return -1; + } + if (*len < sizeof( *results )) + { + *len = sizeof( *results ); + WSASetLastError( WSAEFAULT ); + return -1; + } + if (!lookup) + { + WSASetLastError( WSA_INVALID_HANDLE ); + return -1; + } + + switch (lookup->namespace) + { + case NS_BTH: + return ws_bth_lookup_service_next( &lookup->data.bth, flags, len, results, FALSE ); + default: + WSASetLastError( WSA_INVALID_HANDLE ); + return -1; + } }
/*********************************************************************** * WSALookupServiceNextW (ws2_32.@) */ -int WINAPI WSALookupServiceNextW( HANDLE lookup, DWORD flags, DWORD *len, WSAQUERYSETW *results ) +int WINAPI WSALookupServiceNextW( HANDLE lookup_handle, DWORD flags, DWORD *len, WSAQUERYSETW *results ) { - FIXME( "(%p %#lx %p %p) Stub!\n", lookup, flags, len, results ); - SetLastError( WSA_E_NO_MORE ); - return -1; + struct ws_lookup_service_ctx *lookup = lookup_handle; + + TRACE( "(%p %s %p %p)\n", lookup, debugstr_WSALookupService_flags( flags ), len, results ); + + if (!len || !results || results->dwSize != sizeof( *results )) + { + WSASetLastError( WSAEINVAL ); + return -1; + } + if (*len < sizeof ( *results )) + { + *len = sizeof( *results ); + WSASetLastError( WSAEFAULT ); + return -1; + } + if (!lookup) + { + WSASetLastError( WSA_INVALID_HANDLE ); + return -1; + } + + + switch (lookup->namespace) + { + case NS_BTH: + return ws_bth_lookup_service_next( &lookup->data.bth, flags, len, results, TRUE ); + default: + WSASetLastError( WSA_INVALID_HANDLE ); + return -1; + } }
diff --git a/dlls/ws2_32/tests/protocol.c b/dlls/ws2_32/tests/protocol.c index 29ffaa9c96e..114c982490d 100644 --- a/dlls/ws2_32/tests/protocol.c +++ b/dlls/ws2_32/tests/protocol.c @@ -592,21 +592,21 @@ static void test_WSALookupServiceNext_devices( HANDLE *lookup_handle, DWORD flag
if (flags & LUP_RETURN_NAME) { - todo_wine ok( !!results->lpszServiceInstanceName, "Expected lpszServiceInstanceName to not be NULL\n" ); + ok( !!results->lpszServiceInstanceName, "Expected lpszServiceInstanceName to not be NULL\n" ); if (verbose) trace( "Name: %s\n", debugstr_a( results->lpszServiceInstanceName ) ); }
if (flags & LUP_RETURN_TYPE) { - todo_wine ok( !!results->lpServiceClassId, "Expected lpServiceClassId to not be NULL\n" ); + ok( !!results->lpServiceClassId, "Expected lpServiceClassId to not be NULL\n" ); if (results->lpServiceClassId && verbose) trace( "CoD: %#lx\n", results->lpServiceClassId->Data1 ); }
if (flags & LUP_RETURN_BLOB) { - todo_wine ok( !!results->lpBlob, "Expected lpBlob to not be NULL\n" ); + ok( !!results->lpBlob, "Expected lpBlob to not be NULL\n" ); if (results->lpBlob) { info = (BTH_DEVICE_INFO *)results->lpBlob->pBlobData; @@ -629,7 +629,7 @@ static void test_WSALookupServiceNext_devices( HANDLE *lookup_handle, DWORD flag
if (flags & LUP_RETURN_ADDR) { - todo_wine ok( !!results->lpcsaBuffer, "Expected lpcsaBuffer to not be NULL\n" ); + ok( !!results->lpcsaBuffer, "Expected lpcsaBuffer to not be NULL\n" ); if (results->lpcsaBuffer) { const SOCKET_ADDRESS *remote_sock_addr = &results->lpcsaBuffer->RemoteAddr; @@ -730,8 +730,8 @@ static void test_WSALookupService_devices( void ) ret = WSALookupServiceNextA( lookup_handle, LUP_RETURN_TYPE | LUP_RETURN_BLOB, &buf_len, results ); err = WSAGetLastError(); ok( ret == -1, "Expected WSALookupServiceNextA to fail\n" ); - todo_wine ok( err == WSAEFAULT, "%d != %d", err, WSAEFAULT ); - todo_wine ok( buf_len > orig_len, "%ld should be greater than %ld\n", buf_len, orig_len ); + ok( err == WSAEFAULT, "%d != %d\n", err, WSAEFAULT ); + ok( buf_len > orig_len, "%ld should be greater than %ld\n", buf_len, orig_len ); ok( !results->lpServiceClassId, "lpServiceClassId should not have been filled\n" ); ok( !results->lpBlob, "lpBlob should not have been filled\n" ); free( results );
v2:
* Add a `debugstr_WSALookupService_flags` helper for pretty-printing flags in debug logs. * `WSALookupServiceNextA`: only return results if the buffer is correctly sized, simplify `bth_devices_next`.