sdp.c revision 330449
1/*-
2 * sdp.c
3 *
4 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
5 *
6 * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * $Id: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $
31 * $FreeBSD: stable/11/usr.sbin/bluetooth/bthidcontrol/sdp.c 330449 2018-03-05 07:26:05Z eadler $
32 */
33
34#include <sys/queue.h>
35#define L2CAP_SOCKET_CHECKED
36#include <bluetooth.h>
37#include <dev/usb/usb.h>
38#include <dev/usb/usbhid.h>
39#include <errno.h>
40#include <sdp.h>
41#include <stdio.h>
42#include <string.h>
43#include <usbhid.h>
44#include "bthid_config.h"
45#include "bthidcontrol.h"
46
47static int32_t hid_sdp_query				(bdaddr_t const *local, struct hid_device *hd, int32_t *error);
48static int32_t hid_sdp_parse_protocol_descriptor_list	(sdp_attr_p a);
49static int32_t hid_sdp_parse_hid_descriptor		(sdp_attr_p a);
50static int32_t hid_sdp_parse_boolean			(sdp_attr_p a);
51
52static uint16_t		service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
53
54static uint32_t		attrs[] = {
55SDP_ATTR_RANGE(	SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
56		SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
57SDP_ATTR_RANGE	(SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
58		SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
59SDP_ATTR_RANGE(	0x0205,		/* HIDReconnectInitiate */
60		0x0205),
61SDP_ATTR_RANGE(	0x0206,		/* HIDDescriptorList */
62		0x0206),
63SDP_ATTR_RANGE(	0x0209,		/* HIDBatteryPower */
64		0x0209),
65SDP_ATTR_RANGE(	0x020d,		/* HIDNormallyConnectable */
66		0x020d)
67	};
68#define	nattrs	(sizeof(attrs)/sizeof(attrs[0]))
69
70static sdp_attr_t	values[8];
71#define	nvalues	(sizeof(values)/sizeof(values[0]))
72
73static uint8_t		buffer[nvalues][512];
74
75/*
76 * Query remote device
77 */
78
79#undef	hid_sdp_query_exit
80#define	hid_sdp_query_exit(e) {		\
81	if (error != NULL)		\
82		*error = (e);		\
83	if (ss != NULL) {		\
84		sdp_close(ss);		\
85		ss = NULL;		\
86	}				\
87	return (((e) == 0)? 0 : -1);	\
88}
89
90static int32_t
91hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
92{
93	void	*ss = NULL;
94	uint8_t	*hid_descriptor = NULL;
95	int32_t	 i, control_psm = -1, interrupt_psm = -1,
96		 reconnect_initiate = -1,
97		 normally_connectable = 0, battery_power = 0,
98		 hid_descriptor_length = -1;
99
100	if (local == NULL)
101		local = NG_HCI_BDADDR_ANY;
102	if (hd == NULL)
103		hid_sdp_query_exit(EINVAL);
104
105	for (i = 0; i < nvalues; i ++) {
106		values[i].flags = SDP_ATTR_INVALID;
107		values[i].attr = 0;
108		values[i].vlen = sizeof(buffer[i]);
109		values[i].value = buffer[i];
110	}
111
112	if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
113		hid_sdp_query_exit(ENOMEM);
114	if (sdp_error(ss) != 0)
115		hid_sdp_query_exit(sdp_error(ss));
116	if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
117                hid_sdp_query_exit(sdp_error(ss));
118
119        sdp_close(ss);
120        ss = NULL;
121
122	for (i = 0; i < nvalues; i ++) {
123		if (values[i].flags != SDP_ATTR_OK)
124			continue;
125
126		switch (values[i].attr) {
127		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
128			control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
129			break;
130
131		case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
132			interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
133			break;
134
135		case 0x0205: /* HIDReconnectInitiate */
136			reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
137			break;
138
139		case 0x0206: /* HIDDescriptorList */
140			if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
141				hid_descriptor = values[i].value;
142				hid_descriptor_length = values[i].vlen;
143			}
144			break;
145
146		case 0x0209: /* HIDBatteryPower */
147			battery_power = hid_sdp_parse_boolean(&values[i]);
148			break;
149
150		case 0x020d: /* HIDNormallyConnectable */
151			normally_connectable = hid_sdp_parse_boolean(&values[i]);
152			break;
153		}
154	}
155
156	if (control_psm == -1 || interrupt_psm == -1 ||
157	    reconnect_initiate == -1 ||
158	    hid_descriptor == NULL || hid_descriptor_length == -1)
159		hid_sdp_query_exit(ENOATTR);
160
161	hd->control_psm = control_psm;
162	hd->interrupt_psm = interrupt_psm;
163	hd->reconnect_initiate = reconnect_initiate? 1 : 0;
164	hd->battery_power = battery_power? 1 : 0;
165	hd->normally_connectable = normally_connectable? 1 : 0;
166	hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);
167	if (hd->desc == NULL)
168		hid_sdp_query_exit(ENOMEM);
169
170	return (0);
171}
172
173/*
174 * seq len				2
175 *	seq len				2
176 *		uuid value		3
177 *		uint16 value		3
178 *		seq len			2
179 *			uuid value	3
180 */
181
182static int32_t
183hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)
184{
185	uint8_t	*ptr = a->value;
186	uint8_t	*end = a->value + a->vlen;
187	int32_t	 type, len, uuid, psm;
188
189	if (end - ptr < 15)
190		return (-1);
191
192	if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
193		SDP_GET8(type, ptr);
194		switch (type) {
195		case SDP_DATA_SEQ8:
196			SDP_GET8(len, ptr);
197			break;
198
199		case SDP_DATA_SEQ16:
200			SDP_GET16(len, ptr);
201			break;
202
203		case SDP_DATA_SEQ32:
204			SDP_GET32(len, ptr);
205			break;
206
207		default:
208			return (-1);
209		}
210		if (ptr + len > end)
211			return (-1);
212	}
213
214	SDP_GET8(type, ptr);
215	switch (type) {
216	case SDP_DATA_SEQ8:
217		SDP_GET8(len, ptr);
218		break;
219
220	case SDP_DATA_SEQ16:
221		SDP_GET16(len, ptr);
222		break;
223
224	case SDP_DATA_SEQ32:
225		SDP_GET32(len, ptr);
226		break;
227
228	default:
229		return (-1);
230	}
231	if (ptr + len > end)
232		return (-1);
233
234	/* Protocol */
235	SDP_GET8(type, ptr);
236	switch (type) {
237	case SDP_DATA_SEQ8:
238		SDP_GET8(len, ptr);
239		break;
240
241	case SDP_DATA_SEQ16:
242		SDP_GET16(len, ptr);
243		break;
244
245	case SDP_DATA_SEQ32:
246		SDP_GET32(len, ptr);
247		break;
248
249	default:
250		return (-1);
251	}
252	if (ptr + len > end)
253		return (-1);
254
255	/* UUID */
256	if (ptr + 3 > end)
257		return (-1);
258	SDP_GET8(type, ptr);
259	switch (type) {
260	case SDP_DATA_UUID16:
261		SDP_GET16(uuid, ptr);
262		if (uuid != SDP_UUID_PROTOCOL_L2CAP)
263			return (-1);
264		break;
265
266	case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
267	case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
268	default:
269		return (-1);
270	}
271
272	/* PSM */
273	if (ptr + 3 > end)
274		return (-1);
275	SDP_GET8(type, ptr);
276	if (type != SDP_DATA_UINT16)
277		return (-1);
278	SDP_GET16(psm, ptr);
279
280	return (psm);
281}
282
283/*
284 * seq len			2
285 *	seq len			2
286 *		uint8 value8	2
287 * 		str value	3
288 */
289
290static int32_t
291hid_sdp_parse_hid_descriptor(sdp_attr_p a)
292{
293	uint8_t	*ptr = a->value;
294	uint8_t	*end = a->value + a->vlen;
295	int32_t	 type, len, descriptor_type;
296
297	if (end - ptr < 9)
298		return (-1);
299
300	SDP_GET8(type, ptr);
301	switch (type) {
302	case SDP_DATA_SEQ8:
303		SDP_GET8(len, ptr);
304		break;
305
306	case SDP_DATA_SEQ16:
307		SDP_GET16(len, ptr);
308		break;
309
310	case SDP_DATA_SEQ32:
311		SDP_GET32(len, ptr);
312		break;
313
314	default:
315		return (-1);
316	}
317	if (ptr + len > end)
318		return (-1);
319
320	while (ptr < end) {
321		/* Descriptor */
322		SDP_GET8(type, ptr);
323		switch (type) {
324		case SDP_DATA_SEQ8:
325			if (ptr + 1 > end)
326				return (-1);
327			SDP_GET8(len, ptr);
328			break;
329
330		case SDP_DATA_SEQ16:
331			if (ptr + 2 > end)
332				return (-1);
333			SDP_GET16(len, ptr);
334			break;
335
336		case SDP_DATA_SEQ32:
337			if (ptr + 4 > end)
338				return (-1);
339			SDP_GET32(len, ptr);
340			break;
341
342		default:
343			return (-1);
344		}
345
346		/* Descripor type */
347		if (ptr + 1 > end)
348			return (-1);
349		SDP_GET8(type, ptr);
350		if (type != SDP_DATA_UINT8 || ptr + 1 > end)
351			return (-1);
352		SDP_GET8(descriptor_type, ptr);
353
354		/* Descriptor value */
355		if (ptr + 1 > end)
356			return (-1);
357		SDP_GET8(type, ptr);
358		switch (type) {
359		case SDP_DATA_STR8:
360			if (ptr + 1 > end)
361				return (-1);
362			SDP_GET8(len, ptr);
363			break;
364
365		case SDP_DATA_STR16:
366			if (ptr + 2 > end)
367				return (-1);
368			SDP_GET16(len, ptr);
369			break;
370
371		case SDP_DATA_STR32:
372			if (ptr + 4 > end)
373				return (-1);
374			SDP_GET32(len, ptr);
375			break;
376
377		default:
378			return (-1);
379		}
380		if (ptr + len > end)
381			return (-1);
382
383		if (descriptor_type == UDESC_REPORT && len > 0) {
384			a->value = ptr;
385			a->vlen = len;
386
387			return (0);
388		}
389
390		ptr += len;
391	}
392
393	return (-1);
394}
395
396/* bool8 int8 */
397static int32_t
398hid_sdp_parse_boolean(sdp_attr_p a)
399{
400	if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
401		return (-1);
402
403	return (a->value[1]);
404}
405
406/* Perform SDP query */
407static int32_t
408hid_query(bdaddr_t *bdaddr, int argc, char **argv)
409{
410	struct hid_device	hd;
411	int			e;
412
413	memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr));
414	if (hid_sdp_query(NULL, &hd, &e) < 0) {
415		fprintf(stderr, "Could not perform SDP query on the " \
416			"device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL),
417			strerror(e), e);
418		return (FAILED);
419	}
420
421	print_hid_device(&hd, stdout);
422
423	return (OK);
424}
425
426struct bthid_command	sdp_commands[] =
427{
428{
429"Query",
430"Perform SDP query to the specified device and print HID configuration entry\n"\
431"for the device. The configuration entry should be appended to the Bluetooth\n"\
432"HID daemon configuration file and the daemon should be restarted.\n",
433hid_query
434},
435{ NULL, NULL, NULL }
436};
437
438