1/*
2 * ssar.c
3 *
4 * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $Id: ssar.c,v 1.4 2004/01/12 22:54:31 max Exp $
29 * $FreeBSD: stable/10/usr.sbin/bluetooth/sdpd/ssar.c 344146 2019-02-15 09:22:23Z hselasky $
30 */
31
32#include <sys/queue.h>
33#include <bluetooth.h>
34#include <sdp.h>
35#include <string.h>
36#include "profile.h"
37#include "provider.h"
38#include "server.h"
39#include "uuid-private.h"
40
41/* from sar.c */
42int32_t server_prepare_attr_list(provider_p const provider,
43		uint8_t const *req, uint8_t const * const req_end,
44		uint8_t *rsp, uint8_t const * const rsp_end);
45
46/*
47 * Scan an attribute for matching UUID.
48 */
49static int
50server_search_uuid_sub(uint8_t *buf, uint8_t const * const eob, const uint128_t *uuid)
51{
52        int128_t duuid;
53        uint32_t value;
54        uint8_t type;
55
56        while (buf < eob) {
57
58                SDP_GET8(type, buf);
59
60                switch (type) {
61                case SDP_DATA_UUID16:
62                        if (buf + 2 > eob)
63                                continue;
64                        SDP_GET16(value, buf);
65
66                        memcpy(&duuid, &uuid_base, sizeof(duuid));
67                        duuid.b[2] = value >> 8 & 0xff;
68                        duuid.b[3] = value & 0xff;
69
70                        if (memcmp(&duuid, uuid, sizeof(duuid)) == 0)
71                                return (0);
72                        break;
73                case SDP_DATA_UUID32:
74                        if (buf + 4 > eob)
75                                continue;
76                        SDP_GET32(value, buf);
77                        memcpy(&duuid, &uuid_base, sizeof(duuid));
78                        duuid.b[0] = value >> 24 & 0xff;
79                        duuid.b[1] = value >> 16 & 0xff;
80                        duuid.b[2] = value >> 8 & 0xff;
81                        duuid.b[3] = value & 0xff;
82
83                        if (memcmp(&duuid, uuid, sizeof(duuid)) == 0)
84                                return (0);
85                        break;
86                case SDP_DATA_UUID128:
87                        if (buf + 16 > eob)
88                                continue;
89                        SDP_GET_UUID128(&duuid, buf);
90
91                        if (memcmp(&duuid, uuid, sizeof(duuid)) == 0)
92                                return (0);
93                        break;
94                case SDP_DATA_UINT8:
95                case SDP_DATA_INT8:
96                case SDP_DATA_SEQ8:
97                        buf++;
98                        break;
99                case SDP_DATA_UINT16:
100                case SDP_DATA_INT16:
101                case SDP_DATA_SEQ16:
102                        buf += 2;
103                        break;
104                case SDP_DATA_UINT32:
105                case SDP_DATA_INT32:
106                case SDP_DATA_SEQ32:
107                        buf += 4;
108                        break;
109                case SDP_DATA_UINT64:
110                case SDP_DATA_INT64:
111                        buf += 8;
112                        break;
113                case SDP_DATA_UINT128:
114                case SDP_DATA_INT128:
115                        buf += 16;
116                        break;
117                case SDP_DATA_STR8:
118                        if (buf + 1 > eob)
119                                continue;
120                        SDP_GET8(value, buf);
121                        buf += value;
122                        break;
123                case SDP_DATA_STR16:
124                        if (buf + 2 > eob)
125                                continue;
126                        SDP_GET16(value, buf);
127                        if (value > (eob - buf))
128                                return (1);
129                        buf += value;
130                        break;
131                case SDP_DATA_STR32:
132                        if (buf + 4 > eob)
133                                continue;
134                        SDP_GET32(value, buf);
135                        if (value > (eob - buf))
136                                return (1);
137                        buf += value;
138                        break;
139                case SDP_DATA_BOOL:
140                        buf += 1;
141                        break;
142                default:
143                        return (1);
144                }
145        }
146        return (1);
147}
148
149/*
150 * Search a provider for matching UUID in its attributes.
151 */
152static int
153server_search_uuid(provider_p const provider, const uint128_t *uuid)
154{
155        uint8_t buffer[256];
156        const attr_t *attr;
157        int len;
158
159        for (attr = provider->profile->attrs; attr->create != NULL; attr++) {
160
161                len = attr->create(buffer, buffer + sizeof(buffer),
162                    (const uint8_t *)provider->profile, sizeof(*provider->profile));
163                if (len < 0)
164                        continue;
165                if (server_search_uuid_sub(buffer, buffer + len, uuid) == 0)
166                        return (0);
167        }
168        return (1);
169}
170
171/*
172 * Prepare SDP Service Search Attribute Response
173 */
174
175int32_t
176server_prepare_service_search_attribute_response(server_p srv, int32_t fd)
177{
178	uint8_t const	*req = srv->req + sizeof(sdp_pdu_t);
179	uint8_t const	*req_end = req + ((sdp_pdu_p)(srv->req))->len;
180	uint8_t		*rsp = srv->fdidx[fd].rsp;
181	uint8_t const	*rsp_end = rsp + NG_L2CAP_MTU_MAXIMUM;
182
183	uint8_t const	*sspptr = NULL, *aidptr = NULL;
184	uint8_t		*ptr = NULL;
185
186	provider_t	*provider = NULL;
187	int32_t		 type, rsp_limit, ssplen, aidlen, cslen, cs;
188	uint128_t	 uuid, puuid;
189
190	/*
191	 * Minimal Service Search Attribute Request request
192	 *
193	 * seq8 len8		- 2 bytes
194	 *	uuid16 value16  - 3 bytes ServiceSearchPattern
195	 * value16		- 2 bytes MaximumAttributeByteCount
196	 * seq8 len8		- 2 bytes
197	 *	uint16 value16	- 3 bytes AttributeIDList
198	 * value8		- 1 byte  ContinuationState
199	 */
200
201	if (req_end - req < 13)
202		return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
203
204	/* Get size of ServiceSearchPattern */
205	ssplen = 0;
206	SDP_GET8(type, req);
207	switch (type) {
208	case SDP_DATA_SEQ8:
209		SDP_GET8(ssplen, req);
210		break;
211
212	case SDP_DATA_SEQ16:
213		SDP_GET16(ssplen, req);
214		break;
215
216	case SDP_DATA_SEQ32:
217		SDP_GET32(ssplen, req);
218		break;
219	}
220	if (ssplen <= 0)
221		return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
222
223	sspptr = req;
224	req += ssplen;
225
226	/* Get MaximumAttributeByteCount */
227	if (req + 2 > req_end)
228		return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
229
230	SDP_GET16(rsp_limit, req);
231	if (rsp_limit <= 0)
232		return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
233
234	/* Get size of AttributeIDList */
235	if (req + 1 > req_end)
236		return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
237
238	aidlen = 0;
239	SDP_GET8(type, req);
240	switch (type) {
241	case SDP_DATA_SEQ8:
242		if (req + 1 > req_end)
243			return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
244
245		SDP_GET8(aidlen, req);
246		break;
247
248	case SDP_DATA_SEQ16:
249		if (req + 2 > req_end)
250			return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
251
252		SDP_GET16(aidlen, req);
253		break;
254
255	case SDP_DATA_SEQ32:
256		if (req + 4 > req_end)
257			return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
258
259		SDP_GET32(aidlen, req);
260		break;
261	}
262	if (aidlen <= 0)
263		return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
264
265	aidptr = req;
266	req += aidlen;
267
268	/* Get ContinuationState */
269	if (req + 1 > req_end)
270		return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
271
272	SDP_GET8(cslen, req);
273	if (cslen != 0) {
274		if (cslen != 2 || req_end - req != 2)
275			return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
276
277		SDP_GET16(cs, req);
278	} else
279		cs = 0;
280
281	/* Process the request. First, check continuation state */
282	if (srv->fdidx[fd].rsp_cs != cs)
283		return (SDP_ERROR_CODE_INVALID_CONTINUATION_STATE);
284	if (srv->fdidx[fd].rsp_size > 0)
285		return (0);
286
287	/*
288	 * Service Search Attribute Response format
289	 *
290	 * value16		- 2 bytes  AttributeListByteCount (not incl.)
291	 * seq8 len16		- 3 bytes
292	 *	attr list	- 3+ bytes AttributeLists
293	 *	[ attr list ]
294	 */
295
296	ptr = rsp + 3;
297
298	while (ssplen > 0) {
299		SDP_GET8(type, sspptr);
300		ssplen --;
301
302		switch (type) {
303		case SDP_DATA_UUID16:
304			if (ssplen < 2)
305				return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
306
307			memcpy(&uuid, &uuid_base, sizeof(uuid));
308			uuid.b[2] = *sspptr ++;
309			uuid.b[3] = *sspptr ++;
310			ssplen -= 2;
311			break;
312
313		case SDP_DATA_UUID32:
314			if (ssplen < 4)
315				return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
316
317			memcpy(&uuid, &uuid_base, sizeof(uuid));
318			uuid.b[0] = *sspptr ++;
319			uuid.b[1] = *sspptr ++;
320			uuid.b[2] = *sspptr ++;
321			uuid.b[3] = *sspptr ++;
322			ssplen -= 4;
323			break;
324
325		case SDP_DATA_UUID128:
326			if (ssplen < 16)
327				return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
328
329			memcpy(uuid.b, sspptr, 16);
330			sspptr += 16;
331			ssplen -= 16;
332			break;
333
334		default:
335			return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
336			/* NOT REACHED */
337		}
338
339		for (provider = provider_get_first();
340		     provider != NULL;
341		     provider = provider_get_next(provider)) {
342			if (!provider_match_bdaddr(provider, &srv->req_sa.l2cap_bdaddr))
343				continue;
344
345			memcpy(&puuid, &uuid_base, sizeof(puuid));
346			puuid.b[2] = provider->profile->uuid >> 8;
347			puuid.b[3] = provider->profile->uuid;
348
349			if (memcmp(&uuid, &puuid, sizeof(uuid)) != 0 &&
350			    memcmp(&uuid, &uuid_public_browse_group, sizeof(uuid)) != 0 &&
351			    server_search_uuid(provider, &uuid) != 0)
352				continue;
353
354			cs = server_prepare_attr_list(provider,
355				aidptr, aidptr + aidlen, ptr, rsp_end);
356			if (cs < 0)
357				return (SDP_ERROR_CODE_INSUFFICIENT_RESOURCES);
358
359			ptr += cs;
360		}
361	}
362
363	/* Set reply size (not counting PDU header and continuation state) */
364	srv->fdidx[fd].rsp_limit = srv->fdidx[fd].omtu - sizeof(sdp_pdu_t) - 2;
365	if (srv->fdidx[fd].rsp_limit > rsp_limit)
366		srv->fdidx[fd].rsp_limit = rsp_limit;
367
368	srv->fdidx[fd].rsp_size = ptr - rsp;
369	srv->fdidx[fd].rsp_cs = 0;
370
371	/* Fix AttributeLists sequence header */
372	ptr = rsp;
373	SDP_PUT8(SDP_DATA_SEQ16, ptr);
374	SDP_PUT16(srv->fdidx[fd].rsp_size - 3, ptr);
375
376	return (0);
377}
378
379