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