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
db_init(server_t * srv)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
db_next(server_t * srv,int fd,record_t ** rec)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
db_match_uuid(record_t * rec,uuid_t * uuid)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
db_select_ssp(server_t * srv,int fd,sdp_data_t * ssp)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
db_select_handle(server_t * srv,int fd,uint32_t handle)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
db_create(server_t * srv,int fd,const bdaddr_t * bdaddr,uint32_t handle,sdp_data_t * data)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
db_unselect(server_t * srv,int fd)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
db_release(server_t * srv,int fd)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