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