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