aboutsummaryrefslogblamecommitdiff
path: root/usr.sbin/bluetooth/sdpcontrol/search.c
blob: acc8c167ed6e9daa910676e666c1d80753242264 (plain) (tree)






























                                                                             
                       
                            







                                               
                         














                                                                           
                                                         















                                                                            
                                                                     
 
                                         


                                                                   
                                                                     








































                                                                     
                                                      
                                                                                  





                                                                         


























                                                                               
                                                                   







                                       
                                                 















                                                                               
                                                      
                                                                          





                                                                         










































                                                                                
                                                                       



                                      
                                                         
                                                                           
                                                                

                                                                
                                                                  

                              










                                                                                        






















































                                                                             
                                                                        
 
                                  


                                                                      
                                                                     


































































                                                                               
                                                                                 
 
                                         


                                                                               
                                                                     































































                                                                               
                                                      
                                                                                 





                                                                         






























                                                                            
                                 



















































































                                                                                           
                                               





                               




























































































































                                                                                
/*
 * search.c
 *
 * Copyright (c) 2001-2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
 * 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 <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"

/* 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 */
	}

	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);
			fprintf(stdout, "%*.*s\n", len, len, (char *) start);
			start += len;
			break;

		case SDP_DATA_STR16:
		case SDP_DATA_URL16:
			SDP_GET16(len, start);
			fprintf(stdout, "%*.*s\n", len, len, (char *) start);
			start += len;
			break;

		case SDP_DATA_STR32:
		case SDP_DATA_URL32:
			SDP_GET32(len, start);
			fprintf(stdout, "%*.*s\n", len, len, (char *) start);
			start += len;
			break;

		case SDP_DATA_SEQ8:
		case SDP_DATA_ALT8:
			SDP_GET8(len, start);
			for (; 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 (; 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 (; 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 */
	}

	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 */
		}

		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 */
	}

	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 */
		}

		/* 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 [<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 }
};