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