xref: /dragonfly/usr.bin/sdpquery/search.c (revision d8082429)
1 /* $NetBSD: search.c,v 1.6 2007/11/06 21:35:52 plunky Exp $ */
2 
3 /*-
4  * Copyright (c) 2006 Itronix Inc.
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  * 3. The name of Itronix Inc. may not be used to endorse
16  *    or promote products derived from this software without specific
17  *    prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 /*
32  * search.c
33  *
34  * Copyright (c) 2001-2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
35  * All rights reserved.
36  *
37  * Redistribution and use in source and binary forms, with or without
38  * modification, are permitted provided that the following conditions
39  * are met:
40  * 1. Redistributions of source code must retain the above copyright
41  *    notice, this list of conditions and the following disclaimer.
42  * 2. Redistributions in binary form must reproduce the above copyright
43  *    notice, this list of conditions and the following disclaimer in the
44  *    documentation and/or other materials provided with the distribution.
45  *
46  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
47  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
50  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56  * SUCH DAMAGE.
57  *
58  * $Id: search.c,v 1.6 2007/11/06 21:35:52 plunky Exp $
59  * $FreeBSD: src/usr.sbin/bluetooth/sdpcontrol/search.c,v 1.4 2005/05/27 19:11:33 emax Exp $
60  */
61 
62 #include <netinet/in.h>
63 #include <bluetooth.h>
64 #include <err.h>
65 #include <errno.h>
66 #include <ctype.h>
67 #include <sdp.h>
68 #include <stdio.h>
69 #include <stdlib.h>
70 
71 #include "sdpquery.h"
72 
73 /* List of the attributes we are looking for */
74 static uint32_t	attrs[] =
75 {
76 	SDP_ATTR_RANGE(	SDP_ATTR_SERVICE_RECORD_HANDLE,
77 			SDP_ATTR_SERVICE_RECORD_HANDLE),
78 	SDP_ATTR_RANGE(	SDP_ATTR_SERVICE_CLASS_ID_LIST,
79 			SDP_ATTR_SERVICE_CLASS_ID_LIST),
80 	SDP_ATTR_RANGE(	SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
81 			SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
82 	SDP_ATTR_RANGE(	SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST,
83 			SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST)
84 };
85 #define attrs_len	(sizeof(attrs)/sizeof(attrs[0]))
86 
87 /* Buffer for the attributes */
88 #define NRECS	25	/* request this much records from the SDP server */
89 #define	BSIZE	256	/* one attribute buffer size */
90 static uint8_t		buffer[NRECS * attrs_len][BSIZE];
91 
92 /* SDP attributes */
93 static sdp_attr_t	values[NRECS * attrs_len];
94 #define values_len	(sizeof(values)/sizeof(values[0]))
95 
96 /*
97  * Print Service Class ID List
98  *
99  * The ServiceClassIDList attribute consists of a data element sequence in
100  * which each data element is a UUID representing the service classes that
101  * a given service record conforms to. The UUIDs are listed in order from
102  * the most specific class to the most general class. The ServiceClassIDList
103  * must contain at least one service class UUID.
104  */
105 
106 static void
107 print_service_class_id_list(uint8_t const *start, uint8_t const *end)
108 {
109 	uint32_t	type, len, value;
110 
111 	if (end - start < 2) {
112 		fprintf(stderr, "Invalid Service Class ID List. " \
113 				"Too short, len=%td\n", end - start);
114 		return;
115 	}
116 
117 	SDP_GET8(type, start);
118 	switch (type) {
119 	case SDP_DATA_SEQ8:
120 		SDP_GET8(len, start);
121 		break;
122 
123 	case SDP_DATA_SEQ16:
124 		SDP_GET16(len, start);
125 		break;
126 
127 	case SDP_DATA_SEQ32:
128 		SDP_GET32(len, start);
129 		break;
130 
131 	default:
132 		fprintf(stderr, "Invalid Service Class ID List. " \
133 				"Not a sequence, type=%#x\n", type);
134 		return;
135 		/* NOT REACHED */
136 	}
137 
138 	while (start < end) {
139 		SDP_GET8(type, start);
140 		switch (type) {
141 		case SDP_DATA_UUID16:
142 			SDP_GET16(value, start);
143 			fprintf(stdout, "\t%s (%#4.4x)\n",
144 					sdp_uuid2desc(value), value);
145 			break;
146 
147 		case SDP_DATA_UUID32:
148 			SDP_GET32(value, start);
149 			fprintf(stdout, "\t%#8.8x\n", value);
150 			break;
151 
152 		case SDP_DATA_UUID128: {
153 			int128_t	uuid;
154 
155 			SDP_GET_UUID128(&uuid, start);
156 			fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
157 					ntohl(*(uint32_t *)&uuid.b[0]),
158 					ntohs(*(uint16_t *)&uuid.b[4]),
159 					ntohs(*(uint16_t *)&uuid.b[6]),
160 					ntohs(*(uint16_t *)&uuid.b[8]),
161 					ntohs(*(uint16_t *)&uuid.b[10]),
162 					ntohl(*(uint32_t *)&uuid.b[12]));
163 			} break;
164 
165 		default:
166 			fprintf(stderr, "Invalid Service Class ID List. " \
167 					"Not a UUID, type=%#x\n", type);
168 			return;
169 			/* NOT REACHED */
170 		}
171 	}
172 } /* print_service_class_id_list */
173 
174 /*
175  * Print Protocol Descriptor List
176  *
177  * If the ProtocolDescriptorList describes a single stack, it takes the form
178  * of a data element sequence in which each element of the sequence is a
179  * protocol descriptor. Each protocol descriptor is, in turn, a data element
180  * sequence whose first element is a UUID identifying the protocol and whose
181  * successive elements are protocol-specific parameters. The protocol
182  * descriptors are listed in order from the lowest layer protocol to the
183  * highest layer protocol used to gain access to the service. If it is possible
184  * for more than one kind of protocol stack to be used to gain access to the
185  * service, the ProtocolDescriptorList takes the form of a data element
186  * alternative where each member is a data element sequence as described above.
187  */
188 
189 static void
190 print_protocol_descriptor(uint8_t const *start, uint8_t const *end)
191 {
192 	union {
193 		uint8_t		uint8;
194 		uint16_t	uint16;
195 		uint32_t	uint32;
196 		uint64_t	uint64;
197 		int128_t	int128;
198 	}			value;
199 	uint32_t		type, len, param;
200 
201 	/* Get Protocol UUID */
202 	SDP_GET8(type, start);
203 	switch (type) {
204 	case SDP_DATA_UUID16:
205 		SDP_GET16(value.uint16, start);
206 		fprintf(stdout, "\t%s (%#4.4x)\n", sdp_uuid2desc(value.uint16),
207 				value.uint16);
208 		break;
209 
210 	case SDP_DATA_UUID32:
211 		SDP_GET32(value.uint32, start);
212 		fprintf(stdout, "\t%#8.8x\n", value.uint32);
213 		break;
214 
215 	case SDP_DATA_UUID128:
216 		SDP_GET_UUID128(&value.int128, start);
217 		fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
218 				ntohl(*(uint32_t *)&value.int128.b[0]),
219 				ntohs(*(uint16_t *)&value.int128.b[4]),
220 				ntohs(*(uint16_t *)&value.int128.b[6]),
221 				ntohs(*(uint16_t *)&value.int128.b[8]),
222 				ntohs(*(uint16_t *)&value.int128.b[10]),
223 				ntohl(*(uint32_t *)&value.int128.b[12]));
224 		break;
225 
226 	default:
227 		fprintf(stderr, "Invalid Protocol Descriptor. " \
228 				"Not a UUID, type=%#x\n", type);
229 		return;
230 		/* NOT REACHED */
231 	}
232 
233 	/* Protocol specific parameters */
234 	for (param = 1; start < end; param ++) {
235 		fprintf(stdout, "\t\tProtocol specific parameter #%d: ", param);
236 
237 		SDP_GET8(type, start);
238 		switch (type) {
239 		case SDP_DATA_NIL:
240 			fprintf(stdout, "nil\n");
241 			break;
242 
243 		case SDP_DATA_UINT8:
244 		case SDP_DATA_INT8:
245 		case SDP_DATA_BOOL:
246 			SDP_GET8(value.uint8, start);
247 			fprintf(stdout, "u/int8/bool %u\n", value.uint8);
248 			break;
249 
250 		case SDP_DATA_UINT16:
251 		case SDP_DATA_INT16:
252 		case SDP_DATA_UUID16:
253 			SDP_GET16(value.uint16, start);
254 			fprintf(stdout, "u/int/uuid16 %u\n", value.uint16);
255 			break;
256 
257 		case SDP_DATA_UINT32:
258 		case SDP_DATA_INT32:
259 		case SDP_DATA_UUID32:
260 			SDP_GET32(value.uint32, start);
261 			fprintf(stdout, "u/int/uuid32 %u\n", value.uint32);
262 			break;
263 
264 		case SDP_DATA_UINT64:
265 		case SDP_DATA_INT64:
266 			SDP_GET64(value.uint64, start);
267 			fprintf(stdout, "u/int64 %ju\n", value.uint64);
268 			break;
269 
270 		case SDP_DATA_UINT128:
271 		case SDP_DATA_INT128:
272 			SDP_GET128(&value.int128, start);
273 			fprintf(stdout, "u/int128 %#8.8x%8.8x%8.8x%8.8x\n",
274 				*(uint32_t *)&value.int128.b[0],
275 				*(uint32_t *)&value.int128.b[4],
276 				*(uint32_t *)&value.int128.b[8],
277 				*(uint32_t *)&value.int128.b[12]);
278 			break;
279 
280 		case SDP_DATA_UUID128:
281 			SDP_GET_UUID128(&value.int128, start);
282 			fprintf(stdout, "uuid128 %#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
283 				ntohl(*(uint32_t *)&value.int128.b[0]),
284 				ntohs(*(uint16_t *)&value.int128.b[4]),
285 				ntohs(*(uint16_t *)&value.int128.b[6]),
286 				ntohs(*(uint16_t *)&value.int128.b[8]),
287 				ntohs(*(uint16_t *)&value.int128.b[10]),
288 				ntohl(*(uint32_t *)&value.int128.b[12]));
289 			break;
290 
291 		case SDP_DATA_STR8:
292 		case SDP_DATA_URL8:
293 			SDP_GET8(len, start);
294 			fprintf(stdout, "%*.*s\n", len, len,  start);
295 			start += len;
296 			break;
297 
298 		case SDP_DATA_STR16:
299 		case SDP_DATA_URL16:
300 			SDP_GET16(len, start);
301 			fprintf(stdout, "%*.*s\n", len, len,  start);
302 			start += len;
303 			break;
304 
305 		case SDP_DATA_STR32:
306 		case SDP_DATA_URL32:
307 			SDP_GET32(len, start);
308 			fprintf(stdout, "%*.*s\n", len, len,  start);
309 			start += len;
310 			break;
311 
312 		case SDP_DATA_SEQ8:
313 		case SDP_DATA_ALT8:
314 			SDP_GET8(len, start);
315 			for (; len > 0; start ++, len --)
316 				fprintf(stdout, "%#2.2x ", *start);
317 			fprintf(stdout, "\n");
318 			break;
319 
320 		case SDP_DATA_SEQ16:
321 		case SDP_DATA_ALT16:
322 			SDP_GET16(len, start);
323 			for (; len > 0; start ++, len --)
324 				fprintf(stdout, "%#2.2x ", *start);
325 			fprintf(stdout, "\n");
326 			break;
327 
328 		case SDP_DATA_SEQ32:
329 		case SDP_DATA_ALT32:
330 			SDP_GET32(len, start);
331 			for (; len > 0; start ++, len --)
332 				fprintf(stdout, "%#2.2x ", *start);
333 			fprintf(stdout, "\n");
334 			break;
335 
336 		default:
337 			fprintf(stderr, "Invalid Protocol Descriptor. " \
338 					"Unknown data type: %#02x\n", type);
339 			return;
340 			/* NOT REACHED */
341 		}
342 	}
343 } /* print_protocol_descriptor */
344 
345 static void
346 print_protocol_descriptor_list(uint8_t const *start, uint8_t const *end)
347 {
348 	uint32_t	type, len;
349 
350 	if (end - start < 2) {
351 		fprintf(stderr, "Invalid Protocol Descriptor List. " \
352 				"Too short, len=%td\n", end - start);
353 		return;
354 	}
355 
356 	SDP_GET8(type, start);
357 	switch (type) {
358 	case SDP_DATA_SEQ8:
359 		SDP_GET8(len, start);
360 		break;
361 
362 	case SDP_DATA_SEQ16:
363 		SDP_GET16(len, start);
364 		break;
365 
366 	case SDP_DATA_SEQ32:
367 		SDP_GET32(len, start);
368 		break;
369 
370 	default:
371 		fprintf(stderr, "Invalid Protocol Descriptor List. " \
372 				"Not a sequence, type=%#x\n", type);
373 		return;
374 		/* NOT REACHED */
375 	}
376 
377 	while (start < end) {
378 		SDP_GET8(type, start);
379 		switch (type) {
380 		case SDP_DATA_SEQ8:
381 			SDP_GET8(len, start);
382 			break;
383 
384 		case SDP_DATA_SEQ16:
385 			SDP_GET16(len, start);
386 			break;
387 
388 		case SDP_DATA_SEQ32:
389 			SDP_GET32(len, start);
390 			break;
391 
392 		default:
393 			fprintf(stderr, "Invalid Protocol Descriptor List. " \
394 					"Not a sequence, type=%#x\n", type);
395 			return;
396 			/* NOT REACHED */
397 		}
398 
399 		print_protocol_descriptor(start, start + len);
400 		start += len;
401 	}
402 } /* print_protocol_descriptor_list */
403 
404 /*
405  * Print Bluetooth Profile Descriptor List
406  *
407  * The BluetoothProfileDescriptorList attribute consists of a data element
408  * sequence in which each element is a profile descriptor that contains
409  * information about a Bluetooth profile to which the service represented by
410  * this service record conforms. Each profile descriptor is a data element
411  * sequence whose first element is the UUID assigned to the profile and whose
412  * second element is a 16-bit profile version number. Each version of a profile
413  * is assigned a 16-bit unsigned integer profile version number, which consists
414  * of two 8-bit fields. The higher-order 8 bits contain the major version
415  * number field and the lower-order 8 bits contain the minor version number
416  * field.
417  */
418 
419 static void
420 print_bluetooth_profile_descriptor_list(uint8_t const *start, uint8_t const *end)
421 {
422 	uint32_t	type, len, value;
423 
424 	if (end - start < 2) {
425 		fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \
426 				"Too short, len=%td\n", end - start);
427 		return;
428 	}
429 
430 	SDP_GET8(type, start);
431 	switch (type) {
432 	case SDP_DATA_SEQ8:
433 		SDP_GET8(len, start);
434 		break;
435 
436 	case SDP_DATA_SEQ16:
437 		SDP_GET16(len, start);
438 		break;
439 
440 	case SDP_DATA_SEQ32:
441 		SDP_GET32(len, start);
442 		break;
443 
444 	default:
445 		fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \
446 				"Not a sequence, type=%#x\n", type);
447 		return;
448 		/* NOT REACHED */
449 	}
450 
451 	while (start < end) {
452 		SDP_GET8(type, start);
453 		switch (type) {
454 		case SDP_DATA_SEQ8:
455 			SDP_GET8(len, start);
456 			break;
457 
458 		case SDP_DATA_SEQ16:
459 			SDP_GET16(len, start);
460 			break;
461 
462 		case SDP_DATA_SEQ32:
463 			SDP_GET32(len, start);
464 			break;
465 
466 		default:
467 			fprintf(stderr, "Invalid Bluetooth Profile " \
468 					"Descriptor List. " \
469 					"Not a sequence, type=%#x\n", type);
470 			return;
471 			/* NOT REACHED */
472 		}
473 
474 		/* Get UUID */
475 		SDP_GET8(type, start);
476 		switch (type) {
477 		case SDP_DATA_UUID16:
478 			SDP_GET16(value, start);
479 			fprintf(stdout, "\t%s (%#4.4x) ",
480 					sdp_uuid2desc(value), value);
481 			break;
482 
483 		case SDP_DATA_UUID32:
484 			SDP_GET32(value, start);
485 			fprintf(stdout, "\t%#8.8x ", value);
486 			break;
487 
488 		case SDP_DATA_UUID128: {
489 			int128_t	uuid;
490 
491 			SDP_GET_UUID128(&uuid, start);
492 			fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x ",
493 					ntohl(*(uint32_t *)&uuid.b[0]),
494 					ntohs(*(uint16_t *)&uuid.b[4]),
495 					ntohs(*(uint16_t *)&uuid.b[6]),
496 					ntohs(*(uint16_t *)&uuid.b[8]),
497 					ntohs(*(uint16_t *)&uuid.b[10]),
498 					ntohl(*(uint32_t *)&uuid.b[12]));
499 			} break;
500 
501 		default:
502 			fprintf(stderr, "Invalid Bluetooth Profile " \
503 					"Descriptor List. " \
504 					"Not a UUID, type=%#x\n", type);
505 			return;
506 			/* NOT REACHED */
507 		}
508 
509 		/* Get version */
510 		SDP_GET8(type, start);
511 		if (type != SDP_DATA_UINT16) {
512 			fprintf(stderr, "Invalid Bluetooth Profile " \
513 					"Descriptor List. " \
514 					"Invalid version type=%#x\n", type);
515 			return;
516 		}
517 
518 		SDP_GET16(value, start);
519 		fprintf(stdout, "ver. %d.%d\n",
520 				(value >> 8) & 0xff, value & 0xff);
521 	}
522 } /* print_bluetooth_profile_descriptor_list */
523 
524 struct service {
525 	const char	*name;
526 	uint16_t	class;
527 	const char	*description;
528 } services[] = {
529 	{ "CIP",	SDP_SERVICE_CLASS_COMMON_ISDN_ACCESS,
530 	  "Common ISDN Access"		},
531 	{ "CTP",	SDP_SERVICE_CLASS_CORDLESS_TELEPHONY,
532 	  "Cordless Telephony"		},
533 	{ "DUN",	SDP_SERVICE_CLASS_DIALUP_NETWORKING,
534 	  "Dial Up Networking"		},
535 	{ "FAX",	SDP_SERVICE_CLASS_FAX,
536 	  "Fax"				},
537 	{ "FTRN",	SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER,
538 	  "Obex File Transfer"		},
539 	{ "GN",		SDP_SERVICE_CLASS_GN,
540 	  "Group ad-hoc Network"	},
541 	{ "HID",	SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE,
542 	  "Human Interface Device"	},
543 	{ "HF",		SDP_SERVICE_CLASS_HANDSFREE,
544 	  "Handsfree"			},
545 	{ "HSET",	SDP_SERVICE_CLASS_HEADSET,
546 	  "Headset"			},
547 	{ "LAN",	SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,
548 	  "Lan access using PPP"	},
549 	{ "NAP",	SDP_SERVICE_CLASS_NAP,
550 	  "Network Access Point"	},
551 	{ "OPUSH",	SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH,
552 	  "OBEX Object Push"		},
553 	{ "PANU",	SDP_SERVICE_CLASS_PANU,
554 	  "Personal Area Networking User"		},
555 	{ "SP",		SDP_SERVICE_CLASS_SERIAL_PORT,
556 	  "Serial Port"			},
557 	{ NULL,		0,
558 	  NULL				}
559 };
560 
561 /* Perform SDP search command */
562 int
563 do_sdp_search(bdaddr_t *laddr, bdaddr_t *raddr, int argc, char const **argv)
564 {
565 	struct service	*s;
566 	void		*xs;
567 	char		*ep;
568 	uint32_t	 n;
569 	int32_t		 type, value;
570 	uint16_t	 service;
571 
572 	if (argc != 1)
573 		goto usage;
574 
575 	service = strtoul(*argv, &ep, 16);
576 	if (*ep != 0) {
577 		for (s = services ; ; s++) {
578 			if (s->name == NULL)
579 				goto usage;
580 
581 			if (strcasecmp(s->name, *argv) == 0)
582 				break;
583 		}
584 		service = s->class;
585 	}
586 
587 	/* Initialize attribute values array */
588 	for (n = 0; n < values_len; n ++) {
589 		values[n].flags = SDP_ATTR_INVALID;
590 		values[n].attr = 0;
591 		values[n].vlen = BSIZE;
592 		values[n].value = buffer[n];
593 	}
594 
595 	if (bdaddr_any(raddr))
596 		xs = sdp_open_local(control_socket);
597 	else
598 		xs = sdp_open(laddr, raddr);
599 
600 	if (xs == NULL || (errno = sdp_error(xs)) != 0)
601 		err(EXIT_FAILURE, "sdp_open");
602 
603 	/* Do SDP Service Search Attribute Request */
604 	n = sdp_search(xs, 1, &service, attrs_len, attrs, values_len, values);
605 	if (n != 0)
606 		err(EXIT_FAILURE, "sdp_search");
607 
608 	sdp_close(xs);
609 
610 	/* Print attributes values */
611 	for (n = 0; n < values_len; n ++) {
612 		if (values[n].flags != SDP_ATTR_OK)
613 			break;
614 
615 		switch (values[n].attr) {
616 		case SDP_ATTR_SERVICE_RECORD_HANDLE:
617 			fprintf(stdout, "\n");
618 			if (values[n].vlen == 5) {
619 				SDP_GET8(type, values[n].value);
620 				if (type == SDP_DATA_UINT32) {
621 					SDP_GET32(value, values[n].value);
622 					fprintf(stdout, "Record Handle: " \
623 							"%#8.8x\n", value);
624 				} else
625 					fprintf(stderr, "Invalid type=%#x " \
626 							"Record Handle " \
627 							"attribute!\n", type);
628 			} else
629 				fprintf(stderr, "Invalid size=%d for Record " \
630 						"Handle attribute\n",
631 						values[n].vlen);
632 			break;
633 
634 		case SDP_ATTR_SERVICE_CLASS_ID_LIST:
635 			fprintf(stdout, "Service Class ID List:\n");
636 			print_service_class_id_list(values[n].value,
637 					values[n].value + values[n].vlen);
638 			break;
639 
640 		case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
641 			fprintf(stdout, "Protocol Descriptor List:\n");
642 			print_protocol_descriptor_list(values[n].value,
643 					values[n].value + values[n].vlen);
644 			break;
645 
646 		case SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST:
647 			fprintf(stdout, "Bluetooth Profile Descriptor List:\n");
648 			print_bluetooth_profile_descriptor_list(values[n].value,
649 					values[n].value + values[n].vlen);
650 			break;
651 
652 		default:
653 			fprintf(stderr, "Unexpected attribute ID=%#4.4x\n",
654 					values[n].attr);
655 			break;
656 		}
657 	}
658 
659 	return EXIT_SUCCESS;
660 
661 usage:
662 	fprintf(stderr, "Known services:\n");
663 	for (s = services ; s->name != NULL ; s++)
664 		fprintf(stderr, "\t%s\t%s\n", s->name, s->description);
665 
666 	return EXIT_FAILURE;
667 } /* do_sdp_search */
668 
669 /* Perform SDP browse command */
670 int
671 do_sdp_browse(bdaddr_t *laddr, bdaddr_t *raddr, int argc, char const **argv)
672 {
673 #undef	_STR
674 #undef	STR
675 #define	_STR(x)	#x
676 #define	STR(x)	_STR(x)
677 
678 	static char	const * av[] = {
679 		STR(SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP),
680 		NULL
681 	};
682 
683 	switch (argc) {
684 	case 0:
685 		argc = 1;
686 		argv =  (char const **) av;
687 		/* FALL THROUGH */
688 	case 1:
689 		return (do_sdp_search(laddr, raddr, argc, argv));
690 	}
691 
692 	return EXIT_FAILURE;
693 } /* do_sdp_browse */
694