1 /* $NetBSD: service.c,v 1.1 2009/05/12 10:05:07 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: service.c,v 1.1 2009/05/12 10:05:07 plunky Exp $"); 34 35 #include <bluetooth.h> 36 #include <sdp.h> 37 38 #include "sdpd.h" 39 40 /* 41 * This structure is a collection of pointers describing an output 42 * buffer for sdpd_put_byte(), below. bytes are written at next when 43 * it falls inside the range [start .. end - 1] 44 */ 45 typedef struct { 46 uint8_t *start; /* start of buffer window */ 47 uint8_t *next; /* current write position */ 48 uint8_t *end; /* end of buffer window */ 49 } sdpd_data_t; 50 51 static bool sdpd_valid_ssp(sdp_data_t *); 52 static bool sdpd_valid_ail(sdp_data_t *); 53 static bool sdpd_match_ail(record_t *, sdp_data_t, sdpd_data_t *); 54 static void sdpd_put_byte(sdpd_data_t *, uint8_t); 55 static void sdpd_put_attr(sdpd_data_t *, uint16_t, sdp_data_t *); 56 static void sdpd_open_seq(sdpd_data_t *); 57 static void sdpd_close_seq(sdpd_data_t *, uint8_t *); 58 59 uint16_t 60 service_search_request(server_t *srv, int fd) 61 { 62 record_t *r; 63 sdp_data_t d, s; 64 int max, total, count; 65 66 d.next = srv->ibuf; 67 d.end = srv->ibuf + srv->pdu.len; 68 69 /* 70 * extract ServiceSearchPattern 71 */ 72 if (!sdp_get_seq(&d, &s) 73 || !sdpd_valid_ssp(&s)) 74 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 75 76 /* 77 * extract MaximumServiceRecordCount 78 */ 79 if (d.next + sizeof(uint16_t) > d.end) 80 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 81 82 max = be16dec(d.next); 83 d.next += sizeof(uint16_t); 84 if (max < 0x0001) 85 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 86 87 /* 88 * validate ContinuationState 89 * If none given, this is a new request 90 */ 91 if (d.next + 1 > d.end 92 || d.next[0] > 16 93 || d.next + 1 + d.next[0] != d.end) 94 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 95 96 if (d.next[0] == 0) { 97 srv->fdidx[fd].offset = 0; 98 db_unselect(srv, fd); 99 db_select_ssp(srv, fd, &s); 100 } else if (srv->fdidx[fd].offset == 0 101 || d.next[0] != sizeof(uint16_t) 102 || be16dec(d.next + 1) != srv->fdidx[fd].offset) 103 return SDP_ERROR_CODE_INVALID_CONTINUATION_STATE; 104 105 /* 106 * Ready our output buffer. We leave space at the start for 107 * TotalServiceRecordCount and CurrentServiceRecordCount and 108 * at the end for ContinuationState, and we must have space 109 * for at least one ServiceRecordHandle. Then, step through 110 * selected records and write as many handles that will fit 111 * into the data space 112 */ 113 d.next = srv->obuf + sizeof(uint16_t) + sizeof(uint16_t); 114 d.end = srv->obuf + srv->fdidx[fd].omtu - 1 - sizeof(uint16_t); 115 count = total = 0; 116 117 if (d.next + sizeof(uint32_t) > d.end) 118 return SDP_ERROR_CODE_INSUFFICIENT_RESOURCES; 119 120 r = NULL; 121 while (db_next(srv, fd, &r) && total < max) { 122 if (total >= srv->fdidx[fd].offset 123 && d.next + sizeof(uint32_t) <= d.end) { 124 be32enc(d.next, r->handle); 125 d.next += sizeof(uint32_t); 126 count++; 127 } 128 129 total++; 130 } 131 132 /* 133 * encode TotalServiceRecordCount and CurrentServiceRecordCount 134 */ 135 be16enc(srv->obuf, total); 136 be16enc(srv->obuf + sizeof(uint16_t), count); 137 138 /* 139 * encode ContinuationState which in this case will be the 140 * number of ServiceRecordHandles already sent. 141 */ 142 if (r == NULL || total == max) { 143 srv->fdidx[fd].offset = 0; 144 db_unselect(srv, fd); 145 d.next[0] = 0; 146 d.next += 1; 147 } else { 148 srv->fdidx[fd].offset += count; 149 d.next[0] = sizeof(uint16_t); 150 be16enc(d.next + 1, srv->fdidx[fd].offset); 151 d.next += 1 + sizeof(uint16_t); 152 } 153 154 /* 155 * fill in PDU header and we are done 156 */ 157 srv->pdu.pid = SDP_PDU_SERVICE_SEARCH_RESPONSE; 158 srv->pdu.len = d.next - srv->obuf; 159 return 0; 160 } 161 162 uint16_t 163 service_attribute_request(server_t *srv, int fd) 164 { 165 record_t *r; 166 sdp_data_t a, d; 167 sdpd_data_t b; 168 uint8_t *tmp; 169 uint32_t handle; 170 int max; 171 172 d.next = srv->ibuf; 173 d.end = srv->ibuf + srv->pdu.len; 174 175 /* 176 * extract ServiceRecordHandle 177 */ 178 if (d.next + sizeof(uint32_t) > d.end) 179 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 180 181 handle = be32dec(d.next); 182 d.next += sizeof(uint32_t); 183 184 /* 185 * extract MaximumAttributeByteCount 186 */ 187 if (d.next + sizeof(uint16_t) > d.end) 188 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 189 190 max = be16dec(d.next); 191 d.next += sizeof(uint16_t); 192 if (max < 0x0007) 193 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 194 195 /* 196 * extract AttributeIDList 197 */ 198 if (!sdp_get_seq(&d, &a) 199 || !sdpd_valid_ail(&a)) 200 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 201 202 /* 203 * validate ContinuationState 204 * If none given, this is a new request 205 */ 206 if (d.next + 1 > d.end 207 || d.next[0] > 16 208 || d.next + 1 + d.next[0] != d.end) 209 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 210 211 if (d.next[0] == 0) { 212 srv->fdidx[fd].offset = 0; 213 db_unselect(srv, fd); 214 db_select_handle(srv, fd, handle); 215 } else if (srv->fdidx[fd].offset == 0 216 || d.next[0] != sizeof(uint16_t) 217 || be16dec(d.next + 1) != srv->fdidx[fd].offset) 218 return SDP_ERROR_CODE_INVALID_CONTINUATION_STATE; 219 220 /* 221 * Set up the buffer window and write pointer, leaving space at 222 * buffer start for AttributeListByteCount and for ContinuationState 223 * at the end 224 */ 225 b.start = srv->obuf + sizeof(uint16_t); 226 b.next = b.start - srv->fdidx[fd].offset; 227 b.end = srv->obuf + srv->fdidx[fd].omtu - 1; 228 if (b.start + max < b.end) 229 b.end = b.start + max; 230 231 /* 232 * Match the selected record against AttributeIDList, writing 233 * the data to the sparce buffer. 234 */ 235 r = NULL; 236 db_next(srv, fd, &r); 237 if (r == NULL) 238 return SDP_ERROR_CODE_INVALID_SERVICE_RECORD_HANDLE; 239 240 sdpd_match_ail(r, a, &b); 241 242 if (b.next > b.end) { 243 /* 244 * b.end is the limit of AttributeList that we are allowed 245 * to send so if we have exceeded that we need to adjust our 246 * response downwards. Recalculate the new cut off to allow 247 * writing the ContinuationState offset and ensure we don't 248 * exceed MaximumAttributeByteCount. Also, make sure that 249 * the continued length is not too short. 250 */ 251 tmp = b.next; 252 b.next = srv->obuf + srv->fdidx[fd].omtu - 1 - sizeof(uint16_t); 253 if (b.next > b.end) 254 b.next = b.end; 255 256 if (tmp - b.next < 0x0002) 257 b.next = tmp - 0x0002; 258 259 /* encode AttributeListByteCount */ 260 be16enc(srv->obuf, (b.next - b.start)); 261 262 /* calculate & append ContinuationState */ 263 srv->fdidx[fd].offset += (b.next - b.start); 264 b.next[0] = sizeof(uint16_t); 265 be16enc(b.next + 1, srv->fdidx[fd].offset); 266 b.next += 1 + sizeof(uint16_t); 267 } else { 268 /* encode AttributeListByteCount */ 269 be16enc(srv->obuf, (b.next - b.start)); 270 271 /* reset & append ContinuationState */ 272 srv->fdidx[fd].offset = 0; 273 db_unselect(srv, fd); 274 b.next[0] = 0; 275 b.next += 1; 276 } 277 278 /* 279 * fill in PDU header and we are done 280 */ 281 srv->pdu.pid = SDP_PDU_SERVICE_ATTRIBUTE_RESPONSE; 282 srv->pdu.len = b.next - srv->obuf; 283 return 0; 284 } 285 286 uint16_t 287 service_search_attribute_request(server_t *srv, int fd) 288 { 289 record_t *r; 290 sdpd_data_t b; 291 sdp_data_t a, d, s; 292 uint8_t *tmp; 293 int max; 294 295 d.next = srv->ibuf; 296 d.end = srv->ibuf + srv->pdu.len; 297 298 /* 299 * extract ServiceSearchPattern 300 */ 301 if (!sdp_get_seq(&d, &s) 302 || !sdpd_valid_ssp(&s)) 303 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 304 305 /* 306 * extract MaximumAttributeByteCount 307 */ 308 if (d.next + sizeof(uint16_t) > d.end) 309 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 310 311 max = be16dec(d.next); 312 d.next += sizeof(uint16_t); 313 if (max < 0x0007) 314 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 315 316 /* 317 * extract AttributeIDList 318 */ 319 if (!sdp_get_seq(&d, &a) 320 || !sdpd_valid_ail(&a)) 321 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 322 323 /* 324 * validate ContinuationState 325 * If none given, this is a new request 326 */ 327 if (d.next + 1 > d.end 328 || d.next[0] > 16 329 || d.next + 1 + d.next[0] != d.end) 330 return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; 331 332 if (d.next[0] == 0) { 333 srv->fdidx[fd].offset = 0; 334 db_unselect(srv, fd); 335 db_select_ssp(srv, fd, &s); 336 } else if (srv->fdidx[fd].offset == 0 337 || d.next[0] != sizeof(uint16_t) 338 || be16dec(d.next + 1) != srv->fdidx[fd].offset) 339 return SDP_ERROR_CODE_INVALID_CONTINUATION_STATE; 340 341 /* 342 * Set up the buffer window and write pointer, leaving space at 343 * buffer start for AttributeListByteCount and for ContinuationState 344 * at the end. 345 */ 346 b.start = srv->obuf + sizeof(uint16_t); 347 b.end = srv->obuf + srv->fdidx[fd].omtu - 1; 348 b.next = b.start - srv->fdidx[fd].offset; 349 if (b.start + max < b.end) 350 b.end = b.start + max; 351 352 /* 353 * match all selected records against the AttributeIDList, 354 * wrapping the whole in a sequence. Where a record does 355 * not match any attributes, delete the empty sequence. 356 */ 357 sdpd_open_seq(&b); 358 359 r = NULL; 360 while (db_next(srv, fd, &r)) { 361 tmp = b.next; 362 if (!sdpd_match_ail(r, a, &b)) 363 b.next = tmp; 364 } 365 366 sdpd_close_seq(&b, b.start - srv->fdidx[fd].offset); 367 368 if (b.next > b.end) { 369 /* 370 * b.end is the limit of AttributeLists that we are allowed 371 * to send so if we have exceeded that we need to adjust our 372 * response downwards. Recalculate the new cut off to allow 373 * writing the ContinuationState offset and ensure we don't 374 * exceed MaximumAttributeByteCount. Also, make sure that 375 * the continued length is not too short. 376 */ 377 tmp = b.next; 378 b.next = srv->obuf + srv->fdidx[fd].omtu - 1 - sizeof(uint16_t); 379 if (b.next > b.end) 380 b.next = b.end; 381 382 if (tmp - b.next < 0x0002) 383 b.next = tmp - 0x0002; 384 385 /* encode AttributeListsByteCount */ 386 be16enc(srv->obuf, (b.next - b.start)); 387 388 /* calculate & append ContinuationState */ 389 srv->fdidx[fd].offset += (b.next - b.start); 390 b.next[0] = sizeof(uint16_t); 391 be16enc(b.next + 1, srv->fdidx[fd].offset); 392 b.next += 1 + sizeof(uint16_t); 393 } else { 394 /* encode AttributeListsByteCount */ 395 be16enc(srv->obuf, (b.next - b.start)); 396 397 /* reset & append ContinuationState */ 398 srv->fdidx[fd].offset = 0; 399 db_unselect(srv, fd); 400 b.next[0] = 0; 401 b.next += 1; 402 } 403 404 /* 405 * fill in PDU header and we are done 406 */ 407 srv->pdu.pid = SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_RESPONSE; 408 srv->pdu.len = b.next - srv->obuf; 409 return 0; 410 } 411 412 /* 413 * validate ServiceSearchPattern 414 * 415 * The SerivceSearchPattern is a list of data elements, where each element 416 * is a UUID. The list must contain at least one UUID and the maximum number 417 * of UUIDs is 12 418 */ 419 static bool 420 sdpd_valid_ssp(sdp_data_t *ssp) 421 { 422 sdp_data_t s = *ssp; 423 uuid_t u; 424 int n; 425 426 if (!sdp_data_valid(&s)) 427 return false; 428 429 n = 0; 430 while (sdp_get_uuid(&s, &u)) 431 n++; 432 433 if (n < 1 || n > 12 || s.next != s.end) 434 return false; 435 436 return true; 437 } 438 439 /* 440 * validate AttributeIDList 441 * 442 * The AttributeIDList is a list of data elements, where each element is 443 * either an attribute ID encoded as an unsigned 16-bit integer or a range 444 * of attribute IDs encoded as an unsigned 32-bit integer where the high 445 * order 16-bits are the beginning of the range and the low order 16-bits 446 * are the ending 447 * 448 * The attrbute IDs should be listed in ascending order without duplication 449 * of any attribute ID values but we don't worry about that, since if the 450 * remote party messes up, their results will be messed up 451 */ 452 static bool 453 sdpd_valid_ail(sdp_data_t *ail) 454 { 455 sdp_data_t a = *ail; 456 sdp_data_t d; 457 458 if (!sdp_data_valid(&a)) 459 return false; 460 461 while (sdp_get_data(&a, &d)) { 462 if (sdp_data_type(&d) != SDP_DATA_UINT16 463 && sdp_data_type(&d) != SDP_DATA_UINT32) 464 return false; 465 } 466 467 return true; 468 } 469 470 /* 471 * compare attributes in the ServiceRecord with the AttributeIDList 472 * and copy any matches to a sequence in the output buffer. 473 */ 474 static bool 475 sdpd_match_ail(record_t *rec, sdp_data_t ail, sdpd_data_t *buf) 476 { 477 sdp_data_t r, v; 478 uint16_t a; 479 uintmax_t ui; 480 uint8_t *f; 481 int lo, hi; 482 bool rv; 483 484 r = rec->data; 485 f = buf->next; 486 lo = hi = -1; 487 rv = false; 488 489 sdpd_open_seq(buf); 490 491 while (sdp_get_attr(&r, &a, &v)) { 492 while (a > hi) { 493 if (ail.next == ail.end) 494 goto done; 495 496 if (sdp_data_type(&ail) == SDP_DATA_UINT16) { 497 sdp_get_uint(&ail, &ui); 498 lo = hi = ui; 499 } else { 500 sdp_get_uint(&ail, &ui); 501 lo = (uint16_t)(ui >> 16); 502 hi = (uint16_t)(ui); 503 } 504 } 505 506 if (a < lo) 507 continue; 508 509 sdpd_put_attr(buf, a, &v); 510 rv = true; 511 } 512 513 done: 514 sdpd_close_seq(buf, f); 515 return rv; 516 } 517 518 /* 519 * output data. We only actually store the bytes when the 520 * pointer is within the valid window. 521 */ 522 static void 523 sdpd_put_byte(sdpd_data_t *buf, uint8_t byte) 524 { 525 526 if (buf->next >= buf->start && buf->next < buf->end) 527 buf->next[0] = byte; 528 529 buf->next++; 530 } 531 532 static void 533 sdpd_put_attr(sdpd_data_t *buf, uint16_t attr, sdp_data_t *data) 534 { 535 uint8_t *p; 536 537 sdpd_put_byte(buf, SDP_DATA_UINT16); 538 sdpd_put_byte(buf, (uint8_t)(attr >> 8)); 539 sdpd_put_byte(buf, (uint8_t)(attr)); 540 541 for (p = data->next; p < data->end; p++) 542 sdpd_put_byte(buf, *p); 543 } 544 545 /* 546 * Since we always use a seq16 and never check the length, we will send 547 * an invalid header if it grows too large. We could always use a seq32 548 * but the chance of overflow is small so ignore it for now. 549 */ 550 static void 551 sdpd_open_seq(sdpd_data_t *buf) 552 { 553 554 buf->next += 3; 555 } 556 557 static void 558 sdpd_close_seq(sdpd_data_t *buf, uint8_t *first) 559 { 560 uint8_t *next; 561 size_t len; 562 563 next = buf->next; 564 buf->next = first; 565 len = next - first - 3; 566 567 sdpd_put_byte(buf, SDP_DATA_SEQ16); 568 sdpd_put_byte(buf, 0xff & (len >> 8)); 569 sdpd_put_byte(buf, 0xff & (len >> 0)); 570 buf->next = next; 571 } 572