]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bluetooth/bthidcontrol/sdp.c
Import the skein hashing algorithm, based on the threefish block cipher
[FreeBSD/FreeBSD.git] / usr.sbin / bluetooth / bthidcontrol / sdp.c
1 /*
2  * sdp.c
3  *
4  * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $Id: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $
29  * $FreeBSD$
30  */
31
32 #include <sys/queue.h>
33 #define L2CAP_SOCKET_CHECKED
34 #include <bluetooth.h>
35 #include <dev/usb/usb.h>
36 #include <dev/usb/usbhid.h>
37 #include <errno.h>
38 #include <sdp.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <usbhid.h>
42 #include "bthid_config.h"
43 #include "bthidcontrol.h"
44
45 static int32_t hid_sdp_query                            (bdaddr_t const *local, struct hid_device *hd, int32_t *error);
46 static int32_t hid_sdp_parse_protocol_descriptor_list   (sdp_attr_p a);
47 static int32_t hid_sdp_parse_hid_descriptor             (sdp_attr_p a);
48 static int32_t hid_sdp_parse_boolean                    (sdp_attr_p a);
49
50 static uint16_t         service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
51
52 static uint32_t         attrs[] = {
53 SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
54                 SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
55 SDP_ATTR_RANGE  (SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
56                 SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
57 SDP_ATTR_RANGE( 0x0205,         /* HIDReconnectInitiate */
58                 0x0205),
59 SDP_ATTR_RANGE( 0x0206,         /* HIDDescriptorList */
60                 0x0206),
61 SDP_ATTR_RANGE( 0x0209,         /* HIDBatteryPower */
62                 0x0209),
63 SDP_ATTR_RANGE( 0x020d,         /* HIDNormallyConnectable */
64                 0x020d)
65         };
66 #define nattrs  (sizeof(attrs)/sizeof(attrs[0]))
67
68 static sdp_attr_t       values[8];
69 #define nvalues (sizeof(values)/sizeof(values[0]))
70
71 static uint8_t          buffer[nvalues][512];
72
73 /*
74  * Query remote device
75  */
76
77 #undef  hid_sdp_query_exit
78 #define hid_sdp_query_exit(e) {         \
79         if (error != NULL)              \
80                 *error = (e);           \
81         if (ss != NULL) {               \
82                 sdp_close(ss);          \
83                 ss = NULL;              \
84         }                               \
85         return (((e) == 0)? 0 : -1);    \
86 }
87
88 static int32_t
89 hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
90 {
91         void    *ss = NULL;
92         uint8_t *hid_descriptor = NULL;
93         int32_t  i, control_psm = -1, interrupt_psm = -1,
94                  reconnect_initiate = -1,
95                  normally_connectable = 0, battery_power = 0,
96                  hid_descriptor_length = -1;
97
98         if (local == NULL)
99                 local = NG_HCI_BDADDR_ANY;
100         if (hd == NULL)
101                 hid_sdp_query_exit(EINVAL);
102
103         for (i = 0; i < nvalues; i ++) {
104                 values[i].flags = SDP_ATTR_INVALID;
105                 values[i].attr = 0;
106                 values[i].vlen = sizeof(buffer[i]);
107                 values[i].value = buffer[i];
108         }
109
110         if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
111                 hid_sdp_query_exit(ENOMEM);
112         if (sdp_error(ss) != 0)
113                 hid_sdp_query_exit(sdp_error(ss));
114         if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
115                 hid_sdp_query_exit(sdp_error(ss));
116
117         sdp_close(ss);
118         ss = NULL;
119
120         for (i = 0; i < nvalues; i ++) {
121                 if (values[i].flags != SDP_ATTR_OK)
122                         continue;
123
124                 switch (values[i].attr) {
125                 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
126                         control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
127                         break;
128
129                 case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
130                         interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
131                         break;
132
133                 case 0x0205: /* HIDReconnectInitiate */
134                         reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
135                         break;
136
137                 case 0x0206: /* HIDDescriptorList */
138                         if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
139                                 hid_descriptor = values[i].value;
140                                 hid_descriptor_length = values[i].vlen;
141                         }
142                         break;
143
144                 case 0x0209: /* HIDBatteryPower */
145                         battery_power = hid_sdp_parse_boolean(&values[i]);
146                         break;
147
148                 case 0x020d: /* HIDNormallyConnectable */
149                         normally_connectable = hid_sdp_parse_boolean(&values[i]);
150                         break;
151                 }
152         }
153
154         if (control_psm == -1 || interrupt_psm == -1 ||
155             reconnect_initiate == -1 ||
156             hid_descriptor == NULL || hid_descriptor_length == -1)
157                 hid_sdp_query_exit(ENOATTR);
158
159         hd->control_psm = control_psm;
160         hd->interrupt_psm = interrupt_psm;
161         hd->reconnect_initiate = reconnect_initiate? 1 : 0;
162         hd->battery_power = battery_power? 1 : 0;
163         hd->normally_connectable = normally_connectable? 1 : 0;
164         hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);
165         if (hd->desc == NULL)
166                 hid_sdp_query_exit(ENOMEM);
167
168         return (0);
169 }
170
171 /*
172  * seq len                              2
173  *      seq len                         2
174  *              uuid value              3
175  *              uint16 value            3
176  *              seq len                 2
177  *                      uuid value      3
178  */
179
180 static int32_t
181 hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)
182 {
183         uint8_t *ptr = a->value;
184         uint8_t *end = a->value + a->vlen;
185         int32_t  type, len, uuid, psm;
186
187         if (end - ptr < 15)
188                 return (-1);
189
190         if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
191                 SDP_GET8(type, ptr);
192                 switch (type) {
193                 case SDP_DATA_SEQ8:
194                         SDP_GET8(len, ptr);
195                         break;
196
197                 case SDP_DATA_SEQ16:
198                         SDP_GET16(len, ptr);
199                         break;
200
201                 case SDP_DATA_SEQ32:
202                         SDP_GET32(len, ptr);
203                         break;
204
205                 default:
206                         return (-1);
207                 }
208                 if (ptr + len > end)
209                         return (-1);
210         }
211
212         SDP_GET8(type, ptr);
213         switch (type) {
214         case SDP_DATA_SEQ8:
215                 SDP_GET8(len, ptr);
216                 break;
217
218         case SDP_DATA_SEQ16:
219                 SDP_GET16(len, ptr);
220                 break;
221
222         case SDP_DATA_SEQ32:
223                 SDP_GET32(len, ptr);
224                 break;
225
226         default:
227                 return (-1);
228         }
229         if (ptr + len > end)
230                 return (-1);
231
232         /* Protocol */
233         SDP_GET8(type, ptr);
234         switch (type) {
235         case SDP_DATA_SEQ8:
236                 SDP_GET8(len, ptr);
237                 break;
238
239         case SDP_DATA_SEQ16:
240                 SDP_GET16(len, ptr);
241                 break;
242
243         case SDP_DATA_SEQ32:
244                 SDP_GET32(len, ptr);
245                 break;
246
247         default:
248                 return (-1);
249         }
250         if (ptr + len > end)
251                 return (-1);
252
253         /* UUID */
254         if (ptr + 3 > end)
255                 return (-1);
256         SDP_GET8(type, ptr);
257         switch (type) {
258         case SDP_DATA_UUID16:
259                 SDP_GET16(uuid, ptr);
260                 if (uuid != SDP_UUID_PROTOCOL_L2CAP)
261                         return (-1);
262                 break;
263
264         case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
265         case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
266         default:
267                 return (-1);
268         }
269
270         /* PSM */
271         if (ptr + 3 > end)
272                 return (-1);
273         SDP_GET8(type, ptr);
274         if (type != SDP_DATA_UINT16)
275                 return (-1);
276         SDP_GET16(psm, ptr);
277
278         return (psm);
279 }
280
281 /*
282  * seq len                      2
283  *      seq len                 2
284  *              uint8 value8    2
285  *              str value       3
286  */
287
288 static int32_t
289 hid_sdp_parse_hid_descriptor(sdp_attr_p a)
290 {
291         uint8_t *ptr = a->value;
292         uint8_t *end = a->value + a->vlen;
293         int32_t  type, len, descriptor_type;
294
295         if (end - ptr < 9)
296                 return (-1);
297
298         SDP_GET8(type, ptr);
299         switch (type) {
300         case SDP_DATA_SEQ8:
301                 SDP_GET8(len, ptr);
302                 break;
303
304         case SDP_DATA_SEQ16:
305                 SDP_GET16(len, ptr);
306                 break;
307
308         case SDP_DATA_SEQ32:
309                 SDP_GET32(len, ptr);
310                 break;
311
312         default:
313                 return (-1);
314         }
315         if (ptr + len > end)
316                 return (-1);
317
318         while (ptr < end) {
319                 /* Descriptor */
320                 SDP_GET8(type, ptr);
321                 switch (type) {
322                 case SDP_DATA_SEQ8:
323                         if (ptr + 1 > end)
324                                 return (-1);
325                         SDP_GET8(len, ptr);
326                         break;
327
328                 case SDP_DATA_SEQ16:
329                         if (ptr + 2 > end)
330                                 return (-1);
331                         SDP_GET16(len, ptr);
332                         break;
333
334                 case SDP_DATA_SEQ32:
335                         if (ptr + 4 > end)
336                                 return (-1);
337                         SDP_GET32(len, ptr);
338                         break;
339
340                 default:
341                         return (-1);
342                 }
343
344                 /* Descripor type */
345                 if (ptr + 1 > end)
346                         return (-1);
347                 SDP_GET8(type, ptr);
348                 if (type != SDP_DATA_UINT8 || ptr + 1 > end)
349                         return (-1);
350                 SDP_GET8(descriptor_type, ptr);
351
352                 /* Descriptor value */
353                 if (ptr + 1 > end)
354                         return (-1);
355                 SDP_GET8(type, ptr);
356                 switch (type) {
357                 case SDP_DATA_STR8:
358                         if (ptr + 1 > end)
359                                 return (-1);
360                         SDP_GET8(len, ptr);
361                         break;
362
363                 case SDP_DATA_STR16:
364                         if (ptr + 2 > end)
365                                 return (-1);
366                         SDP_GET16(len, ptr);
367                         break;
368
369                 case SDP_DATA_STR32:
370                         if (ptr + 4 > end)
371                                 return (-1);
372                         SDP_GET32(len, ptr);
373                         break;
374
375                 default:
376                         return (-1);
377                 }
378                 if (ptr + len > end)
379                         return (-1);
380
381                 if (descriptor_type == UDESC_REPORT && len > 0) {
382                         a->value = ptr;
383                         a->vlen = len;
384
385                         return (0);
386                 }
387
388                 ptr += len;
389         }
390
391         return (-1);
392 }
393
394 /* bool8 int8 */
395 static int32_t
396 hid_sdp_parse_boolean(sdp_attr_p a)
397 {
398         if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
399                 return (-1);
400
401         return (a->value[1]);
402 }
403
404 /* Perform SDP query */
405 static int32_t
406 hid_query(bdaddr_t *bdaddr, int argc, char **argv)
407 {
408         struct hid_device       hd;
409         int                     e;
410
411         memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr));
412         if (hid_sdp_query(NULL, &hd, &e) < 0) {
413                 fprintf(stderr, "Could not perform SDP query on the " \
414                         "device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL),
415                         strerror(e), e);
416                 return (FAILED);
417         }
418
419         print_hid_device(&hd, stdout);
420
421         return (OK);
422 }
423
424 struct bthid_command    sdp_commands[] =
425 {
426 {
427 "Query",
428 "Perform SDP query to the specified device and print HID configuration entry\n"\
429 "for the device. The configuration entry should be appended to the Bluetooth\n"\
430 "HID daemon configuration file and the daemon should be restarted.\n",
431 hid_query
432 },
433 { NULL, NULL, NULL }
434 };
435