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