mirror of
https://gitlab.winehq.org/wine/wine.git
synced 2025-08-29 02:33:58 +02:00
415 lines
13 KiB
C
415 lines
13 KiB
C
/*
|
|
* SDP APIs
|
|
*
|
|
* Copyright 2024 Vibhav Pant
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include <windef.h>
|
|
#include <winbase.h>
|
|
#include <winternl.h>
|
|
|
|
#include "wine/debug.h"
|
|
#include "bthsdpdef.h"
|
|
#include "bluetoothapis.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL( bluetoothapis );
|
|
|
|
#define BTH_READ_UINT16( s ) RtlUshortByteSwap( *(USHORT *)(s) )
|
|
#define BTH_READ_UINT32( s ) RtlUlongByteSwap( *(ULONG *)(s) )
|
|
#define BTH_READ_UINT64( s ) RtlUlonglongByteSwap( *(ULONGLONG *)(s) )
|
|
|
|
#define SDP_SIZE_DESC_1_BYTE 0
|
|
#define SDP_SIZE_DESC_2_BYTES 1
|
|
#define SDP_SIZE_DESC_4_BYTES 2
|
|
#define SDP_SIZE_DESC_8_BYTES 3
|
|
#define SDP_SIZE_DESC_16_BYTES 4
|
|
#define SDP_SIZE_DESC_NEXT_UINT8 5
|
|
#define SDP_SIZE_DESC_NEXT_UINT16 6
|
|
#define SDP_SIZE_DESC_NEXT_UINT32 7
|
|
|
|
static void bth_read_uint128( BYTE *s, SDP_ULARGE_INTEGER_16 *v )
|
|
{
|
|
v->HighPart = BTH_READ_UINT64( s );
|
|
v->LowPart = BTH_READ_UINT64( s + 8 );
|
|
}
|
|
|
|
static BYTE data_elem_type( BYTE elem ) { return (elem & 0xf8) >> 3; }
|
|
static BYTE data_elem_size_desc( BYTE elem ) { return elem & 0x7; }
|
|
|
|
#define SDP_ELEMENT_IS_UINT16( d ) ( (d)->type == SDP_TYPE_UINT && (d)->specificType == SDP_ST_UINT16 )
|
|
#define SDP_ELEMENT_IS_ATTRID( d ) SDP_ELEMENT_IS_UINT16((d))
|
|
|
|
/* Read the data element's size/length as described by the size descriptor, starting from stream. Only
|
|
* valid for SDP_SIZE_DESC_NEXT_* types. */
|
|
static BOOL sdp_elem_read_var_size( BYTE *stream, ULONG stream_size, SIZE_T *read, BYTE size_desc,
|
|
UINT32 *size )
|
|
{
|
|
switch (size_desc)
|
|
{
|
|
case SDP_SIZE_DESC_NEXT_UINT8:
|
|
if (stream_size < sizeof( UINT8 )) return FALSE;
|
|
*size = *stream;
|
|
*read += sizeof( UINT8 );
|
|
return TRUE;
|
|
case SDP_SIZE_DESC_NEXT_UINT16:
|
|
if (stream_size < sizeof( UINT16 )) return FALSE;
|
|
*size = BTH_READ_UINT16( stream );
|
|
*read += sizeof( UINT16 );
|
|
return TRUE;
|
|
case SDP_SIZE_DESC_NEXT_UINT32:
|
|
if (stream_size < sizeof( UINT32 )) return FALSE;
|
|
*size = BTH_READ_UINT32( stream );
|
|
*read += sizeof( UINT32 );
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
const static SDP_SPECIFICTYPE SDP_BASIC_TYPES[4][5] = {
|
|
[SDP_TYPE_UINT] =
|
|
{
|
|
[SDP_SIZE_DESC_1_BYTE] = SDP_ST_UINT8,
|
|
[SDP_SIZE_DESC_2_BYTES] = SDP_ST_UINT16,
|
|
[SDP_SIZE_DESC_4_BYTES] = SDP_ST_UINT32,
|
|
[SDP_SIZE_DESC_8_BYTES] = SDP_ST_UINT64,
|
|
[SDP_SIZE_DESC_16_BYTES] = SDP_ST_UINT128,
|
|
},
|
|
[SDP_TYPE_INT] =
|
|
{
|
|
[SDP_SIZE_DESC_1_BYTE] = SDP_ST_INT8,
|
|
[SDP_SIZE_DESC_2_BYTES] = SDP_ST_INT16,
|
|
[SDP_SIZE_DESC_4_BYTES] = SDP_ST_INT32,
|
|
[SDP_SIZE_DESC_8_BYTES] = SDP_ST_INT64,
|
|
[SDP_SIZE_DESC_16_BYTES] = SDP_ST_INT128,
|
|
},
|
|
[SDP_TYPE_UUID] =
|
|
{
|
|
[SDP_SIZE_DESC_2_BYTES] = SDP_ST_UUID16,
|
|
[SDP_SIZE_DESC_4_BYTES] = SDP_ST_UUID32,
|
|
[SDP_SIZE_DESC_16_BYTES] = SDP_ST_UUID128,
|
|
},
|
|
};
|
|
|
|
static BOOL sdp_read_specific_type( BYTE *stream, ULONG stream_size, SDP_SPECIFICTYPE st,
|
|
SDP_ELEMENT_DATA *data, SIZE_T *read )
|
|
{
|
|
switch (st)
|
|
{
|
|
case SDP_ST_UINT8:
|
|
case SDP_ST_INT8:
|
|
if (stream_size < sizeof( UINT8 )) return FALSE;
|
|
data->data.uint8 = *stream;
|
|
*read += sizeof( UINT8 );
|
|
break;
|
|
case SDP_ST_UINT16:
|
|
case SDP_ST_INT16:
|
|
case SDP_ST_UUID16:
|
|
if (stream_size < sizeof( UINT16 )) return FALSE;
|
|
data->data.uint16 = BTH_READ_UINT16( stream );
|
|
*read += sizeof( UINT16 );
|
|
break;
|
|
case SDP_ST_UINT32:
|
|
case SDP_ST_INT32:
|
|
if (stream_size < sizeof( UINT32 )) return FALSE;
|
|
data->data.uint32 = BTH_READ_UINT32( stream );
|
|
*read += sizeof( UINT32 );
|
|
break;
|
|
case SDP_ST_UINT64:
|
|
case SDP_ST_INT64:
|
|
if (stream_size < sizeof( UINT64 )) return FALSE;
|
|
data->data.uint64 = BTH_READ_UINT64( stream );
|
|
*read += sizeof( UINT64 );
|
|
break;
|
|
case SDP_ST_UINT128:
|
|
case SDP_ST_INT128:
|
|
case SDP_ST_UUID128:
|
|
if (stream_size < sizeof( SDP_ULARGE_INTEGER_16 )) return FALSE;
|
|
bth_read_uint128( stream, &data->data.uint128 );
|
|
*read += sizeof( SDP_ULARGE_INTEGER_16 );
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static DWORD sdp_read_element_data( BYTE *stream, ULONG stream_size, SDP_ELEMENT_DATA *data,
|
|
SIZE_T *read )
|
|
{
|
|
BYTE type, size_desc, elem;
|
|
SDP_SPECIFICTYPE st;
|
|
|
|
if (stream_size < sizeof( BYTE )) return ERROR_INVALID_PARAMETER;
|
|
|
|
elem = *stream;
|
|
type = data_elem_type( elem );
|
|
size_desc = data_elem_size_desc( elem );
|
|
|
|
stream += sizeof( BYTE );
|
|
*read += sizeof( BYTE );
|
|
stream_size -= sizeof( BYTE );
|
|
|
|
memset( data, 0, sizeof( *data ) );
|
|
switch (type)
|
|
{
|
|
case SDP_TYPE_NIL:
|
|
if (size_desc != 0) return ERROR_INVALID_PARAMETER;
|
|
|
|
data->type = type;
|
|
data->specificType = SDP_ST_NONE;
|
|
break;
|
|
case SDP_TYPE_UINT:
|
|
case SDP_TYPE_INT:
|
|
case SDP_TYPE_UUID:
|
|
if (size_desc > SDP_SIZE_DESC_16_BYTES) return ERROR_INVALID_PARAMETER;
|
|
|
|
st = SDP_BASIC_TYPES[type][size_desc];
|
|
if (st == SDP_ST_NONE) return ERROR_INVALID_PARAMETER;
|
|
|
|
if (!sdp_read_specific_type( stream, stream_size, st, data, read ))
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
data->type = type;
|
|
data->specificType = st;
|
|
break;
|
|
case SDP_TYPE_BOOLEAN:
|
|
if (size_desc != SDP_SIZE_DESC_1_BYTE) return ERROR_INVALID_PARAMETER;
|
|
if (stream_size < sizeof( BYTE )) return ERROR_INVALID_PARAMETER;
|
|
|
|
data->type = type;
|
|
data->specificType = SDP_ST_NONE;
|
|
data->data.booleanVal = *stream;
|
|
*read += sizeof( BYTE );
|
|
break;
|
|
case SDP_TYPE_STRING:
|
|
case SDP_TYPE_URL:
|
|
case SDP_TYPE_SEQUENCE:
|
|
case SDP_TYPE_ALTERNATIVE:
|
|
{
|
|
UINT32 elems_size;
|
|
SIZE_T size_read = 0;
|
|
|
|
if (!(size_desc >= SDP_SIZE_DESC_NEXT_UINT8 && size_desc <= SDP_SIZE_DESC_NEXT_UINT32))
|
|
return ERROR_INVALID_PARAMETER;
|
|
if (!sdp_elem_read_var_size( stream, stream_size, &size_read, size_desc, &elems_size ))
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
stream_size -= size_read;
|
|
if (type == SDP_TYPE_STRING || type == SDP_TYPE_URL)
|
|
stream += size_read;
|
|
|
|
if (stream_size < elems_size) return ERROR_INVALID_PARAMETER;
|
|
|
|
data->type = type;
|
|
data->specificType = SDP_ST_NONE;
|
|
if (type == SDP_TYPE_STRING || type == SDP_TYPE_URL)
|
|
{
|
|
data->data.string.value = stream;
|
|
data->data.string.length = elems_size;
|
|
}
|
|
else
|
|
{
|
|
/* For sequence and alternative containers, the stream should begin at the container
|
|
* header. */
|
|
data->data.sequence.value = stream - sizeof( BYTE );
|
|
data->data.sequence.length = elems_size + *read + size_read;
|
|
}
|
|
*read += size_read + elems_size;
|
|
break;
|
|
}
|
|
default:
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* BluetoothSdpGetElementData
|
|
*/
|
|
DWORD WINAPI BluetoothSdpGetElementData( BYTE *stream, ULONG stream_size, SDP_ELEMENT_DATA *data )
|
|
{
|
|
SIZE_T read = 0;
|
|
|
|
TRACE( "(%p, %lu, %p)\n", stream, stream_size, data );
|
|
|
|
if (stream == NULL || stream_size < sizeof( BYTE ) || data == NULL)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
return sdp_read_element_data( stream, stream_size, data, &read );
|
|
}
|
|
|
|
/*********************************************************************
|
|
* BluetoothSdpGetContainerElementData
|
|
*/
|
|
DWORD WINAPI BluetoothSdpGetContainerElementData( BYTE *stream, ULONG stream_size,
|
|
HBLUETOOTH_CONTAINER_ELEMENT *handle,
|
|
SDP_ELEMENT_DATA *data )
|
|
{
|
|
BYTE *cursor;
|
|
DWORD result;
|
|
SIZE_T read = 0;
|
|
|
|
TRACE( "(%p, %lu, %p, %p)\n", stream, stream_size, handle, data );
|
|
|
|
if (stream == NULL || stream_size < sizeof( BYTE ) || handle == NULL || data == NULL)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
cursor = (BYTE *)(*handle);
|
|
|
|
if (cursor == NULL)
|
|
{
|
|
BYTE header, type, size_desc;
|
|
UINT32 elems_size = 0;
|
|
SIZE_T read = 0;
|
|
|
|
header = *stream;
|
|
type = data_elem_type( header );
|
|
size_desc = data_elem_size_desc( header );
|
|
|
|
if (type != SDP_TYPE_SEQUENCE && type != SDP_TYPE_ALTERNATIVE)
|
|
return ERROR_INVALID_PARAMETER;
|
|
if (!(size_desc >= SDP_SIZE_DESC_NEXT_UINT8 && size_desc <= SDP_SIZE_DESC_NEXT_UINT32))
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
stream++;
|
|
if (!sdp_elem_read_var_size( stream, stream_size, &read, size_desc, &elems_size ))
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
stream += read;
|
|
stream_size -= read;
|
|
}
|
|
else
|
|
{
|
|
if (cursor < stream) return ERROR_INVALID_PARAMETER;
|
|
if (cursor == (stream + stream_size)) return ERROR_NO_MORE_ITEMS;
|
|
|
|
stream = cursor;
|
|
stream_size = stream_size - (cursor - stream);
|
|
}
|
|
result = sdp_read_element_data( stream, stream_size, data, &read );
|
|
if (result != ERROR_SUCCESS) return result;
|
|
|
|
stream += read;
|
|
TRACE( "handle=%p\n", stream );
|
|
*handle = stream;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* BluetoothSdpEnumAttributes
|
|
*/
|
|
BOOL WINAPI BluetoothSdpEnumAttributes( BYTE *stream, ULONG stream_size,
|
|
PFN_BLUETOOTH_ENUM_ATTRIBUTES_CALLBACK callback, void *param )
|
|
{
|
|
SDP_ELEMENT_DATA data = {0};
|
|
DWORD result;
|
|
HBLUETOOTH_CONTAINER_ELEMENT cursor = NULL;
|
|
|
|
TRACE( "(%p, %ld, %p, %p)\n", stream, stream_size, callback, param );
|
|
|
|
if (stream == NULL || callback == NULL) return ERROR_INVALID_PARAMETER;
|
|
|
|
result = BluetoothSdpGetElementData( stream, stream_size, &data );
|
|
if (result != ERROR_SUCCESS)
|
|
{
|
|
SetLastError( ERROR_INVALID_DATA );
|
|
return FALSE;
|
|
}
|
|
|
|
switch (data.type)
|
|
{
|
|
case SDP_TYPE_SEQUENCE:
|
|
case SDP_TYPE_ALTERNATIVE:
|
|
break;
|
|
default:
|
|
SetLastError( ERROR_INVALID_DATA );
|
|
return FALSE;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
SDP_ELEMENT_DATA attrid = {0};
|
|
SDP_ELEMENT_DATA attr = {0};
|
|
BYTE *raw_attr_stream;
|
|
|
|
result = BluetoothSdpGetContainerElementData( data.data.sequence.value,
|
|
data.data.sequence.length, &cursor, &attrid );
|
|
if (result == ERROR_NO_MORE_ITEMS) return TRUE;
|
|
if (result || !SDP_ELEMENT_IS_ATTRID( &attrid ))
|
|
{
|
|
SetLastError( ERROR_INVALID_DATA );
|
|
return FALSE;
|
|
}
|
|
|
|
raw_attr_stream = cursor;
|
|
result = BluetoothSdpGetContainerElementData( data.data.sequence.value,
|
|
data.data.sequence.length, &cursor, &attr );
|
|
if (result != ERROR_SUCCESS)
|
|
{
|
|
SetLastError( ERROR_INVALID_DATA );
|
|
return FALSE;
|
|
}
|
|
if (!callback( attrid.data.uint16, raw_attr_stream, ((BYTE *)cursor - raw_attr_stream),
|
|
param ))
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
struct get_attr_value_data
|
|
{
|
|
USHORT attr_id;
|
|
BYTE *attr_stream;
|
|
ULONG stream_size;
|
|
};
|
|
|
|
static BOOL WINAPI get_attr_value_callback( ULONG attr_id, BYTE *stream, ULONG stream_size,
|
|
void *params )
|
|
{
|
|
struct get_attr_value_data *args = params;
|
|
if (attr_id == args->attr_id)
|
|
{
|
|
args->attr_stream = stream;
|
|
args->stream_size = stream_size;
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* BluetoothSdpGetAttributeValue
|
|
*/
|
|
DWORD WINAPI BluetoothSdpGetAttributeValue( BYTE *stream, ULONG stream_size, USHORT attr_id,
|
|
SDP_ELEMENT_DATA *data )
|
|
{
|
|
struct get_attr_value_data args = {0};
|
|
|
|
TRACE( "(%p %lu %u %p)\n", stream, stream_size, attr_id, data );
|
|
|
|
if (stream == NULL || data == NULL) return ERROR_INVALID_PARAMETER;
|
|
|
|
args.attr_id = attr_id;
|
|
if (!BluetoothSdpEnumAttributes( stream, stream_size, get_attr_value_callback, &args ))
|
|
return ERROR_INVALID_PARAMETER;
|
|
if (!args.attr_stream) return ERROR_FILE_NOT_FOUND;
|
|
|
|
return BluetoothSdpGetElementData( args.attr_stream, args.stream_size, data );
|
|
}
|