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.
-- v5: ws2_32: Implement WSALookupServiceNext for Bluetooth device discovery. ws2_32: Implement WSALookupServiceBegin for Bluetooth device discovery. ws2_32/tests: Add tests for Bluetooth device discovery in WSALookupServiceBegin/Next.
From: Vibhav Pant vibhavp@gmail.com
--- dlls/ws2_32/tests/protocol.c | 169 +++++++++++++++++++++++++++++++++++ include/winsock2.h | 4 + 2 files changed, 173 insertions(+)
diff --git a/dlls/ws2_32/tests/protocol.c b/dlls/ws2_32/tests/protocol.c index 09c56439ada..b9380f4425a 100644 --- a/dlls/ws2_32/tests/protocol.c +++ b/dlls/ws2_32/tests/protocol.c @@ -536,6 +536,174 @@ 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) + { + todo_wine ok( err == WSA_E_NO_MORE || err == WSAENOMORE || 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}; + INT ret, err; + DWORD init_flags = LUP_CONTAINERS, i = 0; + DWORD all_flags = LUP_RETURN_TYPE | LUP_RETURN_NAME | LUP_RETURN_BLOB | LUP_RETURN_ADDR | LUP_RES_SERVICE; + + 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 )); + HANDLE lookup_handle = NULL; + + winetest_push_context( "flags=%#lx", next_flags ); + SetLastError( 0xdeadbeef ); + ret = WSALookupServiceBeginA( &queryA, init_flags, &lookup_handle ); + err = WSAGetLastError(); + todo_wine ok( !ret || err == WSASERVICE_NOT_FOUND, "WSALookupServiceBeginA failed: %d\n", WSAGetLastError() ); + todo_wine ok( lookup_handle || err == WSASERVICE_NOT_FOUND, "Expected lookup_handle to be non-NULL\n" ); + if (!lookup_handle) + { + skip( "WSALookupServiceBeginA failed\n" ); + winetest_pop_context(); + continue; + } + + test_WSALookupServiceNext_devices( lookup_handle, next_flags, next_flags == all_flags ); + + ok( !WSALookupServiceEnd( lookup_handle ), "WSALookupServiceEnd failed: %d\n", WSAGetLastError() ); + winetest_pop_context(); + } +} + #define WM_ASYNCCOMPLETE (WM_USER + 100) static HWND create_async_message_window(void) { @@ -3154,6 +3322,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 | 260 +++++++++++++++++++++++++++++++++-- dlls/ws2_32/tests/protocol.c | 22 +-- dlls/ws2_32/ws2_32_private.h | 2 + 4 files changed, 264 insertions(+), 22 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..b01e8888918 100644 --- a/dlls/ws2_32/protocol.c +++ b/dlls/ws2_32/protocol.c @@ -24,6 +24,8 @@
#include "ws2_32_private.h"
+#include <wine/exception.h> + WINE_DEFAULT_DEBUG_CHANNEL(winsock); WINE_DECLARE_DEBUG_CHANNEL(winediag);
@@ -2073,35 +2075,271 @@ 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 ULONG bth_device_info_list_size( ULONG num_devices ) +{ + BTH_DEVICE_INFO_LIST *list; + return sizeof( *list ) + (num_devices - 1) * sizeof( list->deviceList[0] ); +} + +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 = bth_device_info_list_size( 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, bth_device_info_list_size( (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 );
/*********************************************************************** * 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 %#lx %p)\n", query, 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 %#lx %p)\n", query, 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 b9380f4425a..1f0deb37939 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 { @@ -688,8 +690,8 @@ static void test_WSALookupService_devices( void ) SetLastError( 0xdeadbeef ); ret = WSALookupServiceBeginA( &queryA, init_flags, &lookup_handle ); err = WSAGetLastError(); - todo_wine ok( !ret || err == WSASERVICE_NOT_FOUND, "WSALookupServiceBeginA failed: %d\n", WSAGetLastError() ); - todo_wine ok( lookup_handle || err == WSASERVICE_NOT_FOUND, "Expected lookup_handle to be non-NULL\n" ); + ok( !ret || err == WSASERVICE_NOT_FOUND, "WSALookupServiceBeginA failed: %d\n", WSAGetLastError() ); + ok( lookup_handle || err == WSASERVICE_NOT_FOUND, "Expected lookup_handle to be non-NULL\n" ); if (!lookup_handle) { skip( "WSALookupServiceBeginA failed\n" ); 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 | 264 ++++++++++++++++++++++++++++++++--- dlls/ws2_32/tests/protocol.c | 12 +- 2 files changed, 254 insertions(+), 22 deletions(-)
diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c index b01e8888918..7229555f513 100644 --- a/dlls/ws2_32/protocol.c +++ b/dlls/ws2_32/protocol.c @@ -2118,12 +2118,6 @@ static DWORD bth_for_all_radios( void (*callback)( HANDLE radio, void *data ), v return ret == ERROR_NO_MORE_ITEMS ? 0 : ret; }
-static ULONG bth_device_info_list_size( ULONG num_devices ) -{ - BTH_DEVICE_INFO_LIST *list; - return sizeof( *list ) + (num_devices - 1) * sizeof( list->deviceList[0] ); -} - static void bth_radio_append_device_list( HANDLE radio, void *data ) { ULONG num_devices = 1; @@ -2133,7 +2127,7 @@ static void bth_radio_append_device_list( HANDLE radio, void *data ) { DWORD size, bytes;
- size = bth_device_info_list_size( num_devices ); + size = offsetof( BTH_DEVICE_INFO_LIST, deviceList[num_devices] ); list = calloc( 1, size ); if (!list) return; @@ -2165,7 +2159,8 @@ static void bth_radio_append_device_list( HANDLE radio, void *data ) { void *ptr;
- ptr = realloc( final_list, bth_device_info_list_size( (final_list)->numOfDevices + list->numOfDevices ) ); + ptr = realloc( final_list, offsetof( BTH_DEVICE_INFO_LIST, + deviceList[final_list->numOfDevices + list->numOfDevices] ) ); if (!ptr) { free( list ); @@ -2344,25 +2339,262 @@ 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_left = *buf_len - queryset_size; + DWORD buf_required = queryset_size; + const BTH_DEVICE_INFO *device_info; + BOOL buf_insufficient = FALSE; + + if (devices->list->numOfDevices == devices->idx) + { + SetLastError( WSA_E_NO_MORE ); + 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) + { + buf_required += sizeof( GUID ); + if (buf_left >= sizeof( GUID )) + { + GUID *cod = (GUID *)buf_start; + + memset( cod, 0, sizeof( *cod )); + cod->Data1 = device_info->classOfDevice; + buf_start += sizeof( *cod ); + buf_left -= sizeof( *cod ); + if (unicode) + results->resultsW.lpServiceClassId = cod; + else + results->resultsA.lpServiceClassId = cod; + } + else + buf_insufficient = TRUE; + } + if (flags & LUP_RETURN_NAME) + { + if (unicode) + { + DWORD lenW = MultiByteToWideChar( CP_ACP, 0, device_info->name, -1, NULL, 0 ); + buf_required += lenW; + if (buf_left >= lenW * sizeof( WCHAR )) + { + results->resultsW.lpszServiceInstanceName = (WCHAR *)buf_start; + buf_start += lenW * sizeof( WCHAR ); + buf_left -= lenW * sizeof( WCHAR ); + MultiByteToWideChar( CP_ACP, 0, device_info->name, -1, results->resultsW.lpszServiceInstanceName, + lenW ); + } + else + buf_insufficient = TRUE; + } + else + { + DWORD lenA = strlen( device_info->name ) + 1; + buf_required += lenA; + if (buf_left >= lenA) + { + results->resultsA.lpszServiceInstanceName = buf_start; + buf_start += lenA; + buf_left -= lenA; + memcpy( results->resultsA.lpszServiceInstanceName, device_info->name, lenA + 1 ); + } + else + buf_insufficient = TRUE; + } + } + if (flags & LUP_RETURN_ADDR) + { + DWORD addr_len = sizeof( CSADDR_INFO ) + sizeof( SOCKADDR_BTH ); + if (flags & LUP_RES_SERVICE) + addr_len += sizeof( SOCKADDR_BTH ); + buf_required += addr_len; + + if (buf_left >= addr_len) + { + 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 ); + buf_left -= sizeof( *bth_addr ) + sizeof( *addr_info ); + + 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 ); + buf_left -= 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; + } + else + buf_insufficient = TRUE; + } + if (flags & LUP_RETURN_BLOB) + { + BLOB *blob; + buf_required += sizeof( *blob ) + sizeof( *device_info ); + if (buf_left >= sizeof( *blob ) + sizeof( *device_info )) + { + BTH_DEVICE_INFO *info; + + blob = (BLOB *)buf_start; + buf_start += sizeof( *blob ); + info = (BTH_DEVICE_INFO *)buf_start; + buf_start += sizeof( *info ); + buf_left -= sizeof( *blob ) + 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; + } + else + buf_insufficient = TRUE; + } + + if (buf_insufficient) + { + WSASetLastError( WSAEFAULT ); + *buf_len = buf_required; + } + else + WSASetLastError( ERROR_SUCCESS ); + + return buf_insufficient ? -1 : 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 %#lx %p %p)\n", lookup_handle, 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 %#lx %p %p)\n", lookup, 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 1f0deb37939..a639ed934fc 100644 --- a/dlls/ws2_32/tests/protocol.c +++ b/dlls/ws2_32/tests/protocol.c @@ -574,8 +574,8 @@ static void test_WSALookupServiceNext_devices( HANDLE *lookup_handle, DWORD flag winetest_push_context( "device %lu", i); if (ret) { - todo_wine ok( err == WSA_E_NO_MORE || err == WSAENOMORE || err == WSAEFAULT, - "WSALookupServiceNextA failed: %d\n", WSAGetLastError() ); + ok( err == WSA_E_NO_MORE || err == WSAENOMORE || err == WSAEFAULT, + "WSALookupServiceNextA failed: %d\n", WSAGetLastError() ); if (err != WSAEFAULT) { free( results ); @@ -591,21 +591,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; @@ -628,7 +628,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;
For a simpler proposal (and one that will take less time running the tests, although I don't know how much that's a concern since I don't have immediate access to a machine that doesn't skip) how about we just tests the flag one at a time?
The tests will only query cached Bluetooth devices (unless winetest_interactive is set, at which point an actual discovery is performed), so these tests don't take more than a second or two.
On what platforms does this happen?
WSALookupServiceBegin can fail with the error code WSASERVICE_NOT_FOUND if no devices are found.
On which platforms do the different errors appear?
WSAEFAULT is expected at least once, so that we can resize the buffer appropriately. I'll remove `WSA_E_NO_MORE`, but it's supposed to be returned by some older versions of Winsock, though I haven't been able to find one that does.
Those flags aren't Bluetooth-specific. Also, what's that union for?
I have replaced it with a `lookup_devices` flag instead. Basically, `WSALookupService` can be used to perform either device or service discovery, controlled by the existence of the `LUP_CONTAINERS` flag. The union only has a single member as of now, but will also have a `ws_query_bth_services` struct once support for service discovery is added as well.
How many other places are you planning to use this function? The callback design looks kind of excessive for how much it's actually doing...
The other place would be for performing service discovery locally. The idea is that we need to go through all radios to collect which SDP records have been registered for each one.
Does native touch the buffer at all if the size is insufficient? This isn't tested.
I wrote a bit of code to test this, and Windows doesn't seem to modify the buffer if it's not appropriately sized. I'll simplify the code and try adding a test case for this, thanks.