Add sdpd(8) (Bluetooth Service Discovery Protocol daemon) and sdpquery(1)
[dragonfly.git] / usr.sbin / sdpquery / search.c
1 /* $NetBSD: search.c,v 1.6 2007/11/06 21:35:52 plunky Exp $ */
2 /* $DragonFly: src/usr.sbin/sdpquery/Attic/search.c,v 1.1 2008/01/06 21:51:30 hasso Exp $ */
3
4 /*-
5  * Copyright (c) 2006 Itronix Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of Itronix Inc. may not be used to endorse
17  *    or promote products derived from this software without specific
18  *    prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
24  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27  * ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 /*
33  * search.c
34  *
35  * Copyright (c) 2001-2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
36  * All rights reserved.
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions
40  * are met:
41  * 1. Redistributions of source code must retain the above copyright
42  *    notice, this list of conditions and the following disclaimer.
43  * 2. Redistributions in binary form must reproduce the above copyright
44  *    notice, this list of conditions and the following disclaimer in the
45  *    documentation and/or other materials provided with the distribution.
46  *
47  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
48  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
51  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57  * SUCH DAMAGE.
58  *
59  * $Id: search.c,v 1.6 2007/11/06 21:35:52 plunky Exp $
60  * $FreeBSD: src/usr.sbin/bluetooth/sdpcontrol/search.c,v 1.4 2005/05/27 19:11:33 emax Exp $
61  */
62
63 #include <netinet/in.h>
64 #include <bluetooth.h>
65 #include <err.h>
66 #include <errno.h>
67 #include <ctype.h>
68 #include <sdp.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71
72 #include "sdpquery.h"
73
74 /* List of the attributes we are looking for */
75 static uint32_t attrs[] =
76 {
77         SDP_ATTR_RANGE( SDP_ATTR_SERVICE_RECORD_HANDLE,
78                         SDP_ATTR_SERVICE_RECORD_HANDLE),
79         SDP_ATTR_RANGE( SDP_ATTR_SERVICE_CLASS_ID_LIST,
80                         SDP_ATTR_SERVICE_CLASS_ID_LIST),
81         SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
82                         SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
83         SDP_ATTR_RANGE( SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST,
84                         SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST)
85 };
86 #define attrs_len       (sizeof(attrs)/sizeof(attrs[0]))
87
88 /* Buffer for the attributes */
89 #define NRECS   25      /* request this much records from the SDP server */
90 #define BSIZE   256     /* one attribute buffer size */
91 static uint8_t          buffer[NRECS * attrs_len][BSIZE];
92
93 /* SDP attributes */
94 static sdp_attr_t       values[NRECS * attrs_len];
95 #define values_len      (sizeof(values)/sizeof(values[0]))
96
97 /*
98  * Print Service Class ID List
99  *
100  * The ServiceClassIDList attribute consists of a data element sequence in
101  * which each data element is a UUID representing the service classes that
102  * a given service record conforms to. The UUIDs are listed in order from
103  * the most specific class to the most general class. The ServiceClassIDList
104  * must contain at least one service class UUID.
105  */
106
107 static void
108 print_service_class_id_list(uint8_t const *start, uint8_t const *end)
109 {
110         uint32_t        type, len, value;
111
112         if (end - start < 2) {
113                 fprintf(stderr, "Invalid Service Class ID List. " \
114                                 "Too short, len=%zd\n", end - start);
115                 return;
116         }
117
118         SDP_GET8(type, start);
119         switch (type) {
120         case SDP_DATA_SEQ8:
121                 SDP_GET8(len, start);
122                 break;
123
124         case SDP_DATA_SEQ16:
125                 SDP_GET16(len, start);
126                 break;
127
128         case SDP_DATA_SEQ32:
129                 SDP_GET32(len, start);
130                 break;
131
132         default:
133                 fprintf(stderr, "Invalid Service Class ID List. " \
134                                 "Not a sequence, type=%#x\n", type);
135                 return;
136                 /* NOT REACHED */
137         }
138
139         while (start < end) {
140                 SDP_GET8(type, start);
141                 switch (type) {
142                 case SDP_DATA_UUID16:
143                         SDP_GET16(value, start);
144                         fprintf(stdout, "\t%s (%#4.4x)\n",
145                                         sdp_uuid2desc(value), value);
146                         break;
147
148                 case SDP_DATA_UUID32:
149                         SDP_GET32(value, start);
150                         fprintf(stdout, "\t%#8.8x\n", value);
151                         break;
152
153                 case SDP_DATA_UUID128: {
154                         int128_t        uuid;
155
156                         SDP_GET_UUID128(&uuid, start);
157                         fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
158                                         ntohl(*(uint32_t *)&uuid.b[0]),
159                                         ntohs(*(uint16_t *)&uuid.b[4]),
160                                         ntohs(*(uint16_t *)&uuid.b[6]),
161                                         ntohs(*(uint16_t *)&uuid.b[8]),
162                                         ntohs(*(uint16_t *)&uuid.b[10]),
163                                         ntohl(*(uint32_t *)&uuid.b[12]));
164                         } break;
165
166                 default:
167                         fprintf(stderr, "Invalid Service Class ID List. " \
168                                         "Not a UUID, type=%#x\n", type);
169                         return;
170                         /* NOT REACHED */
171                 }
172         }
173 } /* print_service_class_id_list */
174
175 /*
176  * Print Protocol Descriptor List
177  *
178  * If the ProtocolDescriptorList describes a single stack, it takes the form
179  * of a data element sequence in which each element of the sequence is a
180  * protocol descriptor. Each protocol descriptor is, in turn, a data element
181  * sequence whose first element is a UUID identifying the protocol and whose
182  * successive elements are protocol-specific parameters. The protocol
183  * descriptors are listed in order from the lowest layer protocol to the
184  * highest layer protocol used to gain access to the service. If it is possible
185  * for more than one kind of protocol stack to be used to gain access to the
186  * service, the ProtocolDescriptorList takes the form of a data element
187  * alternative where each member is a data element sequence as described above.
188  */
189
190 static void
191 print_protocol_descriptor(uint8_t const *start, uint8_t const *end)
192 {
193         union {
194                 uint8_t         uint8;
195                 uint16_t        uint16;
196                 uint32_t        uint32;
197                 uint64_t        uint64;
198                 int128_t        int128;
199         }                       value;
200         uint32_t                type, len, param;
201
202         /* Get Protocol UUID */
203         SDP_GET8(type, start);
204         switch (type) {
205         case SDP_DATA_UUID16:
206                 SDP_GET16(value.uint16, start);
207                 fprintf(stdout, "\t%s (%#4.4x)\n", sdp_uuid2desc(value.uint16),
208                                 value.uint16);
209                 break;
210
211         case SDP_DATA_UUID32:
212                 SDP_GET32(value.uint32, start);
213                 fprintf(stdout, "\t%#8.8x\n", value.uint32);
214                 break;
215
216         case SDP_DATA_UUID128:
217                 SDP_GET_UUID128(&value.int128, start);
218                 fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
219                                 ntohl(*(uint32_t *)&value.int128.b[0]),
220                                 ntohs(*(uint16_t *)&value.int128.b[4]),
221                                 ntohs(*(uint16_t *)&value.int128.b[6]),
222                                 ntohs(*(uint16_t *)&value.int128.b[8]),
223                                 ntohs(*(uint16_t *)&value.int128.b[10]),
224                                 ntohl(*(uint32_t *)&value.int128.b[12]));
225                 break;
226
227         default:
228                 fprintf(stderr, "Invalid Protocol Descriptor. " \
229                                 "Not a UUID, type=%#x\n", type);
230                 return;
231                 /* NOT REACHED */
232         }
233
234         /* Protocol specific parameters */
235         for (param = 1; start < end; param ++) {
236                 fprintf(stdout, "\t\tProtocol specific parameter #%d: ", param);
237
238                 SDP_GET8(type, start);
239                 switch (type) {
240                 case SDP_DATA_NIL:
241                         fprintf(stdout, "nil\n");
242                         break;
243
244                 case SDP_DATA_UINT8:
245                 case SDP_DATA_INT8:
246                 case SDP_DATA_BOOL:
247                         SDP_GET8(value.uint8, start);
248                         fprintf(stdout, "u/int8/bool %u\n", value.uint8);
249                         break;
250
251                 case SDP_DATA_UINT16:
252                 case SDP_DATA_INT16:
253                 case SDP_DATA_UUID16:
254                         SDP_GET16(value.uint16, start);
255                         fprintf(stdout, "u/int/uuid16 %u\n", value.uint16);
256                         break;
257
258                 case SDP_DATA_UINT32:
259                 case SDP_DATA_INT32:
260                 case SDP_DATA_UUID32:
261                         SDP_GET32(value.uint32, start);
262                         fprintf(stdout, "u/int/uuid32 %u\n", value.uint32);
263                         break;
264
265                 case SDP_DATA_UINT64:
266                 case SDP_DATA_INT64:
267                         SDP_GET64(value.uint64, start);
268                         fprintf(stdout, "u/int64 %ju\n", value.uint64);
269                         break;
270
271                 case SDP_DATA_UINT128:
272                 case SDP_DATA_INT128:
273                         SDP_GET128(&value.int128, start);
274                         fprintf(stdout, "u/int128 %#8.8x%8.8x%8.8x%8.8x\n",
275                                 *(uint32_t *)&value.int128.b[0],
276                                 *(uint32_t *)&value.int128.b[4],
277                                 *(uint32_t *)&value.int128.b[8],
278                                 *(uint32_t *)&value.int128.b[12]);
279                         break;
280
281                 case SDP_DATA_UUID128:
282                         SDP_GET_UUID128(&value.int128, start);
283                         fprintf(stdout, "uuid128 %#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
284                                 ntohl(*(uint32_t *)&value.int128.b[0]),
285                                 ntohs(*(uint16_t *)&value.int128.b[4]),
286                                 ntohs(*(uint16_t *)&value.int128.b[6]),
287                                 ntohs(*(uint16_t *)&value.int128.b[8]),
288                                 ntohs(*(uint16_t *)&value.int128.b[10]),
289                                 ntohl(*(uint32_t *)&value.int128.b[12]));
290                         break;
291
292                 case SDP_DATA_STR8:
293                 case SDP_DATA_URL8:
294                         SDP_GET8(len, start);
295                         fprintf(stdout, "%*.*s\n", len, len,  start);
296                         start += len;
297                         break;
298
299                 case SDP_DATA_STR16:
300                 case SDP_DATA_URL16:
301                         SDP_GET16(len, start);
302                         fprintf(stdout, "%*.*s\n", len, len,  start);
303                         start += len;
304                         break;
305
306                 case SDP_DATA_STR32:
307                 case SDP_DATA_URL32:
308                         SDP_GET32(len, start);
309                         fprintf(stdout, "%*.*s\n", len, len,  start);
310                         start += len;
311                         break;
312
313                 case SDP_DATA_SEQ8:
314                 case SDP_DATA_ALT8:
315                         SDP_GET8(len, start);
316                         for (; len > 0; start ++, len --)
317                                 fprintf(stdout, "%#2.2x ", *start);
318                         fprintf(stdout, "\n");
319                         break;
320
321                 case SDP_DATA_SEQ16:
322                 case SDP_DATA_ALT16:
323                         SDP_GET16(len, start);
324                         for (; len > 0; start ++, len --)
325                                 fprintf(stdout, "%#2.2x ", *start);
326                         fprintf(stdout, "\n");
327                         break;
328
329                 case SDP_DATA_SEQ32:
330                 case SDP_DATA_ALT32:
331                         SDP_GET32(len, start);
332                         for (; len > 0; start ++, len --)
333                                 fprintf(stdout, "%#2.2x ", *start);
334                         fprintf(stdout, "\n");
335                         break;
336
337                 default:
338                         fprintf(stderr, "Invalid Protocol Descriptor. " \
339                                         "Unknown data type: %#02x\n", type);
340                         return;
341                         /* NOT REACHED */
342                 }
343         }
344 } /* print_protocol_descriptor */
345
346 static void
347 print_protocol_descriptor_list(uint8_t const *start, uint8_t const *end)
348 {
349         uint32_t        type, len;
350
351         if (end - start < 2) {
352                 fprintf(stderr, "Invalid Protocol Descriptor List. " \
353                                 "Too short, len=%zd\n", end - start);
354                 return;
355         }
356
357         SDP_GET8(type, start);
358         switch (type) {
359         case SDP_DATA_SEQ8:
360                 SDP_GET8(len, start);
361                 break;
362
363         case SDP_DATA_SEQ16:
364                 SDP_GET16(len, start);
365                 break;
366
367         case SDP_DATA_SEQ32:
368                 SDP_GET32(len, start);
369                 break;
370
371         default:
372                 fprintf(stderr, "Invalid Protocol Descriptor List. " \
373                                 "Not a sequence, type=%#x\n", type);
374                 return;
375                 /* NOT REACHED */
376         }
377
378         while (start < end) {
379                 SDP_GET8(type, start);
380                 switch (type) {
381                 case SDP_DATA_SEQ8:
382                         SDP_GET8(len, start);
383                         break;
384
385                 case SDP_DATA_SEQ16:
386                         SDP_GET16(len, start);
387                         break;
388
389                 case SDP_DATA_SEQ32:
390                         SDP_GET32(len, start);
391                         break;
392
393                 default:
394                         fprintf(stderr, "Invalid Protocol Descriptor List. " \
395                                         "Not a sequence, type=%#x\n", type);
396                         return;
397                         /* NOT REACHED */
398                 }
399
400                 print_protocol_descriptor(start, start + len);
401                 start += len;
402         }
403 } /* print_protocol_descriptor_list */
404
405 /*
406  * Print Bluetooth Profile Descriptor List
407  *
408  * The BluetoothProfileDescriptorList attribute consists of a data element
409  * sequence in which each element is a profile descriptor that contains
410  * information about a Bluetooth profile to which the service represented by
411  * this service record conforms. Each profile descriptor is a data element
412  * sequence whose first element is the UUID assigned to the profile and whose
413  * second element is a 16-bit profile version number. Each version of a profile
414  * is assigned a 16-bit unsigned integer profile version number, which consists
415  * of two 8-bit fields. The higher-order 8 bits contain the major version
416  * number field and the lower-order 8 bits contain the minor version number
417  * field.
418  */
419
420 static void
421 print_bluetooth_profile_descriptor_list(uint8_t const *start, uint8_t const *end)
422 {
423         uint32_t        type, len, value;
424
425         if (end - start < 2) {
426                 fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \
427                                 "Too short, len=%zd\n", end - start);
428                 return;
429         }
430
431         SDP_GET8(type, start);
432         switch (type) {
433         case SDP_DATA_SEQ8:
434                 SDP_GET8(len, start);
435                 break;
436
437         case SDP_DATA_SEQ16:
438                 SDP_GET16(len, start);
439                 break;
440
441         case SDP_DATA_SEQ32:
442                 SDP_GET32(len, start);
443                 break;
444
445         default:
446                 fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \
447                                 "Not a sequence, type=%#x\n", type);
448                 return;
449                 /* NOT REACHED */
450         }
451
452         while (start < end) {
453                 SDP_GET8(type, start);
454                 switch (type) {
455                 case SDP_DATA_SEQ8:
456                         SDP_GET8(len, start);
457                         break;
458
459                 case SDP_DATA_SEQ16:
460                         SDP_GET16(len, start);
461                         break;
462
463                 case SDP_DATA_SEQ32:
464                         SDP_GET32(len, start);
465                         break;
466
467                 default:
468                         fprintf(stderr, "Invalid Bluetooth Profile " \
469                                         "Descriptor List. " \
470                                         "Not a sequence, type=%#x\n", type);
471                         return;
472                         /* NOT REACHED */
473                 }
474
475                 /* Get UUID */
476                 SDP_GET8(type, start);
477                 switch (type) {
478                 case SDP_DATA_UUID16:
479                         SDP_GET16(value, start);
480                         fprintf(stdout, "\t%s (%#4.4x) ",
481                                         sdp_uuid2desc(value), value);
482                         break;
483
484                 case SDP_DATA_UUID32:
485                         SDP_GET32(value, start);
486                         fprintf(stdout, "\t%#8.8x ", value);
487                         break;
488
489                 case SDP_DATA_UUID128: {
490                         int128_t        uuid;
491
492                         SDP_GET_UUID128(&uuid, start);
493                         fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x ",
494                                         ntohl(*(uint32_t *)&uuid.b[0]),
495                                         ntohs(*(uint16_t *)&uuid.b[4]),
496                                         ntohs(*(uint16_t *)&uuid.b[6]),
497                                         ntohs(*(uint16_t *)&uuid.b[8]),
498                                         ntohs(*(uint16_t *)&uuid.b[10]),
499                                         ntohl(*(uint32_t *)&uuid.b[12]));
500                         } break;
501
502                 default:
503                         fprintf(stderr, "Invalid Bluetooth Profile " \
504                                         "Descriptor List. " \
505                                         "Not a UUID, type=%#x\n", type);
506                         return;
507                         /* NOT REACHED */
508                 }
509
510                 /* Get version */
511                 SDP_GET8(type, start);
512                 if (type != SDP_DATA_UINT16) {
513                         fprintf(stderr, "Invalid Bluetooth Profile " \
514                                         "Descriptor List. " \
515                                         "Invalid version type=%#x\n", type);
516                         return;
517                 }
518
519                 SDP_GET16(value, start);
520                 fprintf(stdout, "ver. %d.%d\n",
521                                 (value >> 8) & 0xff, value & 0xff);
522         }
523 } /* print_bluetooth_profile_descriptor_list */
524
525 struct service {
526         const char      *name;
527         uint16_t        class;
528         const char      *description;
529 } services[] = {
530         { "CIP",        SDP_SERVICE_CLASS_COMMON_ISDN_ACCESS,
531           "Common ISDN Access"          },
532         { "CTP",        SDP_SERVICE_CLASS_CORDLESS_TELEPHONY,
533           "Cordless Telephony"          },
534         { "DUN",        SDP_SERVICE_CLASS_DIALUP_NETWORKING,
535           "Dial Up Networking"          },
536         { "FAX",        SDP_SERVICE_CLASS_FAX,
537           "Fax"                         },
538         { "FTRN",       SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER,
539           "Obex File Transfer"          },
540         { "GN",         SDP_SERVICE_CLASS_GN,
541           "Group ad-hoc Network"        },
542         { "HID",        SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE,
543           "Human Interface Device"      },
544         { "HF",         SDP_SERVICE_CLASS_HANDSFREE,
545           "Handsfree"                   },
546         { "HSET",       SDP_SERVICE_CLASS_HEADSET,
547           "Headset"                     },
548         { "LAN",        SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,
549           "Lan access using PPP"        },
550         { "NAP",        SDP_SERVICE_CLASS_NAP,
551           "Network Access Point"        },
552         { "OPUSH",      SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH,
553           "OBEX Object Push"            },
554         { "PANU",       SDP_SERVICE_CLASS_PANU,
555           "Personal Area Networking User"               },
556         { "SP",         SDP_SERVICE_CLASS_SERIAL_PORT,
557           "Serial Port"                 },
558         { NULL,         0,
559           NULL                          }
560 };
561
562 /* Perform SDP search command */
563 int
564 do_sdp_search(bdaddr_t *laddr, bdaddr_t *raddr, int argc, char const **argv)
565 {
566         struct service  *s;
567         void            *xs;
568         char            *ep;
569         int32_t          n, type, value;
570         uint16_t         service;
571
572         if (argc != 1)
573                 goto usage;
574
575         service = strtoul(*argv, &ep, 16);
576         if (*ep != 0) {
577                 for (s = services ; ; s++) {
578                         if (s->name == NULL)
579                                 goto usage;
580
581                         if (strcasecmp(s->name, *argv) == 0)
582                                 break;
583                 }
584                 service = s->class;
585         }
586
587         /* Initialize attribute values array */
588         for (n = 0; n < values_len; n ++) {
589                 values[n].flags = SDP_ATTR_INVALID;
590                 values[n].attr = 0;
591                 values[n].vlen = BSIZE;
592                 values[n].value = buffer[n];
593         }
594
595         if (bdaddr_any(raddr))
596                 xs = sdp_open_local(control_socket);
597         else
598                 xs = sdp_open(laddr, raddr);
599
600         if (xs == NULL || (errno = sdp_error(xs)) != 0)
601                 err(EXIT_FAILURE, "sdp_open");
602
603         /* Do SDP Service Search Attribute Request */
604         n = sdp_search(xs, 1, &service, attrs_len, attrs, values_len, values);
605         if (n != 0)
606                 err(EXIT_FAILURE, "sdp_search");
607
608         sdp_close(xs);
609
610         /* Print attributes values */
611         for (n = 0; n < values_len; n ++) {
612                 if (values[n].flags != SDP_ATTR_OK)
613                         break;
614
615                 switch (values[n].attr) {
616                 case SDP_ATTR_SERVICE_RECORD_HANDLE:
617                         fprintf(stdout, "\n");
618                         if (values[n].vlen == 5) {
619                                 SDP_GET8(type, values[n].value);
620                                 if (type == SDP_DATA_UINT32) {
621                                         SDP_GET32(value, values[n].value);
622                                         fprintf(stdout, "Record Handle: " \
623                                                         "%#8.8x\n", value);
624                                 } else
625                                         fprintf(stderr, "Invalid type=%#x " \
626                                                         "Record Handle " \
627                                                         "attribute!\n", type);
628                         } else
629                                 fprintf(stderr, "Invalid size=%d for Record " \
630                                                 "Handle attribute\n",
631                                                 values[n].vlen);
632                         break;
633
634                 case SDP_ATTR_SERVICE_CLASS_ID_LIST:
635                         fprintf(stdout, "Service Class ID List:\n");
636                         print_service_class_id_list(values[n].value,
637                                         values[n].value + values[n].vlen);
638                         break;
639
640                 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
641                         fprintf(stdout, "Protocol Descriptor List:\n");
642                         print_protocol_descriptor_list(values[n].value,
643                                         values[n].value + values[n].vlen);
644                         break;
645
646                 case SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST:
647                         fprintf(stdout, "Bluetooth Profile Descriptor List:\n");
648                         print_bluetooth_profile_descriptor_list(values[n].value,
649                                         values[n].value + values[n].vlen);
650                         break;
651
652                 default:
653                         fprintf(stderr, "Unexpected attribute ID=%#4.4x\n",
654                                         values[n].attr);
655                         break;
656                 }
657         }
658
659         return EXIT_SUCCESS;
660
661 usage:
662         fprintf(stderr, "Known services:\n");
663         for (s = services ; s->name != NULL ; s++)
664                 fprintf(stderr, "\t%s\t%s\n", s->name, s->description);
665
666         return EXIT_FAILURE;
667 } /* do_sdp_search */
668
669 /* Perform SDP browse command */
670 int
671 do_sdp_browse(bdaddr_t *laddr, bdaddr_t *raddr, int argc, char const **argv)
672 {
673 #undef  _STR
674 #undef  STR
675 #define _STR(x) #x
676 #define STR(x)  _STR(x)
677
678         static char     const * av[] = {
679                 STR(SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP),
680                 NULL
681         };
682
683         switch (argc) {
684         case 0:
685                 argc = 1;
686                 argv =  (char const **) av;
687                 /* FALL THROUGH */
688         case 1:
689                 return (do_sdp_search(laddr, raddr, argc, argv));
690         }
691
692         return EXIT_FAILURE;
693 } /* do_sdp_browse */