xref: /dflybsd-src/usr.bin/rfcomm_sppd/rfcomm_sdp.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
186d7f5d3SJohn Marino /* $NetBSD: rfcomm_sdp.c,v 1.1 2006/06/19 15:44:56 gdamore Exp $ */
286d7f5d3SJohn Marino /* $DragonFly: src/usr.bin/rfcomm_sppd/rfcomm_sdp.c,v 1.1 2008/02/08 14:06:25 hasso Exp $ */
386d7f5d3SJohn Marino 
486d7f5d3SJohn Marino /*-
586d7f5d3SJohn Marino  * Copyright (c) 2006 Itronix Inc.
686d7f5d3SJohn Marino  * All rights reserved.
786d7f5d3SJohn Marino  *
886d7f5d3SJohn Marino  * Redistribution and use in source and binary forms, with or without
986d7f5d3SJohn Marino  * modification, are permitted provided that the following conditions
1086d7f5d3SJohn Marino  * are met:
1186d7f5d3SJohn Marino  * 1. Redistributions of source code must retain the above copyright
1286d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer.
1386d7f5d3SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
1486d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
1586d7f5d3SJohn Marino  *    documentation and/or other materials provided with the distribution.
1686d7f5d3SJohn Marino  * 3. The name of Itronix Inc. may not be used to endorse
1786d7f5d3SJohn Marino  *    or promote products derived from this software without specific
1886d7f5d3SJohn Marino  *    prior written permission.
1986d7f5d3SJohn Marino  *
2086d7f5d3SJohn Marino  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
2186d7f5d3SJohn Marino  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2286d7f5d3SJohn Marino  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2386d7f5d3SJohn Marino  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
2486d7f5d3SJohn Marino  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2586d7f5d3SJohn Marino  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2686d7f5d3SJohn Marino  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2786d7f5d3SJohn Marino  * ON ANY THEORY OF LIABILITY, WHETHER IN
2886d7f5d3SJohn Marino  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2986d7f5d3SJohn Marino  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3086d7f5d3SJohn Marino  * POSSIBILITY OF SUCH DAMAGE.
3186d7f5d3SJohn Marino  */
3286d7f5d3SJohn Marino /*
3386d7f5d3SJohn Marino  * rfcomm_sdp.c
3486d7f5d3SJohn Marino  *
3586d7f5d3SJohn Marino  * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
3686d7f5d3SJohn Marino  * All rights reserved.
3786d7f5d3SJohn Marino  *
3886d7f5d3SJohn Marino  * Redistribution and use in source and binary forms, with or without
3986d7f5d3SJohn Marino  * modification, are permitted provided that the following conditions
4086d7f5d3SJohn Marino  * are met:
4186d7f5d3SJohn Marino  * 1. Redistributions of source code must retain the above copyright
4286d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer.
4386d7f5d3SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
4486d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
4586d7f5d3SJohn Marino  *    documentation and/or other materials provided with the distribution.
4686d7f5d3SJohn Marino  *
4786d7f5d3SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
4886d7f5d3SJohn Marino  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4986d7f5d3SJohn Marino  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5086d7f5d3SJohn Marino  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
5186d7f5d3SJohn Marino  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5286d7f5d3SJohn Marino  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5386d7f5d3SJohn Marino  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5486d7f5d3SJohn Marino  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5586d7f5d3SJohn Marino  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5686d7f5d3SJohn Marino  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5786d7f5d3SJohn Marino  * SUCH DAMAGE.
5886d7f5d3SJohn Marino  *
5986d7f5d3SJohn Marino  * $Id: rfcomm_sdp.c,v 1.1 2006/06/19 15:44:56 gdamore Exp $
6086d7f5d3SJohn Marino  * $FreeBSD: src/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sdp.c,v 1.2 2004/04/09 23:26:16 emax Exp $
6186d7f5d3SJohn Marino  */
6286d7f5d3SJohn Marino 
6386d7f5d3SJohn Marino #include <bluetooth.h>
6486d7f5d3SJohn Marino #include <errno.h>
6586d7f5d3SJohn Marino #include <sdp.h>
6686d7f5d3SJohn Marino #include <stdio.h>
6786d7f5d3SJohn Marino 
6886d7f5d3SJohn Marino #include "rfcomm_sdp.h"
6986d7f5d3SJohn Marino 
7086d7f5d3SJohn Marino #undef	PROTOCOL_DESCRIPTOR_LIST_BUFFER_SIZE
7186d7f5d3SJohn Marino #define	PROTOCOL_DESCRIPTOR_LIST_BUFFER_SIZE	256
7286d7f5d3SJohn Marino 
7386d7f5d3SJohn Marino #undef	PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE
7486d7f5d3SJohn Marino #define	PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE	12
7586d7f5d3SJohn Marino 
7686d7f5d3SJohn Marino static int rfcomm_proto_list_parse (uint8_t const *start, uint8_t const *end,
7786d7f5d3SJohn Marino 					uint8_t *channel, int *error);
7886d7f5d3SJohn Marino 
7986d7f5d3SJohn Marino /*
8086d7f5d3SJohn Marino  * Lookup RFCOMM channel number in the Protocol Descriptor List
8186d7f5d3SJohn Marino  */
8286d7f5d3SJohn Marino 
8386d7f5d3SJohn Marino #undef	rfcomm_channel_lookup_exit
8486d7f5d3SJohn Marino #define	rfcomm_channel_lookup_exit(e) { \
8586d7f5d3SJohn Marino 	if (error != NULL) \
8686d7f5d3SJohn Marino 		*error = (e); \
8786d7f5d3SJohn Marino 	if (ss != NULL) { \
8886d7f5d3SJohn Marino 		sdp_close(ss); \
8986d7f5d3SJohn Marino 		ss = NULL; \
9086d7f5d3SJohn Marino 	} \
9186d7f5d3SJohn Marino 	return (((e) == 0)? 0 : -1); \
9286d7f5d3SJohn Marino }
9386d7f5d3SJohn Marino 
9486d7f5d3SJohn Marino int
rfcomm_channel_lookup(bdaddr_t const * local,bdaddr_t const * remote,int service,uint8_t * channel,int * error)9586d7f5d3SJohn Marino rfcomm_channel_lookup(bdaddr_t const *local, bdaddr_t const *remote,
9686d7f5d3SJohn Marino 			int service, uint8_t *channel, int *error)
9786d7f5d3SJohn Marino {
9886d7f5d3SJohn Marino 	uint8_t		 buffer[PROTOCOL_DESCRIPTOR_LIST_BUFFER_SIZE];
9986d7f5d3SJohn Marino 	void		*ss    = NULL;
10086d7f5d3SJohn Marino 	uint16_t	 serv  = (uint16_t) service;
10186d7f5d3SJohn Marino 	uint32_t	 attr  = SDP_ATTR_RANGE(
10286d7f5d3SJohn Marino 					SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
10386d7f5d3SJohn Marino 					SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
10486d7f5d3SJohn Marino 	sdp_attr_t	 proto = { SDP_ATTR_INVALID,0,sizeof(buffer),buffer };
10586d7f5d3SJohn Marino 	uint32_t	 type, len;
10686d7f5d3SJohn Marino 
10786d7f5d3SJohn Marino 	if (local == NULL)
10886d7f5d3SJohn Marino 		local = BDADDR_ANY;
10986d7f5d3SJohn Marino 	if (remote == NULL || channel == NULL)
11086d7f5d3SJohn Marino 		rfcomm_channel_lookup_exit(EINVAL);
11186d7f5d3SJohn Marino 
11286d7f5d3SJohn Marino 	if ((ss = sdp_open(local, remote)) == NULL)
11386d7f5d3SJohn Marino 		rfcomm_channel_lookup_exit(ENOMEM);
11486d7f5d3SJohn Marino 	if (sdp_error(ss) != 0)
11586d7f5d3SJohn Marino 		rfcomm_channel_lookup_exit(sdp_error(ss));
11686d7f5d3SJohn Marino 
11786d7f5d3SJohn Marino 	if (sdp_search(ss, 1, &serv, 1, &attr, 1, &proto) != 0)
11886d7f5d3SJohn Marino 		rfcomm_channel_lookup_exit(sdp_error(ss));
11986d7f5d3SJohn Marino 	if (proto.flags != SDP_ATTR_OK)
12086d7f5d3SJohn Marino 		rfcomm_channel_lookup_exit(ENOATTR);
12186d7f5d3SJohn Marino 
12286d7f5d3SJohn Marino 	sdp_close(ss);
12386d7f5d3SJohn Marino 	ss = NULL;
12486d7f5d3SJohn Marino 
12586d7f5d3SJohn Marino 	/*
12686d7f5d3SJohn Marino 	 * If it is possible for more than one kind of protocol stack to be
12786d7f5d3SJohn Marino 	 * used to gain access to the service, the ProtocolDescriptorList
12886d7f5d3SJohn Marino 	 * takes the form of a data element alternative. We always use the
12986d7f5d3SJohn Marino 	 * first protocol stack.
13086d7f5d3SJohn Marino 	 *
13186d7f5d3SJohn Marino 	 * A minimal Protocol Descriptor List for RFCOMM based service would
13286d7f5d3SJohn Marino 	 * look like
13386d7f5d3SJohn Marino 	 *
13486d7f5d3SJohn Marino 	 * seq8 len8			- 2 bytes
13586d7f5d3SJohn Marino 	 *	seq8 len8		- 2 bytes
13686d7f5d3SJohn Marino 	 *		uuid16 value16	- 3 bytes	L2CAP
13786d7f5d3SJohn Marino 	 *	seq8 len8		- 2 bytes
13886d7f5d3SJohn Marino 	 *		uuid16 value16	- 3 bytes	RFCOMM
13986d7f5d3SJohn Marino 	 *		uint8  value8	- 2 bytes	RFCOMM param #1
14086d7f5d3SJohn Marino 	 *				=========
14186d7f5d3SJohn Marino 	 *				 14 bytes
14286d7f5d3SJohn Marino 	 *
14386d7f5d3SJohn Marino 	 * Lets not count first [seq8 len8] wrapper, so the minimal size of
14486d7f5d3SJohn Marino 	 * the Protocol Descriptor List (the data we are actually interested
14586d7f5d3SJohn Marino 	 * in) for RFCOMM based service would be 12 bytes.
14686d7f5d3SJohn Marino 	 */
14786d7f5d3SJohn Marino 
14886d7f5d3SJohn Marino 	if (proto.vlen < PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE)
14986d7f5d3SJohn Marino 		rfcomm_channel_lookup_exit(EINVAL);
15086d7f5d3SJohn Marino 
15186d7f5d3SJohn Marino 	SDP_GET8(type, proto.value);
15286d7f5d3SJohn Marino 
15386d7f5d3SJohn Marino 	if (type == SDP_DATA_ALT8) {
15486d7f5d3SJohn Marino 		SDP_GET8(len, proto.value);
15586d7f5d3SJohn Marino 	} else if (type == SDP_DATA_ALT16) {
15686d7f5d3SJohn Marino 		SDP_GET16(len, proto.value);
15786d7f5d3SJohn Marino 	} else if (type == SDP_DATA_ALT32) {
15886d7f5d3SJohn Marino 		SDP_GET32(len, proto.value);
15986d7f5d3SJohn Marino 	} else
16086d7f5d3SJohn Marino 		len = 0;
16186d7f5d3SJohn Marino 
16286d7f5d3SJohn Marino 	if (len > 0)
16386d7f5d3SJohn Marino 		SDP_GET8(type, proto.value);
16486d7f5d3SJohn Marino 
16586d7f5d3SJohn Marino 	switch (type) {
16686d7f5d3SJohn Marino 	case SDP_DATA_SEQ8:
16786d7f5d3SJohn Marino 		SDP_GET8(len, proto.value);
16886d7f5d3SJohn Marino 		break;
16986d7f5d3SJohn Marino 
17086d7f5d3SJohn Marino 	case SDP_DATA_SEQ16:
17186d7f5d3SJohn Marino 		SDP_GET16(len, proto.value);
17286d7f5d3SJohn Marino 		break;
17386d7f5d3SJohn Marino 
17486d7f5d3SJohn Marino 	case SDP_DATA_SEQ32:
17586d7f5d3SJohn Marino 		SDP_GET32(len, proto.value);
17686d7f5d3SJohn Marino 		break;
17786d7f5d3SJohn Marino 
17886d7f5d3SJohn Marino 	default:
17986d7f5d3SJohn Marino 		rfcomm_channel_lookup_exit(ENOATTR);
18086d7f5d3SJohn Marino 		/* NOT REACHED */
18186d7f5d3SJohn Marino 	}
18286d7f5d3SJohn Marino 
18386d7f5d3SJohn Marino 	if (len < PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE)
18486d7f5d3SJohn Marino 		rfcomm_channel_lookup_exit(EINVAL);
18586d7f5d3SJohn Marino 
18686d7f5d3SJohn Marino 	return (rfcomm_proto_list_parse(proto.value,
18786d7f5d3SJohn Marino 					buffer + proto.vlen, channel, error));
18886d7f5d3SJohn Marino }
18986d7f5d3SJohn Marino 
19086d7f5d3SJohn Marino /*
19186d7f5d3SJohn Marino  * Parse protocol descriptor list
19286d7f5d3SJohn Marino  *
19386d7f5d3SJohn Marino  * The ProtocolDescriptorList attribute describes one or more protocol
19486d7f5d3SJohn Marino  * stacks that may be used to gain access to the service described by
19586d7f5d3SJohn Marino  * the service record. If the ProtocolDescriptorList describes a single
19686d7f5d3SJohn Marino  * stack, it takes the form of a data element sequence in which each
19786d7f5d3SJohn Marino  * element of the sequence is a protocol descriptor.
19886d7f5d3SJohn Marino  */
19986d7f5d3SJohn Marino 
20086d7f5d3SJohn Marino #undef	rfcomm_proto_list_parse_exit
20186d7f5d3SJohn Marino #define	rfcomm_proto_list_parse_exit(e) { \
20286d7f5d3SJohn Marino 	if (error != NULL) \
20386d7f5d3SJohn Marino 		*error = (e); \
20486d7f5d3SJohn Marino 	return (((e) == 0)? 0 : -1); \
20586d7f5d3SJohn Marino }
20686d7f5d3SJohn Marino 
20786d7f5d3SJohn Marino static int
rfcomm_proto_list_parse(uint8_t const * start,uint8_t const * end,uint8_t * channel,int * error)20886d7f5d3SJohn Marino rfcomm_proto_list_parse(uint8_t const *start, uint8_t const *end,
20986d7f5d3SJohn Marino 			uint8_t *channel, int *error)
21086d7f5d3SJohn Marino {
21186d7f5d3SJohn Marino 	int	type, len, value;
21286d7f5d3SJohn Marino 
21386d7f5d3SJohn Marino 	while (start < end) {
21486d7f5d3SJohn Marino 
21586d7f5d3SJohn Marino 		/*
21686d7f5d3SJohn Marino 		 * Parse protocol descriptor
21786d7f5d3SJohn Marino 		 *
21886d7f5d3SJohn Marino 		 * A protocol descriptor identifies a communications protocol
21986d7f5d3SJohn Marino 		 * and provides protocol specific parameters. A protocol
22086d7f5d3SJohn Marino 		 * descriptor is represented as a data element sequence. The
22186d7f5d3SJohn Marino 		 * first data element in the sequence must be the UUID that
22286d7f5d3SJohn Marino 		 * identifies the protocol. Additional data elements optionally
22386d7f5d3SJohn Marino 		 * provide protocol specific information, such as the L2CAP
22486d7f5d3SJohn Marino 		 * protocol/service multiplexer (PSM) and the RFCOMM server
22586d7f5d3SJohn Marino 		 * channel number (CN).
22686d7f5d3SJohn Marino 		 */
22786d7f5d3SJohn Marino 
22886d7f5d3SJohn Marino 		/* We must have at least one byte (type) */
22986d7f5d3SJohn Marino 		if (end - start < 1)
23086d7f5d3SJohn Marino 			rfcomm_proto_list_parse_exit(EINVAL)
23186d7f5d3SJohn Marino 
23286d7f5d3SJohn Marino 		SDP_GET8(type, start);
23386d7f5d3SJohn Marino 		switch (type) {
23486d7f5d3SJohn Marino 		case SDP_DATA_SEQ8:
23586d7f5d3SJohn Marino 			SDP_GET8(len, start);
23686d7f5d3SJohn Marino 			break;
23786d7f5d3SJohn Marino 
23886d7f5d3SJohn Marino 		case SDP_DATA_SEQ16:
23986d7f5d3SJohn Marino 			SDP_GET16(len, start);
24086d7f5d3SJohn Marino 			break;
24186d7f5d3SJohn Marino 
24286d7f5d3SJohn Marino 		case SDP_DATA_SEQ32:
24386d7f5d3SJohn Marino 			SDP_GET32(len, start);
24486d7f5d3SJohn Marino 			break;
24586d7f5d3SJohn Marino 
24686d7f5d3SJohn Marino 		default:
24786d7f5d3SJohn Marino 			rfcomm_proto_list_parse_exit(ENOATTR)
24886d7f5d3SJohn Marino 			/* NOT REACHED */
24986d7f5d3SJohn Marino 		}
25086d7f5d3SJohn Marino 
25186d7f5d3SJohn Marino 		/* We must have at least 3 bytes (type + UUID16) */
25286d7f5d3SJohn Marino 		if (end - start < 3)
25386d7f5d3SJohn Marino 			rfcomm_proto_list_parse_exit(EINVAL);
25486d7f5d3SJohn Marino 
25586d7f5d3SJohn Marino 		/* Get protocol UUID */
25686d7f5d3SJohn Marino 		SDP_GET8(type, start); len -= sizeof(uint8_t);
25786d7f5d3SJohn Marino 		switch (type) {
25886d7f5d3SJohn Marino 		case SDP_DATA_UUID16:
25986d7f5d3SJohn Marino 			SDP_GET16(value, start); len -= sizeof(uint16_t);
26086d7f5d3SJohn Marino 			if (value != SDP_UUID_PROTOCOL_RFCOMM)
26186d7f5d3SJohn Marino 				goto next_protocol;
26286d7f5d3SJohn Marino 			break;
26386d7f5d3SJohn Marino 
26486d7f5d3SJohn Marino 		case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
26586d7f5d3SJohn Marino 		case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
26686d7f5d3SJohn Marino 		default:
26786d7f5d3SJohn Marino 			rfcomm_proto_list_parse_exit(ENOATTR);
26886d7f5d3SJohn Marino 			/* NOT REACHED */
26986d7f5d3SJohn Marino 		}
27086d7f5d3SJohn Marino 
27186d7f5d3SJohn Marino 		/*
27286d7f5d3SJohn Marino 		 * First protocol specific parameter for RFCOMM procotol must
27386d7f5d3SJohn Marino 		 * be uint8 that represents RFCOMM channel number. So we must
27486d7f5d3SJohn Marino 		 * have at least two bytes.
27586d7f5d3SJohn Marino 		 */
27686d7f5d3SJohn Marino 
27786d7f5d3SJohn Marino 		if (end - start < 2)
27886d7f5d3SJohn Marino 			rfcomm_proto_list_parse_exit(EINVAL);
27986d7f5d3SJohn Marino 
28086d7f5d3SJohn Marino 		SDP_GET8(type, start);
28186d7f5d3SJohn Marino 		if (type != SDP_DATA_UINT8)
28286d7f5d3SJohn Marino 			rfcomm_proto_list_parse_exit(ENOATTR);
28386d7f5d3SJohn Marino 
28486d7f5d3SJohn Marino 		SDP_GET8(*channel, start);
28586d7f5d3SJohn Marino 
28686d7f5d3SJohn Marino 		rfcomm_proto_list_parse_exit(0);
28786d7f5d3SJohn Marino 		/* NOT REACHED */
28886d7f5d3SJohn Marino next_protocol:
28986d7f5d3SJohn Marino 		start += len;
29086d7f5d3SJohn Marino 	}
29186d7f5d3SJohn Marino 
29286d7f5d3SJohn Marino 	/*
29386d7f5d3SJohn Marino 	 * If we got here then it means we could not find RFCOMM protocol
29486d7f5d3SJohn Marino 	 * descriptor, but the reply format was actually valid.
29586d7f5d3SJohn Marino 	 */
29686d7f5d3SJohn Marino 
29786d7f5d3SJohn Marino 	rfcomm_proto_list_parse_exit(ENOATTR);
29886d7f5d3SJohn Marino }
299