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