Home | History | Annotate | Line # | Download | only in sdpd
      1 /*	$NetBSD: service.c,v 1.5 2024/02/05 21:46:07 andvar 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.5 2024/02/05 21:46:07 andvar 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 	log_debug("ServiceSearchRequest by client on fd#%d", fd);
     67 
     68 	d.next = srv->ibuf;
     69 	d.end = srv->ibuf + srv->pdu.len;
     70 
     71 	/*
     72 	 * extract ServiceSearchPattern
     73 	 */
     74 	if (!sdp_get_seq(&d, &s)
     75 	    || !sdpd_valid_ssp(&s))
     76 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
     77 
     78 	/*
     79 	 * extract MaximumServiceRecordCount
     80 	 */
     81 	if (d.next + sizeof(uint16_t) > d.end)
     82 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
     83 
     84 	max = be16dec(d.next);
     85 	d.next += sizeof(uint16_t);
     86 	if (max < 0x0001)
     87 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
     88 
     89 	/*
     90 	 * validate ContinuationState
     91 	 * If none given, this is a new request
     92 	 */
     93 	if (d.next + 1 > d.end
     94 	    || d.next[0] > 16
     95 	    || d.next + 1 + d.next[0] != d.end)
     96 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
     97 
     98 	if (d.next[0] == 0) {
     99 		srv->fdidx[fd].offset = 0;
    100 		db_unselect(srv, fd);
    101 		db_select_ssp(srv, fd, &s);
    102 	} else if (srv->fdidx[fd].offset == 0
    103 	    || d.next[0] != sizeof(uint16_t)
    104 	    || be16dec(d.next + 1) != srv->fdidx[fd].offset)
    105 		return SDP_ERROR_CODE_INVALID_CONTINUATION_STATE;
    106 
    107 	/*
    108 	 * Ready our output buffer. We leave space at the start for
    109 	 * TotalServiceRecordCount and CurrentServiceRecordCount and
    110 	 * at the end for ContinuationState, and we must have space
    111 	 * for at least one ServiceRecordHandle. Then, step through
    112 	 * selected records and write as many handles that will fit
    113 	 * into the data space
    114 	 */
    115 	d.next = srv->obuf + sizeof(uint16_t) + sizeof(uint16_t);
    116 	d.end = srv->obuf + srv->fdidx[fd].omtu - 1 - sizeof(uint16_t);
    117 	count = total = 0;
    118 
    119 	if (d.next + sizeof(uint32_t) > d.end)
    120 		return SDP_ERROR_CODE_INSUFFICIENT_RESOURCES;
    121 
    122 	r = NULL;
    123 	while (db_next(srv, fd, &r) && total < max) {
    124 		if (total >= srv->fdidx[fd].offset
    125 		    && d.next + sizeof(uint32_t) <= d.end) {
    126 			be32enc(d.next, r->handle);
    127 			d.next += sizeof(uint32_t);
    128 			count++;
    129 		}
    130 
    131 		total++;
    132 	}
    133 
    134 	/*
    135 	 * encode TotalServiceRecordCount and CurrentServiceRecordCount
    136 	 */
    137 	be16enc(srv->obuf, total);
    138 	be16enc(srv->obuf + sizeof(uint16_t), count);
    139 
    140 	/*
    141 	 * encode ContinuationState which in this case will be the
    142 	 * number of ServiceRecordHandles already sent.
    143 	 */
    144 	if (r == NULL || total == max) {
    145 		srv->fdidx[fd].offset = 0;
    146 		db_unselect(srv, fd);
    147 		d.next[0] = 0;
    148 		d.next += 1;
    149 	} else {
    150 		srv->fdidx[fd].offset += count;
    151 		d.next[0] = sizeof(uint16_t);
    152 		be16enc(d.next + 1, srv->fdidx[fd].offset);
    153 		d.next += 1 + sizeof(uint16_t);
    154 	}
    155 
    156 	/*
    157 	 * fill in PDU header and we are done
    158 	 */
    159 	srv->pdu.pid = SDP_PDU_SERVICE_SEARCH_RESPONSE;
    160 	srv->pdu.len = d.next - srv->obuf;
    161 	return 0;
    162 }
    163 
    164 uint16_t
    165 service_attribute_request(server_t *srv, int fd)
    166 {
    167 	record_t	*r;
    168 	sdp_data_t	a, d;
    169 	sdpd_data_t	b;
    170 	uint8_t		*tmp;
    171 	uint32_t	handle;
    172 	int		max;
    173 
    174 	log_debug("ServiceAttributeRequest by client on fd#%d", fd);
    175 
    176 	d.next = srv->ibuf;
    177 	d.end = srv->ibuf + srv->pdu.len;
    178 
    179 	/*
    180 	 * extract ServiceRecordHandle
    181 	 */
    182 	if (d.next + sizeof(uint32_t) > d.end)
    183 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    184 
    185 	handle = be32dec(d.next);
    186 	d.next += sizeof(uint32_t);
    187 
    188 	/*
    189 	 * extract MaximumAttributeByteCount
    190 	 */
    191 	if (d.next + sizeof(uint16_t) > d.end)
    192 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    193 
    194 	max = be16dec(d.next);
    195 	d.next += sizeof(uint16_t);
    196 	if (max < 0x0007)
    197 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    198 
    199 	/*
    200 	 * extract AttributeIDList
    201 	 */
    202 	if (!sdp_get_seq(&d, &a)
    203 	    || !sdpd_valid_ail(&a))
    204 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    205 
    206 	/*
    207 	 * validate ContinuationState
    208 	 * If none given, this is a new request
    209 	 */
    210 	if (d.next + 1 > d.end
    211 	    || d.next[0] > 16
    212 	    || d.next + 1 + d.next[0] != d.end)
    213 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    214 
    215 	if (d.next[0] == 0) {
    216 		srv->fdidx[fd].offset = 0;
    217 		db_unselect(srv, fd);
    218 		db_select_handle(srv, fd, handle);
    219 	} else if (srv->fdidx[fd].offset == 0
    220 	    || d.next[0] != sizeof(uint16_t)
    221 	    || be16dec(d.next + 1) != srv->fdidx[fd].offset)
    222 		return SDP_ERROR_CODE_INVALID_CONTINUATION_STATE;
    223 
    224 	/*
    225 	 * Set up the buffer window and write pointer, leaving space at
    226 	 * buffer start for AttributeListByteCount and for ContinuationState
    227 	 * at the end
    228 	 */
    229 	b.start = srv->obuf + sizeof(uint16_t);
    230 	b.next = b.start - srv->fdidx[fd].offset;
    231 	b.end = srv->obuf + srv->fdidx[fd].omtu - 1;
    232 	if (b.start + max < b.end)
    233 		b.end = b.start + max;
    234 
    235 	/*
    236 	 * Match the selected record against AttributeIDList, writing
    237 	 * the data to the sparse buffer.
    238 	 */
    239 	r = NULL;
    240 	db_next(srv, fd, &r);
    241 	if (r == NULL)
    242 		return SDP_ERROR_CODE_INVALID_SERVICE_RECORD_HANDLE;
    243 
    244 	sdpd_match_ail(r, a, &b);
    245 
    246 	if (b.next > b.end) {
    247 		/*
    248 		 * b.end is the limit of AttributeList that we are allowed
    249 		 * to send so if we have exceeded that we need to adjust our
    250 		 * response downwards. Recalculate the new cut off to allow
    251 		 * writing the ContinuationState offset and ensure we don't
    252 		 * exceed MaximumAttributeByteCount. Also, make sure that
    253 		 * the continued length is not too short.
    254 		 */
    255 		tmp = b.next;
    256 		b.next = srv->obuf + srv->fdidx[fd].omtu - 1 - sizeof(uint16_t);
    257 		if (b.next > b.end)
    258 			b.next = b.end;
    259 
    260 		if (tmp - b.next < 0x0002)
    261 			b.next = tmp - 0x0002;
    262 
    263 		/* encode AttributeListByteCount */
    264 		be16enc(srv->obuf, (b.next - b.start));
    265 
    266 		/* calculate & append ContinuationState */
    267 		srv->fdidx[fd].offset += (b.next - b.start);
    268 		b.next[0] = sizeof(uint16_t);
    269 		be16enc(b.next + 1, srv->fdidx[fd].offset);
    270 		b.next += 1 + sizeof(uint16_t);
    271 	} else {
    272 		/* encode AttributeListByteCount */
    273 		be16enc(srv->obuf, (b.next - b.start));
    274 
    275 		/* reset & append ContinuationState */
    276 		srv->fdidx[fd].offset = 0;
    277 		db_unselect(srv, fd);
    278 		b.next[0] = 0;
    279 		b.next += 1;
    280 	}
    281 
    282 	/*
    283 	 * fill in PDU header and we are done
    284 	 */
    285 	srv->pdu.pid = SDP_PDU_SERVICE_ATTRIBUTE_RESPONSE;
    286 	srv->pdu.len = b.next - srv->obuf;
    287 	return 0;
    288 }
    289 
    290 uint16_t
    291 service_search_attribute_request(server_t *srv, int fd)
    292 {
    293 	record_t	*r;
    294 	sdpd_data_t	b;
    295 	sdp_data_t	a, d, s;
    296 	uint8_t		*tmp;
    297 	int		max;
    298 
    299 	log_debug("ServiceSearchAttributeRequest by client on fd#%d", fd);
    300 
    301 	d.next = srv->ibuf;
    302 	d.end = srv->ibuf + srv->pdu.len;
    303 
    304 	/*
    305 	 * extract ServiceSearchPattern
    306 	 */
    307 	if (!sdp_get_seq(&d, &s)
    308 	    || !sdpd_valid_ssp(&s))
    309 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    310 
    311 	/*
    312 	 * extract MaximumAttributeByteCount
    313 	 */
    314 	if (d.next + sizeof(uint16_t) > d.end)
    315 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    316 
    317 	max = be16dec(d.next);
    318 	d.next += sizeof(uint16_t);
    319 	if (max < 0x0007)
    320 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    321 
    322 	/*
    323 	 * extract AttributeIDList
    324 	 */
    325 	if (!sdp_get_seq(&d, &a)
    326 	    || !sdpd_valid_ail(&a))
    327 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    328 
    329 	/*
    330 	 * validate ContinuationState
    331 	 * If none given, this is a new request
    332 	 */
    333 	if (d.next + 1 > d.end
    334 	    || d.next[0] > 16
    335 	    || d.next + 1 + d.next[0] != d.end)
    336 		return SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    337 
    338 	if (d.next[0] == 0) {
    339 		srv->fdidx[fd].offset = 0;
    340 		db_unselect(srv, fd);
    341 		db_select_ssp(srv, fd, &s);
    342 	} else if (srv->fdidx[fd].offset == 0
    343 	    || d.next[0] != sizeof(uint16_t)
    344 	    || be16dec(d.next + 1) != srv->fdidx[fd].offset)
    345 		return SDP_ERROR_CODE_INVALID_CONTINUATION_STATE;
    346 
    347 	/*
    348 	 * Set up the buffer window and write pointer, leaving space at
    349 	 * buffer start for AttributeListByteCount and for ContinuationState
    350 	 * at the end.
    351 	 */
    352 	b.start = srv->obuf + sizeof(uint16_t);
    353 	b.end = srv->obuf + srv->fdidx[fd].omtu - 1;
    354 	b.next = b.start - srv->fdidx[fd].offset;
    355 	if (b.start + max < b.end)
    356 		b.end = b.start + max;
    357 
    358 	/*
    359 	 * match all selected records against the AttributeIDList,
    360 	 * wrapping the whole in a sequence. Where a record does
    361 	 * not match any attributes, delete the empty sequence.
    362 	 */
    363 	sdpd_open_seq(&b);
    364 
    365 	r = NULL;
    366 	while (db_next(srv, fd, &r)) {
    367 		tmp = b.next;
    368 		if (!sdpd_match_ail(r, a, &b))
    369 			b.next = tmp;
    370 	}
    371 
    372 	sdpd_close_seq(&b, b.start - srv->fdidx[fd].offset);
    373 
    374 	if (b.next > b.end) {
    375 		/*
    376 		 * b.end is the limit of AttributeLists that we are allowed
    377 		 * to send so if we have exceeded that we need to adjust our
    378 		 * response downwards. Recalculate the new cut off to allow
    379 		 * writing the ContinuationState offset and ensure we don't
    380 		 * exceed MaximumAttributeByteCount. Also, make sure that
    381 		 * the continued length is not too short.
    382 		 */
    383 		tmp = b.next;
    384 		b.next = srv->obuf + srv->fdidx[fd].omtu - 1 - sizeof(uint16_t);
    385 		if (b.next > b.end)
    386 			b.next = b.end;
    387 
    388 		if (tmp - b.next < 0x0002)
    389 			b.next = tmp - 0x0002;
    390 
    391 		/* encode AttributeListsByteCount */
    392 		be16enc(srv->obuf, (b.next - b.start));
    393 
    394 		/* calculate & append ContinuationState */
    395 		srv->fdidx[fd].offset += (b.next - b.start);
    396 		b.next[0] = sizeof(uint16_t);
    397 		be16enc(b.next + 1, srv->fdidx[fd].offset);
    398 		b.next += 1 + sizeof(uint16_t);
    399 	} else {
    400 		/* encode AttributeListsByteCount */
    401 		be16enc(srv->obuf, (b.next - b.start));
    402 
    403 		/* reset & append ContinuationState */
    404 		srv->fdidx[fd].offset = 0;
    405 		db_unselect(srv, fd);
    406 		b.next[0] = 0;
    407 		b.next += 1;
    408 	}
    409 
    410 	/*
    411 	 * fill in PDU header and we are done
    412 	 */
    413 	srv->pdu.pid = SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_RESPONSE;
    414 	srv->pdu.len = b.next - srv->obuf;
    415 	return 0;
    416 }
    417 
    418 /*
    419  * validate ServiceSearchPattern
    420  *
    421  * The ServiceSearchPattern is a list of data elements, where each element
    422  * is a UUID. The list must contain at least one UUID and the maximum number
    423  * of UUIDs is 12
    424  */
    425 static bool
    426 sdpd_valid_ssp(sdp_data_t *ssp)
    427 {
    428 	sdp_data_t	s = *ssp;
    429 	uuid_t		u;
    430 	int		n;
    431 
    432 	if (!sdp_data_valid(&s))
    433 		return false;
    434 
    435 	n = 0;
    436 	while (sdp_get_uuid(&s, &u))
    437 		n++;
    438 
    439 	if (n < 1 || n > 12 || s.next != s.end)
    440 		return false;
    441 
    442 	return true;
    443 }
    444 
    445 /*
    446  * validate AttributeIDList
    447  *
    448  * The AttributeIDList is a list of data elements, where each element is
    449  * either an attribute ID encoded as an unsigned 16-bit integer or a range
    450  * of attribute IDs encoded as an unsigned 32-bit integer where the high
    451  * order 16-bits are the beginning of the range and the low order 16-bits
    452  * are the ending
    453  *
    454  * The attribute IDs should be listed in ascending order without duplication
    455  * of any attribute ID values but we don't worry about that, since if the
    456  * remote party messes up, their results will be messed up
    457  */
    458 static bool
    459 sdpd_valid_ail(sdp_data_t *ail)
    460 {
    461 	sdp_data_t	a = *ail;
    462 	sdp_data_t	d;
    463 
    464 	if (!sdp_data_valid(&a))
    465 		return false;
    466 
    467 	while (sdp_get_data(&a, &d)) {
    468 		if (sdp_data_type(&d) != SDP_DATA_UINT16
    469 		    && sdp_data_type(&d) != SDP_DATA_UINT32)
    470 			return false;
    471 	}
    472 
    473 	return true;
    474 }
    475 
    476 /*
    477  * compare attributes in the ServiceRecord with the AttributeIDList
    478  * and copy any matches to a sequence in the output buffer.
    479  */
    480 static bool
    481 sdpd_match_ail(record_t *rec, sdp_data_t ail, sdpd_data_t *buf)
    482 {
    483 	sdp_data_t	r, v;
    484 	uint16_t	a;
    485 	uintmax_t	ui;
    486 	uint8_t		*f;
    487 	int		lo, hi;
    488 	bool		rv;
    489 
    490 	r = rec->data;
    491 	f = buf->next;
    492 	lo = hi = -1;
    493 	rv = false;
    494 
    495 	sdpd_open_seq(buf);
    496 
    497 	while (sdp_get_attr(&r, &a, &v)) {
    498 		while (a > hi) {
    499 			if (ail.next == ail.end)
    500 				goto done;
    501 
    502 			if (sdp_data_type(&ail) == SDP_DATA_UINT16) {
    503 				sdp_get_uint(&ail, &ui);
    504 				lo = hi = ui;
    505 			} else {
    506 				sdp_get_uint(&ail, &ui);
    507 				lo = (uint16_t)(ui >> 16);
    508 				hi = (uint16_t)(ui);
    509 			}
    510 		}
    511 
    512 		if (a < lo)
    513 			continue;
    514 
    515 		sdpd_put_attr(buf, a, &v);
    516 		rv = true;
    517 	}
    518 
    519 done:
    520 	sdpd_close_seq(buf, f);
    521 	return rv;
    522 }
    523 
    524 /*
    525  * output data. We only actually store the bytes when the
    526  * pointer is within the valid window.
    527  */
    528 static void
    529 sdpd_put_byte(sdpd_data_t *buf, uint8_t byte)
    530 {
    531 
    532 	if (buf->next >= buf->start && buf->next < buf->end)
    533 		buf->next[0] = byte;
    534 
    535 	buf->next++;
    536 }
    537 
    538 static void
    539 sdpd_put_attr(sdpd_data_t *buf, uint16_t attr, sdp_data_t *data)
    540 {
    541 	uint8_t	*p;
    542 
    543 	sdpd_put_byte(buf, SDP_DATA_UINT16);
    544 	sdpd_put_byte(buf, (uint8_t)(attr >> 8));
    545 	sdpd_put_byte(buf, (uint8_t)(attr));
    546 
    547 	for (p = data->next; p < data->end; p++)
    548 		sdpd_put_byte(buf, *p);
    549 }
    550 
    551 /*
    552  * Since we always use a seq16 and never check the length, we will send
    553  * an invalid header if it grows too large. We could always use a seq32
    554  * but the chance of overflow is small so ignore it for now.
    555  */
    556 static void
    557 sdpd_open_seq(sdpd_data_t *buf)
    558 {
    559 
    560 	buf->next += 3;
    561 }
    562 
    563 static void
    564 sdpd_close_seq(sdpd_data_t *buf, uint8_t *first)
    565 {
    566 	uint8_t	*next;
    567 	size_t	len;
    568 
    569 	next = buf->next;
    570 	buf->next = first;
    571 	len = next - first - 3;
    572 
    573 	sdpd_put_byte(buf, SDP_DATA_SEQ16);
    574 	sdpd_put_byte(buf, 0xff & (len >> 8));
    575 	sdpd_put_byte(buf, 0xff & (len >> 0));
    576 	buf->next = next;
    577 }
    578