/****************************************************************************** * * Copyright 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 #include #include "bt_common.h" #include "bt_types.h" #include "osi/include/osi.h" #include "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_t trans_num, uint16_t param_len, uint8_t* p_req, uint8_t* p_req_end); static void process_service_attr_req(tCONN_CB* p_ccb, uint16_t trans_num, uint16_t param_len, uint8_t* p_req, uint8_t* p_req_end); static void process_service_search_attr_req(tCONN_CB* p_ccb, uint16_t trans_num, uint16_t param_len, uint8_t* p_req, uint8_t* 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_t* p_req = (uint8_t*)(p_msg + 1) + p_msg->offset; uint8_t* p_req_end = p_req + p_msg->len; uint8_t pdu_id; uint16_t trans_num, param_len; /* Start inactivity timer */ alarm_set_on_mloop(p_ccb->sdp_conn_timer, SDP_INACT_TIMEOUT_MS, sdp_conn_timer_timeout, p_ccb); if (p_req + sizeof(pdu_id) + sizeof(trans_num) > p_req_end) { android_errorWriteLog(0x534e4554, "69384124"); trans_num = 0; sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_HEADER); } /* 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); if (p_req + sizeof(param_len) > p_req_end) { android_errorWriteLog(0x534e4554, "69384124"); sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_HEADER); } 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", 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_t trans_num, uint16_t param_len, uint8_t* p_req, uint8_t* p_req_end) { uint16_t max_replies, cur_handles, rem_handles, cont_offset; tSDP_UUID_SEQ uid_seq; uint8_t *p_rsp, *p_rsp_start, *p_rsp_param_len; uint16_t rsp_param_len, num_rsp_handles, xx; uint32_t rsp_handles[SDP_MAX_RECORDS] = {0}; tSDP_RECORD* p_rec = NULL; bool is_cont = false; 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. */ if (p_req + sizeof(max_replies) + sizeof(uint8_t) > p_req_end) { android_errorWriteLog(0x534e4554, "69384124"); sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_MAX_RECORDS_LIST); return; } BE_STREAM_TO_UINT16(max_replies, p_req); if (max_replies > SDP_MAX_RECORDS) max_replies = SDP_MAX_RECORDS; /* 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 + sizeof(cont_offset) > 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_t)((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 */ BT_HDR* p_buf = (BT_HDR*)osi_malloc(SDP_DATA_BUF_SIZE); p_buf->offset = L2CAP_MIN_OFFSET; p_rsp = p_rsp_start = (uint8_t*)(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_t trans_num, uint16_t param_len, uint8_t* p_req, uint8_t* p_req_end) { uint16_t max_list_len, len_to_send, cont_offset; int16_t rem_len; tSDP_ATTR_SEQ attr_seq, attr_seq_sav; uint8_t *p_rsp, *p_rsp_start, *p_rsp_param_len; uint16_t rsp_param_len, xx; uint32_t rec_handle; tSDP_RECORD* p_rec; tSDP_ATTRIBUTE* p_attr; bool is_cont = false; uint16_t attr_len; if (p_req + sizeof(rec_handle) + sizeof(max_list_len) > p_req_end) { android_errorWriteLog(0x534e4554, "69384124"); sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE); return; } /* Extract the record handle */ BE_STREAM_TO_UINT32(rec_handle, p_req); param_len -= sizeof(rec_handle); /* Get the max list length we can send. Cap it at MTU size minus overhead */ BE_STREAM_TO_UINT16(max_list_len, p_req); param_len -= sizeof(max_list_len); 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 + sizeof(uint8_t) > 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; } if (max_list_len < 4) { sdpu_build_n_send_error(p_ccb, trans_num, SDP_ILLEGAL_PARAMETER, NULL); android_errorWriteLog(0x534e4554, "68776054"); return; } /* Free and reallocate buffer */ osi_free(p_ccb->rsp_list); p_ccb->rsp_list = (uint8_t*)osi_malloc(max_list_len); /* Check if this is a continuation request */ if (*p_req) { if (*p_req++ != SDP_CONTINUATION_LEN || (p_req + sizeof(cont_offset) > 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) { sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_INX); 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 { 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_t)(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) { if (attr_len < p_ccb->cont_info.attr_offset) { android_errorWriteLog(0x534e4554, "79217770"); LOG(ERROR) << "offset is bigger than attribute length"; sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_LEN); return; } 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", 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_t)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_t)(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_t)((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD); p_ccb->rsp_list[1] = (uint8_t)((p_ccb->list_len - 3) >> 8); p_ccb->rsp_list[2] = (uint8_t)(p_ccb->list_len - 3); } else { cont_offset = 1; p_ccb->rsp_list[1] = (uint8_t)((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); p_ccb->rsp_list[2] = (uint8_t)(p_ccb->list_len - 3); p_ccb->list_len--; len_to_send--; } } /* Get a buffer to use to build the response */ BT_HDR* p_buf = (BT_HDR*)osi_malloc(SDP_DATA_BUF_SIZE); p_buf->offset = L2CAP_MIN_OFFSET; p_rsp = p_rsp_start = (uint8_t*)(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_t trans_num, uint16_t param_len, uint8_t* p_req, uint8_t* p_req_end) { uint16_t max_list_len; int16_t rem_len; uint16_t len_to_send, cont_offset; tSDP_UUID_SEQ uid_seq; uint8_t *p_rsp, *p_rsp_start, *p_rsp_param_len; uint16_t rsp_param_len, xx; tSDP_RECORD* p_rec; tSDP_ATTR_SEQ attr_seq, attr_seq_sav; tSDP_ATTRIBUTE* p_attr; bool maxxed_out = false, is_cont = false; uint8_t* p_seq_start; uint16_t seq_len, attr_len; /* 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) || (p_req + sizeof(uint16_t) > p_req_end)) { 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; param_len = static_cast(p_req_end - p_req); p_req = sdpu_extract_attr_seq(p_req, param_len, &attr_seq); if ((!p_req) || (!attr_seq.num_attr) || (p_req + sizeof(uint8_t) > 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)); if (max_list_len < 4) { sdpu_build_n_send_error(p_ccb, trans_num, SDP_ILLEGAL_PARAMETER, NULL); android_errorWriteLog(0x534e4554, "68817966"); return; } /* Free and reallocate buffer */ osi_free(p_ccb->rsp_list); p_ccb->rsp_list = (uint8_t*)osi_malloc(max_list_len); /* Check if this is a continuation request */ if (*p_req) { if (*p_req++ != SDP_CONTINUATION_LEN || (p_req + sizeof(uint16_t) > 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) { sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_INX); 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 { 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) { /* See if there is enough room to include a new service in the current * response */ rem_len = max_list_len - (int16_t)(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_t)(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) { if (attr_len < p_ccb->cont_info.attr_offset) { android_errorWriteLog(0x534e4554, "79217770"); LOG(ERROR) << "offset is bigger than attribute length"; sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_LEN); return; } 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", 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_t)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) { 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_t)(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_t)((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD); p_ccb->rsp_list[1] = (uint8_t)((p_ccb->list_len - 3) >> 8); p_ccb->rsp_list[2] = (uint8_t)(p_ccb->list_len - 3); } else { cont_offset = 1; p_ccb->rsp_list[1] = (uint8_t)((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); p_ccb->rsp_list[2] = (uint8_t)(p_ccb->list_len - 3); p_ccb->list_len--; len_to_send--; } } /* Get a buffer to use to build the response */ BT_HDR* p_buf = (BT_HDR*)osi_malloc(SDP_DATA_BUF_SIZE); p_buf->offset = L2CAP_MIN_OFFSET; p_rsp = p_rsp_start = (uint8_t*)(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 */