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