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