472 lines
9.5 KiB
C
472 lines
9.5 KiB
C
/* $NetBSD: sdp_service.c,v 1.4 2010/11/20 12:12:21 plunky Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 2009 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Iain Hibbert.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__RCSID("$NetBSD: sdp_service.c,v 1.4 2010/11/20 12:12:21 plunky Exp $");
|
|
|
|
#include <sys/atomic.h>
|
|
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <sdp.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "sdp-int.h"
|
|
|
|
/*
|
|
* If AttributeIDList is given as NULL, request all attributes.
|
|
* (this is actually const data but we can't declare it const)
|
|
*/
|
|
static uint8_t ail_default[] = { 0x0a, 0x00, 0x00, 0xff, 0xff };
|
|
|
|
/*
|
|
* This provides the maximum size that the response buffer will be
|
|
* allowed to grow to.
|
|
*
|
|
* Default is UINT16_MAX but it can be overridden at runtime.
|
|
*/
|
|
static size_t
|
|
sdp_response_max(void)
|
|
{
|
|
static size_t max = UINT16_MAX;
|
|
static unsigned int check = 1;
|
|
char *env, *ep;
|
|
unsigned long v;
|
|
|
|
while (atomic_swap_uint(&check, 0)) { /* only check env once */
|
|
env = getenv("SDP_RESPONSE_MAX");
|
|
if (env == NULL)
|
|
break;
|
|
|
|
errno = 0;
|
|
v = strtoul(env, &ep, 0);
|
|
if (env[0] == '\0' || *ep != '\0')
|
|
break;
|
|
|
|
if (errno == ERANGE && v == ULONG_MAX)
|
|
break;
|
|
|
|
/* lower limit is arbitrary */
|
|
if (v < UINT8_MAX || v > UINT32_MAX)
|
|
break;
|
|
|
|
max = v;
|
|
}
|
|
|
|
return max;
|
|
}
|
|
|
|
bool
|
|
sdp_service_search(struct sdp_session *ss, const sdp_data_t *ssp,
|
|
uint32_t *id, int *num)
|
|
{
|
|
struct iovec req[5];
|
|
sdp_data_t hdr;
|
|
uint8_t sdata[5], max[2];
|
|
uint8_t *ptr, *end;
|
|
ssize_t len;
|
|
uint16_t total, count, got;
|
|
|
|
/*
|
|
* setup ServiceSearchPattern
|
|
*/
|
|
len = ssp->end - ssp->next;
|
|
if (len < 0 || len > UINT16_MAX) {
|
|
errno = EINVAL;
|
|
return false;
|
|
}
|
|
|
|
hdr.next = sdata;
|
|
hdr.end = sdata + sizeof(sdata) + len;
|
|
sdp_put_seq(&hdr, len);
|
|
req[1].iov_base = sdata;
|
|
req[1].iov_len = hdr.next - sdata;
|
|
|
|
req[2].iov_base = ssp->next;
|
|
req[2].iov_len = len;
|
|
|
|
/*
|
|
* setup MaximumServiceRecordCount
|
|
*/
|
|
if (*num < 0 || *num > UINT16_MAX) {
|
|
errno = EINVAL;
|
|
return false;
|
|
}
|
|
be16enc(max, *num);
|
|
req[3].iov_base = max;
|
|
req[3].iov_len = sizeof(uint16_t);
|
|
|
|
/*
|
|
* clear ContinuationState
|
|
*/
|
|
ss->cs[0] = 0;
|
|
|
|
/*
|
|
* ServiceSearch Transaction
|
|
*/
|
|
got = 0;
|
|
for (;;) {
|
|
/*
|
|
* setup ContinuationState
|
|
*/
|
|
req[4].iov_base = ss->cs;
|
|
req[4].iov_len = ss->cs[0] + 1;
|
|
|
|
if (!_sdp_send_pdu(ss, SDP_PDU_SERVICE_SEARCH_REQUEST,
|
|
req, __arraycount(req)))
|
|
return false;
|
|
|
|
len = _sdp_recv_pdu(ss, SDP_PDU_SERVICE_SEARCH_RESPONSE);
|
|
if (len == -1)
|
|
return false;
|
|
|
|
ptr = ss->ibuf;
|
|
end = ss->ibuf + len;
|
|
|
|
/*
|
|
* extract TotalServiceRecordCount
|
|
*/
|
|
if (ptr + sizeof(uint16_t) > end)
|
|
break;
|
|
|
|
total = be16dec(ptr);
|
|
ptr += sizeof(uint16_t);
|
|
if (total > *num)
|
|
break;
|
|
|
|
/*
|
|
* extract CurrentServiceRecordCount
|
|
*/
|
|
if (ptr + sizeof(uint16_t) > end)
|
|
break;
|
|
|
|
count = be16dec(ptr);
|
|
ptr += sizeof(uint16_t);
|
|
if (got + count > total)
|
|
break;
|
|
|
|
/*
|
|
* extract ServiceRecordHandleList
|
|
*/
|
|
if (ptr + count * sizeof(uint32_t) > end)
|
|
break;
|
|
|
|
while (count-- > 0) {
|
|
id[got++] = be32dec(ptr);
|
|
ptr += sizeof(uint32_t);
|
|
}
|
|
|
|
/*
|
|
* extract ContinuationState
|
|
*/
|
|
if (ptr + 1 > end
|
|
|| ptr[0] > 16
|
|
|| ptr + ptr[0] + 1 != end)
|
|
break;
|
|
|
|
memcpy(ss->cs, ptr, (size_t)(ptr[0] + 1));
|
|
|
|
/*
|
|
* Complete?
|
|
*/
|
|
if (ss->cs[0] == 0) {
|
|
*num = got;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
errno = EIO;
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
sdp_service_attribute(struct sdp_session *ss, uint32_t id,
|
|
const sdp_data_t *ail, sdp_data_t *rsp)
|
|
{
|
|
struct iovec req[6];
|
|
sdp_data_t hdr;
|
|
uint8_t adata[5], handle[4], max[2];
|
|
uint8_t *ptr, *end, *rbuf;
|
|
ssize_t len;
|
|
size_t rlen, count;
|
|
|
|
/*
|
|
* setup ServiceRecordHandle
|
|
*/
|
|
be32enc(handle, id);
|
|
req[1].iov_base = handle;
|
|
req[1].iov_len = sizeof(uint32_t);
|
|
|
|
/*
|
|
* setup MaximumAttributeByteCount
|
|
*/
|
|
be16enc(max, ss->imtu - sizeof(uint16_t) - sizeof(ss->cs));
|
|
req[2].iov_base = max;
|
|
req[2].iov_len = sizeof(uint16_t);
|
|
|
|
/*
|
|
* setup AttributeIDList
|
|
*/
|
|
len = (ail == NULL ? (ssize_t)sizeof(ail_default) : (ail->end - ail->next));
|
|
if (len < 0 || len > UINT16_MAX) {
|
|
errno = EINVAL;
|
|
return false;
|
|
}
|
|
|
|
hdr.next = adata;
|
|
hdr.end = adata + sizeof(adata) + len;
|
|
sdp_put_seq(&hdr, len);
|
|
req[3].iov_base = adata;
|
|
req[3].iov_len = hdr.next - adata;
|
|
|
|
req[4].iov_base = (ail == NULL ? ail_default : ail->next);
|
|
req[4].iov_len = len;
|
|
|
|
/*
|
|
* clear ContinuationState
|
|
*/
|
|
ss->cs[0] = 0;
|
|
|
|
/*
|
|
* ServiceAttribute Transaction
|
|
*/
|
|
rlen = 0;
|
|
for (;;) {
|
|
/*
|
|
* setup ContinuationState
|
|
*/
|
|
req[5].iov_base = ss->cs;
|
|
req[5].iov_len = ss->cs[0] + 1;
|
|
|
|
if (!_sdp_send_pdu(ss, SDP_PDU_SERVICE_ATTRIBUTE_REQUEST,
|
|
req, __arraycount(req)))
|
|
return false;
|
|
|
|
len = _sdp_recv_pdu(ss, SDP_PDU_SERVICE_ATTRIBUTE_RESPONSE);
|
|
if (len == -1)
|
|
return false;
|
|
|
|
ptr = ss->ibuf;
|
|
end = ss->ibuf + len;
|
|
|
|
/*
|
|
* extract AttributeListByteCount
|
|
*/
|
|
if (ptr + sizeof(uint16_t) > end)
|
|
break;
|
|
|
|
count = be16dec(ptr);
|
|
ptr += sizeof(uint16_t);
|
|
if (count == 0 || ptr + count > end)
|
|
break;
|
|
|
|
/*
|
|
* extract AttributeList
|
|
*/
|
|
if (rlen + count > sdp_response_max())
|
|
break;
|
|
|
|
rbuf = realloc(ss->rbuf, rlen + count);
|
|
if (rbuf == NULL)
|
|
return false;
|
|
|
|
ss->rbuf = rbuf;
|
|
memcpy(rbuf + rlen, ptr, count);
|
|
rlen += count;
|
|
ptr += count;
|
|
|
|
/*
|
|
* extract ContinuationState
|
|
*/
|
|
if (ptr + 1 > end
|
|
|| ptr[0] > 16
|
|
|| ptr + ptr[0] + 1 != end)
|
|
break;
|
|
|
|
memcpy(ss->cs, ptr, (size_t)(ptr[0] + 1));
|
|
|
|
/*
|
|
* Complete?
|
|
*/
|
|
if (ss->cs[0] == 0) {
|
|
rsp->next = rbuf;
|
|
rsp->end = rbuf + rlen;
|
|
if (sdp_data_size(rsp) != (ssize_t)rlen
|
|
|| !sdp_data_valid(rsp)
|
|
|| !sdp_get_seq(rsp, rsp))
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
errno = EIO;
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
sdp_service_search_attribute(struct sdp_session *ss, const sdp_data_t *ssp,
|
|
const sdp_data_t *ail, sdp_data_t *rsp)
|
|
{
|
|
struct iovec req[7];
|
|
sdp_data_t hdr;
|
|
uint8_t sdata[5], adata[5], max[2];
|
|
uint8_t *ptr, *end, *rbuf;
|
|
ssize_t len;
|
|
size_t rlen, count;
|
|
|
|
/*
|
|
* setup ServiceSearchPattern
|
|
*/
|
|
len = ssp->end - ssp->next;
|
|
if (len < 0 || len > UINT16_MAX) {
|
|
errno = EINVAL;
|
|
return false;
|
|
}
|
|
|
|
hdr.next = sdata;
|
|
hdr.end = sdata + sizeof(sdata) + len;
|
|
sdp_put_seq(&hdr, len);
|
|
req[1].iov_base = sdata;
|
|
req[1].iov_len = hdr.next - sdata;
|
|
|
|
req[2].iov_base = ssp->next;
|
|
req[2].iov_len = len;
|
|
|
|
/*
|
|
* setup MaximumAttributeByteCount
|
|
*/
|
|
be16enc(max, ss->imtu - sizeof(uint16_t) - sizeof(ss->cs));
|
|
req[3].iov_base = max;
|
|
req[3].iov_len = sizeof(uint16_t);
|
|
|
|
/*
|
|
* setup AttributeIDList
|
|
*/
|
|
len = (ail == NULL ? (ssize_t)sizeof(ail_default) : (ail->end - ail->next));
|
|
if (len < 0 || len > UINT16_MAX) {
|
|
errno = EINVAL;
|
|
return false;
|
|
}
|
|
|
|
hdr.next = adata;
|
|
hdr.end = adata + sizeof(adata) + len;
|
|
sdp_put_seq(&hdr, len);
|
|
req[4].iov_base = adata;
|
|
req[4].iov_len = hdr.next - adata;
|
|
|
|
req[5].iov_base = (ail == NULL ? ail_default : ail->next);
|
|
req[5].iov_len = len;
|
|
|
|
/*
|
|
* clear ContinuationState
|
|
*/
|
|
ss->cs[0] = 0;
|
|
|
|
/*
|
|
* ServiceSearchAttribute Transaction
|
|
*/
|
|
rlen = 0;
|
|
for (;;) {
|
|
/*
|
|
* setup ContinuationState
|
|
*/
|
|
req[6].iov_base = ss->cs;
|
|
req[6].iov_len = ss->cs[0] + 1;
|
|
|
|
if (!_sdp_send_pdu(ss, SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST,
|
|
req, __arraycount(req)))
|
|
return false;
|
|
|
|
len = _sdp_recv_pdu(ss, SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_RESPONSE);
|
|
if (len == -1)
|
|
return false;
|
|
|
|
ptr = ss->ibuf;
|
|
end = ss->ibuf + len;
|
|
|
|
/*
|
|
* extract AttributeListsByteCount
|
|
*/
|
|
if (ptr + sizeof(uint16_t) > end)
|
|
break;
|
|
|
|
count = be16dec(ptr);
|
|
ptr += sizeof(uint16_t);
|
|
if (count == 0 || ptr + count > end)
|
|
break;
|
|
|
|
/*
|
|
* extract AttributeLists
|
|
*/
|
|
if (rlen + count > sdp_response_max())
|
|
break;
|
|
|
|
rbuf = realloc(ss->rbuf, rlen + count);
|
|
if (rbuf == NULL)
|
|
return false;
|
|
|
|
ss->rbuf = rbuf;
|
|
memcpy(rbuf + rlen, ptr, count);
|
|
rlen += count;
|
|
ptr += count;
|
|
|
|
/*
|
|
* extract ContinuationState
|
|
*/
|
|
if (ptr + 1 > end
|
|
|| ptr[0] > 16
|
|
|| ptr + ptr[0] + 1 != end)
|
|
break;
|
|
|
|
memcpy(ss->cs, ptr, (size_t)(ptr[0] + 1));
|
|
|
|
/*
|
|
* Complete?
|
|
*/
|
|
if (ss->cs[0] == 0) {
|
|
rsp->next = rbuf;
|
|
rsp->end = rbuf + rlen;
|
|
if (sdp_data_size(rsp) != (ssize_t)rlen
|
|
|| !sdp_data_valid(rsp)
|
|
|| !sdp_get_seq(rsp, rsp))
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
errno = EIO;
|
|
return false;
|
|
}
|