/*-
* Copyright (c) 2009 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Iain Hibbert.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Iterate through records selected by fd. rec should point to a NULL
* value to start the iteration, and false will be returned when there
* are no more records to return.
*/
bool
db_next(server_t *srv, int fd, record_t **rec)
{
record_t *r;
if (*rec == NULL)
r = LIST_FIRST(&srv->rlist);
else
r = LIST_NEXT(*rec, next);
while (r != NULL && !FD_ISSET(fd, &r->refset))
r = LIST_NEXT(r, next);
*rec = r;
return (r == NULL) ? false : true;
}
/*
* Match a ServiceRecord against a UUID. Note that because we already
* know that the record data is valid, we don't need to recurse here
* and can just skip over SEQ and ALT headers. Return true if equivalent
* UUID is found.
*/
static bool
db_match_uuid(record_t *rec, uuid_t *uuid)
{
uint8_t *p = rec->data.next;
uuid_t u;
while (p < rec->data.end) {
switch(*p++) {
case SDP_DATA_NIL:
break;
case SDP_DATA_BOOL:
case SDP_DATA_INT8:
case SDP_DATA_UINT8:
case SDP_DATA_SEQ8:
case SDP_DATA_ALT8:
p += 1;
break;
case SDP_DATA_INT16:
case SDP_DATA_UINT16:
case SDP_DATA_SEQ16:
case SDP_DATA_ALT16:
p += 2;
break;
case SDP_DATA_INT32:
case SDP_DATA_UINT32:
case SDP_DATA_SEQ32:
case SDP_DATA_ALT32:
p += 4;
break;
case SDP_DATA_INT64:
case SDP_DATA_UINT64:
p += 8;
break;
case SDP_DATA_INT128:
case SDP_DATA_UINT128:
p += 16;
break;
case SDP_DATA_STR8:
case SDP_DATA_URL8:
p += 1 + *p;
break;
case SDP_DATA_STR16:
case SDP_DATA_URL16:
p += 2 + be16dec(p);
break;
case SDP_DATA_STR32:
case SDP_DATA_URL32:
p += 4 + be32dec(p);
break;
case SDP_DATA_UUID16:
u = BLUETOOTH_BASE_UUID;
u.time_low = be16dec(p);
if (uuid_equal(&u, uuid, NULL))
return true;
p += 2;
break;
case SDP_DATA_UUID32:
u = BLUETOOTH_BASE_UUID;
u.time_low = be32dec(p);
if (uuid_equal(&u, uuid, NULL))
return true;
p += 4;
break;
case SDP_DATA_UUID128:
uuid_dec_be(p, &u);
if (uuid_equal(&u, uuid, NULL))
return true;
p += 16;
break;
default:
return false;
}
}
return false;
}
/*
* Select ServiceRecords matching ServiceSearchPattern
*
* A record is selected when it is visible to the client and
* contains each and every UUID from the ServiceSearchPattern
*/
void
db_select_ssp(server_t *srv, int fd, sdp_data_t *ssp)
{
record_t *r;
sdp_data_t s;
uuid_t u;
LIST_FOREACH(r, &srv->rlist, next) {
if (!r->valid)
continue;
if (!srv->fdidx[fd].control
&& !bdaddr_any(&r->bdaddr)
&& !bdaddr_same(&r->bdaddr, &srv->fdidx[fd].bdaddr))
continue;
s = *ssp;
for (;;) {
if (!sdp_get_uuid(&s, &u)) {
/* matched all UUIDs */
FD_SET(fd, &r->refset);
r->refcnt++;
break;
}
if (!db_match_uuid(r, &u)) {
/* does not match UUID */
break;
}
}
}
}
/*
* Select a ServiceRecord given the RecordHandle.
*/
void
db_select_handle(server_t *srv, int fd, uint32_t handle)
{
record_t *r;
LIST_FOREACH(r, &srv->rlist, next) {
if (!r->valid)
continue;
if (!srv->fdidx[fd].control
&& !bdaddr_any(&r->bdaddr)
&& !bdaddr_same(&r->bdaddr, &srv->fdidx[fd].bdaddr))
continue;
/*
* Create a record and insert in server record list in ascending handle
* order. Where a selectable record exists with the same handle number,
* it will be expired.
*/
bool
db_create(server_t *srv, int fd, const bdaddr_t *bdaddr, uint32_t handle, sdp_data_t *data)
{
record_t *n, *r, *rec;
sdp_data_t d, v;
uint16_t a;
size_t len;
d = *data;
if (!sdp_get_attr(&d, &a, &v)
|| a != SDP_ATTR_SERVICE_RECORD_HANDLE
|| sdp_data_type(&v) != SDP_DATA_UINT32)
return false;
sdp_set_uint(&v, handle);
len = data->end - data->next;
rec = malloc(sizeof(record_t) + len);
if (rec == NULL)
return false;
/*
* Note, this does not handle the case where we expire
* the first record on the list, as that won't happen.
*/
n = LIST_FIRST(&srv->rlist);
if (n != NULL) {
do {
r = n;
n = LIST_NEXT(r, next);
} while (n != NULL && n->handle < handle);