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