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