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