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