No empty .Rs/.Re
[netbsd-mini2440.git] / usr.sbin / sdpd / service.c
blob7e5a5a542727d628e56ae094c3f4ddf2a2542997
1 /* $NetBSD: service.c$ */
3 /*-
4 * Copyright (c) 2009 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Iain Hibbert.
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.
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.
32 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: service.c$");
35 #include <bluetooth.h>
36 #include <sdp.h>
38 #include "sdpd.h"
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]
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;
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 *);
59 uint16_t
60 service_search_request(server_t *srv, int fd)
62 record_t *r;
63 sdp_data_t d, s;
64 int max, total, count;
66 d.next = srv->ibuf;
67 d.end = srv->ibuf + srv->pdu.len;
70 * extract ServiceSearchPattern
72 if (!sdp_get_seq(&d, &s)
73 || !sdpd_valid_ssp(&s))
74 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
77 * extract MaximumServiceRecordCount
79 if (d.next + sizeof(uint16_t) > d.end)
80 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
82 max = be16dec(d.next);
83 d.next += sizeof(uint16_t);
84 if (max < 0x0001)
85 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
88 * validate ContinuationState
89 * If none given, this is a new request
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;
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;
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
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;
117 if (d.next + sizeof(uint32_t) > d.end)
118 return SDP_ERROR_CODE_INSUFFICIENT_RESOURCES;
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++;
129 total++;
133 * encode TotalServiceRecordCount and CurrentServiceRecordCount
135 be16enc(srv->obuf, total);
136 be16enc(srv->obuf + sizeof(uint16_t), count);
139 * encode ContinuationState which in this case will be the
140 * number of ServiceRecordHandles already sent.
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);
155 * fill in PDU header and we are done
157 srv->pdu.pid = SDP_PDU_SERVICE_SEARCH_RESPONSE;
158 srv->pdu.len = d.next - srv->obuf;
159 return 0;
162 uint16_t
163 service_attribute_request(server_t *srv, int fd)
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;
172 d.next = srv->ibuf;
173 d.end = srv->ibuf + srv->pdu.len;
176 * extract ServiceRecordHandle
178 if (d.next + sizeof(uint32_t) > d.end)
179 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
181 handle = be32dec(d.next);
182 d.next += sizeof(uint32_t);
185 * extract MaximumAttributeByteCount
187 if (d.next + sizeof(uint16_t) > d.end)
188 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
190 max = be16dec(d.next);
191 d.next += sizeof(uint16_t);
192 if (max < 0x0007)
193 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
196 * extract AttributeIDList
198 if (!sdp_get_seq(&d, &a)
199 || !sdpd_valid_ail(&a))
200 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
203 * validate ContinuationState
204 * If none given, this is a new request
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;
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;
221 * Set up the buffer window and write pointer, leaving space at
222 * buffer start for AttributeListByteCount and for ContinuationState
223 * at the end
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;
232 * Match the selected record against AttributeIDList, writing
233 * the data to the sparce buffer.
235 r = NULL;
236 db_next(srv, fd, &r);
237 if (r == NULL)
238 return SDP_ERROR_CODE_INVALID_SERVICE_RECORD_HANDLE;
240 sdpd_match_ail(r, a, &b);
242 if (b.next > b.end) {
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.
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;
256 if (tmp - b.next < 0x0002)
257 b.next = tmp - 0x0002;
259 /* encode AttributeListByteCount */
260 be16enc(srv->obuf, (b.next - b.start));
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));
271 /* reset & append ContinuationState */
272 srv->fdidx[fd].offset = 0;
273 db_unselect(srv, fd);
274 b.next[0] = 0;
275 b.next += 1;
279 * fill in PDU header and we are done
281 srv->pdu.pid = SDP_PDU_SERVICE_ATTRIBUTE_RESPONSE;
282 srv->pdu.len = b.next - srv->obuf;
283 return 0;
286 uint16_t
287 service_search_attribute_request(server_t *srv, int fd)
289 record_t *r;
290 sdpd_data_t b;
291 sdp_data_t a, d, s;
292 uint8_t *tmp;
293 int max;
295 d.next = srv->ibuf;
296 d.end = srv->ibuf + srv->pdu.len;
299 * extract ServiceSearchPattern
301 if (!sdp_get_seq(&d, &s)
302 || !sdpd_valid_ssp(&s))
303 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
306 * extract MaximumAttributeByteCount
308 if (d.next + sizeof(uint16_t) > d.end)
309 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
311 max = be16dec(d.next);
312 d.next += sizeof(uint16_t);
313 if (max < 0x0007)
314 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
317 * extract AttributeIDList
319 if (!sdp_get_seq(&d, &a)
320 || !sdpd_valid_ail(&a))
321 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
324 * validate ContinuationState
325 * If none given, this is a new request
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;
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;
342 * Set up the buffer window and write pointer, leaving space at
343 * buffer start for AttributeListByteCount and for ContinuationState
344 * at the end.
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;
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.
357 sdpd_open_seq(&b);
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;
366 sdpd_close_seq(&b, b.start - srv->fdidx[fd].offset);
368 if (b.next > b.end) {
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.
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;
382 if (tmp - b.next < 0x0002)
383 b.next = tmp - 0x0002;
385 /* encode AttributeListsByteCount */
386 be16enc(srv->obuf, (b.next - b.start));
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));
397 /* reset & append ContinuationState */
398 srv->fdidx[fd].offset = 0;
399 db_unselect(srv, fd);
400 b.next[0] = 0;
401 b.next += 1;
405 * fill in PDU header and we are done
407 srv->pdu.pid = SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_RESPONSE;
408 srv->pdu.len = b.next - srv->obuf;
409 return 0;
413 * validate ServiceSearchPattern
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
419 static bool
420 sdpd_valid_ssp(sdp_data_t *ssp)
422 sdp_data_t s = *ssp;
423 uuid_t u;
424 int n;
426 if (!sdp_data_valid(&s))
427 return false;
429 n = 0;
430 while (sdp_get_uuid(&s, &u))
431 n++;
433 if (n < 1 || n > 12 || s.next != s.end)
434 return false;
436 return true;
440 * validate AttributeIDList
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
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
452 static bool
453 sdpd_valid_ail(sdp_data_t *ail)
455 sdp_data_t a = *ail;
456 sdp_data_t d;
458 if (!sdp_data_valid(&a))
459 return false;
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;
467 return true;
471 * compare attributes in the ServiceRecord with the AttributeIDList
472 * and copy any matches to a sequence in the output buffer.
474 static bool
475 sdpd_match_ail(record_t *rec, sdp_data_t ail, sdpd_data_t *buf)
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;
484 r = rec->data;
485 f = buf->next;
486 lo = hi = -1;
487 rv = false;
489 sdpd_open_seq(buf);
491 while (sdp_get_attr(&r, &a, &v)) {
492 while (a > hi) {
493 if (ail.next == ail.end)
494 goto done;
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);
506 if (a < lo)
507 continue;
509 sdpd_put_attr(buf, a, &v);
510 rv = true;
513 done:
514 sdpd_close_seq(buf, f);
515 return rv;
519 * output data. We only actually store the bytes when the
520 * pointer is within the valid window.
522 static void
523 sdpd_put_byte(sdpd_data_t *buf, uint8_t byte)
526 if (buf->next >= buf->start && buf->next < buf->end)
527 buf->next[0] = byte;
529 buf->next++;
532 static void
533 sdpd_put_attr(sdpd_data_t *buf, uint16_t attr, sdp_data_t *data)
535 uint8_t *p;
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));
541 for (p = data->next; p < data->end; p++)
542 sdpd_put_byte(buf, *p);
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.
550 static void
551 sdpd_open_seq(sdpd_data_t *buf)
554 buf->next += 3;
557 static void
558 sdpd_close_seq(sdpd_data_t *buf, uint8_t *first)
560 uint8_t *next;
561 size_t len;
563 next = buf->next;
564 buf->next = first;
565 len = next - first - 3;
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;