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