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