1 /****************************************************************************** 2 * 3 * Copyright (C) 1999-2012 Broadcom Corporation 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at: 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 ******************************************************************************/ 18 19 /****************************************************************************** 20 * 21 * This file contains functions that handle the SDP server functions. 22 * This is mainly dealing with client requests 23 * 24 ******************************************************************************/ 25 26 #include <stdlib.h> 27 #include <string.h> 28 #include <stdio.h> 29 30 #include "bt_common.h" 31 #include "bt_types.h" 32 #include "bt_utils.h" 33 #include "btu.h" 34 35 #include "l2cdefs.h" 36 #include "hcidefs.h" 37 #include "hcimsgs.h" 38 39 #include "sdp_api.h" 40 #include "sdpint.h" 41 42 43 #if SDP_SERVER_ENABLED == TRUE 44 45 extern fixed_queue_t *btu_general_alarm_queue; 46 47 /* Maximum number of bytes to reserve out of SDP MTU for response data */ 48 #define SDP_MAX_SERVICE_RSPHDR_LEN 12 49 #define SDP_MAX_SERVATTR_RSPHDR_LEN 10 50 #define SDP_MAX_ATTR_RSPHDR_LEN 10 51 52 /********************************************************************************/ 53 /* L O C A L F U N C T I O N P R O T O T Y P E S */ 54 /********************************************************************************/ 55 static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num, 56 UINT16 param_len, UINT8 *p_req, 57 UINT8 *p_req_end); 58 59 static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, 60 UINT16 param_len, UINT8 *p_req, 61 UINT8 *p_req_end); 62 63 static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, 64 UINT16 param_len, UINT8 *p_req, 65 UINT8 *p_req_end); 66 67 68 /********************************************************************************/ 69 /* E R R O R T E X T S T R I N G S */ 70 /* */ 71 /* The default is to have no text string, but we allow the strings to be */ 72 /* configured in target.h if people want them. */ 73 /********************************************************************************/ 74 #ifndef SDP_TEXT_BAD_HEADER 75 #define SDP_TEXT_BAD_HEADER NULL 76 #endif 77 78 #ifndef SDP_TEXT_BAD_PDU 79 #define SDP_TEXT_BAD_PDU NULL 80 #endif 81 82 #ifndef SDP_TEXT_BAD_UUID_LIST 83 #define SDP_TEXT_BAD_UUID_LIST NULL 84 #endif 85 86 #ifndef SDP_TEXT_BAD_HANDLE 87 #define SDP_TEXT_BAD_HANDLE NULL 88 #endif 89 90 #ifndef SDP_TEXT_BAD_ATTR_LIST 91 #define SDP_TEXT_BAD_ATTR_LIST NULL 92 #endif 93 94 #ifndef SDP_TEXT_BAD_CONT_LEN 95 #define SDP_TEXT_BAD_CONT_LEN NULL 96 #endif 97 98 #ifndef SDP_TEXT_BAD_CONT_INX 99 #define SDP_TEXT_BAD_CONT_INX NULL 100 #endif 101 102 #ifndef SDP_TEXT_BAD_MAX_RECORDS_LIST 103 #define SDP_TEXT_BAD_MAX_RECORDS_LIST NULL 104 #endif 105 106 /******************************************************************************* 107 ** 108 ** Function sdp_server_handle_client_req 109 ** 110 ** Description This is the main dispatcher of the SDP server. It is called 111 ** when any data is received from L2CAP, and dispatches the 112 ** request to the appropriate handler. 113 ** 114 ** Returns void 115 ** 116 *******************************************************************************/ 117 void sdp_server_handle_client_req (tCONN_CB *p_ccb, BT_HDR *p_msg) 118 { 119 UINT8 *p_req = (UINT8 *) (p_msg + 1) + p_msg->offset; 120 UINT8 *p_req_end = p_req + p_msg->len; 121 UINT8 pdu_id; 122 UINT16 trans_num, param_len; 123 124 125 /* Start inactivity timer */ 126 alarm_set_on_queue(p_ccb->sdp_conn_timer, SDP_INACT_TIMEOUT_MS, 127 sdp_conn_timer_timeout, p_ccb, btu_general_alarm_queue); 128 129 /* The first byte in the message is the pdu type */ 130 pdu_id = *p_req++; 131 132 /* Extract the transaction number and parameter length */ 133 BE_STREAM_TO_UINT16 (trans_num, p_req); 134 BE_STREAM_TO_UINT16 (param_len, p_req); 135 136 if ((p_req + param_len) != p_req_end) 137 { 138 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_PDU_SIZE, SDP_TEXT_BAD_HEADER); 139 return; 140 } 141 142 switch (pdu_id) 143 { 144 case SDP_PDU_SERVICE_SEARCH_REQ: 145 process_service_search (p_ccb, trans_num, param_len, p_req, p_req_end); 146 break; 147 148 case SDP_PDU_SERVICE_ATTR_REQ: 149 process_service_attr_req (p_ccb, trans_num, param_len, p_req, p_req_end); 150 break; 151 152 case SDP_PDU_SERVICE_SEARCH_ATTR_REQ: 153 process_service_search_attr_req (p_ccb, trans_num, param_len, p_req, p_req_end); 154 break; 155 156 default: 157 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_PDU); 158 SDP_TRACE_WARNING ("SDP - server got unknown PDU: 0x%x", pdu_id); 159 break; 160 } 161 } 162 163 164 165 /******************************************************************************* 166 ** 167 ** Function process_service_search 168 ** 169 ** Description This function handles a service search request from the 170 ** client. It builds a reply message with info from the database, 171 ** and sends the reply back to the client. 172 ** 173 ** Returns void 174 ** 175 *******************************************************************************/ 176 static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num, 177 UINT16 param_len, UINT8 *p_req, 178 UINT8 *p_req_end) 179 { 180 UINT16 max_replies, cur_handles, rem_handles, cont_offset; 181 tSDP_UUID_SEQ uid_seq; 182 UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len; 183 UINT16 rsp_param_len, num_rsp_handles, xx; 184 UINT32 rsp_handles[SDP_MAX_RECORDS] = {0}; 185 tSDP_RECORD *p_rec = NULL; 186 BOOLEAN is_cont = FALSE; 187 UNUSED(p_req_end); 188 189 p_req = sdpu_extract_uid_seq (p_req, param_len, &uid_seq); 190 191 if ((!p_req) || (!uid_seq.num_uids)) 192 { 193 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_UUID_LIST); 194 return; 195 } 196 197 /* Get the max replies we can send. Cap it at our max anyways. */ 198 BE_STREAM_TO_UINT16 (max_replies, p_req); 199 200 if (max_replies > SDP_MAX_RECORDS) 201 max_replies = SDP_MAX_RECORDS; 202 203 204 if ((!p_req) || (p_req > p_req_end)) 205 { 206 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_MAX_RECORDS_LIST); 207 return; 208 } 209 210 211 /* Get a list of handles that match the UUIDs given to us */ 212 for (num_rsp_handles = 0; num_rsp_handles < max_replies; ) 213 { 214 p_rec = sdp_db_service_search (p_rec, &uid_seq); 215 216 if (p_rec) 217 rsp_handles[num_rsp_handles++] = p_rec->record_handle; 218 else 219 break; 220 } 221 222 /* Check if this is a continuation request */ 223 if (*p_req) 224 { 225 if (*p_req++ != SDP_CONTINUATION_LEN || (p_req >= p_req_end)) 226 { 227 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, 228 SDP_TEXT_BAD_CONT_LEN); 229 return; 230 } 231 BE_STREAM_TO_UINT16 (cont_offset, p_req); 232 233 if (cont_offset != p_ccb->cont_offset) 234 { 235 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, 236 SDP_TEXT_BAD_CONT_INX); 237 return; 238 } 239 240 rem_handles = num_rsp_handles - cont_offset; /* extract the remaining handles */ 241 } 242 else 243 { 244 rem_handles = num_rsp_handles; 245 cont_offset = 0; 246 p_ccb->cont_offset = 0; 247 } 248 249 /* Calculate how many handles will fit in one PDU */ 250 cur_handles = (UINT16)((p_ccb->rem_mtu_size - SDP_MAX_SERVICE_RSPHDR_LEN) / 4); 251 252 if (rem_handles <= cur_handles) 253 cur_handles = rem_handles; 254 else /* Continuation is set */ 255 { 256 p_ccb->cont_offset += cur_handles; 257 is_cont = TRUE; 258 } 259 260 /* Get a buffer to use to build the response */ 261 BT_HDR *p_buf = (BT_HDR *)osi_malloc(SDP_DATA_BUF_SIZE); 262 p_buf->offset = L2CAP_MIN_OFFSET; 263 p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; 264 265 /* Start building a rsponse */ 266 UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_SERVICE_SEARCH_RSP); 267 UINT16_TO_BE_STREAM (p_rsp, trans_num); 268 269 /* Skip the length, we need to add it at the end */ 270 p_rsp_param_len = p_rsp; 271 p_rsp += 2; 272 273 /* Put in total and current number of handles, and handles themselves */ 274 UINT16_TO_BE_STREAM (p_rsp, num_rsp_handles); 275 UINT16_TO_BE_STREAM (p_rsp, cur_handles); 276 277 /* SDP_TRACE_DEBUG("SDP Service Rsp: tothdl %d, curhdlr %d, start %d, end %d, cont %d", 278 num_rsp_handles, cur_handles, cont_offset, 279 cont_offset + cur_handles-1, is_cont); */ 280 for (xx = cont_offset; xx < cont_offset + cur_handles; xx++) 281 UINT32_TO_BE_STREAM (p_rsp, rsp_handles[xx]); 282 283 if (is_cont) 284 { 285 UINT8_TO_BE_STREAM (p_rsp, SDP_CONTINUATION_LEN); 286 UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset); 287 } 288 else 289 UINT8_TO_BE_STREAM (p_rsp, 0); 290 291 /* Go back and put the parameter length into the buffer */ 292 rsp_param_len = p_rsp - p_rsp_param_len - 2; 293 UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len); 294 295 /* Set the length of the SDP data in the buffer */ 296 p_buf->len = p_rsp - p_rsp_start; 297 298 299 /* Send the buffer through L2CAP */ 300 L2CA_DataWrite (p_ccb->connection_id, p_buf); 301 } 302 303 304 /******************************************************************************* 305 ** 306 ** Function process_service_attr_req 307 ** 308 ** Description This function handles an attribute request from the client. 309 ** It builds a reply message with info from the database, 310 ** and sends the reply back to the client. 311 ** 312 ** Returns void 313 ** 314 *******************************************************************************/ 315 static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, 316 UINT16 param_len, UINT8 *p_req, 317 UINT8 *p_req_end) 318 { 319 UINT16 max_list_len, len_to_send, cont_offset; 320 INT16 rem_len; 321 tSDP_ATTR_SEQ attr_seq, attr_seq_sav; 322 UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len; 323 UINT16 rsp_param_len, xx; 324 UINT32 rec_handle; 325 tSDP_RECORD *p_rec; 326 tSDP_ATTRIBUTE *p_attr; 327 BOOLEAN is_cont = FALSE; 328 UINT16 attr_len; 329 330 /* Extract the record handle */ 331 BE_STREAM_TO_UINT32 (rec_handle, p_req); 332 333 if (p_req > p_req_end) 334 { 335 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE); 336 return; 337 } 338 339 /* Get the max list length we can send. Cap it at MTU size minus overhead */ 340 BE_STREAM_TO_UINT16 (max_list_len, p_req); 341 342 if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN)) 343 max_list_len = p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN; 344 345 p_req = sdpu_extract_attr_seq (p_req, param_len, &attr_seq); 346 347 if ((!p_req) || (!attr_seq.num_attr) || (p_req > p_req_end)) 348 { 349 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_ATTR_LIST); 350 return; 351 } 352 353 memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ)) ; 354 355 /* Find a record with the record handle */ 356 p_rec = sdp_db_find_record (rec_handle); 357 if (!p_rec) 358 { 359 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE); 360 return; 361 } 362 363 /* Free and reallocate buffer */ 364 osi_free(p_ccb->rsp_list); 365 p_ccb->rsp_list = (UINT8 *)osi_malloc(max_list_len); 366 367 /* Check if this is a continuation request */ 368 if (*p_req) { 369 if (*p_req++ != SDP_CONTINUATION_LEN) { 370 sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_CONT_STATE, 371 SDP_TEXT_BAD_CONT_LEN); 372 return; 373 } 374 BE_STREAM_TO_UINT16(cont_offset, p_req); 375 376 if (cont_offset != p_ccb->cont_offset) { 377 sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_CONT_STATE, 378 SDP_TEXT_BAD_CONT_INX); 379 return; 380 } 381 is_cont = TRUE; 382 383 /* Initialise for continuation response */ 384 p_rsp = &p_ccb->rsp_list[0]; 385 attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start = 386 p_ccb->cont_info.next_attr_start_id; 387 } else { 388 p_ccb->cont_offset = 0; 389 p_rsp = &p_ccb->rsp_list[3]; /* Leave space for data elem descr */ 390 391 /* Reset continuation parameters in p_ccb */ 392 p_ccb->cont_info.prev_sdp_rec = NULL; 393 p_ccb->cont_info.next_attr_index = 0; 394 p_ccb->cont_info.attr_offset = 0; 395 } 396 397 /* Search for attributes that match the list given to us */ 398 for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++) 399 { 400 p_attr = sdp_db_find_attr_in_rec (p_rec, attr_seq.attr_entry[xx].start, attr_seq.attr_entry[xx].end); 401 402 if (p_attr) 403 { 404 /* Check if attribute fits. Assume 3-byte value type/length */ 405 rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]); 406 407 /* just in case */ 408 if (rem_len <= 0) 409 { 410 p_ccb->cont_info.next_attr_index = xx; 411 p_ccb->cont_info.next_attr_start_id = p_attr->id; 412 break; 413 } 414 415 attr_len = sdpu_get_attrib_entry_len(p_attr); 416 /* if there is a partial attribute pending to be sent */ 417 if (p_ccb->cont_info.attr_offset) 418 { 419 p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, rem_len, 420 &p_ccb->cont_info.attr_offset); 421 422 /* If the partial attrib could not been fully added yet */ 423 if (p_ccb->cont_info.attr_offset != attr_len) 424 break; 425 else /* If the partial attrib has been added in full by now */ 426 p_ccb->cont_info.attr_offset = 0; /* reset attr_offset */ 427 } 428 else if (rem_len < attr_len) /* Not enough space for attr... so add partially */ 429 { 430 if (attr_len >= SDP_MAX_ATTR_LEN) 431 { 432 SDP_TRACE_ERROR("SDP attr too big: max_list_len=%d,attr_len=%d", max_list_len, attr_len); 433 sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL); 434 return; 435 } 436 437 /* add the partial attribute if possible */ 438 p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, (UINT16)rem_len, 439 &p_ccb->cont_info.attr_offset); 440 441 p_ccb->cont_info.next_attr_index = xx; 442 p_ccb->cont_info.next_attr_start_id = p_attr->id; 443 break; 444 } 445 else /* build the whole attribute */ 446 p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr); 447 448 /* If doing a range, stick with this one till no more attributes found */ 449 if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end) 450 { 451 /* Update for next time through */ 452 attr_seq.attr_entry[xx].start = p_attr->id + 1; 453 454 xx--; 455 } 456 } 457 } 458 /* If all the attributes have been accomodated in p_rsp, 459 reset next_attr_index */ 460 if (xx == attr_seq.num_attr) 461 p_ccb->cont_info.next_attr_index = 0; 462 463 len_to_send = (UINT16) (p_rsp - &p_ccb->rsp_list[0]); 464 cont_offset = 0; 465 466 if (!is_cont) 467 { 468 p_ccb->list_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav) + 3; 469 /* Put in the sequence header (2 or 3 bytes) */ 470 if (p_ccb->list_len > 255) 471 { 472 p_ccb->rsp_list[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD); 473 p_ccb->rsp_list[1] = (UINT8) ((p_ccb->list_len - 3) >> 8); 474 p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3); 475 } 476 else 477 { 478 cont_offset = 1; 479 480 p_ccb->rsp_list[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); 481 p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3); 482 483 p_ccb->list_len--; 484 len_to_send--; 485 } 486 } 487 488 /* Get a buffer to use to build the response */ 489 BT_HDR *p_buf = (BT_HDR *)osi_malloc(SDP_DATA_BUF_SIZE); 490 p_buf->offset = L2CAP_MIN_OFFSET; 491 p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; 492 493 /* Start building a rsponse */ 494 UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_SERVICE_ATTR_RSP); 495 UINT16_TO_BE_STREAM (p_rsp, trans_num); 496 497 /* Skip the parameter length, add it when we know the length */ 498 p_rsp_param_len = p_rsp; 499 p_rsp += 2; 500 501 UINT16_TO_BE_STREAM (p_rsp, len_to_send); 502 503 memcpy (p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send); 504 p_rsp += len_to_send; 505 506 p_ccb->cont_offset += len_to_send; 507 508 /* If anything left to send, continuation needed */ 509 if (p_ccb->cont_offset < p_ccb->list_len) 510 { 511 is_cont = TRUE; 512 513 UINT8_TO_BE_STREAM (p_rsp, SDP_CONTINUATION_LEN); 514 UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset); 515 } 516 else 517 UINT8_TO_BE_STREAM (p_rsp, 0); 518 519 /* Go back and put the parameter length into the buffer */ 520 rsp_param_len = p_rsp - p_rsp_param_len - 2; 521 UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len); 522 523 /* Set the length of the SDP data in the buffer */ 524 p_buf->len = p_rsp - p_rsp_start; 525 526 527 /* Send the buffer through L2CAP */ 528 L2CA_DataWrite (p_ccb->connection_id, p_buf); 529 } 530 531 532 533 /******************************************************************************* 534 ** 535 ** Function process_service_search_attr_req 536 ** 537 ** Description This function handles a combined service search and attribute 538 ** read request from the client. It builds a reply message with 539 ** info from the database, and sends the reply back to the client. 540 ** 541 ** Returns void 542 ** 543 *******************************************************************************/ 544 static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, 545 UINT16 param_len, UINT8 *p_req, 546 UINT8 *p_req_end) 547 { 548 UINT16 max_list_len; 549 INT16 rem_len; 550 UINT16 len_to_send, cont_offset; 551 tSDP_UUID_SEQ uid_seq; 552 UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len; 553 UINT16 rsp_param_len, xx; 554 tSDP_RECORD *p_rec; 555 tSDP_ATTR_SEQ attr_seq, attr_seq_sav; 556 tSDP_ATTRIBUTE *p_attr; 557 BOOLEAN maxxed_out = FALSE, is_cont = FALSE; 558 UINT8 *p_seq_start; 559 UINT16 seq_len, attr_len; 560 UNUSED(p_req_end); 561 562 /* Extract the UUID sequence to search for */ 563 p_req = sdpu_extract_uid_seq (p_req, param_len, &uid_seq); 564 565 if ((!p_req) || (!uid_seq.num_uids)) 566 { 567 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_UUID_LIST); 568 return; 569 } 570 571 /* Get the max list length we can send. Cap it at our max list length. */ 572 BE_STREAM_TO_UINT16 (max_list_len, p_req); 573 574 if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN)) 575 max_list_len = p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN; 576 577 p_req = sdpu_extract_attr_seq (p_req, param_len, &attr_seq); 578 579 if ((!p_req) || (!attr_seq.num_attr)) 580 { 581 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_ATTR_LIST); 582 return; 583 } 584 585 memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ)) ; 586 587 /* Free and reallocate buffer */ 588 osi_free(p_ccb->rsp_list); 589 p_ccb->rsp_list = (UINT8 *)osi_malloc(max_list_len); 590 591 /* Check if this is a continuation request */ 592 if (*p_req) { 593 if (*p_req++ != SDP_CONTINUATION_LEN) { 594 sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_CONT_STATE, 595 SDP_TEXT_BAD_CONT_LEN); 596 return; 597 } 598 BE_STREAM_TO_UINT16(cont_offset, p_req); 599 600 if (cont_offset != p_ccb->cont_offset) { 601 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, 602 SDP_TEXT_BAD_CONT_INX); 603 return; 604 } 605 is_cont = TRUE; 606 607 /* Initialise for continuation response */ 608 p_rsp = &p_ccb->rsp_list[0]; 609 attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start = 610 p_ccb->cont_info.next_attr_start_id; 611 } else { 612 p_ccb->cont_offset = 0; 613 p_rsp = &p_ccb->rsp_list[3]; /* Leave space for data elem descr */ 614 615 /* Reset continuation parameters in p_ccb */ 616 p_ccb->cont_info.prev_sdp_rec = NULL; 617 p_ccb->cont_info.next_attr_index = 0; 618 p_ccb->cont_info.last_attr_seq_desc_sent = FALSE; 619 p_ccb->cont_info.attr_offset = 0; 620 } 621 622 /* Get a list of handles that match the UUIDs given to us */ 623 for (p_rec = sdp_db_service_search (p_ccb->cont_info.prev_sdp_rec, &uid_seq); p_rec; p_rec = sdp_db_service_search (p_rec, &uid_seq)) 624 { 625 /* Allow space for attribute sequence type and length */ 626 p_seq_start = p_rsp; 627 if (p_ccb->cont_info.last_attr_seq_desc_sent == FALSE) 628 { 629 /* See if there is enough room to include a new service in the current response */ 630 rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]); 631 if (rem_len < 3) 632 { 633 /* Not enough room. Update continuation info for next response */ 634 p_ccb->cont_info.next_attr_index = 0; 635 p_ccb->cont_info.next_attr_start_id = attr_seq.attr_entry[0].start; 636 break; 637 } 638 p_rsp += 3; 639 } 640 641 /* Get a list of handles that match the UUIDs given to us */ 642 for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++) 643 { 644 p_attr = sdp_db_find_attr_in_rec (p_rec, attr_seq.attr_entry[xx].start, attr_seq.attr_entry[xx].end); 645 646 if (p_attr) 647 { 648 /* Check if attribute fits. Assume 3-byte value type/length */ 649 rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]); 650 651 /* just in case */ 652 if (rem_len <= 0) 653 { 654 p_ccb->cont_info.next_attr_index = xx; 655 p_ccb->cont_info.next_attr_start_id = p_attr->id; 656 maxxed_out = TRUE; 657 break; 658 } 659 660 attr_len = sdpu_get_attrib_entry_len(p_attr); 661 /* if there is a partial attribute pending to be sent */ 662 if (p_ccb->cont_info.attr_offset) 663 { 664 p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, rem_len, 665 &p_ccb->cont_info.attr_offset); 666 667 /* If the partial attrib could not been fully added yet */ 668 if (p_ccb->cont_info.attr_offset != attr_len) 669 { 670 maxxed_out = TRUE; 671 break; 672 } 673 else /* If the partial attrib has been added in full by now */ 674 p_ccb->cont_info.attr_offset = 0; /* reset attr_offset */ 675 } 676 else if (rem_len < attr_len) /* Not enough space for attr... so add partially */ 677 { 678 if (attr_len >= SDP_MAX_ATTR_LEN) 679 { 680 SDP_TRACE_ERROR("SDP attr too big: max_list_len=%d,attr_len=%d", max_list_len, attr_len); 681 sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL); 682 return; 683 } 684 685 /* add the partial attribute if possible */ 686 p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, (UINT16)rem_len, 687 &p_ccb->cont_info.attr_offset); 688 689 p_ccb->cont_info.next_attr_index = xx; 690 p_ccb->cont_info.next_attr_start_id = p_attr->id; 691 maxxed_out = TRUE; 692 break; 693 } 694 else /* build the whole attribute */ 695 p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr); 696 697 /* If doing a range, stick with this one till no more attributes found */ 698 if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end) 699 { 700 /* Update for next time through */ 701 attr_seq.attr_entry[xx].start = p_attr->id + 1; 702 703 xx--; 704 } 705 } 706 } 707 708 /* Go back and put the type and length into the buffer */ 709 if (p_ccb->cont_info.last_attr_seq_desc_sent == FALSE) 710 { 711 seq_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav); 712 if (seq_len != 0) 713 { 714 UINT8_TO_BE_STREAM (p_seq_start, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD); 715 UINT16_TO_BE_STREAM (p_seq_start, seq_len); 716 717 if (maxxed_out) 718 p_ccb->cont_info.last_attr_seq_desc_sent = TRUE; 719 } 720 else 721 p_rsp = p_seq_start; 722 } 723 724 if (maxxed_out) 725 break; 726 727 /* Restore the attr_seq to look for in the next sdp record */ 728 memcpy(&attr_seq, &attr_seq_sav, sizeof(tSDP_ATTR_SEQ)) ; 729 730 /* Reset the next attr index */ 731 p_ccb->cont_info.next_attr_index = 0; 732 p_ccb->cont_info.prev_sdp_rec = p_rec; 733 p_ccb->cont_info.last_attr_seq_desc_sent = FALSE; 734 } 735 736 /* response length */ 737 len_to_send = (UINT16) (p_rsp - &p_ccb->rsp_list[0]); 738 cont_offset = 0; 739 740 // The current SDP server design has a critical flaw where it can run into an infinite 741 // request/response loop with the client. Here's the scenario: 742 // - client makes SDP request 743 // - server returns the first fragment of the response with a continuation token 744 // - an SDP record is deleted from the server 745 // - client issues another request with previous continuation token 746 // - server has nothing to send back because the record is unavailable but in the 747 // first fragment, it had specified more response bytes than are now available 748 // - server sends back no additional response bytes and returns the same continuation token 749 // - client issues another request with the continuation token, and the process repeats 750 // 751 // We work around this design flaw here by checking if we will make forward progress 752 // (i.e. we will send > 0 response bytes) on a continued request. If not, we must have 753 // run into the above situation and we tell the peer an error occurred. 754 // 755 // TODO(sharvil): rewrite SDP server. 756 if (is_cont && len_to_send == 0) { 757 sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_CONT_STATE, NULL); 758 return; 759 } 760 761 /* If first response, insert sequence header */ 762 if (!is_cont) 763 { 764 /* Get the total list length for requested uid and attribute sequence */ 765 p_ccb->list_len = sdpu_get_list_len(&uid_seq, &attr_seq_sav) + 3; 766 /* Put in the sequence header (2 or 3 bytes) */ 767 if (p_ccb->list_len > 255) 768 { 769 p_ccb->rsp_list[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD); 770 p_ccb->rsp_list[1] = (UINT8) ((p_ccb->list_len - 3) >> 8); 771 p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3); 772 } 773 else 774 { 775 cont_offset = 1; 776 777 p_ccb->rsp_list[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); 778 p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3); 779 780 p_ccb->list_len--; 781 len_to_send--; 782 } 783 } 784 785 /* Get a buffer to use to build the response */ 786 BT_HDR *p_buf = (BT_HDR *)osi_malloc(SDP_DATA_BUF_SIZE); 787 p_buf->offset = L2CAP_MIN_OFFSET; 788 p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; 789 790 /* Start building a rsponse */ 791 UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_SERVICE_SEARCH_ATTR_RSP); 792 UINT16_TO_BE_STREAM (p_rsp, trans_num); 793 794 /* Skip the parameter length, add it when we know the length */ 795 p_rsp_param_len = p_rsp; 796 p_rsp += 2; 797 798 /* Stream the list length to send */ 799 UINT16_TO_BE_STREAM (p_rsp, len_to_send); 800 801 /* copy from rsp_list to the actual buffer to be sent */ 802 memcpy (p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send); 803 p_rsp += len_to_send; 804 805 p_ccb->cont_offset += len_to_send; 806 807 /* If anything left to send, continuation needed */ 808 if (p_ccb->cont_offset < p_ccb->list_len) 809 { 810 is_cont = TRUE; 811 812 UINT8_TO_BE_STREAM (p_rsp, SDP_CONTINUATION_LEN); 813 UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset); 814 } 815 else 816 UINT8_TO_BE_STREAM (p_rsp, 0); 817 818 /* Go back and put the parameter length into the buffer */ 819 rsp_param_len = p_rsp - p_rsp_param_len - 2; 820 UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len); 821 822 /* Set the length of the SDP data in the buffer */ 823 p_buf->len = p_rsp - p_rsp_start; 824 825 826 /* Send the buffer through L2CAP */ 827 L2CA_DataWrite (p_ccb->connection_id, p_buf); 828 } 829 830 #endif /* SDP_SERVER_ENABLED == TRUE */ 831