| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838 |
- /******************************************************************************
- *
- * Copyright (C) 1999-2012 Broadcom Corporation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- ******************************************************************************/
- /******************************************************************************
- *
- * This file contains functions that handle the SDP server functions.
- * This is mainly dealing with client requests
- *
- ******************************************************************************/
- //#include <stdlib.h>
- #include <string.h>
- //#include <stdio.h>
- #include "stack/bt_types.h"
- #include "osi/allocator.h"
- #include "stack/btu.h"
- #include "common/bt_defs.h"
- #include "stack/l2cdefs.h"
- #include "stack/hcidefs.h"
- #include "stack/hcimsgs.h"
- #include "stack/sdp_api.h"
- #include "sdpint.h"
- #if SDP_SERVER_ENABLED == TRUE
- /* Maximum number of bytes to reserve out of SDP MTU for response data */
- #define SDP_MAX_SERVICE_RSPHDR_LEN 12
- #define SDP_MAX_SERVATTR_RSPHDR_LEN 10
- #define SDP_MAX_ATTR_RSPHDR_LEN 10
- /********************************************************************************/
- /* L O C A L F U N C T I O N P R O T O T Y P E S */
- /********************************************************************************/
- static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num,
- UINT16 param_len, UINT8 *p_req,
- UINT8 *p_req_end);
- static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
- UINT16 param_len, UINT8 *p_req,
- UINT8 *p_req_end);
- static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
- UINT16 param_len, UINT8 *p_req,
- UINT8 *p_req_end);
- /********************************************************************************/
- /* E R R O R T E X T S T R I N G S */
- /* */
- /* The default is to have no text string, but we allow the strings to be */
- /* configured in target.h if people want them. */
- /********************************************************************************/
- #ifndef SDP_TEXT_BAD_HEADER
- #define SDP_TEXT_BAD_HEADER NULL
- #endif
- #ifndef SDP_TEXT_BAD_PDU
- #define SDP_TEXT_BAD_PDU NULL
- #endif
- #ifndef SDP_TEXT_BAD_UUID_LIST
- #define SDP_TEXT_BAD_UUID_LIST NULL
- #endif
- #ifndef SDP_TEXT_BAD_HANDLE
- #define SDP_TEXT_BAD_HANDLE NULL
- #endif
- #ifndef SDP_TEXT_BAD_ATTR_LIST
- #define SDP_TEXT_BAD_ATTR_LIST NULL
- #endif
- #ifndef SDP_TEXT_BAD_CONT_LEN
- #define SDP_TEXT_BAD_CONT_LEN NULL
- #endif
- #ifndef SDP_TEXT_BAD_CONT_INX
- #define SDP_TEXT_BAD_CONT_INX NULL
- #endif
- #ifndef SDP_TEXT_BAD_MAX_RECORDS_LIST
- #define SDP_TEXT_BAD_MAX_RECORDS_LIST NULL
- #endif
- /*******************************************************************************
- **
- ** Function sdp_server_handle_client_req
- **
- ** Description This is the main dispatcher of the SDP server. It is called
- ** when any data is received from L2CAP, and dispatches the
- ** request to the appropriate handler.
- **
- ** Returns void
- **
- *******************************************************************************/
- void sdp_server_handle_client_req (tCONN_CB *p_ccb, BT_HDR *p_msg)
- {
- UINT8 *p_req = (UINT8 *) (p_msg + 1) + p_msg->offset;
- UINT8 *p_req_end = p_req + p_msg->len;
- UINT8 pdu_id;
- UINT16 trans_num, param_len;
- /* Start inactivity timer */
- btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT);
- /* The first byte in the message is the pdu type */
- pdu_id = *p_req++;
- /* Extract the transaction number and parameter length */
- BE_STREAM_TO_UINT16 (trans_num, p_req);
- BE_STREAM_TO_UINT16 (param_len, p_req);
- if ((p_req + param_len) != p_req_end) {
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_PDU_SIZE, SDP_TEXT_BAD_HEADER);
- return;
- }
- switch (pdu_id) {
- case SDP_PDU_SERVICE_SEARCH_REQ:
- process_service_search (p_ccb, trans_num, param_len, p_req, p_req_end);
- break;
- case SDP_PDU_SERVICE_ATTR_REQ:
- process_service_attr_req (p_ccb, trans_num, param_len, p_req, p_req_end);
- break;
- case SDP_PDU_SERVICE_SEARCH_ATTR_REQ:
- process_service_search_attr_req (p_ccb, trans_num, param_len, p_req, p_req_end);
- break;
- default:
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_PDU);
- SDP_TRACE_WARNING ("SDP - server got unknown PDU: 0x%x\n", pdu_id);
- break;
- }
- }
- /*******************************************************************************
- **
- ** Function process_service_search
- **
- ** Description This function handles a service search request from the
- ** client. It builds a reply message with info from the database,
- ** and sends the reply back to the client.
- **
- ** Returns void
- **
- *******************************************************************************/
- static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num,
- UINT16 param_len, UINT8 *p_req,
- UINT8 *p_req_end)
- {
- UINT16 max_replies, cur_handles, rem_handles, cont_offset;
- tSDP_UUID_SEQ uid_seq;
- UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len;
- UINT16 rsp_param_len, num_rsp_handles, xx;
- UINT32 rsp_handles[SDP_MAX_RECORDS] = {0};
- tSDP_RECORD *p_rec = NULL;
- BT_HDR *p_buf;
- BOOLEAN is_cont = FALSE;
- UNUSED(p_req_end);
- p_req = sdpu_extract_uid_seq (p_req, param_len, &uid_seq);
- if ((!p_req) || (!uid_seq.num_uids)) {
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_UUID_LIST);
- return;
- }
- /* Get the max replies we can send. Cap it at our max anyways. */
- BE_STREAM_TO_UINT16 (max_replies, p_req);
- if (max_replies > SDP_MAX_RECORDS) {
- max_replies = SDP_MAX_RECORDS;
- }
- if ((!p_req) || (p_req > p_req_end)) {
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_MAX_RECORDS_LIST);
- return;
- }
- /* Get a list of handles that match the UUIDs given to us */
- for (num_rsp_handles = 0; num_rsp_handles < max_replies; ) {
- p_rec = sdp_db_service_search (p_rec, &uid_seq);
- if (p_rec) {
- rsp_handles[num_rsp_handles++] = p_rec->record_handle;
- } else {
- break;
- }
- }
- /* Check if this is a continuation request */
- if (*p_req) {
- if (*p_req++ != SDP_CONTINUATION_LEN || (p_req >= p_req_end)) {
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE,
- SDP_TEXT_BAD_CONT_LEN);
- return;
- }
- BE_STREAM_TO_UINT16 (cont_offset, p_req);
- if (cont_offset != p_ccb->cont_offset || num_rsp_handles < cont_offset) {
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE,
- SDP_TEXT_BAD_CONT_INX);
- return;
- }
- rem_handles = num_rsp_handles - cont_offset; /* extract the remaining handles */
- } else {
- rem_handles = num_rsp_handles;
- cont_offset = 0;
- p_ccb->cont_offset = 0;
- }
- /* Calculate how many handles will fit in one PDU */
- cur_handles = (UINT16)((p_ccb->rem_mtu_size - SDP_MAX_SERVICE_RSPHDR_LEN) / 4);
- if (rem_handles <= cur_handles) {
- cur_handles = rem_handles;
- } else { /* Continuation is set */
- p_ccb->cont_offset += cur_handles;
- is_cont = TRUE;
- }
- /* Get a buffer to use to build the response */
- if ((p_buf = (BT_HDR *)osi_malloc(SDP_DATA_BUF_SIZE)) == NULL) {
- SDP_TRACE_ERROR ("SDP - no buf for search rsp\n");
- return;
- }
- p_buf->offset = L2CAP_MIN_OFFSET;
- p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
- /* Start building a rsponse */
- UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_SERVICE_SEARCH_RSP);
- UINT16_TO_BE_STREAM (p_rsp, trans_num);
- /* Skip the length, we need to add it at the end */
- p_rsp_param_len = p_rsp;
- p_rsp += 2;
- /* Put in total and current number of handles, and handles themselves */
- UINT16_TO_BE_STREAM (p_rsp, num_rsp_handles);
- UINT16_TO_BE_STREAM (p_rsp, cur_handles);
- /* SDP_TRACE_DEBUG("SDP Service Rsp: tothdl %d, curhdlr %d, start %d, end %d, cont %d",
- num_rsp_handles, cur_handles, cont_offset,
- cont_offset + cur_handles-1, is_cont); */
- for (xx = cont_offset; xx < cont_offset + cur_handles; xx++) {
- UINT32_TO_BE_STREAM (p_rsp, rsp_handles[xx]);
- }
- if (is_cont) {
- UINT8_TO_BE_STREAM (p_rsp, SDP_CONTINUATION_LEN);
- UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset);
- } else {
- UINT8_TO_BE_STREAM (p_rsp, 0);
- }
- /* Go back and put the parameter length into the buffer */
- rsp_param_len = p_rsp - p_rsp_param_len - 2;
- UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len);
- /* Set the length of the SDP data in the buffer */
- p_buf->len = p_rsp - p_rsp_start;
- /* Send the buffer through L2CAP */
- L2CA_DataWrite (p_ccb->connection_id, p_buf);
- }
- /*******************************************************************************
- **
- ** Function process_service_attr_req
- **
- ** Description This function handles an attribute request from the client.
- ** It builds a reply message with info from the database,
- ** and sends the reply back to the client.
- **
- ** Returns void
- **
- *******************************************************************************/
- static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
- UINT16 param_len, UINT8 *p_req,
- UINT8 *p_req_end)
- {
- UINT16 max_list_len, len_to_send, cont_offset;
- INT16 rem_len;
- tSDP_ATTR_SEQ attr_seq, attr_seq_sav;
- UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len;
- UINT16 rsp_param_len, xx;
- UINT32 rec_handle;
- tSDP_RECORD *p_rec;
- tSDP_ATTRIBUTE *p_attr;
- BT_HDR *p_buf;
- BOOLEAN is_cont = FALSE;
- UINT16 attr_len;
- /* Extract the record handle */
- BE_STREAM_TO_UINT32 (rec_handle, p_req);
- if (p_req > p_req_end) {
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE);
- return;
- }
- /* Get the max list length we can send. Cap it at MTU size minus overhead */
- BE_STREAM_TO_UINT16 (max_list_len, p_req);
- if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN)) {
- max_list_len = p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN;
- }
- p_req = sdpu_extract_attr_seq (p_req, param_len, &attr_seq);
- if ((!p_req) || (!attr_seq.num_attr) || (p_req > p_req_end)) {
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_ATTR_LIST);
- return;
- }
- memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ)) ;
- /* Find a record with the record handle */
- p_rec = sdp_db_find_record (rec_handle);
- if (!p_rec) {
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE);
- return;
- }
- /* Check if this is a continuation request */
- if (*p_req) {
- /* Free and reallocate buffer */
- if (p_ccb->rsp_list) {
- osi_free(p_ccb->rsp_list);
- }
- p_ccb->rsp_list = (UINT8 *)osi_malloc(max_list_len);
- if (p_ccb->rsp_list == NULL) {
- SDP_TRACE_ERROR("%s No scratch buf for attr rsp\n", __func__);
- return;
- }
- if (*p_req++ != SDP_CONTINUATION_LEN) {
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_LEN);
- return;
- }
- BE_STREAM_TO_UINT16 (cont_offset, p_req);
- if (cont_offset != p_ccb->cont_offset) {
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_INX);
- return;
- }
- if (!p_ccb->rsp_list) {
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
- return;
- }
- is_cont = TRUE;
- /* Initialise for continuation response */
- p_rsp = &p_ccb->rsp_list[0];
- attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start = p_ccb->cont_info.next_attr_start_id;
- } else {
- if (p_ccb->rsp_list) {
- osi_free (p_ccb->rsp_list);
- }
- p_ccb->rsp_list = (UINT8 *)osi_malloc (max_list_len);
- if (p_ccb->rsp_list == NULL) {
- SDP_TRACE_ERROR ("SDP - no scratch buf for search rsp\n");
- return;
- }
- p_ccb->cont_offset = 0;
- p_rsp = &p_ccb->rsp_list[3]; /* Leave space for data elem descr */
- /* Reset continuation parameters in p_ccb */
- p_ccb->cont_info.prev_sdp_rec = NULL;
- p_ccb->cont_info.next_attr_index = 0;
- p_ccb->cont_info.attr_offset = 0;
- }
- /* Search for attributes that match the list given to us */
- for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++) {
- p_attr = sdp_db_find_attr_in_rec (p_rec, attr_seq.attr_entry[xx].start, attr_seq.attr_entry[xx].end);
- if (p_attr) {
- /* Check if attribute fits. Assume 3-byte value type/length */
- rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]);
- /* just in case */
- if (rem_len <= 0) {
- p_ccb->cont_info.next_attr_index = xx;
- p_ccb->cont_info.next_attr_start_id = p_attr->id;
- break;
- }
- attr_len = sdpu_get_attrib_entry_len(p_attr);
- /* if there is a partial attribute pending to be sent */
- if (p_ccb->cont_info.attr_offset) {
- p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, rem_len,
- &p_ccb->cont_info.attr_offset);
- /* If the partial attrib could not been fully added yet */
- if (p_ccb->cont_info.attr_offset != attr_len) {
- break;
- } else { /* If the partial attrib has been added in full by now */
- p_ccb->cont_info.attr_offset = 0; /* reset attr_offset */
- }
- } else if (rem_len < attr_len) { /* Not enough space for attr... so add partially */
- if (attr_len >= SDP_MAX_ATTR_LEN) {
- SDP_TRACE_ERROR("SDP attr too big: max_list_len=%d,attr_len=%d\n", max_list_len, attr_len);
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
- return;
- }
- /* add the partial attribute if possible */
- p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, (UINT16)rem_len,
- &p_ccb->cont_info.attr_offset);
- p_ccb->cont_info.next_attr_index = xx;
- p_ccb->cont_info.next_attr_start_id = p_attr->id;
- break;
- } else { /* build the whole attribute */
- p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr);
- }
- /* If doing a range, stick with this one till no more attributes found */
- if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end) {
- /* Update for next time through */
- attr_seq.attr_entry[xx].start = p_attr->id + 1;
- xx--;
- }
- }
- }
- /* If all the attributes have been accomodated in p_rsp,
- reset next_attr_index */
- if (xx == attr_seq.num_attr) {
- p_ccb->cont_info.next_attr_index = 0;
- }
- len_to_send = (UINT16) (p_rsp - &p_ccb->rsp_list[0]);
- cont_offset = 0;
- if (!is_cont) {
- p_ccb->list_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav) + 3;
- /* Put in the sequence header (2 or 3 bytes) */
- if (p_ccb->list_len > 255) {
- p_ccb->rsp_list[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
- p_ccb->rsp_list[1] = (UINT8) ((p_ccb->list_len - 3) >> 8);
- p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
- } else {
- cont_offset = 1;
- p_ccb->rsp_list[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
- p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
- p_ccb->list_len--;
- len_to_send--;
- }
- }
- /* Get a buffer to use to build the response */
- if ((p_buf = (BT_HDR *)osi_malloc(SDP_DATA_BUF_SIZE)) == NULL) {
- SDP_TRACE_ERROR ("SDP - no buf for search rsp\n");
- return;
- }
- p_buf->offset = L2CAP_MIN_OFFSET;
- p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
- /* Start building a rsponse */
- UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_SERVICE_ATTR_RSP);
- UINT16_TO_BE_STREAM (p_rsp, trans_num);
- /* Skip the parameter length, add it when we know the length */
- p_rsp_param_len = p_rsp;
- p_rsp += 2;
- UINT16_TO_BE_STREAM (p_rsp, len_to_send);
- memcpy (p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send);
- p_rsp += len_to_send;
- p_ccb->cont_offset += len_to_send;
- /* If anything left to send, continuation needed */
- if (p_ccb->cont_offset < p_ccb->list_len) {
- is_cont = TRUE;
- UINT8_TO_BE_STREAM (p_rsp, SDP_CONTINUATION_LEN);
- UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset);
- } else {
- UINT8_TO_BE_STREAM (p_rsp, 0);
- }
- /* Go back and put the parameter length into the buffer */
- rsp_param_len = p_rsp - p_rsp_param_len - 2;
- UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len);
- /* Set the length of the SDP data in the buffer */
- p_buf->len = p_rsp - p_rsp_start;
- /* Send the buffer through L2CAP */
- L2CA_DataWrite (p_ccb->connection_id, p_buf);
- }
- /*******************************************************************************
- **
- ** Function process_service_search_attr_req
- **
- ** Description This function handles a combined service search and attribute
- ** read request from the client. It builds a reply message with
- ** info from the database, and sends the reply back to the client.
- **
- ** Returns void
- **
- *******************************************************************************/
- static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
- UINT16 param_len, UINT8 *p_req,
- UINT8 *p_req_end)
- {
- UINT16 max_list_len;
- INT16 rem_len;
- UINT16 len_to_send, cont_offset;
- tSDP_UUID_SEQ uid_seq;
- UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len;
- UINT16 rsp_param_len, xx;
- tSDP_RECORD *p_rec;
- tSDP_ATTR_SEQ attr_seq, attr_seq_sav;
- tSDP_ATTRIBUTE *p_attr;
- BT_HDR *p_buf;
- BOOLEAN maxxed_out = FALSE, is_cont = FALSE;
- UINT8 *p_seq_start;
- UINT16 seq_len, attr_len;
- UNUSED(p_req_end);
- /* Extract the UUID sequence to search for */
- p_req = sdpu_extract_uid_seq (p_req, param_len, &uid_seq);
- if ((!p_req) || (!uid_seq.num_uids)) {
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_UUID_LIST);
- return;
- }
- /* Get the max list length we can send. Cap it at our max list length. */
- BE_STREAM_TO_UINT16 (max_list_len, p_req);
- if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN)) {
- max_list_len = p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN;
- }
- p_req = sdpu_extract_attr_seq (p_req, param_len, &attr_seq);
- if ((!p_req) || (!attr_seq.num_attr)) {
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_ATTR_LIST);
- return;
- }
- memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ)) ;
- /* Check if this is a continuation request */
- if (*p_req) {
- /* Free and reallocate buffer */
- if (p_ccb->rsp_list) {
- osi_free (p_ccb->rsp_list);
- }
- p_ccb->rsp_list = (UINT8 *)osi_malloc (max_list_len);
- if (p_ccb->rsp_list == NULL) {
- SDP_TRACE_ERROR ("SDP - no scratch buf for search rsp\n");
- return;
- }
- if (*p_req++ != SDP_CONTINUATION_LEN) {
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_LEN);
- return;
- }
- BE_STREAM_TO_UINT16 (cont_offset, p_req);
- if (cont_offset != p_ccb->cont_offset) {
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_INX);
- return;
- }
- if (!p_ccb->rsp_list) {
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
- return;
- }
- is_cont = TRUE;
- /* Initialise for continuation response */
- p_rsp = &p_ccb->rsp_list[0];
- attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start = p_ccb->cont_info.next_attr_start_id;
- } else {
- /* Get a scratch buffer to store response */
- /* Free and reallocate if the earlier allocated buffer is small */
- if (p_ccb->rsp_list) {
- osi_free (p_ccb->rsp_list);
- }
- p_ccb->rsp_list = (UINT8 *)osi_malloc (max_list_len);
- if (p_ccb->rsp_list == NULL) {
- SDP_TRACE_ERROR ("SDP - no scratch buf for search rsp\n");
- return;
- }
- p_ccb->cont_offset = 0;
- p_rsp = &p_ccb->rsp_list[3]; /* Leave space for data elem descr */
- /* Reset continuation parameters in p_ccb */
- p_ccb->cont_info.prev_sdp_rec = NULL;
- p_ccb->cont_info.next_attr_index = 0;
- p_ccb->cont_info.last_attr_seq_desc_sent = FALSE;
- p_ccb->cont_info.attr_offset = 0;
- }
- /* Get a list of handles that match the UUIDs given to us */
- 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)) {
- /* Allow space for attribute sequence type and length */
- p_seq_start = p_rsp;
- if (p_ccb->cont_info.last_attr_seq_desc_sent == FALSE) {
- /* See if there is enough room to include a new service in the current response */
- rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]);
- if (rem_len < 3) {
- /* Not enough room. Update continuation info for next response */
- p_ccb->cont_info.next_attr_index = 0;
- p_ccb->cont_info.next_attr_start_id = attr_seq.attr_entry[0].start;
- break;
- }
- p_rsp += 3;
- }
- /* Get a list of handles that match the UUIDs given to us */
- for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++) {
- p_attr = sdp_db_find_attr_in_rec (p_rec, attr_seq.attr_entry[xx].start, attr_seq.attr_entry[xx].end);
- if (p_attr) {
- /* Check if attribute fits. Assume 3-byte value type/length */
- rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]);
- /* just in case */
- if (rem_len <= 0) {
- p_ccb->cont_info.next_attr_index = xx;
- p_ccb->cont_info.next_attr_start_id = p_attr->id;
- maxxed_out = TRUE;
- break;
- }
- attr_len = sdpu_get_attrib_entry_len(p_attr);
- /* if there is a partial attribute pending to be sent */
- if (p_ccb->cont_info.attr_offset) {
- p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, rem_len,
- &p_ccb->cont_info.attr_offset);
- /* If the partial attrib could not been fully added yet */
- if (p_ccb->cont_info.attr_offset != attr_len) {
- maxxed_out = TRUE;
- break;
- } else { /* If the partial attrib has been added in full by now */
- p_ccb->cont_info.attr_offset = 0; /* reset attr_offset */
- }
- } else if (rem_len < attr_len) { /* Not enough space for attr... so add partially */
- if (attr_len >= SDP_MAX_ATTR_LEN) {
- SDP_TRACE_ERROR("SDP attr too big: max_list_len=%d,attr_len=%d\n", max_list_len, attr_len);
- sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
- return;
- }
- /* add the partial attribute if possible */
- p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, (UINT16)rem_len,
- &p_ccb->cont_info.attr_offset);
- p_ccb->cont_info.next_attr_index = xx;
- p_ccb->cont_info.next_attr_start_id = p_attr->id;
- maxxed_out = TRUE;
- break;
- } else { /* build the whole attribute */
- p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr);
- }
- /* If doing a range, stick with this one till no more attributes found */
- if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end) {
- /* Update for next time through */
- attr_seq.attr_entry[xx].start = p_attr->id + 1;
- xx--;
- }
- }
- }
- /* Go back and put the type and length into the buffer */
- if (p_ccb->cont_info.last_attr_seq_desc_sent == FALSE) {
- seq_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav);
- if (seq_len != 0) {
- UINT8_TO_BE_STREAM (p_seq_start, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
- UINT16_TO_BE_STREAM (p_seq_start, seq_len);
- if (maxxed_out) {
- p_ccb->cont_info.last_attr_seq_desc_sent = TRUE;
- }
- } else {
- p_rsp = p_seq_start;
- }
- }
- if (maxxed_out) {
- break;
- }
- /* Restore the attr_seq to look for in the next sdp record */
- memcpy(&attr_seq, &attr_seq_sav, sizeof(tSDP_ATTR_SEQ)) ;
- /* Reset the next attr index */
- p_ccb->cont_info.next_attr_index = 0;
- p_ccb->cont_info.prev_sdp_rec = p_rec;
- p_ccb->cont_info.last_attr_seq_desc_sent = FALSE;
- }
- /* response length */
- len_to_send = (UINT16) (p_rsp - &p_ccb->rsp_list[0]);
- cont_offset = 0;
- // The current SDP server design has a critical flaw where it can run into an infinite
- // request/response loop with the client. Here's the scenario:
- // - client makes SDP request
- // - server returns the first fragment of the response with a continuation token
- // - an SDP record is deleted from the server
- // - client issues another request with previous continuation token
- // - server has nothing to send back because the record is unavailable but in the
- // first fragment, it had specified more response bytes than are now available
- // - server sends back no additional response bytes and returns the same continuation token
- // - client issues another request with the continuation token, and the process repeats
- //
- // We work around this design flaw here by checking if we will make forward progress
- // (i.e. we will send > 0 response bytes) on a continued request. If not, we must have
- // run into the above situation and we tell the peer an error occurred.
- //
- // TODO(sharvil): rewrite SDP server.
- if (is_cont && len_to_send == 0) {
- sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_CONT_STATE, NULL);
- return;
- }
- /* If first response, insert sequence header */
- if (!is_cont) {
- /* Get the total list length for requested uid and attribute sequence */
- p_ccb->list_len = sdpu_get_list_len(&uid_seq, &attr_seq_sav) + 3;
- /* Put in the sequence header (2 or 3 bytes) */
- if (p_ccb->list_len > 255) {
- p_ccb->rsp_list[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
- p_ccb->rsp_list[1] = (UINT8) ((p_ccb->list_len - 3) >> 8);
- p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
- } else {
- cont_offset = 1;
- p_ccb->rsp_list[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
- p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
- p_ccb->list_len--;
- len_to_send--;
- }
- }
- /* Get a buffer to use to build the response */
- if ((p_buf = (BT_HDR *)osi_malloc(SDP_DATA_BUF_SIZE)) == NULL) {
- SDP_TRACE_ERROR ("SDP - no buf for search rsp\n");
- return;
- }
- p_buf->offset = L2CAP_MIN_OFFSET;
- p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
- /* Start building a rsponse */
- UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_SERVICE_SEARCH_ATTR_RSP);
- UINT16_TO_BE_STREAM (p_rsp, trans_num);
- /* Skip the parameter length, add it when we know the length */
- p_rsp_param_len = p_rsp;
- p_rsp += 2;
- /* Stream the list length to send */
- UINT16_TO_BE_STREAM (p_rsp, len_to_send);
- /* copy from rsp_list to the actual buffer to be sent */
- memcpy (p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send);
- p_rsp += len_to_send;
- p_ccb->cont_offset += len_to_send;
- /* If anything left to send, continuation needed */
- if (p_ccb->cont_offset < p_ccb->list_len) {
- is_cont = TRUE;
- UINT8_TO_BE_STREAM (p_rsp, SDP_CONTINUATION_LEN);
- UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset);
- } else {
- UINT8_TO_BE_STREAM (p_rsp, 0);
- }
- /* Go back and put the parameter length into the buffer */
- rsp_param_len = p_rsp - p_rsp_param_len - 2;
- UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len);
- /* Set the length of the SDP data in the buffer */
- p_buf->len = p_rsp - p_rsp_start;
- /* Send the buffer through L2CAP */
- L2CA_DataWrite (p_ccb->connection_id, p_buf);
- }
- #endif /* SDP_SERVER_ENABLED == TRUE */
|