search.c revision 126297
1/*
2 * search.c
3 *
4 * Copyright (c) 2001-2003 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: search.c,v 1.2 2003/09/04 22:12:13 max Exp $
29 * $FreeBSD: head/lib/libsdp/search.c 126297 2004-02-26 20:44:55Z emax $
30 */
31
32#include <sys/uio.h>
33#include <netinet/in.h>
34#include <arpa/inet.h>
35#include <bluetooth.h>
36#include <errno.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <unistd.h>
41
42#include <sdp-int.h>
43#include <sdp.h>
44
45int32_t
46sdp_search(void *xss,
47		uint32_t plen, uint16_t const *pp,
48		uint32_t alen, uint32_t const *ap,
49		uint32_t vlen, sdp_attr_t *vp)
50{
51	struct sdp_xpdu {
52		sdp_pdu_t		 pdu;
53		uint16_t		 len;
54	} __attribute__ ((packed))	 xpdu;
55
56	sdp_session_p			 ss = (sdp_session_p) xss;
57	uint8_t				*req = NULL, *rsp = NULL, *rsp_tmp = NULL;
58	int32_t				 type, len;
59
60	if (ss == NULL)
61		return (-1);
62
63	if (ss->req == NULL || ss->rsp == NULL ||
64	    plen == 0 || pp == NULL || alen == 0 || ap == NULL) {
65		ss->error = EINVAL;
66		return (-1);
67	}
68
69	/* Calculate length of the request */
70	req = ss->req;
71	plen = plen * (sizeof(pp[0]) + 1);
72	alen = alen * (sizeof(ap[0]) + 1);
73
74	len =	plen + sizeof(uint8_t) + sizeof(uint16_t) +
75			/* ServiceSearchPattern */
76		sizeof(uint16_t) +
77			/* MaximumAttributeByteCount */
78		alen + sizeof(uint8_t) + sizeof(uint16_t);
79			/* AttributeIDList */
80
81	if (ss->req_e - req < len) {
82		ss->error = ENOBUFS;
83		return (-1);
84	}
85
86	/* Put ServiceSearchPattern */
87	SDP_PUT8(SDP_DATA_SEQ16, req);
88	SDP_PUT16(plen, req);
89	for (; plen > 0; pp ++, plen -= (sizeof(pp[0]) + 1)) {
90		SDP_PUT8(SDP_DATA_UUID16, req);
91		SDP_PUT16(*pp, req);
92	}
93
94	/* Put MaximumAttributeByteCount */
95	SDP_PUT16(0xffff, req);
96
97	/* Put AttributeIDList */
98	SDP_PUT8(SDP_DATA_SEQ16, req);
99	SDP_PUT16(alen, req);
100	for (; alen > 0; ap ++, alen -= (sizeof(ap[0]) + 1)) {
101		SDP_PUT8(SDP_DATA_UINT32, req);
102		SDP_PUT32(*ap, req);
103	}
104
105	/* Submit ServiceSearchAttributeRequest and wait for response */
106	ss->cslen = 0;
107	rsp = ss->rsp;
108
109	do {
110		struct iovec	 iov[2];
111		uint8_t		*req_cs = req;
112
113		/* Add continuation state (if any) */
114		if (ss->req_e - req_cs < ss->cslen + 1) {
115			ss->error = ENOBUFS;
116			return (-1);
117		}
118
119		SDP_PUT8(ss->cslen, req_cs);
120		if (ss->cslen > 0) {
121			memcpy(req_cs, ss->cs, ss->cslen);
122			req_cs += ss->cslen;
123		}
124
125		/* Prepare SDP PDU header */
126		xpdu.pdu.pid = SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST;
127		xpdu.pdu.tid = htons(ss->tid);
128		xpdu.pdu.len = htons(req_cs - ss->req);
129
130		/* Submit request */
131		iov[0].iov_base = (void *) &xpdu;
132		iov[0].iov_len = sizeof(xpdu.pdu);
133		iov[1].iov_base = (void *) ss->req;
134		iov[1].iov_len = req_cs - ss->req;
135
136		do {
137			len = writev(ss->s, iov, sizeof(iov)/sizeof(iov[0]));
138		} while (len < 0 && errno == EINTR);
139
140		if (len < 0) {
141			ss->error = errno;
142			return (-1);
143		}
144
145		/* Read response */
146		iov[0].iov_base = (void *) &xpdu;
147		iov[0].iov_len = sizeof(xpdu);
148		iov[1].iov_base = (void *) rsp;
149		iov[1].iov_len = ss->imtu;
150
151		do {
152			len = readv(ss->s, iov, sizeof(iov)/sizeof(iov[0]));
153		} while (len < 0 && errno == EINTR);
154
155		if (len < 0) {
156			ss->error = errno;
157			return (-1);
158		}
159		if (len < sizeof(xpdu)) {
160			ss->error = ENOMSG;
161			return (-1);
162		}
163
164		xpdu.pdu.tid = ntohs(xpdu.pdu.tid);
165		xpdu.pdu.len = ntohs(xpdu.pdu.len);
166		xpdu.len = ntohs(xpdu.len);
167
168		if (xpdu.pdu.pid == SDP_PDU_ERROR_RESPONSE ||
169		    xpdu.pdu.tid != ss->tid ||
170		    xpdu.pdu.len > len ||
171		    xpdu.len > xpdu.pdu.len) {
172			ss->error = EIO;
173			return (-1);
174		}
175
176		/* Save continuation state (if any) */
177		ss->cslen = rsp[xpdu.len];
178		if (ss->cslen > 0) {
179			if (ss->cslen > sizeof(ss->cs)) {
180				ss->error = ENOBUFS;
181				return (-1);
182			}
183
184			memcpy(ss->cs, rsp + xpdu.len + 1, ss->cslen);
185
186			/*
187			 * Ensure that we always have ss->imtu bytes
188			 * available in the ss->rsp buffer
189			 */
190
191			if (ss->rsp_e - rsp <= ss->imtu) {
192				uint32_t	 size, offset;
193
194				size = ss->rsp_e - ss->rsp + ss->imtu;
195				offset = rsp - ss->rsp;
196
197				rsp_tmp = realloc(ss->rsp, size);
198				if (rsp_tmp == NULL) {
199					ss->error = ENOMEM;
200					return (-1);
201				}
202
203				ss->rsp = rsp_tmp;
204				ss->rsp_e = ss->rsp + size;
205				rsp = ss->rsp + offset;
206			}
207		}
208
209		rsp += xpdu.len;
210		ss->tid ++;
211	} while (ss->cslen > 0);
212
213	/*
214	 * If we got here then we have completed SDP transaction and now
215	 * we must populate attribute values into vp array. At this point
216	 * ss->rsp points to the beginning of the response and rsp points
217	 * to the end of the response.
218	 *
219	 * From Bluetooth v1.1 spec page 364
220	 *
221	 * The AttributeLists is a data element sequence where each element
222	 * in turn is a data element sequence representing an attribute list.
223	 * Each attribute list contains attribute IDs and attribute values
224	 * from one service record. The first element in each attribute list
225	 * contains the attribute ID of the first attribute to be returned for
226	 * that service record. The second element in each attribute list
227	 * contains the corresponding attribute value. Successive pairs of
228	 * elements in each attribute list contain additional attribute ID
229	 * and value pairs. Only attributes that have non-null values within
230	 * the service record and whose attribute IDs were specified in the
231	 * SDP_ServiceSearchAttributeRequest are contained in the AttributeLists
232	 * Neither an attribute ID nor attribute value is placed in
233	 * AttributeLists for attributes in the service record that have no
234	 * value. Within each attribute list, the attributes are listed in
235	 * ascending order of attribute ID value.
236	 */
237
238	if (vp == NULL)
239		goto done;
240
241	rsp_tmp = ss->rsp;
242
243	/* Skip the first SEQ */
244	SDP_GET8(type, rsp_tmp);
245	switch (type) {
246	case SDP_DATA_SEQ8:
247		SDP_GET8(len, rsp_tmp);
248		break;
249
250	case SDP_DATA_SEQ16:
251		SDP_GET16(len, rsp_tmp);
252		break;
253
254	case SDP_DATA_SEQ32:
255		SDP_GET32(len, rsp_tmp);
256		break;
257
258	default:
259		ss->error = ENOATTR;
260		return (-1);
261		/* NOT REACHED */
262	}
263
264	for (; rsp_tmp < rsp && vlen > 0; ) {
265		/* Get set of attributes for the next record */
266		SDP_GET8(type, rsp_tmp);
267		switch (type) {
268		case SDP_DATA_SEQ8:
269			SDP_GET8(len, rsp_tmp);
270			break;
271
272		case SDP_DATA_SEQ16:
273			SDP_GET16(len, rsp_tmp);
274			break;
275
276		case SDP_DATA_SEQ32:
277			SDP_GET32(len, rsp_tmp);
278			break;
279
280		default:
281			ss->error = ENOATTR;
282			return (-1);
283			/* NOT REACHED */
284		}
285
286		/* Now rsp_tmp points to list of (attr,value) pairs */
287		for (; len > 0 && vlen > 0; vp ++, vlen --) {
288			/* Attribute */
289			SDP_GET8(type, rsp_tmp);
290			if (type != SDP_DATA_UINT16) {
291				ss->error = ENOATTR;
292				return (-1);
293			}
294			SDP_GET16(vp->attr, rsp_tmp);
295
296			/* Attribute value */
297			switch (rsp_tmp[0]) {
298			case SDP_DATA_NIL:
299				alen = 0;
300				break;
301
302			case SDP_DATA_UINT8:
303			case SDP_DATA_INT8:
304			case SDP_DATA_BOOL:
305				alen = sizeof(uint8_t);
306				break;
307
308			case SDP_DATA_UINT16:
309			case SDP_DATA_INT16:
310			case SDP_DATA_UUID16:
311				alen = sizeof(uint16_t);
312				break;
313
314			case SDP_DATA_UINT32:
315			case SDP_DATA_INT32:
316			case SDP_DATA_UUID32:
317				alen = sizeof(uint32_t);
318				break;
319
320			case SDP_DATA_UINT64:
321			case SDP_DATA_INT64:
322				alen = sizeof(uint64_t);
323				break;
324
325			case SDP_DATA_UINT128:
326			case SDP_DATA_INT128:
327			case SDP_DATA_UUID128:
328				alen = sizeof(uint128_t);
329				break;
330
331			case SDP_DATA_STR8:
332			case SDP_DATA_URL8:
333			case SDP_DATA_SEQ8:
334			case SDP_DATA_ALT8:
335				alen = rsp_tmp[1] + sizeof(uint8_t);
336				break;
337
338			case SDP_DATA_STR16:
339			case SDP_DATA_URL16:
340			case SDP_DATA_SEQ16:
341			case SDP_DATA_ALT16:
342				alen =	  ((uint16_t)rsp_tmp[1] << 8)
343					| ((uint16_t)rsp_tmp[2]);
344				alen += sizeof(uint16_t);
345				break;
346
347			case SDP_DATA_STR32:
348			case SDP_DATA_URL32:
349			case SDP_DATA_SEQ32:
350			case SDP_DATA_ALT32:
351				alen =    ((uint32_t)rsp_tmp[1] << 24)
352					| ((uint32_t)rsp_tmp[2] << 16)
353					| ((uint32_t)rsp_tmp[3] <<  8)
354					| ((uint32_t)rsp_tmp[4]);
355				alen += sizeof(uint32_t);
356				break;
357
358			default:
359				ss->error = ENOATTR;
360				return (-1);
361				/* NOT REACHED */
362			}
363
364			alen += sizeof(uint8_t);
365
366			if (vp->value != NULL) {
367				if (alen <= vp->vlen) {
368					vp->flags = SDP_ATTR_OK;
369					vp->vlen = alen;
370				} else
371					vp->flags = SDP_ATTR_TRUNCATED;
372
373				memcpy(vp->value, rsp_tmp, vp->vlen);
374			} else
375				vp->flags = SDP_ATTR_INVALID;
376
377			len -=	(
378				sizeof(uint8_t) + sizeof(uint16_t) +
379				alen
380				);
381
382			rsp_tmp += alen;
383		}
384	}
385done:
386	ss->error = 0;
387
388	return (0);
389}
390
391