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