xref: /btstack/src/classic/sdp_server.c (revision 746ccb7e4a4899fec7ecb02299b827176283ea35)
1*746ccb7eSMatthias Ringwald /*
2*746ccb7eSMatthias Ringwald  * Copyright (C) 2014 BlueKitchen GmbH
3*746ccb7eSMatthias Ringwald  *
4*746ccb7eSMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
5*746ccb7eSMatthias Ringwald  * modification, are permitted provided that the following conditions
6*746ccb7eSMatthias Ringwald  * are met:
7*746ccb7eSMatthias Ringwald  *
8*746ccb7eSMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
9*746ccb7eSMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
10*746ccb7eSMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11*746ccb7eSMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
12*746ccb7eSMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
13*746ccb7eSMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
14*746ccb7eSMatthias Ringwald  *    contributors may be used to endorse or promote products derived
15*746ccb7eSMatthias Ringwald  *    from this software without specific prior written permission.
16*746ccb7eSMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
17*746ccb7eSMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
18*746ccb7eSMatthias Ringwald  *    monetary gain.
19*746ccb7eSMatthias Ringwald  *
20*746ccb7eSMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21*746ccb7eSMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22*746ccb7eSMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23*746ccb7eSMatthias Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24*746ccb7eSMatthias Ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25*746ccb7eSMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26*746ccb7eSMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27*746ccb7eSMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28*746ccb7eSMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29*746ccb7eSMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30*746ccb7eSMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31*746ccb7eSMatthias Ringwald  * SUCH DAMAGE.
32*746ccb7eSMatthias Ringwald  *
33*746ccb7eSMatthias Ringwald  * Please inquire about commercial licensing options at
34*746ccb7eSMatthias Ringwald  * [email protected]
35*746ccb7eSMatthias Ringwald  *
36*746ccb7eSMatthias Ringwald  */
37*746ccb7eSMatthias Ringwald 
38*746ccb7eSMatthias Ringwald /*
39*746ccb7eSMatthias Ringwald  * Implementation of the Service Discovery Protocol Server
40*746ccb7eSMatthias Ringwald  */
41*746ccb7eSMatthias Ringwald 
42*746ccb7eSMatthias Ringwald #include <stdio.h>
43*746ccb7eSMatthias Ringwald #include <string.h>
44*746ccb7eSMatthias Ringwald 
45*746ccb7eSMatthias Ringwald #include "btstack_memory.h"
46*746ccb7eSMatthias Ringwald #include "btstack_debug.h"
47*746ccb7eSMatthias Ringwald #include "hci_dump.h"
48*746ccb7eSMatthias Ringwald #include "l2cap.h"
49*746ccb7eSMatthias Ringwald 
50*746ccb7eSMatthias Ringwald #include "classic/sdp_server.h"
51*746ccb7eSMatthias Ringwald #include "classic/sdp_util.h"
52*746ccb7eSMatthias Ringwald 
53*746ccb7eSMatthias Ringwald // max reserved ServiceRecordHandle
54*746ccb7eSMatthias Ringwald #define maxReservedServiceRecordHandle 0xffff
55*746ccb7eSMatthias Ringwald 
56*746ccb7eSMatthias Ringwald // max SDP response matches L2CAP PDU -- allow to use smaller buffer
57*746ccb7eSMatthias Ringwald #ifndef SDP_RESPONSE_BUFFER_SIZE
58*746ccb7eSMatthias Ringwald #define SDP_RESPONSE_BUFFER_SIZE (HCI_ACL_BUFFER_SIZE-HCI_ACL_HEADER_SIZE)
59*746ccb7eSMatthias Ringwald #endif
60*746ccb7eSMatthias Ringwald 
61*746ccb7eSMatthias Ringwald static void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
62*746ccb7eSMatthias Ringwald 
63*746ccb7eSMatthias Ringwald // registered service records
64*746ccb7eSMatthias Ringwald static btstack_linked_list_t sdp_service_records = NULL;
65*746ccb7eSMatthias Ringwald 
66*746ccb7eSMatthias Ringwald // our handles start after the reserved range
67*746ccb7eSMatthias Ringwald static uint32_t sdp_next_service_record_handle = ((uint32_t) maxReservedServiceRecordHandle) + 2;
68*746ccb7eSMatthias Ringwald 
69*746ccb7eSMatthias Ringwald static uint8_t sdp_response_buffer[SDP_RESPONSE_BUFFER_SIZE];
70*746ccb7eSMatthias Ringwald 
71*746ccb7eSMatthias Ringwald static uint16_t l2cap_cid = 0;
72*746ccb7eSMatthias Ringwald static uint16_t sdp_response_size = 0;
73*746ccb7eSMatthias Ringwald 
74*746ccb7eSMatthias Ringwald void sdp_init(void){
75*746ccb7eSMatthias Ringwald     // register with l2cap psm sevices - max MTU
76*746ccb7eSMatthias Ringwald     l2cap_register_service(sdp_packet_handler, PSM_SDP, 0xffff, LEVEL_0);
77*746ccb7eSMatthias Ringwald }
78*746ccb7eSMatthias Ringwald 
79*746ccb7eSMatthias Ringwald uint32_t sdp_get_service_record_handle(const uint8_t * record){
80*746ccb7eSMatthias Ringwald     // TODO: make sdp_get_attribute_value_for_attribute_id accept const data to remove cast
81*746ccb7eSMatthias Ringwald     uint8_t * serviceRecordHandleAttribute = sdp_get_attribute_value_for_attribute_id((uint8_t *)record, SDP_ServiceRecordHandle);
82*746ccb7eSMatthias Ringwald     if (!serviceRecordHandleAttribute) return 0;
83*746ccb7eSMatthias Ringwald     if (de_get_element_type(serviceRecordHandleAttribute) != DE_UINT) return 0;
84*746ccb7eSMatthias Ringwald     if (de_get_size_type(serviceRecordHandleAttribute) != DE_SIZE_32) return 0;
85*746ccb7eSMatthias Ringwald     return bit_endian_read_32(serviceRecordHandleAttribute, 1);
86*746ccb7eSMatthias Ringwald }
87*746ccb7eSMatthias Ringwald 
88*746ccb7eSMatthias Ringwald static service_record_item_t * sdp_get_record_item_for_handle(uint32_t handle){
89*746ccb7eSMatthias Ringwald     btstack_linked_item_t *it;
90*746ccb7eSMatthias Ringwald     for (it = (btstack_linked_item_t *) sdp_service_records; it ; it = it->next){
91*746ccb7eSMatthias Ringwald         service_record_item_t * item = (service_record_item_t *) it;
92*746ccb7eSMatthias Ringwald         if (item->service_record_handle == handle){
93*746ccb7eSMatthias Ringwald             return item;
94*746ccb7eSMatthias Ringwald         }
95*746ccb7eSMatthias Ringwald     }
96*746ccb7eSMatthias Ringwald     return NULL;
97*746ccb7eSMatthias Ringwald }
98*746ccb7eSMatthias Ringwald 
99*746ccb7eSMatthias Ringwald uint8_t * sdp_get_record_for_handle(uint32_t handle){
100*746ccb7eSMatthias Ringwald     service_record_item_t * record_item =  sdp_get_record_item_for_handle(handle);
101*746ccb7eSMatthias Ringwald     if (!record_item) return 0;
102*746ccb7eSMatthias Ringwald     return record_item->service_record;
103*746ccb7eSMatthias Ringwald }
104*746ccb7eSMatthias Ringwald 
105*746ccb7eSMatthias Ringwald // get next free, unregistered service record handle
106*746ccb7eSMatthias Ringwald uint32_t sdp_create_service_record_handle(void){
107*746ccb7eSMatthias Ringwald     uint32_t handle = 0;
108*746ccb7eSMatthias Ringwald     do {
109*746ccb7eSMatthias Ringwald         handle = sdp_next_service_record_handle++;
110*746ccb7eSMatthias Ringwald         if (sdp_get_record_item_for_handle(handle)) handle = 0;
111*746ccb7eSMatthias Ringwald     } while (handle == 0);
112*746ccb7eSMatthias Ringwald     return handle;
113*746ccb7eSMatthias Ringwald }
114*746ccb7eSMatthias Ringwald 
115*746ccb7eSMatthias Ringwald /**
116*746ccb7eSMatthias Ringwald  * @brief Register Service Record with database using ServiceRecordHandle stored in record
117*746ccb7eSMatthias Ringwald  * @pre AttributeIDs are in ascending order
118*746ccb7eSMatthias Ringwald  * @pre ServiceRecordHandle is first attribute and valid
119*746ccb7eSMatthias Ringwald  * @param record is not copied!
120*746ccb7eSMatthias Ringwald  * @result status
121*746ccb7eSMatthias Ringwald  */
122*746ccb7eSMatthias Ringwald uint8_t sdp_register_service(const uint8_t * record){
123*746ccb7eSMatthias Ringwald 
124*746ccb7eSMatthias Ringwald     // validate service record handle. it must: exist, be in valid range, not have been already used
125*746ccb7eSMatthias Ringwald     uint32_t record_handle = sdp_get_service_record_handle(record);
126*746ccb7eSMatthias Ringwald     if (!record_handle) return SDP_HANDLE_INVALID;
127*746ccb7eSMatthias Ringwald     if (record_handle <= maxReservedServiceRecordHandle) return SDP_HANDLE_INVALID;
128*746ccb7eSMatthias Ringwald     if (sdp_get_record_item_for_handle(record_handle)) return SDP_HANDLE_ALREADY_REGISTERED;
129*746ccb7eSMatthias Ringwald 
130*746ccb7eSMatthias Ringwald     // alloc memory for new service_record_item
131*746ccb7eSMatthias Ringwald     service_record_item_t * newRecordItem = btstack_memory_service_record_item_get();
132*746ccb7eSMatthias Ringwald     if (!newRecordItem) return BTSTACK_MEMORY_ALLOC_FAILED;
133*746ccb7eSMatthias Ringwald 
134*746ccb7eSMatthias Ringwald     // set handle and record
135*746ccb7eSMatthias Ringwald     newRecordItem->service_record_handle = record_handle;
136*746ccb7eSMatthias Ringwald     newRecordItem->service_record = (uint8_t*) record;
137*746ccb7eSMatthias Ringwald 
138*746ccb7eSMatthias Ringwald     // add to linked list
139*746ccb7eSMatthias Ringwald     btstack_linked_list_add(&sdp_service_records, (btstack_linked_item_t *) newRecordItem);
140*746ccb7eSMatthias Ringwald 
141*746ccb7eSMatthias Ringwald     return 0;
142*746ccb7eSMatthias Ringwald }
143*746ccb7eSMatthias Ringwald 
144*746ccb7eSMatthias Ringwald //
145*746ccb7eSMatthias Ringwald // unregister service record
146*746ccb7eSMatthias Ringwald //
147*746ccb7eSMatthias Ringwald void sdp_unregister_service(uint32_t service_record_handle){
148*746ccb7eSMatthias Ringwald     service_record_item_t * record_item = sdp_get_record_item_for_handle(service_record_handle);
149*746ccb7eSMatthias Ringwald     if (!record_item) return;
150*746ccb7eSMatthias Ringwald     btstack_linked_list_remove(&sdp_service_records, (btstack_linked_item_t *) record_item);
151*746ccb7eSMatthias Ringwald }
152*746ccb7eSMatthias Ringwald 
153*746ccb7eSMatthias Ringwald // PDU
154*746ccb7eSMatthias Ringwald // PDU ID (1), Transaction ID (2), Param Length (2), Param 1, Param 2, ..
155*746ccb7eSMatthias Ringwald 
156*746ccb7eSMatthias Ringwald static int sdp_create_error_response(uint16_t transaction_id, uint16_t error_code){
157*746ccb7eSMatthias Ringwald     sdp_response_buffer[0] = SDP_ErrorResponse;
158*746ccb7eSMatthias Ringwald     big_endian_store_16(sdp_response_buffer, 1, transaction_id);
159*746ccb7eSMatthias Ringwald     big_endian_store_16(sdp_response_buffer, 3, 2);
160*746ccb7eSMatthias Ringwald     big_endian_store_16(sdp_response_buffer, 5, error_code); // invalid syntax
161*746ccb7eSMatthias Ringwald     return 7;
162*746ccb7eSMatthias Ringwald }
163*746ccb7eSMatthias Ringwald 
164*746ccb7eSMatthias Ringwald int sdp_handle_service_search_request(uint8_t * packet, uint16_t remote_mtu){
165*746ccb7eSMatthias Ringwald 
166*746ccb7eSMatthias Ringwald     // get request details
167*746ccb7eSMatthias Ringwald     uint16_t  transaction_id = big_endian_read_16(packet, 1);
168*746ccb7eSMatthias Ringwald     // not used yet - uint16_t  param_len = big_endian_read_16(packet, 3);
169*746ccb7eSMatthias Ringwald     uint8_t * serviceSearchPattern = &packet[5];
170*746ccb7eSMatthias Ringwald     uint16_t  serviceSearchPatternLen = de_get_len(serviceSearchPattern);
171*746ccb7eSMatthias Ringwald     uint16_t  maximumServiceRecordCount = big_endian_read_16(packet, 5 + serviceSearchPatternLen);
172*746ccb7eSMatthias Ringwald     uint8_t * continuationState = &packet[5+serviceSearchPatternLen+2];
173*746ccb7eSMatthias Ringwald 
174*746ccb7eSMatthias Ringwald     // calc maxumumServiceRecordCount based on remote MTU
175*746ccb7eSMatthias Ringwald     uint16_t maxNrServiceRecordsPerResponse = (remote_mtu - (9+3))/4;
176*746ccb7eSMatthias Ringwald 
177*746ccb7eSMatthias Ringwald     // continuation state contains index of next service record to examine
178*746ccb7eSMatthias Ringwald     int      continuation = 0;
179*746ccb7eSMatthias Ringwald     uint16_t continuation_index = 0;
180*746ccb7eSMatthias Ringwald     if (continuationState[0] == 2){
181*746ccb7eSMatthias Ringwald         continuation_index = big_endian_read_16(continuationState, 1);
182*746ccb7eSMatthias Ringwald     }
183*746ccb7eSMatthias Ringwald 
184*746ccb7eSMatthias Ringwald     // get and limit total count
185*746ccb7eSMatthias Ringwald     btstack_linked_item_t *it;
186*746ccb7eSMatthias Ringwald     uint16_t total_service_count   = 0;
187*746ccb7eSMatthias Ringwald     for (it = (btstack_linked_item_t *) sdp_service_records; it ; it = it->next){
188*746ccb7eSMatthias Ringwald         service_record_item_t * item = (service_record_item_t *) it;
189*746ccb7eSMatthias Ringwald         if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue;
190*746ccb7eSMatthias Ringwald         total_service_count++;
191*746ccb7eSMatthias Ringwald     }
192*746ccb7eSMatthias Ringwald     if (total_service_count > maximumServiceRecordCount){
193*746ccb7eSMatthias Ringwald         total_service_count = maximumServiceRecordCount;
194*746ccb7eSMatthias Ringwald     }
195*746ccb7eSMatthias Ringwald 
196*746ccb7eSMatthias Ringwald     // ServiceRecordHandleList at 9
197*746ccb7eSMatthias Ringwald     uint16_t pos = 9;
198*746ccb7eSMatthias Ringwald     uint16_t current_service_count  = 0;
199*746ccb7eSMatthias Ringwald     uint16_t current_service_index  = 0;
200*746ccb7eSMatthias Ringwald     uint16_t matching_service_count = 0;
201*746ccb7eSMatthias Ringwald     for (it = (btstack_linked_item_t *) sdp_service_records; it ; it = it->next, ++current_service_index){
202*746ccb7eSMatthias Ringwald         service_record_item_t * item = (service_record_item_t *) it;
203*746ccb7eSMatthias Ringwald 
204*746ccb7eSMatthias Ringwald         if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue;
205*746ccb7eSMatthias Ringwald         matching_service_count++;
206*746ccb7eSMatthias Ringwald 
207*746ccb7eSMatthias Ringwald         if (current_service_index < continuation_index) continue;
208*746ccb7eSMatthias Ringwald 
209*746ccb7eSMatthias Ringwald         big_endian_store_32(sdp_response_buffer, pos, item->service_record_handle);
210*746ccb7eSMatthias Ringwald         pos += 4;
211*746ccb7eSMatthias Ringwald         current_service_count++;
212*746ccb7eSMatthias Ringwald 
213*746ccb7eSMatthias Ringwald         if (matching_service_count >= total_service_count) break;
214*746ccb7eSMatthias Ringwald 
215*746ccb7eSMatthias Ringwald         if (current_service_count >= maxNrServiceRecordsPerResponse){
216*746ccb7eSMatthias Ringwald             continuation = 1;
217*746ccb7eSMatthias Ringwald             continuation_index = current_service_index + 1;
218*746ccb7eSMatthias Ringwald             break;
219*746ccb7eSMatthias Ringwald         }
220*746ccb7eSMatthias Ringwald     }
221*746ccb7eSMatthias Ringwald 
222*746ccb7eSMatthias Ringwald     // Store continuation state
223*746ccb7eSMatthias Ringwald     if (continuation) {
224*746ccb7eSMatthias Ringwald         sdp_response_buffer[pos++] = 2;
225*746ccb7eSMatthias Ringwald         big_endian_store_16(sdp_response_buffer, pos, continuation_index);
226*746ccb7eSMatthias Ringwald         pos += 2;
227*746ccb7eSMatthias Ringwald     } else {
228*746ccb7eSMatthias Ringwald         sdp_response_buffer[pos++] = 0;
229*746ccb7eSMatthias Ringwald     }
230*746ccb7eSMatthias Ringwald 
231*746ccb7eSMatthias Ringwald     // header
232*746ccb7eSMatthias Ringwald     sdp_response_buffer[0] = SDP_ServiceSearchResponse;
233*746ccb7eSMatthias Ringwald     big_endian_store_16(sdp_response_buffer, 1, transaction_id);
234*746ccb7eSMatthias Ringwald     big_endian_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload
235*746ccb7eSMatthias Ringwald     big_endian_store_16(sdp_response_buffer, 5, total_service_count);
236*746ccb7eSMatthias Ringwald     big_endian_store_16(sdp_response_buffer, 7, current_service_count);
237*746ccb7eSMatthias Ringwald 
238*746ccb7eSMatthias Ringwald     return pos;
239*746ccb7eSMatthias Ringwald }
240*746ccb7eSMatthias Ringwald 
241*746ccb7eSMatthias Ringwald int sdp_handle_service_attribute_request(uint8_t * packet, uint16_t remote_mtu){
242*746ccb7eSMatthias Ringwald 
243*746ccb7eSMatthias Ringwald     // get request details
244*746ccb7eSMatthias Ringwald     uint16_t  transaction_id = big_endian_read_16(packet, 1);
245*746ccb7eSMatthias Ringwald     // not used yet - uint16_t  param_len = big_endian_read_16(packet, 3);
246*746ccb7eSMatthias Ringwald     uint32_t  serviceRecordHandle = bit_endian_read_32(packet, 5);
247*746ccb7eSMatthias Ringwald     uint16_t  maximumAttributeByteCount = big_endian_read_16(packet, 9);
248*746ccb7eSMatthias Ringwald     uint8_t * attributeIDList = &packet[11];
249*746ccb7eSMatthias Ringwald     uint16_t  attributeIDListLen = de_get_len(attributeIDList);
250*746ccb7eSMatthias Ringwald     uint8_t * continuationState = &packet[11+attributeIDListLen];
251*746ccb7eSMatthias Ringwald 
252*746ccb7eSMatthias Ringwald     // calc maximumAttributeByteCount based on remote MTU
253*746ccb7eSMatthias Ringwald     uint16_t maximumAttributeByteCount2 = remote_mtu - (7+3);
254*746ccb7eSMatthias Ringwald     if (maximumAttributeByteCount2 < maximumAttributeByteCount) {
255*746ccb7eSMatthias Ringwald         maximumAttributeByteCount = maximumAttributeByteCount2;
256*746ccb7eSMatthias Ringwald     }
257*746ccb7eSMatthias Ringwald 
258*746ccb7eSMatthias Ringwald     // continuation state contains the offset into the complete response
259*746ccb7eSMatthias Ringwald     uint16_t continuation_offset = 0;
260*746ccb7eSMatthias Ringwald     if (continuationState[0] == 2){
261*746ccb7eSMatthias Ringwald         continuation_offset = big_endian_read_16(continuationState, 1);
262*746ccb7eSMatthias Ringwald     }
263*746ccb7eSMatthias Ringwald 
264*746ccb7eSMatthias Ringwald     // get service record
265*746ccb7eSMatthias Ringwald     service_record_item_t * item = sdp_get_record_item_for_handle(serviceRecordHandle);
266*746ccb7eSMatthias Ringwald     if (!item){
267*746ccb7eSMatthias Ringwald         // service record handle doesn't exist
268*746ccb7eSMatthias Ringwald         return sdp_create_error_response(transaction_id, 0x0002); /// invalid Service Record Handle
269*746ccb7eSMatthias Ringwald     }
270*746ccb7eSMatthias Ringwald 
271*746ccb7eSMatthias Ringwald 
272*746ccb7eSMatthias Ringwald     // AttributeList - starts at offset 7
273*746ccb7eSMatthias Ringwald     uint16_t pos = 7;
274*746ccb7eSMatthias Ringwald 
275*746ccb7eSMatthias Ringwald     if (continuation_offset == 0){
276*746ccb7eSMatthias Ringwald 
277*746ccb7eSMatthias Ringwald         // get size of this record
278*746ccb7eSMatthias Ringwald         uint16_t filtered_attributes_size = spd_get_filtered_size(item->service_record, attributeIDList);
279*746ccb7eSMatthias Ringwald 
280*746ccb7eSMatthias Ringwald         // store DES
281*746ccb7eSMatthias Ringwald         de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, filtered_attributes_size);
282*746ccb7eSMatthias Ringwald         maximumAttributeByteCount -= 3;
283*746ccb7eSMatthias Ringwald         pos += 3;
284*746ccb7eSMatthias Ringwald     }
285*746ccb7eSMatthias Ringwald 
286*746ccb7eSMatthias Ringwald     // copy maximumAttributeByteCount from record
287*746ccb7eSMatthias Ringwald     uint16_t bytes_used;
288*746ccb7eSMatthias Ringwald     int complete = sdp_filter_attributes_in_attributeIDList(item->service_record, attributeIDList, continuation_offset, maximumAttributeByteCount, &bytes_used, &sdp_response_buffer[pos]);
289*746ccb7eSMatthias Ringwald     pos += bytes_used;
290*746ccb7eSMatthias Ringwald 
291*746ccb7eSMatthias Ringwald     uint16_t attributeListByteCount = pos - 7;
292*746ccb7eSMatthias Ringwald 
293*746ccb7eSMatthias Ringwald     if (complete) {
294*746ccb7eSMatthias Ringwald         sdp_response_buffer[pos++] = 0;
295*746ccb7eSMatthias Ringwald     } else {
296*746ccb7eSMatthias Ringwald         continuation_offset += bytes_used;
297*746ccb7eSMatthias Ringwald         sdp_response_buffer[pos++] = 2;
298*746ccb7eSMatthias Ringwald         big_endian_store_16(sdp_response_buffer, pos, continuation_offset);
299*746ccb7eSMatthias Ringwald         pos += 2;
300*746ccb7eSMatthias Ringwald     }
301*746ccb7eSMatthias Ringwald 
302*746ccb7eSMatthias Ringwald     // header
303*746ccb7eSMatthias Ringwald     sdp_response_buffer[0] = SDP_ServiceAttributeResponse;
304*746ccb7eSMatthias Ringwald     big_endian_store_16(sdp_response_buffer, 1, transaction_id);
305*746ccb7eSMatthias Ringwald     big_endian_store_16(sdp_response_buffer, 3, pos - 5);  // size of variable payload
306*746ccb7eSMatthias Ringwald     big_endian_store_16(sdp_response_buffer, 5, attributeListByteCount);
307*746ccb7eSMatthias Ringwald 
308*746ccb7eSMatthias Ringwald     return pos;
309*746ccb7eSMatthias Ringwald }
310*746ccb7eSMatthias Ringwald 
311*746ccb7eSMatthias Ringwald static uint16_t sdp_get_size_for_service_search_attribute_response(uint8_t * serviceSearchPattern, uint8_t * attributeIDList){
312*746ccb7eSMatthias Ringwald     uint16_t total_response_size = 0;
313*746ccb7eSMatthias Ringwald     btstack_linked_item_t *it;
314*746ccb7eSMatthias Ringwald     for (it = (btstack_linked_item_t *) sdp_service_records; it ; it = it->next){
315*746ccb7eSMatthias Ringwald         service_record_item_t * item = (service_record_item_t *) it;
316*746ccb7eSMatthias Ringwald 
317*746ccb7eSMatthias Ringwald         if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue;
318*746ccb7eSMatthias Ringwald 
319*746ccb7eSMatthias Ringwald         // for all service records that match
320*746ccb7eSMatthias Ringwald         total_response_size += 3 + spd_get_filtered_size(item->service_record, attributeIDList);
321*746ccb7eSMatthias Ringwald     }
322*746ccb7eSMatthias Ringwald     return total_response_size;
323*746ccb7eSMatthias Ringwald }
324*746ccb7eSMatthias Ringwald 
325*746ccb7eSMatthias Ringwald int sdp_handle_service_search_attribute_request(uint8_t * packet, uint16_t remote_mtu){
326*746ccb7eSMatthias Ringwald 
327*746ccb7eSMatthias Ringwald     // SDP header before attribute sevice list: 7
328*746ccb7eSMatthias Ringwald     // Continuation, worst case: 5
329*746ccb7eSMatthias Ringwald 
330*746ccb7eSMatthias Ringwald     // get request details
331*746ccb7eSMatthias Ringwald     uint16_t  transaction_id = big_endian_read_16(packet, 1);
332*746ccb7eSMatthias Ringwald     // not used yet - uint16_t  param_len = big_endian_read_16(packet, 3);
333*746ccb7eSMatthias Ringwald     uint8_t * serviceSearchPattern = &packet[5];
334*746ccb7eSMatthias Ringwald     uint16_t  serviceSearchPatternLen = de_get_len(serviceSearchPattern);
335*746ccb7eSMatthias Ringwald     uint16_t  maximumAttributeByteCount = big_endian_read_16(packet, 5 + serviceSearchPatternLen);
336*746ccb7eSMatthias Ringwald     uint8_t * attributeIDList = &packet[5+serviceSearchPatternLen+2];
337*746ccb7eSMatthias Ringwald     uint16_t  attributeIDListLen = de_get_len(attributeIDList);
338*746ccb7eSMatthias Ringwald     uint8_t * continuationState = &packet[5+serviceSearchPatternLen+2+attributeIDListLen];
339*746ccb7eSMatthias Ringwald 
340*746ccb7eSMatthias Ringwald     // calc maximumAttributeByteCount based on remote MTU, SDP header and reserved Continuation block
341*746ccb7eSMatthias Ringwald     uint16_t maximumAttributeByteCount2 = remote_mtu - 12;
342*746ccb7eSMatthias Ringwald     if (maximumAttributeByteCount2 < maximumAttributeByteCount) {
343*746ccb7eSMatthias Ringwald         maximumAttributeByteCount = maximumAttributeByteCount2;
344*746ccb7eSMatthias Ringwald     }
345*746ccb7eSMatthias Ringwald 
346*746ccb7eSMatthias Ringwald     // continuation state contains: index of next service record to examine
347*746ccb7eSMatthias Ringwald     // continuation state contains: byte offset into this service record
348*746ccb7eSMatthias Ringwald     uint16_t continuation_service_index = 0;
349*746ccb7eSMatthias Ringwald     uint16_t continuation_offset = 0;
350*746ccb7eSMatthias Ringwald     if (continuationState[0] == 4){
351*746ccb7eSMatthias Ringwald         continuation_service_index = big_endian_read_16(continuationState, 1);
352*746ccb7eSMatthias Ringwald         continuation_offset = big_endian_read_16(continuationState, 3);
353*746ccb7eSMatthias Ringwald     }
354*746ccb7eSMatthias Ringwald 
355*746ccb7eSMatthias Ringwald     // log_info("--> sdp_handle_service_search_attribute_request, cont %u/%u, max %u", continuation_service_index, continuation_offset, maximumAttributeByteCount);
356*746ccb7eSMatthias Ringwald 
357*746ccb7eSMatthias Ringwald     // AttributeLists - starts at offset 7
358*746ccb7eSMatthias Ringwald     uint16_t pos = 7;
359*746ccb7eSMatthias Ringwald 
360*746ccb7eSMatthias Ringwald     // add DES with total size for first request
361*746ccb7eSMatthias Ringwald     if (continuation_service_index == 0 && continuation_offset == 0){
362*746ccb7eSMatthias Ringwald         uint16_t total_response_size = sdp_get_size_for_service_search_attribute_response(serviceSearchPattern, attributeIDList);
363*746ccb7eSMatthias Ringwald         de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, total_response_size);
364*746ccb7eSMatthias Ringwald         // log_info("total response size %u", total_response_size);
365*746ccb7eSMatthias Ringwald         pos += 3;
366*746ccb7eSMatthias Ringwald         maximumAttributeByteCount -= 3;
367*746ccb7eSMatthias Ringwald     }
368*746ccb7eSMatthias Ringwald 
369*746ccb7eSMatthias Ringwald     // create attribute list
370*746ccb7eSMatthias Ringwald     int      first_answer = 1;
371*746ccb7eSMatthias Ringwald     int      continuation = 0;
372*746ccb7eSMatthias Ringwald     uint16_t current_service_index = 0;
373*746ccb7eSMatthias Ringwald     btstack_linked_item_t *it = (btstack_linked_item_t *) sdp_service_records;
374*746ccb7eSMatthias Ringwald     for ( ; it ; it = it->next, ++current_service_index){
375*746ccb7eSMatthias Ringwald         service_record_item_t * item = (service_record_item_t *) it;
376*746ccb7eSMatthias Ringwald 
377*746ccb7eSMatthias Ringwald         if (current_service_index < continuation_service_index ) continue;
378*746ccb7eSMatthias Ringwald         if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue;
379*746ccb7eSMatthias Ringwald 
380*746ccb7eSMatthias Ringwald         if (continuation_offset == 0){
381*746ccb7eSMatthias Ringwald 
382*746ccb7eSMatthias Ringwald             // get size of this record
383*746ccb7eSMatthias Ringwald             uint16_t filtered_attributes_size = spd_get_filtered_size(item->service_record, attributeIDList);
384*746ccb7eSMatthias Ringwald 
385*746ccb7eSMatthias Ringwald             // stop if complete record doesn't fits into response but we already have a partial response
386*746ccb7eSMatthias Ringwald             if ((filtered_attributes_size + 3 > maximumAttributeByteCount) && !first_answer) {
387*746ccb7eSMatthias Ringwald                 continuation = 1;
388*746ccb7eSMatthias Ringwald                 break;
389*746ccb7eSMatthias Ringwald             }
390*746ccb7eSMatthias Ringwald 
391*746ccb7eSMatthias Ringwald             // store DES
392*746ccb7eSMatthias Ringwald             de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, filtered_attributes_size);
393*746ccb7eSMatthias Ringwald             pos += 3;
394*746ccb7eSMatthias Ringwald             maximumAttributeByteCount -= 3;
395*746ccb7eSMatthias Ringwald         }
396*746ccb7eSMatthias Ringwald 
397*746ccb7eSMatthias Ringwald         first_answer = 0;
398*746ccb7eSMatthias Ringwald 
399*746ccb7eSMatthias Ringwald         // copy maximumAttributeByteCount from record
400*746ccb7eSMatthias Ringwald         uint16_t bytes_used;
401*746ccb7eSMatthias Ringwald         int complete = sdp_filter_attributes_in_attributeIDList(item->service_record, attributeIDList, continuation_offset, maximumAttributeByteCount, &bytes_used, &sdp_response_buffer[pos]);
402*746ccb7eSMatthias Ringwald         pos += bytes_used;
403*746ccb7eSMatthias Ringwald         maximumAttributeByteCount -= bytes_used;
404*746ccb7eSMatthias Ringwald 
405*746ccb7eSMatthias Ringwald         if (complete) {
406*746ccb7eSMatthias Ringwald             continuation_offset = 0;
407*746ccb7eSMatthias Ringwald             continue;
408*746ccb7eSMatthias Ringwald         }
409*746ccb7eSMatthias Ringwald 
410*746ccb7eSMatthias Ringwald         continuation = 1;
411*746ccb7eSMatthias Ringwald         continuation_offset += bytes_used;
412*746ccb7eSMatthias Ringwald         break;
413*746ccb7eSMatthias Ringwald     }
414*746ccb7eSMatthias Ringwald 
415*746ccb7eSMatthias Ringwald     uint16_t attributeListsByteCount = pos - 7;
416*746ccb7eSMatthias Ringwald 
417*746ccb7eSMatthias Ringwald     // Continuation State
418*746ccb7eSMatthias Ringwald     if (continuation){
419*746ccb7eSMatthias Ringwald         sdp_response_buffer[pos++] = 4;
420*746ccb7eSMatthias Ringwald         big_endian_store_16(sdp_response_buffer, pos, (uint16_t) current_service_index);
421*746ccb7eSMatthias Ringwald         pos += 2;
422*746ccb7eSMatthias Ringwald         big_endian_store_16(sdp_response_buffer, pos, continuation_offset);
423*746ccb7eSMatthias Ringwald         pos += 2;
424*746ccb7eSMatthias Ringwald     } else {
425*746ccb7eSMatthias Ringwald         // complete
426*746ccb7eSMatthias Ringwald         sdp_response_buffer[pos++] = 0;
427*746ccb7eSMatthias Ringwald     }
428*746ccb7eSMatthias Ringwald 
429*746ccb7eSMatthias Ringwald     // create SDP header
430*746ccb7eSMatthias Ringwald     sdp_response_buffer[0] = SDP_ServiceSearchAttributeResponse;
431*746ccb7eSMatthias Ringwald     big_endian_store_16(sdp_response_buffer, 1, transaction_id);
432*746ccb7eSMatthias Ringwald     big_endian_store_16(sdp_response_buffer, 3, pos - 5);  // size of variable payload
433*746ccb7eSMatthias Ringwald     big_endian_store_16(sdp_response_buffer, 5, attributeListsByteCount);
434*746ccb7eSMatthias Ringwald 
435*746ccb7eSMatthias Ringwald     return pos;
436*746ccb7eSMatthias Ringwald }
437*746ccb7eSMatthias Ringwald 
438*746ccb7eSMatthias Ringwald static void sdp_try_respond(void){
439*746ccb7eSMatthias Ringwald     if (!sdp_response_size ) return;
440*746ccb7eSMatthias Ringwald     if (!l2cap_cid) return;
441*746ccb7eSMatthias Ringwald     if (!l2cap_can_send_packet_now(l2cap_cid)) return;
442*746ccb7eSMatthias Ringwald 
443*746ccb7eSMatthias Ringwald     // update state before sending packet (avoid getting called when new l2cap credit gets emitted)
444*746ccb7eSMatthias Ringwald     uint16_t size = sdp_response_size;
445*746ccb7eSMatthias Ringwald     sdp_response_size = 0;
446*746ccb7eSMatthias Ringwald     l2cap_send(l2cap_cid, sdp_response_buffer, size);
447*746ccb7eSMatthias Ringwald }
448*746ccb7eSMatthias Ringwald 
449*746ccb7eSMatthias Ringwald // we assume that we don't get two requests in a row
450*746ccb7eSMatthias Ringwald static void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
451*746ccb7eSMatthias Ringwald 	uint16_t transaction_id;
452*746ccb7eSMatthias Ringwald     SDP_PDU_ID_t pdu_id;
453*746ccb7eSMatthias Ringwald     uint16_t remote_mtu;
454*746ccb7eSMatthias Ringwald     // uint16_t param_len;
455*746ccb7eSMatthias Ringwald 
456*746ccb7eSMatthias Ringwald 	switch (packet_type) {
457*746ccb7eSMatthias Ringwald 
458*746ccb7eSMatthias Ringwald 		case L2CAP_DATA_PACKET:
459*746ccb7eSMatthias Ringwald             pdu_id = (SDP_PDU_ID_t) packet[0];
460*746ccb7eSMatthias Ringwald             transaction_id = big_endian_read_16(packet, 1);
461*746ccb7eSMatthias Ringwald             // param_len = big_endian_read_16(packet, 3);
462*746ccb7eSMatthias Ringwald             remote_mtu = l2cap_get_remote_mtu_for_local_cid(channel);
463*746ccb7eSMatthias Ringwald             // account for our buffer
464*746ccb7eSMatthias Ringwald             if (remote_mtu > SDP_RESPONSE_BUFFER_SIZE){
465*746ccb7eSMatthias Ringwald                 remote_mtu = SDP_RESPONSE_BUFFER_SIZE;
466*746ccb7eSMatthias Ringwald             }
467*746ccb7eSMatthias Ringwald 
468*746ccb7eSMatthias Ringwald             // log_info("SDP Request: type %u, transaction id %u, len %u, mtu %u", pdu_id, transaction_id, param_len, remote_mtu);
469*746ccb7eSMatthias Ringwald             switch (pdu_id){
470*746ccb7eSMatthias Ringwald 
471*746ccb7eSMatthias Ringwald                 case SDP_ServiceSearchRequest:
472*746ccb7eSMatthias Ringwald                     sdp_response_size = sdp_handle_service_search_request(packet, remote_mtu);
473*746ccb7eSMatthias Ringwald                     break;
474*746ccb7eSMatthias Ringwald 
475*746ccb7eSMatthias Ringwald                 case SDP_ServiceAttributeRequest:
476*746ccb7eSMatthias Ringwald                     sdp_response_size = sdp_handle_service_attribute_request(packet, remote_mtu);
477*746ccb7eSMatthias Ringwald                     break;
478*746ccb7eSMatthias Ringwald 
479*746ccb7eSMatthias Ringwald                 case SDP_ServiceSearchAttributeRequest:
480*746ccb7eSMatthias Ringwald                     sdp_response_size = sdp_handle_service_search_attribute_request(packet, remote_mtu);
481*746ccb7eSMatthias Ringwald                     break;
482*746ccb7eSMatthias Ringwald 
483*746ccb7eSMatthias Ringwald                 default:
484*746ccb7eSMatthias Ringwald                     sdp_response_size = sdp_create_error_response(transaction_id, 0x0003); // invalid syntax
485*746ccb7eSMatthias Ringwald                     break;
486*746ccb7eSMatthias Ringwald             }
487*746ccb7eSMatthias Ringwald 
488*746ccb7eSMatthias Ringwald             sdp_try_respond();
489*746ccb7eSMatthias Ringwald 
490*746ccb7eSMatthias Ringwald 			break;
491*746ccb7eSMatthias Ringwald 
492*746ccb7eSMatthias Ringwald 		case HCI_EVENT_PACKET:
493*746ccb7eSMatthias Ringwald 
494*746ccb7eSMatthias Ringwald 			switch (packet[0]) {
495*746ccb7eSMatthias Ringwald 
496*746ccb7eSMatthias Ringwald 				case L2CAP_EVENT_INCOMING_CONNECTION:
497*746ccb7eSMatthias Ringwald                     if (l2cap_cid) {
498*746ccb7eSMatthias Ringwald                         // CONNECTION REJECTED DUE TO LIMITED RESOURCES
499*746ccb7eSMatthias Ringwald                         l2cap_decline_connection(channel, 0x04);
500*746ccb7eSMatthias Ringwald                         break;
501*746ccb7eSMatthias Ringwald                     }
502*746ccb7eSMatthias Ringwald                     // accept
503*746ccb7eSMatthias Ringwald                     l2cap_cid = channel;
504*746ccb7eSMatthias Ringwald                     sdp_response_size = 0;
505*746ccb7eSMatthias Ringwald                     l2cap_accept_connection(channel);
506*746ccb7eSMatthias Ringwald 					break;
507*746ccb7eSMatthias Ringwald 
508*746ccb7eSMatthias Ringwald                 case L2CAP_EVENT_CHANNEL_OPENED:
509*746ccb7eSMatthias Ringwald                     if (packet[2]) {
510*746ccb7eSMatthias Ringwald                         // open failed -> reset
511*746ccb7eSMatthias Ringwald                         l2cap_cid = 0;
512*746ccb7eSMatthias Ringwald                     }
513*746ccb7eSMatthias Ringwald                     break;
514*746ccb7eSMatthias Ringwald 
515*746ccb7eSMatthias Ringwald                 case L2CAP_EVENT_CAN_SEND_NOW:
516*746ccb7eSMatthias Ringwald                     sdp_try_respond();
517*746ccb7eSMatthias Ringwald                     break;
518*746ccb7eSMatthias Ringwald 
519*746ccb7eSMatthias Ringwald                 case L2CAP_EVENT_CHANNEL_CLOSED:
520*746ccb7eSMatthias Ringwald                     if (channel == l2cap_cid){
521*746ccb7eSMatthias Ringwald                         // reset
522*746ccb7eSMatthias Ringwald                         l2cap_cid = 0;
523*746ccb7eSMatthias Ringwald                     }
524*746ccb7eSMatthias Ringwald                     break;
525*746ccb7eSMatthias Ringwald 
526*746ccb7eSMatthias Ringwald 				default:
527*746ccb7eSMatthias Ringwald 					// other event
528*746ccb7eSMatthias Ringwald 					break;
529*746ccb7eSMatthias Ringwald 			}
530*746ccb7eSMatthias Ringwald 			break;
531*746ccb7eSMatthias Ringwald 
532*746ccb7eSMatthias Ringwald 		default:
533*746ccb7eSMatthias Ringwald 			// other packet type
534*746ccb7eSMatthias Ringwald 			break;
535*746ccb7eSMatthias Ringwald 	}
536*746ccb7eSMatthias Ringwald }
537*746ccb7eSMatthias Ringwald 
538