I read from https://docs.microsoft.com/en-us/windows/win32/bluetooth/bluetooth-and-wsasetservice that there are two ways to submit a service to local SDP server:
The application can have the system advertise a simple Bluetooth SDP service record, constructed from standard members in the WSAQUERYSET structure.
The application can have the system advertise their own Bluetooth SDP record by passing a BTH_SET_SERVICE structure in the lpBlob member of the WSAQUERYSET structure. This is a more complex approach.
For me, the first is preferred, but all code examples I can find now use the second. I think it probably involves setting information about the service, such as address, port, service uuid in WSAQUERYSET members:
// Service Information
int port = 4;
BTH_ADDR address = xxx;
GUID serviceUuid = {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx};
char* serviceName = "xxx";
//....
// Putting service information into WSAQUERYSET
WSAQUERYSET Service;
memset( &Service, 0, sizeof( Service ) );
Service.dwSize = sizeof( Service );
Service.dwNameSpace = NS_BTH;
//Service.xxx = xxx
//...
if ( WSASetService( &Service, RNRSERVICE_REGISTER, 0 ) == SOCKET_ERROR ) {
return WSAGetLastError();
}
else {
return 0;
}
Currently, I'm guessing how to set these attributes, but somehow I could not successfully advertise a service that could be detected. I really hope to have some workable code examples.
CodePudding user response:
at first you need not hardcode port = 4;
but use bind
for get dynamic free port and getsockname
for get port number. for instance (without error check)
SOCKADDR_BTH asi = { AF_BTH, 0, {}, BT_PORT_ANY };
SOCKET s = WSASocket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM,
0, 0, WSA_FLAG_NO_HANDLE_INHERIT|WSA_FLAG_OVERLAPPED);
bind(s, (PSOCKADDR)&asi, sizeof(asi));
int len = sizeof(asi);
getsockname(s, (PSOCKADDR)&asi, &len);
then you need call WSASetService, as described in Bluetooth and WSAQUERYSET for Set Service. here described all params, used in WSAQUERYSET
. only one exception - lpszServiceInstanceName described as
Optional, but recommended.
but if not use it - we got An invalid argument was supplied. so this is mandatory parameter.
ULONG BthRegisterService(LPCGUID lpServiceClassId, PCWSTR ServiceName, PSOCKADDR_BTH asi)
{
CSADDR_INFO csa = {
{ (PSOCKADDR)asi, sizeof(SOCKADDR_BTH) }, {}, SOCK_STREAM, BTHPROTO_RFCOMM
};
WSAQUERYSET wqs = { sizeof(wqs) };
wqs.lpszServiceInstanceName = const_cast<PWSTR>(ServiceName);
wqs.lpServiceClassId = const_cast<PGUID>(lpServiceClassId);
wqs.dwNameSpace = NS_BTH;
wqs.dwNumberOfCsAddrs = 1;
wqs.lpcsaBuffer = &csa;
return WSASetService(&wqs, RNRSERVICE_REGISTER, 0) ? WSAGetLastError() : NOERROR;
}
cons of this way - we can not got back HANDLE
of record which we register, for late call WSASetService
with RNRSERVICE_DELETE
- for this need use BLOB
which point to BTH_SET_SERVICE
. for this we need format by self raw SDP record stream.
so call BthRegisterService
we can in whis way:
struct __declspec(uuid("00112233-4455-6677-8899-aabbccddeeff")) MyServiceClass;
BthRegisterService(&__uuidof(MyServiceClass), L"*", &asi);
however, if we can format SDP record, we also can direct use IOCTL_BTH_SDP_SUBMIT_RECORD
for register and later IOCTL_BTH_SDP_REMOVE_RECORD
for deregister
for open bluetooth device need use CM_Get_Device_Interface_ListW
with GUID_BTHPORT_DEVICE_INTERFACE
SDP record formated by NspSetService look like
UCHAR SdpRecordTmplt[] = {
// [//////////////////////////////////////////////////////////////////////////////////////////////////////////
0x35, 0x33, //
//
// UINT16:SDP_ATTRIB_CLASS_ID_LIST //
0x09, 0x00, 0x01, //
// [///////////////////////////////////////////////////////////////////////////////////////////////// //
0x35, 0x11, // //
// UUID128:{guid} // //
0x1c, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // //
// ]///////////////////////////////////////////////////////////////////////////////////////////////// //
//
// UINT16:SDP_ATTRIB_PROTOCOL_DESCRIPTOR_LIST //
0x09, 0x00, 0x04, //
// [///////////////////////////////// //
0x35, 0x0f, // //
// (L2CAP, PSM=PSM_RFCOMM) // //
// [///////////////////////// // //
0x35, 0x06, // // //
// UUID16:L2CAP_PROTOCOL_UUID16 // // //
0x19, 0x01, 0x00, // // //
// UINT16:PSM_RFCOMM // // //
0x09, 0x00, 0x03, // // //
// ]///////////////////////// // //
// (RFCOMM, CN=Port) // //
// [///////////////////////// // //
0x35, 0x05, // // //
// UUID16:RFCOMM_PROTOCOL_UUID16 // // //
0x19, 0x00, 0x03, // // //
// UINT8:CN // // //
0x08, 0x**, // // //
// ]///////////////////////// // //
// ]///////////////////////////////// //
//
// UINT16:SDP_ATTRIB_SERVICE_NAME //
0x09, 0x01, 0x00, //
// STR:4 "ABCD" //
0x25, 0x04, 0x41, 0x42, 0x43, 0x44 //
// ]//////////////////////////////////////////////////////////////////////////////////////////////////////////
};
here only UUID128 (ServiceClassId), rfcomm port number and name is changed
even if we format raw SDP record himself - windows add to it 2 attributes:
(UINT16:ServiceRecordHandle) (UINT32:xxxxx)
and
(UINT16:BrowseGroupList) [ (UUID16:PublicBrowseRoot) ]
so for example if we pass next sdp record:
[ // 30
(UINT16:ServiceClassIDList)
[ // 11
(UUID128:00112233-4455-6677-8899-aabbccddeeff)
]
(UINT16:ProtocolDescriptorList)
[ // C
[ // 3
(UUID16:L2CAP_PROTOCOL_UUID16)
]
[ // 5
(UUID16:PSM_RFCOMM)
(UINT8:CN=Port)
]
]
(UINT16:ServiceName)
("ABCD")
]
windows convert it (extend) to
[ // 40
(UINT16:ServiceRecordHandle) (UINT32:10001)
(UINT16:ServiceClassIDList)
[ // 11
(UUID128:00112233-4455-6677-8899-aabbccddeeff)
]
(UINT16:ProtocolDescriptorList)
[ // C
[ // 3
(UUID16:L2CAP_PROTOCOL_UUID16)
]
[ // 5
(UUID16:PSM_RFCOMM)
(UINT8:CN=Port)
]
]
(UINT16:BrowseGroupList)
[ // 3
(UUID16:PublicBrowseRoot)
]
(UINT16:ServiceName)
("ABCD")
]
or in raw bytes - the record:
UCHAR sdp[] = {
0x35 ,0x30 ,0x09 ,0x00 ,0x01 ,0x35 ,0x11 ,0x1c
,0x00 ,0x11 ,0x22 ,0x33 ,0x44 ,0x55 ,0x66 ,0x77
,0x88 ,0x99 ,0xaa ,0xbb ,0xcc ,0xdd ,0xee ,0xff
,0x09 ,0x00 ,0x04 ,0x35 ,0x0c ,0x35 ,0x03 ,0x19
,0x01 ,0x00 ,0x35 ,0x05 ,0x19 ,0x00 ,0x03 ,0x08
,0x04 ,0x09 ,0x01 ,0x00 ,0x25 ,0x04 ,0x41 ,0x42
,0x43 ,0x44
};
converted to
UCHAR sdp_final[] = {
0x35 ,0x40 ,0x09 ,0x00 ,0x00 ,0x0a ,0x00 ,0x01
,0x00 ,0x01 ,0x09 ,0x00 ,0x01 ,0x35 ,0x11 ,0x1c
,0x00 ,0x11 ,0x22 ,0x33 ,0x44 ,0x55 ,0x66 ,0x77
,0x88 ,0x99 ,0xaa ,0xbb ,0xcc ,0xdd ,0xee ,0xff
,0x09 ,0x00 ,0x04 ,0x35 ,0x0c ,0x35 ,0x03 ,0x19
,0x01 ,0x00 ,0x35 ,0x05 ,0x19 ,0x00 ,0x03 ,0x08
,0x04 ,0x09 ,0x00 ,0x05 ,0x35 ,0x03 ,0x19 ,0x10
,0x02 ,0x09 ,0x01 ,0x00 ,0x25 ,0x04 ,0x41 ,0x42
,0x43 ,0x44
};