1121054Semax/*
2121054Semax * search.c
3121054Semax *
4121054Semax * Copyright (c) 2001-2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
5121054Semax * All rights reserved.
6121054Semax *
7121054Semax * Redistribution and use in source and binary forms, with or without
8121054Semax * modification, are permitted provided that the following conditions
9121054Semax * are met:
10121054Semax * 1. Redistributions of source code must retain the above copyright
11121054Semax *    notice, this list of conditions and the following disclaimer.
12121054Semax * 2. Redistributions in binary form must reproduce the above copyright
13121054Semax *    notice, this list of conditions and the following disclaimer in the
14121054Semax *    documentation and/or other materials provided with the distribution.
15121054Semax *
16121054Semax * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17121054Semax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18121054Semax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19121054Semax * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20121054Semax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21121054Semax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22121054Semax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23121054Semax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24121054Semax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25121054Semax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26121054Semax * SUCH DAMAGE.
27121054Semax *
28121054Semax * $Id: search.c,v 1.2 2003/09/04 22:12:13 max Exp $
29121054Semax * $FreeBSD: releng/10.3/lib/libsdp/search.c 173673 2007-11-16 15:13:12Z emax $
30121054Semax */
31121054Semax
32121054Semax#include <sys/uio.h>
33121054Semax#include <netinet/in.h>
34121054Semax#include <arpa/inet.h>
35121054Semax#include <bluetooth.h>
36121054Semax#include <errno.h>
37121054Semax#include <stdio.h>
38121054Semax#include <stdlib.h>
39121054Semax#include <string.h>
40121054Semax#include <unistd.h>
41121054Semax
42121054Semax#include <sdp-int.h>
43121054Semax#include <sdp.h>
44121054Semax
45121054Semaxint32_t
46121054Semaxsdp_search(void *xss,
47124305Semax		uint32_t plen, uint16_t const *pp,
48124305Semax		uint32_t alen, uint32_t const *ap,
49124305Semax		uint32_t vlen, sdp_attr_t *vp)
50121054Semax{
51121054Semax	struct sdp_xpdu {
52124305Semax		sdp_pdu_t		 pdu;
53124305Semax		uint16_t		 len;
54121054Semax	} __attribute__ ((packed))	 xpdu;
55121054Semax
56121054Semax	sdp_session_p			 ss = (sdp_session_p) xss;
57124305Semax	uint8_t				*req = NULL, *rsp = NULL, *rsp_tmp = NULL;
58138633Semax	int32_t				 t, len;
59138633Semax	uint16_t			 lo, hi;
60121054Semax
61121054Semax	if (ss == NULL)
62121054Semax		return (-1);
63121054Semax
64121054Semax	if (ss->req == NULL || ss->rsp == NULL ||
65121054Semax	    plen == 0 || pp == NULL || alen == 0 || ap == NULL) {
66121054Semax		ss->error = EINVAL;
67121054Semax		return (-1);
68121054Semax	}
69121054Semax
70121054Semax	req = ss->req;
71138633Semax
72138633Semax	/* Calculate ServiceSearchPattern length */
73121054Semax	plen = plen * (sizeof(pp[0]) + 1);
74121054Semax
75138633Semax	/* Calculate AttributeIDList length */
76138633Semax	for (len = 0, t = 0; t < alen; t ++) {
77138633Semax		lo = (uint16_t) (ap[t] >> 16);
78138633Semax		hi = (uint16_t) (ap[t]);
79138633Semax
80138633Semax		if (lo > hi) {
81138633Semax			ss->error = EINVAL;
82138633Semax			return (-1);
83138633Semax		}
84138633Semax
85138633Semax		if (lo != hi)
86138633Semax			len += (sizeof(ap[t]) + 1);
87138633Semax		else
88138633Semax			len += (sizeof(lo) + 1);
89138633Semax	}
90138633Semax	alen = len;
91138633Semax
92138633Semax	/* Calculate length of the request */
93124305Semax	len =	plen + sizeof(uint8_t) + sizeof(uint16_t) +
94121054Semax			/* ServiceSearchPattern */
95124305Semax		sizeof(uint16_t) +
96121054Semax			/* MaximumAttributeByteCount */
97124305Semax		alen + sizeof(uint8_t) + sizeof(uint16_t);
98121054Semax			/* AttributeIDList */
99121054Semax
100121054Semax	if (ss->req_e - req < len) {
101121054Semax		ss->error = ENOBUFS;
102121054Semax		return (-1);
103121054Semax	}
104121054Semax
105121054Semax	/* Put ServiceSearchPattern */
106121054Semax	SDP_PUT8(SDP_DATA_SEQ16, req);
107121054Semax	SDP_PUT16(plen, req);
108121054Semax	for (; plen > 0; pp ++, plen -= (sizeof(pp[0]) + 1)) {
109121054Semax		SDP_PUT8(SDP_DATA_UUID16, req);
110121054Semax		SDP_PUT16(*pp, req);
111121054Semax	}
112121054Semax
113121054Semax	/* Put MaximumAttributeByteCount */
114121054Semax	SDP_PUT16(0xffff, req);
115121054Semax
116121054Semax	/* Put AttributeIDList */
117121054Semax	SDP_PUT8(SDP_DATA_SEQ16, req);
118121054Semax	SDP_PUT16(alen, req);
119138633Semax	for (; alen > 0; ap ++) {
120138633Semax		lo = (uint16_t) (*ap >> 16);
121138633Semax		hi = (uint16_t) (*ap);
122138633Semax
123138633Semax		if (lo != hi) {
124138633Semax			/* Put attribute range */
125138633Semax			SDP_PUT8(SDP_DATA_UINT32, req);
126138633Semax			SDP_PUT32(*ap, req);
127138633Semax			alen -= (sizeof(ap[0]) + 1);
128138633Semax		} else {
129138633Semax			/* Put attribute */
130138633Semax			SDP_PUT8(SDP_DATA_UINT16, req);
131138633Semax			SDP_PUT16(lo, req);
132138633Semax			alen -= (sizeof(lo) + 1);
133138633Semax		}
134121054Semax	}
135121054Semax
136121054Semax	/* Submit ServiceSearchAttributeRequest and wait for response */
137121054Semax	ss->cslen = 0;
138121054Semax	rsp = ss->rsp;
139121054Semax
140121054Semax	do {
141121054Semax		struct iovec	 iov[2];
142124305Semax		uint8_t		*req_cs = req;
143121054Semax
144121054Semax		/* Add continuation state (if any) */
145121054Semax		if (ss->req_e - req_cs < ss->cslen + 1) {
146121054Semax			ss->error = ENOBUFS;
147121054Semax			return (-1);
148121054Semax		}
149121054Semax
150121054Semax		SDP_PUT8(ss->cslen, req_cs);
151121054Semax		if (ss->cslen > 0) {
152121054Semax			memcpy(req_cs, ss->cs, ss->cslen);
153121054Semax			req_cs += ss->cslen;
154121054Semax		}
155121054Semax
156121054Semax		/* Prepare SDP PDU header */
157121054Semax		xpdu.pdu.pid = SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST;
158121054Semax		xpdu.pdu.tid = htons(ss->tid);
159121054Semax		xpdu.pdu.len = htons(req_cs - ss->req);
160121054Semax
161121054Semax		/* Submit request */
162121054Semax		iov[0].iov_base = (void *) &xpdu;
163121054Semax		iov[0].iov_len = sizeof(xpdu.pdu);
164121054Semax		iov[1].iov_base = (void *) ss->req;
165121054Semax		iov[1].iov_len = req_cs - ss->req;
166121054Semax
167124758Semax		do {
168124758Semax			len = writev(ss->s, iov, sizeof(iov)/sizeof(iov[0]));
169124758Semax		} while (len < 0 && errno == EINTR);
170124758Semax
171121054Semax		if (len < 0) {
172121054Semax			ss->error = errno;
173121054Semax			return (-1);
174121054Semax		}
175121054Semax
176121054Semax		/* Read response */
177121054Semax		iov[0].iov_base = (void *) &xpdu;
178121054Semax		iov[0].iov_len = sizeof(xpdu);
179121054Semax		iov[1].iov_base = (void *) rsp;
180121054Semax		iov[1].iov_len = ss->imtu;
181121054Semax
182124758Semax		do {
183124758Semax			len = readv(ss->s, iov, sizeof(iov)/sizeof(iov[0]));
184124758Semax		} while (len < 0 && errno == EINTR);
185124758Semax
186121054Semax		if (len < 0) {
187121054Semax			ss->error = errno;
188121054Semax			return (-1);
189121054Semax		}
190121054Semax		if (len < sizeof(xpdu)) {
191121054Semax			ss->error = ENOMSG;
192121054Semax			return (-1);
193121054Semax		}
194121054Semax
195121054Semax		xpdu.pdu.tid = ntohs(xpdu.pdu.tid);
196121054Semax		xpdu.pdu.len = ntohs(xpdu.pdu.len);
197121054Semax		xpdu.len = ntohs(xpdu.len);
198121054Semax
199121054Semax		if (xpdu.pdu.pid == SDP_PDU_ERROR_RESPONSE ||
200121054Semax		    xpdu.pdu.tid != ss->tid ||
201126297Semax		    xpdu.pdu.len > len ||
202121054Semax		    xpdu.len > xpdu.pdu.len) {
203121054Semax			ss->error = EIO;
204121054Semax			return (-1);
205121054Semax		}
206121054Semax
207173673Semax		rsp += xpdu.len;
208173673Semax		ss->tid ++;
209173673Semax
210121054Semax		/* Save continuation state (if any) */
211173673Semax		ss->cslen = rsp[0];
212121054Semax		if (ss->cslen > 0) {
213121054Semax			if (ss->cslen > sizeof(ss->cs)) {
214121054Semax				ss->error = ENOBUFS;
215121054Semax				return (-1);
216121054Semax			}
217121054Semax
218173673Semax			memcpy(ss->cs, rsp + 1, ss->cslen);
219121054Semax
220121054Semax			/*
221121054Semax			 * Ensure that we always have ss->imtu bytes
222121054Semax			 * available in the ss->rsp buffer
223121054Semax			 */
224121054Semax
225121054Semax			if (ss->rsp_e - rsp <= ss->imtu) {
226124305Semax				uint32_t	 size, offset;
227121054Semax
228121054Semax				size = ss->rsp_e - ss->rsp + ss->imtu;
229121054Semax				offset = rsp - ss->rsp;
230121054Semax
231121054Semax				rsp_tmp = realloc(ss->rsp, size);
232121054Semax				if (rsp_tmp == NULL) {
233121054Semax					ss->error = ENOMEM;
234121054Semax					return (-1);
235121054Semax				}
236121054Semax
237121054Semax				ss->rsp = rsp_tmp;
238121054Semax				ss->rsp_e = ss->rsp + size;
239121054Semax				rsp = ss->rsp + offset;
240121054Semax			}
241121054Semax		}
242121054Semax	} while (ss->cslen > 0);
243121054Semax
244121054Semax	/*
245121054Semax	 * If we got here then we have completed SDP transaction and now
246121054Semax	 * we must populate attribute values into vp array. At this point
247121054Semax	 * ss->rsp points to the beginning of the response and rsp points
248121054Semax	 * to the end of the response.
249121054Semax	 *
250121054Semax	 * From Bluetooth v1.1 spec page 364
251121054Semax	 *
252121054Semax	 * The AttributeLists is a data element sequence where each element
253121054Semax	 * in turn is a data element sequence representing an attribute list.
254121054Semax	 * Each attribute list contains attribute IDs and attribute values
255121054Semax	 * from one service record. The first element in each attribute list
256121054Semax	 * contains the attribute ID of the first attribute to be returned for
257121054Semax	 * that service record. The second element in each attribute list
258121054Semax	 * contains the corresponding attribute value. Successive pairs of
259121054Semax	 * elements in each attribute list contain additional attribute ID
260121054Semax	 * and value pairs. Only attributes that have non-null values within
261121054Semax	 * the service record and whose attribute IDs were specified in the
262121054Semax	 * SDP_ServiceSearchAttributeRequest are contained in the AttributeLists
263121054Semax	 * Neither an attribute ID nor attribute value is placed in
264121054Semax	 * AttributeLists for attributes in the service record that have no
265121054Semax	 * value. Within each attribute list, the attributes are listed in
266121054Semax	 * ascending order of attribute ID value.
267121054Semax	 */
268121054Semax
269121054Semax	if (vp == NULL)
270121054Semax		goto done;
271121054Semax
272121054Semax	rsp_tmp = ss->rsp;
273121054Semax
274121054Semax	/* Skip the first SEQ */
275138633Semax	SDP_GET8(t, rsp_tmp);
276138633Semax	switch (t) {
277121054Semax	case SDP_DATA_SEQ8:
278121054Semax		SDP_GET8(len, rsp_tmp);
279121054Semax		break;
280121054Semax
281121054Semax	case SDP_DATA_SEQ16:
282121054Semax		SDP_GET16(len, rsp_tmp);
283121054Semax		break;
284121054Semax
285121054Semax	case SDP_DATA_SEQ32:
286121054Semax		SDP_GET32(len, rsp_tmp);
287121054Semax		break;
288121054Semax
289121054Semax	default:
290121054Semax		ss->error = ENOATTR;
291121054Semax		return (-1);
292121054Semax		/* NOT REACHED */
293121054Semax	}
294121054Semax
295121054Semax	for (; rsp_tmp < rsp && vlen > 0; ) {
296121054Semax		/* Get set of attributes for the next record */
297138633Semax		SDP_GET8(t, rsp_tmp);
298138633Semax		switch (t) {
299121054Semax		case SDP_DATA_SEQ8:
300121054Semax			SDP_GET8(len, rsp_tmp);
301121054Semax			break;
302121054Semax
303121054Semax		case SDP_DATA_SEQ16:
304121054Semax			SDP_GET16(len, rsp_tmp);
305121054Semax			break;
306121054Semax
307121054Semax		case SDP_DATA_SEQ32:
308121054Semax			SDP_GET32(len, rsp_tmp);
309121054Semax			break;
310121054Semax
311121054Semax		default:
312121054Semax			ss->error = ENOATTR;
313121054Semax			return (-1);
314121054Semax			/* NOT REACHED */
315121054Semax		}
316121054Semax
317121054Semax		/* Now rsp_tmp points to list of (attr,value) pairs */
318121054Semax		for (; len > 0 && vlen > 0; vp ++, vlen --) {
319121054Semax			/* Attribute */
320138633Semax			SDP_GET8(t, rsp_tmp);
321138633Semax			if (t != SDP_DATA_UINT16) {
322121054Semax				ss->error = ENOATTR;
323121054Semax				return (-1);
324121054Semax			}
325121054Semax			SDP_GET16(vp->attr, rsp_tmp);
326121054Semax
327121054Semax			/* Attribute value */
328121054Semax			switch (rsp_tmp[0]) {
329121054Semax			case SDP_DATA_NIL:
330121054Semax				alen = 0;
331121054Semax				break;
332121054Semax
333121054Semax			case SDP_DATA_UINT8:
334121054Semax			case SDP_DATA_INT8:
335121054Semax			case SDP_DATA_BOOL:
336124305Semax				alen = sizeof(uint8_t);
337121054Semax				break;
338121054Semax
339121054Semax			case SDP_DATA_UINT16:
340121054Semax			case SDP_DATA_INT16:
341121054Semax			case SDP_DATA_UUID16:
342124305Semax				alen = sizeof(uint16_t);
343121054Semax				break;
344121054Semax
345121054Semax			case SDP_DATA_UINT32:
346121054Semax			case SDP_DATA_INT32:
347121054Semax			case SDP_DATA_UUID32:
348124305Semax				alen = sizeof(uint32_t);
349121054Semax				break;
350121054Semax
351121054Semax			case SDP_DATA_UINT64:
352121054Semax			case SDP_DATA_INT64:
353124305Semax				alen = sizeof(uint64_t);
354121054Semax				break;
355121054Semax
356121054Semax			case SDP_DATA_UINT128:
357121054Semax			case SDP_DATA_INT128:
358121054Semax			case SDP_DATA_UUID128:
359124305Semax				alen = sizeof(uint128_t);
360121054Semax				break;
361121054Semax
362121054Semax			case SDP_DATA_STR8:
363121054Semax			case SDP_DATA_URL8:
364121054Semax			case SDP_DATA_SEQ8:
365121054Semax			case SDP_DATA_ALT8:
366124305Semax				alen = rsp_tmp[1] + sizeof(uint8_t);
367121054Semax				break;
368121054Semax
369121054Semax			case SDP_DATA_STR16:
370121054Semax			case SDP_DATA_URL16:
371121054Semax			case SDP_DATA_SEQ16:
372121054Semax			case SDP_DATA_ALT16:
373124305Semax				alen =	  ((uint16_t)rsp_tmp[1] << 8)
374124305Semax					| ((uint16_t)rsp_tmp[2]);
375124305Semax				alen += sizeof(uint16_t);
376121054Semax				break;
377121054Semax
378121054Semax			case SDP_DATA_STR32:
379121054Semax			case SDP_DATA_URL32:
380121054Semax			case SDP_DATA_SEQ32:
381121054Semax			case SDP_DATA_ALT32:
382124305Semax				alen =    ((uint32_t)rsp_tmp[1] << 24)
383124305Semax					| ((uint32_t)rsp_tmp[2] << 16)
384124305Semax					| ((uint32_t)rsp_tmp[3] <<  8)
385124305Semax					| ((uint32_t)rsp_tmp[4]);
386124305Semax				alen += sizeof(uint32_t);
387121054Semax				break;
388121054Semax
389121054Semax			default:
390121054Semax				ss->error = ENOATTR;
391121054Semax				return (-1);
392121054Semax				/* NOT REACHED */
393121054Semax			}
394121054Semax
395124305Semax			alen += sizeof(uint8_t);
396121054Semax
397121054Semax			if (vp->value != NULL) {
398121054Semax				if (alen <= vp->vlen) {
399121054Semax					vp->flags = SDP_ATTR_OK;
400121054Semax					vp->vlen = alen;
401121054Semax				} else
402121054Semax					vp->flags = SDP_ATTR_TRUNCATED;
403121054Semax
404121054Semax				memcpy(vp->value, rsp_tmp, vp->vlen);
405121054Semax			} else
406121054Semax				vp->flags = SDP_ATTR_INVALID;
407121054Semax
408121054Semax			len -=	(
409124305Semax				sizeof(uint8_t) + sizeof(uint16_t) +
410121054Semax				alen
411121054Semax				);
412121054Semax
413121054Semax			rsp_tmp += alen;
414121054Semax		}
415121054Semax	}
416121054Semaxdone:
417121054Semax	ss->error = 0;
418121054Semax
419121054Semax	return (0);
420121054Semax}
421121054Semax
422