From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/sdp.c | 228 ++++++++++++++++++++++++++++++++- dlls/bluetoothapis/tests/sdp.c | 38 +++--- 2 files changed, 245 insertions(+), 21 deletions(-)
diff --git a/dlls/bluetoothapis/sdp.c b/dlls/bluetoothapis/sdp.c index e3c6b27afc2..0b87e9488da 100644 --- a/dlls/bluetoothapis/sdp.c +++ b/dlls/bluetoothapis/sdp.c @@ -30,11 +30,235 @@
WINE_DEFAULT_DEBUG_CHANNEL( bluetoothapis );
+#ifdef WORDS_BIGENDIAN +#define BTH_READ_UINT16( s ) (s) +#define BTH_READ_UINT32( s ) (s) +#define BTH_READ_UINT64( s ) (s) +#else +#define BTH_READ_UINT16( s ) RtlUshortByteSwap( *(USHORT *)(s) ) +#define BTH_READ_UINT32( s ) RtlUlongByteSwap( *(ULONG *)(s) ) +#define BTH_READ_UINT64( s ) RtlUlonglongByteSwap( *(ULONGLONG *)(s) ) +#endif + +#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 ) { - FIXME( "(%p, %lu, %p) stub!\n", stream, stream_size, data ); - return ERROR_CALL_NOT_IMPLEMENTED; + 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 ); } diff --git a/dlls/bluetoothapis/tests/sdp.c b/dlls/bluetoothapis/tests/sdp.c index d6210d986d1..899acc55108 100644 --- a/dlls/bluetoothapis/tests/sdp.c +++ b/dlls/bluetoothapis/tests/sdp.c @@ -60,16 +60,16 @@ static void test_BluetoothSdpGetElementData_invalid( void ) DWORD ret;
ret = BluetoothSdpGetElementData( NULL, 10, &data ); - todo_wine ok( ret == ERROR_INVALID_PARAMETER, "%d != %ld.\n", ERROR_INVALID_PARAMETER, ret ); + ok( ret == ERROR_INVALID_PARAMETER, "%d != %ld.\n", ERROR_INVALID_PARAMETER, ret );
ret = BluetoothSdpGetElementData( stream, 1, NULL ); - todo_wine ok( ret == ERROR_INVALID_PARAMETER, "%d != %ld.\n", ERROR_INVALID_PARAMETER, ret ); + ok( ret == ERROR_INVALID_PARAMETER, "%d != %ld.\n", ERROR_INVALID_PARAMETER, ret );
ret = BluetoothSdpGetElementData( stream, 0, &data ); - todo_wine ok( ret == ERROR_INVALID_PARAMETER, "%d != %ld.\n", ERROR_INVALID_PARAMETER, ret ); + ok( ret == ERROR_INVALID_PARAMETER, "%d != %ld.\n", ERROR_INVALID_PARAMETER, ret );
ret = BluetoothSdpGetElementData( NULL, 0, NULL ); - todo_wine ok( ret == ERROR_INVALID_PARAMETER, "%d != %ld.\n", ERROR_INVALID_PARAMETER, ret ); + ok( ret == ERROR_INVALID_PARAMETER, "%d != %ld.\n", ERROR_INVALID_PARAMETER, ret ); }
static void test_BluetoothSdpGetElementData_nil( void ) @@ -90,8 +90,8 @@ static void test_BluetoothSdpGetElementData_nil( void ) for (i = 0; i < ARRAY_SIZE( test_cases ); i++) { winetest_push_context( "test_cases nil %d", (int)i ); - todo_wine test_BluetoothSdpGetElementData( &test_cases[i].data_elem, 1, test_cases[i].error, - &test_cases[i].data ); + test_BluetoothSdpGetElementData( &test_cases[i].data_elem, 1, test_cases[i].error, + &test_cases[i].data ); winetest_pop_context(); } } @@ -209,8 +209,8 @@ static void test_BluetoothSdpGetElementData_ints( void ) for (i = 0; i < ARRAY_SIZE( test_cases ); i++) { winetest_push_context( "test_cases int %d", (int)i ); - todo_wine test_BluetoothSdpGetElementData( test_cases[i].data_elem, test_cases[i].size, - test_cases[i].error, &test_cases[i].data ); + test_BluetoothSdpGetElementData( test_cases[i].data_elem, test_cases[i].size, + test_cases[i].error, &test_cases[i].data ); winetest_pop_context(); } } @@ -251,22 +251,22 @@ static void test_BluetoothSdpGetElementData_str( void ) for (i = 0; i < ARRAY_SIZE( test_cases ); i++) { winetest_push_context( "test_cases str %d", (int)i ); - todo_wine test_BluetoothSdpGetElementData( test_cases[i].stream, test_cases[i].size, - test_cases[i].error, &test_cases[i].data ); + test_BluetoothSdpGetElementData( test_cases[i].stream, test_cases[i].size, + test_cases[i].error, &test_cases[i].data ); if (test_cases[i].error == ERROR_SUCCESS) { SDP_ELEMENT_DATA result = {0}; if (!BluetoothSdpGetElementData( test_cases[i].stream, test_cases[i].size, &result )) { - todo_wine ok( strlen( test_cases[i].string ) == result.data.string.length, - "%s != %s.\n", debugstr_a( test_cases[i].string ), - debugstr_an( (const char *)result.data.string.value, - result.data.string.length ) ); - todo_wine ok( !memcmp( result.data.string.value, test_cases[i].string, - result.data.string.length ), - "%s != %s.\n", debugstr_a( test_cases[i].string ), - debugstr_an( (const char *)result.data.string.value, - result.data.string.length ) ); + ok( strlen( test_cases[i].string ) == result.data.string.length, "%s != %s.\n", + debugstr_a( test_cases[i].string ), + debugstr_an( (const char *)result.data.string.value, + result.data.string.length ) ); + ok( !memcmp( result.data.string.value, test_cases[i].string, + result.data.string.length ), + "%s != %s.\n", debugstr_a( test_cases[i].string ), + debugstr_an( (const char *)result.data.string.value, + result.data.string.length ) ); } } winetest_pop_context();