Home | History | Annotate | Line # | Download | only in sdpd
service.c revision 1.1
      1 /*	$NetBSD: service.c,v 1.1 2009/05/12 10:05:07 plunky Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2009 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Iain Hibbert.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 __RCSID("$NetBSD: service.c,v 1.1 2009/05/12 10:05:07 plunky Exp $");
     34 
     35 #include <bluetooth.h>
     36 #include <sdp.h>
     37 
     38 #include "sdpd.h"
     39 
     40 /*
     41  * This structure is a collection of pointers describing an output
     42  * buffer for sdpd_put_byte(), below. bytes are written at next when
     43  * it falls inside the range [start .. end - 1]
     44  */
     45 typedef struct {
     46 	uint8_t *start;	/* start of buffer window */
     47 	uint8_t	*next;	/* current write position */
     48 	uint8_t *end;	/* end of buffer window */
     49 } sdpd_data_t;
     50 
     51 static bool sdpd_valid_ssp(sdp_data_t *);
     52 static bool sdpd_valid_ail(sdp_data_t *);
     53 static bool sdpd_match_ail(record_t *, sdp_data_t, sdpd_data_t *);
     54 static void sdpd_put_byte(sdpd_data_t *, uint8_t);
     55 static void sdpd_put_attr(sdpd_data_t *, uint16_t, sdp_data_t *);
     56 static void sdpd_open_seq(sdpd_data_t *);
     57 static void sdpd_close_seq(sdpd_data_t *, uint8_t *);
     58 
     59 uint16_t
     60 service_search_request(server_t *srv, int fd)
     61 {
     62 	record_t	*r;
     63 	sdp_data_t	d, s;
     64 	int		max, total, count;
     65 
     66 	d.next = srv->ibuf;
     67 	d.end = srv->ibuf + srv->pdu.len;
     68 
     69 	/*
     70 	 * extract ServiceSearchPattern
     71 	 */
     72 	if (!sdp_get_seq(&d, &s)
     73 	    || !sdpd_valid_ssp(&s))
     74 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
     75 
     76 	/*
     77 	 * extract MaximumServiceRecordCount
     78 	 */
     79 	if (d.next + sizeof(uint16_t) > d.end)
     80 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
     81 
     82 	max = be16dec(d.next);
     83 	d.next += sizeof(uint16_t);
     84 	if (max < 0x0001)
     85 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
     86 
     87 	/*
     88 	 * validate ContinuationState
     89 	 * If none given, this is a new request
     90 	 */
     91 	if (d.next + 1 > d.end
     92 	    || d.next[0] > 16
     93 	    || d.next + 1 + d.next[0] != d.end)
     94 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
     95 
     96 	if (d.next[0] == 0) {
     97 		srv->fdidx[fd].offset = 0;
     98 		db_unselect(srv, fd);
     99 		db_select_ssp(srv, fd, &s);
    100 	} else if (srv->fdidx[fd].offset == 0
    101 	    || d.next[0] != sizeof(uint16_t)
    102 	    || be16dec(d.next + 1) != srv->fdidx[fd].offset)
    103 		return SDP_ERROR_CODE_INVALID_CONTINUATION_STATE;
    104 
    105 	/*
    106 	 * Ready our output buffer. We leave space at the start for
    107 	 * TotalServiceRecordCount and CurrentServiceRecordCount and
    108 	 * at the end for ContinuationState, and we must have space
    109 	 * for at least one ServiceRecordHandle. Then, step through
    110 	 * selected records and write as many handles that will fit
    111 	 * into the data space
    112 	 */
    113 	d.next = srv->obuf + sizeof(uint16_t) + sizeof(uint16_t);
    114 	d.end = srv->obuf + srv->fdidx[fd].omtu - 1 - sizeof(uint16_t);
    115 	count = total = 0;
    116 
    117 	if (d.next + sizeof(uint32_t) > d.end)
    118 		return SDP_ERROR_CODE_INSUFFICIENT_RESOURCES;
    119 
    120 	r = NULL;
    121 	while (db_next(srv, fd, &r) && total < max) {
    122 		if (total >= srv->fdidx[fd].offset
    123 		    && d.next + sizeof(uint32_t) <= d.end) {
    124 			be32enc(d.next, r->handle);
    125 			d.next += sizeof(uint32_t);
    126 			count++;
    127 		}
    128 
    129 		total++;
    130 	}
    131 
    132 	/*
    133 	 * encode TotalServiceRecordCount and CurrentServiceRecordCount
    134 	 */
    135 	be16enc(srv->obuf, total);
    136 	be16enc(srv->obuf + sizeof(uint16_t), count);
    137 
    138 	/*
    139 	 * encode ContinuationState which in this case will be the
    140 	 * number of ServiceRecordHandles already sent.
    141 	 */
    142 	if (r == NULL || total == max) {
    143 		srv->fdidx[fd].offset = 0;
    144 		db_unselect(srv, fd);
    145 		d.next[0] = 0;
    146 		d.next += 1;
    147 	} else {
    148 		srv->fdidx[fd].offset += count;
    149 		d.next[0] = sizeof(uint16_t);
    150 		be16enc(d.next + 1, srv->fdidx[fd].offset);
    151 		d.next += 1 + sizeof(uint16_t);
    152 	}
    153 
    154 	/*
    155 	 * fill in PDU header and we are done
    156 	 */
    157 	srv->pdu.pid = SDP_PDU_SERVICE_SEARCH_RESPONSE;
    158 	srv->pdu.len = d.next - srv->obuf;
    159 	return 0;
    160 }
    161 
    162 uint16_t
    163 service_attribute_request(server_t *srv, int fd)
    164 {
    165 	record_t	*r;
    166 	sdp_data_t	a, d;
    167 	sdpd_data_t	b;
    168 	uint8_t		*tmp;
    169 	uint32_t	handle;
    170 	int		max;
    171 
    172 	d.next = srv->ibuf;
    173 	d.end = srv->ibuf + srv->pdu.len;
    174 
    175 	/*
    176 	 * extract ServiceRecordHandle
    177 	 */
    178 	if (d.next + sizeof(uint32_t) > d.end)
    179 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    180 
    181 	handle = be32dec(d.next);
    182 	d.next += sizeof(uint32_t);
    183 
    184 	/*
    185 	 * extract MaximumAttributeByteCount
    186 	 */
    187 	if (d.next + sizeof(uint16_t) > d.end)
    188 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    189 
    190 	max = be16dec(d.next);
    191 	d.next += sizeof(uint16_t);
    192 	if (max < 0x0007)
    193 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    194 
    195 	/*
    196 	 * extract AttributeIDList
    197 	 */
    198 	if (!sdp_get_seq(&d, &a)
    199 	    || !sdpd_valid_ail(&a))
    200 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    201 
    202 	/*
    203 	 * validate ContinuationState
    204 	 * If none given, this is a new request
    205 	 */
    206 	if (d.next + 1 > d.end
    207 	    || d.next[0] > 16
    208 	    || d.next + 1 + d.next[0] != d.end)
    209 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    210 
    211 	if (d.next[0] == 0) {
    212 		srv->fdidx[fd].offset = 0;
    213 		db_unselect(srv, fd);
    214 		db_select_handle(srv, fd, handle);
    215 	} else if (srv->fdidx[fd].offset == 0
    216 	    || d.next[0] != sizeof(uint16_t)
    217 	    || be16dec(d.next + 1) != srv->fdidx[fd].offset)
    218 		return SDP_ERROR_CODE_INVALID_CONTINUATION_STATE;
    219 
    220 	/*
    221 	 * Set up the buffer window and write pointer, leaving space at
    222 	 * buffer start for AttributeListByteCount and for ContinuationState
    223 	 * at the end
    224 	 */
    225 	b.start = srv->obuf + sizeof(uint16_t);
    226 	b.next = b.start - srv->fdidx[fd].offset;
    227 	b.end = srv->obuf + srv->fdidx[fd].omtu - 1;
    228 	if (b.start + max < b.end)
    229 		b.end = b.start + max;
    230 
    231 	/*
    232 	 * Match the selected record against AttributeIDList, writing
    233 	 * the data to the sparce buffer.
    234 	 */
    235 	r = NULL;
    236 	db_next(srv, fd, &r);
    237 	if (r == NULL)
    238 		return SDP_ERROR_CODE_INVALID_SERVICE_RECORD_HANDLE;
    239 
    240 	sdpd_match_ail(r, a, &b);
    241 
    242 	if (b.next > b.end) {
    243 		/*
    244 		 * b.end is the limit of AttributeList that we are allowed
    245 		 * to send so if we have exceeded that we need to adjust our
    246 		 * response downwards. Recalculate the new cut off to allow
    247 		 * writing the ContinuationState offset and ensure we don't
    248 		 * exceed MaximumAttributeByteCount. Also, make sure that
    249 		 * the continued length is not too short.
    250 		 */
    251 		tmp = b.next;
    252 		b.next = srv->obuf + srv->fdidx[fd].omtu - 1 - sizeof(uint16_t);
    253 		if (b.next > b.end)
    254 			b.next = b.end;
    255 
    256 		if (tmp - b.next < 0x0002)
    257 			b.next = tmp - 0x0002;
    258 
    259 		/* encode AttributeListByteCount */
    260 		be16enc(srv->obuf, (b.next - b.start));
    261 
    262 		/* calculate & append ContinuationState */
    263 		srv->fdidx[fd].offset += (b.next - b.start);
    264 		b.next[0] = sizeof(uint16_t);
    265 		be16enc(b.next + 1, srv->fdidx[fd].offset);
    266 		b.next += 1 + sizeof(uint16_t);
    267 	} else {
    268 		/* encode AttributeListByteCount */
    269 		be16enc(srv->obuf, (b.next - b.start));
    270 
    271 		/* reset & append ContinuationState */
    272 		srv->fdidx[fd].offset = 0;
    273 		db_unselect(srv, fd);
    274 		b.next[0] = 0;
    275 		b.next += 1;
    276 	}
    277 
    278 	/*
    279 	 * fill in PDU header and we are done
    280 	 */
    281 	srv->pdu.pid = SDP_PDU_SERVICE_ATTRIBUTE_RESPONSE;
    282 	srv->pdu.len = b.next - srv->obuf;
    283 	return 0;
    284 }
    285 
    286 uint16_t
    287 service_search_attribute_request(server_t *srv, int fd)
    288 {
    289 	record_t	*r;
    290 	sdpd_data_t	b;
    291 	sdp_data_t	a, d, s;
    292 	uint8_t		*tmp;
    293 	int		max;
    294 
    295 	d.next = srv->ibuf;
    296 	d.end = srv->ibuf + srv->pdu.len;
    297 
    298 	/*
    299 	 * extract ServiceSearchPattern
    300 	 */
    301 	if (!sdp_get_seq(&d, &s)
    302 	    || !sdpd_valid_ssp(&s))
    303 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    304 
    305 	/*
    306 	 * extract MaximumAttributeByteCount
    307 	 */
    308 	if (d.next + sizeof(uint16_t) > d.end)
    309 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    310 
    311 	max = be16dec(d.next);
    312 	d.next += sizeof(uint16_t);
    313 	if (max < 0x0007)
    314 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    315 
    316 	/*
    317 	 * extract AttributeIDList
    318 	 */
    319 	if (!sdp_get_seq(&d, &a)
    320 	    || !sdpd_valid_ail(&a))
    321 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    322 
    323 	/*
    324 	 * validate ContinuationState
    325 	 * If none given, this is a new request
    326 	 */
    327 	if (d.next + 1 > d.end
    328 	    || d.next[0] > 16
    329 	    || d.next + 1 + d.next[0] != d.end)
    330 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    331 
    332 	if (d.next[0] == 0) {
    333 		srv->fdidx[fd].offset = 0;
    334 		db_unselect(srv, fd);
    335 		db_select_ssp(srv, fd, &s);
    336 	} else if (srv->fdidx[fd].offset == 0
    337 	    || d.next[0] != sizeof(uint16_t)
    338 	    || be16dec(d.next + 1) != srv->fdidx[fd].offset)
    339 		return SDP_ERROR_CODE_INVALID_CONTINUATION_STATE;
    340 
    341 	/*
    342 	 * Set up the buffer window and write pointer, leaving space at
    343 	 * buffer start for AttributeListByteCount and for ContinuationState
    344 	 * at the end.
    345 	 */
    346 	b.start = srv->obuf + sizeof(uint16_t);
    347 	b.end = srv->obuf + srv->fdidx[fd].omtu - 1;
    348 	b.next = b.start - srv->fdidx[fd].offset;
    349 	if (b.start + max < b.end)
    350 		b.end = b.start + max;
    351 
    352 	/*
    353 	 * match all selected records against the AttributeIDList,
    354 	 * wrapping the whole in a sequence. Where a record does
    355 	 * not match any attributes, delete the empty sequence.
    356 	 */
    357 	sdpd_open_seq(&b);
    358 
    359 	r = NULL;
    360 	while (db_next(srv, fd, &r)) {
    361 		tmp = b.next;
    362 		if (!sdpd_match_ail(r, a, &b))
    363 			b.next = tmp;
    364 	}
    365 
    366 	sdpd_close_seq(&b, b.start - srv->fdidx[fd].offset);
    367 
    368 	if (b.next > b.end) {
    369 		/*
    370 		 * b.end is the limit of AttributeLists that we are allowed
    371 		 * to send so if we have exceeded that we need to adjust our
    372 		 * response downwards. Recalculate the new cut off to allow
    373 		 * writing the ContinuationState offset and ensure we don't
    374 		 * exceed MaximumAttributeByteCount. Also, make sure that
    375 		 * the continued length is not too short.
    376 		 */
    377 		tmp = b.next;
    378 		b.next = srv->obuf + srv->fdidx[fd].omtu - 1 - sizeof(uint16_t);
    379 		if (b.next > b.end)
    380 			b.next = b.end;
    381 
    382 		if (tmp - b.next < 0x0002)
    383 			b.next = tmp - 0x0002;
    384 
    385 		/* encode AttributeListsByteCount */
    386 		be16enc(srv->obuf, (b.next - b.start));
    387 
    388 		/* calculate & append ContinuationState */
    389 		srv->fdidx[fd].offset += (b.next - b.start);
    390 		b.next[0] = sizeof(uint16_t);
    391 		be16enc(b.next + 1, srv->fdidx[fd].offset);
    392 		b.next += 1 + sizeof(uint16_t);
    393 	} else {
    394 		/* encode AttributeListsByteCount */
    395 		be16enc(srv->obuf, (b.next - b.start));
    396 
    397 		/* reset & append ContinuationState */
    398 		srv->fdidx[fd].offset = 0;
    399 		db_unselect(srv, fd);
    400 		b.next[0] = 0;
    401 		b.next += 1;
    402 	}
    403 
    404 	/*
    405 	 * fill in PDU header and we are done
    406 	 */
    407 	srv->pdu.pid = SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_RESPONSE;
    408 	srv->pdu.len = b.next - srv->obuf;
    409 	return 0;
    410 }
    411 
    412 /*
    413  * validate ServiceSearchPattern
    414  *
    415  * The SerivceSearchPattern is a list of data elements, where each element
    416  * is a UUID. The list must contain at least one UUID and the maximum number
    417  * of UUIDs is 12
    418  */
    419 static bool
    420 sdpd_valid_ssp(sdp_data_t *ssp)
    421 {
    422 	sdp_data_t	s = *ssp;
    423 	uuid_t		u;
    424 	int		n;
    425 
    426 	if (!sdp_data_valid(&s))
    427 		return false;
    428 
    429 	n = 0;
    430 	while (sdp_get_uuid(&s, &u))
    431 		n++;
    432 
    433 	if (n < 1 || n > 12 || s.next != s.end)
    434 		return false;
    435 
    436 	return true;
    437 }
    438 
    439 /*
    440  * validate AttributeIDList
    441  *
    442  * The AttributeIDList is a list of data elements, where each element is
    443  * either an attribute ID encoded as an unsigned 16-bit integer or a range
    444  * of attribute IDs encoded as an unsigned 32-bit integer where the high
    445  * order 16-bits are the beginning of the range and the low order 16-bits
    446  * are the ending
    447  *
    448  * The attrbute IDs should be listed in ascending order without duplication
    449  * of any attribute ID values but we don't worry about that, since if the
    450  * remote party messes up, their results will be messed up
    451  */
    452 static bool
    453 sdpd_valid_ail(sdp_data_t *ail)
    454 {
    455 	sdp_data_t	a = *ail;
    456 	sdp_data_t	d;
    457 
    458 	if (!sdp_data_valid(&a))
    459 		return false;
    460 
    461 	while (sdp_get_data(&a, &d)) {
    462 		if (sdp_data_type(&d) != SDP_DATA_UINT16
    463 		    && sdp_data_type(&d) != SDP_DATA_UINT32)
    464 			return false;
    465 	}
    466 
    467 	return true;
    468 }
    469 
    470 /*
    471  * compare attributes in the ServiceRecord with the AttributeIDList
    472  * and copy any matches to a sequence in the output buffer.
    473  */
    474 static bool
    475 sdpd_match_ail(record_t *rec, sdp_data_t ail, sdpd_data_t *buf)
    476 {
    477 	sdp_data_t	r, v;
    478 	uint16_t	a;
    479 	uintmax_t	ui;
    480 	uint8_t		*f;
    481 	int		lo, hi;
    482 	bool		rv;
    483 
    484 	r = rec->data;
    485 	f = buf->next;
    486 	lo = hi = -1;
    487 	rv = false;
    488 
    489 	sdpd_open_seq(buf);
    490 
    491 	while (sdp_get_attr(&r, &a, &v)) {
    492 		while (a > hi) {
    493 			if (ail.next == ail.end)
    494 				goto done;
    495 
    496 			if (sdp_data_type(&ail) == SDP_DATA_UINT16) {
    497 				sdp_get_uint(&ail, &ui);
    498 				lo = hi = ui;
    499 			} else {
    500 				sdp_get_uint(&ail, &ui);
    501 				lo = (uint16_t)(ui >> 16);
    502 				hi = (uint16_t)(ui);
    503 			}
    504 		}
    505 
    506 		if (a < lo)
    507 			continue;
    508 
    509 		sdpd_put_attr(buf, a, &v);
    510 		rv = true;
    511 	}
    512 
    513 done:
    514 	sdpd_close_seq(buf, f);
    515 	return rv;
    516 }
    517 
    518 /*
    519  * output data. We only actually store the bytes when the
    520  * pointer is within the valid window.
    521  */
    522 static void
    523 sdpd_put_byte(sdpd_data_t *buf, uint8_t byte)
    524 {
    525 
    526 	if (buf->next >= buf->start && buf->next < buf->end)
    527 		buf->next[0] = byte;
    528 
    529 	buf->next++;
    530 }
    531 
    532 static void
    533 sdpd_put_attr(sdpd_data_t *buf, uint16_t attr, sdp_data_t *data)
    534 {
    535 	uint8_t	*p;
    536 
    537 	sdpd_put_byte(buf, SDP_DATA_UINT16);
    538 	sdpd_put_byte(buf, (uint8_t)(attr >> 8));
    539 	sdpd_put_byte(buf, (uint8_t)(attr));
    540 
    541 	for (p = data->next; p < data->end; p++)
    542 		sdpd_put_byte(buf, *p);
    543 }
    544 
    545 /*
    546  * Since we always use a seq16 and never check the length, we will send
    547  * an invalid header if it grows too large. We could always use a seq32
    548  * but the chance of overflow is small so ignore it for now.
    549  */
    550 static void
    551 sdpd_open_seq(sdpd_data_t *buf)
    552 {
    553 
    554 	buf->next += 3;
    555 }
    556 
    557 static void
    558 sdpd_close_seq(sdpd_data_t *buf, uint8_t *first)
    559 {
    560 	uint8_t	*next;
    561 	size_t	len;
    562 
    563 	next = buf->next;
    564 	buf->next = first;
    565 	len = next - first - 3;
    566 
    567 	sdpd_put_byte(buf, SDP_DATA_SEQ16);
    568 	sdpd_put_byte(buf, 0xff & (len >> 8));
    569 	sdpd_put_byte(buf, 0xff & (len >> 0));
    570 	buf->next = next;
    571 }
    572