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 <[email protected]>
.\" 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 [email protected]
.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);
+}