1/*-
2 * ssar.c
3 *
4 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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: ssar.c,v 1.4 2004/01/12 22:54:31 max Exp $
31 * $FreeBSD$
32 */
33
34#include <sys/queue.h>
35#define L2CAP_SOCKET_CHECKED
36#include <bluetooth.h>
37#include <sdp.h>
38#include <string.h>
39#include "profile.h"
40#include "provider.h"
41#include "server.h"
42#include "uuid-private.h"
43
44/* from sar.c */
45int32_t server_prepare_attr_list(provider_p const provider,
46		uint8_t const *req, uint8_t const * const req_end,
47		uint8_t *rsp, uint8_t const * const rsp_end);
48
49/*
50 * Scan an attribute for matching UUID.
51 */
52static int
53server_search_uuid_sub(uint8_t *buf, uint8_t const * const eob, const uint128_t *uuid)
54{
55        int128_t duuid;
56        uint32_t value;
57        uint8_t type;
58
59        while (buf < eob) {
60
61                SDP_GET8(type, buf);
62
63                switch (type) {
64                case SDP_DATA_UUID16:
65                        if (buf + 2 > eob)
66                                continue;
67                        SDP_GET16(value, buf);
68
69                        memcpy(&duuid, &uuid_base, sizeof(duuid));
70                        duuid.b[2] = value >> 8 & 0xff;
71                        duuid.b[3] = value & 0xff;
72
73                        if (memcmp(&duuid, uuid, sizeof(duuid)) == 0)
74                                return (0);
75                        break;
76                case SDP_DATA_UUID32:
77                        if (buf + 4 > eob)
78                                continue;
79                        SDP_GET32(value, buf);
80                        memcpy(&duuid, &uuid_base, sizeof(duuid));
81                        duuid.b[0] = value >> 24 & 0xff;
82                        duuid.b[1] = value >> 16 & 0xff;
83                        duuid.b[2] = value >> 8 & 0xff;
84                        duuid.b[3] = value & 0xff;
85
86                        if (memcmp(&duuid, uuid, sizeof(duuid)) == 0)
87                                return (0);
88                        break;
89                case SDP_DATA_UUID128:
90                        if (buf + 16 > eob)
91                                continue;
92                        SDP_GET_UUID128(&duuid, buf);
93
94                        if (memcmp(&duuid, uuid, sizeof(duuid)) == 0)
95                                return (0);
96                        break;
97                case SDP_DATA_UINT8:
98                case SDP_DATA_INT8:
99                case SDP_DATA_SEQ8:
100                        buf++;
101                        break;
102                case SDP_DATA_UINT16:
103                case SDP_DATA_INT16:
104                case SDP_DATA_SEQ16:
105                        buf += 2;
106                        break;
107                case SDP_DATA_UINT32:
108                case SDP_DATA_INT32:
109                case SDP_DATA_SEQ32:
110                        buf += 4;
111                        break;
112                case SDP_DATA_UINT64:
113                case SDP_DATA_INT64:
114                        buf += 8;
115                        break;
116                case SDP_DATA_UINT128:
117                case SDP_DATA_INT128:
118                        buf += 16;
119                        break;
120                case SDP_DATA_STR8:
121                        if (buf + 1 > eob)
122                                continue;
123                        SDP_GET8(value, buf);
124                        buf += value;
125                        break;
126                case SDP_DATA_STR16:
127                        if (buf + 2 > eob)
128                                continue;
129                        SDP_GET16(value, buf);
130                        if (value > (eob - buf))
131                                return (1);
132                        buf += value;
133                        break;
134                case SDP_DATA_STR32:
135                        if (buf + 4 > eob)
136                                continue;
137                        SDP_GET32(value, buf);
138                        if (value > (eob - buf))
139                                return (1);
140                        buf += value;
141                        break;
142                case SDP_DATA_BOOL:
143                        buf += 1;
144                        break;
145                default:
146                        return (1);
147                }
148        }
149        return (1);
150}
151
152/*
153 * Search a provider for matching UUID in its attributes.
154 */
155static int
156server_search_uuid(provider_p const provider, const uint128_t *uuid)
157{
158        uint8_t buffer[256];
159        const attr_t *attr;
160        int len;
161
162        for (attr = provider->profile->attrs; attr->create != NULL; attr++) {
163
164                len = attr->create(buffer, buffer + sizeof(buffer),
165                    (const uint8_t *)provider->profile, sizeof(*provider->profile));
166                if (len < 0)
167                        continue;
168                if (server_search_uuid_sub(buffer, buffer + len, uuid) == 0)
169                        return (0);
170        }
171        return (1);
172}
173
174/*
175 * Prepare SDP Service Search Attribute Response
176 */
177
178int32_t
179server_prepare_service_search_attribute_response(server_p srv, int32_t fd)
180{
181	uint8_t const	*req = srv->req + sizeof(sdp_pdu_t);
182	uint8_t const	*req_end = req + ((sdp_pdu_p)(srv->req))->len;
183	uint8_t		*rsp = srv->fdidx[fd].rsp;
184	uint8_t const	*rsp_end = rsp + NG_L2CAP_MTU_MAXIMUM;
185
186	uint8_t const	*sspptr = NULL, *aidptr = NULL;
187	uint8_t		*ptr = NULL;
188
189	provider_t	*provider = NULL;
190	int32_t		 type, rsp_limit, ssplen, aidlen, cslen, cs;
191	uint128_t	 uuid, puuid;
192
193	/*
194	 * Minimal Service Search Attribute Request request
195	 *
196	 * seq8 len8		- 2 bytes
197	 *	uuid16 value16  - 3 bytes ServiceSearchPattern
198	 * value16		- 2 bytes MaximumAttributeByteCount
199	 * seq8 len8		- 2 bytes
200	 *	uint16 value16	- 3 bytes AttributeIDList
201	 * value8		- 1 byte  ContinuationState
202	 */
203
204	if (req_end - req < 13)
205		return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
206
207	/* Get size of ServiceSearchPattern */
208	ssplen = 0;
209	SDP_GET8(type, req);
210	switch (type) {
211	case SDP_DATA_SEQ8:
212		SDP_GET8(ssplen, req);
213		break;
214
215	case SDP_DATA_SEQ16:
216		SDP_GET16(ssplen, req);
217		break;
218
219	case SDP_DATA_SEQ32:
220		SDP_GET32(ssplen, req);
221		break;
222	}
223	if (ssplen <= 0)
224		return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
225
226	sspptr = req;
227	req += ssplen;
228
229	/* Get MaximumAttributeByteCount */
230	if (req + 2 > req_end)
231		return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
232
233	SDP_GET16(rsp_limit, req);
234	if (rsp_limit <= 0)
235		return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
236
237	/* Get size of AttributeIDList */
238	if (req + 1 > req_end)
239		return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
240
241	aidlen = 0;
242	SDP_GET8(type, req);
243	switch (type) {
244	case SDP_DATA_SEQ8:
245		if (req + 1 > req_end)
246			return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
247
248		SDP_GET8(aidlen, req);
249		break;
250
251	case SDP_DATA_SEQ16:
252		if (req + 2 > req_end)
253			return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
254
255		SDP_GET16(aidlen, req);
256		break;
257
258	case SDP_DATA_SEQ32:
259		if (req + 4 > req_end)
260			return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
261
262		SDP_GET32(aidlen, req);
263		break;
264	}
265	if (aidlen <= 0)
266		return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
267
268	aidptr = req;
269	req += aidlen;
270
271	/* Get ContinuationState */
272	if (req + 1 > req_end)
273		return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
274
275	SDP_GET8(cslen, req);
276	if (cslen != 0) {
277		if (cslen != 2 || req_end - req != 2)
278			return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
279
280		SDP_GET16(cs, req);
281	} else
282		cs = 0;
283
284	/* Process the request. First, check continuation state */
285	if (srv->fdidx[fd].rsp_cs != cs)
286		return (SDP_ERROR_CODE_INVALID_CONTINUATION_STATE);
287	if (srv->fdidx[fd].rsp_size > 0)
288		return (0);
289
290	/*
291	 * Service Search Attribute Response format
292	 *
293	 * value16		- 2 bytes  AttributeListByteCount (not incl.)
294	 * seq8 len16		- 3 bytes
295	 *	attr list	- 3+ bytes AttributeLists
296	 *	[ attr list ]
297	 */
298
299	ptr = rsp + 3;
300
301	while (ssplen > 0) {
302		SDP_GET8(type, sspptr);
303		ssplen --;
304
305		switch (type) {
306		case SDP_DATA_UUID16:
307			if (ssplen < 2)
308				return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
309
310			memcpy(&uuid, &uuid_base, sizeof(uuid));
311			uuid.b[2] = *sspptr ++;
312			uuid.b[3] = *sspptr ++;
313			ssplen -= 2;
314			break;
315
316		case SDP_DATA_UUID32:
317			if (ssplen < 4)
318				return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
319
320			memcpy(&uuid, &uuid_base, sizeof(uuid));
321			uuid.b[0] = *sspptr ++;
322			uuid.b[1] = *sspptr ++;
323			uuid.b[2] = *sspptr ++;
324			uuid.b[3] = *sspptr ++;
325			ssplen -= 4;
326			break;
327
328		case SDP_DATA_UUID128:
329			if (ssplen < 16)
330				return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
331
332			memcpy(uuid.b, sspptr, 16);
333			sspptr += 16;
334			ssplen -= 16;
335			break;
336
337		default:
338			return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
339			/* NOT REACHED */
340		}
341
342		for (provider = provider_get_first();
343		     provider != NULL;
344		     provider = provider_get_next(provider)) {
345			if (!provider_match_bdaddr(provider, &srv->req_sa.l2cap_bdaddr))
346				continue;
347
348			memcpy(&puuid, &uuid_base, sizeof(puuid));
349			puuid.b[2] = provider->profile->uuid >> 8;
350			puuid.b[3] = provider->profile->uuid;
351
352			if (memcmp(&uuid, &puuid, sizeof(uuid)) != 0 &&
353			    memcmp(&uuid, &uuid_public_browse_group, sizeof(uuid)) != 0 &&
354			    server_search_uuid(provider, &uuid) != 0)
355				continue;
356
357			cs = server_prepare_attr_list(provider,
358				aidptr, aidptr + aidlen, ptr, rsp_end);
359			if (cs < 0)
360				return (SDP_ERROR_CODE_INSUFFICIENT_RESOURCES);
361
362			ptr += cs;
363		}
364	}
365
366	/* Set reply size (not counting PDU header and continuation state) */
367	srv->fdidx[fd].rsp_limit = srv->fdidx[fd].omtu - sizeof(sdp_pdu_t) - 2;
368	if (srv->fdidx[fd].rsp_limit > rsp_limit)
369		srv->fdidx[fd].rsp_limit = rsp_limit;
370
371	srv->fdidx[fd].rsp_size = ptr - rsp;
372	srv->fdidx[fd].rsp_cs = 0;
373
374	/* Fix AttributeLists sequence header */
375	ptr = rsp;
376	SDP_PUT8(SDP_DATA_SEQ16, ptr);
377	SDP_PUT16(srv->fdidx[fd].rsp_size - 3, ptr);
378
379	return (0);
380}
381
382