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