Path: blob/main/usr.sbin/bluetooth/sdpcontrol/search.c
104428 views
/*-1* search.c2*3* SPDX-License-Identifier: BSD-2-Clause4*5* Copyright (c) 2001-2003 Maksim Yevmenkin <[email protected]>6* All rights reserved.7*8* Redistribution and use in source and binary forms, with or without9* modification, are permitted provided that the following conditions10* are met:11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions and the following disclaimer.13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND18* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE21* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL22* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS23* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)24* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT25* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY26* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF27* SUCH DAMAGE.28*29* $Id: search.c,v 1.2 2003/09/08 17:35:15 max Exp $30*/3132#include <sys/param.h>33#include <netinet/in.h>34#define L2CAP_SOCKET_CHECKED35#include <bluetooth.h>36#include <ctype.h>37#include <sdp.h>38#include <stdio.h>39#include <stdlib.h>40#include "sdpcontrol.h"4142/* List of the attributes we are looking for */43static uint32_t attrs[] =44{45SDP_ATTR_RANGE( SDP_ATTR_SERVICE_RECORD_HANDLE,46SDP_ATTR_SERVICE_RECORD_HANDLE),47SDP_ATTR_RANGE( SDP_ATTR_SERVICE_CLASS_ID_LIST,48SDP_ATTR_SERVICE_CLASS_ID_LIST),49SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,50SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),51SDP_ATTR_RANGE( SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST,52SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST)53};54#define attrs_len nitems(attrs)5556/* Buffer for the attributes */57#define NRECS 25 /* request this much records from the SDP server */58#define BSIZE 256 /* one attribute buffer size */59static uint8_t buffer[NRECS * attrs_len][BSIZE];6061/* SDP attributes */62static sdp_attr_t values[NRECS * attrs_len];63#define values_len nitems(values)6465/*66* Print Service Class ID List67*68* The ServiceClassIDList attribute consists of a data element sequence in69* which each data element is a UUID representing the service classes that70* a given service record conforms to. The UUIDs are listed in order from71* the most specific class to the most general class. The ServiceClassIDList72* must contain at least one service class UUID.73*/7475static void76print_service_class_id_list(uint8_t const *start, uint8_t const *end)77{78uint32_t type, len, value;7980if (end - start < 2) {81fprintf(stderr, "Invalid Service Class ID List. " \82"Too short, len=%zd\n", end - start);83return;84}8586SDP_GET8(type, start);87switch (type) {88case SDP_DATA_SEQ8:89SDP_GET8(len, start);90break;9192case SDP_DATA_SEQ16:93SDP_GET16(len, start);94break;9596case SDP_DATA_SEQ32:97SDP_GET32(len, start);98break;99100default:101fprintf(stderr, "Invalid Service Class ID List. " \102"Not a sequence, type=%#x\n", type);103return;104/* NOT REACHED */105}106107if (len > (end - start)) {108fprintf(stderr, "Invalid Service Class ID List. " \109"Too long len=%d\n", len);110return;111}112113while (start < end) {114SDP_GET8(type, start);115switch (type) {116case SDP_DATA_UUID16:117SDP_GET16(value, start);118fprintf(stdout, "\t%s (%#4.4x)\n",119sdp_uuid2desc(value), value);120break;121122case SDP_DATA_UUID32:123SDP_GET32(value, start);124fprintf(stdout, "\t%#8.8x\n", value);125break;126127case SDP_DATA_UUID128: {128int128_t uuid;129130SDP_GET_UUID128(&uuid, start);131fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",132ntohl(*(uint32_t *)&uuid.b[0]),133ntohs(*(uint16_t *)&uuid.b[4]),134ntohs(*(uint16_t *)&uuid.b[6]),135ntohs(*(uint16_t *)&uuid.b[8]),136ntohs(*(uint16_t *)&uuid.b[10]),137ntohl(*(uint32_t *)&uuid.b[12]));138} break;139140default:141fprintf(stderr, "Invalid Service Class ID List. " \142"Not a UUID, type=%#x\n", type);143return;144/* NOT REACHED */145}146}147} /* print_service_class_id_list */148149/*150* Print Protocol Descriptor List151*152* If the ProtocolDescriptorList describes a single stack, it takes the form153* of a data element sequence in which each element of the sequence is a154* protocol descriptor. Each protocol descriptor is, in turn, a data element155* sequence whose first element is a UUID identifying the protocol and whose156* successive elements are protocol-specific parameters. The protocol157* descriptors are listed in order from the lowest layer protocol to the158* highest layer protocol used to gain access to the service. If it is possible159* for more than one kind of protocol stack to be used to gain access to the160* service, the ProtocolDescriptorList takes the form of a data element161* alternative where each member is a data element sequence as described above.162*/163164static void165print_protocol_descriptor(uint8_t const *start, uint8_t const *end)166{167union {168uint8_t uint8;169uint16_t uint16;170uint32_t uint32;171uint64_t uint64;172int128_t int128;173} value;174uint32_t type, len, param;175176/* Get Protocol UUID */177SDP_GET8(type, start);178switch (type) {179case SDP_DATA_UUID16:180SDP_GET16(value.uint16, start);181fprintf(stdout, "\t%s (%#4.4x)\n", sdp_uuid2desc(value.uint16),182value.uint16);183break;184185case SDP_DATA_UUID32:186SDP_GET32(value.uint32, start);187fprintf(stdout, "\t%#8.8x\n", value.uint32);188break;189190case SDP_DATA_UUID128:191SDP_GET_UUID128(&value.int128, start);192fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",193ntohl(*(uint32_t *)&value.int128.b[0]),194ntohs(*(uint16_t *)&value.int128.b[4]),195ntohs(*(uint16_t *)&value.int128.b[6]),196ntohs(*(uint16_t *)&value.int128.b[8]),197ntohs(*(uint16_t *)&value.int128.b[10]),198ntohl(*(uint32_t *)&value.int128.b[12]));199break;200201default:202fprintf(stderr, "Invalid Protocol Descriptor. " \203"Not a UUID, type=%#x\n", type);204return;205/* NOT REACHED */206}207208/* Protocol specific parameters */209for (param = 1; start < end; param ++) {210fprintf(stdout, "\t\tProtocol specific parameter #%d: ", param);211212SDP_GET8(type, start);213switch (type) {214case SDP_DATA_NIL:215fprintf(stdout, "nil\n");216break;217218case SDP_DATA_UINT8:219case SDP_DATA_INT8:220case SDP_DATA_BOOL:221SDP_GET8(value.uint8, start);222fprintf(stdout, "u/int8/bool %u\n", value.uint8);223break;224225case SDP_DATA_UINT16:226case SDP_DATA_INT16:227case SDP_DATA_UUID16:228SDP_GET16(value.uint16, start);229fprintf(stdout, "u/int/uuid16 %u\n", value.uint16);230break;231232case SDP_DATA_UINT32:233case SDP_DATA_INT32:234case SDP_DATA_UUID32:235SDP_GET32(value.uint32, start);236fprintf(stdout, "u/int/uuid32 %u\n", value.uint32);237break;238239case SDP_DATA_UINT64:240case SDP_DATA_INT64:241SDP_GET64(value.uint64, start);242fprintf(stdout, "u/int64 %ju\n", value.uint64);243break;244245case SDP_DATA_UINT128:246case SDP_DATA_INT128:247SDP_GET128(&value.int128, start);248fprintf(stdout, "u/int128 %#8.8x%8.8x%8.8x%8.8x\n",249*(uint32_t *)&value.int128.b[0],250*(uint32_t *)&value.int128.b[4],251*(uint32_t *)&value.int128.b[8],252*(uint32_t *)&value.int128.b[12]);253break;254255case SDP_DATA_UUID128:256SDP_GET_UUID128(&value.int128, start);257fprintf(stdout, "uuid128 %#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",258ntohl(*(uint32_t *)&value.int128.b[0]),259ntohs(*(uint16_t *)&value.int128.b[4]),260ntohs(*(uint16_t *)&value.int128.b[6]),261ntohs(*(uint16_t *)&value.int128.b[8]),262ntohs(*(uint16_t *)&value.int128.b[10]),263ntohl(*(uint32_t *)&value.int128.b[12]));264break;265266case SDP_DATA_STR8:267case SDP_DATA_URL8:268SDP_GET8(len, start);269for (; start < end && len > 0; start ++, len --)270fprintf(stdout, "%c", *start);271fprintf(stdout, "\n");272break;273274case SDP_DATA_STR16:275case SDP_DATA_URL16:276SDP_GET16(len, start);277for (; start < end && len > 0; start ++, len --)278fprintf(stdout, "%c", *start);279fprintf(stdout, "\n");280break;281282case SDP_DATA_STR32:283case SDP_DATA_URL32:284SDP_GET32(len, start);285for (; start < end && len > 0; start ++, len --)286fprintf(stdout, "%c", *start);287fprintf(stdout, "\n");288break;289290case SDP_DATA_SEQ8:291case SDP_DATA_ALT8:292SDP_GET8(len, start);293for (; start < end && len > 0; start ++, len --)294fprintf(stdout, "%#2.2x ", *start);295fprintf(stdout, "\n");296break;297298case SDP_DATA_SEQ16:299case SDP_DATA_ALT16:300SDP_GET16(len, start);301for (; start < end && len > 0; start ++, len --)302fprintf(stdout, "%#2.2x ", *start);303fprintf(stdout, "\n");304break;305306case SDP_DATA_SEQ32:307case SDP_DATA_ALT32:308SDP_GET32(len, start);309for (; start < end && len > 0; start ++, len --)310fprintf(stdout, "%#2.2x ", *start);311fprintf(stdout, "\n");312break;313314default:315fprintf(stderr, "Invalid Protocol Descriptor. " \316"Unknown data type: %#02x\n", type);317return;318/* NOT REACHED */319}320}321} /* print_protocol_descriptor */322323static void324print_protocol_descriptor_list(uint8_t const *start, uint8_t const *end)325{326uint32_t type, len;327328if (end - start < 2) {329fprintf(stderr, "Invalid Protocol Descriptor List. " \330"Too short, len=%zd\n", end - start);331return;332}333334SDP_GET8(type, start);335switch (type) {336case SDP_DATA_SEQ8:337SDP_GET8(len, start);338break;339340case SDP_DATA_SEQ16:341SDP_GET16(len, start);342break;343344case SDP_DATA_SEQ32:345SDP_GET32(len, start);346break;347348default:349fprintf(stderr, "Invalid Protocol Descriptor List. " \350"Not a sequence, type=%#x\n", type);351return;352/* NOT REACHED */353}354355if (len > (end - start)) {356fprintf(stderr, "Invalid Protocol Descriptor List. " \357"Too long, len=%d\n", len);358return;359}360361while (start < end) {362SDP_GET8(type, start);363switch (type) {364case SDP_DATA_SEQ8:365SDP_GET8(len, start);366break;367368case SDP_DATA_SEQ16:369SDP_GET16(len, start);370break;371372case SDP_DATA_SEQ32:373SDP_GET32(len, start);374break;375376default:377fprintf(stderr, "Invalid Protocol Descriptor List. " \378"Not a sequence, type=%#x\n", type);379return;380/* NOT REACHED */381}382383if (len > (end - start)) {384fprintf(stderr, "Invalid Protocol Descriptor List. " \385"Too long, len=%d\n", len);386return;387}388389print_protocol_descriptor(start, start + len);390start += len;391}392} /* print_protocol_descriptor_list */393394/*395* Print Bluetooth Profile Descriptor List396*397* The BluetoothProfileDescriptorList attribute consists of a data element398* sequence in which each element is a profile descriptor that contains399* information about a Bluetooth profile to which the service represented by400* this service record conforms. Each profile descriptor is a data element401* sequence whose first element is the UUID assigned to the profile and whose402* second element is a 16-bit profile version number. Each version of a profile403* is assigned a 16-bit unsigned integer profile version number, which consists404* of two 8-bit fields. The higher-order 8 bits contain the major version405* number field and the lower-order 8 bits contain the minor version number406* field.407*/408409static void410print_bluetooth_profile_descriptor_list(uint8_t const *start, uint8_t const *end)411{412uint32_t type, len, value;413414if (end - start < 2) {415fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \416"Too short, len=%zd\n", end - start);417return;418}419420SDP_GET8(type, start);421switch (type) {422case SDP_DATA_SEQ8:423SDP_GET8(len, start);424break;425426case SDP_DATA_SEQ16:427SDP_GET16(len, start);428break;429430case SDP_DATA_SEQ32:431SDP_GET32(len, start);432break;433434default:435fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \436"Not a sequence, type=%#x\n", type);437return;438/* NOT REACHED */439}440441if (len > (end - start)) {442fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \443"Too long, len=%d\n", len);444return;445}446447while (start < end) {448SDP_GET8(type, start);449switch (type) {450case SDP_DATA_SEQ8:451SDP_GET8(len, start);452break;453454case SDP_DATA_SEQ16:455SDP_GET16(len, start);456break;457458case SDP_DATA_SEQ32:459SDP_GET32(len, start);460break;461462default:463fprintf(stderr, "Invalid Bluetooth Profile " \464"Descriptor List. " \465"Not a sequence, type=%#x\n", type);466return;467/* NOT REACHED */468}469470if (len > (end - start)) {471fprintf(stderr, "Invalid Bluetooth Profile " \472"Descriptor List. " \473"Too long, len=%d\n", len);474return;475}476477/* Get UUID */478SDP_GET8(type, start);479switch (type) {480case SDP_DATA_UUID16:481SDP_GET16(value, start);482fprintf(stdout, "\t%s (%#4.4x) ",483sdp_uuid2desc(value), value);484break;485486case SDP_DATA_UUID32:487SDP_GET32(value, start);488fprintf(stdout, "\t%#8.8x ", value);489break;490491case SDP_DATA_UUID128: {492int128_t uuid;493494SDP_GET_UUID128(&uuid, start);495fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x ",496ntohl(*(uint32_t *)&uuid.b[0]),497ntohs(*(uint16_t *)&uuid.b[4]),498ntohs(*(uint16_t *)&uuid.b[6]),499ntohs(*(uint16_t *)&uuid.b[8]),500ntohs(*(uint16_t *)&uuid.b[10]),501ntohl(*(uint32_t *)&uuid.b[12]));502} break;503504default:505fprintf(stderr, "Invalid Bluetooth Profile " \506"Descriptor List. " \507"Not a UUID, type=%#x\n", type);508return;509/* NOT REACHED */510}511512/* Get version */513SDP_GET8(type, start);514if (type != SDP_DATA_UINT16) {515fprintf(stderr, "Invalid Bluetooth Profile " \516"Descriptor List. " \517"Invalid version type=%#x\n", type);518return;519}520521SDP_GET16(value, start);522fprintf(stdout, "ver. %d.%d\n",523(value >> 8) & 0xff, value & 0xff);524}525} /* print_bluetooth_profile_descriptor_list */526527/* Perform SDP search command */528static int529do_sdp_search(void *xs, int argc, char **argv)530{531char *ep = NULL;532int32_t n, type, value;533uint16_t service;534535/* Parse command line arguments */536switch (argc) {537case 1:538n = strtoul(argv[0], &ep, 16);539if (*ep != 0) {540switch (tolower(argv[0][0])) {541case 'c': /* CIP/CTP */542switch (tolower(argv[0][1])) {543case 'i':544service = SDP_SERVICE_CLASS_COMMON_ISDN_ACCESS;545break;546547case 't':548service = SDP_SERVICE_CLASS_CORDLESS_TELEPHONY;549break;550551default:552return (USAGE);553/* NOT REACHED */554}555break;556557case 'd': /* DialUp Networking */558service = SDP_SERVICE_CLASS_DIALUP_NETWORKING;559break;560561case 'f': /* Fax/OBEX File Transfer */562switch (tolower(argv[0][1])) {563case 'a':564service = SDP_SERVICE_CLASS_FAX;565break;566567case 't':568service = SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER;569break;570571default:572return (USAGE);573/* NOT REACHED */574}575break;576577case 'g': /* GN */578service = SDP_SERVICE_CLASS_GN;579break;580581case 'h': /* Headset/HID */582switch (tolower(argv[0][1])) {583case 'i':584service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;585break;586587case 's':588service = SDP_SERVICE_CLASS_HEADSET;589break;590591default:592return (USAGE);593/* NOT REACHED */594}595break;596597case 'l': /* LAN Access Using PPP */598service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP;599break;600601case 'n': /* NAP */602service = SDP_SERVICE_CLASS_NAP;603break;604605case 'o': /* OBEX Object Push */606service = SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH;607break;608609case 's': /* Serial Port */610service = SDP_SERVICE_CLASS_SERIAL_PORT;611break;612613default:614return (USAGE);615/* NOT REACHED */616}617} else618service = (uint16_t) n;619break;620621default:622return (USAGE);623}624625/* Initialize attribute values array */626for (n = 0; n < values_len; n ++) {627values[n].flags = SDP_ATTR_INVALID;628values[n].attr = 0;629values[n].vlen = BSIZE;630values[n].value = buffer[n];631}632633/* Do SDP Service Search Attribute Request */634n = sdp_search(xs, 1, &service, attrs_len, attrs, values_len, values);635if (n != 0)636return (ERROR);637638/* Print attributes values */639for (n = 0; n < values_len; n ++) {640if (values[n].flags != SDP_ATTR_OK)641break;642643switch (values[n].attr) {644case SDP_ATTR_SERVICE_RECORD_HANDLE:645fprintf(stdout, "\n");646if (values[n].vlen == 5) {647SDP_GET8(type, values[n].value);648if (type == SDP_DATA_UINT32) {649SDP_GET32(value, values[n].value);650fprintf(stdout, "Record Handle: " \651"%#8.8x\n", value);652} else653fprintf(stderr, "Invalid type=%#x " \654"Record Handle " \655"attribute!\n", type);656} else657fprintf(stderr, "Invalid size=%d for Record " \658"Handle attribute\n",659values[n].vlen);660break;661662case SDP_ATTR_SERVICE_CLASS_ID_LIST:663fprintf(stdout, "Service Class ID List:\n");664print_service_class_id_list(values[n].value,665values[n].value + values[n].vlen);666break;667668case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:669fprintf(stdout, "Protocol Descriptor List:\n");670print_protocol_descriptor_list(values[n].value,671values[n].value + values[n].vlen);672break;673674case SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST:675fprintf(stdout, "Bluetooth Profile Descriptor List:\n");676print_bluetooth_profile_descriptor_list(values[n].value,677values[n].value + values[n].vlen);678break;679680default:681fprintf(stderr, "Unexpected attribute ID=%#4.4x\n",682values[n].attr);683break;684}685}686687return (OK);688} /* do_sdp_search */689690/* Perform SDP browse command */691static int692do_sdp_browse(void *xs, int argc, char **argv)693{694#undef _STR695#undef STR696#define _STR(x) #x697#define STR(x) _STR(x)698699static char const * const av[] = {700STR(SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP),701NULL702};703704switch (argc) {705case 0:706argc = 1;707argv = (char **) av;708/* FALL THROUGH */709case 1:710return (do_sdp_search(xs, argc, argv));711}712713return (USAGE);714} /* do_sdp_browse */715716/* List of SDP commands */717struct sdp_command sdp_commands[] = {718{719"Browse [<Group>]",720"Browse for services. The <Group> parameter is a 16-bit UUID of the group\n" \721"to browse. If omitted <Group> is set to Public Browse Group.\n\n" \722"\t<Group> - xxxx; 16-bit UUID of the group to browse\n",723do_sdp_browse724},725{726"Search <Service>",727"Search for the <Service>. The <Service> parameter is a 16-bit UUID of the\n" \728"service to search for. For some services it is possible to use service name\n"\729"instead of service UUID\n\n" \730"\t<Service> - xxxx; 16-bit UUID of the service to search for\n\n" \731"\tKnown service names\n" \732"\t===================\n" \733"\tCIP - Common ISDN Access\n" \734"\tCTP - Cordless Telephony\n" \735"\tDUN - DialUp Networking\n" \736"\tFAX - Fax\n" \737"\tFTRN - OBEX File Transfer\n" \738"\tGN - GN\n" \739"\tHID - Human Interface Device\n" \740"\tHSET - Headset\n" \741"\tLAN - LAN Access Using PPP\n" \742"\tNAP - Network Access Point\n" \743"\tOPUSH - OBEX Object Push\n" \744"\tSP - Serial Port\n",745do_sdp_search746},747{ NULL, NULL, NULL }748};749750751752