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