xref: /freebsd/usr.sbin/bluetooth/sdpd/sar.c (revision f81cdf24ba5436367377f7c8e8f51f6df2a75ca7)
1 /*-
2  * sar.c
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  *
6  * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $Id: sar.c,v 1.2 2004/01/08 23:46:51 max Exp $
31  */
32 
33 #include <sys/queue.h>
34 #include <sys/uio.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 #include <assert.h>
38 #define L2CAP_SOCKET_CHECKED
39 #include <bluetooth.h>
40 #include <errno.h>
41 #include <sdp.h>
42 #include <stdio.h> /* for NULL */
43 #include "profile.h"
44 #include "provider.h"
45 #include "server.h"
46 
47 /*
48  * Prepare SDP attr/value pair. Check if profile implements the attribute
49  * and if so call the attribute value function.
50  *
51  * uint16 value16	- 3 bytes (attribute)
52  * value		- N bytes (value)
53  */
54 
55 static int32_t
56 server_prepare_attr_value_pair(
57 		provider_p const provider, uint16_t attr,
58 		uint8_t *buf, uint8_t const * const eob)
59 {
60 	profile_attr_create_p	cf = profile_get_attr(provider->profile, attr);
61 	int32_t			len;
62 
63 	if (cf == NULL)
64 		return (0); /* no attribute */
65 
66 	if (buf + 3 > eob)
67 		return (-1);
68 
69 	SDP_PUT8(SDP_DATA_UINT16, buf);
70 	SDP_PUT16(attr, buf);
71 
72 	len = cf(buf, eob, (uint8_t const *) provider, sizeof(*provider));
73 	if (len < 0)
74 		return (-1);
75 
76 	return (3 + len);
77 }
78 
79 /*
80  * seq16 value16	- 3 bytes
81  *	attr value	- 3+ bytes
82  *	[ attr value ]
83  */
84 
85 int32_t
86 server_prepare_attr_list(provider_p const provider,
87 		uint8_t const *req, uint8_t const * const req_end,
88 		uint8_t *rsp, uint8_t const * const rsp_end)
89 {
90 	uint8_t	*ptr = rsp + 3;
91 	int32_t	 type, hi, lo, len;
92 
93 	if (ptr > rsp_end)
94 		return (-1);
95 
96 	while (req < req_end) {
97 		SDP_GET8(type, req);
98 
99 		switch (type) {
100 		case SDP_DATA_UINT16:
101 			if (req + 2 > req_end)
102 				return (-1);
103 
104 			SDP_GET16(lo, req);
105 			hi = lo;
106 			break;
107 
108 		case SDP_DATA_UINT32:
109 			if (req + 4 > req_end)
110 				return (-1);
111 
112 			SDP_GET16(lo, req);
113 			SDP_GET16(hi, req);
114 			break;
115 
116 		default:
117 			return (-1);
118 			/* NOT REACHED */
119 		}
120 
121 		for (; lo <= hi; lo ++) {
122 			len = server_prepare_attr_value_pair(provider, lo, ptr, rsp_end);
123 			if (len < 0)
124 				return (-1);
125 
126 			ptr += len;
127 		}
128 	}
129 
130 	len = ptr - rsp; /* we put this much bytes in rsp */
131 
132 	/* Fix SEQ16 header for the rsp */
133 	SDP_PUT8(SDP_DATA_SEQ16, rsp);
134 	SDP_PUT16(len - 3, rsp);
135 
136 	return (len);
137 }
138 
139 /*
140  * Prepare SDP Service Attribute Response
141  */
142 
143 int32_t
144 server_prepare_service_attribute_response(server_p srv, int32_t fd)
145 {
146 	uint8_t const	*req = srv->req + sizeof(sdp_pdu_t);
147 	uint8_t const	*req_end = req + ((sdp_pdu_p)(srv->req))->len;
148 	uint8_t		*rsp = srv->fdidx[fd].rsp;
149 	uint8_t const	*rsp_end = rsp + NG_L2CAP_MTU_MAXIMUM;
150 
151 	uint8_t		*ptr = NULL;
152 	provider_t	*provider = NULL;
153 	uint32_t	 handle;
154 	int32_t		 type, rsp_limit, aidlen, cslen, cs;
155 
156 	/*
157 	 * Minimal Service Attribute Request request
158 	 *
159 	 * value32		- 4 bytes ServiceRecordHandle
160 	 * value16		- 2 bytes MaximumAttributeByteCount
161 	 * seq8 len8		- 2 bytes
162 	 *	uint16 value16	- 3 bytes AttributeIDList
163 	 * value8		- 1 byte  ContinuationState
164 	 */
165 
166 	if (req_end - req < 12)
167 		return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
168 
169 	/* Get ServiceRecordHandle and MaximumAttributeByteCount */
170 	SDP_GET32(handle, req);
171 	SDP_GET16(rsp_limit, req);
172 	if (rsp_limit <= 0)
173 		return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
174 
175 	/* Get size of AttributeIDList */
176 	aidlen = 0;
177 	SDP_GET8(type, req);
178 	switch (type) {
179 	case SDP_DATA_SEQ8:
180 		SDP_GET8(aidlen, req);
181 		break;
182 
183 	case SDP_DATA_SEQ16:
184 		SDP_GET16(aidlen, req);
185 		break;
186 
187 	case SDP_DATA_SEQ32:
188 		SDP_GET32(aidlen, req);
189  		break;
190 	}
191 	if (aidlen <= 0)
192 		return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
193 
194 	ptr = (uint8_t *) req + aidlen;
195 
196 	/* Get ContinuationState */
197 	if (ptr + 1 > req_end)
198 		return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
199 
200 	SDP_GET8(cslen, ptr);
201 	if (cslen != 0) {
202 		if (cslen != 2 || req_end - ptr != 2)
203 			return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
204 
205 		SDP_GET16(cs, ptr);
206 	} else
207 		cs = 0;
208 
209 	/* Process the request. First, check continuation state */
210 	if (srv->fdidx[fd].rsp_cs != cs)
211 		return (SDP_ERROR_CODE_INVALID_CONTINUATION_STATE);
212 	if (srv->fdidx[fd].rsp_size > 0)
213 		return (0);
214 
215 	/* Lookup record handle */
216 	if ((provider = provider_by_handle(handle)) == NULL)
217 		return (SDP_ERROR_CODE_INVALID_SERVICE_RECORD_HANDLE);
218 
219 	/*
220 	 * Service Attribute Response format
221 	 *
222 	 * value16		- 2 bytes  AttributeListByteCount (not incl.)
223 	 * seq8 len16		- 3 bytes
224 	 *	attr value	- 3+ bytes AttributeList
225 	 *	[ attr value ]
226 	 */
227 
228 	cs = server_prepare_attr_list(provider, req, req+aidlen, rsp, rsp_end);
229 	if (cs < 0)
230 		return (SDP_ERROR_CODE_INSUFFICIENT_RESOURCES);
231 
232 	/* Set reply size (not counting PDU header and continuation state) */
233 	srv->fdidx[fd].rsp_limit = srv->fdidx[fd].omtu - sizeof(sdp_pdu_t) - 2;
234 	if (srv->fdidx[fd].rsp_limit > rsp_limit)
235 		srv->fdidx[fd].rsp_limit = rsp_limit;
236 
237 	srv->fdidx[fd].rsp_size = cs;
238 	srv->fdidx[fd].rsp_cs = 0;
239 
240 	return (0);
241 }
242 
243 /*
244  * Send SDP Service [Search] Attribute Response
245  */
246 
247 int32_t
248 server_send_service_attribute_response(server_p srv, int32_t fd)
249 {
250 	uint8_t		*rsp = srv->fdidx[fd].rsp + srv->fdidx[fd].rsp_cs;
251 	uint8_t		*rsp_end = srv->fdidx[fd].rsp + srv->fdidx[fd].rsp_size;
252 
253 	struct iovec	iov[4];
254 	sdp_pdu_t	pdu;
255 	uint16_t	bcount;
256 	uint8_t		cs[3];
257 	int32_t		size;
258 
259 	/* First update continuation state  (assume we will send all data) */
260 	size = rsp_end - rsp;
261 	srv->fdidx[fd].rsp_cs += size;
262 
263 	if (size + 1 > srv->fdidx[fd].rsp_limit) {
264 		/*
265 		 * We need to split out response. Add 3 more bytes for the
266 		 * continuation state and move rsp_end and rsp_cs backwards.
267 		 */
268 
269 		while ((rsp_end - rsp) + 3 > srv->fdidx[fd].rsp_limit) {
270 			rsp_end --;
271 			srv->fdidx[fd].rsp_cs --;
272 		}
273 
274 		cs[0] = 2;
275 		cs[1] = srv->fdidx[fd].rsp_cs >> 8;
276 		cs[2] = srv->fdidx[fd].rsp_cs & 0xff;
277 	} else
278 		cs[0] = 0;
279 
280 	assert(rsp_end >= rsp);
281 
282 	bcount = rsp_end - rsp;
283 
284 	if (((sdp_pdu_p)(srv->req))->pid == SDP_PDU_SERVICE_ATTRIBUTE_REQUEST)
285 		pdu.pid = SDP_PDU_SERVICE_ATTRIBUTE_RESPONSE;
286 	else
287 		pdu.pid = SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_RESPONSE;
288 
289 	pdu.tid = ((sdp_pdu_p)(srv->req))->tid;
290 	pdu.len = htons(sizeof(bcount) + bcount + 1 + cs[0]);
291 
292 	bcount = htons(bcount);
293 
294 	iov[0].iov_base = &pdu;
295 	iov[0].iov_len = sizeof(pdu);
296 
297 	iov[1].iov_base = &bcount;
298 	iov[1].iov_len = sizeof(bcount);
299 
300 	iov[2].iov_base = rsp;
301 	iov[2].iov_len = rsp_end - rsp;
302 
303 	iov[3].iov_base = cs;
304 	iov[3].iov_len = 1 + cs[0];
305 
306 	do {
307 		size = writev(fd, (struct iovec const *) &iov, sizeof(iov)/sizeof(iov[0]));
308 	} while (size < 0 && errno == EINTR);
309 
310 	/* Check if we have sent (or failed to sent) last response chunk */
311 	if (srv->fdidx[fd].rsp_cs == srv->fdidx[fd].rsp_size) {
312 		srv->fdidx[fd].rsp_cs = 0;
313 		srv->fdidx[fd].rsp_size = 0;
314 		srv->fdidx[fd].rsp_limit = 0;
315 	}
316 
317 	return ((size < 0)? errno : 0);
318 }
319 
320