Home | History | Annotate | Download | only in sdp
      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