Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/dlls/bluetoothapis/main.c
5965 views
1
/*
2
* Bluetooth APIs
3
*
4
* Copyright 2016 Austin English
5
* Copyright 2025 Vibhav Pant
6
*
7
* This library is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU Lesser General Public
9
* License as published by the Free Software Foundation; either
10
* version 2.1 of the License, or (at your option) any later version.
11
*
12
* This library is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* Lesser General Public License for more details.
16
*
17
* You should have received a copy of the GNU Lesser General Public
18
* License along with this library; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20
*
21
*/
22
23
#include <stdarg.h>
24
#include <winternl.h>
25
#include <windef.h>
26
#include <winbase.h>
27
#include <winuser.h>
28
#include <winreg.h>
29
#include <winnls.h>
30
31
#include "bthsdpdef.h"
32
#include "cfgmgr32.h"
33
#include "setupapi.h"
34
#include "winioctl.h"
35
36
#include "initguid.h"
37
#include "bluetoothapis.h"
38
#include "bthledef.h"
39
#include "bthdef.h"
40
#include "bthioctl.h"
41
#include "wine/winebth.h"
42
43
#include "wine/debug.h"
44
#include "wine/list.h"
45
46
#include "resource.h"
47
48
WINE_DEFAULT_DEBUG_CHANNEL(bluetoothapis);
49
50
static HINSTANCE instance;
51
52
struct bluetooth_find_radio_handle
53
{
54
HDEVINFO devinfo;
55
DWORD idx;
56
};
57
58
struct bluetooth_find_device
59
{
60
BLUETOOTH_DEVICE_SEARCH_PARAMS params;
61
BTH_DEVICE_INFO_LIST *device_list;
62
SIZE_T idx;
63
};
64
65
static const char *debugstr_BLUETOOTH_DEVICE_SEARCH_PARAMS( const BLUETOOTH_DEVICE_SEARCH_PARAMS *params )
66
{
67
if (!params || params->dwSize != sizeof( *params ))
68
return wine_dbg_sprintf("%p", params );
69
70
return wine_dbg_sprintf( "{ %ld %d %d %d %d %d %u %p }", params->dwSize, params->fReturnAuthenticated,
71
params->fReturnRemembered, params->fReturnUnknown, params->fReturnConnected,
72
params->fIssueInquiry, params->cTimeoutMultiplier, params->hRadio );
73
}
74
75
static const char *debugstr_addr( const BYTE *addr )
76
{
77
if (!addr)
78
return wine_dbg_sprintf( "(null)" );
79
80
return wine_dbg_sprintf( "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] );
81
}
82
83
static BOOL radio_set_inquiry( HANDLE radio, BOOL enable )
84
{
85
DWORD bytes;
86
87
return DeviceIoControl( radio, enable ? IOCTL_WINEBTH_RADIO_START_DISCOVERY : IOCTL_WINEBTH_RADIO_STOP_DISCOVERY,
88
NULL, 0, NULL, 0, &bytes, NULL );
89
}
90
91
static BTH_DEVICE_INFO_LIST *radio_get_devices( HANDLE radio )
92
{
93
DWORD num_devices = 1;
94
95
for (;;)
96
{
97
BTH_DEVICE_INFO_LIST *list;
98
DWORD size, bytes;
99
100
size = sizeof( *list ) + (num_devices - 1) * sizeof( list->deviceList[0] );
101
list = calloc( 1, size );
102
if (!list)
103
{
104
SetLastError( ERROR_OUTOFMEMORY );
105
return NULL;
106
}
107
if (!DeviceIoControl( radio, IOCTL_BTH_GET_DEVICE_INFO, NULL, 0, list, size, &bytes, NULL )
108
&& GetLastError() != ERROR_INVALID_USER_BUFFER)
109
{
110
free( list );
111
return NULL;
112
}
113
if (!list->numOfDevices)
114
{
115
free( list );
116
SetLastError( ERROR_NO_MORE_ITEMS );
117
return NULL;
118
}
119
if (num_devices != list->numOfDevices)
120
{
121
/* The buffer is incorrectly sized, try again. */
122
num_devices = list->numOfDevices;
123
free( list );
124
continue;
125
}
126
return list;
127
}
128
}
129
130
static void device_info_from_bth_info( BLUETOOTH_DEVICE_INFO *info, const BTH_DEVICE_INFO *bth_info )
131
{
132
memset( info, 0, sizeof( *info ));
133
info->dwSize = sizeof( *info );
134
135
if (bth_info->flags & BDIF_ADDRESS)
136
info->Address.ullLong = bth_info->address;
137
info->ulClassofDevice = bth_info->classOfDevice;
138
info->fConnected = !!(bth_info->flags & BDIF_CONNECTED);
139
info->fRemembered = !!(bth_info->flags & BDIF_PERSONAL);
140
info->fAuthenticated = !!(bth_info->flags & BDIF_PAIRED);
141
142
if (bth_info->flags & BDIF_NAME)
143
MultiByteToWideChar( CP_ACP, 0, bth_info->name, -1, info->szName, ARRAY_SIZE( info->szName ) );
144
}
145
146
static BOOL device_find_next( struct bluetooth_find_device *find, BLUETOOTH_DEVICE_INFO *info )
147
{
148
while (find->idx < find->device_list->numOfDevices)
149
{
150
const BTH_DEVICE_INFO *bth_info;
151
BOOL matches;
152
153
bth_info = &find->device_list->deviceList[find->idx++];
154
matches = (find->params.fReturnAuthenticated && bth_info->flags & BDIF_PAIRED) ||
155
(find->params.fReturnRemembered && bth_info->flags & BDIF_PERSONAL) ||
156
(find->params.fReturnUnknown && !(bth_info->flags & BDIF_PERSONAL)) ||
157
(find->params.fReturnConnected && bth_info->flags & BDIF_CONNECTED);
158
159
if (!matches)
160
continue;
161
162
device_info_from_bth_info( info, bth_info );
163
/* If the user is looking for unknown devices as part of device inquiry, or wants connected devices,
164
* we can set stLastSeen to the current UTC time. */
165
if ((find->params.fIssueInquiry && find->params.fReturnUnknown && !info->fRemembered)
166
|| (find->params.fReturnConnected && info->fConnected))
167
GetSystemTime( &info->stLastSeen );
168
else
169
FIXME( "semi-stub!\n" );
170
return TRUE;
171
}
172
173
return FALSE;
174
}
175
176
/*********************************************************************
177
* BluetoothFindFirstDevice
178
*/
179
HBLUETOOTH_DEVICE_FIND WINAPI BluetoothFindFirstDevice( BLUETOOTH_DEVICE_SEARCH_PARAMS *params,
180
BLUETOOTH_DEVICE_INFO *info )
181
{
182
struct bluetooth_find_device *hfind;
183
BTH_DEVICE_INFO_LIST *device_list;
184
185
TRACE( "(%s %p)\n", debugstr_BLUETOOTH_DEVICE_SEARCH_PARAMS( params ), info );
186
187
if (!params || !info)
188
{
189
SetLastError( ERROR_INVALID_PARAMETER );
190
return NULL;
191
}
192
if (params->dwSize != sizeof( *params ) || info->dwSize != sizeof( *info ))
193
{
194
SetLastError( ERROR_REVISION_MISMATCH );
195
return NULL;
196
}
197
if (params->fIssueInquiry && params->cTimeoutMultiplier > 48)
198
{
199
SetLastError( ERROR_INVALID_PARAMETER );
200
return NULL;
201
}
202
if (!params->hRadio)
203
{
204
/* TODO: Perform device inquiry on all local radios */
205
FIXME( "semi-stub!\n" );
206
SetLastError( ERROR_INVALID_PARAMETER );
207
return NULL;
208
}
209
210
if (params->fIssueInquiry)
211
{
212
if (!radio_set_inquiry( params->hRadio, TRUE ))
213
return NULL;
214
Sleep( params->cTimeoutMultiplier * 1280 );
215
radio_set_inquiry( params->hRadio, FALSE );
216
}
217
218
if (!(device_list = radio_get_devices( params->hRadio )))
219
return NULL;
220
221
hfind = malloc( sizeof( *hfind ) );
222
if (!hfind)
223
{
224
free( device_list );
225
SetLastError( ERROR_OUTOFMEMORY );
226
return NULL;
227
}
228
229
hfind->params = *params;
230
hfind->device_list = device_list;
231
hfind->idx = 0;
232
if (!BluetoothFindNextDevice( hfind, info ))
233
{
234
free( hfind->device_list );
235
free( hfind );
236
SetLastError( ERROR_NO_MORE_ITEMS );
237
return NULL;
238
}
239
SetLastError( ERROR_SUCCESS );
240
return hfind;
241
}
242
243
/*********************************************************************
244
* BluetoothFindFirstRadio
245
*/
246
HBLUETOOTH_RADIO_FIND WINAPI BluetoothFindFirstRadio( BLUETOOTH_FIND_RADIO_PARAMS *params, HANDLE *radio )
247
{
248
struct bluetooth_find_radio_handle *find;
249
HANDLE device_ret;
250
DWORD err;
251
252
TRACE( "(%p, %p)\n", params, radio );
253
254
if (!params)
255
{
256
SetLastError( ERROR_INVALID_PARAMETER );
257
return NULL;
258
}
259
if (params->dwSize != sizeof( *params ))
260
{
261
SetLastError( ERROR_REVISION_MISMATCH );
262
return NULL;
263
}
264
if (!(find = calloc( 1, sizeof( *find ) )))
265
{
266
SetLastError( ERROR_OUTOFMEMORY );
267
return NULL;
268
}
269
270
find->devinfo = SetupDiGetClassDevsW( &GUID_BTHPORT_DEVICE_INTERFACE, NULL, NULL,
271
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE );
272
if (find->devinfo == INVALID_HANDLE_VALUE)
273
{
274
free( find );
275
return NULL;
276
}
277
278
if (BluetoothFindNextRadio( find, &device_ret ))
279
{
280
*radio = device_ret;
281
return find;
282
}
283
284
err = GetLastError();
285
BluetoothFindRadioClose( find );
286
SetLastError( err );
287
return NULL;
288
}
289
290
/*********************************************************************
291
* BluetoothFindRadioClose
292
*/
293
BOOL WINAPI BluetoothFindRadioClose( HBLUETOOTH_RADIO_FIND find_handle )
294
{
295
struct bluetooth_find_radio_handle *find = find_handle;
296
297
TRACE( "(%p)\n", find_handle );
298
299
if (!find_handle)
300
{
301
SetLastError( ERROR_INVALID_HANDLE );
302
return FALSE;
303
}
304
305
SetupDiDestroyDeviceInfoList( find->devinfo );
306
free( find );
307
SetLastError( ERROR_SUCCESS );
308
return TRUE;
309
}
310
311
/*********************************************************************
312
* BluetoothFindDeviceClose
313
*/
314
BOOL WINAPI BluetoothFindDeviceClose( HBLUETOOTH_DEVICE_FIND find_handle )
315
{
316
struct bluetooth_find_device *find = find_handle;
317
318
TRACE( "(%p)\n", find_handle );
319
320
if (!find_handle)
321
{
322
SetLastError( ERROR_INVALID_HANDLE );
323
return FALSE;
324
}
325
326
free( find->device_list );
327
free( find );
328
SetLastError( ERROR_SUCCESS );
329
return TRUE;
330
}
331
332
/*********************************************************************
333
* BluetoothFindNextRadio
334
*/
335
BOOL WINAPI BluetoothFindNextRadio( HBLUETOOTH_RADIO_FIND find_handle, HANDLE *radio )
336
{
337
char buffer[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + MAX_PATH * sizeof( WCHAR )];
338
struct bluetooth_find_radio_handle *find = find_handle;
339
SP_DEVICE_INTERFACE_DETAIL_DATA_W *iface_detail = (SP_DEVICE_INTERFACE_DETAIL_DATA_W *)buffer;
340
SP_DEVICE_INTERFACE_DATA iface_data;
341
HANDLE device_ret;
342
BOOL found;
343
344
TRACE( "(%p, %p)\n", find_handle, radio );
345
346
if (!find_handle)
347
{
348
SetLastError( ERROR_INVALID_HANDLE );
349
return FALSE;
350
}
351
352
iface_detail->cbSize = sizeof( *iface_detail );
353
iface_data.cbSize = sizeof( iface_data );
354
found = FALSE;
355
while (SetupDiEnumDeviceInterfaces( find->devinfo, NULL, &GUID_BTHPORT_DEVICE_INTERFACE, find->idx++,
356
&iface_data ))
357
{
358
if (!SetupDiGetDeviceInterfaceDetailW( find->devinfo, &iface_data, iface_detail, sizeof( buffer ), NULL,
359
NULL ))
360
continue;
361
device_ret = CreateFileW( iface_detail->DevicePath, GENERIC_READ | GENERIC_WRITE,
362
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
363
if (device_ret != INVALID_HANDLE_VALUE)
364
{
365
found = TRUE;
366
break;
367
}
368
}
369
370
if (found)
371
*radio = device_ret;
372
373
return found;
374
}
375
376
/*********************************************************************
377
* BluetoothGetRadioInfo
378
*/
379
DWORD WINAPI BluetoothGetRadioInfo( HANDLE radio, PBLUETOOTH_RADIO_INFO info )
380
{
381
BOOL ret;
382
DWORD bytes = 0;
383
BTH_LOCAL_RADIO_INFO radio_info = {0};
384
385
TRACE( "(%p, %p)\n", radio, info );
386
387
if (!radio || !info)
388
return ERROR_INVALID_PARAMETER;
389
if (info->dwSize != sizeof( *info ))
390
return ERROR_REVISION_MISMATCH;
391
392
ret = DeviceIoControl( radio, IOCTL_BTH_GET_LOCAL_INFO, NULL, 0, &radio_info, sizeof( radio_info ), &bytes, NULL );
393
if (!ret)
394
return GetLastError();
395
396
if (radio_info.localInfo.flags & BDIF_ADDRESS)
397
info->address.ullLong = RtlUlonglongByteSwap( radio_info.localInfo.address ) >> 16;
398
if (radio_info.localInfo.flags & BDIF_COD)
399
info->ulClassofDevice = radio_info.localInfo.classOfDevice;
400
if (radio_info.localInfo.flags & BDIF_NAME)
401
MultiByteToWideChar( CP_ACP, 0, radio_info.localInfo.name, -1, info->szName, ARRAY_SIZE( info->szName ) );
402
403
info->lmpSubversion = radio_info.radioInfo.lmpSubversion;
404
info->manufacturer = radio_info.radioInfo.mfg;
405
406
return ERROR_SUCCESS;
407
}
408
409
/*********************************************************************
410
* BluetoothGetDeviceInfo
411
*/
412
DWORD WINAPI BluetoothGetDeviceInfo( HANDLE radio, BLUETOOTH_DEVICE_INFO *info )
413
{
414
const static BYTE addr_zero[6];
415
BTH_DEVICE_INFO_LIST *devices;
416
DWORD i, ret = ERROR_NOT_FOUND;
417
418
TRACE( "(%p, %p)\n", radio, info );
419
420
if (!radio)
421
return E_HANDLE;
422
if (!info)
423
return ERROR_INVALID_PARAMETER;
424
if (info->dwSize != sizeof( *info ))
425
return ERROR_REVISION_MISMATCH;
426
if (!memcmp( info->Address.rgBytes, addr_zero, sizeof( addr_zero ) ))
427
return ERROR_NOT_FOUND;
428
429
devices = radio_get_devices( radio );
430
if (!devices)
431
return GetLastError();
432
for (i = 0; i < devices->numOfDevices; i++)
433
{
434
if (devices->deviceList[i].address == info->Address.ullLong)
435
{
436
device_info_from_bth_info( info, &devices->deviceList[i] );
437
if (info->fConnected)
438
GetSystemTime( &info->stLastSeen );
439
else
440
FIXME( "semi-stub!\n" );
441
ret = ERROR_SUCCESS;
442
break;
443
}
444
}
445
446
free( devices );
447
return ret;
448
}
449
450
/*********************************************************************
451
* BluetoothIsConnectable
452
*/
453
BOOL WINAPI BluetoothIsConnectable( HANDLE radio )
454
{
455
TRACE( "(%p)\n", radio );
456
457
if (!radio)
458
{
459
BLUETOOTH_FIND_RADIO_PARAMS params = {.dwSize = sizeof( params )};
460
HBLUETOOTH_RADIO_FIND find = BluetoothFindFirstRadio( &params, &radio );
461
462
if (!find)
463
return FALSE;
464
for (;;)
465
{
466
if (BluetoothIsConnectable( radio ))
467
{
468
CloseHandle( radio );
469
BluetoothFindRadioClose( find );
470
return TRUE;
471
}
472
473
CloseHandle( radio );
474
if (!BluetoothFindNextRadio( find, &radio ))
475
{
476
BluetoothFindRadioClose( find );
477
return FALSE;
478
}
479
}
480
}
481
else
482
{
483
BTH_LOCAL_RADIO_INFO radio_info = {0};
484
DWORD bytes;
485
DWORD ret;
486
487
ret = DeviceIoControl( radio, IOCTL_BTH_GET_LOCAL_INFO, NULL, 0, &radio_info, sizeof( radio_info ), &bytes,
488
NULL );
489
if (!ret)
490
{
491
ERR( "DeviceIoControl failed: %#lx\n", GetLastError() );
492
return FALSE;
493
}
494
return !!(radio_info.flags & LOCAL_RADIO_CONNECTABLE);
495
}
496
}
497
498
/*********************************************************************
499
* BluetoothEnableIncomingConnections
500
*/
501
BOOL WINAPI BluetoothEnableIncomingConnections( HANDLE radio, BOOL enable )
502
{
503
TRACE( "(%p, %d)\n", radio, enable );
504
if (!radio)
505
{
506
BLUETOOTH_FIND_RADIO_PARAMS params = {.dwSize = sizeof( params )};
507
HBLUETOOTH_RADIO_FIND find = BluetoothFindFirstRadio( &params, &radio );
508
509
if (!find)
510
return FALSE;
511
for (;;)
512
{
513
if (BluetoothEnableIncomingConnections( radio, enable ))
514
{
515
CloseHandle( radio );
516
BluetoothFindRadioClose( find );
517
return TRUE;
518
}
519
520
CloseHandle(radio );
521
if (!BluetoothFindNextRadio( find, &radio ))
522
{
523
BluetoothFindRadioClose( find );
524
return FALSE;
525
}
526
}
527
}
528
else if (!enable && !BluetoothIsDiscoverable( radio ))
529
/* The local radio can only be made non-connectable if it is non-discoverable. */
530
return FALSE;
531
else
532
{
533
struct winebth_radio_set_flag_params params = {0};
534
BOOL ret;
535
DWORD bytes;
536
537
params.flag = LOCAL_RADIO_CONNECTABLE;
538
params.enable = !!enable;
539
ret = DeviceIoControl( radio, IOCTL_WINEBTH_RADIO_SET_FLAG, &params, sizeof( params ), NULL, 0, &bytes, NULL );
540
if (!ret)
541
ERR("DeviceIoControl failed: %#lx\n", GetLastError());
542
return ret;
543
}
544
}
545
546
/*********************************************************************
547
* BluetoothIsDiscoverable
548
*/
549
BOOL WINAPI BluetoothIsDiscoverable( HANDLE radio )
550
{
551
TRACE( "(%p)\n", radio );
552
553
if (!radio)
554
{
555
BLUETOOTH_FIND_RADIO_PARAMS params = {.dwSize = sizeof( params )};
556
HBLUETOOTH_RADIO_FIND find = BluetoothFindFirstRadio( &params, &radio );
557
558
if (!find)
559
return FALSE;
560
for (;;)
561
{
562
if (BluetoothIsDiscoverable( radio ))
563
{
564
CloseHandle( radio );
565
BluetoothFindRadioClose( find );
566
return TRUE;
567
}
568
569
CloseHandle( radio );
570
if (!BluetoothFindNextRadio( find, &radio ))
571
{
572
BluetoothFindRadioClose( find );
573
return FALSE;
574
}
575
}
576
}
577
else
578
{
579
BTH_LOCAL_RADIO_INFO radio_info = {0};
580
DWORD bytes;
581
DWORD ret;
582
583
ret = DeviceIoControl( radio, IOCTL_BTH_GET_LOCAL_INFO, NULL, 0, &radio_info, sizeof( radio_info ), &bytes,
584
NULL );
585
if (!ret)
586
{
587
ERR( "DeviceIoControl failed: %#lx\n", GetLastError() );
588
return FALSE;
589
}
590
return !!(radio_info.flags & LOCAL_RADIO_DISCOVERABLE);
591
}
592
}
593
594
/*********************************************************************
595
* BluetoothEnableDiscovery
596
*/
597
BOOL WINAPI BluetoothEnableDiscovery( HANDLE radio, BOOL enabled )
598
{
599
TRACE( "(%p, %d)\n", radio, enabled );
600
if (!radio)
601
{
602
BLUETOOTH_FIND_RADIO_PARAMS params = {.dwSize = sizeof( params )};
603
HBLUETOOTH_RADIO_FIND find = BluetoothFindFirstRadio( &params, &radio );
604
605
if (!find)
606
return FALSE;
607
for (;;)
608
{
609
if (BluetoothEnableDiscovery( radio, enabled ))
610
{
611
CloseHandle( radio );
612
BluetoothFindRadioClose( find );
613
return TRUE;
614
}
615
616
CloseHandle(radio );
617
if (!BluetoothFindNextRadio( find, &radio ))
618
{
619
BluetoothFindRadioClose( find );
620
return FALSE;
621
}
622
}
623
}
624
else if (enabled && !BluetoothIsConnectable( radio ))
625
/* The local radio can only be made discoverable if it is connectable. */
626
return FALSE;
627
else
628
{
629
struct winebth_radio_set_flag_params params = {0};
630
BOOL ret;
631
DWORD bytes;
632
633
params.flag = LOCAL_RADIO_DISCOVERABLE;
634
params.enable = !!enabled;
635
ret = DeviceIoControl( radio, IOCTL_WINEBTH_RADIO_SET_FLAG, &params, sizeof( params ), NULL, 0, &bytes, NULL );
636
if (!ret)
637
ERR("DeviceIoControl failed: %#lx\n", GetLastError());
638
return ret;
639
}
640
}
641
642
/*********************************************************************
643
* BluetoothFindNextDevice
644
*/
645
BOOL WINAPI BluetoothFindNextDevice( HBLUETOOTH_DEVICE_FIND find, BLUETOOTH_DEVICE_INFO *info )
646
{
647
BOOL success;
648
649
TRACE( "(%p, %p)\n", find, info );
650
651
/* This method doesn't perform any validation for info, for some reason. */
652
if (!find)
653
{
654
SetLastError( ERROR_INVALID_HANDLE );
655
return FALSE;
656
}
657
658
success = device_find_next( find, info );
659
if (!success)
660
SetLastError( ERROR_NO_MORE_ITEMS );
661
else
662
SetLastError( ERROR_SUCCESS );
663
return success;
664
}
665
666
struct bluetooth_auth_listener
667
{
668
struct list entry;
669
670
unsigned int all_devices : 1;
671
BLUETOOTH_ADDRESS addr;
672
673
PFN_AUTHENTICATION_CALLBACK_EX callback;
674
675
void *user_data;
676
};
677
678
struct bluetooth_auth_wizard_listener
679
{
680
struct list entry;
681
ULONG refcount;
682
683
BLUETOOTH_ADDRESS addr;
684
};
685
686
static const char *debugstr_bluetooth_auth_listener( const struct bluetooth_auth_listener *listener )
687
{
688
if (!listener)
689
return wine_dbg_sprintf( "(null)" );
690
if (listener->all_devices)
691
return wine_dbg_sprintf( "{ all_devices=1 %p %p }", listener->callback, listener->user_data );
692
return wine_dbg_sprintf( "{ all_devices=0 %s %p %p }", debugstr_addr( listener->addr.rgBytes ),
693
listener->callback, listener->user_data );
694
}
695
696
static SRWLOCK bluetooth_auth_lock = SRWLOCK_INIT;
697
/* A list of bluetooth_auth_listener objects added by BluetoothRegisterForAuthenticationEx.
698
* Guarded by bluetooth_auth_lock. */
699
static struct list bluetooth_auth_listeners = LIST_INIT( bluetooth_auth_listeners );
700
/* A list of fallback "listeners" created by BluetoothAuthenticateDeviceEx, used if no matching listener object
701
* could be found in bluetooth_auth_listeners. We use this to decide which pairing requests can be forwarded to a user
702
* wizard. Guarded by bluetooth_auth_lock. */
703
static struct list bluetooth_auth_wizard_listeners = LIST_INIT( bluetooth_auth_wizard_listeners );
704
static HCMNOTIFICATION bluetooth_auth_event_notify; /* Guarded by bluetooth_auth_lock */
705
706
struct auth_callback_data
707
{
708
PFN_AUTHENTICATION_CALLBACK_EX callback;
709
struct winebth_authentication_request request;
710
void *user_data;
711
};
712
713
static CALLBACK void tp_auth_callback_proc( TP_CALLBACK_INSTANCE *instance, void *ctx )
714
{
715
BLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS params;
716
struct auth_callback_data *data = ctx;
717
718
device_info_from_bth_info( &params.deviceInfo, &data->request.device_info );
719
/* For some reason, Windows uses the *local* time here. */
720
GetLocalTime( &params.deviceInfo.stLastSeen );
721
GetLocalTime( &params.deviceInfo.stLastUsed );
722
params.authenticationMethod = data->request.auth_method;
723
params.ioCapability = BLUETOOTH_IO_CAPABILITY_UNDEFINED;
724
params.authenticationRequirements = BLUETOOTH_MITM_ProtectionNotDefined;
725
params.Numeric_Value = data->request.numeric_value_or_passkey;
726
data->callback( data->user_data, &params );
727
free( data );
728
}
729
730
static void bluetooth_auth_wizard_ask_response( struct bluetooth_auth_wizard_listener *listener,
731
const struct winebth_authentication_request *request )
732
{
733
WCHAR *msg = NULL, title[1024], format[1024], name_or_addr[BLUETOOTH_MAX_NAME_SIZE];
734
BLUETOOTH_FIND_RADIO_PARAMS find_params = {.dwSize = sizeof( find_params )};
735
struct winebth_radio_send_auth_response_params params = {0};
736
HBLUETOOTH_RADIO_FIND radio_find;
737
DWORD_PTR format_args[2];
738
BLUETOOTH_ADDRESS addr;
739
DWORD ret, bytes;
740
UINT resource_id;
741
HANDLE radio;
742
743
switch (request->auth_method)
744
{
745
case BLUETOOTH_AUTHENTICATION_METHOD_NUMERIC_COMPARISON:
746
case BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY_NOTIFICATION:
747
break;
748
default:
749
FIXME( "Unsupported authentication method: %d\n", request->auth_method );
750
goto done;
751
}
752
753
addr.ullLong = request->device_info.address;
754
title[0] = format[0] = name_or_addr[0] = '\0';
755
if (request->device_info.flags & BDIF_NAME)
756
{
757
resource_id = IDS_AUTH_WIZARD_NAMED_PAIR_REQUEST;
758
MultiByteToWideChar( CP_ACP, 0, request->device_info.name, -1, name_or_addr, ARRAY_SIZE( name_or_addr ) );
759
}
760
else
761
{
762
resource_id = IDS_AUTH_WIZARD_UNNAMED_PAIR_REQUEST;
763
swprintf( name_or_addr, ARRAY_SIZE( name_or_addr ), L"%02X:%02X:%02X:%02X:%02X:%02X", addr.rgBytes[0],
764
addr.rgBytes[1], addr.rgBytes[2], addr.rgBytes[3], addr.rgBytes[4], addr.rgBytes[5] );
765
}
766
format_args[0] = (DWORD_PTR)name_or_addr;
767
format_args[1] = request->numeric_value_or_passkey;
768
if (LoadStringW( instance, resource_id, format, ARRAY_SIZE( format )))
769
FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
770
format, 0, 0, (WCHAR *)&msg, 0, (va_list *)format_args );
771
LoadStringW( instance, IDS_AUTH_WIZARD_PAIR_REQUEST_TITLE, title, ARRAY_SIZE( title ) );
772
ret = MessageBoxW( NULL, msg, title, MB_YESNO );
773
if (msg)
774
LocalFree( msg );
775
if (!ret)
776
{
777
ERR("MessageBoxW failed: %lu\n", ret );
778
goto done;
779
}
780
781
params.address = RtlUlonglongByteSwap( addr.ullLong ) >> 16;
782
params.method = request->auth_method;
783
params.numeric_value_or_passkey = request->numeric_value_or_passkey;
784
params.negative = ret != IDYES;
785
786
radio_find = BluetoothFindFirstRadio( &find_params, &radio );
787
if (!radio_find)
788
{
789
ERR( "BluetoothFindFirstRadio failed: %lu\n", GetLastError() );
790
goto done;
791
}
792
793
do {
794
if (DeviceIoControl( radio, IOCTL_WINEBTH_RADIO_SEND_AUTH_RESPONSE, &params, sizeof( params ),
795
&params, sizeof( params ), &bytes, NULL ))
796
{
797
ret = ERROR_SUCCESS;
798
CloseHandle( radio );
799
break;
800
}
801
ret = GetLastError();
802
CloseHandle( radio );
803
} while (BluetoothFindNextRadio( radio_find, &radio ));
804
805
BluetoothFindRadioClose( radio_find );
806
if (ret)
807
ERR( "Failed to send pairing response: %lu\n", ret );
808
done:
809
if (!--listener->refcount)
810
{
811
list_remove( &listener->entry );
812
free( listener );
813
}
814
}
815
816
static CALLBACK DWORD bluetooth_auth_event_callback( HCMNOTIFICATION notify, void *ctx, CM_NOTIFY_ACTION action,
817
CM_NOTIFY_EVENT_DATA *event_data, DWORD size )
818
{
819
struct winebth_authentication_request *request = (struct winebth_authentication_request *)event_data->u.DeviceHandle.Data;
820
struct bluetooth_auth_listener *listener;
821
BOOL try_wizard = TRUE;
822
823
TRACE( "(%p, %p, %d, %p, %lu)\n", notify, ctx, action, event_data, size );
824
825
switch (action)
826
{
827
case CM_NOTIFY_ACTION_DEVICECUSTOMEVENT:
828
if (!IsEqualGUID( &event_data->u.DeviceHandle.EventGuid, &GUID_WINEBTH_AUTHENTICATION_REQUEST ))
829
{
830
FIXME( "Unexpected EventGUID: %s\n", debugstr_guid(&event_data->u.DeviceHandle.EventGuid) );
831
break;
832
}
833
834
AcquireSRWLockExclusive( &bluetooth_auth_lock );
835
LIST_FOR_EACH_ENTRY( listener, &bluetooth_auth_listeners, struct bluetooth_auth_listener, entry )
836
{
837
struct auth_callback_data *data;
838
839
if (!(listener->all_devices || listener->addr.ullLong == request->device_info.address))
840
continue;
841
try_wizard = FALSE;
842
data = calloc( 1, sizeof( *data ) );
843
if (!data)
844
continue;
845
846
data->request = *request;
847
data->callback = listener->callback;
848
data->user_data = listener->user_data;
849
if (!TrySubmitThreadpoolCallback( tp_auth_callback_proc, data, NULL ))
850
{
851
ERR( "TrySubmitThreadpoolCallback failed: %lu\n", GetLastError() );
852
free( data );
853
continue;
854
}
855
}
856
857
/* No appropriate registered callback could be found for this request. Lets ask the user what to do instead. */
858
if (try_wizard)
859
{
860
struct bluetooth_auth_wizard_listener *listener, *next;
861
LIST_FOR_EACH_ENTRY_SAFE( listener, next, &bluetooth_auth_wizard_listeners,
862
struct bluetooth_auth_wizard_listener, entry )
863
{
864
if (listener->addr.ullLong == request->device_info.address)
865
{
866
bluetooth_auth_wizard_ask_response( listener, request );
867
break;
868
}
869
}
870
}
871
ReleaseSRWLockExclusive( &bluetooth_auth_lock );
872
break;
873
default:
874
FIXME( "Unexpected CM_NOTIFY_ACTION: %d\n", action );
875
break;
876
}
877
878
return 0;
879
}
880
881
static DWORD bluetooth_auth_register( void )
882
{
883
DWORD ret;
884
CM_NOTIFY_FILTER handle_filter = {0};
885
HANDLE winebth_auth;
886
DWORD bytes;
887
888
889
winebth_auth = CreateFileW( WINEBTH_AUTH_DEVICE_PATH, GENERIC_READ | GENERIC_WRITE,
890
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
891
if (winebth_auth == INVALID_HANDLE_VALUE)
892
return GetLastError();
893
894
handle_filter.cbSize = sizeof( handle_filter );
895
handle_filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE;
896
handle_filter.u.DeviceHandle.hTarget = winebth_auth;
897
898
ret = CM_Register_Notification( &handle_filter, NULL, bluetooth_auth_event_callback,
899
&bluetooth_auth_event_notify );
900
if (ret)
901
{
902
ERR( "CM_Register_Notification failed: %#lx\n", ret );
903
CloseHandle( winebth_auth );
904
return CM_MapCrToWin32Err( ret, ERROR_INTERNAL_ERROR );
905
}
906
907
if (!DeviceIoControl( winebth_auth, IOCTL_WINEBTH_AUTH_REGISTER, NULL, 0, NULL, 0, &bytes, NULL ))
908
ret = GetLastError();
909
910
CloseHandle( winebth_auth );
911
return ret;
912
}
913
914
/*********************************************************************
915
* BluetoothRegisterForAuthenticationEx
916
*/
917
DWORD WINAPI BluetoothRegisterForAuthenticationEx( const BLUETOOTH_DEVICE_INFO *device_info,
918
HBLUETOOTH_AUTHENTICATION_REGISTRATION *out,
919
PFN_AUTHENTICATION_CALLBACK_EX callback, void *param )
920
{
921
DWORD ret;
922
struct bluetooth_auth_listener *listener;
923
924
TRACE( "(%p, %p, %p, %p)\n", device_info, out, callback, param );
925
926
if (!out || (device_info && device_info->dwSize != sizeof ( *device_info )) || !callback)
927
return ERROR_INVALID_PARAMETER;
928
929
listener = calloc( 1, sizeof( *listener ) );
930
if (!listener)
931
return ERROR_OUTOFMEMORY;
932
listener->all_devices = !device_info;
933
if (!listener->all_devices)
934
{
935
TRACE( "Registering for authentication events from %s\n", debugstr_addr( device_info->Address.rgBytes ) );
936
listener->addr = device_info->Address;
937
}
938
listener->callback = callback;
939
listener->user_data = param;
940
941
AcquireSRWLockExclusive( &bluetooth_auth_lock );
942
if (list_empty( &bluetooth_auth_listeners ))
943
{
944
ret = bluetooth_auth_register();
945
if (ret)
946
{
947
free( listener );
948
ReleaseSRWLockExclusive( &bluetooth_auth_lock );
949
return ret;
950
}
951
}
952
/* The MSDN documentation for BluetoothRegisterForAuthentication states if applications call this method
953
* multiple times, only the first callback registered is invoked during an authentication session.
954
* This is incorrect, at least for Windows 11, where all registered callbacks do get called. */
955
list_add_tail( &bluetooth_auth_listeners, &listener->entry );
956
ReleaseSRWLockExclusive( &bluetooth_auth_lock );
957
958
*out = listener;
959
ret = ERROR_SUCCESS;
960
return ret;
961
}
962
963
/*********************************************************************
964
* BluetoothUnregisterAuthentication
965
*/
966
BOOL WINAPI BluetoothUnregisterAuthentication( HBLUETOOTH_AUTHENTICATION_REGISTRATION handle )
967
{
968
struct bluetooth_auth_listener *listener = handle;
969
DWORD ret = ERROR_SUCCESS;
970
971
TRACE( "(%s)\n", debugstr_bluetooth_auth_listener( handle ) );
972
973
if (!handle)
974
{
975
SetLastError( ERROR_INVALID_HANDLE );
976
return FALSE;
977
}
978
979
AcquireSRWLockExclusive( &bluetooth_auth_lock );
980
list_remove( &listener->entry );
981
if (list_empty( &bluetooth_auth_listeners ))
982
{
983
ret = CM_Unregister_Notification( bluetooth_auth_event_notify );
984
if (ret)
985
ERR( "CM_Unregister_Notification failed: %#lx\n", ret );
986
}
987
ReleaseSRWLockExclusive( &bluetooth_auth_lock );
988
989
SetLastError( ret );
990
return !ret;
991
}
992
993
static const char *debugstr_BLUETOOTH_AUTHENTICATE_RESPONSE( const BLUETOOTH_AUTHENTICATE_RESPONSE *resp )
994
{
995
if (!resp)
996
return wine_dbg_sprintf("(null)");
997
return wine_dbg_sprintf("{ bthAddressRemote: %s, authMethod: %d, negativeResponse: %d }",
998
debugstr_addr( resp->bthAddressRemote.rgBytes ), resp->authMethod, resp->negativeResponse );
999
}
1000
1001
DWORD WINAPI BluetoothSendAuthenticationResponseEx( HANDLE handle_radio, BLUETOOTH_AUTHENTICATE_RESPONSE *auth_response )
1002
{
1003
struct winebth_radio_send_auth_response_params params = {0};
1004
DWORD ret, bytes;
1005
1006
TRACE( "(%p, %s)\n", handle_radio, debugstr_BLUETOOTH_AUTHENTICATE_RESPONSE( auth_response ) );
1007
1008
if (!auth_response)
1009
return ERROR_INVALID_PARAMETER;
1010
switch (auth_response->authMethod)
1011
{
1012
case BLUETOOTH_AUTHENTICATION_METHOD_NUMERIC_COMPARISON:
1013
break;
1014
case BLUETOOTH_AUTHENTICATION_METHOD_LEGACY:
1015
case BLUETOOTH_AUTHENTICATION_METHOD_OOB:
1016
case BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY_NOTIFICATION:
1017
case BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY:
1018
FIXME( "Unsupported authMethod: %d\n", auth_response->authMethod );
1019
return ERROR_CALL_NOT_IMPLEMENTED;
1020
default:
1021
return ERROR_INVALID_PARAMETER;
1022
}
1023
if (!handle_radio)
1024
{
1025
HBLUETOOTH_RADIO_FIND radio_find;
1026
BLUETOOTH_FIND_RADIO_PARAMS find_params = {.dwSize = sizeof( find_params ) };
1027
1028
radio_find = BluetoothFindFirstRadio( &find_params, &handle_radio );
1029
if (!radio_find)
1030
return GetLastError();
1031
for(;;)
1032
{
1033
ret = BluetoothSendAuthenticationResponseEx( handle_radio, auth_response );
1034
CloseHandle( handle_radio );
1035
if (!ret)
1036
{
1037
BluetoothFindRadioClose( radio_find );
1038
return ERROR_SUCCESS;
1039
}
1040
1041
if (!BluetoothFindNextRadio( radio_find, &handle_radio ))
1042
{
1043
BluetoothFindRadioClose( radio_find );
1044
return ret;
1045
}
1046
}
1047
}
1048
1049
AcquireSRWLockShared( &bluetooth_auth_lock );
1050
if (list_empty( &bluetooth_auth_listeners ))
1051
{
1052
ret = ERROR_INVALID_PARAMETER;
1053
goto done;
1054
}
1055
params.address = RtlUlonglongByteSwap( auth_response->bthAddressRemote.ullLong ) >> 16;
1056
params.method = auth_response->authMethod;
1057
params.numeric_value_or_passkey = auth_response->numericCompInfo.NumericValue;
1058
params.negative = !!auth_response->negativeResponse;
1059
1060
if (!DeviceIoControl( handle_radio, IOCTL_WINEBTH_RADIO_SEND_AUTH_RESPONSE, &params, sizeof( params ),
1061
&params, sizeof( params ), &bytes, NULL ))
1062
{
1063
ret = GetLastError();
1064
goto done;
1065
}
1066
ret = params.authenticated ? ERROR_SUCCESS : ERROR_NOT_AUTHENTICATED;
1067
1068
done:
1069
ReleaseSRWLockShared( &bluetooth_auth_lock );
1070
return ret;
1071
}
1072
1073
DWORD WINAPI BluetoothAuthenticateDeviceEx( HWND parent, HANDLE handle_radio, BLUETOOTH_DEVICE_INFO *device_info,
1074
BLUETOOTH_OOB_DATA_INFO *oob_info,
1075
AUTHENTICATION_REQUIREMENTS auth_req )
1076
{
1077
OVERLAPPED ovl = {0};
1078
struct bluetooth_auth_wizard_listener *listener;
1079
BOOL wizard_listener_exists;
1080
BTH_ADDR device_addr;
1081
BOOL success;
1082
DWORD ret, bytes;
1083
1084
FIXME( "(%p, %p, %p, %p, %d): semi-stub!\n", parent, handle_radio, device_info, oob_info, auth_req );
1085
1086
if (!device_info || auth_req < MITMProtectionNotRequired || auth_req > MITMProtectionNotDefined)
1087
return ERROR_INVALID_PARAMETER;
1088
if (device_info->dwSize != sizeof( *device_info ))
1089
return ERROR_REVISION_MISMATCH;
1090
1091
TRACE( "Initiating pairing with %s\n", debugstr_addr( device_info->Address.rgBytes ) );
1092
if (!handle_radio)
1093
{
1094
BLUETOOTH_FIND_RADIO_PARAMS find_params = {.dwSize = sizeof( find_params )};
1095
HBLUETOOTH_RADIO_FIND radio_find;
1096
1097
radio_find = BluetoothFindFirstRadio( &find_params, &handle_radio );
1098
ret = GetLastError();
1099
if (!radio_find)
1100
return ret == ERROR_NO_MORE_ITEMS ? ERROR_NOT_FOUND : ret;
1101
do {
1102
ret = BluetoothAuthenticateDeviceEx( parent, handle_radio, device_info, oob_info, auth_req );
1103
CloseHandle( handle_radio );
1104
if (!ret || ret == ERROR_NO_MORE_ITEMS)
1105
break;
1106
} while (BluetoothFindNextRadio( radio_find, &handle_radio ));
1107
1108
BluetoothFindRadioClose( radio_find );
1109
return ret;
1110
}
1111
1112
AcquireSRWLockExclusive( &bluetooth_auth_lock );
1113
if (list_empty( &bluetooth_auth_listeners ) && list_empty( &bluetooth_auth_wizard_listeners ))
1114
{
1115
ret = bluetooth_auth_register();
1116
if (ret)
1117
{
1118
ReleaseSRWLockExclusive( &bluetooth_auth_lock );
1119
return ret;
1120
}
1121
}
1122
1123
wizard_listener_exists = FALSE;
1124
LIST_FOR_EACH_ENTRY( listener, &bluetooth_auth_wizard_listeners, struct bluetooth_auth_wizard_listener, entry )
1125
{
1126
if (listener->addr.ullLong == device_info->Address.ullLong)
1127
{
1128
listener->refcount++;
1129
wizard_listener_exists = TRUE;
1130
break;
1131
}
1132
}
1133
if (!wizard_listener_exists)
1134
{
1135
listener = calloc( 1, sizeof( *listener ) );
1136
if (!listener)
1137
{
1138
ReleaseSRWLockExclusive( &bluetooth_auth_lock );
1139
return ERROR_OUTOFMEMORY;
1140
}
1141
listener->addr = device_info->Address;
1142
list_add_tail( &bluetooth_auth_wizard_listeners, &listener->entry );
1143
}
1144
ReleaseSRWLockExclusive( &bluetooth_auth_lock );
1145
1146
ovl.hEvent = CreateEventW( NULL, TRUE, FALSE, NULL );
1147
device_addr = RtlUlonglongByteSwap( device_info->Address.ullLong ) >> 16;
1148
success = DeviceIoControl( handle_radio, IOCTL_WINEBTH_RADIO_START_AUTH, &device_addr, sizeof( device_addr ),
1149
NULL, 0, &bytes, &ovl );
1150
ret = ERROR_SUCCESS;
1151
if (!success)
1152
{
1153
ret = GetLastError();
1154
if (ret == ERROR_IO_PENDING)
1155
{
1156
ret = ERROR_SUCCESS;
1157
if (!GetOverlappedResult( handle_radio, &ovl, &bytes, TRUE ))
1158
ret = GetLastError();
1159
}
1160
}
1161
CloseHandle( ovl.hEvent );
1162
1163
return ret;
1164
}
1165
1166
DWORD WINAPI BluetoothRemoveDevice( BLUETOOTH_ADDRESS *addr )
1167
{
1168
BLUETOOTH_FIND_RADIO_PARAMS find_params = {0};
1169
HBLUETOOTH_RADIO_FIND radio_find;
1170
const static BYTE addr_zero[6];
1171
BOOL success = FALSE;
1172
HANDLE radio;
1173
DWORD ret;
1174
1175
TRACE( "(%s)\n", debugstr_addr( (BYTE *)addr ) );
1176
1177
if (!memcmp( addr_zero, &addr->rgBytes, sizeof( addr_zero ) ))
1178
return ERROR_NOT_FOUND;
1179
1180
find_params.dwSize = sizeof( find_params );
1181
radio_find = BluetoothFindFirstRadio( &find_params, &radio );
1182
if (!radio_find)
1183
{
1184
ret = GetLastError();
1185
return ret == ERROR_NO_MORE_ITEMS ? ERROR_NOT_FOUND : ret;
1186
}
1187
1188
do {
1189
DWORD bytes;
1190
success |= DeviceIoControl( radio, IOCTL_WINEBTH_RADIO_REMOVE_DEVICE, addr, sizeof( *addr ), NULL, 0, &bytes, NULL );
1191
CloseHandle( radio );
1192
} while (BluetoothFindNextRadio( radio_find, &radio ));
1193
1194
BluetoothFindRadioClose( radio_find );
1195
return success ? ERROR_SUCCESS : ERROR_NOT_FOUND;
1196
}
1197
1198
BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, void *reserved )
1199
{
1200
TRACE( "(%p, %lu, %p)\n", inst, reason, reserved );
1201
1202
if (reason == DLL_PROCESS_ATTACH)
1203
instance = inst;
1204
1205
return TRUE;
1206
}
1207
1208