xref: /netbsd-src/usr.sbin/sdpd/service.c (revision 82bba4e936dd1e9880e4f07ab0ecd4335b2b46ea)
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