Home > Back-end >  How to use WSASetService to advertise bluetooth service using WSAQUERYSET structure on Windows devic
How to use WSASetService to advertise bluetooth service using WSAQUERYSET structure on Windows devic

Time:12-25

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
};
  • Related