• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "stack/bt_types.h"
31 #include "osi/allocator.h"
32 #include "stack/btu.h"
33 #include "common/bt_defs.h"
34 #include "stack/l2cdefs.h"
35 #include "stack/hcidefs.h"
36 #include "stack/hcimsgs.h"
37 
38 #include "stack/sdp_api.h"
39 #include "sdpint.h"
40 
41 
42 #if SDP_SERVER_ENABLED == 1
43 
44 /* Maximum number of bytes to reserve out of SDP MTU for response data */
45 #define SDP_MAX_SERVICE_RSPHDR_LEN      12
46 #define SDP_MAX_SERVATTR_RSPHDR_LEN     10
47 #define SDP_MAX_ATTR_RSPHDR_LEN         10
48 
49 /********************************************************************************/
50 /*              L O C A L    F U N C T I O N     P R O T O T Y P E S            */
51 /********************************************************************************/
52 static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num,
53                                     UINT16 param_len, UINT8 *p_req,
54                                     UINT8 *p_req_end);
55 
56 static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
57                                       UINT16 param_len, UINT8 *p_req,
58                                       UINT8 *p_req_end);
59 
60 static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
61         UINT16 param_len, UINT8 *p_req,
62         UINT8 *p_req_end);
63 
64 
65 /********************************************************************************/
66 /*                  E R R O R   T E X T   S T R I N G S                         */
67 /*                                                                              */
68 /* The default is to have no text string, but we allow the strings to be        */
69 /* configured in target.h if people want them.                                  */
70 /********************************************************************************/
71 #ifndef SDP_TEXT_BAD_HEADER
72 #define SDP_TEXT_BAD_HEADER     NULL
73 #endif
74 
75 #ifndef SDP_TEXT_BAD_PDU
76 #define SDP_TEXT_BAD_PDU        NULL
77 #endif
78 
79 #ifndef SDP_TEXT_BAD_UUID_LIST
80 #define SDP_TEXT_BAD_UUID_LIST  NULL
81 #endif
82 
83 #ifndef SDP_TEXT_BAD_HANDLE
84 #define SDP_TEXT_BAD_HANDLE     NULL
85 #endif
86 
87 #ifndef SDP_TEXT_BAD_ATTR_LIST
88 #define SDP_TEXT_BAD_ATTR_LIST  NULL
89 #endif
90 
91 #ifndef SDP_TEXT_BAD_CONT_LEN
92 #define SDP_TEXT_BAD_CONT_LEN   NULL
93 #endif
94 
95 #ifndef SDP_TEXT_BAD_CONT_INX
96 #define SDP_TEXT_BAD_CONT_INX   NULL
97 #endif
98 
99 #ifndef SDP_TEXT_BAD_MAX_RECORDS_LIST
100 #define SDP_TEXT_BAD_MAX_RECORDS_LIST   NULL
101 #endif
102 
103 /*******************************************************************************
104 **
105 ** Function         sdp_server_handle_client_req
106 **
107 ** Description      This is the main dispatcher of the SDP server. It is called
108 **                  when any data is received from L2CAP, and dispatches the
109 **                  request to the appropriate handler.
110 **
111 ** Returns          void
112 **
113 *******************************************************************************/
sdp_server_handle_client_req(tCONN_CB * p_ccb,BT_HDR * p_msg)114 void sdp_server_handle_client_req (tCONN_CB *p_ccb, BT_HDR *p_msg)
115 {
116     UINT8   *p_req     = (UINT8 *) (p_msg + 1) + p_msg->offset;
117     UINT8   *p_req_end = p_req + p_msg->len;
118     UINT8   pdu_id;
119     UINT16  trans_num, param_len;
120 
121 
122     /* Start inactivity timer */
123     btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT);
124 
125     /* The first byte in the message is the pdu type */
126     pdu_id = *p_req++;
127 
128     /* Extract the transaction number and parameter length */
129     BE_STREAM_TO_UINT16 (trans_num, p_req);
130     BE_STREAM_TO_UINT16 (param_len, p_req);
131 
132     if ((p_req + param_len) != p_req_end) {
133         sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_PDU_SIZE, SDP_TEXT_BAD_HEADER);
134         return;
135     }
136 
137     switch (pdu_id) {
138     case SDP_PDU_SERVICE_SEARCH_REQ:
139         process_service_search (p_ccb, trans_num, param_len, p_req, p_req_end);
140         break;
141 
142     case SDP_PDU_SERVICE_ATTR_REQ:
143         process_service_attr_req (p_ccb, trans_num, param_len, p_req, p_req_end);
144         break;
145 
146     case SDP_PDU_SERVICE_SEARCH_ATTR_REQ:
147         process_service_search_attr_req (p_ccb, trans_num, param_len, p_req, p_req_end);
148         break;
149 
150     default:
151         sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_PDU);
152         SDP_TRACE_WARNING ("SDP - server got unknown PDU: 0x%x\n", pdu_id);
153         break;
154     }
155 }
156 
157 
158 
159 /*******************************************************************************
160 **
161 ** Function         process_service_search
162 **
163 ** Description      This function handles a service search request from the
164 **                  client. It builds a reply message with info from the database,
165 **                  and sends the reply back to the client.
166 **
167 ** Returns          void
168 **
169 *******************************************************************************/
process_service_search(tCONN_CB * p_ccb,UINT16 trans_num,UINT16 param_len,UINT8 * p_req,UINT8 * p_req_end)170 static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num,
171                                     UINT16 param_len, UINT8 *p_req,
172                                     UINT8 *p_req_end)
173 {
174     UINT16          max_replies, cur_handles, rem_handles, cont_offset;
175     tSDP_UUID_SEQ   uid_seq;
176     UINT8           *p_rsp, *p_rsp_start, *p_rsp_param_len;
177     UINT16          rsp_param_len, num_rsp_handles, xx;
178     UINT32          rsp_handles[SDP_MAX_RECORDS] = {0};
179     tSDP_RECORD    *p_rec = NULL;
180     BT_HDR         *p_buf;
181     BOOLEAN         is_cont = 0;
182     UNUSED(p_req_end);
183 
184     p_req = sdpu_extract_uid_seq (p_req, param_len, &uid_seq);
185 
186     if ((!p_req) || (!uid_seq.num_uids)) {
187         sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_UUID_LIST);
188         return;
189     }
190 
191     /* Get the max replies we can send. Cap it at our max anyways. */
192     BE_STREAM_TO_UINT16 (max_replies, p_req);
193 
194     if (max_replies > SDP_MAX_RECORDS) {
195         max_replies = SDP_MAX_RECORDS;
196     }
197 
198 
199     if ((!p_req) || (p_req > p_req_end)) {
200         sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_MAX_RECORDS_LIST);
201         return;
202     }
203 
204 
205     /* Get a list of handles that match the UUIDs given to us */
206     for (num_rsp_handles = 0; num_rsp_handles < max_replies; ) {
207         p_rec = sdp_db_service_search (p_rec, &uid_seq);
208 
209         if (p_rec) {
210             rsp_handles[num_rsp_handles++] = p_rec->record_handle;
211         } else {
212             break;
213         }
214     }
215 
216     /* Check if this is a continuation request */
217     if (*p_req) {
218         if (*p_req++ != SDP_CONTINUATION_LEN || (p_req >= p_req_end)) {
219             sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE,
220                                      SDP_TEXT_BAD_CONT_LEN);
221             return;
222         }
223         BE_STREAM_TO_UINT16 (cont_offset, p_req);
224 
225         if (cont_offset != p_ccb->cont_offset || num_rsp_handles < cont_offset) {
226             sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE,
227                                      SDP_TEXT_BAD_CONT_INX);
228             return;
229         }
230 
231         rem_handles = num_rsp_handles - cont_offset;    /* extract the remaining handles */
232     } else {
233         rem_handles = num_rsp_handles;
234         cont_offset = 0;
235         p_ccb->cont_offset = 0;
236     }
237 
238     /* Calculate how many handles will fit in one PDU */
239     cur_handles = (UINT16)((p_ccb->rem_mtu_size - SDP_MAX_SERVICE_RSPHDR_LEN) / 4);
240 
241     if (rem_handles <= cur_handles) {
242         cur_handles = rem_handles;
243     } else { /* Continuation is set */
244         p_ccb->cont_offset += cur_handles;
245         is_cont = 1;
246     }
247 
248     /* Get a buffer to use to build the response */
249     if ((p_buf = (BT_HDR *)osi_malloc(SDP_DATA_BUF_SIZE)) == NULL) {
250         SDP_TRACE_ERROR ("SDP - no buf for search rsp\n");
251         return;
252     }
253     p_buf->offset = L2CAP_MIN_OFFSET;
254     p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
255 
256     /* Start building a rsponse */
257     UINT8_TO_BE_STREAM  (p_rsp, SDP_PDU_SERVICE_SEARCH_RSP);
258     UINT16_TO_BE_STREAM (p_rsp, trans_num);
259 
260     /* Skip the length, we need to add it at the end */
261     p_rsp_param_len = p_rsp;
262     p_rsp += 2;
263 
264     /* Put in total and current number of handles, and handles themselves */
265     UINT16_TO_BE_STREAM (p_rsp, num_rsp_handles);
266     UINT16_TO_BE_STREAM (p_rsp, cur_handles);
267 
268     /*    SDP_TRACE_DEBUG("SDP Service Rsp: tothdl %d, curhdlr %d, start %d, end %d, cont %d",
269                          num_rsp_handles, cur_handles, cont_offset,
270                          cont_offset + cur_handles-1, is_cont); */
271     for (xx = cont_offset; xx < cont_offset + cur_handles; xx++) {
272         UINT32_TO_BE_STREAM (p_rsp, rsp_handles[xx]);
273     }
274 
275     if (is_cont) {
276         UINT8_TO_BE_STREAM  (p_rsp, SDP_CONTINUATION_LEN);
277         UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset);
278     } else {
279         UINT8_TO_BE_STREAM (p_rsp, 0);
280     }
281 
282     /* Go back and put the parameter length into the buffer */
283     rsp_param_len = p_rsp - p_rsp_param_len - 2;
284     UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len);
285 
286     /* Set the length of the SDP data in the buffer */
287     p_buf->len = p_rsp - p_rsp_start;
288 
289 
290     /* Send the buffer through L2CAP */
291     L2CA_DataWrite (p_ccb->connection_id, p_buf);
292 }
293 
294 
295 /*******************************************************************************
296 **
297 ** Function         process_service_attr_req
298 **
299 ** Description      This function handles an attribute request from the client.
300 **                  It builds a reply message with info from the database,
301 **                  and sends the reply back to the client.
302 **
303 ** Returns          void
304 **
305 *******************************************************************************/
process_service_attr_req(tCONN_CB * p_ccb,UINT16 trans_num,UINT16 param_len,UINT8 * p_req,UINT8 * p_req_end)306 static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
307                                       UINT16 param_len, UINT8 *p_req,
308                                       UINT8 *p_req_end)
309 {
310     UINT16          max_list_len, len_to_send, cont_offset;
311     INT16           rem_len;
312     tSDP_ATTR_SEQ   attr_seq, attr_seq_sav;
313     UINT8           *p_rsp, *p_rsp_start, *p_rsp_param_len;
314     UINT16          rsp_param_len, xx;
315     UINT32          rec_handle;
316     tSDP_RECORD     *p_rec;
317     tSDP_ATTRIBUTE  *p_attr;
318     BT_HDR          *p_buf;
319     BOOLEAN         is_cont = 0;
320     UINT16          attr_len;
321 
322     /* Extract the record handle */
323     BE_STREAM_TO_UINT32 (rec_handle, p_req);
324 
325     if (p_req > p_req_end) {
326         sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE);
327         return;
328     }
329 
330     /* Get the max list length we can send. Cap it at MTU size minus overhead */
331     BE_STREAM_TO_UINT16 (max_list_len, p_req);
332 
333     if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN)) {
334         max_list_len = p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN;
335     }
336 
337     p_req = sdpu_extract_attr_seq (p_req, param_len, &attr_seq);
338 
339     if ((!p_req) || (!attr_seq.num_attr) || (p_req > p_req_end)) {
340         sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_ATTR_LIST);
341         return;
342     }
343 
344     memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ)) ;
345 
346     /* Find a record with the record handle */
347     p_rec = sdp_db_find_record (rec_handle);
348     if (!p_rec) {
349         sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE);
350         return;
351     }
352 
353     /* Check if this is a continuation request */
354     if (*p_req) {
355         /* Free and reallocate buffer */
356         if (p_ccb->rsp_list) {
357             osi_free(p_ccb->rsp_list);
358         }
359 
360         p_ccb->rsp_list = (UINT8 *)osi_malloc(max_list_len);
361         if (p_ccb->rsp_list == NULL) {
362             SDP_TRACE_ERROR("%s No scratch buf for attr rsp\n", __func__);
363             return;
364         }
365 
366         if (*p_req++ != SDP_CONTINUATION_LEN) {
367             sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_LEN);
368             return;
369         }
370         BE_STREAM_TO_UINT16 (cont_offset, p_req);
371 
372         if (cont_offset != p_ccb->cont_offset) {
373             sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_INX);
374             return;
375         }
376 
377         if (!p_ccb->rsp_list) {
378             sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
379             return;
380         }
381         is_cont = 1;
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 = p_ccb->cont_info.next_attr_start_id;
386     } else {
387 	    if (p_ccb->rsp_list) {
388             osi_free (p_ccb->rsp_list);
389         }
390 
391         p_ccb->rsp_list = (UINT8 *)osi_malloc (max_list_len);
392         if (p_ccb->rsp_list == NULL) {
393             SDP_TRACE_ERROR ("SDP - no scratch buf for search rsp\n");
394             return;
395         }
396 
397         p_ccb->cont_offset = 0;
398         p_rsp = &p_ccb->rsp_list[3];            /* Leave space for data elem descr */
399 
400         /* Reset continuation parameters in p_ccb */
401         p_ccb->cont_info.prev_sdp_rec = NULL;
402         p_ccb->cont_info.next_attr_index = 0;
403         p_ccb->cont_info.attr_offset = 0;
404     }
405 
406     /* Search for attributes that match the list given to us */
407     for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++) {
408         p_attr = sdp_db_find_attr_in_rec (p_rec, attr_seq.attr_entry[xx].start, attr_seq.attr_entry[xx].end);
409 
410         if (p_attr) {
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                 p_ccb->cont_info.next_attr_index = xx;
417                 p_ccb->cont_info.next_attr_start_id = p_attr->id;
418                 break;
419             }
420 
421             attr_len = sdpu_get_attrib_entry_len(p_attr);
422             /* if there is a partial attribute pending to be sent */
423             if (p_ccb->cont_info.attr_offset) {
424                 p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, rem_len,
425                         &p_ccb->cont_info.attr_offset);
426 
427                 /* If the partial attrib could not been fully added yet */
428                 if (p_ccb->cont_info.attr_offset != attr_len) {
429                     break;
430                 } else { /* If the partial attrib has been added in full by now */
431                     p_ccb->cont_info.attr_offset = 0;    /* reset attr_offset */
432                 }
433             } else if (rem_len < attr_len) { /* Not enough space for attr... so add partially */
434                 if (attr_len >= SDP_MAX_ATTR_LEN) {
435                     SDP_TRACE_ERROR("SDP attr too big: max_list_len=%d,attr_len=%d\n", max_list_len, attr_len);
436                     sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
437                     return;
438                 }
439 
440                 /* add the partial attribute if possible */
441                 p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, (UINT16)rem_len,
442                         &p_ccb->cont_info.attr_offset);
443 
444                 p_ccb->cont_info.next_attr_index = xx;
445                 p_ccb->cont_info.next_attr_start_id = p_attr->id;
446                 break;
447             } else { /* build the whole attribute */
448                 p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr);
449             }
450 
451             /* If doing a range, stick with this one till no more attributes found */
452             if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end) {
453                 /* Update for next time through */
454                 attr_seq.attr_entry[xx].start = p_attr->id + 1;
455 
456                 xx--;
457             }
458         }
459     }
460     /* If all the attributes have been accomodated in p_rsp,
461        reset next_attr_index */
462     if (xx == attr_seq.num_attr) {
463         p_ccb->cont_info.next_attr_index = 0;
464     }
465 
466     len_to_send = (UINT16) (p_rsp - &p_ccb->rsp_list[0]);
467     cont_offset = 0;
468 
469     if (!is_cont) {
470         p_ccb->list_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav) + 3;
471         /* Put in the sequence header (2 or 3 bytes) */
472         if (p_ccb->list_len > 255) {
473             p_ccb->rsp_list[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
474             p_ccb->rsp_list[1] = (UINT8) ((p_ccb->list_len - 3) >> 8);
475             p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
476         } else {
477             cont_offset = 1;
478 
479             p_ccb->rsp_list[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
480             p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
481 
482             p_ccb->list_len--;
483             len_to_send--;
484         }
485     }
486 
487     /* Get a buffer to use to build the response */
488     if ((p_buf = (BT_HDR *)osi_malloc(SDP_DATA_BUF_SIZE)) == NULL) {
489         SDP_TRACE_ERROR ("SDP - no buf for search rsp\n");
490         return;
491     }
492     p_buf->offset = L2CAP_MIN_OFFSET;
493     p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
494 
495     /* Start building a rsponse */
496     UINT8_TO_BE_STREAM  (p_rsp, SDP_PDU_SERVICE_ATTR_RSP);
497     UINT16_TO_BE_STREAM (p_rsp, trans_num);
498 
499     /* Skip the parameter length, add it when we know the length */
500     p_rsp_param_len = p_rsp;
501     p_rsp += 2;
502 
503     UINT16_TO_BE_STREAM (p_rsp, len_to_send);
504 
505     memcpy (p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send);
506     p_rsp += len_to_send;
507 
508     p_ccb->cont_offset += len_to_send;
509 
510     /* If anything left to send, continuation needed */
511     if (p_ccb->cont_offset < p_ccb->list_len) {
512         is_cont = 1;
513 
514         UINT8_TO_BE_STREAM  (p_rsp, SDP_CONTINUATION_LEN);
515         UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset);
516     } else {
517         UINT8_TO_BE_STREAM (p_rsp, 0);
518     }
519 
520     /* Go back and put the parameter length into the buffer */
521     rsp_param_len = p_rsp - p_rsp_param_len - 2;
522     UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len);
523 
524     /* Set the length of the SDP data in the buffer */
525     p_buf->len = p_rsp - p_rsp_start;
526 
527 
528     /* Send the buffer through L2CAP */
529     L2CA_DataWrite (p_ccb->connection_id, p_buf);
530 }
531 
532 
533 
534 /*******************************************************************************
535 **
536 ** Function         process_service_search_attr_req
537 **
538 ** Description      This function handles a combined service search and attribute
539 **                  read request from the client. It builds a reply message with
540 **                  info from the database, and sends the reply back to the client.
541 **
542 ** Returns          void
543 **
544 *******************************************************************************/
process_service_search_attr_req(tCONN_CB * p_ccb,UINT16 trans_num,UINT16 param_len,UINT8 * p_req,UINT8 * p_req_end)545 static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
546         UINT16 param_len, UINT8 *p_req,
547         UINT8 *p_req_end)
548 {
549     UINT16         max_list_len;
550     INT16          rem_len;
551     UINT16         len_to_send, cont_offset;
552     tSDP_UUID_SEQ   uid_seq;
553     UINT8           *p_rsp, *p_rsp_start, *p_rsp_param_len;
554     UINT16          rsp_param_len, xx;
555     tSDP_RECORD    *p_rec;
556     tSDP_ATTR_SEQ   attr_seq, attr_seq_sav;
557     tSDP_ATTRIBUTE *p_attr;
558     BT_HDR         *p_buf;
559     BOOLEAN         maxxed_out = 0, is_cont = 0;
560     UINT8           *p_seq_start;
561     UINT16          seq_len, attr_len;
562     UNUSED(p_req_end);
563 
564     /* Extract the UUID sequence to search for */
565     p_req = sdpu_extract_uid_seq (p_req, param_len, &uid_seq);
566 
567     if ((!p_req) || (!uid_seq.num_uids)) {
568         sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_UUID_LIST);
569         return;
570     }
571 
572     /* Get the max list length we can send. Cap it at our max list length. */
573     BE_STREAM_TO_UINT16 (max_list_len, p_req);
574 
575     if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN)) {
576         max_list_len = p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN;
577     }
578 
579     p_req = sdpu_extract_attr_seq (p_req, param_len, &attr_seq);
580 
581     if ((!p_req) || (!attr_seq.num_attr)) {
582         sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_ATTR_LIST);
583         return;
584     }
585 
586     memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ)) ;
587 
588     /* Check if this is a continuation request */
589     if (*p_req) {
590         /* Free and reallocate buffer */
591         if (p_ccb->rsp_list) {
592             osi_free (p_ccb->rsp_list);
593         }
594 
595         p_ccb->rsp_list = (UINT8 *)osi_malloc (max_list_len);
596         if (p_ccb->rsp_list == NULL) {
597             SDP_TRACE_ERROR ("SDP - no scratch buf for search rsp\n");
598             return;
599         }
600 
601         if (*p_req++ != SDP_CONTINUATION_LEN) {
602             sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_LEN);
603             return;
604         }
605         BE_STREAM_TO_UINT16 (cont_offset, p_req);
606 
607         if (cont_offset != p_ccb->cont_offset) {
608             sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_INX);
609             return;
610         }
611 
612         if (!p_ccb->rsp_list) {
613             sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
614             return;
615         }
616         is_cont = 1;
617 
618         /* Initialise for continuation response */
619         p_rsp = &p_ccb->rsp_list[0];
620         attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start = p_ccb->cont_info.next_attr_start_id;
621     } else {
622         /* Get a scratch buffer to store response */
623         /* Free and reallocate if the earlier allocated buffer is small */
624         if (p_ccb->rsp_list) {
625             osi_free (p_ccb->rsp_list);
626         }
627 
628         p_ccb->rsp_list = (UINT8 *)osi_malloc (max_list_len);
629         if (p_ccb->rsp_list == NULL) {
630             SDP_TRACE_ERROR ("SDP - no scratch buf for search rsp\n");
631             return;
632         }
633 
634         p_ccb->cont_offset = 0;
635         p_rsp = &p_ccb->rsp_list[3];            /* Leave space for data elem descr */
636 
637         /* Reset continuation parameters in p_ccb */
638         p_ccb->cont_info.prev_sdp_rec = NULL;
639         p_ccb->cont_info.next_attr_index = 0;
640         p_ccb->cont_info.last_attr_seq_desc_sent = 0;
641         p_ccb->cont_info.attr_offset = 0;
642     }
643 
644     /* Get a list of handles that match the UUIDs given to us */
645     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)) {
646         /* Allow space for attribute sequence type and length */
647         p_seq_start = p_rsp;
648         if (p_ccb->cont_info.last_attr_seq_desc_sent == 0) {
649             /* See if there is enough room to include a new service in the current response */
650             rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]);
651             if (rem_len < 3) {
652                 /* Not enough room. Update continuation info for next response */
653                 p_ccb->cont_info.next_attr_index = 0;
654                 p_ccb->cont_info.next_attr_start_id = attr_seq.attr_entry[0].start;
655                 break;
656             }
657             p_rsp += 3;
658         }
659 
660         /* Get a list of handles that match the UUIDs given to us */
661         for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++) {
662             p_attr = sdp_db_find_attr_in_rec (p_rec, attr_seq.attr_entry[xx].start, attr_seq.attr_entry[xx].end);
663 
664             if (p_attr) {
665                 /* Check if attribute fits. Assume 3-byte value type/length */
666                 rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]);
667 
668                 /* just in case */
669                 if (rem_len <= 0) {
670                     p_ccb->cont_info.next_attr_index = xx;
671                     p_ccb->cont_info.next_attr_start_id = p_attr->id;
672                     maxxed_out = 1;
673                     break;
674                 }
675 
676                 attr_len = sdpu_get_attrib_entry_len(p_attr);
677                 /* if there is a partial attribute pending to be sent */
678                 if (p_ccb->cont_info.attr_offset) {
679                     p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, rem_len,
680                             &p_ccb->cont_info.attr_offset);
681 
682                     /* If the partial attrib could not been fully added yet */
683                     if (p_ccb->cont_info.attr_offset != attr_len) {
684                         maxxed_out = 1;
685                         break;
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                     if (attr_len >= SDP_MAX_ATTR_LEN) {
691                         SDP_TRACE_ERROR("SDP attr too big: max_list_len=%d,attr_len=%d\n", max_list_len, attr_len);
692                         sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
693                         return;
694                     }
695 
696                     /* add the partial attribute if possible */
697                     p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, (UINT16)rem_len,
698                             &p_ccb->cont_info.attr_offset);
699 
700                     p_ccb->cont_info.next_attr_index = xx;
701                     p_ccb->cont_info.next_attr_start_id = p_attr->id;
702                     maxxed_out = 1;
703                     break;
704                 } else { /* build the whole attribute */
705                     p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr);
706                 }
707 
708                 /* If doing a range, stick with this one till no more attributes found */
709                 if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end) {
710                     /* Update for next time through */
711                     attr_seq.attr_entry[xx].start = p_attr->id + 1;
712 
713                     xx--;
714                 }
715             }
716         }
717 
718         /* Go back and put the type and length into the buffer */
719         if (p_ccb->cont_info.last_attr_seq_desc_sent == 0) {
720             seq_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav);
721             if (seq_len != 0) {
722                 UINT8_TO_BE_STREAM  (p_seq_start, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
723                 UINT16_TO_BE_STREAM (p_seq_start, seq_len);
724 
725                 if (maxxed_out) {
726                     p_ccb->cont_info.last_attr_seq_desc_sent = 1;
727                 }
728             } else {
729                 p_rsp = p_seq_start;
730             }
731         }
732 
733         if (maxxed_out) {
734             break;
735         }
736 
737         /* Restore the attr_seq to look for in the next sdp record */
738         memcpy(&attr_seq, &attr_seq_sav, sizeof(tSDP_ATTR_SEQ)) ;
739 
740         /* Reset the next attr index */
741         p_ccb->cont_info.next_attr_index = 0;
742         p_ccb->cont_info.prev_sdp_rec = p_rec;
743         p_ccb->cont_info.last_attr_seq_desc_sent = 0;
744     }
745 
746     /* response length */
747     len_to_send = (UINT16) (p_rsp - &p_ccb->rsp_list[0]);
748     cont_offset = 0;
749 
750     // The current SDP server design has a critical flaw where it can run into an infinite
751     // request/response loop with the client. Here's the scenario:
752     // - client makes SDP request
753     // - server returns the first fragment of the response with a continuation token
754     // - an SDP record is deleted from the server
755     // - client issues another request with previous continuation token
756     // - server has nothing to send back because the record is unavailable but in the
757     //   first fragment, it had specified more response bytes than are now available
758     // - server sends back no additional response bytes and returns the same continuation token
759     // - client issues another request with the continuation token, and the process repeats
760     //
761     // We work around this design flaw here by checking if we will make forward progress
762     // (i.e. we will send > 0 response bytes) on a continued request. If not, we must have
763     // run into the above situation and we tell the peer an error occurred.
764     //
765     // TODO(sharvil): rewrite SDP server.
766     if (is_cont && len_to_send == 0) {
767         sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_CONT_STATE, NULL);
768         return;
769     }
770 
771     /* If first response, insert sequence header */
772     if (!is_cont) {
773         /* Get the total list length for requested uid and attribute sequence */
774         p_ccb->list_len = sdpu_get_list_len(&uid_seq, &attr_seq_sav) + 3;
775         /* Put in the sequence header (2 or 3 bytes) */
776         if (p_ccb->list_len > 255) {
777             p_ccb->rsp_list[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
778             p_ccb->rsp_list[1] = (UINT8) ((p_ccb->list_len - 3) >> 8);
779             p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
780         } else {
781             cont_offset = 1;
782 
783             p_ccb->rsp_list[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
784             p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
785 
786             p_ccb->list_len--;
787             len_to_send--;
788         }
789     }
790 
791     /* Get a buffer to use to build the response */
792     if ((p_buf = (BT_HDR *)osi_malloc(SDP_DATA_BUF_SIZE)) == NULL) {
793         SDP_TRACE_ERROR ("SDP - no buf for search rsp\n");
794         return;
795     }
796     p_buf->offset = L2CAP_MIN_OFFSET;
797     p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
798 
799     /* Start building a rsponse */
800     UINT8_TO_BE_STREAM  (p_rsp, SDP_PDU_SERVICE_SEARCH_ATTR_RSP);
801     UINT16_TO_BE_STREAM (p_rsp, trans_num);
802 
803     /* Skip the parameter length, add it when we know the length */
804     p_rsp_param_len = p_rsp;
805     p_rsp += 2;
806 
807     /* Stream the list length to send */
808     UINT16_TO_BE_STREAM (p_rsp, len_to_send);
809 
810     /* copy from rsp_list to the actual buffer to be sent */
811     memcpy (p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send);
812     p_rsp += len_to_send;
813 
814     p_ccb->cont_offset += len_to_send;
815 
816     /* If anything left to send, continuation needed */
817     if (p_ccb->cont_offset < p_ccb->list_len) {
818         is_cont = 1;
819 
820         UINT8_TO_BE_STREAM  (p_rsp, SDP_CONTINUATION_LEN);
821         UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset);
822     } else {
823         UINT8_TO_BE_STREAM (p_rsp, 0);
824     }
825 
826     /* Go back and put the parameter length into the buffer */
827     rsp_param_len = p_rsp - p_rsp_param_len - 2;
828     UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len);
829 
830     /* Set the length of the SDP data in the buffer */
831     p_buf->len = p_rsp - p_rsp_start;
832 
833 
834     /* Send the buffer through L2CAP */
835     L2CA_DataWrite (p_ccb->connection_id, p_buf);
836 }
837 
838 #endif  /* SDP_SERVER_ENABLED == 1 */
839