Home | History | Annotate | Line # | Download | only in libbluetooth
      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
     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
     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
    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
    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