xref: /netbsd/lib/libbluetooth/sdp_service.c (revision 231f6b58)
1 /*	$NetBSD: sdp_service.c,v 1.4 2010/11/20 12:12:21 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: sdp_service.c,v 1.4 2010/11/20 12:12:21 plunky Exp $");
34 
35 #include <sys/atomic.h>
36 
37 #include <errno.h>
38 #include <limits.h>
39 #include <sdp.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 
44 #include "sdp-int.h"
45 
46 /*
47  * If AttributeIDList is given as NULL, request all attributes.
48  * (this is actually const data but we can't declare it const)
49  */
50 static uint8_t ail_default[] = { 0x0a, 0x00, 0x00, 0xff, 0xff };
51 
52 /*
53  * This provides the maximum size that the response buffer will be
54  * allowed to grow to.
55  *
56  * Default is UINT16_MAX but it can be overridden at runtime.
57  */
58 static size_t
sdp_response_max(void)59 sdp_response_max(void)
60 {
61 	static size_t max = UINT16_MAX;
62 	static unsigned int check = 1;
63 	char *env, *ep;
64 	unsigned long v;
65 
66 	while (atomic_swap_uint(&check, 0)) { /* only check env once */
67 		env = getenv("SDP_RESPONSE_MAX");
68 		if (env == NULL)
69 			break;
70 
71 		errno = 0;
72 		v = strtoul(env, &ep, 0);
73 		if (env[0] == '\0' || *ep != '\0')
74 			break;
75 
76 		if (errno == ERANGE && v == ULONG_MAX)
77 			break;
78 
79 		/* lower limit is arbitrary */
80 		if (v < UINT8_MAX || v > UINT32_MAX)
81 			break;
82 
83 		max = v;
84 	}
85 
86 	return max;
87 }
88 
89 bool
sdp_service_search(struct sdp_session * ss,const sdp_data_t * ssp,uint32_t * id,int * num)90 sdp_service_search(struct sdp_session *ss, const sdp_data_t *ssp,
91     uint32_t *id, int *num)
92 {
93 	struct iovec	req[5];
94 	sdp_data_t	hdr;
95 	uint8_t		sdata[5], max[2];
96 	uint8_t		*ptr, *end;
97 	ssize_t		len;
98 	uint16_t	total, count, got;
99 
100 	/*
101 	 * setup ServiceSearchPattern
102 	 */
103 	len = ssp->end - ssp->next;
104 	if (len < 0 || len > UINT16_MAX) {
105 		errno = EINVAL;
106 		return false;
107 	}
108 
109 	hdr.next = sdata;
110 	hdr.end = sdata + sizeof(sdata) + len;
111 	sdp_put_seq(&hdr, len);
112 	req[1].iov_base = sdata;
113 	req[1].iov_len = hdr.next - sdata;
114 
115 	req[2].iov_base = ssp->next;
116 	req[2].iov_len = len;
117 
118 	/*
119 	 * setup MaximumServiceRecordCount
120 	 */
121 	if (*num < 0 || *num > UINT16_MAX) {
122 		errno = EINVAL;
123 		return false;
124 	}
125 	be16enc(max, *num);
126 	req[3].iov_base = max;
127 	req[3].iov_len = sizeof(uint16_t);
128 
129 	/*
130 	 * clear ContinuationState
131 	 */
132 	ss->cs[0] = 0;
133 
134 	/*
135 	 * ServiceSearch Transaction
136 	 */
137 	got = 0;
138 	for (;;) {
139 		/*
140 		 * setup ContinuationState
141 		 */
142 		req[4].iov_base = ss->cs;
143 		req[4].iov_len = ss->cs[0] + 1;
144 
145 		if (!_sdp_send_pdu(ss, SDP_PDU_SERVICE_SEARCH_REQUEST,
146 		    req, __arraycount(req)))
147 			return false;
148 
149 		len = _sdp_recv_pdu(ss, SDP_PDU_SERVICE_SEARCH_RESPONSE);
150 		if (len == -1)
151 			return false;
152 
153 		ptr = ss->ibuf;
154 		end = ss->ibuf + len;
155 
156 		/*
157 		 * extract TotalServiceRecordCount
158 		 */
159 		if (ptr + sizeof(uint16_t) > end)
160 			break;
161 
162 		total = be16dec(ptr);
163 		ptr += sizeof(uint16_t);
164 		if (total > *num)
165 			break;
166 
167 		/*
168 		 * extract CurrentServiceRecordCount
169 		 */
170 		if (ptr + sizeof(uint16_t) > end)
171 			break;
172 
173 		count = be16dec(ptr);
174 		ptr += sizeof(uint16_t);
175 		if (got + count > total)
176 			break;
177 
178 		/*
179 		 * extract ServiceRecordHandleList
180 		 */
181 		if (ptr + count * sizeof(uint32_t) > end)
182 			break;
183 
184 		while (count-- > 0) {
185 			id[got++] = be32dec(ptr);
186 			ptr += sizeof(uint32_t);
187 		}
188 
189 		/*
190 		 * extract ContinuationState
191 		 */
192 		if (ptr + 1 > end
193 		    || ptr[0] > 16
194 		    || ptr + ptr[0] + 1 != end)
195 			break;
196 
197 		memcpy(ss->cs, ptr, (size_t)(ptr[0] + 1));
198 
199 		/*
200 		 * Complete?
201 		 */
202 		if (ss->cs[0] == 0) {
203 			*num = got;
204 			return true;
205 		}
206 	}
207 
208 	errno = EIO;
209 	return false;
210 }
211 
212 bool
sdp_service_attribute(struct sdp_session * ss,uint32_t id,const sdp_data_t * ail,sdp_data_t * rsp)213 sdp_service_attribute(struct sdp_session *ss, uint32_t id,
214     const sdp_data_t *ail, sdp_data_t *rsp)
215 {
216 	struct iovec	req[6];
217 	sdp_data_t	hdr;
218 	uint8_t		adata[5], handle[4], max[2];
219 	uint8_t		*ptr, *end, *rbuf;
220 	ssize_t		len;
221 	size_t		rlen, count;
222 
223 	/*
224 	 * setup ServiceRecordHandle
225 	 */
226 	be32enc(handle, id);
227 	req[1].iov_base = handle;
228 	req[1].iov_len = sizeof(uint32_t);
229 
230 	/*
231 	 * setup MaximumAttributeByteCount
232 	 */
233 	be16enc(max, ss->imtu - sizeof(uint16_t) - sizeof(ss->cs));
234 	req[2].iov_base = max;
235 	req[2].iov_len = sizeof(uint16_t);
236 
237 	/*
238 	 * setup AttributeIDList
239 	 */
240 	len = (ail == NULL ? (ssize_t)sizeof(ail_default) : (ail->end - ail->next));
241 	if (len < 0 || len > UINT16_MAX) {
242 		errno = EINVAL;
243 		return false;
244 	}
245 
246 	hdr.next = adata;
247 	hdr.end = adata + sizeof(adata) + len;
248 	sdp_put_seq(&hdr, len);
249 	req[3].iov_base = adata;
250 	req[3].iov_len = hdr.next - adata;
251 
252 	req[4].iov_base = (ail == NULL ? ail_default : ail->next);
253 	req[4].iov_len = len;
254 
255 	/*
256 	 * clear ContinuationState
257 	 */
258 	ss->cs[0] = 0;
259 
260 	/*
261 	 * ServiceAttribute Transaction
262 	 */
263 	rlen = 0;
264 	for (;;) {
265 		/*
266 		 * setup ContinuationState
267 		 */
268 		req[5].iov_base = ss->cs;
269 		req[5].iov_len = ss->cs[0] + 1;
270 
271 		if (!_sdp_send_pdu(ss, SDP_PDU_SERVICE_ATTRIBUTE_REQUEST,
272 		    req, __arraycount(req)))
273 			return false;
274 
275 		len = _sdp_recv_pdu(ss, SDP_PDU_SERVICE_ATTRIBUTE_RESPONSE);
276 		if (len == -1)
277 			return false;
278 
279 		ptr = ss->ibuf;
280 		end = ss->ibuf + len;
281 
282 		/*
283 		 * extract AttributeListByteCount
284 		 */
285 		if (ptr + sizeof(uint16_t) > end)
286 			break;
287 
288 		count = be16dec(ptr);
289 		ptr += sizeof(uint16_t);
290 		if (count == 0 || ptr + count > end)
291 			break;
292 
293 		/*
294 		 * extract AttributeList
295 		 */
296 		if (rlen + count > sdp_response_max())
297 			break;
298 
299 		rbuf = realloc(ss->rbuf, rlen + count);
300 		if (rbuf == NULL)
301 			return false;
302 
303 		ss->rbuf = rbuf;
304 		memcpy(rbuf + rlen, ptr, count);
305 		rlen += count;
306 		ptr += count;
307 
308 		/*
309 		 * extract ContinuationState
310 		 */
311 		if (ptr + 1 > end
312 		    || ptr[0] > 16
313 		    || ptr + ptr[0] + 1 != end)
314 			break;
315 
316 		memcpy(ss->cs, ptr, (size_t)(ptr[0] + 1));
317 
318 		/*
319 		 * Complete?
320 		 */
321 		if (ss->cs[0] == 0) {
322 			rsp->next = rbuf;
323 			rsp->end = rbuf + rlen;
324 			if (sdp_data_size(rsp) != (ssize_t)rlen
325 			    || !sdp_data_valid(rsp)
326 			    || !sdp_get_seq(rsp, rsp))
327 				break;
328 
329 			return true;
330 		}
331 	}
332 
333 	errno = EIO;
334 	return false;
335 }
336 
337 bool
sdp_service_search_attribute(struct sdp_session * ss,const sdp_data_t * ssp,const sdp_data_t * ail,sdp_data_t * rsp)338 sdp_service_search_attribute(struct sdp_session *ss, const sdp_data_t *ssp,
339     const sdp_data_t *ail, sdp_data_t *rsp)
340 {
341 	struct iovec	req[7];
342 	sdp_data_t	hdr;
343 	uint8_t		sdata[5], adata[5], max[2];
344 	uint8_t		*ptr, *end, *rbuf;
345 	ssize_t		len;
346 	size_t		rlen, count;
347 
348 	/*
349 	 * setup ServiceSearchPattern
350 	 */
351 	len = ssp->end - ssp->next;
352 	if (len < 0 || len > UINT16_MAX) {
353 		errno = EINVAL;
354 		return false;
355 	}
356 
357 	hdr.next = sdata;
358 	hdr.end = sdata + sizeof(sdata) + len;
359 	sdp_put_seq(&hdr, len);
360 	req[1].iov_base = sdata;
361 	req[1].iov_len = hdr.next - sdata;
362 
363 	req[2].iov_base = ssp->next;
364 	req[2].iov_len = len;
365 
366 	/*
367 	 * setup MaximumAttributeByteCount
368 	 */
369 	be16enc(max, ss->imtu - sizeof(uint16_t) - sizeof(ss->cs));
370 	req[3].iov_base = max;
371 	req[3].iov_len = sizeof(uint16_t);
372 
373 	/*
374 	 * setup AttributeIDList
375 	 */
376 	len = (ail == NULL ? (ssize_t)sizeof(ail_default) : (ail->end - ail->next));
377 	if (len < 0 || len > UINT16_MAX) {
378 		errno = EINVAL;
379 		return false;
380 	}
381 
382 	hdr.next = adata;
383 	hdr.end = adata + sizeof(adata) + len;
384 	sdp_put_seq(&hdr, len);
385 	req[4].iov_base = adata;
386 	req[4].iov_len = hdr.next - adata;
387 
388 	req[5].iov_base = (ail == NULL ? ail_default : ail->next);
389 	req[5].iov_len = len;
390 
391 	/*
392 	 * clear ContinuationState
393 	 */
394 	ss->cs[0] = 0;
395 
396 	/*
397 	 * ServiceSearchAttribute Transaction
398 	 */
399 	rlen = 0;
400 	for (;;) {
401 		/*
402 		 * setup ContinuationState
403 		 */
404 		req[6].iov_base = ss->cs;
405 		req[6].iov_len = ss->cs[0] + 1;
406 
407 		if (!_sdp_send_pdu(ss, SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST,
408 		    req, __arraycount(req)))
409 			return false;
410 
411 		len = _sdp_recv_pdu(ss, SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_RESPONSE);
412 		if (len == -1)
413 			return false;
414 
415 		ptr = ss->ibuf;
416 		end = ss->ibuf + len;
417 
418 		/*
419 		 * extract AttributeListsByteCount
420 		 */
421 		if (ptr + sizeof(uint16_t) > end)
422 			break;
423 
424 		count = be16dec(ptr);
425 		ptr += sizeof(uint16_t);
426 		if (count == 0 || ptr + count > end)
427 			break;
428 
429 		/*
430 		 * extract AttributeLists
431 		 */
432 		if (rlen + count > sdp_response_max())
433 			break;
434 
435 		rbuf = realloc(ss->rbuf, rlen + count);
436 		if (rbuf == NULL)
437 			return false;
438 
439 		ss->rbuf = rbuf;
440 		memcpy(rbuf + rlen, ptr, count);
441 		rlen += count;
442 		ptr += count;
443 
444 		/*
445 		 * extract ContinuationState
446 		 */
447 		if (ptr + 1 > end
448 		    || ptr[0] > 16
449 		    || ptr + ptr[0] + 1 != end)
450 			break;
451 
452 		memcpy(ss->cs, ptr, (size_t)(ptr[0] + 1));
453 
454 		/*
455 		 * Complete?
456 		 */
457 		if (ss->cs[0] == 0) {
458 			rsp->next = rbuf;
459 			rsp->end = rbuf + rlen;
460 			if (sdp_data_size(rsp) != (ssize_t)rlen
461 			    || !sdp_data_valid(rsp)
462 			    || !sdp_get_seq(rsp, rsp))
463 				break;
464 
465 			return true;
466 		}
467 	}
468 
469 	errno = EIO;
470 	return false;
471 }
472