xref: /btstack/src/classic/sdp_client.c (revision 8caefee39d444df6d8908a96a844825f10fbdaa4)
1 /*
2  * Copyright (C) 2014 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 /*
39  *  sdp_client.c
40  */
41 
42 #include "btstack-config.h"
43 #include "sdp_client.h"
44 
45 #include "hci_cmds.h"
46 
47 #include "l2cap.h"
48 #include "sdp_parser.h"
49 #include "sdp.h"
50 #include "debug.h"
51 
52 typedef enum {
53     INIT, W4_CONNECT, W2_SEND, W4_RESPONSE, QUERY_COMPLETE
54 } sdp_client_state_t;
55 
56 
57 void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
58 
59 static uint16_t setup_service_search_attribute_request(uint8_t * data);
60 
61 #ifdef HAVE_SDP_EXTRA_QUERIES
62 static uint16_t setup_service_search_request(uint8_t * data);
63 static uint16_t setup_service_attribute_request(uint8_t * data);
64 static void     parse_service_search_response(uint8_t* packet);
65 static void     parse_service_attribute_response(uint8_t* packet);
66 static uint32_t serviceRecordHandle;
67 #endif
68 
69 // SDP Client Query
70 static uint16_t  mtu;
71 static uint16_t  sdp_cid = 0x40;
72 static uint8_t * serviceSearchPattern;
73 static uint8_t * attributeIDList;
74 static uint16_t  transactionID = 0;
75 static uint8_t   continuationState[16];
76 static uint8_t   continuationStateLen;
77 static sdp_client_state_t sdp_client_state = INIT;
78 static SDP_PDU_ID_t PDU_ID = SDP_Invalid;
79 
80 // TODO: inline if not needed (des(des))
81 static void parse_attribute_lists(uint8_t* packet, uint16_t length){
82     sdp_parser_handle_chunk(packet, length);
83 }
84 
85 /* Queries the SDP service of the remote device given a service search pattern
86 and a list of attribute IDs. The remote data is handled by the SDP parser. The
87 SDP parser delivers attribute values and done event via a registered callback. */
88 
89 void sdp_client_query(bd_addr_t remote, uint8_t * des_serviceSearchPattern, uint8_t * des_attributeIDList){
90     serviceSearchPattern = des_serviceSearchPattern;
91     attributeIDList = des_attributeIDList;
92     continuationStateLen = 0;
93     PDU_ID = SDP_ServiceSearchAttributeResponse;
94 
95     sdp_client_state = W4_CONNECT;
96     l2cap_create_channel_internal(NULL, sdp_packet_handler, remote, PSM_SDP, l2cap_max_mtu());
97 }
98 
99 static int can_send_now(uint16_t channel){
100     if (sdp_client_state != W2_SEND) return 0;
101     if (!l2cap_can_send_packet_now(channel)) return 0;
102     return 1;
103 }
104 
105 static void send_request(uint16_t channel){
106     l2cap_reserve_packet_buffer();
107     uint8_t * data = l2cap_get_outgoing_buffer();
108     uint16_t request_len = 0;
109 
110     switch (PDU_ID){
111 #ifdef HAVE_SDP_EXTRA_QUERIES
112         case SDP_ServiceSearchResponse:
113             request_len = setup_service_search_request(data);
114             break;
115         case SDP_ServiceAttributeResponse:
116             request_len = setup_service_attribute_request(data);
117             break;
118 #endif
119         case SDP_ServiceSearchAttributeResponse:
120             request_len = setup_service_search_attribute_request(data);
121             break;
122         default:
123             log_error("SDP Client send_request :: PDU ID invalid. %u", PDU_ID);
124             return;
125     }
126 
127     // prevent re-entrance
128     sdp_client_state = W4_RESPONSE;
129     int err = l2cap_send_prepared(channel, request_len);
130     // l2cap_send_prepared shouldn't have failed as l2ap_can_send_packet_now() was true
131     switch (err){
132         case 0:
133             log_debug("l2cap_send_internal() -> OK");
134             PDU_ID = SDP_Invalid;
135             break;
136         case BTSTACK_ACL_BUFFERS_FULL:
137             sdp_client_state = W2_SEND;
138             log_info("l2cap_send_internal() ->BTSTACK_ACL_BUFFERS_FULL");
139             break;
140         default:
141             sdp_client_state = W2_SEND;
142             log_error("l2cap_send_internal() -> err %d", err);
143             break;
144     }
145 }
146 
147 
148 static void parse_service_search_attribute_response(uint8_t* packet){
149     uint16_t offset = 3;
150     uint16_t parameterLength = READ_NET_16(packet,offset);
151     offset+=2;
152     // AttributeListByteCount <= mtu
153     uint16_t attributeListByteCount = READ_NET_16(packet,offset);
154     offset+=2;
155 
156     if (attributeListByteCount > mtu){
157         log_error("Error parsing ServiceSearchAttributeResponse: Number of bytes in found attribute list is larger then the MaximumAttributeByteCount.");
158         return;
159     }
160 
161     // AttributeLists
162     parse_attribute_lists(packet+offset, attributeListByteCount);
163     offset+=attributeListByteCount;
164 
165     continuationStateLen = packet[offset];
166     offset++;
167 
168     if (continuationStateLen > 16){
169         log_error("Error parsing ServiceSearchAttributeResponse: Number of bytes in continuation state exceedes 16.");
170         return;
171     }
172     memcpy(continuationState, packet+offset, continuationStateLen);
173     offset+=continuationStateLen;
174 
175     if (parameterLength != offset - 5){
176         log_error("Error parsing ServiceSearchAttributeResponse: wrong size of parameters, number of expected bytes%u, actual number %u.", parameterLength, offset);
177     }
178 }
179 
180 void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
181     // uint16_t handle;
182     if (packet_type == L2CAP_DATA_PACKET){
183         uint16_t responseTransactionID = READ_NET_16(packet,1);
184         if ( responseTransactionID != transactionID){
185             log_error("Missmatching transaction ID, expected %u, found %u.", transactionID, responseTransactionID);
186             return;
187         }
188 
189         if (packet[0] != SDP_ServiceSearchAttributeResponse
190             && packet[0] != SDP_ServiceSearchResponse
191             && packet[0] != SDP_ServiceAttributeResponse){
192             log_error("Not a valid PDU ID, expected %u, %u or %u, found %u.", SDP_ServiceSearchResponse,
193                                     SDP_ServiceAttributeResponse, SDP_ServiceSearchAttributeResponse, packet[0]);
194             return;
195         }
196 
197         PDU_ID = (SDP_PDU_ID_t)packet[0];
198         log_info("SDP Client :: PDU ID. %u ,%u", PDU_ID, packet[0]);
199         switch (PDU_ID){
200 #ifdef HAVE_SDP_EXTRA_QUERIES
201             case SDP_ServiceSearchResponse:
202                 parse_service_search_response(packet);
203                 break;
204             case SDP_ServiceAttributeResponse:
205                 parse_service_attribute_response(packet);
206                 break;
207 #endif
208             case SDP_ServiceSearchAttributeResponse:
209                 parse_service_search_attribute_response(packet);
210                 break;
211             default:
212                 log_error("SDP Client :: PDU ID invalid. %u ,%u", PDU_ID, packet[0]);
213                 return;
214         }
215 
216         // continuation set or DONE?
217         if (continuationStateLen == 0){
218             log_info("SDP Client Query DONE! ");
219             sdp_client_state = QUERY_COMPLETE;
220             l2cap_disconnect_internal(sdp_cid, 0);
221             // sdp_parser_handle_done(0);
222             return;
223         }
224         // prepare next request and send
225         sdp_client_state = W2_SEND;
226         if (can_send_now(sdp_cid)) send_request(sdp_cid);
227         return;
228     }
229 
230     if (packet_type != HCI_EVENT_PACKET) return;
231 
232     switch(packet[0]){
233         case L2CAP_EVENT_TIMEOUT_CHECK:
234             log_info("sdp client: L2CAP_EVENT_TIMEOUT_CHECK");
235             break;
236         case L2CAP_EVENT_CHANNEL_OPENED:
237             if (sdp_client_state != W4_CONNECT) break;
238             // data: event (8), len(8), status (8), address(48), handle (16), psm (16), local_cid(16), remote_cid (16), local_mtu(16), remote_mtu(16)
239             if (packet[2]) {
240                 log_error("SDP Client Connection failed.");
241                 sdp_parser_handle_done(packet[2]);
242                 break;
243             }
244             sdp_cid = channel;
245             mtu = READ_BT_16(packet, 17);
246             // handle = READ_BT_16(packet, 9);
247             log_info("SDP Client Connected, cid %x, mtu %u.", sdp_cid, mtu);
248 
249             sdp_client_state = W2_SEND;
250             if (can_send_now(sdp_cid)) send_request(sdp_cid);
251 
252             break;
253         case L2CAP_EVENT_CREDITS:
254         case DAEMON_EVENT_HCI_PACKET_SENT:
255             if (can_send_now(sdp_cid)) send_request(sdp_cid);
256             break;
257         case L2CAP_EVENT_CHANNEL_CLOSED: {
258             if (sdp_cid != READ_BT_16(packet, 2)) {
259                 // log_info("Received L2CAP_EVENT_CHANNEL_CLOSED for cid %x, current cid %x\n",  READ_BT_16(packet, 2),sdp_cid);
260                 break;
261             }
262             log_info("SDP Client disconnected.");
263             uint8_t status = sdp_client_state == QUERY_COMPLETE ? 0 : SDP_QUERY_INCOMPLETE;
264             sdp_client_state = INIT;
265             sdp_parser_handle_done(status);
266             break;
267         }
268         default:
269             break;
270     }
271 }
272 
273 
274 static uint16_t setup_service_search_attribute_request(uint8_t * data){
275 
276     uint16_t offset = 0;
277     transactionID++;
278     // uint8_t SDP_PDU_ID_t.SDP_ServiceSearchRequest;
279     data[offset++] = SDP_ServiceSearchAttributeRequest;
280     // uint16_t transactionID
281     net_store_16(data, offset, transactionID);
282     offset += 2;
283 
284     // param legnth
285     offset += 2;
286 
287     // parameters:
288     //     ServiceSearchPattern - DES (min 1 UUID, max 12)
289     uint16_t serviceSearchPatternLen = de_get_len(serviceSearchPattern);
290     memcpy(data + offset, serviceSearchPattern, serviceSearchPatternLen);
291     offset += serviceSearchPatternLen;
292 
293     //     MaximumAttributeByteCount - uint16_t  0x0007 - 0xffff -> mtu
294     net_store_16(data, offset, mtu);
295     offset += 2;
296 
297     //     AttibuteIDList
298     uint16_t attributeIDListLen = de_get_len(attributeIDList);
299     memcpy(data + offset, attributeIDList, attributeIDListLen);
300     offset += attributeIDListLen;
301 
302     //     ContinuationState - uint8_t number of cont. bytes N<=16
303     data[offset++] = continuationStateLen;
304     //                       - N-bytes previous response from server
305     memcpy(data + offset, continuationState, continuationStateLen);
306     offset += continuationStateLen;
307 
308     // uint16_t paramLength
309     net_store_16(data, 3, offset - 5);
310 
311     return offset;
312 }
313 
314 #ifdef HAVE_SDP_EXTRA_QUERIES
315 void parse_service_record_handle_list(uint8_t* packet, uint16_t total_count, uint16_t current_count){
316     sdp_parser_handle_service_search(packet, total_count, current_count);
317 }
318 
319 static uint16_t setup_service_search_request(uint8_t * data){
320     uint16_t offset = 0;
321     transactionID++;
322     // uint8_t SDP_PDU_ID_t.SDP_ServiceSearchRequest;
323     data[offset++] = SDP_ServiceSearchRequest;
324     // uint16_t transactionID
325     net_store_16(data, offset, transactionID);
326     offset += 2;
327 
328     // param legnth
329     offset += 2;
330 
331     // parameters:
332     //     ServiceSearchPattern - DES (min 1 UUID, max 12)
333     uint16_t serviceSearchPatternLen = de_get_len(serviceSearchPattern);
334     memcpy(data + offset, serviceSearchPattern, serviceSearchPatternLen);
335     offset += serviceSearchPatternLen;
336 
337     //     MaximumAttributeByteCount - uint16_t  0x0007 - 0xffff -> mtu
338     net_store_16(data, offset, mtu);
339     offset += 2;
340 
341     //     ContinuationState - uint8_t number of cont. bytes N<=16
342     data[offset++] = continuationStateLen;
343     //                       - N-bytes previous response from server
344     memcpy(data + offset, continuationState, continuationStateLen);
345     offset += continuationStateLen;
346 
347     // uint16_t paramLength
348     net_store_16(data, 3, offset - 5);
349 
350     return offset;
351 }
352 
353 
354 static uint16_t setup_service_attribute_request(uint8_t * data){
355 
356     uint16_t offset = 0;
357     transactionID++;
358     // uint8_t SDP_PDU_ID_t.SDP_ServiceSearchRequest;
359     data[offset++] = SDP_ServiceAttributeRequest;
360     // uint16_t transactionID
361     net_store_16(data, offset, transactionID);
362     offset += 2;
363 
364     // param legnth
365     offset += 2;
366 
367     // parameters:
368     //     ServiceRecordHandle
369     net_store_32(data, offset, serviceRecordHandle);
370     offset += 4;
371 
372     //     MaximumAttributeByteCount - uint16_t  0x0007 - 0xffff -> mtu
373     net_store_16(data, offset, mtu);
374     offset += 2;
375 
376     //     AttibuteIDList
377     uint16_t attributeIDListLen = de_get_len(attributeIDList);
378     memcpy(data + offset, attributeIDList, attributeIDListLen);
379     offset += attributeIDListLen;
380 
381     //     ContinuationState - uint8_t number of cont. bytes N<=16
382     data[offset++] = continuationStateLen;
383     //                       - N-bytes previous response from server
384     memcpy(data + offset, continuationState, continuationStateLen);
385     offset += continuationStateLen;
386 
387     // uint16_t paramLength
388     net_store_16(data, 3, offset - 5);
389 
390     return offset;
391 }
392 
393 static void parse_service_search_response(uint8_t* packet){
394     uint16_t offset = 3;
395     uint16_t parameterLength = READ_NET_16(packet,offset);
396     offset+=2;
397 
398     uint16_t totalServiceRecordCount = READ_NET_16(packet,offset);
399     offset+=2;
400 
401     uint16_t currentServiceRecordCount = READ_NET_16(packet,offset);
402     offset+=2;
403     if (currentServiceRecordCount > totalServiceRecordCount){
404         log_error("CurrentServiceRecordCount is larger then TotalServiceRecordCount.");
405         return;
406     }
407 
408     parse_service_record_handle_list(packet+offset, totalServiceRecordCount, currentServiceRecordCount);
409     offset+=(currentServiceRecordCount * 4);
410 
411     continuationStateLen = packet[offset];
412     offset++;
413     if (continuationStateLen > 16){
414         log_error("Error parsing ServiceSearchResponse: Number of bytes in continuation state exceedes 16.");
415         return;
416     }
417     memcpy(continuationState, packet+offset, continuationStateLen);
418     offset+=continuationStateLen;
419 
420     if (parameterLength != offset - 5){
421         log_error("Error parsing ServiceSearchResponse: wrong size of parameters, number of expected bytes%u, actual number %u.", parameterLength, offset);
422     }
423 }
424 
425 static void parse_service_attribute_response(uint8_t* packet){
426     uint16_t offset = 3;
427     uint16_t parameterLength = READ_NET_16(packet,offset);
428     offset+=2;
429 
430     // AttributeListByteCount <= mtu
431     uint16_t attributeListByteCount = READ_NET_16(packet,offset);
432     offset+=2;
433 
434     if (attributeListByteCount > mtu){
435         log_error("Error parsing ServiceSearchAttributeResponse: Number of bytes in found attribute list is larger then the MaximumAttributeByteCount.");
436         return;
437     }
438 
439     // AttributeLists
440     parse_attribute_lists(packet+offset, attributeListByteCount);
441     offset+=attributeListByteCount;
442 
443     continuationStateLen = packet[offset];
444     offset++;
445 
446     if (continuationStateLen > 16){
447         log_error("Error parsing ServiceAttributeResponse: Number of bytes in continuation state exceedes 16.");
448         return;
449     }
450     memcpy(continuationState, packet+offset, continuationStateLen);
451     offset+=continuationStateLen;
452 
453     if (parameterLength != offset - 5){
454         log_error("Error parsing ServiceAttributeResponse: wrong size of parameters, number of expected bytes%u, actual number %u.", parameterLength, offset);
455     }
456 }
457 
458 void sdp_client_service_attribute_search(bd_addr_t remote, uint32_t search_serviceRecordHandle, uint8_t * des_attributeIDList){
459     serviceRecordHandle = search_serviceRecordHandle;
460     attributeIDList = des_attributeIDList;
461     continuationStateLen = 0;
462     PDU_ID = SDP_ServiceAttributeResponse;
463 
464     sdp_client_state = W4_CONNECT;
465     l2cap_create_channel_internal(NULL, sdp_packet_handler, remote, PSM_SDP, l2cap_max_mtu());
466 }
467 
468 void sdp_client_service_search(bd_addr_t remote, uint8_t * des_serviceSearchPattern){
469     serviceSearchPattern = des_serviceSearchPattern;
470     continuationStateLen = 0;
471     PDU_ID = SDP_ServiceSearchResponse;
472 
473     sdp_client_state = W4_CONNECT;
474     l2cap_create_channel_internal(NULL, sdp_packet_handler, remote, PSM_SDP, l2cap_max_mtu());
475 }
476 #endif
477 
478