1/*	$NetBSD: sdp_service.c,v 1.3 2010/11/13 19:43:56 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.3 2010/11/13 19:43:56 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 */
50static 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 */
58static size_t
59sdp_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
89bool
90sdp_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
212bool
213sdp_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
337bool
338sdp_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