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