xref: /freebsd/usr.sbin/bluetooth/bthidcontrol/sdp.c (revision a7623790fb345e6dc986dfd31df0ace115e6f2e4)
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$
32  */
33 
34 #include <sys/types.h>
35 #include <sys/queue.h>
36 #include <sys/sysctl.h>
37 #define L2CAP_SOCKET_CHECKED
38 #include <bluetooth.h>
39 #include <dev/usb/usb.h>
40 #include <dev/usb/usbhid.h>
41 #include <errno.h>
42 #include <sdp.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <usbhid.h>
46 #include "bthid_config.h"
47 #include "bthidcontrol.h"
48 
49 static int32_t hid_sdp_query				(bdaddr_t const *local, struct hid_device *hd, int32_t *error);
50 static int32_t hid_sdp_parse_protocol_descriptor_list	(sdp_attr_p a);
51 static int32_t hid_sdp_parse_hid_descriptor		(sdp_attr_p a);
52 static int32_t hid_sdp_parse_boolean			(sdp_attr_p a);
53 
54 /*
55  * Hard coded attibute IDs taken from the
56  * DEVICE IDENTIFICATION PROFILE SPECIFICATION V13 p.12
57  */
58 
59 #define SDP_ATTR_DEVICE_ID_SERVICE_VENDORID  0x0201
60 #define SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID 0x0202
61 #define SDP_ATTR_DEVICE_ID_SERVICE_VERSION   0x0203
62 #define SDP_ATTR_DEVICE_ID_RANGE SDP_ATTR_RANGE( \
63  SDP_ATTR_DEVICE_ID_SERVICE_VENDORID, SDP_ATTR_DEVICE_ID_SERVICE_VERSION )
64 
65 static uint16_t		service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
66 static uint16_t		service_devid = SDP_SERVICE_CLASS_PNP_INFORMATION;
67 static uint32_t 	attrs_devid   = SDP_ATTR_DEVICE_ID_RANGE;
68 
69 static uint32_t		attrs[] = {
70 SDP_ATTR_RANGE(	SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
71 		SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
72 SDP_ATTR_RANGE	(SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
73 		SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
74 SDP_ATTR_RANGE(	0x0205,		/* HIDReconnectInitiate */
75 		0x0205),
76 SDP_ATTR_RANGE(	0x0206,		/* HIDDescriptorList */
77 		0x0206),
78 SDP_ATTR_RANGE(	0x0209,		/* HIDBatteryPower */
79 		0x0209),
80 SDP_ATTR_RANGE(	0x020d,		/* HIDNormallyConnectable */
81 		0x020d)
82 	};
83 #define	nattrs	(sizeof(attrs)/sizeof(attrs[0]))
84 
85 static sdp_attr_t	values[8];
86 #define	nvalues	(sizeof(values)/sizeof(values[0]))
87 
88 static uint8_t		buffer[nvalues][512];
89 
90 /*
91  * Query remote device
92  */
93 
94 #undef	hid_sdp_query_exit
95 #define	hid_sdp_query_exit(e) {		\
96 	if (error != NULL)		\
97 		*error = (e);		\
98 	if (ss != NULL) {		\
99 		sdp_close(ss);		\
100 		ss = NULL;		\
101 	}				\
102 	return (((e) == 0)? 0 : -1);	\
103 }
104 
105 static void
106 hid_init_return_values() {
107 	int i;
108 	for (i = 0; i < nvalues; i ++) {
109 		values[i].flags = SDP_ATTR_INVALID;
110 		values[i].attr = 0;
111 		values[i].vlen = sizeof(buffer[i]);
112 		values[i].value = buffer[i];
113 	}
114 }
115 
116 static int32_t
117 hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
118 {
119 	void			*ss = NULL;
120 	uint8_t			*hid_descriptor = NULL, *v;
121 	int32_t			 i, control_psm = -1, interrupt_psm = -1,
122 				 reconnect_initiate = -1,
123 				 normally_connectable = 0, battery_power = 0,
124 				 hid_descriptor_length = -1, type;
125 	int16_t 		 vendor_id = 0, product_id = 0, version = 0;
126 	bdaddr_t		 sdp_local;
127 	char			 devname[HCI_DEVNAME_SIZE];
128 
129 	if (local == NULL)
130 		local = NG_HCI_BDADDR_ANY;
131 	if (hd == NULL)
132 		hid_sdp_query_exit(EINVAL);
133 
134 	hid_init_return_values();
135 
136 	if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
137 		hid_sdp_query_exit(ENOMEM);
138 	if (sdp_error(ss) != 0)
139 		hid_sdp_query_exit(sdp_error(ss));
140 	if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
141                 hid_sdp_query_exit(sdp_error(ss));
142 
143 	for (i = 0; i < nvalues; i ++) {
144 		if (values[i].flags != SDP_ATTR_OK)
145 			continue;
146 
147 		switch (values[i].attr) {
148 		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
149 			control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
150 			break;
151 
152 		case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
153 			interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
154 			break;
155 
156 		case 0x0205: /* HIDReconnectInitiate */
157 			reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
158 			break;
159 
160 		case 0x0206: /* HIDDescriptorList */
161 			if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
162 				hid_descriptor = values[i].value;
163 				hid_descriptor_length = values[i].vlen;
164 			}
165 			break;
166 
167 		case 0x0209: /* HIDBatteryPower */
168 			battery_power = hid_sdp_parse_boolean(&values[i]);
169 			break;
170 
171 		case 0x020d: /* HIDNormallyConnectable */
172 			normally_connectable = hid_sdp_parse_boolean(&values[i]);
173 			break;
174 		}
175 	}
176 
177 	hid_init_return_values();
178 
179 	if (sdp_search(ss, 1, &service_devid, 1, &attrs_devid, nvalues, values) != 0)
180                 hid_sdp_query_exit(sdp_error(ss));
181 
182 	/* Try extract HCI bdaddr from opened SDP session */
183 	if (sdp_get_lcaddr(ss, &sdp_local) != 0 ||
184 	    bt_devname(devname, &sdp_local) == 0)
185 		hid_sdp_query_exit(ENOATTR);
186 
187         sdp_close(ss);
188         ss = NULL;
189 
190 	/* If search is successful, scan through return vals */
191 	for (i = 0; i < 3; i ++ ) {
192 		if (values[i].flags == SDP_ATTR_INVALID )
193 			continue;
194 
195 		/* Expecting tag + uint16_t on all 3 attributes */
196 		if (values[i].vlen != 3)
197 			continue;
198 
199 		/* Make sure, we're reading a uint16_t */
200 		v = values[i].value;
201 		SDP_GET8(type, v);
202 		if (type != SDP_DATA_UINT16 )
203 			continue;
204 
205 		switch (values[i].attr) {
206 			case SDP_ATTR_DEVICE_ID_SERVICE_VENDORID:
207 				SDP_GET16(vendor_id, v);
208 				break;
209 			case SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID:
210 				SDP_GET16(product_id, v);
211 				break;
212 			case SDP_ATTR_DEVICE_ID_SERVICE_VERSION:
213 				SDP_GET16(version, v);
214 				break;
215 			default:
216 				break;
217 		}
218 	}
219 
220 	if (control_psm == -1 || interrupt_psm == -1 ||
221 	    reconnect_initiate == -1 ||
222 	    hid_descriptor == NULL || hid_descriptor_length == -1)
223 		hid_sdp_query_exit(ENOATTR);
224 	hd->name = bt_devremote_name_gen(devname, &hd->bdaddr);
225 	hd->vendor_id = vendor_id;
226 	hd->product_id = product_id;
227 	hd->version = version;
228 	hd->control_psm = control_psm;
229 	hd->interrupt_psm = interrupt_psm;
230 	hd->reconnect_initiate = reconnect_initiate? 1 : 0;
231 	hd->battery_power = battery_power? 1 : 0;
232 	hd->normally_connectable = normally_connectable? 1 : 0;
233 	hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);
234 	if (hd->desc == NULL)
235 		hid_sdp_query_exit(ENOMEM);
236 
237 	return (0);
238 }
239 
240 /*
241  * seq len				2
242  *	seq len				2
243  *		uuid value		3
244  *		uint16 value		3
245  *		seq len			2
246  *			uuid value	3
247  */
248 
249 static int32_t
250 hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)
251 {
252 	uint8_t	*ptr = a->value;
253 	uint8_t	*end = a->value + a->vlen;
254 	int32_t	 type, len, uuid, psm;
255 
256 	if (end - ptr < 15)
257 		return (-1);
258 
259 	if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
260 		SDP_GET8(type, ptr);
261 		switch (type) {
262 		case SDP_DATA_SEQ8:
263 			SDP_GET8(len, ptr);
264 			break;
265 
266 		case SDP_DATA_SEQ16:
267 			SDP_GET16(len, ptr);
268 			break;
269 
270 		case SDP_DATA_SEQ32:
271 			SDP_GET32(len, ptr);
272 			break;
273 
274 		default:
275 			return (-1);
276 		}
277 		if (ptr + len > end)
278 			return (-1);
279 	}
280 
281 	SDP_GET8(type, ptr);
282 	switch (type) {
283 	case SDP_DATA_SEQ8:
284 		SDP_GET8(len, ptr);
285 		break;
286 
287 	case SDP_DATA_SEQ16:
288 		SDP_GET16(len, ptr);
289 		break;
290 
291 	case SDP_DATA_SEQ32:
292 		SDP_GET32(len, ptr);
293 		break;
294 
295 	default:
296 		return (-1);
297 	}
298 	if (ptr + len > end)
299 		return (-1);
300 
301 	/* Protocol */
302 	SDP_GET8(type, ptr);
303 	switch (type) {
304 	case SDP_DATA_SEQ8:
305 		SDP_GET8(len, ptr);
306 		break;
307 
308 	case SDP_DATA_SEQ16:
309 		SDP_GET16(len, ptr);
310 		break;
311 
312 	case SDP_DATA_SEQ32:
313 		SDP_GET32(len, ptr);
314 		break;
315 
316 	default:
317 		return (-1);
318 	}
319 	if (ptr + len > end)
320 		return (-1);
321 
322 	/* UUID */
323 	if (ptr + 3 > end)
324 		return (-1);
325 	SDP_GET8(type, ptr);
326 	switch (type) {
327 	case SDP_DATA_UUID16:
328 		SDP_GET16(uuid, ptr);
329 		if (uuid != SDP_UUID_PROTOCOL_L2CAP)
330 			return (-1);
331 		break;
332 
333 	case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
334 	case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
335 	default:
336 		return (-1);
337 	}
338 
339 	/* PSM */
340 	if (ptr + 3 > end)
341 		return (-1);
342 	SDP_GET8(type, ptr);
343 	if (type != SDP_DATA_UINT16)
344 		return (-1);
345 	SDP_GET16(psm, ptr);
346 
347 	return (psm);
348 }
349 
350 /*
351  * seq len			2
352  *	seq len			2
353  *		uint8 value8	2
354  * 		str value	3
355  */
356 
357 static int32_t
358 hid_sdp_parse_hid_descriptor(sdp_attr_p a)
359 {
360 	uint8_t	*ptr = a->value;
361 	uint8_t	*end = a->value + a->vlen;
362 	int32_t	 type, len, descriptor_type;
363 
364 	if (end - ptr < 9)
365 		return (-1);
366 
367 	SDP_GET8(type, ptr);
368 	switch (type) {
369 	case SDP_DATA_SEQ8:
370 		SDP_GET8(len, ptr);
371 		break;
372 
373 	case SDP_DATA_SEQ16:
374 		SDP_GET16(len, ptr);
375 		break;
376 
377 	case SDP_DATA_SEQ32:
378 		SDP_GET32(len, ptr);
379 		break;
380 
381 	default:
382 		return (-1);
383 	}
384 	if (ptr + len > end)
385 		return (-1);
386 
387 	while (ptr < end) {
388 		/* Descriptor */
389 		SDP_GET8(type, ptr);
390 		switch (type) {
391 		case SDP_DATA_SEQ8:
392 			if (ptr + 1 > end)
393 				return (-1);
394 			SDP_GET8(len, ptr);
395 			break;
396 
397 		case SDP_DATA_SEQ16:
398 			if (ptr + 2 > end)
399 				return (-1);
400 			SDP_GET16(len, ptr);
401 			break;
402 
403 		case SDP_DATA_SEQ32:
404 			if (ptr + 4 > end)
405 				return (-1);
406 			SDP_GET32(len, ptr);
407 			break;
408 
409 		default:
410 			return (-1);
411 		}
412 
413 		/* Descripor type */
414 		if (ptr + 1 > end)
415 			return (-1);
416 		SDP_GET8(type, ptr);
417 		if (type != SDP_DATA_UINT8 || ptr + 1 > end)
418 			return (-1);
419 		SDP_GET8(descriptor_type, ptr);
420 
421 		/* Descriptor value */
422 		if (ptr + 1 > end)
423 			return (-1);
424 		SDP_GET8(type, ptr);
425 		switch (type) {
426 		case SDP_DATA_STR8:
427 			if (ptr + 1 > end)
428 				return (-1);
429 			SDP_GET8(len, ptr);
430 			break;
431 
432 		case SDP_DATA_STR16:
433 			if (ptr + 2 > end)
434 				return (-1);
435 			SDP_GET16(len, ptr);
436 			break;
437 
438 		case SDP_DATA_STR32:
439 			if (ptr + 4 > end)
440 				return (-1);
441 			SDP_GET32(len, ptr);
442 			break;
443 
444 		default:
445 			return (-1);
446 		}
447 		if (ptr + len > end)
448 			return (-1);
449 
450 		if (descriptor_type == UDESC_REPORT && len > 0) {
451 			a->value = ptr;
452 			a->vlen = len;
453 
454 			return (0);
455 		}
456 
457 		ptr += len;
458 	}
459 
460 	return (-1);
461 }
462 
463 /* bool8 int8 */
464 static int32_t
465 hid_sdp_parse_boolean(sdp_attr_p a)
466 {
467 	if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
468 		return (-1);
469 
470 	return (a->value[1]);
471 }
472 
473 /* Perform SDP query */
474 static int32_t
475 hid_query(bdaddr_t *bdaddr, int argc, char **argv)
476 {
477 	struct hid_device	hd;
478 	int			e;
479 
480 	memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr));
481 	if (hid_sdp_query(NULL, &hd, &e) < 0) {
482 		fprintf(stderr, "Could not perform SDP query on the " \
483 			"device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL),
484 			strerror(e), e);
485 		return (FAILED);
486 	}
487 
488 	print_hid_device(&hd, stdout);
489 
490 	return (OK);
491 }
492 
493 struct bthid_command	sdp_commands[] =
494 {
495 {
496 "Query",
497 "Perform SDP query to the specified device and print HID configuration entry\n"\
498 "for the device. The configuration entry should be appended to the Bluetooth\n"\
499 "HID daemon configuration file and the daemon should be restarted.\n",
500 hid_query
501 },
502 { NULL, NULL, NULL }
503 };
504 
505