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