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