1 1.2 plunky /* $NetBSD: db.c,v 1.2 2015/11/24 21:11:39 plunky Exp $ */ 2 1.1 plunky 3 1.1 plunky /*- 4 1.1 plunky * Copyright (c) 2009 The NetBSD Foundation, Inc. 5 1.1 plunky * All rights reserved. 6 1.1 plunky * 7 1.1 plunky * This code is derived from software contributed to The NetBSD Foundation 8 1.1 plunky * by Iain Hibbert. 9 1.1 plunky * 10 1.1 plunky * Redistribution and use in source and binary forms, with or without 11 1.1 plunky * modification, are permitted provided that the following conditions 12 1.1 plunky * are met: 13 1.1 plunky * 1. Redistributions of source code must retain the above copyright 14 1.1 plunky * notice, this list of conditions and the following disclaimer. 15 1.1 plunky * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 plunky * notice, this list of conditions and the following disclaimer in the 17 1.1 plunky * documentation and/or other materials provided with the distribution. 18 1.1 plunky * 19 1.1 plunky * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 plunky * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 plunky * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 plunky * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 plunky * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 plunky * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 plunky * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 plunky * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 plunky * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 plunky * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 plunky * POSSIBILITY OF SUCH DAMAGE. 30 1.1 plunky */ 31 1.1 plunky 32 1.1 plunky #include <sys/cdefs.h> 33 1.2 plunky __RCSID("$NetBSD: db.c,v 1.2 2015/11/24 21:11:39 plunky Exp $"); 34 1.1 plunky 35 1.1 plunky #include <bluetooth.h> 36 1.1 plunky #include <sdp.h> 37 1.1 plunky #include <stdbool.h> 38 1.1 plunky #include <stdlib.h> 39 1.1 plunky #include <string.h> 40 1.1 plunky #include <uuid.h> 41 1.1 plunky 42 1.1 plunky #include "sdpd.h" 43 1.1 plunky 44 1.1 plunky /* 45 1.1 plunky * Using a prebuilt service record means that providing ServerState 46 1.1 plunky * and a non-hardcoded ProviderName are difficult. Look into that later. 47 1.1 plunky */ 48 1.1 plunky 49 1.1 plunky /* ServiceDiscoveryServer service record */ 50 1.1 plunky static uint8_t sds_data[] = { 51 1.1 plunky 0x09, 0x00, 0x00, // uint16 ServiceRecordHandle 52 1.1 plunky 0x0a, 0x00, 0x00, 0x00, // uint32 0x00000000 53 1.1 plunky 0x00, 54 1.1 plunky 55 1.1 plunky 0x09, 0x00, 0x01, // uint16 ServiceClassIDList 56 1.1 plunky 0x35, 0x03, // seq8(3) 57 1.1 plunky 0x19, 0x10, 0x00, // uuid16 ServiceDiscoveryServer 58 1.1 plunky 59 1.1 plunky 0x09, 0x00, 0x04, // uint16 ProtocolDescriptorList 60 1.1 plunky 0x35, 0x0d, // seq8(13) 61 1.1 plunky 0x35, 0x06, // seq8(6) 62 1.1 plunky 0x19, 0x01, 0x00, // uuid16 L2CAP 63 1.1 plunky 0x09, 0x00, 0x01, // uint16 L2CAP_PSM_SDP 64 1.1 plunky 0x35, 0x03, // seq8(3) 65 1.1 plunky 0x19, 0x00, 0x01, // uuid16 SDP 66 1.1 plunky 67 1.1 plunky 0x09, 0x00, 0x05, // uint16 BrowseGroupList 68 1.1 plunky 0x35, 0x03, // seq8(3) 69 1.1 plunky 0x19, 0x10, 0x02, // uuid16 PublicBrowseGroup 70 1.1 plunky 71 1.1 plunky 0x09, 0x00, 0x06, // uint16 LanguageBaseAttributeIDList 72 1.1 plunky 0x35, 0x09, // seq8(9) 73 1.1 plunky 0x09, 0x65, 0x6e, // uint16 0x656e ("en") 74 1.1 plunky 0x09, 0x00, 0x6a, // uint16 106 (UTF-8) 75 1.1 plunky 0x09, 0x01, 0x00, // uint16 PrimaryLanguageBaseID 76 1.1 plunky 77 1.1 plunky 0x09, 0x01, 0x00, // uint16 PrimaryLanguageBaseID + ServiceNameOffset 78 1.1 plunky 0x25, 0x1b, 0x42, 0x6c, // str8(27) "Bluetooth service discovery" 79 1.1 plunky 0x75, 0x65, 0x74, 0x6f, 80 1.1 plunky 0x6f, 0x74, 0x68, 0x20, 81 1.1 plunky 0x73, 0x65, 0x72, 0x76, 82 1.1 plunky 0x69, 0x63, 0x65, 0x20, 83 1.1 plunky 0x64, 0x69, 0x73, 0x63, 84 1.1 plunky 0x6f, 0x76, 0x65, 0x72, 85 1.1 plunky 0x79, 86 1.1 plunky 87 1.1 plunky 0x09, 0x01, 0x02, // uint16 PrimaryLanguageBaseID + ProviderNameOffset 88 1.1 plunky 0x25, 0x06, 0x4e, 0x65, // str8(6) "NetBSD" 89 1.1 plunky 0x74, 0x42, 0x53, 0x44, 90 1.1 plunky 91 1.1 plunky 0x09, 0x02, 0x00, // uint16 VersionNumberList 92 1.1 plunky 0x35, 0x03, // seq8(3) 93 1.1 plunky 0x09, 0x01, 0x00, // uint16 v1.0 94 1.1 plunky }; 95 1.1 plunky 96 1.1 plunky /* BrowseGroupDescriptor service record */ 97 1.1 plunky static uint8_t bgd_data[] = { 98 1.1 plunky 0x09, 0x00, 0x00, // uint16 ServiceRecordHandle 99 1.2 plunky 0x0a, 0x00, 0x01, 0x00, // uint32 0x00010000 100 1.2 plunky 0x00, 101 1.1 plunky 102 1.1 plunky 0x09, 0x00, 0x01, // uint16 ServiceClassIDList 103 1.1 plunky 0x35, 0x03, // seq8(3) 104 1.1 plunky 0x19, 0x10, 0x01, // uuid16 BrowseGroupDescriptor 105 1.1 plunky 106 1.1 plunky 0x09, 0x00, 0x06, // uint16 LanguageBaseAttributeIDList 107 1.1 plunky 0x35, 0x09, // seq8(9) 108 1.1 plunky 0x09, 0x65, 0x6e, // uint16 0x656e ("en") 109 1.1 plunky 0x09, 0x00, 0x6a, // uint16 106 (UTF-8) 110 1.1 plunky 0x09, 0x01, 0x00, // uint16 PrimaryLanguageBaseID 111 1.1 plunky 112 1.1 plunky 0x09, 0x01, 0x00, // uint16 PrimaryLanguageBaseID + ServiceNameOffset 113 1.1 plunky 0x25, 0x12, 0x50, 0x75, // str8(18) "Public Browse Root" 114 1.1 plunky 0x62, 0x6c, 0x69, 0x63, 115 1.1 plunky 0x20, 0x42, 0x72, 0x6f, 116 1.1 plunky 0x77, 0x73, 0x65, 0x20, 117 1.1 plunky 0x52, 0x6f, 0x6f, 0x74, 118 1.1 plunky 119 1.1 plunky 0x09, 0x02, 0x00, // uint16 GroupID 120 1.1 plunky 0x19, 0x10, 0x02, // uuid16 PublicBrowseRoot 121 1.1 plunky }; 122 1.1 plunky 123 1.1 plunky /* 124 1.1 plunky * Initialise the record database with the ServiceDiscoveryServer 125 1.1 plunky * and BrowseGroupDescriptor records 126 1.1 plunky */ 127 1.1 plunky bool 128 1.1 plunky db_init(server_t *srv) 129 1.1 plunky { 130 1.1 plunky sdp_data_t d; 131 1.1 plunky 132 1.1 plunky LIST_INIT(&srv->rlist); 133 1.2 plunky srv->handle = 0x00010000; /* values 0x00000001->0x0000FFFF are reserved */ 134 1.1 plunky 135 1.1 plunky d.next = sds_data; 136 1.1 plunky d.end = sds_data + sizeof(sds_data); 137 1.2 plunky if (!db_create(srv, -1, BDADDR_ANY, 0x00000000, &d)) 138 1.1 plunky return false; 139 1.1 plunky 140 1.1 plunky d.next = bgd_data; 141 1.1 plunky d.end = bgd_data + sizeof(bgd_data); 142 1.1 plunky if (!db_create(srv, -1, BDADDR_ANY, srv->handle++, &d)) 143 1.1 plunky return false; 144 1.1 plunky 145 1.1 plunky return true; 146 1.1 plunky } 147 1.1 plunky 148 1.1 plunky /* 149 1.1 plunky * Iterate through records selected by fd. rec should point to a NULL 150 1.1 plunky * value to start the iteration, and false will be returned when there 151 1.1 plunky * are no more records to return. 152 1.1 plunky */ 153 1.1 plunky bool 154 1.1 plunky db_next(server_t *srv, int fd, record_t **rec) 155 1.1 plunky { 156 1.1 plunky record_t *r; 157 1.1 plunky 158 1.1 plunky if (*rec == NULL) 159 1.1 plunky r = LIST_FIRST(&srv->rlist); 160 1.1 plunky else 161 1.1 plunky r = LIST_NEXT(*rec, next); 162 1.1 plunky 163 1.1 plunky while (r != NULL && !FD_ISSET(fd, &r->refset)) 164 1.1 plunky r = LIST_NEXT(r, next); 165 1.1 plunky 166 1.1 plunky *rec = r; 167 1.1 plunky return (r == NULL) ? false : true; 168 1.1 plunky } 169 1.1 plunky 170 1.1 plunky /* 171 1.1 plunky * Match a ServiceRecord against a UUID. Note that because we already 172 1.1 plunky * know that the record data is valid, we don't need to recurse here 173 1.1 plunky * and can just skip over SEQ and ALT headers. Return true if equivalent 174 1.1 plunky * UUID is found. 175 1.1 plunky */ 176 1.1 plunky static bool 177 1.1 plunky db_match_uuid(record_t *rec, uuid_t *uuid) 178 1.1 plunky { 179 1.1 plunky uint8_t *p = rec->data.next; 180 1.1 plunky uuid_t u; 181 1.1 plunky 182 1.1 plunky while (p < rec->data.end) { 183 1.1 plunky switch(*p++) { 184 1.1 plunky case SDP_DATA_NIL: 185 1.1 plunky break; 186 1.1 plunky 187 1.1 plunky case SDP_DATA_BOOL: 188 1.1 plunky case SDP_DATA_INT8: 189 1.1 plunky case SDP_DATA_UINT8: 190 1.1 plunky case SDP_DATA_SEQ8: 191 1.1 plunky case SDP_DATA_ALT8: 192 1.1 plunky p += 1; 193 1.1 plunky break; 194 1.1 plunky 195 1.1 plunky case SDP_DATA_INT16: 196 1.1 plunky case SDP_DATA_UINT16: 197 1.1 plunky case SDP_DATA_SEQ16: 198 1.1 plunky case SDP_DATA_ALT16: 199 1.1 plunky p += 2; 200 1.1 plunky break; 201 1.1 plunky 202 1.1 plunky case SDP_DATA_INT32: 203 1.1 plunky case SDP_DATA_UINT32: 204 1.1 plunky case SDP_DATA_SEQ32: 205 1.1 plunky case SDP_DATA_ALT32: 206 1.1 plunky p += 4; 207 1.1 plunky break; 208 1.1 plunky 209 1.1 plunky case SDP_DATA_INT64: 210 1.1 plunky case SDP_DATA_UINT64: 211 1.1 plunky p += 8; 212 1.1 plunky break; 213 1.1 plunky 214 1.1 plunky case SDP_DATA_INT128: 215 1.1 plunky case SDP_DATA_UINT128: 216 1.1 plunky p += 16; 217 1.1 plunky break; 218 1.1 plunky 219 1.1 plunky case SDP_DATA_STR8: 220 1.1 plunky case SDP_DATA_URL8: 221 1.1 plunky p += 1 + *p; 222 1.1 plunky break; 223 1.1 plunky 224 1.1 plunky case SDP_DATA_STR16: 225 1.1 plunky case SDP_DATA_URL16: 226 1.1 plunky p += 2 + be16dec(p); 227 1.1 plunky break; 228 1.1 plunky 229 1.1 plunky case SDP_DATA_STR32: 230 1.1 plunky case SDP_DATA_URL32: 231 1.1 plunky p += 4 + be32dec(p); 232 1.1 plunky break; 233 1.1 plunky 234 1.1 plunky case SDP_DATA_UUID16: 235 1.1 plunky u = BLUETOOTH_BASE_UUID; 236 1.1 plunky u.time_low = be16dec(p); 237 1.1 plunky 238 1.1 plunky if (uuid_equal(&u, uuid, NULL)) 239 1.1 plunky return true; 240 1.1 plunky 241 1.1 plunky p += 2; 242 1.1 plunky break; 243 1.1 plunky 244 1.1 plunky case SDP_DATA_UUID32: 245 1.1 plunky u = BLUETOOTH_BASE_UUID; 246 1.1 plunky u.time_low = be32dec(p); 247 1.1 plunky 248 1.1 plunky if (uuid_equal(&u, uuid, NULL)) 249 1.1 plunky return true; 250 1.1 plunky 251 1.1 plunky p += 4; 252 1.1 plunky break; 253 1.1 plunky 254 1.1 plunky case SDP_DATA_UUID128: 255 1.1 plunky uuid_dec_be(p, &u); 256 1.1 plunky 257 1.1 plunky if (uuid_equal(&u, uuid, NULL)) 258 1.1 plunky return true; 259 1.1 plunky 260 1.1 plunky p += 16; 261 1.1 plunky break; 262 1.1 plunky 263 1.1 plunky default: 264 1.1 plunky return false; 265 1.1 plunky } 266 1.1 plunky } 267 1.1 plunky 268 1.1 plunky return false; 269 1.1 plunky } 270 1.1 plunky 271 1.1 plunky /* 272 1.1 plunky * Select ServiceRecords matching ServiceSearchPattern 273 1.1 plunky * 274 1.1 plunky * A record is selected when it is visible to the client and 275 1.1 plunky * contains each and every UUID from the ServiceSearchPattern 276 1.1 plunky */ 277 1.1 plunky void 278 1.1 plunky db_select_ssp(server_t *srv, int fd, sdp_data_t *ssp) 279 1.1 plunky { 280 1.1 plunky record_t *r; 281 1.1 plunky sdp_data_t s; 282 1.1 plunky uuid_t u; 283 1.1 plunky 284 1.1 plunky LIST_FOREACH(r, &srv->rlist, next) { 285 1.1 plunky if (!r->valid) 286 1.1 plunky continue; 287 1.1 plunky 288 1.1 plunky if (!srv->fdidx[fd].control 289 1.1 plunky && !bdaddr_any(&r->bdaddr) 290 1.1 plunky && !bdaddr_same(&r->bdaddr, &srv->fdidx[fd].bdaddr)) 291 1.1 plunky continue; 292 1.1 plunky 293 1.1 plunky s = *ssp; 294 1.1 plunky for (;;) { 295 1.1 plunky if (!sdp_get_uuid(&s, &u)) { 296 1.1 plunky /* matched all UUIDs */ 297 1.1 plunky FD_SET(fd, &r->refset); 298 1.1 plunky r->refcnt++; 299 1.1 plunky break; 300 1.1 plunky } 301 1.1 plunky 302 1.1 plunky if (!db_match_uuid(r, &u)) { 303 1.1 plunky /* does not match UUID */ 304 1.1 plunky break; 305 1.1 plunky } 306 1.1 plunky } 307 1.1 plunky } 308 1.1 plunky } 309 1.1 plunky 310 1.1 plunky /* 311 1.1 plunky * Select a ServiceRecord given the RecordHandle. 312 1.1 plunky */ 313 1.1 plunky void 314 1.1 plunky db_select_handle(server_t *srv, int fd, uint32_t handle) 315 1.1 plunky { 316 1.1 plunky record_t *r; 317 1.1 plunky 318 1.1 plunky LIST_FOREACH(r, &srv->rlist, next) { 319 1.1 plunky if (!r->valid) 320 1.1 plunky continue; 321 1.1 plunky 322 1.1 plunky if (!srv->fdidx[fd].control 323 1.1 plunky && !bdaddr_any(&r->bdaddr) 324 1.1 plunky && !bdaddr_same(&r->bdaddr, &srv->fdidx[fd].bdaddr)) 325 1.1 plunky continue; 326 1.1 plunky 327 1.1 plunky if (handle == r->handle) { 328 1.1 plunky FD_SET(fd, &r->refset); 329 1.1 plunky r->refcnt++; 330 1.1 plunky break; 331 1.1 plunky } 332 1.1 plunky } 333 1.1 plunky } 334 1.1 plunky 335 1.1 plunky /* 336 1.1 plunky * Create a record and insert in server record list in ascending handle 337 1.1 plunky * order. Where a selectable record exists with the same handle number, 338 1.1 plunky * it will be expired. 339 1.1 plunky */ 340 1.1 plunky bool 341 1.1 plunky db_create(server_t *srv, int fd, const bdaddr_t *bdaddr, uint32_t handle, sdp_data_t *data) 342 1.1 plunky { 343 1.1 plunky record_t *n, *r, *rec; 344 1.1 plunky sdp_data_t d, v; 345 1.1 plunky uint16_t a; 346 1.1 plunky size_t len; 347 1.1 plunky 348 1.1 plunky d = *data; 349 1.1 plunky if (!sdp_get_attr(&d, &a, &v) 350 1.1 plunky || a != SDP_ATTR_SERVICE_RECORD_HANDLE 351 1.1 plunky || sdp_data_type(&v) != SDP_DATA_UINT32) 352 1.1 plunky return false; 353 1.1 plunky 354 1.1 plunky sdp_set_uint(&v, handle); 355 1.1 plunky 356 1.1 plunky len = data->end - data->next; 357 1.1 plunky rec = malloc(sizeof(record_t) + len); 358 1.1 plunky if (rec == NULL) 359 1.1 plunky return false; 360 1.1 plunky 361 1.1 plunky memset(rec, 0, sizeof(record_t)); 362 1.1 plunky FD_ZERO(&rec->refset); 363 1.1 plunky rec->handle = handle; 364 1.1 plunky rec->valid = true; 365 1.1 plunky rec->fd = fd; 366 1.1 plunky bdaddr_copy(&rec->bdaddr, bdaddr); 367 1.1 plunky rec->data.next = rec->ext; 368 1.1 plunky rec->data.end = rec->ext + len; 369 1.1 plunky memcpy(rec->ext, data->next, len); 370 1.1 plunky 371 1.1 plunky /* 372 1.1 plunky * Note, this does not handle the case where we expire 373 1.1 plunky * the first record on the list, as that won't happen. 374 1.1 plunky */ 375 1.1 plunky n = LIST_FIRST(&srv->rlist); 376 1.1 plunky if (n != NULL) { 377 1.1 plunky do { 378 1.1 plunky r = n; 379 1.1 plunky n = LIST_NEXT(r, next); 380 1.1 plunky } while (n != NULL && n->handle < handle); 381 1.1 plunky 382 1.1 plunky if (n != NULL && n->valid && n->handle == handle) { 383 1.1 plunky if (n->refcnt-- == 0) { 384 1.1 plunky LIST_REMOVE(n, next); 385 1.1 plunky free(n); 386 1.1 plunky } else { 387 1.1 plunky n->valid = false; 388 1.1 plunky n->fd = -1; 389 1.1 plunky } 390 1.1 plunky } 391 1.1 plunky 392 1.1 plunky LIST_INSERT_AFTER(r, rec, next); 393 1.1 plunky } else { 394 1.1 plunky LIST_INSERT_HEAD(&srv->rlist, rec, next); 395 1.1 plunky } 396 1.1 plunky 397 1.1 plunky return true; 398 1.1 plunky } 399 1.1 plunky 400 1.1 plunky /* 401 1.1 plunky * Unselect any ServiceRecords selected by fd 402 1.1 plunky */ 403 1.1 plunky void 404 1.1 plunky db_unselect(server_t *srv, int fd) 405 1.1 plunky { 406 1.1 plunky record_t *n, *r; 407 1.1 plunky 408 1.1 plunky n = LIST_FIRST(&srv->rlist); 409 1.1 plunky while (n != NULL) { 410 1.1 plunky r = n; 411 1.1 plunky n = LIST_NEXT(r, next); 412 1.1 plunky 413 1.1 plunky if (FD_ISSET(fd, &r->refset)) { 414 1.1 plunky if (r->refcnt-- == 0) { 415 1.1 plunky LIST_REMOVE(r, next); 416 1.1 plunky free(r); 417 1.1 plunky } else { 418 1.1 plunky FD_CLR(fd, &r->refset); 419 1.1 plunky } 420 1.1 plunky } 421 1.1 plunky } 422 1.1 plunky } 423 1.1 plunky 424 1.1 plunky /* 425 1.1 plunky * Invalidate or release all records owned by fd 426 1.1 plunky */ 427 1.1 plunky void 428 1.1 plunky db_release(server_t *srv, int fd) 429 1.1 plunky { 430 1.1 plunky record_t *n, *r; 431 1.1 plunky 432 1.1 plunky n = LIST_FIRST(&srv->rlist); 433 1.1 plunky while (n != NULL) { 434 1.1 plunky r = n; 435 1.1 plunky n = LIST_NEXT(r, next); 436 1.1 plunky 437 1.1 plunky if (r->fd == fd) { 438 1.1 plunky if (r->refcnt-- == 0) { 439 1.1 plunky LIST_REMOVE(r, next); 440 1.1 plunky free(r); 441 1.1 plunky } else { 442 1.1 plunky r->valid = false; 443 1.1 plunky r->fd = -1; 444 1.1 plunky } 445 1.1 plunky } 446 1.1 plunky } 447 1.1 plunky } 448