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