Home | History | Annotate | Line # | Download | only in sdpd
      1 /*	$NetBSD: db.c,v 1.2 2015/11/24 21:11:39 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.2 2015/11/24 21:11:39 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 */
     50 static 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 */
     97 static uint8_t bgd_data[] = {
     98 	0x09, 0x00, 0x00,	//  uint16	ServiceRecordHandle
     99 	0x0a, 0x00, 0x01, 0x00,	//  uint32	0x00010000
    100 	0x00,
    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  */
    127 bool
    128 db_init(server_t *srv)
    129 {
    130 	sdp_data_t d;
    131 
    132 	LIST_INIT(&srv->rlist);
    133 	srv->handle = 0x00010000; /* values 0x00000001->0x0000FFFF are reserved */
    134 
    135 	d.next = sds_data;
    136 	d.end = sds_data + sizeof(sds_data);
    137 	if (!db_create(srv, -1, BDADDR_ANY, 0x00000000, &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  */
    153 bool
    154 db_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  */
    176 static bool
    177 db_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  */
    277 void
    278 db_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  */
    313 void
    314 db_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  */
    340 bool
    341 db_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  */
    403 void
    404 db_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  */
    427 void
    428 db_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