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 *******************************************************************************/
sdp_server_handle_client_req(tCONN_CB * p_ccb,BT_HDR * p_msg)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 *******************************************************************************/
process_service_search(tCONN_CB * p_ccb,UINT16 trans_num,UINT16 param_len,UINT8 * p_req,UINT8 * p_req_end)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 *******************************************************************************/
process_service_attr_req(tCONN_CB * p_ccb,UINT16 trans_num,UINT16 param_len,UINT8 * p_req,UINT8 * p_req_end)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 *******************************************************************************/
process_service_search_attr_req(tCONN_CB * p_ccb,UINT16 trans_num,UINT16 param_len,UINT8 * p_req,UINT8 * p_req_end)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