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 "adapter.h" 50 #include "manager.h" 51 52 static sdp_record_t *server = NULL; 53 54 /* 55 * List of version numbers supported by the SDP server. 56 * Add to this list when newer versions are supported. 57 */ 58 static sdp_version_t sdpVnumArray[1] = { 59 { 1, 0 } 60 }; 61 static const int sdpServerVnumEntries = 1; 62 63 /* 64 * A simple function which returns the time of day in 65 * seconds. Used for updating the service db state 66 * attribute of the service record of the SDP server 67 */ 68 uint32_t sdp_get_time(void) 69 { 70 /* 71 * To handle failure in gettimeofday, so an old 72 * value is returned and service does not fail 73 */ 74 static struct timeval tm; 75 76 gettimeofday(&tm, NULL); 77 return (uint32_t) tm.tv_sec; 78 } 79 80 /* 81 * The service database state is an attribute of the service record 82 * of the SDP server itself. This attribute is guaranteed to 83 * change if any of the contents of the service repository 84 * changes. This function updates the timestamp of value of 85 * the svcDBState attribute 86 * Set the SDP server DB. Simply a timestamp which is the marker 87 * when the DB was modified. 88 */ 89 static void update_db_timestamp(void) 90 { 91 uint32_t dbts = sdp_get_time(); 92 sdp_data_t *d = sdp_data_alloc(SDP_UINT32, &dbts); 93 sdp_attr_replace(server, SDP_ATTR_SVCDB_STATE, d); 94 } 95 96 void register_public_browse_group(void) 97 { 98 sdp_list_t *browselist; 99 uuid_t bgscid, pbgid; 100 sdp_data_t *sdpdata; 101 sdp_record_t *browse = sdp_record_alloc(); 102 103 browse->handle = SDP_SERVER_RECORD_HANDLE + 1; 104 105 sdp_record_add(BDADDR_ANY, browse); 106 sdpdata = sdp_data_alloc(SDP_UINT32, &browse->handle); 107 sdp_attr_add(browse, SDP_ATTR_RECORD_HANDLE, sdpdata); 108 109 sdp_uuid16_create(&bgscid, BROWSE_GRP_DESC_SVCLASS_ID); 110 browselist = sdp_list_append(0, &bgscid); 111 sdp_set_service_classes(browse, browselist); 112 sdp_list_free(browselist, 0); 113 114 sdp_uuid16_create(&pbgid, PUBLIC_BROWSE_GROUP); 115 sdp_attr_add_new(browse, SDP_ATTR_GROUP_ID, 116 SDP_UUID16, &pbgid.value.uuid16); 117 } 118 119 /* 120 * The SDP server must present its own service record to 121 * the service repository. This can be accessed by service 122 * discovery clients. This method constructs a service record 123 * and stores it in the repository 124 */ 125 void register_server_service(void) 126 { 127 sdp_list_t *classIDList; 128 uuid_t classID; 129 void **versions, **versionDTDs; 130 uint8_t dtd; 131 sdp_data_t *pData; 132 int i; 133 134 server = sdp_record_alloc(); 135 server->pattern = NULL; 136 137 /* Force the record to be SDP_SERVER_RECORD_HANDLE */ 138 server->handle = SDP_SERVER_RECORD_HANDLE; 139 140 sdp_record_add(BDADDR_ANY, server); 141 sdp_attr_add(server, SDP_ATTR_RECORD_HANDLE, 142 sdp_data_alloc(SDP_UINT32, &server->handle)); 143 144 sdp_uuid16_create(&classID, SDP_SERVER_SVCLASS_ID); 145 classIDList = sdp_list_append(0, &classID); 146 sdp_set_service_classes(server, classIDList); 147 sdp_list_free(classIDList, 0); 148 149 /* 150 * Set the version numbers supported, these are passed as arguments 151 * to the server on command line. Now defaults to 1.0 152 * Build the version number sequence first 153 */ 154 versions = malloc(sdpServerVnumEntries * sizeof(void *)); 155 versionDTDs = malloc(sdpServerVnumEntries * sizeof(void *)); 156 dtd = SDP_UINT16; 157 for (i = 0; i < sdpServerVnumEntries; i++) { 158 uint16_t *version = malloc(sizeof(uint16_t)); 159 *version = sdpVnumArray[i].major; 160 *version = (*version << 8); 161 *version |= sdpVnumArray[i].minor; 162 versions[i] = version; 163 versionDTDs[i] = &dtd; 164 } 165 pData = sdp_seq_alloc(versionDTDs, versions, sdpServerVnumEntries); 166 for (i = 0; i < sdpServerVnumEntries; i++) 167 free(versions[i]); 168 free(versions); 169 free(versionDTDs); 170 sdp_attr_add(server, SDP_ATTR_VERSION_NUM_LIST, pData); 171 172 update_db_timestamp(); 173 } 174 175 void register_device_id(const uint16_t vendor, const uint16_t product, 176 const uint16_t version) 177 { 178 const uint16_t spec = 0x0102, source = 0x0002; 179 const uint8_t primary = 1; 180 sdp_list_t *class_list, *group_list, *profile_list; 181 uuid_t class_uuid, group_uuid; 182 sdp_data_t *sdp_data, *primary_data, *source_data; 183 sdp_data_t *spec_data, *vendor_data, *product_data, *version_data; 184 sdp_profile_desc_t profile; 185 sdp_record_t *record = sdp_record_alloc(); 186 187 info("Adding device id record for %04x:%04x", vendor, product); 188 189 btd_manager_set_did(vendor, product, version); 190 191 record->handle = sdp_next_handle(); 192 193 sdp_record_add(BDADDR_ANY, record); 194 sdp_data = sdp_data_alloc(SDP_UINT32, &record->handle); 195 sdp_attr_add(record, SDP_ATTR_RECORD_HANDLE, sdp_data); 196 197 sdp_uuid16_create(&class_uuid, PNP_INFO_SVCLASS_ID); 198 class_list = sdp_list_append(0, &class_uuid); 199 sdp_set_service_classes(record, class_list); 200 sdp_list_free(class_list, NULL); 201 202 sdp_uuid16_create(&group_uuid, PUBLIC_BROWSE_GROUP); 203 group_list = sdp_list_append(NULL, &group_uuid); 204 sdp_set_browse_groups(record, group_list); 205 sdp_list_free(group_list, NULL); 206 207 sdp_uuid16_create(&profile.uuid, PNP_INFO_PROFILE_ID); 208 profile.version = spec; 209 profile_list = sdp_list_append(NULL, &profile); 210 sdp_set_profile_descs(record, profile_list); 211 sdp_list_free(profile_list, NULL); 212 213 spec_data = sdp_data_alloc(SDP_UINT16, &spec); 214 sdp_attr_add(record, 0x0200, spec_data); 215 216 vendor_data = sdp_data_alloc(SDP_UINT16, &vendor); 217 sdp_attr_add(record, 0x0201, vendor_data); 218 219 product_data = sdp_data_alloc(SDP_UINT16, &product); 220 sdp_attr_add(record, 0x0202, product_data); 221 222 version_data = sdp_data_alloc(SDP_UINT16, &version); 223 sdp_attr_add(record, 0x0203, version_data); 224 225 primary_data = sdp_data_alloc(SDP_BOOL, &primary); 226 sdp_attr_add(record, 0x0204, primary_data); 227 228 source_data = sdp_data_alloc(SDP_UINT16, &source); 229 sdp_attr_add(record, 0x0205, source_data); 230 231 update_db_timestamp(); 232 } 233 234 int add_record_to_server(const bdaddr_t *src, sdp_record_t *rec) 235 { 236 sdp_data_t *data; 237 sdp_list_t *pattern; 238 239 if (rec->handle == 0xffffffff) { 240 rec->handle = sdp_next_handle(); 241 if (rec->handle < 0x10000) 242 return -ENOSPC; 243 } else { 244 if (sdp_record_find(rec->handle)) 245 return -EEXIST; 246 } 247 248 DBG("Adding record with handle 0x%05x", rec->handle); 249 250 sdp_record_add(src, rec); 251 252 data = sdp_data_alloc(SDP_UINT32, &rec->handle); 253 sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data); 254 255 if (sdp_data_get(rec, SDP_ATTR_BROWSE_GRP_LIST) == NULL) { 256 uuid_t uuid; 257 sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); 258 sdp_pattern_add_uuid(rec, &uuid); 259 } 260 261 for (pattern = rec->pattern; pattern; pattern = pattern->next) { 262 char uuid[32]; 263 264 if (pattern->data == NULL) 265 continue; 266 267 sdp_uuid2strn((uuid_t *) pattern->data, uuid, sizeof(uuid)); 268 DBG("Record pattern UUID %s", uuid); 269 } 270 271 update_db_timestamp(); 272 273 return 0; 274 } 275 276 int remove_record_from_server(uint32_t handle) 277 { 278 sdp_record_t *rec; 279 280 DBG("Removing record with handle 0x%05x", handle); 281 282 rec = sdp_record_find(handle); 283 if (!rec) 284 return -ENOENT; 285 286 if (sdp_record_remove(handle) == 0) 287 update_db_timestamp(); 288 289 sdp_record_free(rec); 290 291 return 0; 292 } 293 294 /* FIXME: refactor for server-side */ 295 static sdp_record_t *extract_pdu_server(bdaddr_t *device, uint8_t *p, 296 unsigned int bufsize, 297 uint32_t handleExpected, int *scanned) 298 { 299 int extractStatus = -1, localExtractedLength = 0; 300 uint8_t dtd; 301 int seqlen = 0; 302 sdp_record_t *rec = NULL; 303 uint16_t attrId, lookAheadAttrId; 304 sdp_data_t *pAttr = NULL; 305 uint32_t handle = 0xffffffff; 306 307 *scanned = sdp_extract_seqtype(p, bufsize, &dtd, &seqlen); 308 p += *scanned; 309 bufsize -= *scanned; 310 311 if (bufsize < sizeof(uint8_t) + sizeof(uint8_t)) { 312 SDPDBG("Unexpected end of packet"); 313 return NULL; 314 } 315 316 lookAheadAttrId = ntohs(bt_get_unaligned((uint16_t *) (p + sizeof(uint8_t)))); 317 318 SDPDBG("Look ahead attr id : %d", lookAheadAttrId); 319 320 if (lookAheadAttrId == SDP_ATTR_RECORD_HANDLE) { 321 if (bufsize < (sizeof(uint8_t) * 2) + 322 sizeof(uint16_t) + sizeof(uint32_t)) { 323 SDPDBG("Unexpected end of packet"); 324 return NULL; 325 } 326 handle = ntohl(bt_get_unaligned((uint32_t *) (p + 327 sizeof(uint8_t) + sizeof(uint16_t) + 328 sizeof(uint8_t)))); 329 SDPDBG("SvcRecHandle : 0x%x", handle); 330 rec = sdp_record_find(handle); 331 } else if (handleExpected != 0xffffffff) 332 rec = sdp_record_find(handleExpected); 333 334 if (!rec) { 335 rec = sdp_record_alloc(); 336 rec->attrlist = NULL; 337 if (lookAheadAttrId == SDP_ATTR_RECORD_HANDLE) { 338 rec->handle = handle; 339 sdp_record_add(device, rec); 340 } else if (handleExpected != 0xffffffff) { 341 rec->handle = handleExpected; 342 sdp_record_add(device, rec); 343 } 344 } else { 345 sdp_list_free(rec->attrlist, (sdp_free_func_t) sdp_data_free); 346 rec->attrlist = NULL; 347 } 348 349 while (localExtractedLength < seqlen) { 350 int attrSize = sizeof(uint8_t); 351 int attrValueLength = 0; 352 353 if (bufsize < attrSize + sizeof(uint16_t)) { 354 SDPDBG("Unexpected end of packet: Terminating extraction of attributes"); 355 break; 356 } 357 358 SDPDBG("Extract PDU, sequenceLength: %d localExtractedLength: %d", 359 seqlen, localExtractedLength); 360 dtd = *(uint8_t *) p; 361 362 attrId = ntohs(bt_get_unaligned((uint16_t *) (p + attrSize))); 363 attrSize += sizeof(uint16_t); 364 365 SDPDBG("DTD of attrId : %d Attr id : 0x%x", dtd, attrId); 366 367 pAttr = sdp_extract_attr(p + attrSize, bufsize - attrSize, 368 &attrValueLength, rec); 369 370 SDPDBG("Attr id : 0x%x attrValueLength : %d", attrId, attrValueLength); 371 372 attrSize += attrValueLength; 373 if (pAttr == NULL) { 374 SDPDBG("Terminating extraction of attributes"); 375 break; 376 } 377 localExtractedLength += attrSize; 378 p += attrSize; 379 bufsize -= attrSize; 380 sdp_attr_replace(rec, attrId, pAttr); 381 extractStatus = 0; 382 SDPDBG("Extract PDU, seqLength: %d localExtractedLength: %d", 383 seqlen, localExtractedLength); 384 } 385 386 if (extractStatus == 0) { 387 SDPDBG("Successful extracting of Svc Rec attributes"); 388 #ifdef SDP_DEBUG 389 sdp_print_service_attr(rec->attrlist); 390 #endif 391 *scanned += seqlen; 392 } 393 return rec; 394 } 395 396 /* 397 * Add the newly created service record to the service repository 398 */ 399 int service_register_req(sdp_req_t *req, sdp_buf_t *rsp) 400 { 401 int scanned = 0; 402 sdp_data_t *handle; 403 uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t); 404 int bufsize = req->len - sizeof(sdp_pdu_hdr_t); 405 sdp_record_t *rec; 406 407 req->flags = *p++; 408 if (req->flags & SDP_DEVICE_RECORD) { 409 bacpy(&req->device, (bdaddr_t *) p); 410 p += sizeof(bdaddr_t); 411 bufsize -= sizeof(bdaddr_t); 412 } 413 414 /* save image of PDU: we need it when clients request this attribute */ 415 rec = extract_pdu_server(&req->device, p, bufsize, 0xffffffff, &scanned); 416 if (!rec) 417 goto invalid; 418 419 if (rec->handle == 0xffffffff) { 420 rec->handle = sdp_next_handle(); 421 if (rec->handle < 0x10000) { 422 sdp_record_free(rec); 423 goto invalid; 424 } 425 } else { 426 if (sdp_record_find(rec->handle)) { 427 /* extract_pdu_server will add the record handle 428 * if it is missing. So instead of failing, skip 429 * the record adding to avoid duplication. */ 430 goto success; 431 } 432 } 433 434 sdp_record_add(&req->device, rec); 435 if (!(req->flags & SDP_RECORD_PERSIST)) 436 sdp_svcdb_set_collectable(rec, req->sock); 437 438 handle = sdp_data_alloc(SDP_UINT32, &rec->handle); 439 sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, handle); 440 441 success: 442 /* if the browse group descriptor is NULL, 443 * ensure that the record belongs to the ROOT group */ 444 if (sdp_data_get(rec, SDP_ATTR_BROWSE_GRP_LIST) == NULL) { 445 uuid_t uuid; 446 sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); 447 sdp_pattern_add_uuid(rec, &uuid); 448 } 449 450 update_db_timestamp(); 451 452 /* Build a rsp buffer */ 453 bt_put_unaligned(htonl(rec->handle), (uint32_t *) rsp->data); 454 rsp->data_size = sizeof(uint32_t); 455 456 return 0; 457 458 invalid: 459 bt_put_unaligned(htons(SDP_INVALID_SYNTAX), (uint16_t *) rsp->data); 460 rsp->data_size = sizeof(uint16_t); 461 462 return -1; 463 } 464 465 /* 466 * Update a service record 467 */ 468 int service_update_req(sdp_req_t *req, sdp_buf_t *rsp) 469 { 470 sdp_record_t *orec, *nrec; 471 int status = 0, scanned = 0; 472 uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t); 473 int bufsize = req->len - sizeof(sdp_pdu_hdr_t); 474 uint32_t handle = ntohl(bt_get_unaligned((uint32_t *) p)); 475 476 SDPDBG("Svc Rec Handle: 0x%x", handle); 477 478 p += sizeof(uint32_t); 479 bufsize -= sizeof(uint32_t); 480 481 orec = sdp_record_find(handle); 482 483 SDPDBG("SvcRecOld: %p", orec); 484 485 if (!orec) { 486 status = SDP_INVALID_RECORD_HANDLE; 487 goto done; 488 } 489 490 nrec = extract_pdu_server(BDADDR_ANY, p, bufsize, handle, &scanned); 491 if (!nrec) { 492 status = SDP_INVALID_SYNTAX; 493 goto done; 494 } 495 496 assert(nrec == orec); 497 498 update_db_timestamp(); 499 500 done: 501 p = rsp->data; 502 bt_put_unaligned(htons(status), (uint16_t *) p); 503 rsp->data_size = sizeof(uint16_t); 504 return status; 505 } 506 507 /* 508 * Remove a registered service record 509 */ 510 int service_remove_req(sdp_req_t *req, sdp_buf_t *rsp) 511 { 512 uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t); 513 uint32_t handle = ntohl(bt_get_unaligned((uint32_t *) p)); 514 sdp_record_t *rec; 515 int status = 0; 516 517 /* extract service record handle */ 518 519 rec = sdp_record_find(handle); 520 if (rec) { 521 sdp_svcdb_collect(rec); 522 status = sdp_record_remove(handle); 523 sdp_record_free(rec); 524 if (status == 0) 525 update_db_timestamp(); 526 } else { 527 status = SDP_INVALID_RECORD_HANDLE; 528 SDPDBG("Could not find record : 0x%x", handle); 529 } 530 531 p = rsp->data; 532 bt_put_unaligned(htons(status), (uint16_t *) p); 533 rsp->data_size = sizeof(uint16_t); 534 535 return status; 536 } 537