search.c revision 124758
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 124758 2004-01-20 20:48:26Z 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.len > xpdu.pdu.len) {
171			ss->error = EIO;
172			return (-1);
173		}
174
175		/* Save continuation state (if any) */
176		ss->cslen = rsp[xpdu.len];
177		if (ss->cslen > 0) {
178			if (ss->cslen > sizeof(ss->cs)) {
179				ss->error = ENOBUFS;
180				return (-1);
181			}
182
183			memcpy(ss->cs, rsp + xpdu.len + 1, ss->cslen);
184
185			/*
186			 * Ensure that we always have ss->imtu bytes
187			 * available in the ss->rsp buffer
188			 */
189
190			if (ss->rsp_e - rsp <= ss->imtu) {
191				uint32_t	 size, offset;
192
193				size = ss->rsp_e - ss->rsp + ss->imtu;
194				offset = rsp - ss->rsp;
195
196				rsp_tmp = realloc(ss->rsp, size);
197				if (rsp_tmp == NULL) {
198					ss->error = ENOMEM;
199					return (-1);
200				}
201
202				ss->rsp = rsp_tmp;
203				ss->rsp_e = ss->rsp + size;
204				rsp = ss->rsp + offset;
205			}
206		}
207
208		rsp += xpdu.len;
209		ss->tid ++;
210	} while (ss->cslen > 0);
211
212	/*
213	 * If we got here then we have completed SDP transaction and now
214	 * we must populate attribute values into vp array. At this point
215	 * ss->rsp points to the beginning of the response and rsp points
216	 * to the end of the response.
217	 *
218	 * From Bluetooth v1.1 spec page 364
219	 *
220	 * The AttributeLists is a data element sequence where each element
221	 * in turn is a data element sequence representing an attribute list.
222	 * Each attribute list contains attribute IDs and attribute values
223	 * from one service record. The first element in each attribute list
224	 * contains the attribute ID of the first attribute to be returned for
225	 * that service record. The second element in each attribute list
226	 * contains the corresponding attribute value. Successive pairs of
227	 * elements in each attribute list contain additional attribute ID
228	 * and value pairs. Only attributes that have non-null values within
229	 * the service record and whose attribute IDs were specified in the
230	 * SDP_ServiceSearchAttributeRequest are contained in the AttributeLists
231	 * Neither an attribute ID nor attribute value is placed in
232	 * AttributeLists for attributes in the service record that have no
233	 * value. Within each attribute list, the attributes are listed in
234	 * ascending order of attribute ID value.
235	 */
236
237	if (vp == NULL)
238		goto done;
239
240	rsp_tmp = ss->rsp;
241
242	/* Skip the first SEQ */
243	SDP_GET8(type, rsp_tmp);
244	switch (type) {
245	case SDP_DATA_SEQ8:
246		SDP_GET8(len, rsp_tmp);
247		break;
248
249	case SDP_DATA_SEQ16:
250		SDP_GET16(len, rsp_tmp);
251		break;
252
253	case SDP_DATA_SEQ32:
254		SDP_GET32(len, rsp_tmp);
255		break;
256
257	default:
258		ss->error = ENOATTR;
259		return (-1);
260		/* NOT REACHED */
261	}
262
263	for (; rsp_tmp < rsp && vlen > 0; ) {
264		/* Get set of attributes for the next record */
265		SDP_GET8(type, rsp_tmp);
266		switch (type) {
267		case SDP_DATA_SEQ8:
268			SDP_GET8(len, rsp_tmp);
269			break;
270
271		case SDP_DATA_SEQ16:
272			SDP_GET16(len, rsp_tmp);
273			break;
274
275		case SDP_DATA_SEQ32:
276			SDP_GET32(len, rsp_tmp);
277			break;
278
279		default:
280			ss->error = ENOATTR;
281			return (-1);
282			/* NOT REACHED */
283		}
284
285		/* Now rsp_tmp points to list of (attr,value) pairs */
286		for (; len > 0 && vlen > 0; vp ++, vlen --) {
287			/* Attribute */
288			SDP_GET8(type, rsp_tmp);
289			if (type != SDP_DATA_UINT16) {
290				ss->error = ENOATTR;
291				return (-1);
292			}
293			SDP_GET16(vp->attr, rsp_tmp);
294
295			/* Attribute value */
296			switch (rsp_tmp[0]) {
297			case SDP_DATA_NIL:
298				alen = 0;
299				break;
300
301			case SDP_DATA_UINT8:
302			case SDP_DATA_INT8:
303			case SDP_DATA_BOOL:
304				alen = sizeof(uint8_t);
305				break;
306
307			case SDP_DATA_UINT16:
308			case SDP_DATA_INT16:
309			case SDP_DATA_UUID16:
310				alen = sizeof(uint16_t);
311				break;
312
313			case SDP_DATA_UINT32:
314			case SDP_DATA_INT32:
315			case SDP_DATA_UUID32:
316				alen = sizeof(uint32_t);
317				break;
318
319			case SDP_DATA_UINT64:
320			case SDP_DATA_INT64:
321				alen = sizeof(uint64_t);
322				break;
323
324			case SDP_DATA_UINT128:
325			case SDP_DATA_INT128:
326			case SDP_DATA_UUID128:
327				alen = sizeof(uint128_t);
328				break;
329
330			case SDP_DATA_STR8:
331			case SDP_DATA_URL8:
332			case SDP_DATA_SEQ8:
333			case SDP_DATA_ALT8:
334				alen = rsp_tmp[1] + sizeof(uint8_t);
335				break;
336
337			case SDP_DATA_STR16:
338			case SDP_DATA_URL16:
339			case SDP_DATA_SEQ16:
340			case SDP_DATA_ALT16:
341				alen =	  ((uint16_t)rsp_tmp[1] << 8)
342					| ((uint16_t)rsp_tmp[2]);
343				alen += sizeof(uint16_t);
344				break;
345
346			case SDP_DATA_STR32:
347			case SDP_DATA_URL32:
348			case SDP_DATA_SEQ32:
349			case SDP_DATA_ALT32:
350				alen =    ((uint32_t)rsp_tmp[1] << 24)
351					| ((uint32_t)rsp_tmp[2] << 16)
352					| ((uint32_t)rsp_tmp[3] <<  8)
353					| ((uint32_t)rsp_tmp[4]);
354				alen += sizeof(uint32_t);
355				break;
356
357			default:
358				ss->error = ENOATTR;
359				return (-1);
360				/* NOT REACHED */
361			}
362
363			alen += sizeof(uint8_t);
364
365			if (vp->value != NULL) {
366				if (alen <= vp->vlen) {
367					vp->flags = SDP_ATTR_OK;
368					vp->vlen = alen;
369				} else
370					vp->flags = SDP_ATTR_TRUNCATED;
371
372				memcpy(vp->value, rsp_tmp, vp->vlen);
373			} else
374				vp->flags = SDP_ATTR_INVALID;
375
376			len -=	(
377				sizeof(uint8_t) + sizeof(uint16_t) +
378				alen
379				);
380
381			rsp_tmp += alen;
382		}
383	}
384done:
385	ss->error = 0;
386
387	return (0);
388}
389
390