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 */
45typedef 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
51static bool sdpd_valid_ssp(sdp_data_t *);
52static bool sdpd_valid_ail(sdp_data_t *);
53static bool sdpd_match_ail(record_t *, sdp_data_t, sdpd_data_t *);
54static void sdpd_put_byte(sdpd_data_t *, uint8_t);
55static void sdpd_put_attr(sdpd_data_t *, uint16_t, sdp_data_t *);
56static void sdpd_open_seq(sdpd_data_t *);
57static void sdpd_close_seq(sdpd_data_t *, uint8_t *);
58
59uint16_t
60service_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
164uint16_t
165service_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 sparce 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
290uint16_t
291service_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 SerivceSearchPattern 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 */
425static bool
426sdpd_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 attrbute 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 */
458static bool
459sdpd_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 */
480static bool
481sdpd_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
519done:
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 */
528static void
529sdpd_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
538static void
539sdpd_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 */
556static void
557sdpd_open_seq(sdpd_data_t *buf)
558{
559
560	buf->next += 3;
561}
562
563static void
564sdpd_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