1 /* 2 * 3 * BlueZ - Bluetooth protocol stack for Linux 4 * 5 * Copyright (C) 2001-2002 Nokia Corporation 6 * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk (at) qualcomm.com> 7 * Copyright (C) 2002-2010 Marcel Holtmann <marcel (at) holtmann.org> 8 * Copyright (C) 2002-2003 Stephen Crane <steve.crane (at) rococosoft.com> 9 * 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation; either version 2 of the License, or 14 * (at your option) any later version. 15 * 16 * This program is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with this program; if not, write to the Free Software 23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 24 * 25 */ 26 27 #ifdef HAVE_CONFIG_H 28 #include <config.h> 29 #endif 30 31 #include <stdio.h> 32 #include <errno.h> 33 #include <stdlib.h> 34 #include <assert.h> 35 #include <sys/time.h> 36 #include <sys/socket.h> 37 38 #include <bluetooth/bluetooth.h> 39 #include <bluetooth/sdp.h> 40 #include <bluetooth/sdp_lib.h> 41 42 #include <netinet/in.h> 43 44 #include <glib.h> 45 #include <dbus/dbus.h> 46 47 #include "sdpd.h" 48 #include "log.h" 49 #include "manager.h" 50 #include "adapter.h" 51 52 static sdp_record_t *server = NULL; 53 54 static uint16_t did_vendor = 0x0000; 55 static uint16_t did_product = 0x0000; 56 static uint16_t did_version = 0x0000; 57 58 /* 59 * List of version numbers supported by the SDP server. 60 * Add to this list when newer versions are supported. 61 */ 62 static sdp_version_t sdpVnumArray[1] = { 63 { 1, 0 } 64 }; 65 static const int sdpServerVnumEntries = 1; 66 67 /* 68 * A simple function which returns the time of day in 69 * seconds. Used for updating the service db state 70 * attribute of the service record of the SDP server 71 */ 72 uint32_t sdp_get_time() 73 { 74 /* 75 * To handle failure in gettimeofday, so an old 76 * value is returned and service does not fail 77 */ 78 static struct timeval tm; 79 80 gettimeofday(&tm, NULL); 81 return (uint32_t) tm.tv_sec; 82 } 83 84 /* 85 * The service database state is an attribute of the service record 86 * of the SDP server itself. This attribute is guaranteed to 87 * change if any of the contents of the service repository 88 * changes. This function updates the timestamp of value of 89 * the svcDBState attribute 90 * Set the SDP server DB. Simply a timestamp which is the marker 91 * when the DB was modified. 92 */ 93 static void update_db_timestamp(void) 94 { 95 uint32_t dbts = sdp_get_time(); 96 sdp_data_t *d = sdp_data_alloc(SDP_UINT32, &dbts); 97 sdp_attr_replace(server, SDP_ATTR_SVCDB_STATE, d); 98 } 99 100 static void update_adapter_svclass_list(struct btd_adapter *adapter) 101 { 102 sdp_list_t *list = adapter_get_services(adapter); 103 uint8_t val = 0; 104 105 for (; list; list = list->next) { 106 sdp_record_t *rec = (sdp_record_t *) list->data; 107 108 if (rec->svclass.type != SDP_UUID16) 109 continue; 110 111 switch (rec->svclass.value.uuid16) { 112 case DIALUP_NET_SVCLASS_ID: 113 case CIP_SVCLASS_ID: 114 val |= 0x42; /* Telephony & Networking */ 115 break; 116 case IRMC_SYNC_SVCLASS_ID: 117 case OBEX_OBJPUSH_SVCLASS_ID: 118 case OBEX_FILETRANS_SVCLASS_ID: 119 case IRMC_SYNC_CMD_SVCLASS_ID: 120 case PBAP_PSE_SVCLASS_ID: 121 val |= 0x10; /* Object Transfer */ 122 break; 123 case HEADSET_SVCLASS_ID: 124 case HANDSFREE_SVCLASS_ID: 125 val |= 0x20; /* Audio */ 126 break; 127 case CORDLESS_TELEPHONY_SVCLASS_ID: 128 case INTERCOM_SVCLASS_ID: 129 case FAX_SVCLASS_ID: 130 case SAP_SVCLASS_ID: 131 /* 132 * Setting the telephony bit for the handsfree audio gateway 133 * role is not required by the HFP specification, but the 134 * Nokia 616 carkit is just plain broken! It will refuse 135 * pairing without this bit set. 136 */ 137 case HANDSFREE_AGW_SVCLASS_ID: 138 val |= 0x40; /* Telephony */ 139 break; 140 case AUDIO_SOURCE_SVCLASS_ID: 141 case VIDEO_SOURCE_SVCLASS_ID: 142 val |= 0x08; /* Capturing */ 143 break; 144 case AUDIO_SINK_SVCLASS_ID: 145 case VIDEO_SINK_SVCLASS_ID: 146 val |= 0x04; /* Rendering */ 147 break; 148 case PANU_SVCLASS_ID: 149 case NAP_SVCLASS_ID: 150 case GN_SVCLASS_ID: 151 val |= 0x02; /* Networking */ 152 break; 153 } 154 } 155 156 SDPDBG("Service classes 0x%02x", val); 157 158 manager_update_svc(adapter, val); 159 } 160 161 static void update_svclass_list(const bdaddr_t *src) 162 { 163 GSList *adapters = manager_get_adapters(); 164 165 for (; adapters; adapters = adapters->next) { 166 struct btd_adapter *adapter = adapters->data; 167 bdaddr_t bdaddr; 168 169 adapter_get_address(adapter, &bdaddr); 170 171 if (bacmp(src, BDADDR_ANY) == 0 || bacmp(src, &bdaddr) == 0) 172 update_adapter_svclass_list(adapter); 173 } 174 175 } 176 177 void create_ext_inquiry_response(const char *name, 178 int8_t tx_power, sdp_list_t *services, 179 uint8_t *data) 180 { 181 sdp_list_t *list = services; 182 uint8_t *ptr = data; 183 uint16_t uuid[24]; 184 int i, index = 0; 185 186 if (name) { 187 int len = strlen(name); 188 189 if (len > 48) { 190 len = 48; 191 ptr[1] = 0x08; 192 } else 193 ptr[1] = 0x09; 194 195 ptr[0] = len + 1; 196 197 memcpy(ptr + 2, name, len); 198 199 ptr += len + 2; 200 } 201 202 if (tx_power != 0) { 203 *ptr++ = 2; 204 *ptr++ = 0x0a; 205 *ptr++ = (uint8_t) tx_power; 206 } 207 208 if (did_vendor != 0x0000) { 209 uint16_t source = 0x0002; 210 *ptr++ = 9; 211 *ptr++ = 0x10; 212 *ptr++ = (source & 0x00ff); 213 *ptr++ = (source & 0xff00) >> 8; 214 *ptr++ = (did_vendor & 0x00ff); 215 *ptr++ = (did_vendor & 0xff00) >> 8; 216 *ptr++ = (did_product & 0x00ff); 217 *ptr++ = (did_product & 0xff00) >> 8; 218 *ptr++ = (did_version & 0x00ff); 219 *ptr++ = (did_version & 0xff00) >> 8; 220 } 221 222 ptr[1] = 0x03; 223 224 for (; list; list = list->next) { 225 sdp_record_t *rec = (sdp_record_t *) list->data; 226 227 if (rec->svclass.type != SDP_UUID16) 228 continue; 229 230 if (rec->svclass.value.uuid16 < 0x1100) 231 continue; 232 233 if (rec->svclass.value.uuid16 == PNP_INFO_SVCLASS_ID) 234 continue; 235 236 if (index > 23) { 237 ptr[1] = 0x02; 238 break; 239 } 240 241 for (i = 0; i < index; i++) 242 if (uuid[i] == rec->svclass.value.uuid16) 243 break; 244 245 if (i == index - 1) 246 continue; 247 248 uuid[index++] = rec->svclass.value.uuid16; 249 } 250 251 if (index > 0) { 252 ptr[0] = (index * 2) + 1; 253 ptr += 2; 254 255 for (i = 0; i < index; i++) { 256 *ptr++ = (uuid[i] & 0x00ff); 257 *ptr++ = (uuid[i] & 0xff00) >> 8; 258 } 259 } 260 } 261 262 void register_public_browse_group(void) 263 { 264 sdp_list_t *browselist; 265 uuid_t bgscid, pbgid; 266 sdp_data_t *sdpdata; 267 sdp_record_t *browse = sdp_record_alloc(); 268 269 browse->handle = SDP_SERVER_RECORD_HANDLE + 1; 270 271 sdp_record_add(BDADDR_ANY, browse); 272 sdpdata = sdp_data_alloc(SDP_UINT32, &browse->handle); 273 sdp_attr_add(browse, SDP_ATTR_RECORD_HANDLE, sdpdata); 274 275 sdp_uuid16_create(&bgscid, BROWSE_GRP_DESC_SVCLASS_ID); 276 browselist = sdp_list_append(0, &bgscid); 277 sdp_set_service_classes(browse, browselist); 278 sdp_list_free(browselist, 0); 279 280 sdp_uuid16_create(&pbgid, PUBLIC_BROWSE_GROUP); 281 sdp_attr_add_new(browse, SDP_ATTR_GROUP_ID, 282 SDP_UUID16, &pbgid.value.uuid16); 283 } 284 285 /* 286 * The SDP server must present its own service record to 287 * the service repository. This can be accessed by service 288 * discovery clients. This method constructs a service record 289 * and stores it in the repository 290 */ 291 void register_server_service(void) 292 { 293 sdp_list_t *classIDList; 294 uuid_t classID; 295 void **versions, **versionDTDs; 296 uint8_t dtd; 297 sdp_data_t *pData; 298 int i; 299 300 server = sdp_record_alloc(); 301 server->pattern = NULL; 302 303 /* Force the record to be SDP_SERVER_RECORD_HANDLE */ 304 server->handle = SDP_SERVER_RECORD_HANDLE; 305 306 sdp_record_add(BDADDR_ANY, server); 307 sdp_attr_add(server, SDP_ATTR_RECORD_HANDLE, 308 sdp_data_alloc(SDP_UINT32, &server->handle)); 309 310 sdp_uuid16_create(&classID, SDP_SERVER_SVCLASS_ID); 311 classIDList = sdp_list_append(0, &classID); 312 sdp_set_service_classes(server, classIDList); 313 sdp_list_free(classIDList, 0); 314 315 /* 316 * Set the version numbers supported, these are passed as arguments 317 * to the server on command line. Now defaults to 1.0 318 * Build the version number sequence first 319 */ 320 versions = (void **)malloc(sdpServerVnumEntries * sizeof(void *)); 321 versionDTDs = (void **)malloc(sdpServerVnumEntries * sizeof(void *)); 322 dtd = SDP_UINT16; 323 for (i = 0; i < sdpServerVnumEntries; i++) { 324 uint16_t *version = malloc(sizeof(uint16_t)); 325 *version = sdpVnumArray[i].major; 326 *version = (*version << 8); 327 *version |= sdpVnumArray[i].minor; 328 versions[i] = version; 329 versionDTDs[i] = &dtd; 330 } 331 pData = sdp_seq_alloc(versionDTDs, versions, sdpServerVnumEntries); 332 for (i = 0; i < sdpServerVnumEntries; i++) 333 free(versions[i]); 334 free(versions); 335 free(versionDTDs); 336 sdp_attr_add(server, SDP_ATTR_VERSION_NUM_LIST, pData); 337 338 update_db_timestamp(); 339 update_svclass_list(BDADDR_ANY); 340 } 341 342 void register_device_id(const uint16_t vendor, const uint16_t product, 343 const uint16_t version) 344 { 345 const uint16_t spec = 0x0102, source = 0x0002; 346 const uint8_t primary = 1; 347 sdp_list_t *class_list, *group_list, *profile_list; 348 uuid_t class_uuid, group_uuid; 349 sdp_data_t *sdp_data, *primary_data, *source_data; 350 sdp_data_t *spec_data, *vendor_data, *product_data, *version_data; 351 sdp_profile_desc_t profile; 352 sdp_record_t *record = sdp_record_alloc(); 353 354 info("Adding device id record for %04x:%04x", vendor, product); 355 356 did_vendor = vendor; 357 did_product = product; 358 did_version = version; 359 360 record->handle = sdp_next_handle(); 361 362 sdp_record_add(BDADDR_ANY, record); 363 sdp_data = sdp_data_alloc(SDP_UINT32, &record->handle); 364 sdp_attr_add(record, SDP_ATTR_RECORD_HANDLE, sdp_data); 365 366 sdp_uuid16_create(&class_uuid, PNP_INFO_SVCLASS_ID); 367 class_list = sdp_list_append(0, &class_uuid); 368 sdp_set_service_classes(record, class_list); 369 sdp_list_free(class_list, NULL); 370 371 sdp_uuid16_create(&group_uuid, PUBLIC_BROWSE_GROUP); 372 group_list = sdp_list_append(NULL, &group_uuid); 373 sdp_set_browse_groups(record, group_list); 374 sdp_list_free(group_list, NULL); 375 376 sdp_uuid16_create(&profile.uuid, PNP_INFO_PROFILE_ID); 377 profile.version = spec; 378 profile_list = sdp_list_append(NULL, &profile); 379 sdp_set_profile_descs(record, profile_list); 380 sdp_list_free(profile_list, NULL); 381 382 spec_data = sdp_data_alloc(SDP_UINT16, &spec); 383 sdp_attr_add(record, 0x0200, spec_data); 384 385 vendor_data = sdp_data_alloc(SDP_UINT16, &vendor); 386 sdp_attr_add(record, 0x0201, vendor_data); 387 388 product_data = sdp_data_alloc(SDP_UINT16, &product); 389 sdp_attr_add(record, 0x0202, product_data); 390 391 version_data = sdp_data_alloc(SDP_UINT16, &version); 392 sdp_attr_add(record, 0x0203, version_data); 393 394 primary_data = sdp_data_alloc(SDP_BOOL, &primary); 395 sdp_attr_add(record, 0x0204, primary_data); 396 397 source_data = sdp_data_alloc(SDP_UINT16, &source); 398 sdp_attr_add(record, 0x0205, source_data); 399 400 update_db_timestamp(); 401 update_svclass_list(BDADDR_ANY); 402 } 403 404 int add_record_to_server(const bdaddr_t *src, sdp_record_t *rec) 405 { 406 sdp_data_t *data; 407 sdp_list_t *pattern; 408 409 if (rec->handle == 0xffffffff) { 410 rec->handle = sdp_next_handle(); 411 if (rec->handle < 0x10000) 412 return -1; 413 } else { 414 if (sdp_record_find(rec->handle)) 415 return -1; 416 } 417 418 DBG("Adding record with handle 0x%05x", rec->handle); 419 420 sdp_record_add(src, rec); 421 422 data = sdp_data_alloc(SDP_UINT32, &rec->handle); 423 sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data); 424 425 if (sdp_data_get(rec, SDP_ATTR_BROWSE_GRP_LIST) == NULL) { 426 uuid_t uuid; 427 sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); 428 sdp_pattern_add_uuid(rec, &uuid); 429 } 430 431 for (pattern = rec->pattern; pattern; pattern = pattern->next) { 432 char uuid[32]; 433 434 if (pattern->data == NULL) 435 continue; 436 437 sdp_uuid2strn((uuid_t *) pattern->data, uuid, sizeof(uuid)); 438 DBG("Record pattern UUID %s", uuid); 439 } 440 441 update_db_timestamp(); 442 update_svclass_list(src); 443 444 return 0; 445 } 446 447 int remove_record_from_server(uint32_t handle) 448 { 449 sdp_record_t *rec; 450 451 DBG("Removing record with handle 0x%05x", handle); 452 453 rec = sdp_record_find(handle); 454 if (!rec) 455 return -ENOENT; 456 457 if (sdp_record_remove(handle) == 0) { 458 update_db_timestamp(); 459 update_svclass_list(BDADDR_ANY); 460 } 461 462 sdp_record_free(rec); 463 464 return 0; 465 } 466 467 /* FIXME: refactor for server-side */ 468 static sdp_record_t *extract_pdu_server(bdaddr_t *device, uint8_t *p, 469 unsigned int bufsize, 470 uint32_t handleExpected, int *scanned) 471 { 472 int extractStatus = -1, localExtractedLength = 0; 473 uint8_t dtd; 474 int seqlen = 0; 475 sdp_record_t *rec = NULL; 476 uint16_t attrId, lookAheadAttrId; 477 sdp_data_t *pAttr = NULL; 478 uint32_t handle = 0xffffffff; 479 480 *scanned = sdp_extract_seqtype(p, bufsize, &dtd, &seqlen); 481 p += *scanned; 482 bufsize -= *scanned; 483 484 if (bufsize < sizeof(uint8_t) + sizeof(uint8_t)) { 485 SDPDBG("Unexpected end of packet"); 486 return NULL; 487 } 488 489 lookAheadAttrId = ntohs(bt_get_unaligned((uint16_t *) (p + sizeof(uint8_t)))); 490 491 SDPDBG("Look ahead attr id : %d", lookAheadAttrId); 492 493 if (lookAheadAttrId == SDP_ATTR_RECORD_HANDLE) { 494 if (bufsize < (sizeof(uint8_t) * 2) + 495 sizeof(uint16_t) + sizeof(uint32_t)) { 496 SDPDBG("Unexpected end of packet"); 497 return NULL; 498 } 499 handle = ntohl(bt_get_unaligned((uint32_t *) (p + 500 sizeof(uint8_t) + sizeof(uint16_t) + 501 sizeof(uint8_t)))); 502 SDPDBG("SvcRecHandle : 0x%x", handle); 503 rec = sdp_record_find(handle); 504 } else if (handleExpected != 0xffffffff) 505 rec = sdp_record_find(handleExpected); 506 507 if (!rec) { 508 rec = sdp_record_alloc(); 509 rec->attrlist = NULL; 510 if (lookAheadAttrId == SDP_ATTR_RECORD_HANDLE) { 511 rec->handle = handle; 512 sdp_record_add(device, rec); 513 } else if (handleExpected != 0xffffffff) { 514 rec->handle = handleExpected; 515 sdp_record_add(device, rec); 516 } 517 } else { 518 sdp_list_free(rec->attrlist, (sdp_free_func_t) sdp_data_free); 519 rec->attrlist = NULL; 520 } 521 522 while (localExtractedLength < seqlen) { 523 int attrSize = sizeof(uint8_t); 524 int attrValueLength = 0; 525 526 if (bufsize < attrSize + sizeof(uint16_t)) { 527 SDPDBG("Unexpected end of packet: Terminating extraction of attributes"); 528 break; 529 } 530 531 SDPDBG("Extract PDU, sequenceLength: %d localExtractedLength: %d", 532 seqlen, localExtractedLength); 533 dtd = *(uint8_t *) p; 534 535 attrId = ntohs(bt_get_unaligned((uint16_t *) (p + attrSize))); 536 attrSize += sizeof(uint16_t); 537 538 SDPDBG("DTD of attrId : %d Attr id : 0x%x", dtd, attrId); 539 540 pAttr = sdp_extract_attr(p + attrSize, bufsize - attrSize, 541 &attrValueLength, rec); 542 543 SDPDBG("Attr id : 0x%x attrValueLength : %d", attrId, attrValueLength); 544 545 attrSize += attrValueLength; 546 if (pAttr == NULL) { 547 SDPDBG("Terminating extraction of attributes"); 548 break; 549 } 550 localExtractedLength += attrSize; 551 p += attrSize; 552 bufsize -= attrSize; 553 sdp_attr_replace(rec, attrId, pAttr); 554 extractStatus = 0; 555 SDPDBG("Extract PDU, seqLength: %d localExtractedLength: %d", 556 seqlen, localExtractedLength); 557 } 558 559 if (extractStatus == 0) { 560 SDPDBG("Successful extracting of Svc Rec attributes"); 561 #ifdef SDP_DEBUG 562 sdp_print_service_attr(rec->attrlist); 563 #endif 564 *scanned += seqlen; 565 } 566 return rec; 567 } 568 569 /* 570 * Add the newly created service record to the service repository 571 */ 572 int service_register_req(sdp_req_t *req, sdp_buf_t *rsp) 573 { 574 int scanned = 0; 575 sdp_data_t *handle; 576 uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t); 577 int bufsize = req->len - sizeof(sdp_pdu_hdr_t); 578 sdp_record_t *rec; 579 580 req->flags = *p++; 581 if (req->flags & SDP_DEVICE_RECORD) { 582 bacpy(&req->device, (bdaddr_t *) p); 583 p += sizeof(bdaddr_t); 584 bufsize -= sizeof(bdaddr_t); 585 } 586 587 // save image of PDU: we need it when clients request this attribute 588 rec = extract_pdu_server(&req->device, p, bufsize, 0xffffffff, &scanned); 589 if (!rec) 590 goto invalid; 591 592 if (rec->handle == 0xffffffff) { 593 rec->handle = sdp_next_handle(); 594 if (rec->handle < 0x10000) { 595 sdp_record_free(rec); 596 goto invalid; 597 } 598 } else { 599 if (sdp_record_find(rec->handle)) { 600 /* extract_pdu_server will add the record handle 601 * if it is missing. So instead of failing, skip 602 * the record adding to avoid duplication. */ 603 goto success; 604 } 605 } 606 607 sdp_record_add(&req->device, rec); 608 if (!(req->flags & SDP_RECORD_PERSIST)) 609 sdp_svcdb_set_collectable(rec, req->sock); 610 611 handle = sdp_data_alloc(SDP_UINT32, &rec->handle); 612 sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, handle); 613 614 success: 615 /* if the browse group descriptor is NULL, 616 * ensure that the record belongs to the ROOT group */ 617 if (sdp_data_get(rec, SDP_ATTR_BROWSE_GRP_LIST) == NULL) { 618 uuid_t uuid; 619 sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); 620 sdp_pattern_add_uuid(rec, &uuid); 621 } 622 623 update_db_timestamp(); 624 update_svclass_list(&req->device); 625 626 /* Build a rsp buffer */ 627 bt_put_unaligned(htonl(rec->handle), (uint32_t *) rsp->data); 628 rsp->data_size = sizeof(uint32_t); 629 630 return 0; 631 632 invalid: 633 bt_put_unaligned(htons(SDP_INVALID_SYNTAX), (uint16_t *) rsp->data); 634 rsp->data_size = sizeof(uint16_t); 635 636 return -1; 637 } 638 639 /* 640 * Update a service record 641 */ 642 int service_update_req(sdp_req_t *req, sdp_buf_t *rsp) 643 { 644 sdp_record_t *orec, *nrec; 645 int status = 0, scanned = 0; 646 uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t); 647 int bufsize = req->len - sizeof(sdp_pdu_hdr_t); 648 uint32_t handle = ntohl(bt_get_unaligned((uint32_t *) p)); 649 650 SDPDBG("Svc Rec Handle: 0x%x", handle); 651 652 p += sizeof(uint32_t); 653 bufsize -= sizeof(uint32_t); 654 655 orec = sdp_record_find(handle); 656 657 SDPDBG("SvcRecOld: %p", orec); 658 659 if (!orec) { 660 status = SDP_INVALID_RECORD_HANDLE; 661 goto done; 662 } 663 664 nrec = extract_pdu_server(BDADDR_ANY, p, bufsize, handle, &scanned); 665 if (!nrec) { 666 status = SDP_INVALID_SYNTAX; 667 goto done; 668 } 669 670 assert(nrec == orec); 671 672 update_db_timestamp(); 673 update_svclass_list(BDADDR_ANY); 674 675 done: 676 p = rsp->data; 677 bt_put_unaligned(htons(status), (uint16_t *) p); 678 rsp->data_size = sizeof(uint16_t); 679 return status; 680 } 681 682 /* 683 * Remove a registered service record 684 */ 685 int service_remove_req(sdp_req_t *req, sdp_buf_t *rsp) 686 { 687 uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t); 688 uint32_t handle = ntohl(bt_get_unaligned((uint32_t *) p)); 689 sdp_record_t *rec; 690 int status = 0; 691 692 /* extract service record handle */ 693 p += sizeof(uint32_t); 694 695 rec = sdp_record_find(handle); 696 if (rec) { 697 sdp_svcdb_collect(rec); 698 status = sdp_record_remove(handle); 699 sdp_record_free(rec); 700 if (status == 0) { 701 update_db_timestamp(); 702 update_svclass_list(BDADDR_ANY); 703 } 704 } else { 705 status = SDP_INVALID_RECORD_HANDLE; 706 SDPDBG("Could not find record : 0x%x", handle); 707 } 708 709 p = rsp->data; 710 bt_put_unaligned(htons(status), (uint16_t *) p); 711 rsp->data_size = sizeof(uint16_t); 712 713 return status; 714 } 715