Module Name:    src
Committed By:   plunky
Date:           Tue May 12 18:37:50 UTC 2009

Modified Files:
        src/usr.bin/sdpquery: Makefile sdpquery.1 sdpquery.c sdpquery.h
Added Files:
        src/usr.bin/sdpquery: command.c print.c
Removed Files:
        src/usr.bin/sdpquery: search.c

Log Message:
update sdpquery with a newer version that fetches the whole service
record and displays it in a human readable fashion.


To generate a diff of this commit:
cvs rdiff -u -r1.5 -r1.6 src/usr.bin/sdpquery/Makefile
cvs rdiff -u -r0 -r1.1 src/usr.bin/sdpquery/command.c \
    src/usr.bin/sdpquery/print.c
cvs rdiff -u -r1.9 -r1.10 src/usr.bin/sdpquery/sdpquery.1
cvs rdiff -u -r1.4 -r1.5 src/usr.bin/sdpquery/sdpquery.c
cvs rdiff -u -r1.2 -r1.3 src/usr.bin/sdpquery/sdpquery.h
cvs rdiff -u -r1.7 -r0 src/usr.bin/sdpquery/search.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.bin/sdpquery/Makefile
diff -u src/usr.bin/sdpquery/Makefile:1.5 src/usr.bin/sdpquery/Makefile:1.6
--- src/usr.bin/sdpquery/Makefile:1.5	Tue May 12 13:15:10 2009
+++ src/usr.bin/sdpquery/Makefile	Tue May 12 18:37:50 2009
@@ -1,14 +1,12 @@
-# $NetBSD: Makefile,v 1.5 2009/05/12 13:15:10 plunky Exp $
+# $NetBSD: Makefile,v 1.6 2009/05/12 18:37:50 plunky Exp $
 
 USE_FORT?= yes	# network client
 
 PROG=		sdpquery
 MAN=		sdpquery.1
-SRCS=		sdpquery.c search.c
+SRCS=		sdpquery.c command.c print.c
 
 DPADD+=		${LIBBLUETOOTH}
 LDADD+=		-lbluetooth
 
-CPPFLAGS+=	-DSDP_COMPAT
-
 .include <bsd.prog.mk>

Index: src/usr.bin/sdpquery/sdpquery.1
diff -u src/usr.bin/sdpquery/sdpquery.1:1.9 src/usr.bin/sdpquery/sdpquery.1:1.10
--- src/usr.bin/sdpquery/sdpquery.1:1.9	Tue Mar 10 20:30:08 2009
+++ src/usr.bin/sdpquery/sdpquery.1	Tue May 12 18:37:50 2009
@@ -1,4 +1,4 @@
-.\"	$NetBSD: sdpquery.1,v 1.9 2009/03/10 20:30:08 joerg Exp $
+.\"	$NetBSD: sdpquery.1,v 1.10 2009/05/12 18:37:50 plunky Exp $
 .\"
 .\" Copyright (c) 2006 Itronix Inc.
 .\" All rights reserved.
@@ -27,6 +27,7 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
+.\" Copyright (c) 2009 The NetBSD Foundation, Inc.
 .\" Copyright (c) 2003 Maksim Yevmenkin <m_evmen...@yahoo.com>
 .\" All rights reserved.
 .\"
@@ -51,24 +52,23 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $Id: sdpquery.1,v 1.9 2009/03/10 20:30:08 joerg Exp $
 .\" $FreeBSD: src/usr.sbin/bluetooth/sdpcontrol/sdpcontrol.8,v 1.6 2005/07/09 19:04:43 markus Exp $
 .\"
-.Dd February 17, 2007
+.Dd May 7, 2009
 .Dt SDPQUERY 1
 .Os
 .Sh NAME
 .Nm sdpquery
-.Nd SDP query utility
+.Nd Service Discovery Protocol query utility
 .Sh SYNOPSIS
 .Nm
-.Fl h
-.Nm
+.Op Fl NRX
 .Op Fl d Ar device
 .Fl a Ar address
 .Ar command
 .Op Ar parameters ...
 .Nm
+.Op Fl NRX
 .Op Fl c Ar path
 .Fl l
 .Ar command
@@ -82,8 +82,9 @@
 Connection to the local SDP server is made via the control socket.
 The
 .Nm
-utility uses Service Search Attribute Requests and prints results to
-standard output and error messages to standard error.
+utility retrieves complete Service Records as directed and prints
+each records attribute ID/value list to standard output and error
+messages to standard error.
 .Pp
 The options are as follows:
 .Bl -tag -width ".Fl a Ar address"
@@ -112,61 +113,84 @@
 utility will use the best available.
 .It Fl l
 Query the local SDP server via the control socket.
-.It Fl h
-Display usage message and exit.
+.It Fl N
+Additionally display numerical values.
+.It Fl R
+Display service attributes in raw (uninterpreted) format.
+.It Fl X
+Display service attribute values in hex.
 .El
 .Pp
 The currently supported commands in
 .Nm
 are:
 .Pp
-.Bl -tag -width ".Cm search Ar service" -compact
+.Bl -tag -width Browse -compact
 .It Cm Browse Op Ar group
 Browse for services.
 The
 .Ar group
 parameter is a 16-bit UUID of the group to browse.
-If omitted, the Public Browse Group. is used.
-.Pp
-.It Cm Search Ar service
-Search for the
-.Ar service .
-The
-.Ar service
-parameter is a 16-bit UUID of the service to search for.
-For the following services it is possible to use service name
-instead of service UUID:
+If omitted, the "Public Browse Group" is used.
 .Pp
-.Bl -tag -compact -width OPUSH
+.It Cm Record Ar handle Op Ar handle...
+Retrieve the Service Record with the given
+.Ar handle .
+Multiple handles can be given.
+.Pp
+.It Cm Search Ar uuid Op Ar uuid...
+Search for records matching the list of
+.Pq 16-bit
+UUIDs which can be given in numerical form, or the
+following aliases are known:
+.Pp
+.Bl -tag -offset indent -compact -width RFCOMMxxx
+.It A2DP
+Advanced Audio Distribution Profile
+.It BNEP
+Bluetooth Network Encapsulation Protocol
 .It CIP
-Common ISDN Access
+Common ISDN Access Service
 .It CTP
-Cordless Telephony
+Cordless Telephony Service
 .It DUN
-DialUp Networking
+Dialup Networking Service
 .It FAX
-Fax
+Fax Service
 .It FTRN
-OBEX File Transfer
+File Transfer Service
 .It GN
-Group ad-hoc Network
+Group ad-hoc Network Service
 .It HID
-Human Interface Device
+Human Interface Device Service
 .It HF
-Handsfree
+Handsfree Service
 .It HSET
-Headset
+Headset Service
+.It L2CAP
+Logical Link Control and Adaptation Protocol
 .It LAN
-LAN Access Using PPP
+LAN Access Using PPP Service
 .It NAP
-Network Access Point
+Network Access Point Service
+.It OBEX
+Object Exchange Protocol
 .It OPUSH
-OBEX Object Push
+Object Push Service
 .It PANU
-Personal Area Networking User
+Personal Area Networking User Service
+.It RFCOMM
+RFCOMM Protocol
+.It SDP
+Service Discovery Protocol
 .It SP
-Serial Port
+Serial Port Service
+.It SYNC
+IrMC Sync Client Service
 .El
+.Pp
+Note that in order for a record to match, it must contain all the
+UUIDs in the ServiceSearchPattern and the maximum number is 12.
 .El
 .Sh EXIT STATUS
 .Ex -std
@@ -191,18 +215,8 @@
 .An Maksim Yevmenkin Aq m_evmen...@yahoo.com
 .An Iain Hibbert
 for Itronix, Inc.
-.Sh CAVEATS
-The
+.Sh BUGS
 .Nm
-utility only requests the following attributes from the SDP server:
-.Pp
-.Bl -enum -offset indent -compact
-.It
-Service Record Handle
-.It
-Service Class ID List
-.It
-Protocol Descriptor List
-.It
-Bluetooth Profile Descriptor List
-.El
+will only search for Bluetooth
+.Qq short alias
+16-bit UUIDs.

