summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2005-05-06 16:10:26 +0000
committerMarcel Holtmann <marcel@holtmann.org>2005-05-06 16:10:26 +0000
commite7b5f3c69c6a7303b951e7a1398c87a568a16761 (patch)
treec9450ea29d7ab5ccabbcbc2fa127b727662d83c3 /tools
parent18cb95673231d41455528a782b168ee05f32fe2b (diff)
Include a raw mode for showing the original record tree
Diffstat (limited to 'tools')
-rw-r--r--tools/sdptool.121
-rw-r--r--tools/sdptool.c326
2 files changed, 289 insertions, 58 deletions
diff --git a/tools/sdptool.1 b/tools/sdptool.1
index c4f248eb..5ed9e33e 100644
--- a/tools/sdptool.1
+++ b/tools/sdptool.1
@@ -65,15 +65,16 @@ local \fBsdpd\fR.
.SH "COMMANDS"
.PP
The following commands are available.
-.IP "\fBsearch [--bdaddr bdaddr] [--tree] service\fP" 10
+.IP "\fBsearch [--bdaddr bdaddr] [--tree] [--raw] service\fP" 10
Search for services..
.IP "" 10
-Known service names are SP< DUN, LAN, FAX, OPUSH,
-FTRN, NAP, GN, HID, CIP.
-.IP "\fBbrowse [--tree] [bdaddr]\fP" 10
+Known service names are DID, SP, DUN, LAN, FAX, OPUSH,
+FTP, HS, HF, SAP, NAP, GN, PANU, HID, CIP, CTP, A2SRC, A2SNK
+and SYNCML.
+.IP "\fBbrowse [--tree] [--raw] [bdaddr]\fP" 10
Browse all available services on the device
specified by a Bluetooth address as a parameter.
-.IP "\fBrecords [--tree] bdaddr\fP" 10
+.IP "\fBrecords [--tree] [--raw] bdaddr\fP" 10
Retrieve all possible service records.
.IP "\fBadd [ --channel=N ]\fP" 10
Add a service to the local
@@ -84,7 +85,7 @@ using the \fB--channel\fP option.
.IP "\fBdel record_handle\fP" 10
Remove a service from the local
\fBsdpd\fR.
-.IP "\fBget [--tree] [--bdaddr bdaddr] record_handle\fP" 10
+.IP "\fBget [--tree] [--raw] [--bdaddr bdaddr] record_handle\fP" 10
Retrieve a service from the local
\fBsdpd\fR.
.IP "\fBsetattr record_handle attrib_id attrib_value\fP" 10
@@ -99,13 +100,13 @@ Displays help on using sdptool.
.SH "EXAMPLES"
.PP
-sdptool browse 00:80:98:24:15:6D
+sdptool browse 00:80:98:24:15:6D
.PP
-sdptool browse local
+sdptool browse local
.PP
-sdptool add DUN
+sdptool add DUN
.PP
-sdptool del LAN
+sdptool del 0x10000
.SH "BUGS"
.PP
Documentation needs improving.
diff --git a/tools/sdptool.c b/tools/sdptool.c
index 64e3a464..d015f12f 100644
--- a/tools/sdptool.c
+++ b/tools/sdptool.c
@@ -36,9 +36,11 @@
#include <stdio.h>
#include <errno.h>
+#include <ctype.h>
#include <stdlib.h>
+#include <string.h>
#include <getopt.h>
-#include <netinet/in.h>
+#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
@@ -46,6 +48,8 @@
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
+#include <netinet/in.h>
+
#define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, 0)) != -1)
/*
@@ -61,11 +65,15 @@ static int estr2ba(char *str, bdaddr_t *ba)
return str2ba(str, ba);
}
+#define DEFAULT_VIEW 0 /* Display only known attribute */
+#define TREE_VIEW 1 /* Display full attribute tree */
+#define RAW_VIEW 2 /* Display raw tree */
+
/* Pass args to the inquiry/search handler */
struct search_context {
char *svc; /* Service */
uuid_t group; /* Browse group */
- int tree; /* Display full attribute tree */
+ int view; /* View mode */
uint32_t handle; /* Service record handle */
};
@@ -219,7 +227,7 @@ static struct attrib_def audio_attrib_names[] = {
/* Same for the UUIDs. See BT assigned numbers */
static struct uuid_def uuid16_names[] = {
/* -- Protocols -- */
- { 0x0001, "SDP (Service Discovery Protocol)", NULL, 0 },
+ { 0x0001, "SDP", NULL, 0 },
{ 0x0002, "UDP", NULL, 0 },
{ 0x0003, "RFCOMM", NULL, 0 },
{ 0x0004, "TCP", NULL, 0 },
@@ -230,7 +238,7 @@ static struct uuid_def uuid16_names[] = {
{ 0x000a, "FTP", NULL, 0 },
{ 0x000c, "HTTP", NULL, 0 },
{ 0x000e, "WSP", NULL, 0 },
- { 0x000f, "BNEP (PAN/BNEP)", NULL, 0 },
+ { 0x000f, "BNEP", NULL, 0 },
{ 0x0010, "UPnP/ESDP", NULL, 0 },
{ 0x0011, "HIDP", NULL, 0 },
{ 0x0012, "HardcopyControlChannel", NULL, 0 },
@@ -242,11 +250,11 @@ static struct uuid_def uuid16_names[] = {
{ 0x001d, "UDI_C-Plane", NULL, 0 },
{ 0x0100, "L2CAP", NULL, 0 },
/* -- Services -- */
- { 0x1000, "ServiceDiscoveryServerServiceClassID (SDP)",
+ { 0x1000, "ServiceDiscoveryServerServiceClassID",
sdp_attrib_names, sizeof(sdp_attrib_names)/sizeof(struct attrib_def) },
- { 0x1001, "BrowseGroupDescriptorServiceClassID (SDP)",
+ { 0x1001, "BrowseGroupDescriptorServiceClassID",
browse_attrib_names, sizeof(browse_attrib_names)/sizeof(struct attrib_def) },
- { 0x1002, "PublicBrowseGroup (SDP)", NULL, 0 },
+ { 0x1002, "PublicBrowseGroup", NULL, 0 },
{ 0x1101, "SerialPort", NULL, 0 },
{ 0x1102, "LANAccessUsingPPP", NULL, 0 },
{ 0x1103, "DialupNetworking (DUN)", NULL, 0 },
@@ -276,7 +284,10 @@ static struct uuid_def uuid16_names[] = {
pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) },
{ 0x1118, "DirectPrinting (BPP)", NULL, 0 },
{ 0x1119, "ReferencePrinting (BPP)", NULL, 0 },
- /* ... */
+ { 0x111a, "Imaging (BIP)", NULL, 0 },
+ { 0x111b, "ImagingResponder (BIP)", NULL, 0 },
+ { 0x111c, "ImagingAutomaticArchive (BIP)", NULL, 0 },
+ { 0x111d, "ImagingReferencedObjects (BIP)", NULL, 0 },
{ 0x111e, "Handsfree", NULL, 0 },
{ 0x111f, "HandsfreeAudioGateway", NULL, 0 },
{ 0x1120, "DirectPrintingReferenceObjectsService (BPP)", NULL, 0 },
@@ -411,9 +422,9 @@ static void sdp_data_printf(sdp_data_t *sdpdata, struct attrib_context *context,
char *member_name = NULL;
/* Find member name. Almost black magic ;-) */
- if (context->attrib && context->attrib->members &&
- context->member_index < context->attrib->member_max) {
- member_name = context->attrib->members[context->member_index].name;
+ if (context && context->attrib && context->attrib->members &&
+ context->member_index < context->attrib->member_max) {
+ member_name = context->attrib->members[context->member_index].name;
}
switch (sdpdata->dtd) {
@@ -484,7 +495,7 @@ static void sdp_data_printf(sdp_data_t *sdpdata, struct attrib_context *context,
/*
* Parse a single attribute.
*/
-static void sdp_attr_printf_func(void *value, void *userData)
+static void print_tree_attr_func(void *value, void *userData)
{
sdp_data_t *sdpdata = NULL;
uint16_t attrId;
@@ -535,11 +546,191 @@ static void sdp_attr_printf_func(void *value, void *userData)
* We assume the record has already been read, parsed and cached
* locally. Jean II
*/
-static void sdp_printf_service_attr(sdp_record_t *rec)
+static void print_tree_attr(sdp_record_t *rec)
{
if (rec && rec->attrlist) {
struct service_context service = { NULL };
- sdp_list_foreach(rec->attrlist, sdp_attr_printf_func, &service);
+ sdp_list_foreach(rec->attrlist, print_tree_attr_func, &service);
+ }
+}
+
+static void print_raw_data(sdp_data_t *data, int indent)
+{
+ struct uuid_def *def;
+ char *str;
+ int i, hex;
+
+ if (!data)
+ return;
+
+ for (i = 0; i < indent; i++)
+ printf("\t");
+
+ switch (data->dtd) {
+ case SDP_DATA_NIL:
+ printf("NIL\n");
+ break;
+ case SDP_BOOL:
+ printf("Bool %s\n", data->val.uint8 ? "True" : "False");
+ break;
+ case SDP_UINT8:
+ printf("UINT8 0x%02x\n", data->val.uint8);
+ break;
+ case SDP_UINT16:
+ printf("UINT16 0x%04x\n", data->val.uint16);
+ break;
+ case SDP_UINT32:
+ printf("UINT32 0x%08x\n", data->val.uint32);
+ break;
+ case SDP_UINT64:
+ printf("UINT64 0x%016llx\n", data->val.uint64);
+ break;
+ case SDP_UINT128:
+ printf("UINT128 ...\n");
+ break;
+ case SDP_INT8:
+ printf("INT8 %d\n", data->val.int8);
+ break;
+ case SDP_INT16:
+ printf("INT16 %d\n", data->val.int16);
+ break;
+ case SDP_INT32:
+ printf("INT32 %d\n", data->val.int32);
+ break;
+ case SDP_INT64:
+ printf("INT64 %lld\n", data->val.int64);
+ break;
+ case SDP_INT128:
+ printf("INT128 ...\n");
+ break;
+ case SDP_UUID16:
+ case SDP_UUID32:
+ case SDP_UUID128:
+ switch (data->val.uuid.type) {
+ case SDP_UUID16:
+ def = NULL;
+ for (i = 0; i < uuid16_max; i++)
+ if (uuid16_names[i].num == data->val.uuid.value.uuid16) {
+ def = &uuid16_names[i];
+ break;
+ }
+ if (def)
+ printf("UUID16 0x%04x - %s\n", data->val.uuid.value.uuid16, def->name);
+ else
+ printf("UUID16 0x%04x\n", data->val.uuid.value.uuid16);
+ break;
+ case SDP_UUID32:
+ def = NULL;
+ if (!(data->val.uuid.value.uuid32 & 0xffff0000)) {
+ uint16_t value = data->val.uuid.value.uuid32;
+ for (i = 0; i < uuid16_max; i++)
+ if (uuid16_names[i].num == value) {
+ def = &uuid16_names[i];
+ break;
+ }
+ }
+ if (def)
+ printf("UUID32 0x%08x - %s\n", data->val.uuid.value.uuid32, def->name);
+ else
+ printf("UUID32 0x%08x\n", data->val.uuid.value.uuid32);
+ break;
+ case SDP_UUID128:
+ printf("UUID128 ");
+ for (i = 0; i < 16; i++) {
+ switch (i) {
+ case 4:
+ case 6:
+ case 8:
+ case 10:
+ printf("-");
+ break;
+ }
+ printf("%02x", (unsigned char ) data->val.uuid.value.uuid128.data[i]);
+ }
+ printf("\n");
+ break;
+ default:
+ printf("UUID type 0x%02x\n", data->val.uuid.type);
+ break;
+ }
+ break;
+ case SDP_TEXT_STR8:
+ case SDP_TEXT_STR16:
+ case SDP_TEXT_STR32:
+ str = data->val.str;
+ if (data->unitSize > strlen(str) + 1) {
+ hex = 0;
+ for (i = 0; i < data->unitSize - 1; i++)
+ if (!isprint(str[i])) {
+ hex = 1;
+ break;
+ }
+ if (str[data->unitSize - 1] != '\0')
+ hex = 1;
+ } else
+ hex = 0;
+ if (hex) {
+ printf("String");
+ for (i = 0; i < data->unitSize; i++)
+ printf(" %02x", (unsigned char) str[i]);
+ printf("\n");
+ } else
+ printf("String %s\n", str);
+ break;
+ case SDP_URL_STR8:
+ case SDP_URL_STR16:
+ case SDP_URL_STR32:
+ printf("URL %s\n", data->val.str);
+ break;
+ case SDP_SEQ8:
+ case SDP_SEQ16:
+ case SDP_SEQ32:
+ printf("Sequence\n");
+ print_raw_data(data->val.dataseq, indent + 1);
+ break;
+ case SDP_ALT8:
+ case SDP_ALT16:
+ case SDP_ALT32:
+ printf("Alternate\n");
+ print_raw_data(data->val.dataseq, indent + 1);
+ break;
+ default:
+ printf("Unknown type 0x%02x\n", data->dtd);
+ break;
+ }
+
+ print_raw_data(data->next, indent);
+}
+
+static void print_raw_attr_func(void *value, void *userData)
+{
+ sdp_data_t *data = (sdp_data_t *) value;
+ struct attrib_def *def = NULL;
+ int i;
+
+ /* Search amongst the generic attributes */
+ for (i = 0; i < attrib_max; i++)
+ if (attrib_names[i].num == data->attrId) {
+ def = &attrib_names[i];
+ break;
+ }
+
+ if (def)
+ printf("\tAttribute 0x%04x - %s\n", data->attrId, def->name);
+ else
+ printf("\tAttribute 0x%04x\n", data->attrId);
+
+ if (data)
+ print_raw_data(data, 2);
+ else
+ printf(" NULL value\n");
+}
+
+static void print_raw_attr(sdp_record_t *rec)
+{
+ if (rec && rec->attrlist) {
+ printf("Sequence\n");
+ sdp_list_foreach(rec->attrlist, print_raw_attr_func, 0);
}
}
@@ -2219,10 +2410,12 @@ static int do_search(bdaddr_t *bdaddr, struct search_context *context)
return -1;
}
- if (context->svc)
- printf("Searching for %s on %s ...\n", context->svc, str);
- else
- printf("Browsing %s ...\n", str);
+ if (context->view != RAW_VIEW) {
+ if (context->svc)
+ printf("Searching for %s on %s ...\n", context->svc, str);
+ else
+ printf("Browsing %s ...\n", str);
+ }
attrid = sdp_list_append(0, &range);
search = sdp_list_append(0, &context->group);
@@ -2238,15 +2431,23 @@ static int do_search(bdaddr_t *bdaddr, struct search_context *context)
sdp_record_t *rec = (sdp_record_t *) seq->data;
struct search_context sub_context;
- if (context->tree) {
- /* Display full tree */
- sdp_printf_service_attr(rec);
- } else {
+ switch (context->view) {
+ case DEFAULT_VIEW:
/* Display user friendly form */
print_service_attr(rec);
+ printf("\n");
+ break;
+ case TREE_VIEW:
+ /* Display full tree */
+ print_tree_attr(rec);
+ printf("\n");
+ break;
+ default:
+ /* Display raw tree */
+ print_raw_attr(rec);
+ break;
}
- printf("\n");
-
+
if (sdp_get_group_id(rec, &sub_context.group) != -1) {
/* Set the subcontext for browsing the sub tree */
memcpy(&sub_context, context, sizeof(struct search_context));
@@ -2266,6 +2467,7 @@ static int do_search(bdaddr_t *bdaddr, struct search_context *context)
static struct option browse_options[] = {
{ "help", 0, 0, 'h' },
{ "tree", 0, 0, 't' },
+ { "raw", 0, 0, 'r' },
{ "uuid", 1, 0, 'u' },
{ "l2cap", 0, 0, 'l' },
{ 0, 0, 0, 0 }
@@ -2273,7 +2475,7 @@ static struct option browse_options[] = {
static char *browse_help =
"Usage:\n"
- "\tbrowse [--tree] [--uuid uuid] [--l2cap] [bdaddr]\n";
+ "\tbrowse [--tree] [--raw] [--uuid uuid] [--l2cap] [bdaddr]\n";
/*
* Browse the full SDP database (i.e. list all services starting from the
@@ -2292,7 +2494,10 @@ static int cmd_browse(int argc, char **argv)
for_each_opt(opt, browse_options, 0) {
switch (opt) {
case 't':
- context.tree = 1;
+ context.view = TREE_VIEW;
+ break;
+ case 'r':
+ context.view = RAW_VIEW;
break;
case 'u':
if (sscanf(optarg, "%i", &num) != 1 || num < 0 || num > 0xffff) {
@@ -2322,15 +2527,16 @@ static int cmd_browse(int argc, char **argv)
}
static struct option search_options[] = {
- { "help", 0,0, 'h' },
- { "bdaddr", 1,0, 'b' },
- { "tree", 0,0, 't' },
+ { "help", 0,0, 'h' },
+ { "bdaddr", 1,0, 'b' },
+ { "tree", 0,0, 't' },
+ { "raw", 0, 0, 'r' },
{ 0, 0, 0, 0}
};
static char *search_help =
"Usage:\n"
- "\tsearch [--bdaddr bdaddr] [--tree] SERVICE\n"
+ "\tsearch [--bdaddr bdaddr] [--tree] [--raw] SERVICE\n"
"SERVICE is a name (string) or UUID (0x1002)\n";
/*
@@ -2361,7 +2567,10 @@ static int cmd_search(int argc, char **argv)
has_addr = 1;
break;
case 't':
- context.tree = 1;
+ context.view = TREE_VIEW;
+ break;
+ case 'r':
+ context.view = RAW_VIEW;
break;
default:
printf(search_help);
@@ -2435,19 +2644,29 @@ static int get_service(bdaddr_t *bdaddr, struct search_context *context, int qui
sdp_list_free(attrid, 0);
sdp_close(session);
if (!rec) {
- if (!quite)
+ if (!quite) {
printf("Service get request failed.\n");
- return -1;
+ return -1;
+ } else
+ return 0;
}
- if (context->tree) {
- /* Display full tree */
- sdp_printf_service_attr(rec);
- } else {
+ switch (context->view) {
+ case DEFAULT_VIEW:
/* Display user friendly form */
print_service_attr(rec);
+ printf("\n");
+ break;
+ case TREE_VIEW:
+ /* Display full tree */
+ print_tree_attr(rec);
+ printf("\n");
+ break;
+ default:
+ /* Display raw tree */
+ print_raw_attr(rec);
+ break;
}
- printf("\n");
sdp_record_free(rec);
return 0;
@@ -2456,12 +2675,13 @@ static int get_service(bdaddr_t *bdaddr, struct search_context *context, int qui
static struct option records_options[] = {
{ "help", 0, 0, 'h' },
{ "tree", 0, 0, 't' },
+ { "raw", 0, 0, 'r' },
{ 0, 0, 0, 0 }
};
static char *records_help =
"Usage:\n"
- "\trecords [--tree] bdaddr\n";
+ "\trecords [--tree] [--raw] bdaddr\n";
/*
* Request possible SDP service records
@@ -2469,9 +2689,9 @@ static char *records_help =
static int cmd_records(int argc, char **argv)
{
struct search_context context;
- uint32_t base[] = { 0x10000, 0x1002e };
+ uint32_t base[] = { 0x10000, 0x1002e, 0x110b };
bdaddr_t bdaddr;
- int i, n, opt, num = 32;
+ int i, n, opt, err = 0, num = 32;
/* Initialise context */
memset(&context, '\0', sizeof(struct search_context));
@@ -2479,7 +2699,10 @@ static int cmd_records(int argc, char **argv)
for_each_opt(opt, records_options, 0) {
switch (opt) {
case 't':
- context.tree = 1;
+ context.view = TREE_VIEW;
+ break;
+ case 'r':
+ context.view = RAW_VIEW;
break;
default:
printf(records_help);
@@ -2500,22 +2723,26 @@ static int cmd_records(int argc, char **argv)
for (i = 0; i < sizeof(base) / sizeof(uint32_t); i++)
for (n = 0; n < num; n++) {
context.handle = base[i] + n;
- get_service(&bdaddr, &context, 1);
+ err = get_service(&bdaddr, &context, 1);
+ if (err < 0)
+ goto done;
}
+done:
return 0;
}
static struct option get_options[] = {
- { "help", 0,0, 'h' },
- { "bdaddr", 1,0, 'b' },
- { "tree", 0,0, 't' },
+ { "help", 0, 0, 'h' },
+ { "bdaddr", 1, 0, 'b' },
+ { "tree", 0, 0, 't' },
+ { "raw", 0, 0, 'r' },
{ 0, 0, 0, 0 }
};
static char *get_help =
"Usage:\n"
- "\tget [--tree] [--bdaddr bdaddr] record_handle\n";
+ "\tget [--tree] [--raw] [--bdaddr bdaddr] record_handle\n";
/*
* Get a specific SDP record on the local SDP server
@@ -2537,7 +2764,10 @@ static int cmd_get(int argc, char **argv)
has_addr = 1;
break;
case 't':
- context.tree = 1;
+ context.view = TREE_VIEW;
+ break;
+ case 'r':
+ context.view = RAW_VIEW;
break;
default:
printf(get_help);