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