Index: src/usr.bin/sdpquery/sdpquery.c
diff -u src/usr.bin/sdpquery/sdpquery.c:1.4 src/usr.bin/sdpquery/sdpquery.c:1.5
--- src/usr.bin/sdpquery/sdpquery.c:1.4	Mon Jul 21 14:19:26 2008
+++ src/usr.bin/sdpquery/sdpquery.c	Tue May 12 18:37:50 2009
@@ -1,4 +1,4 @@
-/*	$NetBSD: sdpquery.c,v 1.4 2008/07/21 14:19:26 lukem Exp $	*/
+/*	$NetBSD: sdpquery.c,v 1.5 2009/05/12 18:37:50 plunky Exp $	*/
 
 /*-
  * Copyright (c) 2006 Itronix Inc.
@@ -32,10 +32,11 @@
  */
 
 #include <sys/cdefs.h>
-__COPYRIGHT("@(#) Copyright (c) 2006 Itronix, Inc.  All rights reserved.");
-__RCSID("$NetBSD: sdpquery.c,v 1.4 2008/07/21 14:19:26 lukem Exp $");
+__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc.\
+ Copyright (c) 2006 Itronix, Inc.\
+ All rights reserved.");
+__RCSID("$NetBSD: sdpquery.c,v 1.5 2009/05/12 18:37:50 plunky Exp $");
 
-#include <assert.h>
 #include <bluetooth.h>
 #include <err.h>
 #include <errno.h>
@@ -51,39 +52,47 @@
 
 const char *control_socket;
 
