db.c revision 1.1 1 1.1 plunky /* $NetBSD: db.c,v 1.1 2009/05/12 10:05:06 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.1 plunky __RCSID("$NetBSD: db.c,v 1.1 2009/05/12 10:05:06 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.1 plunky 0x0a, 0x00, 0x00, 0x00, // uint32 0x00000001
100 1.1 plunky 0x01,
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.1 plunky srv->handle = 0;
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.1 plunky if (!db_create(srv, -1, BDADDR_ANY, srv->handle++, &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