Home | History | Annotate | Download | only in src
      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