+bdaddr_t local_addr;
+bdaddr_t remote_addr;
+
+bool Nflag;	/* numerical output */
+bool Rflag;	/* uncooked output */
+bool Xflag;	/* hex output */
+
 static struct command {
 	const char	*command;
-	int		(*handler)(bdaddr_t *, bdaddr_t *, int, char const **);
+	int		(*handler)(int, char const **);
 	const char	*usage;
 } commands[] = {
-	{ "Browse",	do_sdp_browse,	"[UUID]"	},
-	{ "Search",	do_sdp_search,	"<service>"	},
-	{ NULL,		NULL,		NULL		}
+	{ "Browse",	do_sdp_browse,	"[group]"		},
+	{ "Record",	do_sdp_record,	"handle [handle...]"	},
+	{ "Search",	do_sdp_search,	"uuid [uuid...]"	},
+	{ NULL,		NULL,		NULL			}
 };
 
 int
 main(int argc, char *argv[])
 {
-	bdaddr_t	laddr, raddr;
 	struct command *cmd;
 	int		ch, local;
 
-	bdaddr_copy(&laddr, BDADDR_ANY);
-	bdaddr_copy(&raddr, BDADDR_ANY);
+	bdaddr_copy(&local_addr, BDADDR_ANY);
+	bdaddr_copy(&remote_addr, BDADDR_ANY);
 	control_socket = NULL;
+	Nflag = Rflag = Xflag = false;
 	local = 0;
 
-	while ((ch = getopt(argc, argv, "a:c:d:hl")) != -1) {
+	while ((ch = getopt(argc, argv, "a:c:d:hlNRX")) != -1) {
 		switch (ch) {
 		case 'a': /* remote address */
-			if (!bt_aton(optarg, &raddr)) {
+			if (!bt_aton(optarg, &remote_addr)) {
 				struct hostent  *he = NULL;
 
 				if ((he = bt_gethostbyname(optarg)) == NULL)
 					errx(EXIT_FAILURE, "%s: %s",
 						optarg, hstrerror(h_errno));
 
-				bdaddr_copy(&raddr, (bdaddr_t *)he->h_addr);
+				bdaddr_copy(&remote_addr, (bdaddr_t *)he->h_addr);
 			}
 			break;
 
@@ -92,7 +101,7 @@
 			break;
 
 		case 'd': /* local device address */
-			if (!bt_devaddr(optarg, &laddr))
+			if (!bt_devaddr(optarg, &local_addr))
 				err(EXIT_FAILURE, "%s", optarg);
 
 			break;
@@ -101,6 +110,18 @@
 			local = 1;
 			break;
 
+		case 'N': /* Numerical output */
+			Nflag = true;
+			break;
+
+		case 'R': /* Raw output */
+			Rflag = true;
+			break;
+
+		case 'X': /* Hex output */
+			Xflag = true;
+			break;
+
 		case 'h':
 		default:
 			usage();
@@ -115,27 +136,27 @@
 	optreset = 1;
 
 	if (argc < 1
-	    || (bdaddr_any(&raddr) && !local)
-	    || (!bdaddr_any(&raddr) && local))
+	    || (bdaddr_any(&remote_addr) && !local)
+	    || (!bdaddr_any(&remote_addr) && local))
 		usage();
 
 	for (cmd = commands ; cmd->command != NULL; cmd++) {
 		if (strcasecmp(*argv, cmd->command) == 0)
-			return (*cmd->handler)(&laddr, &raddr, --argc, (char const **)++argv);
+			return (*cmd->handler)(--argc, (char const **)++argv);
 	}
 
-	errx(EXIT_FAILURE, "%s: Unknown Command", *argv);
+	usage();
+	return EXIT_FAILURE;
 }
 
-/* Usage */
 static void
 usage(void)
 {
 	struct command *cmd;
 
 	fprintf(stderr,
-		"Usage: %s [-d device] -a bdaddr <command> [parameters..]\n"
-		"       %s [-c path] -l <command> [parameters..]\n"
+		"Usage: %s [-NRX] [-d device] -a bdaddr <command> [parameters..]\n"
+		"       %s [-NRX] [-c path] -l <command> [parameters..]\n"
 		"\n", getprogname(), getprogname());
 
 	fprintf(stderr,
@@ -143,8 +164,10 @@
 		"\t-a bdaddr    remote address\n"
 		"\t-c path      path to control socket\n"
 		"\t-d device    local device address\n"
-		"\t-l           connect to the local SDP server via control socket\n"
-		"\t-h           display usage and quit\n"
+		"\t-l           query local SDP server daemon\n"
+		"\t-N           print numerical values\n"
+		"\t-R           print raw attribute values\n"
+		"\t-X           print attribute values in hex\n"
 		"\n"
 		"Commands:\n");
 

Index: src/usr.bin/sdpquery/sdpquery.h
diff -u src/usr.bin/sdpquery/sdpquery.h:1.2 src/usr.bin/sdpquery/sdpquery.h:1.3
--- src/usr.bin/sdpquery/sdpquery.h:1.2	Wed Jul 26 10:54:49 2006
+++ src/usr.bin/sdpquery/sdpquery.h	Tue May 12 18:37:50 2009
@@ -1,4 +1,4 @@
-/*	$NetBSD: sdpquery.h,v 1.2 2006/07/26 10:54:49 tron Exp $	*/
+/*	$NetBSD: sdpquery.h,v 1.3 2009/05/12 18:37:50 plunky Exp $	*/
 
 /*-
  * Copyright (c) 2006 Itronix Inc.
@@ -31,12 +31,14 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef __BTQUERY_H__
-#define __BTQUERY_H__
-
 extern const char *control_socket;
+extern bdaddr_t local_addr;
+extern bdaddr_t remote_addr;
+
+extern bool Nflag, Rflag, Xflag;
 
-int do_sdp_browse(bdaddr_t *, bdaddr_t *, int, char const **);
-int do_sdp_search(bdaddr_t *, bdaddr_t *, int, char const **);
+int do_sdp_browse(int, const char **);
+int do_sdp_record(int, const char **);
+int do_sdp_search(int, const char **);
 
-#endif /* __BTQUERY_H__ */
+void print_record(sdp_data_t *);

Added files:

Index: src/usr.bin/sdpquery/command.c
diff -u /dev/null src/usr.bin/sdpquery/command.c:1.1
--- /dev/null	Tue May 12 18:37:50 2009
+++ src/usr.bin/sdpquery/command.c	Tue May 12 18:37:50 2009
@@ -0,0 +1,240 @@
+/*	$NetBSD: command.c,v 1.1 2009/05/12 18:37:50 plunky Exp $	*/
+
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Iain Hibbert.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: command.c,v 1.1 2009/05/12 18:37:50 plunky Exp $");
+
+#include <bluetooth.h>
+#include <err.h>
+#include <sdp.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sdpquery.h"
+
+static sdp_session_t open_session(void);
+static void build_ssp(sdp_data_t *, int, const char **);
+
+static struct alias {
+	uint16_t	uuid;
+	const char *	name;
+	const char *	desc;
+} aliases[] = {
+	{ SDP_SERVICE_CLASS_ADVANCED_AUDIO_DISTRIBUTION,
+	  "A2DP",	"Advanced Audio Distribution Profile"		},
+	{ SDP_UUID_PROTOCOL_BNEP,
+	  "BNEP",	"Bluetooth Network Encapsulation Protocol"	},
+	{ SDP_SERVICE_CLASS_COMMON_ISDN_ACCESS,
+	  "CIP",	"Common ISDN Access Service"			},
+	{ SDP_SERVICE_CLASS_CORDLESS_TELEPHONY,
+	  "CTP",	"Cordless Telephony Service"			},
+	{ SDP_SERVICE_CLASS_DIALUP_NETWORKING,
+	  "DUN",	"Dial Up Networking Service"			},
+	{ SDP_SERVICE_CLASS_FAX,
+	  "FAX",	"Fax Service"					},
+	{ SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER,
+	  "FTRN",	"File Transfer Service"				},
+	{ SDP_SERVICE_CLASS_GN,
+	  "GN",		"Group ad-hoc Network Service"			},
+	{ SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE,
+	  "HID",	"Human Interface Device Service"		},
+	{ SDP_SERVICE_CLASS_HANDSFREE,
+	  "HF",		"Handsfree Service"				},
+	{ SDP_SERVICE_CLASS_HEADSET,
+	  "HSET",	"Headset Service"				},
+	{ SDP_UUID_PROTOCOL_L2CAP,
+	  "L2CAP",	"Logical Link Control and Adapatation Protocol"	},
+	{ SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,
+	  "LAN",	"Lan access using PPP Service"			},
+	{ SDP_SERVICE_CLASS_NAP,
+	  "NAP",	"Network Access Point Service"			},
+	{ SDP_UUID_PROTOCOL_OBEX,
+	  "OBEX",	"Object Exchange Protocol"			},
+	{ SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH,
+	  "OPUSH",	"Object Push Service"				},
+	{ SDP_SERVICE_CLASS_PANU,
+	  "PANU",	"Personal Area Networking User Service"		},
+	{ SDP_UUID_PROTOCOL_RFCOMM,
+	  "RFCOMM",	"RFCOMM Protocol"				},
+	{ SDP_UUID_PROTOCOL_SDP,
+	  "SDP",	"Service Discovery Protocol"			},
+	{ SDP_SERVICE_CLASS_SERIAL_PORT,
+	  "SP",		"Serial Port Service"				},
+	{ SDP_SERVICE_CLASS_IR_MC_SYNC,
+	  "SYNC",	"IrMC Sync Client Service"			},
+};
+
+int
+do_sdp_browse(int argc, const char **argv)
+{
+#define STR(x)	__STRING(x)
+	const char *av = STR(SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP);
+#undef STR
+
+	if (argc > 1)
+		errx(EXIT_FAILURE, "Too many arguments");
+
+	if (argc == 0) {
+		argc = 1;
+		argv = &av;
+	}
+
+	return do_sdp_search(argc, argv);
+}
+
+int
+do_sdp_record(int argc, const char **argv)
+{
+	sdp_session_t	ss;
+	sdp_data_t	rsp;
+	char *		ep;
+	unsigned long	handle;
+	bool		rv;
+
+	if (argc == 0)
+		errx(EXIT_FAILURE, "Record handle required");
+
+	ss = open_session();
+
+	for (; argc-- > 0; argv++) {
+		handle = strtoul(*argv, &ep, 0);
+		if (*argv[0] == '\0' || *ep != '\0' || handle > UINT32_MAX)
+			errx(EXIT_FAILURE, "Invalid handle: %s\n", *argv);
+
+		rv = sdp_service_attribute(ss, handle, NULL, &rsp);
+		if (!rv)
+			warn("%s", *argv);
+		else
+			print_record(&rsp);
+
+		if (argc > 0)
+			printf("\n\n");
+	}
+
+	sdp_close(ss);
+	return EXIT_SUCCESS;
+}
+
+int
+do_sdp_search(int argc, const char **argv)
+{
+	sdp_session_t	ss;
+	sdp_data_t	ssp, rec, rsp;
+	bool		rv;
+
+	if (argc < 1)
+		errx(EXIT_FAILURE, "UUID required");
+
+	if (argc > 12)
+		errx(EXIT_FAILURE, "Too many UUIDs");
+
+	build_ssp(&ssp, argc, argv);
+
+	ss = open_session();
+
+	rv = sdp_service_search_attribute(ss, &ssp, NULL, &rsp);
+	if (!rv)
+		err(EXIT_FAILURE, "sdp_service_search_attribute");
+
+	while (sdp_get_seq(&rsp, &rec)) {
+		if (!rv)
+			printf("\n\n");
+		else
+			rv = false;
+
+		print_record(&rec);
+	}
+
+	if (rsp.next != rsp.end) {
+		printf("\n\nAdditional Data:\n");
+		sdp_data_print(&rsp, 4);
+	}
+
+	sdp_close(ss);
+
+	return EXIT_SUCCESS;
+}
+
+static sdp_session_t
+open_session(void)
+{
+	sdp_session_t ss;
+
+	if (bdaddr_any(&remote_addr))
+		ss = sdp_open_local(control_socket);
+	else
+		ss = sdp_open(&local_addr, &remote_addr);
+
+	if (ss == NULL)
+		err(EXIT_FAILURE, "sdp_open");
+
+	return ss;
+}
+
+/*
+ * build ServiceSearchPattern from arglist
+ */
+static void
+build_ssp(sdp_data_t *ssp, int argc, const char **argv)
+{
+	static uint8_t	data[36];	/* 12 * sizeof(uuid16) */
+	char *		ep;
+	unsigned long	uuid;
+	int		i;
+
+	ssp->next = data;
+	ssp->end = data + sizeof(data);
+
+	for (; argc-- > 0; argv++) {
+		uuid = strtoul(*argv, &ep, 0);
+		if (*argv[0] == '\0' || *ep != '\0') {
+			for (i = 0;; i++) {
+				if (i == __arraycount(aliases))
+					errx(EXIT_FAILURE,
+					    "Unknown alias \"%s\"", *argv);
+
+				if (strcasecmp(aliases[i].name, *argv) == 0)
+					break;
+			}
+
+			uuid = aliases[i].uuid;
+		}
+
+		if (uuid > UINT16_MAX)
+			errx(EXIT_FAILURE, "%s: Not 16-bit UUID", *argv);
+
+		sdp_put_uuid16(ssp, uuid);
+	}
+
+	ssp->end = ssp->next;
+	ssp->next = data;
+	return;
+}
Index: src/usr.bin/sdpquery/print.c
diff -u /dev/null src/usr.bin/sdpquery/print.c:1.1
--- /dev/null	Tue May 12 18:37:50 2009
+++ src/usr.bin/sdpquery/print.c	Tue May 12 18:37:50 2009
@@ -0,0 +1,1301 @@
+/*	$NetBSD: print.c,v 1.1 2009/05/12 18:37:50 plunky Exp $	*/
+
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Iain Hibbert.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: print.c,v 1.1 2009/05/12 18:37:50 plunky Exp $");
+
+#include <ctype.h>
+#include <iconv.h>
+#include <langinfo.h>
+#include <sdp.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <uuid.h>
+#include <vis.h>
+
+#include "sdpquery.h"
+
+typedef struct {
+	uint16_t	id;
+	const char *	desc;
+	void		(*print)(sdp_data_t *);
+} attr_t;
+
+typedef struct {
+	uint16_t	class;
+	const char *	desc;
+	attr_t *	attrs;
+	size_t		nattr;
+} service_t;
+
+typedef struct {
+	uint16_t	base;
+	const char *	codeset;
+} language_t;
+
+static const char *string_uuid(uuid_t *);
+static const char *string_vis(int, const char *, size_t);
+
+static void print_hexdump(const char *, const uint8_t *, size_t);
+static bool print_attribute(uint16_t, sdp_data_t *, attr_t *, int);
+static bool print_universal_attribute(uint16_t, sdp_data_t *);
+static bool print_language_attribute(uint16_t, sdp_data_t *);
+static bool print_service_attribute(uint16_t, sdp_data_t *);
+
+static void print_bool(sdp_data_t *);
+static void print_uint8x(sdp_data_t *);
+static void print_uint16d(sdp_data_t *);
+static void print_uint32x(sdp_data_t *);
+static void print_uint32d(sdp_data_t *);
+static void print_uuid(sdp_data_t *);
+static void print_uuid_list(sdp_data_t *);
+static void print_string(sdp_data_t *);
+static void print_url(sdp_data_t *);
+static void print_profile_version(sdp_data_t *);
+static void print_language_string(sdp_data_t *);
+
+static void print_service_class_id_list(sdp_data_t *);
+static void print_protocol_descriptor(sdp_data_t *);
+static void print_protocol_descriptor_list(sdp_data_t *);
+static void print_language_base_attribute_id_list(sdp_data_t *);
+static void print_service_availability(sdp_data_t *);
+static void print_bluetooth_profile_descriptor_list(sdp_data_t *);
+static void print_additional_protocol_descriptor_lists(sdp_data_t *);
+static void print_sds_version_number_list(sdp_data_t *);
+static void print_ct_network(sdp_data_t *);
+static void print_asrc_features(sdp_data_t *);
+static void print_asink_features(sdp_data_t *);
+static void print_avrcp_features(sdp_data_t *);
+static void print_supported_data_stores(sdp_data_t *);
+static void print_supported_formats(sdp_data_t *);
+static void print_hid_version(sdp_data_t *);
+static void print_hid_device_subclass(sdp_data_t *);
+static void print_hid_descriptor_list(sdp_data_t *);
+static void print_security_description(sdp_data_t *);
+static void print_hf_features(sdp_data_t *);
+static void print_hfag_network(sdp_data_t *);
+static void print_hfag_features(sdp_data_t *);
+static void print_net_access_type(sdp_data_t *);
+
+static void print_rfcomm(sdp_data_t *);
+static void print_bnep(sdp_data_t *);
+static void print_avctp(sdp_data_t *);
+static void print_avdtp(sdp_data_t *);
+static void print_l2cap(sdp_data_t *);
+
+attr_t protocol_list[] = {
+	{ 0x0001, "SDP",				NULL },
+	{ 0x0002, "UDP",				NULL },
+	{ 0x0003, "RFCOMM",				print_rfcomm },
+	{ 0x0004, "TCP",				NULL },
+	{ 0x0005, "TCS_BIN",				NULL },
+	{ 0x0006, "TCS_AT",				NULL },
+	{ 0x0008, "OBEX",				NULL },
+	{ 0x0009, "IP",					NULL },
+	{ 0x000a, "FTP",				NULL },
+	{ 0x000c, "HTTP",				NULL },
+	{ 0x000e, "WSP",				NULL },
+	{ 0x000f, "BNEP",				print_bnep },
+	{ 0x0010, "UPNP",				NULL },
+	{ 0x0011, "HIDP",				NULL },
+	{ 0x0012, "HARDCOPY_CONTROL_CHANNEL",		NULL },
+	{ 0x0014, "HARDCOPY_DATA_CHANNEL",		NULL },
+	{ 0x0016, "HARDCOPY_NOTIFICATION",		NULL },
+	{ 0x0017, "AVCTP",				print_avctp },
+	{ 0x0019, "AVDTP",				print_avdtp },
+	{ 0x001b, "CMTP",				NULL },
+	{ 0x001d, "UDI_C_PLANE",			NULL },
+	{ 0x0100, "L2CAP",				print_l2cap },
+};
+
+attr_t universal_attrs[] = {
+	{ 0x0000, "ServiceRecordHandle",		print_uint32x },
+	{ 0x0001, "ServiceClassIDList",			print_service_class_id_list },
+	{ 0x0002, "ServiceRecordState",			print_uint32x },
+	{ 0x0003, "ServiceID",				print_uuid },
+	{ 0x0004, "ProtocolDescriptorList",		print_protocol_descriptor_list },
+	{ 0x0005, "BrowseGroupList",			print_uuid_list },
+	{ 0x0006, "LanguageBaseAttributeIDList",	print_language_base_attribute_id_list },
+	{ 0x0007, "ServiceInfoTimeToLive",		print_uint32d },
+	{ 0x0008, "ServiceAvailability",		print_service_availability },
+	{ 0x0009, "BluetoothProfileDescriptorList",	print_bluetooth_profile_descriptor_list },
+	{ 0x000a, "DocumentationURL",			print_url },
+	{ 0x000b, "ClientExecutableURL",		print_url },
+	{ 0x000c, "IconURL",				print_url },
+	{ 0x000d, "AdditionalProtocolDescriptorLists",	print_additional_protocol_descriptor_lists },
+};
+
+attr_t language_attrs[] = { /* Language Attribute Offsets */
+	{ 0x0000, "ServiceName",			print_language_string },
+	{ 0x0001, "ServiceDescription",			print_language_string },
+	{ 0x0002, "ProviderName",			print_language_string },
+};
+
+attr_t sds_attrs[] = {	/* Service Discovery Server */
+	{ 0x0200, "VersionNumberList",			print_sds_version_number_list },
+	{ 0x0201, "ServiceDatabaseState",		print_uint32x },
+};
+
+attr_t bgd_attrs[] = {	/* Browse Group Descriptor */
+	{ 0x0200, "GroupID",				print_uuid },
+};
+
+attr_t ct_attrs[] = { /* Cordless Telephony */
+	{ 0x0301, "ExternalNetwork",			print_ct_network },
+};
+
+attr_t asrc_attrs[] = { /* Audio Source */
+	{ 0x0311, "SupportedFeatures",			print_asrc_features },
+};
+
+attr_t asink_attrs[] = { /* Audio Sink */
+	{ 0x0311, "SupportedFeatures",			print_asink_features },
+};
+
+attr_t avrcp_attrs[] = { /* Audio Video Remote Control Profile */
+	{ 0x0311, "SupportedFeatures",			print_avrcp_features },
+};
+
+attr_t lan_attrs[] = {	/* LAN Access Using PPP */
+	{ 0x0200, "IPSubnet",				print_string },
+};
+
+attr_t dun_attrs[] = {	/* Dialup Networking */
+	{ 0x0305, "AudioFeedbackSupport",		print_bool },
+};
+
+attr_t irmc_sync_attrs[] = { /* IrMC Sync */
+	{ 0x0301, "SupportedDataStoresList",		print_supported_data_stores },
+};
+
+attr_t opush_attrs[] = { /* Object Push */
+	{ 0x0303, "SupportedFormatsList",		print_supported_formats },
+};
+
+attr_t hset_attrs[] = {	/* Headset */
+	{ 0x0302, "RemoteAudioVolumeControl",		print_bool },
+};
+
+attr_t fax_attrs[] = {	/* Fax */
+	{ 0x0302, "FAXClass1",				print_bool },
+	{ 0x0303, "FAXClass2.0",			print_bool },
+	{ 0x0304, "FAXClass2",				print_bool },
+	{ 0x0305, "AudioFeedbackSupport",		print_bool },
+};
+
+attr_t panu_attrs[] = {	/* Personal Area Networking User */
+	{ 0x030a, "SecurityDescription",		print_security_description },
+};
+
+attr_t nap_attrs[] = {	/* Network Access Point */
+	{ 0x030a, "SecurityDescription",		print_security_description },
+	{ 0x030b, "NetAccessType",			print_net_access_type },
+	{ 0x030c, "MaxNetAccessRate",			print_uint32d },
+	{ 0x030d, "IPv4Subnet",				print_string },
+	{ 0x030e, "IPv6Subnet",				print_string },
+};
+
+attr_t gn_attrs[] = {	/* Group Network */
+	{ 0x030a, "SecurityDescription",		print_security_description },
+	{ 0x030d, "IPv4Subnet",				print_string },
+	{ 0x030e, "IPv6Subnet",				print_string },
+};
+
+attr_t hf_attrs[] = {	/* Handsfree */
+	{ 0x0311, "SupportedFeatures",			print_hf_features },
+};
+
+attr_t hfag_attrs[] = {	/* Handsfree Audio Gateway */
+	{ 0x0301, "Network",				print_hfag_network },
+	{ 0x0311, "SupportedFeatures",			print_hfag_features },
+};
+
+attr_t hid_attrs[] = {	/* Human Interface Device */
+	{ 0x0200, "HIDDeviceReleaseNumber",		print_hid_version },
+	{ 0x0201, "HIDParserVersion",			print_hid_version },
+	{ 0x0202, "HIDDeviceSubClass",			print_hid_device_subclass },
+	{ 0x0203, "HIDCountryCode",			print_uint8x },
+	{ 0x0204, "HIDVirtualCable",			print_bool },
+	{ 0x0205, "HIDReconnectInitiate",		print_bool },
+	{ 0x0206, "HIDDescriptorList",			print_hid_descriptor_list },
+	{ 0x0207, "HIDLANGIDBaseList",			NULL },
+	{ 0x0208, "HIDSDPDisable",			print_bool },
+	{ 0x0209, "HIDBatteryPower",			print_bool },
+	{ 0x020a, "HIDRemoteWake",			print_bool },
+	{ 0x020b, "HIDProfileVersion",			print_profile_version },
+	{ 0x020c, "HIDSupervisionTimeout",		print_uint16d },
+	{ 0x020d, "HIDNormallyConnectable",		print_bool },
+	{ 0x020e, "HIDBootDevice",			print_bool },
+};
+
+#define A(a)	a, __arraycount(a)
+service_t service_list[] = {
+	{ 0x1000, "Service Discovery Server",		A(sds_attrs) },
+	{ 0x1001, "Browse Group Descriptor",		A(bgd_attrs) },
+	{ 0x1002, "Public Browse Root",			NULL, 0 },
+	{ 0x1101, "Serial Port",			NULL, 0 },
+	{ 0x1102, "LAN Access Using PPP",		A(lan_attrs) },
+	{ 0x1103, "Dialup Networking",			A(dun_attrs) },
+	{ 0x1104, "IrMC Sync",				A(irmc_sync_attrs) },
+	{ 0x1105, "Object Push",			A(opush_attrs) },
+	{ 0x1106, "File Transfer",			NULL, 0 },
+	{ 0x1107, "IrMC Sync Command",			NULL, 0 },
+	{ 0x1108, "Headset",				A(hset_attrs) },
+	{ 0x1109, "Cordless Telephony",			A(ct_attrs) },
+	{ 0x110a, "Audio Source",			A(asrc_attrs) },
+	{ 0x110b, "Audio Sink",				A(asink_attrs) },
+	{ 0x110c, "A/V Remote Control Target",		A(avrcp_attrs) },
+	{ 0x110d, "Advanced Audio Distribution",	NULL, 0 },
+	{ 0x110e, "A/V Remote Control",			A(avrcp_attrs) },
+	{ 0x110f, "Video Conferencing",			NULL, 0 },
+	{ 0x1110, "Intercom",				NULL, 0 },
+	{ 0x1111, "Fax",				A(fax_attrs) },
+	{ 0x1112, "Headset Audio Gateway",		NULL, 0 },
+	{ 0x1113, "WAP",				NULL, 0 },
+	{ 0x1114, "WAP Client",				NULL, 0 },
+	{ 0x1115, "Personal Area Networking User",	A(panu_attrs) },
+	{ 0x1116, "Network Access Point",		A(nap_attrs) },
+	{ 0x1117, "Group Network",			A(gn_attrs) },
+	{ 0x1118, "Direct Printing",			NULL, 0 },
+	{ 0x1119, "Reference Printing",			NULL, 0 },
+	{ 0x111a, "Imaging",				NULL, 0 },
+	{ 0x111b, "Imaging Responder",			NULL, 0 },
+	{ 0x111c, "Imaging Automatic Archive",		NULL, 0 },
+	{ 0x111d, "Imaging Referenced Objects",		NULL, 0 },
+	{ 0x111e, "Handsfree",				A(hf_attrs) },
+	{ 0x111f, "Handsfree Audio Gateway",		A(hfag_attrs) },
+	{ 0x1120, "Direct Printing Reference Objects",	NULL, 0 },
+	{ 0x1121, "Reflected User Interface",		NULL, 0 },
+	{ 0x1122, "Basic Printing",			NULL, 0 },
+	{ 0x1123, "Printing Status",			NULL, 0 },
+	{ 0x1124, "Human Interface Device",		A(hid_attrs) },
+	{ 0x1125, "Hardcopy Cable Replacement",		NULL, 0 },
+	{ 0x1126, "Hardcopy Cable Replacement Print",	NULL, 0 },
+	{ 0x1127, "Hardcopy Cable Replacement Scan",	NULL, 0 },
+	{ 0x1128, "Common ISDN Access",			NULL, 0 },
+	{ 0x1129, "Video Conferencing GW",		NULL, 0 },
+	{ 0x112a, "UDI MT",				NULL, 0 },
+	{ 0x112b, "UDI TA",				NULL, 0 },
+	{ 0x112c, "Audio/Video",			NULL, 0 },
+	{ 0x112d, "SIM Access",				NULL, 0 },
+	{ 0x1200, "PNP Information",			NULL, 0 },
+	{ 0x1201, "Generic Networking",			NULL, 0 },
+	{ 0x1202, "Generic File Transfer",		NULL, 0 },
+	{ 0x1203, "Generic Audio",			NULL, 0 },
+	{ 0x1204, "Generic Telephony",			NULL, 0 },
+	{ 0x1205, "UPNP",				NULL, 0 },
+	{ 0x1206, "UPNP IP",				NULL, 0 },
+	{ 0x1300, "UPNP IP PAN",			NULL, 0 },
+	{ 0x1301, "UPNP IP LAP",			NULL, 0 },
+	{ 0x1302, "UPNP IP L2CAP",			NULL, 0 },
+};
+#undef A
+
+/* extracted Service Class ID List */
+#define MAX_SERVICES		16
+static size_t nservices;
+static uint16_t service_class[MAX_SERVICES];
+
+/* extracted Language Base Attribute ID List */
+#define MAX_LANGUAGES		16
+static int nlanguages;
+static language_t language[MAX_LANGUAGES];
+static int current;
+
+static bool
+sdp_get_uint8(sdp_data_t *d, uint8_t *vp)
+{
+	uintmax_t v;
+
+	if (sdp_data_type(d) != SDP_DATA_UINT8
+	    || !sdp_get_uint(d, &v))
+		return false;
+
+	*vp = (uint8_t)v;
+	return true;
+}
+
+static bool
+sdp_get_uint16(sdp_data_t *d, uint16_t *vp)
+{
+	uintmax_t v;
+
+	if (sdp_data_type(d) != SDP_DATA_UINT16
+	    || !sdp_get_uint(d, &v))
+		return false;
+
+	*vp = (uint16_t)v;
+	return true;
+}
+
+static bool
+sdp_get_uint32(sdp_data_t *d, uint32_t *vp)
+{
+	uintmax_t v;
+
+	if (sdp_data_type(d) != SDP_DATA_UINT32
+	    || !sdp_get_uint(d, &v))
+		return false;
+
+	*vp = (uint32_t)v;
+	return true;
+}
+
+void
+print_record(sdp_data_t *rec)
+{
+	sdp_data_t value;
+	uint16_t id;
+
+	nservices = 0;
+	nlanguages = 0;
+	current = -1;
+
+	while (sdp_get_attr(rec, &id, &value)) {
+		if (Xflag) {
+			printf("AttributeID 0x%04x:\n", id);
+			print_hexdump("     ", value.next, value.end - value.next);
+		} else if (Rflag) {
+			printf("AttributeID 0x%04x:\n", id);
+			sdp_data_print(&value, 4);
+		} else if (print_universal_attribute(id, &value)
+		    || print_language_attribute(id, &value)
+		    || print_service_attribute(id, &value)) {
+			if (value.next != value.end)
+			    printf("    [additional data ignored]\n");
+		} else {
+			printf("AttributeID 0x%04x:\n", id);
+			sdp_data_print(&value, 4);
+		}
+	}
+}
+
+static const char *
+string_uuid(uuid_t *uuid)
+{
+	static char buf[64];
+	const char *desc;
+	uuid_t u;
+	size_t i;
+
+	u = *uuid;
+	u.time_low = 0;
+	if (!uuid_equal(&u, &BLUETOOTH_BASE_UUID, NULL)) {
+		snprintf(buf, sizeof(buf),
+		    "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+		    uuid->time_low, uuid->time_mid, uuid->time_hi_and_version,
+		    uuid->clock_seq_hi_and_reserved, uuid->clock_seq_low,
+		    uuid->node[0], uuid->node[1], uuid->node[2],
+		    uuid->node[3], uuid->node[4], uuid->node[5]);
+
+		return buf;
+	}
+
+	desc = NULL;
+	for (i = 0; i < __arraycount(service_list); i++) {
+		if (uuid->time_low == service_list[i].class) {
+			desc = service_list[i].desc;
+			break;
+		}
+	}
+
+	for (i = 0; i < __arraycount(protocol_list); i++) {
+		if (uuid->time_low == protocol_list[i].id) {
+			desc = protocol_list[i].desc;
+			break;
+		}
+	}
+
+	if (!Nflag && desc) {
+		snprintf(buf, sizeof(buf), "%s", desc);
+		return buf;
+	}
+
+	snprintf(buf, sizeof(buf), "%s%s(0x%*.*x)",
+	    (desc == NULL ? "" : desc),
+	    (desc == NULL ? "" : " "),
+	    (uuid->time_low > UINT16_MAX ? 8 : 4),
+	    (uuid->time_low > UINT16_MAX ? 8 : 4),
+	    uuid->time_low);
+
+	return buf;
+}
+
+static const char *
+string_vis(int style, const char *src, size_t len)
+{
+	static char buf[50];
+	char *dst = buf;
+
+	style |= VIS_NL;
+	while (len > 0 && (dst + 5) < (buf + sizeof(buf))) {
+		dst = vis(dst, src[0], style, (len > 1 ? src[1] : 0));
+		src++;
+		len--;
+	}
+
+	return buf;
+}
+
+static void
+print_hexdump(const char *title, const uint8_t *data, size_t len)
+{
+	int n, i;
+
+	i = 0;
+	n = printf("%s", title);
+
+	while (len-- > 0) {
+		if (++i > 8) {
+			printf("\n%*s", n, "");
+			i = 1;
+		}
+
+		printf(" 0x%02x", *data++);
+	}
+
+	printf("\n");
+}
+
+static bool
+print_attribute(uint16_t id, sdp_data_t *value, attr_t *attr, int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		if (id == attr[i].id) {
+			printf("%s", attr[i].desc);
+
+			if (Nflag) {
+				printf(" (");
+
+				if (current != -1)
+					printf("0x%04x + ", language[current].base);
+
+				printf("0x%04x)", id);
+			}
+
+			printf(": ");
+
+			if (attr[i].print == NULL) {
+				printf("\n");
+				sdp_data_print(value, 4);
+				value->next = value->end;
+			} else {
+				(attr[i].print)(value);
+			}
+
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static bool
+print_universal_attribute(uint16_t id, sdp_data_t *value)
+{
+
+	return print_attribute(id, value,
+	    universal_attrs, __arraycount(universal_attrs));
+}
+
+static bool
+print_language_attribute(uint16_t id, sdp_data_t *value)
+{
+	bool done = false;
+
+	for (current = 0; current < nlanguages && !done; current++)
+		done = print_attribute(id - language[current].base, value,
+		    language_attrs, __arraycount(language_attrs));
+
+	current = -1;
+	return done;
+}
+
+static bool
+print_service_attribute(uint16_t id, sdp_data_t *value)
+{
+	size_t i, j;
+
+	for (i = 0; i < nservices; i++) {
+		for (j = 0; j < __arraycount(service_list); j++) {
+			if (service_class[i] == service_list[j].class)
+				return print_attribute(id, value,
+				    service_list[j].attrs,
+				    service_list[j].nattr);
+		}
+	}
+
+	return false;
+}
+
+static void
+print_bool(sdp_data_t *data)
+{
+	bool v;
+
+	if (!sdp_get_bool(data, &v))
+		return;
+
+	printf("%s\n", (v ? "true" : "false"));
+}
+
+static void
+print_uint8x(sdp_data_t *data)
+{
+	uint8_t v;
+
+	if (!sdp_get_uint8(data, &v))
+		return;
+
+	printf("0x%02x\n", v);
+}
+
+static void
+print_uint16d(sdp_data_t *data)
+{
+	uint16_t v;
+
+	if (!sdp_get_uint16(data, &v))
+		return;
+
+	printf("%d\n", v);
+}
+
+static void
+print_uint32x(sdp_data_t *data)
+{
+	uint32_t v;
+
+	if (!sdp_get_uint32(data, &v))
+		return;
+
+	printf("0x%08x\n", v);
+}
+
+static void
+print_uint32d(sdp_data_t *data)
+{
+	uint32_t v;
+
+	if (!sdp_get_uint32(data, &v))
+		return;
+
+	printf("%d\n", v);
+}
+
+static void
+print_uuid(sdp_data_t *data)
+{
+	uuid_t uuid;
+
+	if (!sdp_get_uuid(data, &uuid))
+		return;
+
+	printf("%s\n", string_uuid(&uuid));
+}
+
+static void
+print_uuid_list(sdp_data_t *data)
+{
+	sdp_data_t seq;
+	uuid_t uuid;
+
+	if (!sdp_get_seq(data, &seq))
+		return;
+
+	printf("\n");
+	while (sdp_get_uuid(&seq, &uuid))
+		printf("    %s\n", string_uuid(&uuid));
+
+	if (seq.next != seq.end)
+		printf("    [additional data]\n");
+}
+
+static void
+print_string(sdp_data_t *data)
+{
+	char *str;
+	size_t len;
+
+	if (!sdp_get_str(data, &str, &len))
+		return;
+
+	printf("\"%s\"\n", string_vis(VIS_CSTYLE, str, len));
+}
+
+static void
+print_url(sdp_data_t *data)
+{
+	char *url;
+	size_t len;
+
+	if (!sdp_get_url(data, &url, &len))
+		return;
+
+	printf("\"%s\"\n", string_vis(VIS_HTTPSTYLE, url, len));
+}
+
+static void
+print_profile_version(sdp_data_t *data)
+{
+	uint16_t v;
+
+	if (!sdp_get_uint16(data, &v))
+		return;
+
+	printf("v%d.%d\n", (v >> 8), (v & 0xff));
+}
+
+/*
+ * This should only be called through print_language_attribute() which
+ * sets codeset of the string to be printed.
+ */
+static void
+print_language_string(sdp_data_t *data)
+{
+	char buf[50], *dst, *src;
+	iconv_t ih;
+	size_t n, srcleft, dstleft;
+
+	if (!sdp_get_str(data, &src, &srcleft))
+		return;
+
+	dst = buf;
+	dstleft = sizeof(buf);
+
+	ih = iconv_open(nl_langinfo(CODESET), language[current].codeset);
+	if (ih == (iconv_t)-1) {
+		printf("Can't convert %s string\n", language[current].codeset);
+		return;
+	}
+
+	n = iconv(ih, (const char **)&src, &srcleft, &dst, &dstleft);
+
+	iconv_close(ih);
+
+	if (Nflag || n > 0)
+		printf("(%s) ", language[current].codeset);
+
+	printf("\"%.*s%s\n", (int)(sizeof(buf) - dstleft), buf,
+	    (srcleft > 0 ? " ..." : "\""));
+}
+
+static void
+print_service_class_id_list(sdp_data_t *data)
+{
+	sdp_data_t seq;
+	uuid_t uuid;
+
+	if (!sdp_get_seq(data, &seq))
+		return;
+
+	printf("\n");
+	while (sdp_get_uuid(&seq, &uuid)) {
+		printf("    %s\n", string_uuid(&uuid));
+
+		if (nservices < MAX_SERVICES) {
+			service_class[nservices] = uuid.time_low;
+			uuid.time_low = 0;
+			if (uuid_equal(&uuid, &BLUETOOTH_BASE_UUID, NULL))
+				nservices++;
+		}
+	}
+
+	if (seq.next != seq.end)
+		printf("    [additional data]\n");
+}
+
+static void
+print_protocol_descriptor(sdp_data_t *data)
+{
+	uuid_t u0, uuid;
+	size_t i;
+
+	if (!sdp_get_uuid(data, &uuid))
+		return;
+
+	u0 = uuid;
+	u0.time_low = 0;
+	if (uuid_equal(&u0, &BLUETOOTH_BASE_UUID, NULL)) {
+		for (i = 0; i < __arraycount(protocol_list); i++) {
+			if (uuid.time_low == protocol_list[i].id) {
+				printf("    %s", protocol_list[i].desc);
+
+				if (Nflag)
+					printf(" (0x%04x)", protocol_list[i].id);
+
+				if (protocol_list[i].print)
+					(protocol_list[i].print)(data);
+
+				if (data->next != data->end)
+					printf(" [additional data ignored]");
+
+				printf("\n");
+				return;
+			}
+		}
+	}
+
+	printf("    %s\n", string_uuid(&uuid));
+	sdp_data_print(data, 4);
+	data->next = data->end;
+}
+
+static void
+print_protocol_descriptor_list(sdp_data_t *data)
+{
+	sdp_data_t seq, proto;
+
+	printf("\n");
+	sdp_get_alt(data, data);	/* strip [optional] alt header */
+
+	while (sdp_get_seq(data, &seq))
+		while (sdp_get_seq(&seq, &proto))
+			print_protocol_descriptor(&proto);
+}
+
+static void
+print_language_base_attribute_id_list(sdp_data_t *data)
+{
+	sdp_data_t list;
+	uint16_t v;
+	const char *codeset;
+	char lang[2];
+
+	if (!sdp_get_seq(data, &list))
+		return;
+
+	printf("\n");
+	while (list.next < list.end) {
+		/*
+		 * ISO-639-1 natural language values are published at
+		 *	http://www.loc.gov/standards/iso639-2/php/code-list.php
+		 */
+		if (!sdp_get_uint16(&list, &v))
+			break;
+
+		be16enc(lang, v);
+		if (!islower((int)lang[0]) || !islower((int)lang[1]))
+			break;
+
+		/*
+		 * MIBenum values are published at
+		 *	http://www.iana.org/assignments/character-sets
+		 */
+		if (!sdp_get_uint16(&list, &v))
+			break;
+
+		switch(v) {
+		case 3:		codeset = "US-ASCII";		break;
+		case 4:		codeset = "ISO-8859-1";		break;
+		case 5:		codeset = "ISO-8859-2";		break;
+		case 106:	codeset = "UTF-8";		break;
+		case 1013:	codeset = "UTF-16BE";		break;
+		case 1014:	codeset = "UTF-16LE";		break;
+		default:	codeset = "Unknown";		break;
+		}
+
+		if (!sdp_get_uint16(&list, &v))
+			break;
+
+		printf("    %.2s.%s base 0x%04x\n", lang, codeset, v);
+
+		if (nlanguages < MAX_LANGUAGES) {
+			language[nlanguages].base = v;
+			language[nlanguages].codeset = codeset;
+			nlanguages++;
+		}
+	}
+
+	if (list.next != list.end)
+		printf("    [additional data]\n");
+}
+
+static void
+print_service_availability(sdp_data_t *data)
+{
+	uint8_t v;
+
+	if (!sdp_get_uint8(data, &v))
+		return;
+
+	printf("%d/%d\n", v, UINT8_MAX);
+}
+
+static void
+print_bluetooth_profile_descriptor_list(sdp_data_t *data)
+{
+	sdp_data_t seq, profile;
+	uuid_t uuid;
+	uint16_t v;
+
+	if (!sdp_get_seq(data, &seq))
+		return;
+
+	printf("\n");
+	while (seq.next < seq.end) {
+		if (!sdp_get_seq(&seq, &profile)
+		    || !sdp_get_uuid(&profile, &uuid)
+		    || !sdp_get_uint16(&profile, &v))
+			break;
+
+		printf("    %s, v%d.%d", string_uuid(&uuid),
+		    (v >> 8), (v & 0xff));
+
+		if (profile.next != profile.end)
+			printf(" [additional profile data]");
+
+		printf("\n");
+	}
+
+	if (seq.next != seq.end)
+		printf("    [additional data]\n");
+}
+
+static void
+print_additional_protocol_descriptor_lists(sdp_data_t *data)
+{
+	sdp_data_t seq, stack, proto;
+
+	printf("\n");
+	sdp_get_seq(data, &seq);
+
+	while (sdp_get_seq(&seq, &stack))
+		while (sdp_get_seq(&stack, &proto))
+			print_protocol_descriptor(&proto);
+
+	if (seq.next != seq.end)
+		printf("    [additional data]\n");
+}
+
+static void
+print_sds_version_number_list(sdp_data_t *data)
+{
+	sdp_data_t list;
+	const char *sep;
+	uint16_t v;
+
+	if (!sdp_get_seq(data, &list))
+		return;
+
+	sep = "";
+	while (sdp_get_uint16(&list, &v)) {
+		printf("%sv%d.%d", sep, (v >> 8), (v & 0xff));
+		sep = ", ";
+	}
+
+	if (list.next != list.end)
+		printf(" [additional data]");
+
+	printf("\n");
+}
+
+static void
+print_ct_network(sdp_data_t *data)
+{
+	uint8_t v;
+
+	if (!sdp_get_uint8(data, &v))
+		return;
+
+	switch (v) {
+	case 0x01:	printf("PSTN");			break;
+	case 0x02:	printf("ISDN");			break;
+	case 0x03:	printf("GSM");			break;
+	case 0x04:	printf("CDMA");			break;
+	case 0x05:	printf("Analogue Cellular");	break;
+	case 0x06:	printf("Packet Switched");	break;
+	case 0x07:	printf("Other");		break;
+	default:	printf("0x%02x", v);		break;
+	}
+
+	printf("\n");
+}
+
+static void
+print_asrc_features(sdp_data_t *data)
+{
+	uint16_t v;
+
+	if (!sdp_get_uint16(data, &v))
+		return;
+
+	if (Nflag)
+		printf("(0x%04x)", v);
+
+	printf("\n");
+	if (v & (1<<0))	printf("    Player\n");
+	if (v & (1<<1))	printf("    Microphone\n");
+	if (v & (1<<2))	printf("    Tuner\n");
+	if (v & (1<<3))	printf("    Mixer\n");
+}
+
+static void
+print_asink_features(sdp_data_t *data)
+{
+	uint16_t v;
+
+	if (!sdp_get_uint16(data, &v))
+		return;
+
+	if (Nflag)
+		printf("(0x%04x)", v);
+
+	printf("\n");
+	if (v & (1<<0))	printf("    Headphone\n");
+	if (v & (1<<1))	printf("    Speaker\n");
+	if (v & (1<<2))	printf("    Recorder\n");
+	if (v & (1<<3))	printf("    Amplifier\n");
+}
+
+static void
+print_avrcp_features(sdp_data_t *data)
+{
+	uint16_t v;
+
+	if (!sdp_get_uint16(data, &v))
+		return;
+
+	if (Nflag)
+		printf("(0x%04x)", v);
+
+	printf("\n");
+	if (v & (1<<0))	printf("    Category 1\n");
+	if (v & (1<<1))	printf("    Category 2\n");
+	if (v & (1<<2))	printf("    Category 3\n");
+	if (v & (1<<3))	printf("    Category 4\n");
+}
+
+static void
+print_supported_data_stores(sdp_data_t *data)
+{
+	sdp_data_t list;
+	const char *sep;
+	uint8_t v;
+
+	if (!sdp_get_seq(data, &list))
+		return;
+
+	sep = "\n    ";
+	while (sdp_get_uint8(&list, &v)) {
+		printf(sep);
+		sep = ", ";
+
+		switch(v) {
+		case 0x01:	printf("Phonebook");	break;
+		case 0x03:	printf("Calendar");	break;
+		case 0x05:	printf("Notes");	break;
+		case 0x06:	printf("Messages");	break;
+		default:	printf("0x%02x", v);	break;
+		}
+	}
+
+	if (list.next != list.end)
+		printf("   [additional data]");
+
+	printf("\n");
+}
+
+static void
+print_supported_formats(sdp_data_t *data)
+{
+	sdp_data_t list;
+	const char *sep;
+	uint8_t v;
+
+	if (!sdp_get_seq(data, &list))
+		return;
+
+	sep = "\n    ";
+	while (sdp_get_uint8(&list, &v)) {
+		printf(sep);
+		sep = ", ";
+
+		switch(v) {
+		case 0x01:	printf("vCard 2.1");	break;
+		case 0x02:	printf("vCard 3.0");	break;
+		case 0x03:	printf("vCal 1.0");	break;
+		case 0x04:	printf("iCal 2.0");	break;
+		case 0x05:	printf("vNote");	break;
+		case 0x06:	printf("vMessage");	break;
+		case 0xff:	printf("Any");		break;
+		default:	printf("0x%02x", v);	break;
+		}
+	}
+
+	if (list.next != list.end)
+		printf("   [additional data]");
+
+	printf("\n");
+}
+
+static void
+print_hid_version(sdp_data_t *data)
+{
+	uint16_t v;
+
+	if (!sdp_get_uint16(data, &v))
+		return;
+
+	printf("v%d.%d.%d\n",
+	    ((v & 0xff00) >> 8), ((v & 0x00f0) >> 4), (v & 0x000f));
+}
+
+static void
+print_hid_device_subclass(sdp_data_t *data)
+{
+	uint8_t v;
+
+	if (!sdp_get_uint8(data, &v))
+		return;
+
+	switch ((v & 0x3c) >> 2) {
+	case 1:		printf("Joystick");		break;
+	case 2:		printf("Gamepad");		break;
+	case 3:		printf("Remote Control");	break;
+	case 4:		printf("Sensing Device");	break;
+	case 5:		printf("Digitiser Tablet");	break;
+	case 6:		printf("Card Reader");		break;
+	default:	printf("Peripheral");		break;
+	}
+
+	if (v & 0x40)	printf(" <Keyboard>");
+	if (v & 0x80)	printf(" <Mouse>");
+
+	printf("\n");
+}
+
+static void
+print_hid_descriptor_list(sdp_data_t *data)
+{
+	sdp_data_t list, seq;
+	uint8_t type;
+	const char *name;
+	char *str;
+	size_t len;
+
+
+	if (!sdp_get_seq(data, &list))
+		return;
+
+	printf("\n");
+	while (list.next < list.end) {
+		if (!sdp_get_seq(&list, &seq)
+		    || !sdp_get_uint8(&seq, &type)
+		    || !sdp_get_str(&seq, &str, &len))
+			return;
+
+		switch (type) {
+		case 0x22:	name = "Report";		break;
+		case 0x23:	name = "Physical Descriptor";	break;
+		default:	name = "";			break;
+		}
+
+		printf("    Type 0x%02x: %s\n", type, name);
+		print_hexdump("    Data", (uint8_t *)str, len);
+
+		if (seq.next != seq.end)
+			printf("    [additional data]\n");
+	}
+}
+
+static void
+print_security_description(sdp_data_t *data)
+{
+	uint16_t v;
+
+	if (!sdp_get_uint16(data, &v))
+		return;
+
+	switch (v) {
+	case 0x0000:	printf("None");				break;
+	case 0x0001:	printf("Service-level Security");	break;
+	case 0x0002:	printf("802.1x Security");		break;
+	default:	printf("0x%04x", v);			break;
+	}
+
+	printf("\n");
+}
+
+static void
+print_hf_features(sdp_data_t *data)
+{
+	uint16_t v;
+
+	if (!sdp_get_uint16(data, &v))
+		return;
+
+	if (Nflag)
+		printf("(0x%04x)", v);
+
+	printf("\n");
+	if (v & (1<<0))	printf("    Echo Cancellation/Noise Reduction\n");
+	if (v & (1<<1))	printf("    Call Waiting\n");
+	if (v & (1<<2))	printf("    Caller Line Identification\n");
+	if (v & (1<<3))	printf("    Voice Recognition\n");
+	if (v & (1<<4))	printf("    Volume Control\n");
+}
+
+static void
+print_hfag_network(sdp_data_t *data)
+{
+	uint8_t v;
+
+	if (!sdp_get_uint8(data, &v))
+		return;
+
+	switch (v) {
+	case 0x01:	printf("Ability to reject a call");	break;
+	case 0x02:	printf("No ability to reject a call");	break;
+	default:	printf("0x%02x", v);			break;
+	}
+
+	printf("\n");
+}
+
+static void
+print_hfag_features(sdp_data_t *data)
+{
+	uint16_t v;
+
+	if (!sdp_get_uint16(data, &v))
+		return;
+
+	if (Nflag)
+		printf("(0x%04x)", v);
+
+	printf("\n");
+	if (v & (1<<0))	printf("    3 Way Calling\n");
+	if (v & (1<<1))	printf("    Echo Cancellation/Noise Reduction\n");
+	if (v & (1<<2))	printf("    Voice Recognition\n");
+	if (v & (1<<3))	printf("    In-band Ring Tone\n");
+	if (v & (1<<4))	printf("    Voice Tags\n");
+}
+
+static void
+print_net_access_type(sdp_data_t *data)
+{
+	uint16_t v;
+
+	if (!sdp_get_uint16(data, &v))
+		return;
+
+	switch(v) {
+	case 0x0000:	printf("PSTN");			break;
+	case 0x0001:	printf("ISDN");			break;
+	case 0x0002:	printf("DSL");			break;
+	case 0x0003:	printf("Cable Modem");		break;
+	case 0x0004:	printf("10Mb Ethernet");	break;
+	case 0x0005:	printf("100Mb Ethernet");	break;
+	case 0x0006:	printf("4Mb Token Ring");	break;
+	case 0x0007:	printf("16Mb Token Ring");	break;
+	case 0x0008:	printf("100Mb Token Ring");	break;
+	case 0x0009:	printf("FDDI");			break;
+	case 0x000a:	printf("GSM");			break;
+	case 0x000b:	printf("CDMA");			break;
+	case 0x000c:	printf("GPRS");			break;
+	case 0x000d:	printf("3G Cellular");		break;
+	case 0xfffe:	printf("other");		break;
+	default:	printf("0x%04x", v);		break;
+	}
+
+	printf("\n");
+}
+
+static void
+print_rfcomm(sdp_data_t *data)
+{
+	uint8_t v;
+
+	if (sdp_get_uint8(data, &v))
+		printf(" (channel %d)", v);
+}
+
+static void
+print_bnep(sdp_data_t *data)
+{
+	sdp_data_t seq;
+	uint16_t v;
+	const char *sep;
+
+	if (!sdp_get_uint16(data, &v)
+	    || !sdp_get_seq(data, &seq))
+		return;
+
+	printf(" (v%d.%d", (v >> 8), (v & 0xff));
+	sep = "; ";
+	while (sdp_get_uint16(&seq, &v)) {
+		printf(sep);
+		sep = ", ";
+
+		switch (v) {
+		case 0x0800:	printf("IPv4");		break;
+		case 0x0806:	printf("ARP");		break;
+		case 0x86dd:	printf("IPv6");		break;
+		default:	printf("0x%04x", v);	break;
+		}
+	}
+	printf(")");
+
+	if (seq.next != seq.end)
+		printf(" [additional data]");
+}
+
+static void
+print_avctp(sdp_data_t *data)
+{
+	uint16_t v;
+
+	if (sdp_get_uint16(data, &v))
+		printf(" (v%d.%d)", (v >> 8), (v & 0xff));
+}
+
+static void
+print_avdtp(sdp_data_t *data)
+{
+	uint16_t v;
+
+	if (sdp_get_uint16(data, &v))
+		printf(" (v%d.%d)", (v >> 8), (v & 0xff));
+}
+
+static void
+print_l2cap(sdp_data_t *data)
+{
+	uint16_t v;
+
+	if (sdp_get_uint16(data, &v))
+		printf(" (PSM 0x%04x)", v);
+}

Reply via email to