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