No empty .Rs/.Re
[netbsd-mini2440.git] / usr.sbin / sdpd / db.c
blob595af7c2405df035beef6b1402c1414cb96e224a
1 /* $NetBSD: db.c$ */
3 /*-
4 * Copyright (c) 2009 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Iain Hibbert.
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.
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.
32 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: db.c$");
35 #include <bluetooth.h>
36 #include <sdp.h>
37 #include <stdbool.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <uuid.h>
42 #include "sdpd.h"
45 * Using a prebuilt service record means that providing ServerState
46 * and a non-hardcoded ProviderName are difficult. Look into that later.
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,
55 0x09, 0x00, 0x01, // uint16 ServiceClassIDList
56 0x35, 0x03, // seq8(3)
57 0x19, 0x10, 0x00, // uuid16 ServiceDiscoveryServer
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
67 0x09, 0x00, 0x05, // uint16 BrowseGroupList
68 0x35, 0x03, // seq8(3)
69 0x19, 0x10, 0x02, // uuid16 PublicBrowseGroup
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
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,
87 0x09, 0x01, 0x02, // uint16 PrimaryLanguageBaseID + ProviderNameOffset
88 0x25, 0x06, 0x4e, 0x65, // str8(6) "NetBSD"
89 0x74, 0x42, 0x53, 0x44,
91 0x09, 0x02, 0x00, // uint16 VersionNumberList
92 0x35, 0x03, // seq8(3)
93 0x09, 0x01, 0x00, // uint16 v1.0
96 /* BrowseGroupDescriptor service record */
97 static uint8_t bgd_data[] = {
98 0x09, 0x00, 0x00, // uint16 ServiceRecordHandle
99 0x0a, 0x00, 0x00, 0x00, // uint32 0x00000001
100 0x01,
102 0x09, 0x00, 0x01, // uint16 ServiceClassIDList
103 0x35, 0x03, // seq8(3)
104 0x19, 0x10, 0x01, // uuid16 BrowseGroupDescriptor
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
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,
119 0x09, 0x02, 0x00, // uint16 GroupID
120 0x19, 0x10, 0x02, // uuid16 PublicBrowseRoot
124 * Initialise the record database with the ServiceDiscoveryServer
125 * and BrowseGroupDescriptor records
127 bool
128 db_init(server_t *srv)
130 sdp_data_t d;
132 LIST_INIT(&srv->rlist);
133 srv->handle = 0;
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;
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;
145 return true;
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.
153 bool
154 db_next(server_t *srv, int fd, record_t **rec)
156 record_t *r;
158 if (*rec == NULL)
159 r = LIST_FIRST(&srv->rlist);
160 else
161 r = LIST_NEXT(*rec, next);
163 while (r != NULL && !FD_ISSET(fd, &r->refset))
164 r = LIST_NEXT(r, next);
166 *rec = r;
167 return (r == NULL) ? false : true;
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.
176 static bool
177 db_match_uuid(record_t *rec, uuid_t *uuid)
179 uint8_t *p = rec->data.next;
180 uuid_t u;
182 while (p < rec->data.end) {
183 switch(*p++) {
184 case SDP_DATA_NIL:
185 break;
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;
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;
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;
209 case SDP_DATA_INT64:
210 case SDP_DATA_UINT64:
211 p += 8;
212 break;
214 case SDP_DATA_INT128:
215 case SDP_DATA_UINT128:
216 p += 16;
217 break;
219 case SDP_DATA_STR8:
220 case SDP_DATA_URL8:
221 p += 1 + *p;
222 break;
224 case SDP_DATA_STR16:
225 case SDP_DATA_URL16:
226 p += 2 + be16dec(p);
227 break;
229 case SDP_DATA_STR32:
230 case SDP_DATA_URL32:
231 p += 4 + be32dec(p);
232 break;
234 case SDP_DATA_UUID16:
235 u = BLUETOOTH_BASE_UUID;
236 u.time_low = be16dec(p);
238 if (uuid_equal(&u, uuid, NULL))
239 return true;
241 p += 2;
242 break;
244 case SDP_DATA_UUID32:
245 u = BLUETOOTH_BASE_UUID;
246 u.time_low = be32dec(p);
248 if (uuid_equal(&u, uuid, NULL))
249 return true;
251 p += 4;
252 break;
254 case SDP_DATA_UUID128:
255 uuid_dec_be(p, &u);
257 if (uuid_equal(&u, uuid, NULL))
258 return true;
260 p += 16;
261 break;
263 default:
264 return false;
268 return false;
272 * Select ServiceRecords matching ServiceSearchPattern
274 * A record is selected when it is visible to the client and
275 * contains each and every UUID from the ServiceSearchPattern
277 void
278 db_select_ssp(server_t *srv, int fd, sdp_data_t *ssp)
280 record_t *r;
281 sdp_data_t s;
282 uuid_t u;
284 LIST_FOREACH(r, &srv->rlist, next) {
285 if (!r->valid)
286 continue;
288 if (!srv->fdidx[fd].control
289 && !bdaddr_any(&r->bdaddr)
290 && !bdaddr_same(&r->bdaddr, &srv->fdidx[fd].bdaddr))
291 continue;
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;
302 if (!db_match_uuid(r, &u)) {
303 /* does not match UUID */
304 break;
311 * Select a ServiceRecord given the RecordHandle.
313 void
314 db_select_handle(server_t *srv, int fd, uint32_t handle)
316 record_t *r;
318 LIST_FOREACH(r, &srv->rlist, next) {
319 if (!r->valid)
320 continue;
322 if (!srv->fdidx[fd].control
323 && !bdaddr_any(&r->bdaddr)
324 && !bdaddr_same(&r->bdaddr, &srv->fdidx[fd].bdaddr))
325 continue;
327 if (handle == r->handle) {
328 FD_SET(fd, &r->refset);
329 r->refcnt++;
330 break;
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.
340 bool
341 db_create(server_t *srv, int fd, const bdaddr_t *bdaddr, uint32_t handle, sdp_data_t *data)
343 record_t *n, *r, *rec;
344 sdp_data_t d, v;
345 uint16_t a;
346 size_t len;
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;
354 sdp_set_uint(&v, handle);
356 len = data->end - data->next;
357 rec = malloc(sizeof(record_t) + len);
358 if (rec == NULL)
359 return false;
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);
372 * Note, this does not handle the case where we expire
373 * the first record on the list, as that won't happen.
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);
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;
392 LIST_INSERT_AFTER(r, rec, next);
393 } else {
394 LIST_INSERT_HEAD(&srv->rlist, rec, next);
397 return true;
401 * Unselect any ServiceRecords selected by fd
403 void
404 db_unselect(server_t *srv, int fd)
406 record_t *n, *r;
408 n = LIST_FIRST(&srv->rlist);
409 while (n != NULL) {
410 r = n;
411 n = LIST_NEXT(r, next);
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);
425 * Invalidate or release all records owned by fd
427 void
428 db_release(server_t *srv, int fd)
430 record_t *n, *r;
432 n = LIST_FIRST(&srv->rlist);
433 while (n != NULL) {
434 r = n;
435 n = LIST_NEXT(r, next);
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;