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