1*86d7f5d3SJohn Marino /* $NetBSD: rfcomm_sdp.c,v 1.1 2006/06/19 15:44:56 gdamore Exp $ */
2*86d7f5d3SJohn Marino /* $DragonFly: src/usr.bin/rfcomm_sppd/rfcomm_sdp.c,v 1.1 2008/02/08 14:06:25 hasso Exp $ */
3*86d7f5d3SJohn Marino 
4*86d7f5d3SJohn Marino /*-
5*86d7f5d3SJohn Marino  * Copyright (c) 2006 Itronix Inc.
6*86d7f5d3SJohn Marino  * All rights reserved.
7*86d7f5d3SJohn Marino  *
8*86d7f5d3SJohn Marino  * Redistribution and use in source and binary forms, with or without
9*86d7f5d3SJohn Marino  * modification, are permitted provided that the following conditions
10*86d7f5d3SJohn Marino  * are met:
11*86d7f5d3SJohn Marino  * 1. Redistributions of source code must retain the above copyright
12*86d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer.
13*86d7f5d3SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
14*86d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
15*86d7f5d3SJohn Marino  *    documentation and/or other materials provided with the distribution.
16*86d7f5d3SJohn Marino  * 3. The name of Itronix Inc. may not be used to endorse
17*86d7f5d3SJohn Marino  *    or promote products derived from this software without specific
18*86d7f5d3SJohn Marino  *    prior written permission.
19*86d7f5d3SJohn Marino  *
20*86d7f5d3SJohn Marino  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
21*86d7f5d3SJohn Marino  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22*86d7f5d3SJohn Marino  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23*86d7f5d3SJohn Marino  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
24*86d7f5d3SJohn Marino  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25*86d7f5d3SJohn Marino  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26*86d7f5d3SJohn Marino  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27*86d7f5d3SJohn Marino  * ON ANY THEORY OF LIABILITY, WHETHER IN
28*86d7f5d3SJohn Marino  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29*86d7f5d3SJohn Marino  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30*86d7f5d3SJohn Marino  * POSSIBILITY OF SUCH DAMAGE.
31*86d7f5d3SJohn Marino  */
32*86d7f5d3SJohn Marino /*
33*86d7f5d3SJohn Marino  * rfcomm_sdp.c
34*86d7f5d3SJohn Marino  *
35*86d7f5d3SJohn Marino  * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
36*86d7f5d3SJohn Marino  * All rights reserved.
37*86d7f5d3SJohn Marino  *
38*86d7f5d3SJohn Marino  * Redistribution and use in source and binary forms, with or without
39*86d7f5d3SJohn Marino  * modification, are permitted provided that the following conditions
40*86d7f5d3SJohn Marino  * are met:
41*86d7f5d3SJohn Marino  * 1. Redistributions of source code must retain the above copyright
42*86d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer.
43*86d7f5d3SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
44*86d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
45*86d7f5d3SJohn Marino  *    documentation and/or other materials provided with the distribution.
46*86d7f5d3SJohn Marino  *
47*86d7f5d3SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
48*86d7f5d3SJohn Marino  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49*86d7f5d3SJohn Marino  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50*86d7f5d3SJohn Marino  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
51*86d7f5d3SJohn Marino  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52*86d7f5d3SJohn Marino  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53*86d7f5d3SJohn Marino  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54*86d7f5d3SJohn Marino  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55*86d7f5d3SJohn Marino  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56*86d7f5d3SJohn Marino  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57*86d7f5d3SJohn Marino  * SUCH DAMAGE.
58*86d7f5d3SJohn Marino  *
59*86d7f5d3SJohn Marino  * $Id: rfcomm_sdp.c,v 1.1 2006/06/19 15:44:56 gdamore Exp $
60*86d7f5d3SJohn Marino  * $FreeBSD: src/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sdp.c,v 1.2 2004/04/09 23:26:16 emax Exp $
61*86d7f5d3SJohn Marino  */
62*86d7f5d3SJohn Marino 
63*86d7f5d3SJohn Marino #include <bluetooth.h>
64*86d7f5d3SJohn Marino #include <errno.h>
65*86d7f5d3SJohn Marino #include <sdp.h>
66*86d7f5d3SJohn Marino #include <stdio.h>
67*86d7f5d3SJohn Marino 
68*86d7f5d3SJohn Marino #include "rfcomm_sdp.h"
69*86d7f5d3SJohn Marino 
70*86d7f5d3SJohn Marino #undef	PROTOCOL_DESCRIPTOR_LIST_BUFFER_SIZE
71*86d7f5d3SJohn Marino #define	PROTOCOL_DESCRIPTOR_LIST_BUFFER_SIZE	256
72*86d7f5d3SJohn Marino 
73*86d7f5d3SJohn Marino #undef	PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE
74*86d7f5d3SJohn Marino #define	PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE	12
75*86d7f5d3SJohn Marino 
76*86d7f5d3SJohn Marino static int rfcomm_proto_list_parse (uint8_t const *start, uint8_t const *end,
77*86d7f5d3SJohn Marino 					uint8_t *channel, int *error);
78*86d7f5d3SJohn Marino 
79*86d7f5d3SJohn Marino /*
80*86d7f5d3SJohn Marino  * Lookup RFCOMM channel number in the Protocol Descriptor List
81*86d7f5d3SJohn Marino  */
82*86d7f5d3SJohn Marino 
83*86d7f5d3SJohn Marino #undef	rfcomm_channel_lookup_exit
84*86d7f5d3SJohn Marino #define	rfcomm_channel_lookup_exit(e) { \
85*86d7f5d3SJohn Marino 	if (error != NULL) \
86*86d7f5d3SJohn Marino 		*error = (e); \
87*86d7f5d3SJohn Marino 	if (ss != NULL) { \
88*86d7f5d3SJohn Marino 		sdp_close(ss); \
89*86d7f5d3SJohn Marino 		ss = NULL; \
90*86d7f5d3SJohn Marino 	} \
91*86d7f5d3SJohn Marino 	return (((e) == 0)? 0 : -1); \
92*86d7f5d3SJohn Marino }
93*86d7f5d3SJohn Marino 
94*86d7f5d3SJohn Marino int
rfcomm_channel_lookup(bdaddr_t const * local,bdaddr_t const * remote,int service,uint8_t * channel,int * error)95*86d7f5d3SJohn Marino rfcomm_channel_lookup(bdaddr_t const *local, bdaddr_t const *remote,
96*86d7f5d3SJohn Marino 			int service, uint8_t *channel, int *error)
97*86d7f5d3SJohn Marino {
98*86d7f5d3SJohn Marino 	uint8_t		 buffer[PROTOCOL_DESCRIPTOR_LIST_BUFFER_SIZE];
99*86d7f5d3SJohn Marino 	void		*ss    = NULL;
100*86d7f5d3SJohn Marino 	uint16_t	 serv  = (uint16_t) service;
101*86d7f5d3SJohn Marino 	uint32_t	 attr  = SDP_ATTR_RANGE(
102*86d7f5d3SJohn Marino 					SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
103*86d7f5d3SJohn Marino 					SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
104*86d7f5d3SJohn Marino 	sdp_attr_t	 proto = { SDP_ATTR_INVALID,0,sizeof(buffer),buffer };
105*86d7f5d3SJohn Marino 	uint32_t	 type, len;
106*86d7f5d3SJohn Marino 
107*86d7f5d3SJohn Marino 	if (local == NULL)
108*86d7f5d3SJohn Marino 		local = BDADDR_ANY;
109*86d7f5d3SJohn Marino 	if (remote == NULL || channel == NULL)
110*86d7f5d3SJohn Marino 		rfcomm_channel_lookup_exit(EINVAL);
111*86d7f5d3SJohn Marino 
112*86d7f5d3SJohn Marino 	if ((ss = sdp_open(local, remote)) == NULL)
113*86d7f5d3SJohn Marino 		rfcomm_channel_lookup_exit(ENOMEM);
114*86d7f5d3SJohn Marino 	if (sdp_error(ss) != 0)
115*86d7f5d3SJohn Marino 		rfcomm_channel_lookup_exit(sdp_error(ss));
116*86d7f5d3SJohn Marino 
117*86d7f5d3SJohn Marino 	if (sdp_search(ss, 1, &serv, 1, &attr, 1, &proto) != 0)
118*86d7f5d3SJohn Marino 		rfcomm_channel_lookup_exit(sdp_error(ss));
119*86d7f5d3SJohn Marino 	if (proto.flags != SDP_ATTR_OK)
120*86d7f5d3SJohn Marino 		rfcomm_channel_lookup_exit(ENOATTR);
121*86d7f5d3SJohn Marino 
122*86d7f5d3SJohn Marino 	sdp_close(ss);
123*86d7f5d3SJohn Marino 	ss = NULL;
124*86d7f5d3SJohn Marino 
125*86d7f5d3SJohn Marino 	/*
126*86d7f5d3SJohn Marino 	 * If it is possible for more than one kind of protocol stack to be
127*86d7f5d3SJohn Marino 	 * used to gain access to the service, the ProtocolDescriptorList
128*86d7f5d3SJohn Marino 	 * takes the form of a data element alternative. We always use the
129*86d7f5d3SJohn Marino 	 * first protocol stack.
130*86d7f5d3SJohn Marino 	 *
131*86d7f5d3SJohn Marino 	 * A minimal Protocol Descriptor List for RFCOMM based service would
132*86d7f5d3SJohn Marino 	 * look like
133*86d7f5d3SJohn Marino 	 *
134*86d7f5d3SJohn Marino 	 * seq8 len8			- 2 bytes
135*86d7f5d3SJohn Marino 	 *	seq8 len8		- 2 bytes
136*86d7f5d3SJohn Marino 	 *		uuid16 value16	- 3 bytes	L2CAP
137*86d7f5d3SJohn Marino 	 *	seq8 len8		- 2 bytes
138*86d7f5d3SJohn Marino 	 *		uuid16 value16	- 3 bytes	RFCOMM
139*86d7f5d3SJohn Marino 	 *		uint8  value8	- 2 bytes	RFCOMM param #1
140*86d7f5d3SJohn Marino 	 *				=========
141*86d7f5d3SJohn Marino 	 *				 14 bytes
142*86d7f5d3SJohn Marino 	 *
143*86d7f5d3SJohn Marino 	 * Lets not count first [seq8 len8] wrapper, so the minimal size of
144*86d7f5d3SJohn Marino 	 * the Protocol Descriptor List (the data we are actually interested
145*86d7f5d3SJohn Marino 	 * in) for RFCOMM based service would be 12 bytes.
146*86d7f5d3SJohn Marino 	 */
147*86d7f5d3SJohn Marino 
148*86d7f5d3SJohn Marino 	if (proto.vlen < PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE)
149*86d7f5d3SJohn Marino 		rfcomm_channel_lookup_exit(EINVAL);
150*86d7f5d3SJohn Marino 
151*86d7f5d3SJohn Marino 	SDP_GET8(type, proto.value);
152*86d7f5d3SJohn Marino 
153*86d7f5d3SJohn Marino 	if (type == SDP_DATA_ALT8) {
154*86d7f5d3SJohn Marino 		SDP_GET8(len, proto.value);
155*86d7f5d3SJohn Marino 	} else if (type == SDP_DATA_ALT16) {
156*86d7f5d3SJohn Marino 		SDP_GET16(len, proto.value);
157*86d7f5d3SJohn Marino 	} else if (type == SDP_DATA_ALT32) {
158*86d7f5d3SJohn Marino 		SDP_GET32(len, proto.value);
159*86d7f5d3SJohn Marino 	} else
160*86d7f5d3SJohn Marino 		len = 0;
161*86d7f5d3SJohn Marino 
162*86d7f5d3SJohn Marino 	if (len > 0)
163*86d7f5d3SJohn Marino 		SDP_GET8(type, proto.value);
164*86d7f5d3SJohn Marino 
165*86d7f5d3SJohn Marino 	switch (type) {
166*86d7f5d3SJohn Marino 	case SDP_DATA_SEQ8:
167*86d7f5d3SJohn Marino 		SDP_GET8(len, proto.value);
168*86d7f5d3SJohn Marino 		break;
169*86d7f5d3SJohn Marino 
170*86d7f5d3SJohn Marino 	case SDP_DATA_SEQ16:
171*86d7f5d3SJohn Marino 		SDP_GET16(len, proto.value);
172*86d7f5d3SJohn Marino 		break;
173*86d7f5d3SJohn Marino 
174*86d7f5d3SJohn Marino 	case SDP_DATA_SEQ32:
175*86d7f5d3SJohn Marino 		SDP_GET32(len, proto.value);
176*86d7f5d3SJohn Marino 		break;
177*86d7f5d3SJohn Marino 
178*86d7f5d3SJohn Marino 	default:
179*86d7f5d3SJohn Marino 		rfcomm_channel_lookup_exit(ENOATTR);
180*86d7f5d3SJohn Marino 		/* NOT REACHED */
181*86d7f5d3SJohn Marino 	}
182*86d7f5d3SJohn Marino 
183*86d7f5d3SJohn Marino 	if (len < PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE)
184*86d7f5d3SJohn Marino 		rfcomm_channel_lookup_exit(EINVAL);
185*86d7f5d3SJohn Marino 
186*86d7f5d3SJohn Marino 	return (rfcomm_proto_list_parse(proto.value,
187*86d7f5d3SJohn Marino 					buffer + proto.vlen, channel, error));
188*86d7f5d3SJohn Marino }
189*86d7f5d3SJohn Marino 
190*86d7f5d3SJohn Marino /*
191*86d7f5d3SJohn Marino  * Parse protocol descriptor list
192*86d7f5d3SJohn Marino  *
193*86d7f5d3SJohn Marino  * The ProtocolDescriptorList attribute describes one or more protocol
194*86d7f5d3SJohn Marino  * stacks that may be used to gain access to the service described by
195*86d7f5d3SJohn Marino  * the service record. If the ProtocolDescriptorList describes a single
196*86d7f5d3SJohn Marino  * stack, it takes the form of a data element sequence in which each
197*86d7f5d3SJohn Marino  * element of the sequence is a protocol descriptor.
198*86d7f5d3SJohn Marino  */
199*86d7f5d3SJohn Marino 
200*86d7f5d3SJohn Marino #undef	rfcomm_proto_list_parse_exit
201*86d7f5d3SJohn Marino #define	rfcomm_proto_list_parse_exit(e) { \
202*86d7f5d3SJohn Marino 	if (error != NULL) \
203*86d7f5d3SJohn Marino 		*error = (e); \
204*86d7f5d3SJohn Marino 	return (((e) == 0)? 0 : -1); \
205*86d7f5d3SJohn Marino }
206*86d7f5d3SJohn Marino 
207*86d7f5d3SJohn Marino static int
rfcomm_proto_list_parse(uint8_t const * start,uint8_t const * end,uint8_t * channel,int * error)208*86d7f5d3SJohn Marino rfcomm_proto_list_parse(uint8_t const *start, uint8_t const *end,
209*86d7f5d3SJohn Marino 			uint8_t *channel, int *error)
210*86d7f5d3SJohn Marino {
211*86d7f5d3SJohn Marino 	int	type, len, value;
212*86d7f5d3SJohn Marino 
213*86d7f5d3SJohn Marino 	while (start < end) {
214*86d7f5d3SJohn Marino 
215*86d7f5d3SJohn Marino 		/*
216*86d7f5d3SJohn Marino 		 * Parse protocol descriptor
217*86d7f5d3SJohn Marino 		 *
218*86d7f5d3SJohn Marino 		 * A protocol descriptor identifies a communications protocol
219*86d7f5d3SJohn Marino 		 * and provides protocol specific parameters. A protocol
220*86d7f5d3SJohn Marino 		 * descriptor is represented as a data element sequence. The
221*86d7f5d3SJohn Marino 		 * first data element in the sequence must be the UUID that
222*86d7f5d3SJohn Marino 		 * identifies the protocol. Additional data elements optionally
223*86d7f5d3SJohn Marino 		 * provide protocol specific information, such as the L2CAP
224*86d7f5d3SJohn Marino 		 * protocol/service multiplexer (PSM) and the RFCOMM server
225*86d7f5d3SJohn Marino 		 * channel number (CN).
226*86d7f5d3SJohn Marino 		 */
227*86d7f5d3SJohn Marino 
228*86d7f5d3SJohn Marino 		/* We must have at least one byte (type) */
229*86d7f5d3SJohn Marino 		if (end - start < 1)
230*86d7f5d3SJohn Marino 			rfcomm_proto_list_parse_exit(EINVAL)
231*86d7f5d3SJohn Marino 
232*86d7f5d3SJohn Marino 		SDP_GET8(type, start);
233*86d7f5d3SJohn Marino 		switch (type) {
234*86d7f5d3SJohn Marino 		case SDP_DATA_SEQ8:
235*86d7f5d3SJohn Marino 			SDP_GET8(len, start);
236*86d7f5d3SJohn Marino 			break;
237*86d7f5d3SJohn Marino 
238*86d7f5d3SJohn Marino 		case SDP_DATA_SEQ16:
239*86d7f5d3SJohn Marino 			SDP_GET16(len, start);
240*86d7f5d3SJohn Marino 			break;
241*86d7f5d3SJohn Marino 
242*86d7f5d3SJohn Marino 		case SDP_DATA_SEQ32:
243*86d7f5d3SJohn Marino 			SDP_GET32(len, start);
244*86d7f5d3SJohn Marino 			break;
245*86d7f5d3SJohn Marino 
246*86d7f5d3SJohn Marino 		default:
247*86d7f5d3SJohn Marino 			rfcomm_proto_list_parse_exit(ENOATTR)
248*86d7f5d3SJohn Marino 			/* NOT REACHED */
249*86d7f5d3SJohn Marino 		}
250*86d7f5d3SJohn Marino 
251*86d7f5d3SJohn Marino 		/* We must have at least 3 bytes (type + UUID16) */
252*86d7f5d3SJohn Marino 		if (end - start < 3)
253*86d7f5d3SJohn Marino 			rfcomm_proto_list_parse_exit(EINVAL);
254*86d7f5d3SJohn Marino 
255*86d7f5d3SJohn Marino 		/* Get protocol UUID */
256*86d7f5d3SJohn Marino 		SDP_GET8(type, start); len -= sizeof(uint8_t);
257*86d7f5d3SJohn Marino 		switch (type) {
258*86d7f5d3SJohn Marino 		case SDP_DATA_UUID16:
259*86d7f5d3SJohn Marino 			SDP_GET16(value, start); len -= sizeof(uint16_t);
260*86d7f5d3SJohn Marino 			if (value != SDP_UUID_PROTOCOL_RFCOMM)
261*86d7f5d3SJohn Marino 				goto next_protocol;
262*86d7f5d3SJohn Marino 			break;
263*86d7f5d3SJohn Marino 
264*86d7f5d3SJohn Marino 		case SDP_DATA_UUID32:  /* XXX FIXME can we have 32-bit UUID */
265*86d7f5d3SJohn Marino 		case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
266*86d7f5d3SJohn Marino 		default:
267*86d7f5d3SJohn Marino 			rfcomm_proto_list_parse_exit(ENOATTR);
268*86d7f5d3SJohn Marino 			/* NOT REACHED */
269*86d7f5d3SJohn Marino 		}
270*86d7f5d3SJohn Marino 
271*86d7f5d3SJohn Marino 		/*
272*86d7f5d3SJohn Marino 		 * First protocol specific parameter for RFCOMM procotol must
273*86d7f5d3SJohn Marino 		 * be uint8 that represents RFCOMM channel number. So we must
274*86d7f5d3SJohn Marino 		 * have at least two bytes.
275*86d7f5d3SJohn Marino 		 */
276*86d7f5d3SJohn Marino 
277*86d7f5d3SJohn Marino 		if (end - start < 2)
278*86d7f5d3SJohn Marino 			rfcomm_proto_list_parse_exit(EINVAL);
279*86d7f5d3SJohn Marino 
280*86d7f5d3SJohn Marino 		SDP_GET8(type, start);
281*86d7f5d3SJohn Marino 		if (type != SDP_DATA_UINT8)
282*86d7f5d3SJohn Marino 			rfcomm_proto_list_parse_exit(ENOATTR);
283*86d7f5d3SJohn Marino 
284*86d7f5d3SJohn Marino 		SDP_GET8(*channel, start);
285*86d7f5d3SJohn Marino 
286*86d7f5d3SJohn Marino 		rfcomm_proto_list_parse_exit(0);
287*86d7f5d3SJohn Marino 		/* NOT REACHED */
288*86d7f5d3SJohn Marino next_protocol:
289*86d7f5d3SJohn Marino 		start += len;
290*86d7f5d3SJohn Marino 	}
291*86d7f5d3SJohn Marino 
292*86d7f5d3SJohn Marino 	/*
293*86d7f5d3SJohn Marino 	 * If we got here then it means we could not find RFCOMM protocol
294*86d7f5d3SJohn Marino 	 * descriptor, but the reply format was actually valid.
295*86d7f5d3SJohn Marino 	 */
296*86d7f5d3SJohn Marino 
297*86d7f5d3SJohn Marino 	rfcomm_proto_list_parse_exit(ENOATTR);
298*86d7f5d3SJohn Marino }
299