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.
-- v9: 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..f518de062bc 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) + /* Windows will always require the maximum buffer length for storing a name, regardless of the name's actual length. */ + buf_required += unicode ? BTH_MAX_NAME_SIZE * sizeof( WCHAR ) : BTH_MAX_NAME_SIZE; + 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 );
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.
Fair enough. I think it's still a bit simpler, mind, since you don't need that get_nth_lookup_flags() helper.
On what platforms does this happen?
WSALookupServiceBegin can fail with the error code WSASERVICE_NOT_FOUND if no devices are found.
Thanks. Can you add a comment to this effect please?
Those flags aren't Bluetooth-specific. Also, what's that union for?
I have replaced it with a `lookup_devices` flag instead.
That's not quite what I was saying though. The flag still isn't Bluetooth-specific but you've just changed its form. Rather I was suggesting to store the flags outside the union, in the ws_lookup_service_ctx structure.
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.
If you don't specify either flag, what happens? The documentation looks like both things are returned.
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.
If it's only two I'd kinda be inclined to not use the callback, but I can't say I feel that strongly about it.
On what platforms does this happen?
WSALookupServiceBegin can fail with the error code WSASERVICE_NOT_FOUND if no devices are found.
Thanks. Can you add a comment to this effect please?
Done.
That's not quite what I was saying though. The flag still isn't Bluetooth-specific but you've just changed its form. Rather I was suggesting to store the flags outside the union, in the ws_lookup_service_ctx structure.
Ah. Nevertheless, `ws_lookup_service_ctx` only needs to know whether we're performing a device or service inquiry, so the boolean is sufficient and simpler.
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.
If you don't specify either flag, what happens? The documentation looks like both things are returned.
So, the _only_ flag for controlling what kind of inquiry needs to be performed is `LUP_CONTAINERS`. If it's there, WSALookupServiceBegin will look up Bluetooth devices. If it's not, it'll go through Bluetooth services (on either the local radio(s) or a remote device, determined by `WSAQUERYSET` fields). The documentation doesn't do a great job of explaining this (though the API itself is quite unintuitive to begin with). I've added some documentation to ``sh_lookup_service_begin` explaining this.