sdp_server.c revision abd70abb5e42c9431df94fe9d2c4a78a0d8d9af9
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 45extern 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/********************************************************************************/ 55static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num, 56 UINT16 param_len, UINT8 *p_req, 57 UINT8 *p_req_end); 58 59static 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 63static 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*******************************************************************************/ 117void 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*******************************************************************************/ 176static 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 BT_HDR *p_buf; 187 BOOLEAN is_cont = FALSE; 188 UNUSED(p_req_end); 189 190 p_req = sdpu_extract_uid_seq (p_req, param_len, &uid_seq); 191 192 if ((!p_req) || (!uid_seq.num_uids)) 193 { 194 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_UUID_LIST); 195 return; 196 } 197 198 /* Get the max replies we can send. Cap it at our max anyways. */ 199 BE_STREAM_TO_UINT16 (max_replies, p_req); 200 201 if (max_replies > SDP_MAX_RECORDS) 202 max_replies = SDP_MAX_RECORDS; 203 204 205 if ((!p_req) || (p_req > p_req_end)) 206 { 207 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_MAX_RECORDS_LIST); 208 return; 209 } 210 211 212 /* Get a list of handles that match the UUIDs given to us */ 213 for (num_rsp_handles = 0; num_rsp_handles < max_replies; ) 214 { 215 p_rec = sdp_db_service_search (p_rec, &uid_seq); 216 217 if (p_rec) 218 rsp_handles[num_rsp_handles++] = p_rec->record_handle; 219 else 220 break; 221 } 222 223 /* Check if this is a continuation request */ 224 if (*p_req) 225 { 226 if (*p_req++ != SDP_CONTINUATION_LEN || (p_req >= p_req_end)) 227 { 228 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, 229 SDP_TEXT_BAD_CONT_LEN); 230 return; 231 } 232 BE_STREAM_TO_UINT16 (cont_offset, p_req); 233 234 if (cont_offset != p_ccb->cont_offset) 235 { 236 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, 237 SDP_TEXT_BAD_CONT_INX); 238 return; 239 } 240 241 rem_handles = num_rsp_handles - cont_offset; /* extract the remaining handles */ 242 } 243 else 244 { 245 rem_handles = num_rsp_handles; 246 cont_offset = 0; 247 p_ccb->cont_offset = 0; 248 } 249 250 /* Calculate how many handles will fit in one PDU */ 251 cur_handles = (UINT16)((p_ccb->rem_mtu_size - SDP_MAX_SERVICE_RSPHDR_LEN) / 4); 252 253 if (rem_handles <= cur_handles) 254 cur_handles = rem_handles; 255 else /* Continuation is set */ 256 { 257 p_ccb->cont_offset += cur_handles; 258 is_cont = TRUE; 259 } 260 261 /* Get a buffer to use to build the response */ 262 p_buf = (BT_HDR *)osi_malloc(SDP_DATA_BUF_SIZE); 263 if (p_buf == NULL) 264 { 265 SDP_TRACE_ERROR ("SDP - no buf for search rsp"); 266 return; 267 } 268 p_buf->offset = L2CAP_MIN_OFFSET; 269 p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; 270 271 /* Start building a rsponse */ 272 UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_SERVICE_SEARCH_RSP); 273 UINT16_TO_BE_STREAM (p_rsp, trans_num); 274 275 /* Skip the length, we need to add it at the end */ 276 p_rsp_param_len = p_rsp; 277 p_rsp += 2; 278 279 /* Put in total and current number of handles, and handles themselves */ 280 UINT16_TO_BE_STREAM (p_rsp, num_rsp_handles); 281 UINT16_TO_BE_STREAM (p_rsp, cur_handles); 282 283/* SDP_TRACE_DEBUG("SDP Service Rsp: tothdl %d, curhdlr %d, start %d, end %d, cont %d", 284 num_rsp_handles, cur_handles, cont_offset, 285 cont_offset + cur_handles-1, is_cont); */ 286 for (xx = cont_offset; xx < cont_offset + cur_handles; xx++) 287 UINT32_TO_BE_STREAM (p_rsp, rsp_handles[xx]); 288 289 if (is_cont) 290 { 291 UINT8_TO_BE_STREAM (p_rsp, SDP_CONTINUATION_LEN); 292 UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset); 293 } 294 else 295 UINT8_TO_BE_STREAM (p_rsp, 0); 296 297 /* Go back and put the parameter length into the buffer */ 298 rsp_param_len = p_rsp - p_rsp_param_len - 2; 299 UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len); 300 301 /* Set the length of the SDP data in the buffer */ 302 p_buf->len = p_rsp - p_rsp_start; 303 304 305 /* Send the buffer through L2CAP */ 306 L2CA_DataWrite (p_ccb->connection_id, p_buf); 307} 308 309 310/******************************************************************************* 311** 312** Function process_service_attr_req 313** 314** Description This function handles an attribute request from the client. 315** It builds a reply message with info from the database, 316** and sends the reply back to the client. 317** 318** Returns void 319** 320*******************************************************************************/ 321static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, 322 UINT16 param_len, UINT8 *p_req, 323 UINT8 *p_req_end) 324{ 325 UINT16 max_list_len, len_to_send, cont_offset; 326 INT16 rem_len; 327 tSDP_ATTR_SEQ attr_seq, attr_seq_sav; 328 UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len; 329 UINT16 rsp_param_len, xx; 330 UINT32 rec_handle; 331 tSDP_RECORD *p_rec; 332 tSDP_ATTRIBUTE *p_attr; 333 BT_HDR *p_buf; 334 BOOLEAN is_cont = FALSE; 335 UINT16 attr_len; 336 337 /* Extract the record handle */ 338 BE_STREAM_TO_UINT32 (rec_handle, p_req); 339 340 if (p_req > p_req_end) 341 { 342 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE); 343 return; 344 } 345 346 /* Get the max list length we can send. Cap it at MTU size minus overhead */ 347 BE_STREAM_TO_UINT16 (max_list_len, p_req); 348 349 if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN)) 350 max_list_len = p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN; 351 352 p_req = sdpu_extract_attr_seq (p_req, param_len, &attr_seq); 353 354 if ((!p_req) || (!attr_seq.num_attr) || (p_req > p_req_end)) 355 { 356 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_ATTR_LIST); 357 return; 358 } 359 360 memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ)) ; 361 362 /* Find a record with the record handle */ 363 p_rec = sdp_db_find_record (rec_handle); 364 if (!p_rec) 365 { 366 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE); 367 return; 368 } 369 370 /* Free and reallocate buffer */ 371 osi_free(p_ccb->rsp_list); 372 p_ccb->rsp_list = (UINT8 *)osi_malloc(max_list_len); 373 374 /* Check if this is a continuation request */ 375 if (*p_req) { 376 if (*p_req++ != SDP_CONTINUATION_LEN) { 377 sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_CONT_STATE, 378 SDP_TEXT_BAD_CONT_LEN); 379 return; 380 } 381 BE_STREAM_TO_UINT16(cont_offset, p_req); 382 383 if (cont_offset != p_ccb->cont_offset) { 384 sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_CONT_STATE, 385 SDP_TEXT_BAD_CONT_INX); 386 return; 387 } 388 is_cont = TRUE; 389 390 /* Initialise for continuation response */ 391 p_rsp = &p_ccb->rsp_list[0]; 392 attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start = 393 p_ccb->cont_info.next_attr_start_id; 394 } else { 395 p_ccb->cont_offset = 0; 396 p_rsp = &p_ccb->rsp_list[3]; /* Leave space for data elem descr */ 397 398 /* Reset continuation parameters in p_ccb */ 399 p_ccb->cont_info.prev_sdp_rec = NULL; 400 p_ccb->cont_info.next_attr_index = 0; 401 p_ccb->cont_info.attr_offset = 0; 402 } 403 404 /* Search for attributes that match the list given to us */ 405 for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++) 406 { 407 p_attr = sdp_db_find_attr_in_rec (p_rec, attr_seq.attr_entry[xx].start, attr_seq.attr_entry[xx].end); 408 409 if (p_attr) 410 { 411 /* Check if attribute fits. Assume 3-byte value type/length */ 412 rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]); 413 414 /* just in case */ 415 if (rem_len <= 0) 416 { 417 p_ccb->cont_info.next_attr_index = xx; 418 p_ccb->cont_info.next_attr_start_id = p_attr->id; 419 break; 420 } 421 422 attr_len = sdpu_get_attrib_entry_len(p_attr); 423 /* if there is a partial attribute pending to be sent */ 424 if (p_ccb->cont_info.attr_offset) 425 { 426 p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, rem_len, 427 &p_ccb->cont_info.attr_offset); 428 429 /* If the partial attrib could not been fully added yet */ 430 if (p_ccb->cont_info.attr_offset != attr_len) 431 break; 432 else /* If the partial attrib has been added in full by now */ 433 p_ccb->cont_info.attr_offset = 0; /* reset attr_offset */ 434 } 435 else if (rem_len < attr_len) /* Not enough space for attr... so add partially */ 436 { 437 if (attr_len >= SDP_MAX_ATTR_LEN) 438 { 439 SDP_TRACE_ERROR("SDP attr too big: max_list_len=%d,attr_len=%d", max_list_len, attr_len); 440 sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL); 441 return; 442 } 443 444 /* add the partial attribute if possible */ 445 p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, (UINT16)rem_len, 446 &p_ccb->cont_info.attr_offset); 447 448 p_ccb->cont_info.next_attr_index = xx; 449 p_ccb->cont_info.next_attr_start_id = p_attr->id; 450 break; 451 } 452 else /* build the whole attribute */ 453 p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr); 454 455 /* If doing a range, stick with this one till no more attributes found */ 456 if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end) 457 { 458 /* Update for next time through */ 459 attr_seq.attr_entry[xx].start = p_attr->id + 1; 460 461 xx--; 462 } 463 } 464 } 465 /* If all the attributes have been accomodated in p_rsp, 466 reset next_attr_index */ 467 if (xx == attr_seq.num_attr) 468 p_ccb->cont_info.next_attr_index = 0; 469 470 len_to_send = (UINT16) (p_rsp - &p_ccb->rsp_list[0]); 471 cont_offset = 0; 472 473 if (!is_cont) 474 { 475 p_ccb->list_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav) + 3; 476 /* Put in the sequence header (2 or 3 bytes) */ 477 if (p_ccb->list_len > 255) 478 { 479 p_ccb->rsp_list[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD); 480 p_ccb->rsp_list[1] = (UINT8) ((p_ccb->list_len - 3) >> 8); 481 p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3); 482 } 483 else 484 { 485 cont_offset = 1; 486 487 p_ccb->rsp_list[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); 488 p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3); 489 490 p_ccb->list_len--; 491 len_to_send--; 492 } 493 } 494 495 /* Get a buffer to use to build the response */ 496 p_buf = (BT_HDR *)osi_malloc(SDP_DATA_BUF_SIZE); 497 if (p_buf == NULL) 498 { 499 SDP_TRACE_ERROR ("SDP - no buf for search rsp"); 500 return; 501 } 502 p_buf->offset = L2CAP_MIN_OFFSET; 503 p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; 504 505 /* Start building a rsponse */ 506 UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_SERVICE_ATTR_RSP); 507 UINT16_TO_BE_STREAM (p_rsp, trans_num); 508 509 /* Skip the parameter length, add it when we know the length */ 510 p_rsp_param_len = p_rsp; 511 p_rsp += 2; 512 513 UINT16_TO_BE_STREAM (p_rsp, len_to_send); 514 515 memcpy (p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send); 516 p_rsp += len_to_send; 517 518 p_ccb->cont_offset += len_to_send; 519 520 /* If anything left to send, continuation needed */ 521 if (p_ccb->cont_offset < p_ccb->list_len) 522 { 523 is_cont = TRUE; 524 525 UINT8_TO_BE_STREAM (p_rsp, SDP_CONTINUATION_LEN); 526 UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset); 527 } 528 else 529 UINT8_TO_BE_STREAM (p_rsp, 0); 530 531 /* Go back and put the parameter length into the buffer */ 532 rsp_param_len = p_rsp - p_rsp_param_len - 2; 533 UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len); 534 535 /* Set the length of the SDP data in the buffer */ 536 p_buf->len = p_rsp - p_rsp_start; 537 538 539 /* Send the buffer through L2CAP */ 540 L2CA_DataWrite (p_ccb->connection_id, p_buf); 541} 542 543 544 545/******************************************************************************* 546** 547** Function process_service_search_attr_req 548** 549** Description This function handles a combined service search and attribute 550** read request from the client. It builds a reply message with 551** info from the database, and sends the reply back to the client. 552** 553** Returns void 554** 555*******************************************************************************/ 556static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, 557 UINT16 param_len, UINT8 *p_req, 558 UINT8 *p_req_end) 559{ 560 UINT16 max_list_len; 561 INT16 rem_len; 562 UINT16 len_to_send, cont_offset; 563 tSDP_UUID_SEQ uid_seq; 564 UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len; 565 UINT16 rsp_param_len, xx; 566 tSDP_RECORD *p_rec; 567 tSDP_ATTR_SEQ attr_seq, attr_seq_sav; 568 tSDP_ATTRIBUTE *p_attr; 569 BT_HDR *p_buf; 570 BOOLEAN maxxed_out = FALSE, is_cont = FALSE; 571 UINT8 *p_seq_start; 572 UINT16 seq_len, attr_len; 573 UNUSED(p_req_end); 574 575 /* Extract the UUID sequence to search for */ 576 p_req = sdpu_extract_uid_seq (p_req, param_len, &uid_seq); 577 578 if ((!p_req) || (!uid_seq.num_uids)) 579 { 580 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_UUID_LIST); 581 return; 582 } 583 584 /* Get the max list length we can send. Cap it at our max list length. */ 585 BE_STREAM_TO_UINT16 (max_list_len, p_req); 586 587 if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN)) 588 max_list_len = p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN; 589 590 p_req = sdpu_extract_attr_seq (p_req, param_len, &attr_seq); 591 592 if ((!p_req) || (!attr_seq.num_attr)) 593 { 594 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_ATTR_LIST); 595 return; 596 } 597 598 memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ)) ; 599 600 /* Free and reallocate buffer */ 601 osi_free(p_ccb->rsp_list); 602 p_ccb->rsp_list = (UINT8 *)osi_malloc(max_list_len); 603 604 /* Check if this is a continuation request */ 605 if (*p_req) { 606 if (*p_req++ != SDP_CONTINUATION_LEN) { 607 sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_CONT_STATE, 608 SDP_TEXT_BAD_CONT_LEN); 609 return; 610 } 611 BE_STREAM_TO_UINT16(cont_offset, p_req); 612 613 if (cont_offset != p_ccb->cont_offset) { 614 sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, 615 SDP_TEXT_BAD_CONT_INX); 616 return; 617 } 618 is_cont = TRUE; 619 620 /* Initialise for continuation response */ 621 p_rsp = &p_ccb->rsp_list[0]; 622 attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start = 623 p_ccb->cont_info.next_attr_start_id; 624 } else { 625 p_ccb->cont_offset = 0; 626 p_rsp = &p_ccb->rsp_list[3]; /* Leave space for data elem descr */ 627 628 /* Reset continuation parameters in p_ccb */ 629 p_ccb->cont_info.prev_sdp_rec = NULL; 630 p_ccb->cont_info.next_attr_index = 0; 631 p_ccb->cont_info.last_attr_seq_desc_sent = FALSE; 632 p_ccb->cont_info.attr_offset = 0; 633 } 634 635 /* Get a list of handles that match the UUIDs given to us */ 636 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)) 637 { 638 /* Allow space for attribute sequence type and length */ 639 p_seq_start = p_rsp; 640 if (p_ccb->cont_info.last_attr_seq_desc_sent == FALSE) 641 { 642 /* See if there is enough room to include a new service in the current response */ 643 rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]); 644 if (rem_len < 3) 645 { 646 /* Not enough room. Update continuation info for next response */ 647 p_ccb->cont_info.next_attr_index = 0; 648 p_ccb->cont_info.next_attr_start_id = attr_seq.attr_entry[0].start; 649 break; 650 } 651 p_rsp += 3; 652 } 653 654 /* Get a list of handles that match the UUIDs given to us */ 655 for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++) 656 { 657 p_attr = sdp_db_find_attr_in_rec (p_rec, attr_seq.attr_entry[xx].start, attr_seq.attr_entry[xx].end); 658 659 if (p_attr) 660 { 661 /* Check if attribute fits. Assume 3-byte value type/length */ 662 rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]); 663 664 /* just in case */ 665 if (rem_len <= 0) 666 { 667 p_ccb->cont_info.next_attr_index = xx; 668 p_ccb->cont_info.next_attr_start_id = p_attr->id; 669 maxxed_out = TRUE; 670 break; 671 } 672 673 attr_len = sdpu_get_attrib_entry_len(p_attr); 674 /* if there is a partial attribute pending to be sent */ 675 if (p_ccb->cont_info.attr_offset) 676 { 677 p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, rem_len, 678 &p_ccb->cont_info.attr_offset); 679 680 /* If the partial attrib could not been fully added yet */ 681 if (p_ccb->cont_info.attr_offset != attr_len) 682 { 683 maxxed_out = TRUE; 684 break; 685 } 686 else /* If the partial attrib has been added in full by now */ 687 p_ccb->cont_info.attr_offset = 0; /* reset attr_offset */ 688 } 689 else if (rem_len < attr_len) /* Not enough space for attr... so add partially */ 690 { 691 if (attr_len >= SDP_MAX_ATTR_LEN) 692 { 693 SDP_TRACE_ERROR("SDP attr too big: max_list_len=%d,attr_len=%d", max_list_len, attr_len); 694 sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL); 695 return; 696 } 697 698 /* add the partial attribute if possible */ 699 p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, (UINT16)rem_len, 700 &p_ccb->cont_info.attr_offset); 701 702 p_ccb->cont_info.next_attr_index = xx; 703 p_ccb->cont_info.next_attr_start_id = p_attr->id; 704 maxxed_out = TRUE; 705 break; 706 } 707 else /* build the whole attribute */ 708 p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr); 709 710 /* If doing a range, stick with this one till no more attributes found */ 711 if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end) 712 { 713 /* Update for next time through */ 714 attr_seq.attr_entry[xx].start = p_attr->id + 1; 715 716 xx--; 717 } 718 } 719 } 720 721 /* Go back and put the type and length into the buffer */ 722 if (p_ccb->cont_info.last_attr_seq_desc_sent == FALSE) 723 { 724 seq_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav); 725 if (seq_len != 0) 726 { 727 UINT8_TO_BE_STREAM (p_seq_start, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD); 728 UINT16_TO_BE_STREAM (p_seq_start, seq_len); 729 730 if (maxxed_out) 731 p_ccb->cont_info.last_attr_seq_desc_sent = TRUE; 732 } 733 else 734 p_rsp = p_seq_start; 735 } 736 737 if (maxxed_out) 738 break; 739 740 /* Restore the attr_seq to look for in the next sdp record */ 741 memcpy(&attr_seq, &attr_seq_sav, sizeof(tSDP_ATTR_SEQ)) ; 742 743 /* Reset the next attr index */ 744 p_ccb->cont_info.next_attr_index = 0; 745 p_ccb->cont_info.prev_sdp_rec = p_rec; 746 p_ccb->cont_info.last_attr_seq_desc_sent = FALSE; 747 } 748 749 /* response length */ 750 len_to_send = (UINT16) (p_rsp - &p_ccb->rsp_list[0]); 751 cont_offset = 0; 752 753 // The current SDP server design has a critical flaw where it can run into an infinite 754 // request/response loop with the client. Here's the scenario: 755 // - client makes SDP request 756 // - server returns the first fragment of the response with a continuation token 757 // - an SDP record is deleted from the server 758 // - client issues another request with previous continuation token 759 // - server has nothing to send back because the record is unavailable but in the 760 // first fragment, it had specified more response bytes than are now available 761 // - server sends back no additional response bytes and returns the same continuation token 762 // - client issues another request with the continuation token, and the process repeats 763 // 764 // We work around this design flaw here by checking if we will make forward progress 765 // (i.e. we will send > 0 response bytes) on a continued request. If not, we must have 766 // run into the above situation and we tell the peer an error occurred. 767 // 768 // TODO(sharvil): rewrite SDP server. 769 if (is_cont && len_to_send == 0) { 770 sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_CONT_STATE, NULL); 771 return; 772 } 773 774 /* If first response, insert sequence header */ 775 if (!is_cont) 776 { 777 /* Get the total list length for requested uid and attribute sequence */ 778 p_ccb->list_len = sdpu_get_list_len(&uid_seq, &attr_seq_sav) + 3; 779 /* Put in the sequence header (2 or 3 bytes) */ 780 if (p_ccb->list_len > 255) 781 { 782 p_ccb->rsp_list[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD); 783 p_ccb->rsp_list[1] = (UINT8) ((p_ccb->list_len - 3) >> 8); 784 p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3); 785 } 786 else 787 { 788 cont_offset = 1; 789 790 p_ccb->rsp_list[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); 791 p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3); 792 793 p_ccb->list_len--; 794 len_to_send--; 795 } 796 } 797 798 /* Get a buffer to use to build the response */ 799 p_buf = (BT_HDR *)osi_malloc(SDP_DATA_BUF_SIZE); 800 if (p_buf == NULL) 801 { 802 SDP_TRACE_ERROR ("SDP - no buf for search rsp"); 803 return; 804 } 805 p_buf->offset = L2CAP_MIN_OFFSET; 806 p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; 807 808 /* Start building a rsponse */ 809 UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_SERVICE_SEARCH_ATTR_RSP); 810 UINT16_TO_BE_STREAM (p_rsp, trans_num); 811 812 /* Skip the parameter length, add it when we know the length */ 813 p_rsp_param_len = p_rsp; 814 p_rsp += 2; 815 816 /* Stream the list length to send */ 817 UINT16_TO_BE_STREAM (p_rsp, len_to_send); 818 819 /* copy from rsp_list to the actual buffer to be sent */ 820 memcpy (p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send); 821 p_rsp += len_to_send; 822 823 p_ccb->cont_offset += len_to_send; 824 825 /* If anything left to send, continuation needed */ 826 if (p_ccb->cont_offset < p_ccb->list_len) 827 { 828 is_cont = TRUE; 829 830 UINT8_TO_BE_STREAM (p_rsp, SDP_CONTINUATION_LEN); 831 UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset); 832 } 833 else 834 UINT8_TO_BE_STREAM (p_rsp, 0); 835 836 /* Go back and put the parameter length into the buffer */ 837 rsp_param_len = p_rsp - p_rsp_param_len - 2; 838 UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len); 839 840 /* Set the length of the SDP data in the buffer */ 841 p_buf->len = p_rsp - p_rsp_start; 842 843 844 /* Send the buffer through L2CAP */ 845 L2CA_DataWrite (p_ccb->connection_id, p_buf); 846} 847 848#endif /* SDP_SERVER_ENABLED == TRUE */ 849