/*- * search.c * * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2001-2003 Maksim Yevmenkin * All rights reserved. * * 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 AUTHOR 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 AUTHOR 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. * * $Id: search.c,v 1.2 2003/09/08 17:35:15 max Exp $ * $FreeBSD$ */ #include #define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include "sdpcontrol.h" /* List of the attributes we are looking for */ 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 (sizeof(attrs)/sizeof(attrs[0])) /* Buffer for the attributes */ #define NRECS 25 /* request this much records from the SDP server */ #define BSIZE 256 /* one attribute buffer size */ static uint8_t buffer[NRECS * attrs_len][BSIZE]; /* SDP attributes */ static sdp_attr_t values[NRECS * attrs_len]; #define values_len (sizeof(values)/sizeof(values[0])) /* * Print Service Class ID List * * The ServiceClassIDList attribute consists of a data element sequence in * which each data element is a UUID representing the service classes that * a given service record conforms to. The UUIDs are listed in order from * the most specific class to the most general class. The ServiceClassIDList * must contain at least one service class UUID. */ 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; /* NOT REACHED */ } 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; /* NOT REACHED */ } } } /* print_service_class_id_list */ /* * Print Protocol Descriptor List * * If the ProtocolDescriptorList describes a single stack, it takes the form * of a data element sequence in which each element of the sequence is a * protocol descriptor. Each protocol descriptor is, in turn, a data element * sequence whose first element is a UUID identifying the protocol and whose * successive elements are protocol-specific parameters. The protocol * descriptors are listed in order from the lowest layer protocol to the * highest layer protocol used to gain access to the service. If it is possible * for more than one kind of protocol stack to be used to gain access to the * service, the ProtocolDescriptorList takes the form of a data element * alternative where each member is a data element sequence as described above. */ 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; /* Get Protocol UUID */ 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; /* NOT REACHED */ } /* Protocol specific parameters */ 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; /* NOT REACHED */ } } } /* print_protocol_descriptor */ 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; /* NOT REACHED */ } 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; /* NOT REACHED */ } 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; } } /* print_protocol_descriptor_list */ /* * Print Bluetooth Profile Descriptor List * * The BluetoothProfileDescriptorList attribute consists of a data element * sequence in which each element is a profile descriptor that contains * information about a Bluetooth profile to which the service represented by * this service record conforms. Each profile descriptor is a data element * sequence whose first element is the UUID assigned to the profile and whose * second element is a 16-bit profile version number. Each version of a profile * is assigned a 16-bit unsigned integer profile version number, which consists * of two 8-bit fields. The higher-order 8 bits contain the major version * number field and the lower-order 8 bits contain the minor version number * field. */ 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; /* NOT REACHED */ } 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; /* NOT REACHED */ } if (len > (end - start)) { fprintf(stderr, "Invalid Bluetooth Profile " \ "Descriptor List. " \ "Too long, len=%d\n", len); return; } /* Get UUID */ 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; /* NOT REACHED */ } /* Get version */ 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); } } /* print_bluetooth_profile_descriptor_list */ /* Perform SDP search command */ static int do_sdp_search(void *xs, int argc, char **argv) { char *ep = NULL; int32_t n, type, value; uint16_t service; /* Parse command line arguments */ switch (argc) { case 1: n = strtoul(argv[0], &ep, 16); if (*ep != 0) { switch (tolower(argv[0][0])) { case 'c': /* CIP/CTP */ 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); /* NOT REACHED */ } break; case 'd': /* DialUp Networking */ service = SDP_SERVICE_CLASS_DIALUP_NETWORKING; break; case 'f': /* Fax/OBEX File Transfer */ 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); /* NOT REACHED */ } break; case 'g': /* GN */ service = SDP_SERVICE_CLASS_GN; break; case 'h': /* Headset/HID */ 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); /* NOT REACHED */ } break; case 'l': /* LAN Access Using PPP */ service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP; break; case 'n': /* NAP */ service = SDP_SERVICE_CLASS_NAP; break; case 'o': /* OBEX Object Push */ service = SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH; break; case 's': /* Serial Port */ service = SDP_SERVICE_CLASS_SERIAL_PORT; break; default: return (USAGE); /* NOT REACHED */ } } else service = (uint16_t) n; break; default: return (USAGE); } /* Initialize attribute values array */ 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]; } /* Do SDP Service Search Attribute Request */ n = sdp_search(xs, 1, &service, attrs_len, attrs, values_len, values); if (n != 0) return (ERROR); /* Print attributes values */ 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); } /* do_sdp_search */ /* Perform SDP browse command */ 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; /* FALL THROUGH */ case 1: return (do_sdp_search(xs, argc, argv)); } return (USAGE); } /* do_sdp_browse */ /* List of SDP commands */ struct sdp_command sdp_commands[] = { { "Browse []", "Browse for services. The parameter is a 16-bit UUID of the group\n" \ "to browse. If omitted is set to Public Browse Group.\n\n" \ "\t - xxxx; 16-bit UUID of the group to browse\n", do_sdp_browse }, { "Search ", "Search for the . The 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 - 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 } };