#include <sys/param.h>
#include <netinet/in.h>
#define L2CAP_SOCKET_CHECKED
#include <bluetooth.h>
#include <ctype.h>
#include <sdp.h>
#include <stdio.h>
#include <stdlib.h>
#include "sdpcontrol.h"
static uint32_t attrs[] =
{
SDP_ATTR_RANGE( SDP_ATTR_SERVICE_RECORD_HANDLE,
SDP_ATTR_SERVICE_RECORD_HANDLE),
SDP_ATTR_RANGE( SDP_ATTR_SERVICE_CLASS_ID_LIST,
SDP_ATTR_SERVICE_CLASS_ID_LIST),
SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
SDP_ATTR_RANGE( SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST,
SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST)
};
#define attrs_len nitems(attrs)
#define NRECS 25
#define BSIZE 256
static uint8_t buffer[NRECS * attrs_len][BSIZE];
static sdp_attr_t values[NRECS * attrs_len];
#define values_len nitems(values)
static void
print_service_class_id_list(uint8_t const *start, uint8_t const *end)
{
uint32_t type, len, value;
if (end - start < 2) {
fprintf(stderr, "Invalid Service Class ID List. " \
"Too short, len=%zd\n", end - start);
return;
}
SDP_GET8(type, start);
switch (type) {
case SDP_DATA_SEQ8:
SDP_GET8(len, start);
break;
case SDP_DATA_SEQ16:
SDP_GET16(len, start);
break;
case SDP_DATA_SEQ32:
SDP_GET32(len, start);
break;
default:
fprintf(stderr, "Invalid Service Class ID List. " \
"Not a sequence, type=%#x\n", type);
return;
}
if (len > (end - start)) {
fprintf(stderr, "Invalid Service Class ID List. " \
"Too long len=%d\n", len);
return;
}
while (start < end) {
SDP_GET8(type, start);
switch (type) {
case SDP_DATA_UUID16:
SDP_GET16(value, start);
fprintf(stdout, "\t%s (%#4.4x)\n",
sdp_uuid2desc(value), value);
break;
case SDP_DATA_UUID32:
SDP_GET32(value, start);
fprintf(stdout, "\t%#8.8x\n", value);
break;
case SDP_DATA_UUID128: {
int128_t uuid;
SDP_GET_UUID128(&uuid, start);
fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
ntohl(*(uint32_t *)&uuid.b[0]),
ntohs(*(uint16_t *)&uuid.b[4]),
ntohs(*(uint16_t *)&uuid.b[6]),
ntohs(*(uint16_t *)&uuid.b[8]),
ntohs(*(uint16_t *)&uuid.b[10]),
ntohl(*(uint32_t *)&uuid.b[12]));
} break;
default:
fprintf(stderr, "Invalid Service Class ID List. " \
"Not a UUID, type=%#x\n", type);
return;
}
}
}
static void
print_protocol_descriptor(uint8_t const *start, uint8_t const *end)
{
union {
uint8_t uint8;
uint16_t uint16;
uint32_t uint32;
uint64_t uint64;
int128_t int128;
} value;
uint32_t type, len, param;
SDP_GET8(type, start);
switch (type) {
case SDP_DATA_UUID16:
SDP_GET16(value.uint16, start);
fprintf(stdout, "\t%s (%#4.4x)\n", sdp_uuid2desc(value.uint16),
value.uint16);
break;
case SDP_DATA_UUID32:
SDP_GET32(value.uint32, start);
fprintf(stdout, "\t%#8.8x\n", value.uint32);
break;
case SDP_DATA_UUID128:
SDP_GET_UUID128(&value.int128, start);
fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
ntohl(*(uint32_t *)&value.int128.b[0]),
ntohs(*(uint16_t *)&value.int128.b[4]),
ntohs(*(uint16_t *)&value.int128.b[6]),
ntohs(*(uint16_t *)&value.int128.b[8]),
ntohs(*(uint16_t *)&value.int128.b[10]),
ntohl(*(uint32_t *)&value.int128.b[12]));
break;
default:
fprintf(stderr, "Invalid Protocol Descriptor. " \
"Not a UUID, type=%#x\n", type);
return;
}
for (param = 1; start < end; param ++) {
fprintf(stdout, "\t\tProtocol specific parameter #%d: ", param);
SDP_GET8(type, start);
switch (type) {
case SDP_DATA_NIL:
fprintf(stdout, "nil\n");
break;
case SDP_DATA_UINT8:
case SDP_DATA_INT8:
case SDP_DATA_BOOL:
SDP_GET8(value.uint8, start);
fprintf(stdout, "u/int8/bool %u\n", value.uint8);
break;
case SDP_DATA_UINT16:
case SDP_DATA_INT16:
case SDP_DATA_UUID16:
SDP_GET16(value.uint16, start);
fprintf(stdout, "u/int/uuid16 %u\n", value.uint16);
break;
case SDP_DATA_UINT32:
case SDP_DATA_INT32:
case SDP_DATA_UUID32:
SDP_GET32(value.uint32, start);
fprintf(stdout, "u/int/uuid32 %u\n", value.uint32);
break;
case SDP_DATA_UINT64:
case SDP_DATA_INT64:
SDP_GET64(value.uint64, start);
fprintf(stdout, "u/int64 %ju\n", value.uint64);
break;
case SDP_DATA_UINT128:
case SDP_DATA_INT128:
SDP_GET128(&value.int128, start);
fprintf(stdout, "u/int128 %#8.8x%8.8x%8.8x%8.8x\n",
*(uint32_t *)&value.int128.b[0],
*(uint32_t *)&value.int128.b[4],
*(uint32_t *)&value.int128.b[8],
*(uint32_t *)&value.int128.b[12]);
break;
case SDP_DATA_UUID128:
SDP_GET_UUID128(&value.int128, start);
fprintf(stdout, "uuid128 %#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
ntohl(*(uint32_t *)&value.int128.b[0]),
ntohs(*(uint16_t *)&value.int128.b[4]),
ntohs(*(uint16_t *)&value.int128.b[6]),
ntohs(*(uint16_t *)&value.int128.b[8]),
ntohs(*(uint16_t *)&value.int128.b[10]),
ntohl(*(uint32_t *)&value.int128.b[12]));
break;
case SDP_DATA_STR8:
case SDP_DATA_URL8:
SDP_GET8(len, start);
for (; start < end && len > 0; start ++, len --)
fprintf(stdout, "%c", *start);
fprintf(stdout, "\n");
break;
case SDP_DATA_STR16:
case SDP_DATA_URL16:
SDP_GET16(len, start);
for (; start < end && len > 0; start ++, len --)
fprintf(stdout, "%c", *start);
fprintf(stdout, "\n");
break;
case SDP_DATA_STR32:
case SDP_DATA_URL32:
SDP_GET32(len, start);
for (; start < end && len > 0; start ++, len --)
fprintf(stdout, "%c", *start);
fprintf(stdout, "\n");
break;
case SDP_DATA_SEQ8:
case SDP_DATA_ALT8:
SDP_GET8(len, start);
for (; start < end && len > 0; start ++, len --)
fprintf(stdout, "%#2.2x ", *start);
fprintf(stdout, "\n");
break;
case SDP_DATA_SEQ16:
case SDP_DATA_ALT16:
SDP_GET16(len, start);
for (; start < end && len > 0; start ++, len --)
fprintf(stdout, "%#2.2x ", *start);
fprintf(stdout, "\n");
break;
case SDP_DATA_SEQ32:
case SDP_DATA_ALT32:
SDP_GET32(len, start);
for (; start < end && len > 0; start ++, len --)
fprintf(stdout, "%#2.2x ", *start);
fprintf(stdout, "\n");
break;
default:
fprintf(stderr, "Invalid Protocol Descriptor. " \
"Unknown data type: %#02x\n", type);
return;
}
}
}
static void
print_protocol_descriptor_list(uint8_t const *start, uint8_t const *end)
{
uint32_t type, len;
if (end - start < 2) {
fprintf(stderr, "Invalid Protocol Descriptor List. " \
"Too short, len=%zd\n", end - start);
return;
}
SDP_GET8(type, start);
switch (type) {
case SDP_DATA_SEQ8:
SDP_GET8(len, start);
break;
case SDP_DATA_SEQ16:
SDP_GET16(len, start);
break;
case SDP_DATA_SEQ32:
SDP_GET32(len, start);
break;
default:
fprintf(stderr, "Invalid Protocol Descriptor List. " \
"Not a sequence, type=%#x\n", type);
return;
}
if (len > (end - start)) {
fprintf(stderr, "Invalid Protocol Descriptor List. " \
"Too long, len=%d\n", len);
return;
}
while (start < end) {
SDP_GET8(type, start);
switch (type) {
case SDP_DATA_SEQ8:
SDP_GET8(len, start);
break;
case SDP_DATA_SEQ16:
SDP_GET16(len, start);
break;
case SDP_DATA_SEQ32:
SDP_GET32(len, start);
break;
default:
fprintf(stderr, "Invalid Protocol Descriptor List. " \
"Not a sequence, type=%#x\n", type);
return;
}
if (len > (end - start)) {
fprintf(stderr, "Invalid Protocol Descriptor List. " \
"Too long, len=%d\n", len);
return;
}
print_protocol_descriptor(start, start + len);
start += len;
}
}
static void
print_bluetooth_profile_descriptor_list(uint8_t const *start, uint8_t const *end)
{
uint32_t type, len, value;
if (end - start < 2) {
fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \
"Too short, len=%zd\n", end - start);
return;
}
SDP_GET8(type, start);
switch (type) {
case SDP_DATA_SEQ8:
SDP_GET8(len, start);
break;
case SDP_DATA_SEQ16:
SDP_GET16(len, start);
break;
case SDP_DATA_SEQ32:
SDP_GET32(len, start);
break;
default:
fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \
"Not a sequence, type=%#x\n", type);
return;
}
if (len > (end - start)) {
fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \
"Too long, len=%d\n", len);
return;
}
while (start < end) {
SDP_GET8(type, start);
switch (type) {
case SDP_DATA_SEQ8:
SDP_GET8(len, start);
break;
case SDP_DATA_SEQ16:
SDP_GET16(len, start);
break;
case SDP_DATA_SEQ32:
SDP_GET32(len, start);
break;
default:
fprintf(stderr, "Invalid Bluetooth Profile " \
"Descriptor List. " \
"Not a sequence, type=%#x\n", type);
return;
}
if (len > (end - start)) {
fprintf(stderr, "Invalid Bluetooth Profile " \
"Descriptor List. " \
"Too long, len=%d\n", len);
return;
}
SDP_GET8(type, start);
switch (type) {
case SDP_DATA_UUID16:
SDP_GET16(value, start);
fprintf(stdout, "\t%s (%#4.4x) ",
sdp_uuid2desc(value), value);
break;
case SDP_DATA_UUID32:
SDP_GET32(value, start);
fprintf(stdout, "\t%#8.8x ", value);
break;
case SDP_DATA_UUID128: {
int128_t uuid;
SDP_GET_UUID128(&uuid, start);
fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x ",
ntohl(*(uint32_t *)&uuid.b[0]),
ntohs(*(uint16_t *)&uuid.b[4]),
ntohs(*(uint16_t *)&uuid.b[6]),
ntohs(*(uint16_t *)&uuid.b[8]),
ntohs(*(uint16_t *)&uuid.b[10]),
ntohl(*(uint32_t *)&uuid.b[12]));
} break;
default:
fprintf(stderr, "Invalid Bluetooth Profile " \
"Descriptor List. " \
"Not a UUID, type=%#x\n", type);
return;
}
SDP_GET8(type, start);
if (type != SDP_DATA_UINT16) {
fprintf(stderr, "Invalid Bluetooth Profile " \
"Descriptor List. " \
"Invalid version type=%#x\n", type);
return;
}
SDP_GET16(value, start);
fprintf(stdout, "ver. %d.%d\n",
(value >> 8) & 0xff, value & 0xff);
}
}
static int
do_sdp_search(void *xs, int argc, char **argv)
{
char *ep = NULL;
int32_t n, type, value;
uint16_t service;
switch (argc) {
case 1:
n = strtoul(argv[0], &ep, 16);
if (*ep != 0) {
switch (tolower(argv[0][0])) {
case 'c':
switch (tolower(argv[0][1])) {
case 'i':
service = SDP_SERVICE_CLASS_COMMON_ISDN_ACCESS;
break;
case 't':
service = SDP_SERVICE_CLASS_CORDLESS_TELEPHONY;
break;
default:
return (USAGE);
}
break;
case 'd':
service = SDP_SERVICE_CLASS_DIALUP_NETWORKING;
break;
case 'f':
switch (tolower(argv[0][1])) {
case 'a':
service = SDP_SERVICE_CLASS_FAX;
break;
case 't':
service = SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER;
break;
default:
return (USAGE);
}
break;
case 'g':
service = SDP_SERVICE_CLASS_GN;
break;
case 'h':
switch (tolower(argv[0][1])) {
case 'i':
service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
break;
case 's':
service = SDP_SERVICE_CLASS_HEADSET;
break;
default:
return (USAGE);
}
break;
case 'l':
service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP;
break;
case 'n':
service = SDP_SERVICE_CLASS_NAP;
break;
case 'o':
service = SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH;
break;
case 's':
service = SDP_SERVICE_CLASS_SERIAL_PORT;
break;
default:
return (USAGE);
}
} else
service = (uint16_t) n;
break;
default:
return (USAGE);
}
for (n = 0; n < values_len; n ++) {
values[n].flags = SDP_ATTR_INVALID;
values[n].attr = 0;
values[n].vlen = BSIZE;
values[n].value = buffer[n];
}
n = sdp_search(xs, 1, &service, attrs_len, attrs, values_len, values);
if (n != 0)
return (ERROR);
for (n = 0; n < values_len; n ++) {
if (values[n].flags != SDP_ATTR_OK)
break;
switch (values[n].attr) {
case SDP_ATTR_SERVICE_RECORD_HANDLE:
fprintf(stdout, "\n");
if (values[n].vlen == 5) {
SDP_GET8(type, values[n].value);
if (type == SDP_DATA_UINT32) {
SDP_GET32(value, values[n].value);
fprintf(stdout, "Record Handle: " \
"%#8.8x\n", value);
} else
fprintf(stderr, "Invalid type=%#x " \
"Record Handle " \
"attribute!\n", type);
} else
fprintf(stderr, "Invalid size=%d for Record " \
"Handle attribute\n",
values[n].vlen);
break;
case SDP_ATTR_SERVICE_CLASS_ID_LIST:
fprintf(stdout, "Service Class ID List:\n");
print_service_class_id_list(values[n].value,
values[n].value + values[n].vlen);
break;
case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
fprintf(stdout, "Protocol Descriptor List:\n");
print_protocol_descriptor_list(values[n].value,
values[n].value + values[n].vlen);
break;
case SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST:
fprintf(stdout, "Bluetooth Profile Descriptor List:\n");
print_bluetooth_profile_descriptor_list(values[n].value,
values[n].value + values[n].vlen);
break;
default:
fprintf(stderr, "Unexpected attribute ID=%#4.4x\n",
values[n].attr);
break;
}
}
return (OK);
}
static int
do_sdp_browse(void *xs, int argc, char **argv)
{
#undef _STR
#undef STR
#define _STR(x) #x
#define STR(x) _STR(x)
static char const * const av[] = {
STR(SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP),
NULL
};
switch (argc) {
case 0:
argc = 1;
argv = (char **) av;
case 1:
return (do_sdp_search(xs, argc, argv));
}
return (USAGE);
}
struct sdp_command sdp_commands[] = {
{
"Browse [<Group>]",
"Browse for services. The <Group> parameter is a 16-bit UUID of the group\n" \
"to browse. If omitted <Group> is set to Public Browse Group.\n\n" \
"\t<Group> - xxxx; 16-bit UUID of the group to browse\n",
do_sdp_browse
},
{
"Search <Service>",
"Search for the <Service>. The <Service> parameter is a 16-bit UUID of the\n" \
"service to search for. For some services it is possible to use service name\n"\
"instead of service UUID\n\n" \
"\t<Service> - xxxx; 16-bit UUID of the service to search for\n\n" \
"\tKnown service names\n" \
"\t===================\n" \
"\tCIP - Common ISDN Access\n" \
"\tCTP - Cordless Telephony\n" \
"\tDUN - DialUp Networking\n" \
"\tFAX - Fax\n" \
"\tFTRN - OBEX File Transfer\n" \
"\tGN - GN\n" \
"\tHID - Human Interface Device\n" \
"\tHSET - Headset\n" \
"\tLAN - LAN Access Using PPP\n" \
"\tNAP - Network Access Point\n" \
"\tOPUSH - OBEX Object Push\n" \
"\tSP - Serial Port\n",
do_sdp_search
},
{ NULL, NULL, NULL }
};