]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - usr.sbin/bluetooth/sdpcontrol/search.c
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.git] / usr.sbin / bluetooth / sdpcontrol / search.c
1 /*
2  * search.c
3  *
4  * Copyright (c) 2001-2003 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: search.c,v 1.2 2003/09/08 17:35:15 max Exp $
29  * $FreeBSD$
30  */
31
32 #include <netinet/in.h>
33 #include <bluetooth.h>
34 #include <ctype.h>
35 #include <sdp.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include "sdpcontrol.h"
39
40 /* List of the attributes we are looking for */
41 static uint32_t attrs[] =
42 {
43         SDP_ATTR_RANGE( SDP_ATTR_SERVICE_RECORD_HANDLE,
44                         SDP_ATTR_SERVICE_RECORD_HANDLE),
45         SDP_ATTR_RANGE( SDP_ATTR_SERVICE_CLASS_ID_LIST,
46                         SDP_ATTR_SERVICE_CLASS_ID_LIST),
47         SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
48                         SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
49         SDP_ATTR_RANGE( SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST,
50                         SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST)
51 };
52 #define attrs_len       (sizeof(attrs)/sizeof(attrs[0]))
53
54 /* Buffer for the attributes */
55 #define NRECS   25      /* request this much records from the SDP server */
56 #define BSIZE   256     /* one attribute buffer size */
57 static uint8_t          buffer[NRECS * attrs_len][BSIZE];
58
59 /* SDP attributes */
60 static sdp_attr_t       values[NRECS * attrs_len];
61 #define values_len      (sizeof(values)/sizeof(values[0]))
62
63 /*
64  * Print Service Class ID List
65  *
66  * The ServiceClassIDList attribute consists of a data element sequence in 
67  * which each data element is a UUID representing the service classes that
68  * a given service record conforms to. The UUIDs are listed in order from 
69  * the most specific class to the most general class. The ServiceClassIDList
70  * must contain at least one service class UUID.
71  */
72
73 static void
74 print_service_class_id_list(uint8_t const *start, uint8_t const *end)
75 {
76         uint32_t        type, len, value;
77
78         if (end - start < 2) {
79                 fprintf(stderr, "Invalid Service Class ID List. " \
80                                 "Too short, len=%zd\n", end - start);
81                 return;
82         }
83
84         SDP_GET8(type, start);
85         switch (type) {
86         case SDP_DATA_SEQ8:
87                 SDP_GET8(len, start);
88                 break;
89
90         case SDP_DATA_SEQ16:
91                 SDP_GET16(len, start);
92                 break;
93
94         case SDP_DATA_SEQ32:
95                 SDP_GET32(len, start);
96                 break;
97
98         default:
99                 fprintf(stderr, "Invalid Service Class ID List. " \
100                                 "Not a sequence, type=%#x\n", type);
101                 return;
102                 /* NOT REACHED */
103         }
104
105         if (len > (end - start)) {
106                 fprintf(stderr, "Invalid Service Class ID List. " \
107                                 "Too long len=%d\n", len);
108                 return;
109         }
110
111         while (start < end) {
112                 SDP_GET8(type, start);
113                 switch (type) {
114                 case SDP_DATA_UUID16:
115                         SDP_GET16(value, start);
116                         fprintf(stdout, "\t%s (%#4.4x)\n",
117                                         sdp_uuid2desc(value), value);
118                         break;
119
120                 case SDP_DATA_UUID32:
121                         SDP_GET32(value, start);
122                         fprintf(stdout, "\t%#8.8x\n", value);
123                         break;
124
125                 case SDP_DATA_UUID128: {
126                         int128_t        uuid;
127
128                         SDP_GET_UUID128(&uuid, start);
129                         fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
130                                         ntohl(*(uint32_t *)&uuid.b[0]),
131                                         ntohs(*(uint16_t *)&uuid.b[4]),
132                                         ntohs(*(uint16_t *)&uuid.b[6]),
133                                         ntohs(*(uint16_t *)&uuid.b[8]),
134                                         ntohs(*(uint16_t *)&uuid.b[10]),
135                                         ntohl(*(uint32_t *)&uuid.b[12]));
136                         } break;
137
138                 default:
139                         fprintf(stderr, "Invalid Service Class ID List. " \
140                                         "Not a UUID, type=%#x\n", type);
141                         return;
142                         /* NOT REACHED */
143                 }
144         }
145 } /* print_service_class_id_list */
146
147 /*
148  * Print Protocol Descriptor List
149  *
150  * If the ProtocolDescriptorList describes a single stack, it takes the form 
151  * of a data element sequence in which each element of the sequence is a 
152  * protocol descriptor. Each protocol descriptor is, in turn, a data element 
153  * sequence whose first element is a UUID identifying the protocol and whose 
154  * successive elements are protocol-specific parameters. The protocol 
155  * descriptors are listed in order from the lowest layer protocol to the 
156  * highest layer protocol used to gain access to the service. If it is possible
157  * for more than one kind of protocol stack to be used to gain access to the 
158  * service, the ProtocolDescriptorList takes the form of a data element 
159  * alternative where each member is a data element sequence as described above.
160  */
161
162 static void
163 print_protocol_descriptor(uint8_t const *start, uint8_t const *end)
164 {
165         union {
166                 uint8_t         uint8;
167                 uint16_t        uint16;
168                 uint32_t        uint32;
169                 uint64_t        uint64;
170                 int128_t        int128;
171         }                       value;
172         uint32_t                type, len, param;
173
174         /* Get Protocol UUID */
175         SDP_GET8(type, start);
176         switch (type) {
177         case SDP_DATA_UUID16:
178                 SDP_GET16(value.uint16, start);
179                 fprintf(stdout, "\t%s (%#4.4x)\n", sdp_uuid2desc(value.uint16),
180                                 value.uint16);
181                 break;
182
183         case SDP_DATA_UUID32:
184                 SDP_GET32(value.uint32, start);
185                 fprintf(stdout, "\t%#8.8x\n", value.uint32);
186                 break;
187
188         case SDP_DATA_UUID128:
189                 SDP_GET_UUID128(&value.int128, start);
190                 fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
191                                 ntohl(*(uint32_t *)&value.int128.b[0]),
192                                 ntohs(*(uint16_t *)&value.int128.b[4]),
193                                 ntohs(*(uint16_t *)&value.int128.b[6]),
194                                 ntohs(*(uint16_t *)&value.int128.b[8]),
195                                 ntohs(*(uint16_t *)&value.int128.b[10]),
196                                 ntohl(*(uint32_t *)&value.int128.b[12]));
197                 break;
198
199         default:
200                 fprintf(stderr, "Invalid Protocol Descriptor. " \
201                                 "Not a UUID, type=%#x\n", type);
202                 return;
203                 /* NOT REACHED */
204         }
205
206         /* Protocol specific parameters */
207         for (param = 1; start < end; param ++) {
208                 fprintf(stdout, "\t\tProtocol specific parameter #%d: ", param);
209
210                 SDP_GET8(type, start);
211                 switch (type) {
212                 case SDP_DATA_NIL:
213                         fprintf(stdout, "nil\n");
214                         break;
215
216                 case SDP_DATA_UINT8:
217                 case SDP_DATA_INT8:
218                 case SDP_DATA_BOOL:
219                         SDP_GET8(value.uint8, start);
220                         fprintf(stdout, "u/int8/bool %u\n", value.uint8);
221                         break;
222
223                 case SDP_DATA_UINT16:
224                 case SDP_DATA_INT16:
225                 case SDP_DATA_UUID16:
226                         SDP_GET16(value.uint16, start);
227                         fprintf(stdout, "u/int/uuid16 %u\n", value.uint16);
228                         break;
229
230                 case SDP_DATA_UINT32:
231                 case SDP_DATA_INT32:
232                 case SDP_DATA_UUID32:
233                         SDP_GET32(value.uint32, start);
234                         fprintf(stdout, "u/int/uuid32 %u\n", value.uint32);
235                         break;
236
237                 case SDP_DATA_UINT64:
238                 case SDP_DATA_INT64:
239                         SDP_GET64(value.uint64, start);
240                         fprintf(stdout, "u/int64 %ju\n", value.uint64);
241                         break;
242
243                 case SDP_DATA_UINT128:
244                 case SDP_DATA_INT128:
245                         SDP_GET128(&value.int128, start);
246                         fprintf(stdout, "u/int128 %#8.8x%8.8x%8.8x%8.8x\n",
247                                 *(uint32_t *)&value.int128.b[0],
248                                 *(uint32_t *)&value.int128.b[4],
249                                 *(uint32_t *)&value.int128.b[8],
250                                 *(uint32_t *)&value.int128.b[12]);
251                         break;
252
253                 case SDP_DATA_UUID128:
254                         SDP_GET_UUID128(&value.int128, start);
255                         fprintf(stdout, "uuid128 %#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
256                                 ntohl(*(uint32_t *)&value.int128.b[0]),
257                                 ntohs(*(uint16_t *)&value.int128.b[4]),
258                                 ntohs(*(uint16_t *)&value.int128.b[6]),
259                                 ntohs(*(uint16_t *)&value.int128.b[8]),
260                                 ntohs(*(uint16_t *)&value.int128.b[10]),
261                                 ntohl(*(uint32_t *)&value.int128.b[12]));
262                         break;
263
264                 case SDP_DATA_STR8:
265                 case SDP_DATA_URL8:
266                         SDP_GET8(len, start);
267                         for (; start < end && len > 0; start ++, len --)
268                                 fprintf(stdout, "%c", *start);
269                         fprintf(stdout, "\n");
270                         break;
271
272                 case SDP_DATA_STR16:
273                 case SDP_DATA_URL16:
274                         SDP_GET16(len, start);
275                         for (; start < end && len > 0; start ++, len --)
276                                 fprintf(stdout, "%c", *start);
277                         fprintf(stdout, "\n");
278                         break;
279
280                 case SDP_DATA_STR32:
281                 case SDP_DATA_URL32:
282                         SDP_GET32(len, start);
283                         for (; start < end && len > 0; start ++, len --)
284                                 fprintf(stdout, "%c", *start);
285                         fprintf(stdout, "\n");
286                         break;
287
288                 case SDP_DATA_SEQ8:
289                 case SDP_DATA_ALT8:
290                         SDP_GET8(len, start);
291                         for (; start < end && len > 0; start ++, len --)
292                                 fprintf(stdout, "%#2.2x ", *start);
293                         fprintf(stdout, "\n");
294                         break;
295
296                 case SDP_DATA_SEQ16:
297                 case SDP_DATA_ALT16:
298                         SDP_GET16(len, start);
299                         for (; start < end && len > 0; start ++, len --)
300                                 fprintf(stdout, "%#2.2x ", *start);
301                         fprintf(stdout, "\n");
302                         break;
303
304                 case SDP_DATA_SEQ32:
305                 case SDP_DATA_ALT32:
306                         SDP_GET32(len, start);
307                         for (; start < end && len > 0; start ++, len --)
308                                 fprintf(stdout, "%#2.2x ", *start);
309                         fprintf(stdout, "\n");
310                         break;
311
312                 default:
313                         fprintf(stderr, "Invalid Protocol Descriptor. " \
314                                         "Unknown data type: %#02x\n", type);
315                         return;
316                         /* NOT REACHED */
317                 }
318         }
319 } /* print_protocol_descriptor */
320
321 static void
322 print_protocol_descriptor_list(uint8_t const *start, uint8_t const *end)
323 {
324         uint32_t        type, len;
325
326         if (end - start < 2) {
327                 fprintf(stderr, "Invalid Protocol Descriptor List. " \
328                                 "Too short, len=%zd\n", end - start);
329                 return;
330         }
331
332         SDP_GET8(type, start);
333         switch (type) {
334         case SDP_DATA_SEQ8:
335                 SDP_GET8(len, start);
336                 break;
337
338         case SDP_DATA_SEQ16:
339                 SDP_GET16(len, start);
340                 break;
341
342         case SDP_DATA_SEQ32:
343                 SDP_GET32(len, start);
344                 break;
345
346         default:
347                 fprintf(stderr, "Invalid Protocol Descriptor List. " \
348                                 "Not a sequence, type=%#x\n", type);
349                 return;
350                 /* NOT REACHED */
351         }
352
353         if (len > (end - start)) {
354                 fprintf(stderr, "Invalid Protocol Descriptor List. " \
355                                 "Too long, len=%d\n", len);
356                 return;
357         }
358
359         while (start < end) {
360                 SDP_GET8(type, start);
361                 switch (type) {
362                 case SDP_DATA_SEQ8:
363                         SDP_GET8(len, start);
364                         break;
365
366                 case SDP_DATA_SEQ16:
367                         SDP_GET16(len, start);
368                         break;
369
370                 case SDP_DATA_SEQ32:
371                         SDP_GET32(len, start);
372                         break;
373
374                 default:
375                         fprintf(stderr, "Invalid Protocol Descriptor List. " \
376                                         "Not a sequence, type=%#x\n", type);
377                         return;
378                         /* NOT REACHED */
379                 }
380
381                 if (len > (end - start)) {
382                         fprintf(stderr, "Invalid Protocol Descriptor List. " \
383                                         "Too long, len=%d\n", len);
384                         return;
385                 }
386
387                 print_protocol_descriptor(start, start + len);
388                 start += len;
389         }
390 } /* print_protocol_descriptor_list */
391
392 /*
393  * Print Bluetooth Profile Descriptor List
394  *
395  * The BluetoothProfileDescriptorList attribute consists of a data element 
396  * sequence in which each element is a profile descriptor that contains 
397  * information about a Bluetooth profile to which the service represented by 
398  * this service record conforms. Each profile descriptor is a data element 
399  * sequence whose first element is the UUID assigned to the profile and whose 
400  * second element is a 16-bit profile version number. Each version of a profile
401  * is assigned a 16-bit unsigned integer profile version number, which consists
402  * of two 8-bit fields. The higher-order 8 bits contain the major version 
403  * number field and the lower-order 8 bits contain the minor version number 
404  * field.
405  */
406
407 static void
408 print_bluetooth_profile_descriptor_list(uint8_t const *start, uint8_t const *end)
409 {
410         uint32_t        type, len, value;
411
412         if (end - start < 2) {
413                 fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \
414                                 "Too short, len=%zd\n", end - start);
415                 return;
416         }
417
418         SDP_GET8(type, start);
419         switch (type) {
420         case SDP_DATA_SEQ8:
421                 SDP_GET8(len, start);
422                 break;
423
424         case SDP_DATA_SEQ16:
425                 SDP_GET16(len, start);
426                 break;
427
428         case SDP_DATA_SEQ32:
429                 SDP_GET32(len, start);
430                 break;
431
432         default:
433                 fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \
434                                 "Not a sequence, type=%#x\n", type);
435                 return;
436                 /* NOT REACHED */
437         }
438
439         if (len > (end - start)) {
440                 fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \
441                                 "Too long, len=%d\n", len);
442                 return;
443         }
444
445         while (start < end) {
446                 SDP_GET8(type, start);
447                 switch (type) {
448                 case SDP_DATA_SEQ8:
449                         SDP_GET8(len, start);
450                         break;
451
452                 case SDP_DATA_SEQ16:
453                         SDP_GET16(len, start);
454                         break;
455
456                 case SDP_DATA_SEQ32:
457                         SDP_GET32(len, start);
458                         break;
459
460                 default:
461                         fprintf(stderr, "Invalid Bluetooth Profile " \
462                                         "Descriptor List. " \
463                                         "Not a sequence, type=%#x\n", type);
464                         return;
465                         /* NOT REACHED */
466                 }
467
468                 if (len > (end - start)) {
469                         fprintf(stderr, "Invalid Bluetooth Profile " \
470                                         "Descriptor List. " \
471                                         "Too long, len=%d\n", len);
472                         return;
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 /* Perform SDP search command */
526 static int
527 do_sdp_search(void *xs, int argc, char **argv)
528 {
529         char            *ep = NULL;
530         int32_t          n, type, value;
531         uint16_t         service;
532
533         /* Parse command line arguments */
534         switch (argc) {
535         case 1:
536                 n = strtoul(argv[0], &ep, 16);
537                 if (*ep != 0) {
538                         switch (tolower(argv[0][0])) {
539                         case 'c': /* CIP/CTP */
540                                 switch (tolower(argv[0][1])) {
541                                 case 'i':
542                                         service = SDP_SERVICE_CLASS_COMMON_ISDN_ACCESS;
543                                         break;
544
545                                 case 't':
546                                         service = SDP_SERVICE_CLASS_CORDLESS_TELEPHONY;
547                                         break;
548
549                                 default:
550                                         return (USAGE);
551                                         /* NOT REACHED */
552                                 }
553                                 break;
554
555                         case 'd': /* DialUp Networking */
556                                 service = SDP_SERVICE_CLASS_DIALUP_NETWORKING;
557                                 break;
558
559                         case 'f': /* Fax/OBEX File Transfer */
560                                 switch (tolower(argv[0][1])) {
561                                 case 'a':
562                                         service = SDP_SERVICE_CLASS_FAX;
563                                         break;
564
565                                 case 't':
566                                         service = SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER;
567                                         break;
568
569                                 default:
570                                         return (USAGE);
571                                         /* NOT REACHED */
572                                 }
573                                 break;
574
575                         case 'g': /* GN */
576                                 service = SDP_SERVICE_CLASS_GN;
577                                 break;
578
579                         case 'h': /* Headset/HID */
580                                 switch (tolower(argv[0][1])) {
581                                 case 'i':
582                                         service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
583                                         break;
584
585                                 case 's':
586                                         service = SDP_SERVICE_CLASS_HEADSET;
587                                         break;
588
589                                 default:
590                                         return (USAGE);
591                                         /* NOT REACHED */
592                                 }
593                                 break;
594
595                         case 'l': /* LAN Access Using PPP */
596                                 service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP;
597                                 break;
598
599                         case 'n': /* NAP */
600                                 service = SDP_SERVICE_CLASS_NAP;
601                                 break;
602
603                         case 'o': /* OBEX Object Push */
604                                 service = SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH;
605                                 break;
606
607                         case 's': /* Serial Port */
608                                 service = SDP_SERVICE_CLASS_SERIAL_PORT;
609                                 break;
610
611                         default:
612                                 return (USAGE);
613                                 /* NOT REACHED */
614                         }
615                 } else
616                         service = (uint16_t) n;
617                 break;
618
619         default:
620                 return (USAGE);
621         }
622
623         /* Initialize attribute values array */
624         for (n = 0; n < values_len; n ++) {
625                 values[n].flags = SDP_ATTR_INVALID;
626                 values[n].attr = 0;
627                 values[n].vlen = BSIZE;
628                 values[n].value = buffer[n];
629         }
630
631         /* Do SDP Service Search Attribute Request */
632         n = sdp_search(xs, 1, &service, attrs_len, attrs, values_len, values);
633         if (n != 0)
634                 return (ERROR);
635
636         /* Print attributes values */
637         for (n = 0; n < values_len; n ++) {
638                 if (values[n].flags != SDP_ATTR_OK)
639                         break;
640
641                 switch (values[n].attr) {
642                 case SDP_ATTR_SERVICE_RECORD_HANDLE:
643                         fprintf(stdout, "\n");
644                         if (values[n].vlen == 5) {
645                                 SDP_GET8(type, values[n].value);
646                                 if (type == SDP_DATA_UINT32) {
647                                         SDP_GET32(value, values[n].value);
648                                         fprintf(stdout, "Record Handle: " \
649                                                         "%#8.8x\n", value);
650                                 } else
651                                         fprintf(stderr, "Invalid type=%#x " \
652                                                         "Record Handle " \
653                                                         "attribute!\n", type);
654                         } else
655                                 fprintf(stderr, "Invalid size=%d for Record " \
656                                                 "Handle attribute\n",
657                                                 values[n].vlen);
658                         break;
659
660                 case SDP_ATTR_SERVICE_CLASS_ID_LIST:
661                         fprintf(stdout, "Service Class ID List:\n");
662                         print_service_class_id_list(values[n].value,
663                                         values[n].value + values[n].vlen);
664                         break;
665
666                 case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
667                         fprintf(stdout, "Protocol Descriptor List:\n");
668                         print_protocol_descriptor_list(values[n].value,
669                                         values[n].value + values[n].vlen);
670                         break;
671
672                 case SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST:
673                         fprintf(stdout, "Bluetooth Profile Descriptor List:\n");
674                         print_bluetooth_profile_descriptor_list(values[n].value,
675                                         values[n].value + values[n].vlen);
676                         break;
677
678                 default:
679                         fprintf(stderr, "Unexpected attribute ID=%#4.4x\n",
680                                         values[n].attr);
681                         break;
682                 }
683         }
684
685         return (OK);
686 } /* do_sdp_search */
687
688 /* Perform SDP browse command */
689 static int
690 do_sdp_browse(void *xs, int argc, char **argv)
691 {
692 #undef  _STR
693 #undef  STR
694 #define _STR(x) #x
695 #define STR(x)  _STR(x)
696
697         static char const * const       av[] = {
698                 STR(SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP),
699                 NULL
700         }; 
701
702         switch (argc) {
703         case 0:
704                 argc = 1;
705                 argv = (char **) av;
706                 /* FALL THROUGH */
707         case 1:
708                 return (do_sdp_search(xs, argc, argv));
709         }
710
711         return (USAGE);
712 } /* do_sdp_browse */
713
714 /* List of SDP commands */
715 struct sdp_command      sdp_commands[] = {
716 {
717 "Browse [<Group>]",
718 "Browse for services. The <Group> parameter is a 16-bit UUID of the group\n" \
719 "to browse. If omitted <Group> is set to Public Browse Group.\n\n" \
720 "\t<Group> - xxxx; 16-bit UUID of the group to browse\n",
721 do_sdp_browse
722 },
723 {
724 "Search <Service>",
725 "Search for the <Service>. The <Service> parameter is a 16-bit UUID of the\n" \
726 "service to search for. For some services it is possible to use service name\n"\
727 "instead of service UUID\n\n" \
728 "\t<Service> - xxxx; 16-bit UUID of the service to search for\n\n" \
729 "\tKnown service names\n" \
730 "\t===================\n" \
731 "\tCIP   - Common ISDN Access\n" \
732 "\tCTP   - Cordless Telephony\n" \
733 "\tDUN   - DialUp Networking\n" \
734 "\tFAX   - Fax\n" \
735 "\tFTRN  - OBEX File Transfer\n" \
736 "\tGN    - GN\n" \
737 "\tHID   - Human Interface Device\n" \
738 "\tHSET  - Headset\n" \
739 "\tLAN   - LAN Access Using PPP\n" \
740 "\tNAP   - Network Access Point\n" \
741 "\tOPUSH - OBEX Object Push\n" \
742 "\tSP    - Serial Port\n",
743 do_sdp_search
744 },
745 { NULL, NULL, NULL }
746 };
747