1 /* 2 * Copyright (C) 2014 BlueKitchen GmbH 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the copyright holders nor the names of 14 * contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 4. Any redistribution, use, or modification is done solely for 17 * personal benefit and not for any commercial purpose or for 18 * monetary gain. 19 * 20 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 24 * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * Please inquire about commercial licensing options at 34 * [email protected] 35 * 36 */ 37 38 /* 39 * sdp_client.c 40 */ 41 42 #include "btstack-config.h" 43 #include "sdp_client.h" 44 45 #include "hci_cmds.h" 46 47 #include "l2cap.h" 48 #include "sdp_parser.h" 49 #include "sdp.h" 50 #include "debug.h" 51 52 typedef enum { 53 INIT, W4_CONNECT, W2_SEND, W4_RESPONSE, QUERY_COMPLETE 54 } sdp_client_state_t; 55 56 57 void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 58 59 static uint16_t setup_service_search_attribute_request(uint8_t * data); 60 61 #ifdef HAVE_SDP_EXTRA_QUERIES 62 static uint16_t setup_service_search_request(uint8_t * data); 63 static uint16_t setup_service_attribute_request(uint8_t * data); 64 static void parse_service_search_response(uint8_t* packet); 65 static void parse_service_attribute_response(uint8_t* packet); 66 static uint32_t serviceRecordHandle; 67 #endif 68 69 // SDP Client Query 70 static uint16_t mtu; 71 static uint16_t sdp_cid = 0x40; 72 static uint8_t * serviceSearchPattern; 73 static uint8_t * attributeIDList; 74 static uint16_t transactionID = 0; 75 static uint8_t continuationState[16]; 76 static uint8_t continuationStateLen; 77 static sdp_client_state_t sdp_client_state = INIT; 78 static SDP_PDU_ID_t PDU_ID = SDP_Invalid; 79 80 // TODO: inline if not needed (des(des)) 81 static void parse_attribute_lists(uint8_t* packet, uint16_t length){ 82 sdp_parser_handle_chunk(packet, length); 83 } 84 85 /* Queries the SDP service of the remote device given a service search pattern 86 and a list of attribute IDs. The remote data is handled by the SDP parser. The 87 SDP parser delivers attribute values and done event via a registered callback. */ 88 89 void sdp_client_query(bd_addr_t remote, uint8_t * des_serviceSearchPattern, uint8_t * des_attributeIDList){ 90 serviceSearchPattern = des_serviceSearchPattern; 91 attributeIDList = des_attributeIDList; 92 continuationStateLen = 0; 93 PDU_ID = SDP_ServiceSearchAttributeResponse; 94 95 sdp_client_state = W4_CONNECT; 96 l2cap_create_channel_internal(NULL, sdp_packet_handler, remote, PSM_SDP, l2cap_max_mtu()); 97 } 98 99 static int can_send_now(uint16_t channel){ 100 if (sdp_client_state != W2_SEND) return 0; 101 if (!l2cap_can_send_packet_now(channel)) return 0; 102 return 1; 103 } 104 105 static void send_request(uint16_t channel){ 106 l2cap_reserve_packet_buffer(); 107 uint8_t * data = l2cap_get_outgoing_buffer(); 108 uint16_t request_len = 0; 109 110 switch (PDU_ID){ 111 #ifdef HAVE_SDP_EXTRA_QUERIES 112 case SDP_ServiceSearchResponse: 113 request_len = setup_service_search_request(data); 114 break; 115 case SDP_ServiceAttributeResponse: 116 request_len = setup_service_attribute_request(data); 117 break; 118 #endif 119 case SDP_ServiceSearchAttributeResponse: 120 request_len = setup_service_search_attribute_request(data); 121 break; 122 default: 123 log_error("SDP Client send_request :: PDU ID invalid. %u", PDU_ID); 124 return; 125 } 126 127 // prevent re-entrance 128 sdp_client_state = W4_RESPONSE; 129 int err = l2cap_send_prepared(channel, request_len); 130 // l2cap_send_prepared shouldn't have failed as l2ap_can_send_packet_now() was true 131 switch (err){ 132 case 0: 133 log_debug("l2cap_send_internal() -> OK"); 134 PDU_ID = SDP_Invalid; 135 break; 136 case BTSTACK_ACL_BUFFERS_FULL: 137 sdp_client_state = W2_SEND; 138 log_info("l2cap_send_internal() ->BTSTACK_ACL_BUFFERS_FULL"); 139 break; 140 default: 141 sdp_client_state = W2_SEND; 142 log_error("l2cap_send_internal() -> err %d", err); 143 break; 144 } 145 } 146 147 148 static void parse_service_search_attribute_response(uint8_t* packet){ 149 uint16_t offset = 3; 150 uint16_t parameterLength = READ_NET_16(packet,offset); 151 offset+=2; 152 // AttributeListByteCount <= mtu 153 uint16_t attributeListByteCount = READ_NET_16(packet,offset); 154 offset+=2; 155 156 if (attributeListByteCount > mtu){ 157 log_error("Error parsing ServiceSearchAttributeResponse: Number of bytes in found attribute list is larger then the MaximumAttributeByteCount."); 158 return; 159 } 160 161 // AttributeLists 162 parse_attribute_lists(packet+offset, attributeListByteCount); 163 offset+=attributeListByteCount; 164 165 continuationStateLen = packet[offset]; 166 offset++; 167 168 if (continuationStateLen > 16){ 169 log_error("Error parsing ServiceSearchAttributeResponse: Number of bytes in continuation state exceedes 16."); 170 return; 171 } 172 memcpy(continuationState, packet+offset, continuationStateLen); 173 offset+=continuationStateLen; 174 175 if (parameterLength != offset - 5){ 176 log_error("Error parsing ServiceSearchAttributeResponse: wrong size of parameters, number of expected bytes%u, actual number %u.", parameterLength, offset); 177 } 178 } 179 180 void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 181 // uint16_t handle; 182 if (packet_type == L2CAP_DATA_PACKET){ 183 uint16_t responseTransactionID = READ_NET_16(packet,1); 184 if ( responseTransactionID != transactionID){ 185 log_error("Missmatching transaction ID, expected %u, found %u.", transactionID, responseTransactionID); 186 return; 187 } 188 189 if (packet[0] != SDP_ServiceSearchAttributeResponse 190 && packet[0] != SDP_ServiceSearchResponse 191 && packet[0] != SDP_ServiceAttributeResponse){ 192 log_error("Not a valid PDU ID, expected %u, %u or %u, found %u.", SDP_ServiceSearchResponse, 193 SDP_ServiceAttributeResponse, SDP_ServiceSearchAttributeResponse, packet[0]); 194 return; 195 } 196 197 PDU_ID = (SDP_PDU_ID_t)packet[0]; 198 log_info("SDP Client :: PDU ID. %u ,%u", PDU_ID, packet[0]); 199 switch (PDU_ID){ 200 #ifdef HAVE_SDP_EXTRA_QUERIES 201 case SDP_ServiceSearchResponse: 202 parse_service_search_response(packet); 203 break; 204 case SDP_ServiceAttributeResponse: 205 parse_service_attribute_response(packet); 206 break; 207 #endif 208 case SDP_ServiceSearchAttributeResponse: 209 parse_service_search_attribute_response(packet); 210 break; 211 default: 212 log_error("SDP Client :: PDU ID invalid. %u ,%u", PDU_ID, packet[0]); 213 return; 214 } 215 216 // continuation set or DONE? 217 if (continuationStateLen == 0){ 218 log_info("SDP Client Query DONE! "); 219 sdp_client_state = QUERY_COMPLETE; 220 l2cap_disconnect_internal(sdp_cid, 0); 221 // sdp_parser_handle_done(0); 222 return; 223 } 224 // prepare next request and send 225 sdp_client_state = W2_SEND; 226 if (can_send_now(sdp_cid)) send_request(sdp_cid); 227 return; 228 } 229 230 if (packet_type != HCI_EVENT_PACKET) return; 231 232 switch(packet[0]){ 233 case L2CAP_EVENT_TIMEOUT_CHECK: 234 log_info("sdp client: L2CAP_EVENT_TIMEOUT_CHECK"); 235 break; 236 case L2CAP_EVENT_CHANNEL_OPENED: 237 if (sdp_client_state != W4_CONNECT) break; 238 // data: event (8), len(8), status (8), address(48), handle (16), psm (16), local_cid(16), remote_cid (16), local_mtu(16), remote_mtu(16) 239 if (packet[2]) { 240 log_error("SDP Client Connection failed."); 241 sdp_parser_handle_done(packet[2]); 242 break; 243 } 244 sdp_cid = channel; 245 mtu = READ_BT_16(packet, 17); 246 // handle = READ_BT_16(packet, 9); 247 log_info("SDP Client Connected, cid %x, mtu %u.", sdp_cid, mtu); 248 249 sdp_client_state = W2_SEND; 250 if (can_send_now(sdp_cid)) send_request(sdp_cid); 251 252 break; 253 case L2CAP_EVENT_CREDITS: 254 case DAEMON_EVENT_HCI_PACKET_SENT: 255 if (can_send_now(sdp_cid)) send_request(sdp_cid); 256 break; 257 case L2CAP_EVENT_CHANNEL_CLOSED: { 258 if (sdp_cid != READ_BT_16(packet, 2)) { 259 // log_info("Received L2CAP_EVENT_CHANNEL_CLOSED for cid %x, current cid %x\n", READ_BT_16(packet, 2),sdp_cid); 260 break; 261 } 262 log_info("SDP Client disconnected."); 263 uint8_t status = sdp_client_state == QUERY_COMPLETE ? 0 : SDP_QUERY_INCOMPLETE; 264 sdp_client_state = INIT; 265 sdp_parser_handle_done(status); 266 break; 267 } 268 default: 269 break; 270 } 271 } 272 273 274 static uint16_t setup_service_search_attribute_request(uint8_t * data){ 275 276 uint16_t offset = 0; 277 transactionID++; 278 // uint8_t SDP_PDU_ID_t.SDP_ServiceSearchRequest; 279 data[offset++] = SDP_ServiceSearchAttributeRequest; 280 // uint16_t transactionID 281 net_store_16(data, offset, transactionID); 282 offset += 2; 283 284 // param legnth 285 offset += 2; 286 287 // parameters: 288 // ServiceSearchPattern - DES (min 1 UUID, max 12) 289 uint16_t serviceSearchPatternLen = de_get_len(serviceSearchPattern); 290 memcpy(data + offset, serviceSearchPattern, serviceSearchPatternLen); 291 offset += serviceSearchPatternLen; 292 293 // MaximumAttributeByteCount - uint16_t 0x0007 - 0xffff -> mtu 294 net_store_16(data, offset, mtu); 295 offset += 2; 296 297 // AttibuteIDList 298 uint16_t attributeIDListLen = de_get_len(attributeIDList); 299 memcpy(data + offset, attributeIDList, attributeIDListLen); 300 offset += attributeIDListLen; 301 302 // ContinuationState - uint8_t number of cont. bytes N<=16 303 data[offset++] = continuationStateLen; 304 // - N-bytes previous response from server 305 memcpy(data + offset, continuationState, continuationStateLen); 306 offset += continuationStateLen; 307 308 // uint16_t paramLength 309 net_store_16(data, 3, offset - 5); 310 311 return offset; 312 } 313 314 #ifdef HAVE_SDP_EXTRA_QUERIES 315 void parse_service_record_handle_list(uint8_t* packet, uint16_t total_count, uint16_t current_count){ 316 sdp_parser_handle_service_search(packet, total_count, current_count); 317 } 318 319 static uint16_t setup_service_search_request(uint8_t * data){ 320 uint16_t offset = 0; 321 transactionID++; 322 // uint8_t SDP_PDU_ID_t.SDP_ServiceSearchRequest; 323 data[offset++] = SDP_ServiceSearchRequest; 324 // uint16_t transactionID 325 net_store_16(data, offset, transactionID); 326 offset += 2; 327 328 // param legnth 329 offset += 2; 330 331 // parameters: 332 // ServiceSearchPattern - DES (min 1 UUID, max 12) 333 uint16_t serviceSearchPatternLen = de_get_len(serviceSearchPattern); 334 memcpy(data + offset, serviceSearchPattern, serviceSearchPatternLen); 335 offset += serviceSearchPatternLen; 336 337 // MaximumAttributeByteCount - uint16_t 0x0007 - 0xffff -> mtu 338 net_store_16(data, offset, mtu); 339 offset += 2; 340 341 // ContinuationState - uint8_t number of cont. bytes N<=16 342 data[offset++] = continuationStateLen; 343 // - N-bytes previous response from server 344 memcpy(data + offset, continuationState, continuationStateLen); 345 offset += continuationStateLen; 346 347 // uint16_t paramLength 348 net_store_16(data, 3, offset - 5); 349 350 return offset; 351 } 352 353 354 static uint16_t setup_service_attribute_request(uint8_t * data){ 355 356 uint16_t offset = 0; 357 transactionID++; 358 // uint8_t SDP_PDU_ID_t.SDP_ServiceSearchRequest; 359 data[offset++] = SDP_ServiceAttributeRequest; 360 // uint16_t transactionID 361 net_store_16(data, offset, transactionID); 362 offset += 2; 363 364 // param legnth 365 offset += 2; 366 367 // parameters: 368 // ServiceRecordHandle 369 net_store_32(data, offset, serviceRecordHandle); 370 offset += 4; 371 372 // MaximumAttributeByteCount - uint16_t 0x0007 - 0xffff -> mtu 373 net_store_16(data, offset, mtu); 374 offset += 2; 375 376 // AttibuteIDList 377 uint16_t attributeIDListLen = de_get_len(attributeIDList); 378 memcpy(data + offset, attributeIDList, attributeIDListLen); 379 offset += attributeIDListLen; 380 381 // ContinuationState - uint8_t number of cont. bytes N<=16 382 data[offset++] = continuationStateLen; 383 // - N-bytes previous response from server 384 memcpy(data + offset, continuationState, continuationStateLen); 385 offset += continuationStateLen; 386 387 // uint16_t paramLength 388 net_store_16(data, 3, offset - 5); 389 390 return offset; 391 } 392 393 static void parse_service_search_response(uint8_t* packet){ 394 uint16_t offset = 3; 395 uint16_t parameterLength = READ_NET_16(packet,offset); 396 offset+=2; 397 398 uint16_t totalServiceRecordCount = READ_NET_16(packet,offset); 399 offset+=2; 400 401 uint16_t currentServiceRecordCount = READ_NET_16(packet,offset); 402 offset+=2; 403 if (currentServiceRecordCount > totalServiceRecordCount){ 404 log_error("CurrentServiceRecordCount is larger then TotalServiceRecordCount."); 405 return; 406 } 407 408 parse_service_record_handle_list(packet+offset, totalServiceRecordCount, currentServiceRecordCount); 409 offset+=(currentServiceRecordCount * 4); 410 411 continuationStateLen = packet[offset]; 412 offset++; 413 if (continuationStateLen > 16){ 414 log_error("Error parsing ServiceSearchResponse: Number of bytes in continuation state exceedes 16."); 415 return; 416 } 417 memcpy(continuationState, packet+offset, continuationStateLen); 418 offset+=continuationStateLen; 419 420 if (parameterLength != offset - 5){ 421 log_error("Error parsing ServiceSearchResponse: wrong size of parameters, number of expected bytes%u, actual number %u.", parameterLength, offset); 422 } 423 } 424 425 static void parse_service_attribute_response(uint8_t* packet){ 426 uint16_t offset = 3; 427 uint16_t parameterLength = READ_NET_16(packet,offset); 428 offset+=2; 429 430 // AttributeListByteCount <= mtu 431 uint16_t attributeListByteCount = READ_NET_16(packet,offset); 432 offset+=2; 433 434 if (attributeListByteCount > mtu){ 435 log_error("Error parsing ServiceSearchAttributeResponse: Number of bytes in found attribute list is larger then the MaximumAttributeByteCount."); 436 return; 437 } 438 439 // AttributeLists 440 parse_attribute_lists(packet+offset, attributeListByteCount); 441 offset+=attributeListByteCount; 442 443 continuationStateLen = packet[offset]; 444 offset++; 445 446 if (continuationStateLen > 16){ 447 log_error("Error parsing ServiceAttributeResponse: Number of bytes in continuation state exceedes 16."); 448 return; 449 } 450 memcpy(continuationState, packet+offset, continuationStateLen); 451 offset+=continuationStateLen; 452 453 if (parameterLength != offset - 5){ 454 log_error("Error parsing ServiceAttributeResponse: wrong size of parameters, number of expected bytes%u, actual number %u.", parameterLength, offset); 455 } 456 } 457 458 void sdp_client_service_attribute_search(bd_addr_t remote, uint32_t search_serviceRecordHandle, uint8_t * des_attributeIDList){ 459 serviceRecordHandle = search_serviceRecordHandle; 460 attributeIDList = des_attributeIDList; 461 continuationStateLen = 0; 462 PDU_ID = SDP_ServiceAttributeResponse; 463 464 sdp_client_state = W4_CONNECT; 465 l2cap_create_channel_internal(NULL, sdp_packet_handler, remote, PSM_SDP, l2cap_max_mtu()); 466 } 467 468 void sdp_client_service_search(bd_addr_t remote, uint8_t * des_serviceSearchPattern){ 469 serviceSearchPattern = des_serviceSearchPattern; 470 continuationStateLen = 0; 471 PDU_ID = SDP_ServiceSearchResponse; 472 473 sdp_client_state = W4_CONNECT; 474 l2cap_create_channel_internal(NULL, sdp_packet_handler, remote, PSM_SDP, l2cap_max_mtu()); 475 } 476 #endif 477 478