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: stable/11/usr.sbin/bluetooth/sdpcontrol/search.c 330449 2018-03-05 07:26:05Z eadler $
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 */
44static 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 */
60static uint8_t		buffer[NRECS * attrs_len][BSIZE];
61
62/* SDP attributes */
63static 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
76static void
77print_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
165static void
166print_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
324static void
325print_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
410static void
411print_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 */
529static int
530do_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 */
692static int
693do_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 */
718struct 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",
724do_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",
746do_sdp_search
747},
748{ NULL, NULL, NULL }
749};
750
751