1 /* $NetBSD: service.c$ */
4 * Copyright (c) 2009 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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>
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]
46 uint8_t *start
; /* start of buffer window */
47 uint8_t *next
; /* current write position */
48 uint8_t *end
; /* end of buffer window */
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 *);
60 service_search_request(server_t
*srv
, int fd
)
64 int max
, total
, count
;
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);
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
93 || d
.next
+ 1 + d
.next
[0] != d
.end
)
94 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX
;
97 srv
->fdidx
[fd
].offset
= 0;
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);
117 if (d
.next
+ sizeof(uint32_t) > d
.end
)
118 return SDP_ERROR_CODE_INSUFFICIENT_RESOURCES
;
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);
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
);
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
;
163 service_attribute_request(server_t
*srv
, int fd
)
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);
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
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
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.
236 db_next(srv
, fd
, &r
);
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.
252 b
.next
= srv
->obuf
+ srv
->fdidx
[fd
].omtu
- 1 - sizeof(uint16_t);
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);
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
);
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
;
287 service_search_attribute_request(server_t
*srv
, int fd
)
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);
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
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
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.
360 while (db_next(srv
, fd
, &r
)) {
362 if (!sdpd_match_ail(r
, a
, &b
))
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.
378 b
.next
= srv
->obuf
+ srv
->fdidx
[fd
].omtu
- 1 - sizeof(uint16_t);
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);
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
);
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
;
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
420 sdpd_valid_ssp(sdp_data_t
*ssp
)
426 if (!sdp_data_valid(&s
))
430 while (sdp_get_uuid(&s
, &u
))
433 if (n
< 1 || n
> 12 || s
.next
!= s
.end
)
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
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
453 sdpd_valid_ail(sdp_data_t
*ail
)
458 if (!sdp_data_valid(&a
))
461 while (sdp_get_data(&a
, &d
)) {
462 if (sdp_data_type(&d
) != SDP_DATA_UINT16
463 && sdp_data_type(&d
) != SDP_DATA_UINT32
)
471 * compare attributes in the ServiceRecord with the AttributeIDList
472 * and copy any matches to a sequence in the output buffer.
475 sdpd_match_ail(record_t
*rec
, sdp_data_t ail
, sdpd_data_t
*buf
)
491 while (sdp_get_attr(&r
, &a
, &v
)) {
493 if (ail
.next
== ail
.end
)
496 if (sdp_data_type(&ail
) == SDP_DATA_UINT16
) {
497 sdp_get_uint(&ail
, &ui
);
500 sdp_get_uint(&ail
, &ui
);
501 lo
= (uint16_t)(ui
>> 16);
509 sdpd_put_attr(buf
, a
, &v
);
514 sdpd_close_seq(buf
, f
);
519 * output data. We only actually store the bytes when the
520 * pointer is within the valid window.
523 sdpd_put_byte(sdpd_data_t
*buf
, uint8_t byte
)
526 if (buf
->next
>= buf
->start
&& buf
->next
< buf
->end
)
533 sdpd_put_attr(sdpd_data_t
*buf
, uint16_t attr
, sdp_data_t
*data
)
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.
551 sdpd_open_seq(sdpd_data_t
*buf
)
558 sdpd_close_seq(sdpd_data_t
*buf
, uint8_t *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));