1 /* $NetBSD: service.c,v 1.5 2024/02/05 21:46:07 andvar 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: service.c,v 1.5 2024/02/05 21:46:07 andvar Exp $");
34
35 #include <bluetooth.h>
36 #include <sdp.h>
37
38 #include "sdpd.h"
39
40 /*
41 * This structure is a collection of pointers describing an output
42 * buffer for sdpd_put_byte(), below. bytes are written at next when
43 * it falls inside the range [start .. end - 1]
44 */
45 typedef struct {
46 uint8_t *start; /* start of buffer window */
47 uint8_t *next; /* current write position */
48 uint8_t *end; /* end of buffer window */
49 } sdpd_data_t;
50
51 static bool sdpd_valid_ssp(sdp_data_t *);
52 static bool sdpd_valid_ail(sdp_data_t *);
53 static bool sdpd_match_ail(record_t *, sdp_data_t, sdpd_data_t *);
54 static void sdpd_put_byte(sdpd_data_t *, uint8_t);
55 static void sdpd_put_attr(sdpd_data_t *, uint16_t, sdp_data_t *);
56 static void sdpd_open_seq(sdpd_data_t *);
57 static void sdpd_close_seq(sdpd_data_t *, uint8_t *);
58
59 uint16_t
service_search_request(server_t * srv,int fd)60 service_search_request(server_t *srv, int fd)
61 {
62 record_t *r;
63 sdp_data_t d, s;
64 int max, total, count;
65
66 log_debug("ServiceSearchRequest by client on fd#%d", fd);
67
68 d.next = srv->ibuf;
69 d.end = srv->ibuf + srv->pdu.len;
70
71 /*
72 * extract ServiceSearchPattern
73 */
74 if (!sdp_get_seq(&d, &s)
75 || !sdpd_valid_ssp(&s))
76 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
77
78 /*
79 * extract MaximumServiceRecordCount
80 */
81 if (d.next + sizeof(uint16_t) > d.end)
82 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
83
84 max = be16dec(d.next);
85 d.next += sizeof(uint16_t);
86 if (max < 0x0001)
87 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
88
89 /*
90 * validate ContinuationState
91 * If none given, this is a new request
92 */
93 if (d.next + 1 > d.end
94 || d.next[0] > 16
95 || d.next + 1 + d.next[0] != d.end)
96 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
97
98 if (d.next[0] == 0) {
99 srv->fdidx[fd].offset = 0;
100 db_unselect(srv, fd);
101 db_select_ssp(srv, fd, &s);
102 } else if (srv->fdidx[fd].offset == 0
103 || d.next[0] != sizeof(uint16_t)
104 || be16dec(d.next + 1) != srv->fdidx[fd].offset)
105 return SDP_ERROR_CODE_INVALID_CONTINUATION_STATE;
106
107 /*
108 * Ready our output buffer. We leave space at the start for
109 * TotalServiceRecordCount and CurrentServiceRecordCount and
110 * at the end for ContinuationState, and we must have space
111 * for at least one ServiceRecordHandle. Then, step through
112 * selected records and write as many handles that will fit
113 * into the data space
114 */
115 d.next = srv->obuf + sizeof(uint16_t) + sizeof(uint16_t);
116 d.end = srv->obuf + srv->fdidx[fd].omtu - 1 - sizeof(uint16_t);
117 count = total = 0;
118
119 if (d.next + sizeof(uint32_t) > d.end)
120 return SDP_ERROR_CODE_INSUFFICIENT_RESOURCES;
121
122 r = NULL;
123 while (db_next(srv, fd, &r) && total < max) {
124 if (total >= srv->fdidx[fd].offset
125 && d.next + sizeof(uint32_t) <= d.end) {
126 be32enc(d.next, r->handle);
127 d.next += sizeof(uint32_t);
128 count++;
129 }
130
131 total++;
132 }
133
134 /*
135 * encode TotalServiceRecordCount and CurrentServiceRecordCount
136 */
137 be16enc(srv->obuf, total);
138 be16enc(srv->obuf + sizeof(uint16_t), count);
139
140 /*
141 * encode ContinuationState which in this case will be the
142 * number of ServiceRecordHandles already sent.
143 */
144 if (r == NULL || total == max) {
145 srv->fdidx[fd].offset = 0;
146 db_unselect(srv, fd);
147 d.next[0] = 0;
148 d.next += 1;
149 } else {
150 srv->fdidx[fd].offset += count;
151 d.next[0] = sizeof(uint16_t);
152 be16enc(d.next + 1, srv->fdidx[fd].offset);
153 d.next += 1 + sizeof(uint16_t);
154 }
155
156 /*
157 * fill in PDU header and we are done
158 */
159 srv->pdu.pid = SDP_PDU_SERVICE_SEARCH_RESPONSE;
160 srv->pdu.len = d.next - srv->obuf;
161 return 0;
162 }
163
164 uint16_t
service_attribute_request(server_t * srv,int fd)165 service_attribute_request(server_t *srv, int fd)
166 {
167 record_t *r;
168 sdp_data_t a, d;
169 sdpd_data_t b;
170 uint8_t *tmp;
171 uint32_t handle;
172 int max;
173
174 log_debug("ServiceAttributeRequest by client on fd#%d", fd);
175
176 d.next = srv->ibuf;
177 d.end = srv->ibuf + srv->pdu.len;
178
179 /*
180 * extract ServiceRecordHandle
181 */
182 if (d.next + sizeof(uint32_t) > d.end)
183 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
184
185 handle = be32dec(d.next);
186 d.next += sizeof(uint32_t);
187
188 /*
189 * extract MaximumAttributeByteCount
190 */
191 if (d.next + sizeof(uint16_t) > d.end)
192 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
193
194 max = be16dec(d.next);
195 d.next += sizeof(uint16_t);
196 if (max < 0x0007)
197 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
198
199 /*
200 * extract AttributeIDList
201 */
202 if (!sdp_get_seq(&d, &a)
203 || !sdpd_valid_ail(&a))
204 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
205
206 /*
207 * validate ContinuationState
208 * If none given, this is a new request
209 */
210 if (d.next + 1 > d.end
211 || d.next[0] > 16
212 || d.next + 1 + d.next[0] != d.end)
213 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
214
215 if (d.next[0] == 0) {
216 srv->fdidx[fd].offset = 0;
217 db_unselect(srv, fd);
218 db_select_handle(srv, fd, handle);
219 } else if (srv->fdidx[fd].offset == 0
220 || d.next[0] != sizeof(uint16_t)
221 || be16dec(d.next + 1) != srv->fdidx[fd].offset)
222 return SDP_ERROR_CODE_INVALID_CONTINUATION_STATE;
223
224 /*
225 * Set up the buffer window and write pointer, leaving space at
226 * buffer start for AttributeListByteCount and for ContinuationState
227 * at the end
228 */
229 b.start = srv->obuf + sizeof(uint16_t);
230 b.next = b.start - srv->fdidx[fd].offset;
231 b.end = srv->obuf + srv->fdidx[fd].omtu - 1;
232 if (b.start + max < b.end)
233 b.end = b.start + max;
234
235 /*
236 * Match the selected record against AttributeIDList, writing
237 * the data to the sparse buffer.
238 */
239 r = NULL;
240 db_next(srv, fd, &r);
241 if (r == NULL)
242 return SDP_ERROR_CODE_INVALID_SERVICE_RECORD_HANDLE;
243
244 sdpd_match_ail(r, a, &b);
245
246 if (b.next > b.end) {
247 /*
248 * b.end is the limit of AttributeList that we are allowed
249 * to send so if we have exceeded that we need to adjust our
250 * response downwards. Recalculate the new cut off to allow
251 * writing the ContinuationState offset and ensure we don't
252 * exceed MaximumAttributeByteCount. Also, make sure that
253 * the continued length is not too short.
254 */
255 tmp = b.next;
256 b.next = srv->obuf + srv->fdidx[fd].omtu - 1 - sizeof(uint16_t);
257 if (b.next > b.end)
258 b.next = b.end;
259
260 if (tmp - b.next < 0x0002)
261 b.next = tmp - 0x0002;
262
263 /* encode AttributeListByteCount */
264 be16enc(srv->obuf, (b.next - b.start));
265
266 /* calculate & append ContinuationState */
267 srv->fdidx[fd].offset += (b.next - b.start);
268 b.next[0] = sizeof(uint16_t);
269 be16enc(b.next + 1, srv->fdidx[fd].offset);
270 b.next += 1 + sizeof(uint16_t);
271 } else {
272 /* encode AttributeListByteCount */
273 be16enc(srv->obuf, (b.next - b.start));
274
275 /* reset & append ContinuationState */
276 srv->fdidx[fd].offset = 0;
277 db_unselect(srv, fd);
278 b.next[0] = 0;
279 b.next += 1;
280 }
281
282 /*
283 * fill in PDU header and we are done
284 */
285 srv->pdu.pid = SDP_PDU_SERVICE_ATTRIBUTE_RESPONSE;
286 srv->pdu.len = b.next - srv->obuf;
287 return 0;
288 }
289
290 uint16_t
service_search_attribute_request(server_t * srv,int fd)291 service_search_attribute_request(server_t *srv, int fd)
292 {
293 record_t *r;
294 sdpd_data_t b;
295 sdp_data_t a, d, s;
296 uint8_t *tmp;
297 int max;
298
299 log_debug("ServiceSearchAttributeRequest by client on fd#%d", fd);
300
301 d.next = srv->ibuf;
302 d.end = srv->ibuf + srv->pdu.len;
303
304 /*
305 * extract ServiceSearchPattern
306 */
307 if (!sdp_get_seq(&d, &s)
308 || !sdpd_valid_ssp(&s))
309 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
310
311 /*
312 * extract MaximumAttributeByteCount
313 */
314 if (d.next + sizeof(uint16_t) > d.end)
315 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
316
317 max = be16dec(d.next);
318 d.next += sizeof(uint16_t);
319 if (max < 0x0007)
320 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
321
322 /*
323 * extract AttributeIDList
324 */
325 if (!sdp_get_seq(&d, &a)
326 || !sdpd_valid_ail(&a))
327 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
328
329 /*
330 * validate ContinuationState
331 * If none given, this is a new request
332 */
333 if (d.next + 1 > d.end
334 || d.next[0] > 16
335 || d.next + 1 + d.next[0] != d.end)
336 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
337
338 if (d.next[0] == 0) {
339 srv->fdidx[fd].offset = 0;
340 db_unselect(srv, fd);
341 db_select_ssp(srv, fd, &s);
342 } else if (srv->fdidx[fd].offset == 0
343 || d.next[0] != sizeof(uint16_t)
344 || be16dec(d.next + 1) != srv->fdidx[fd].offset)
345 return SDP_ERROR_CODE_INVALID_CONTINUATION_STATE;
346
347 /*
348 * Set up the buffer window and write pointer, leaving space at
349 * buffer start for AttributeListByteCount and for ContinuationState
350 * at the end.
351 */
352 b.start = srv->obuf + sizeof(uint16_t);
353 b.end = srv->obuf + srv->fdidx[fd].omtu - 1;
354 b.next = b.start - srv->fdidx[fd].offset;
355 if (b.start + max < b.end)
356 b.end = b.start + max;
357
358 /*
359 * match all selected records against the AttributeIDList,
360 * wrapping the whole in a sequence. Where a record does
361 * not match any attributes, delete the empty sequence.
362 */
363 sdpd_open_seq(&b);
364
365 r = NULL;
366 while (db_next(srv, fd, &r)) {
367 tmp = b.next;
368 if (!sdpd_match_ail(r, a, &b))
369 b.next = tmp;
370 }
371
372 sdpd_close_seq(&b, b.start - srv->fdidx[fd].offset);
373
374 if (b.next > b.end) {
375 /*
376 * b.end is the limit of AttributeLists that we are allowed
377 * to send so if we have exceeded that we need to adjust our
378 * response downwards. Recalculate the new cut off to allow
379 * writing the ContinuationState offset and ensure we don't
380 * exceed MaximumAttributeByteCount. Also, make sure that
381 * the continued length is not too short.
382 */
383 tmp = b.next;
384 b.next = srv->obuf + srv->fdidx[fd].omtu - 1 - sizeof(uint16_t);
385 if (b.next > b.end)
386 b.next = b.end;
387
388 if (tmp - b.next < 0x0002)
389 b.next = tmp - 0x0002;
390
391 /* encode AttributeListsByteCount */
392 be16enc(srv->obuf, (b.next - b.start));
393
394 /* calculate & append ContinuationState */
395 srv->fdidx[fd].offset += (b.next - b.start);
396 b.next[0] = sizeof(uint16_t);
397 be16enc(b.next + 1, srv->fdidx[fd].offset);
398 b.next += 1 + sizeof(uint16_t);
399 } else {
400 /* encode AttributeListsByteCount */
401 be16enc(srv->obuf, (b.next - b.start));
402
403 /* reset & append ContinuationState */
404 srv->fdidx[fd].offset = 0;
405 db_unselect(srv, fd);
406 b.next[0] = 0;
407 b.next += 1;
408 }
409
410 /*
411 * fill in PDU header and we are done
412 */
413 srv->pdu.pid = SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_RESPONSE;
414 srv->pdu.len = b.next - srv->obuf;
415 return 0;
416 }
417
418 /*
419 * validate ServiceSearchPattern
420 *
421 * The ServiceSearchPattern is a list of data elements, where each element
422 * is a UUID. The list must contain at least one UUID and the maximum number
423 * of UUIDs is 12
424 */
425 static bool
sdpd_valid_ssp(sdp_data_t * ssp)426 sdpd_valid_ssp(sdp_data_t *ssp)
427 {
428 sdp_data_t s = *ssp;
429 uuid_t u;
430 int n;
431
432 if (!sdp_data_valid(&s))
433 return false;
434
435 n = 0;
436 while (sdp_get_uuid(&s, &u))
437 n++;
438
439 if (n < 1 || n > 12 || s.next != s.end)
440 return false;
441
442 return true;
443 }
444
445 /*
446 * validate AttributeIDList
447 *
448 * The AttributeIDList is a list of data elements, where each element is
449 * either an attribute ID encoded as an unsigned 16-bit integer or a range
450 * of attribute IDs encoded as an unsigned 32-bit integer where the high
451 * order 16-bits are the beginning of the range and the low order 16-bits
452 * are the ending
453 *
454 * The attribute IDs should be listed in ascending order without duplication
455 * of any attribute ID values but we don't worry about that, since if the
456 * remote party messes up, their results will be messed up
457 */
458 static bool
sdpd_valid_ail(sdp_data_t * ail)459 sdpd_valid_ail(sdp_data_t *ail)
460 {
461 sdp_data_t a = *ail;
462 sdp_data_t d;
463
464 if (!sdp_data_valid(&a))
465 return false;
466
467 while (sdp_get_data(&a, &d)) {
468 if (sdp_data_type(&d) != SDP_DATA_UINT16
469 && sdp_data_type(&d) != SDP_DATA_UINT32)
470 return false;
471 }
472
473 return true;
474 }
475
476 /*
477 * compare attributes in the ServiceRecord with the AttributeIDList
478 * and copy any matches to a sequence in the output buffer.
479 */
480 static bool
sdpd_match_ail(record_t * rec,sdp_data_t ail,sdpd_data_t * buf)481 sdpd_match_ail(record_t *rec, sdp_data_t ail, sdpd_data_t *buf)
482 {
483 sdp_data_t r, v;
484 uint16_t a;
485 uintmax_t ui;
486 uint8_t *f;
487 int lo, hi;
488 bool rv;
489
490 r = rec->data;
491 f = buf->next;
492 lo = hi = -1;
493 rv = false;
494
495 sdpd_open_seq(buf);
496
497 while (sdp_get_attr(&r, &a, &v)) {
498 while (a > hi) {
499 if (ail.next == ail.end)
500 goto done;
501
502 if (sdp_data_type(&ail) == SDP_DATA_UINT16) {
503 sdp_get_uint(&ail, &ui);
504 lo = hi = ui;
505 } else {
506 sdp_get_uint(&ail, &ui);
507 lo = (uint16_t)(ui >> 16);
508 hi = (uint16_t)(ui);
509 }
510 }
511
512 if (a < lo)
513 continue;
514
515 sdpd_put_attr(buf, a, &v);
516 rv = true;
517 }
518
519 done:
520 sdpd_close_seq(buf, f);
521 return rv;
522 }
523
524 /*
525 * output data. We only actually store the bytes when the
526 * pointer is within the valid window.
527 */
528 static void
sdpd_put_byte(sdpd_data_t * buf,uint8_t byte)529 sdpd_put_byte(sdpd_data_t *buf, uint8_t byte)
530 {
531
532 if (buf->next >= buf->start && buf->next < buf->end)
533 buf->next[0] = byte;
534
535 buf->next++;
536 }
537
538 static void
sdpd_put_attr(sdpd_data_t * buf,uint16_t attr,sdp_data_t * data)539 sdpd_put_attr(sdpd_data_t *buf, uint16_t attr, sdp_data_t *data)
540 {
541 uint8_t *p;
542
543 sdpd_put_byte(buf, SDP_DATA_UINT16);
544 sdpd_put_byte(buf, (uint8_t)(attr >> 8));
545 sdpd_put_byte(buf, (uint8_t)(attr));
546
547 for (p = data->next; p < data->end; p++)
548 sdpd_put_byte(buf, *p);
549 }
550
551 /*
552 * Since we always use a seq16 and never check the length, we will send
553 * an invalid header if it grows too large. We could always use a seq32
554 * but the chance of overflow is small so ignore it for now.
555 */
556 static void
sdpd_open_seq(sdpd_data_t * buf)557 sdpd_open_seq(sdpd_data_t *buf)
558 {
559
560 buf->next += 3;
561 }
562
563 static void
sdpd_close_seq(sdpd_data_t * buf,uint8_t * first)564 sdpd_close_seq(sdpd_data_t *buf, uint8_t *first)
565 {
566 uint8_t *next;
567 size_t len;
568
569 next = buf->next;
570 buf->next = first;
571 len = next - first - 3;
572
573 sdpd_put_byte(buf, SDP_DATA_SEQ16);
574 sdpd_put_byte(buf, 0xff & (len >> 8));
575 sdpd_put_byte(buf, 0xff & (len >> 0));
576 buf->next = next;
577 }
578