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