diff options
author | 2008-11-26 21:48:30 +0000 | |
---|---|---|
committer | 2008-11-26 21:48:30 +0000 | |
commit | c4173fe87551318a9c49d9b369af5b08f1aef53c (patch) | |
tree | f9acd5df2e81d44fbf4f60e808f859a5c00bc438 | |
parent | only the pool_get() needs to be spl protected; ok claudio dlg (diff) | |
download | wireguard-openbsd-c4173fe87551318a9c49d9b369af5b08f1aef53c.tar.xz wireguard-openbsd-c4173fe87551318a9c49d9b369af5b08f1aef53c.zip |
SDP works, but it needs to be redone (and non-blocking)
With this in bt.conf, the HID driver should work:
# Enable all interfaces.
interface * "OpenBSD BT"
# Attach a keyboard and/or mouse, or a mobile phone emulating such things.
attach 00:02:76:08:1c:37 type hid pin "0000"
Then start btd and run btctl -f bt.conf.
-rw-r--r-- | usr.sbin/btd/Makefile | 5 | ||||
-rw-r--r-- | usr.sbin/btd/bt.c | 50 | ||||
-rw-r--r-- | usr.sbin/btd/btd.h | 6 | ||||
-rw-r--r-- | usr.sbin/btd/devinfo.c | 12 | ||||
-rw-r--r-- | usr.sbin/btd/hci.c | 5 | ||||
-rw-r--r-- | usr.sbin/btd/sdp.c | 226 | ||||
-rw-r--r-- | usr.sbin/btd/sdp.h | 564 | ||||
-rw-r--r-- | usr.sbin/btd/sdp_query.c | 730 | ||||
-rw-r--r-- | usr.sbin/btd/sdp_search.c | 422 |
9 files changed, 1995 insertions, 25 deletions
diff --git a/usr.sbin/btd/Makefile b/usr.sbin/btd/Makefile index 35ca5020fb8..0a9cf592838 100644 --- a/usr.sbin/btd/Makefile +++ b/usr.sbin/btd/Makefile @@ -1,8 +1,9 @@ -# $OpenBSD: Makefile,v 1.4 2008/11/26 15:32:56 uwe Exp $ +# $OpenBSD: Makefile,v 1.5 2008/11/26 21:48:30 uwe Exp $ PROG= btd SRCS= atomicio.c bt.c bt_subr.c btd.c conf.c control.c db.c \ - devinfo.c fdpass.c hci.c log.c sdp.c util.c + devinfo.c fdpass.c hci.c log.c sdp.c sdp_query.c \ + sdp_search.c util.c NOMAN= LDADD+= -levent -lusbhid diff --git a/usr.sbin/btd/bt.c b/usr.sbin/btd/bt.c index 57da5e3ee4c..7ea19bcbb97 100644 --- a/usr.sbin/btd/bt.c +++ b/usr.sbin/btd/bt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bt.c,v 1.4 2008/11/26 15:32:56 uwe Exp $ */ +/* $OpenBSD: bt.c,v 1.5 2008/11/26 21:48:30 uwe Exp $ */ /* * Copyright (c) 2008 Uwe Stuehler <uwe@openbsd.org> @@ -160,7 +160,7 @@ bt_find_inquiry_interface(struct btd *env) { struct bt_interface *iface; - TAILQ_FOREACH(iface, &env->interfaces, entry) { + TAILQ_FOREACH_REVERSE(iface, &env->interfaces, interfaces, entry) { if (iface->physif != NULL) return iface; } @@ -173,8 +173,7 @@ bt_find_attach_interface(struct bt_device *btdev) { struct bt_interface *iface; - TAILQ_FOREACH_REVERSE(iface, &btdev->env->interfaces, - interfaces, entry) { + TAILQ_FOREACH(iface, &btdev->env->interfaces, entry) { if (iface->physif != NULL) return iface; } @@ -188,6 +187,8 @@ bt_devices_changed(void) struct timeval tv; memset(&tv, 0, sizeof(tv)); + tv.tv_sec = 1; + evtimer_add(&ev_check_devices, &tv); } @@ -198,7 +199,8 @@ bt_check_devices(int fd, short evflags, void *arg) struct bt_device *btdev; for (btdev = TAILQ_FIRST(&env->devices); btdev != NULL;) { - if ((btdev->flags & (BTDF_ATTACH|BTDF_ATTACH_DONE)) == + if (!bdaddr_same(&btdev->addr, BDADDR_ANY) && + (btdev->flags & (BTDF_ATTACH|BTDF_ATTACH_DONE)) == BTDF_ATTACH_DONE) { if (bt_device_detach(btdev) < 0) { btdev = TAILQ_NEXT(btdev, entry); @@ -219,7 +221,8 @@ bt_check_devices(int fd, short evflags, void *arg) continue; } - if ((btdev->flags & (BTDF_ATTACH|BTDF_ATTACH_DONE)) == + if (!bdaddr_same(&btdev->addr, BDADDR_ANY) && + (btdev->flags & (BTDF_ATTACH|BTDF_ATTACH_DONE)) == BTDF_ATTACH) { if (bt_device_attach(btdev) < 0) { btdev = TAILQ_NEXT(btdev, entry); @@ -245,11 +248,32 @@ bt_get_devinfo(struct bt_device *btdev) if (res < 0) return -1; else if (res != 0) { - /* try sdp query */ - return 0; + struct bt_interface *iface; + + if ((iface = bt_find_attach_interface(btdev)) == NULL) + return 0; + + res = sdp_get_devinfo(iface, btdev); + if (res < 0) { + btdev->flags &= ~BTDF_SDP_STARTED; + return -1; + } else if (res == 1) { + btdev->flags &= ~BTDF_SDP_STARTED; + log_info("%s: SDP query failed", + bt_ntoa(&btdev->addr, NULL)); + return 0; + } else if (res == 2) { + if (!(btdev->flags & BTDF_SDP_STARTED)) { + btdev->flags |= BTDF_SDP_STARTED; + log_info("%s: SDP query started", + bt_ntoa(&btdev->addr, NULL)); + } + return 0; + } else if (res != 0) + fatalx("bt_get_devinfo: sdp_get_devinfo"); } - bdaddr_copy(&btdev->info.baa.bd_raddr, &btdev->addr); + bdaddr_copy(devinfo_raddr(&btdev->info), &btdev->addr); btdev->flags |= BTDF_DEVINFO_VALID; return 0; } @@ -272,11 +296,11 @@ bt_device_attach(struct bt_device *btdev) return 0; } - if (bdaddr_any(&btdev->info.baa.bd_laddr)) { + if (bdaddr_any(devinfo_laddr(&btdev->info))) { if ((iface = bt_find_attach_interface(btdev)) == NULL) return 0; - bdaddr_copy(&btdev->info.baa.bd_laddr, &iface->addr); + bdaddr_copy(devinfo_laddr(&btdev->info), &iface->addr); } if (devinfo_store(&btdev->info, &buf, &n) < 0) @@ -319,11 +343,11 @@ bt_device_detach(struct bt_device *btdev) return 0; } - if (bdaddr_any(&btdev->info.baa.bd_laddr)) { + if (bdaddr_any(devinfo_laddr(&btdev->info))) { if ((iface = bt_find_attach_interface(btdev)) == NULL) return 0; - bdaddr_copy(&btdev->info.baa.bd_laddr, &iface->addr); + bdaddr_copy(devinfo_laddr(&btdev->info), &iface->addr); } if (devinfo_store(&btdev->info, &buf, &n) < 0) diff --git a/usr.sbin/btd/btd.h b/usr.sbin/btd/btd.h index 3b390d8abc1..aef9d24fc98 100644 --- a/usr.sbin/btd/btd.h +++ b/usr.sbin/btd/btd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: btd.h,v 1.4 2008/11/26 15:32:56 uwe Exp $ */ +/* $OpenBSD: btd.h,v 1.5 2008/11/26 21:48:30 uwe Exp $ */ /* * Copyright (c) 2008 Uwe Stuehler <uwe@openbsd.org> @@ -155,6 +155,8 @@ int db_get_devinfo(struct btd_db *, const bdaddr_t *, struct bt_devinfo *); void db_dump(struct btd_db *); /* devinfo.c */ +bdaddr_t *devinfo_laddr(struct bt_devinfo *); +bdaddr_t *devinfo_raddr(struct bt_devinfo *); int devinfo_load(struct bt_devinfo *, void *, size_t); void devinfo_unload(struct bt_devinfo *); int devinfo_store(const struct bt_devinfo *, void **, size_t *); @@ -184,7 +186,7 @@ void fatalx(const char *); /* sdp.c */ void sdp_init(struct btd *); -int sdp_query(struct bt_interface *, struct bt_device *); +int sdp_get_devinfo(struct bt_interface *, struct bt_device *); /* util.c */ time_t getmonotime(void); diff --git a/usr.sbin/btd/devinfo.c b/usr.sbin/btd/devinfo.c index 08b54bc66bb..521b9bf9122 100644 --- a/usr.sbin/btd/devinfo.c +++ b/usr.sbin/btd/devinfo.c @@ -3,6 +3,18 @@ #include "btd.h" +bdaddr_t * +devinfo_laddr(struct bt_devinfo *info) +{ + return &info->baa.bd_laddr; +} + +bdaddr_t * +devinfo_raddr(struct bt_devinfo *info) +{ + return &info->baa.bd_raddr; +} + int devinfo_store(const struct bt_devinfo *info, void **data, size_t *datalen) { diff --git a/usr.sbin/btd/hci.c b/usr.sbin/btd/hci.c index a8070bcf84d..88e7fbd8d4b 100644 --- a/usr.sbin/btd/hci.c +++ b/usr.sbin/btd/hci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hci.c,v 1.4 2008/11/26 15:32:56 uwe Exp $ */ +/* $OpenBSD: hci.c,v 1.5 2008/11/26 21:48:30 uwe Exp $ */ /* $NetBSD: btconfig.c,v 1.13 2008/07/21 13:36:57 lukem Exp $ */ /*- @@ -395,7 +395,8 @@ hci_interface_open(struct bt_interface *iface) return -1; } - log_info("listening on %s", bt_ntoa(&physif->addr, NULL)); + log_info("listening on %s (%s)", bt_ntoa(&physif->addr, NULL), + physif->xname); return 0; } diff --git a/usr.sbin/btd/sdp.c b/usr.sbin/btd/sdp.c index 9aae4eb7ef7..0445bf1b26b 100644 --- a/usr.sbin/btd/sdp.c +++ b/usr.sbin/btd/sdp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sdp.c,v 1.3 2008/11/26 15:32:56 uwe Exp $ */ +/* $OpenBSD: sdp.c,v 1.4 2008/11/26 21:48:30 uwe Exp $ */ /* * Copyright (c) 2008 Uwe Stuehler <uwe@openbsd.org> @@ -16,18 +16,26 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <netbt/l2cap.h> + #include <stdlib.h> +#include <string.h> +#include <unistd.h> #include "btd.h" - -struct sdp_session { - TAILQ_ENTRY(sdp_session) entry; -}; +#include "sdp.h" struct sdp_state { TAILQ_HEAD(, sdp_session) sessions; }; +struct sdp_session *sdp_new_session(struct sdp_state *, const bdaddr_t *); +struct sdp_session *sdp_find_session(struct sdp_state *, const bdaddr_t *); +int sdp_open_session(struct sdp_session *, const bdaddr_t *); +void sdp_close_session(struct sdp_session *); +void sdp_delete_session(struct sdp_session *); +void sdp_eventcb(int, short, void *); + void sdp_init(struct btd *env) { @@ -38,8 +46,214 @@ sdp_init(struct btd *env) TAILQ_INIT(&sdp->sessions); } +/* return 0 on success, 2 if the SDP query is still in progress, 1 if + * the device was not found, -1 on error */ +int +sdp_get_devinfo(struct bt_interface *iface, struct bt_device *btdev) +{ + struct sdp_state *sdp = iface->env->sdp; + struct sdp_session *sess; + const char *service; + + log_debug("sdp_get_devinfo: raddr %s", bt_ntoa(&btdev->addr, NULL)); + + switch (btdev->type) { + case BTDEV_NONE: + return 1; + case BTDEV_HID: + service = "HID"; + break; + case BTDEV_HSET: + service = "HSET"; + break; + case BTDEV_HF: + service = "HF"; + break; + default: + log_warnx("sdp_get_devinfo: invalid device type %#x", + btdev->type); + return -1; + } + + if ((sess = sdp_find_session(sdp, &btdev->addr)) == NULL && + (sess = sdp_new_session(sdp, &btdev->addr)) == NULL) + return -1; + + if (sdp_open_session(sess, &iface->addr) < 0) + return -1; + + if (sdp_query(sess, &btdev->info.baa, &iface->addr, &btdev->addr, + service) < 0) { + log_warnx("sdp_query failed"); + sdp_close_session(sess); + return -1; + } + + sdp_close_session(sess); + return 0; +} + +struct sdp_session * +sdp_new_session(struct sdp_state *sdp, const bdaddr_t *raddr) +{ + struct sdp_session *sess; + + if ((sess = calloc(1, sizeof(*sess))) == NULL) { + log_warn("sdp_new_session"); + return NULL; + } + + sess->sdp = sdp; + sess->state = SDP_SESSION_CLOSED; + bdaddr_copy(&sess->raddr, raddr); + + TAILQ_INSERT_TAIL(&sdp->sessions, sess, entry); + + return sess; +} + +struct sdp_session * +sdp_find_session(struct sdp_state *sdp, const bdaddr_t *raddr) +{ + struct sdp_session *sess; + + TAILQ_FOREACH(sess, &sdp->sessions, entry) + if (bdaddr_same(&sess->raddr, raddr)) + return sess; + + return NULL; +} + int -sdp_query(struct bt_interface *iface, struct bt_device *btdev) +sdp_open_session(struct sdp_session *sess, const bdaddr_t *laddr) { + struct sockaddr_bt sa; + socklen_t size; + + if (sess->state != SDP_SESSION_CLOSED) { + if (bdaddr_same(&sess->laddr, laddr)) + return 0; + + sdp_close_session(sess); + } + + bdaddr_copy(&sess->laddr, laddr); + + sess->fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); + if (sess->fd == -1) { + log_warn("sdp_open_session: socket"); + goto fail; + } + + memset(&sa, 0, sizeof(sa)); + sa.bt_len = sizeof(sa); + sa.bt_family = AF_BLUETOOTH; + bdaddr_copy(&sa.bt_bdaddr, &sess->laddr); + + if (bind(sess->fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { + log_warn("sdp_open_session: bind"); + goto fail; + } + + sa.bt_psm = L2CAP_PSM_SDP; + bdaddr_copy(&sa.bt_bdaddr, &sess->raddr); + + if (connect(sess->fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { + log_warn("sdp_open_session: connect"); + goto fail; + } + + size = sizeof(sess->omtu); + if (getsockopt(sess->fd, BTPROTO_L2CAP, SO_L2CAP_OMTU, &sess->omtu, + &size) < 0) { + log_warn("sdp_open_session: getsockopt"); + goto fail; + } + + size = sizeof(sess->imtu); + if (getsockopt(sess->fd, BTPROTO_L2CAP, SO_L2CAP_IMTU, &sess->imtu, + &size) < 0) { + log_warn("sdp_open_session: getsockopt"); + goto fail; + } + + if ((sess->req = malloc((size_t)sess->omtu)) == NULL) { + log_warn("sdp_open_session: malloc req"); + goto fail; + } + sess->req_e = sess->req + sess->omtu; + + if ((sess->rsp = malloc((size_t)sess->imtu)) == NULL) { + log_warn("sdp_open_session: malloc rsp"); + goto fail; + } + sess->rsp_e = sess->rsp + sess->imtu; + + event_set(&sess->ev, sess->fd, EV_READ | EV_PERSIST, + sdp_eventcb, sess); + if (event_add(&sess->ev, NULL)) { + log_warnx("event_add failed"); + goto fail; + } + + sess->state = SDP_SESSION_OPEN; return 0; + +fail: + if (sess->fd != -1) { + close(sess->fd); + sess->fd = -1; + } + + if (sess->req != NULL) { + free(sess->req); + sess->req = NULL; + } + + if (sess->rsp != NULL) { + free(sess->rsp); + sess->rsp = NULL; + } + + return -1; +} + +void +sdp_close_session(struct sdp_session *sess) +{ + if (sess->state == SDP_SESSION_CLOSED) + return; + + log_debug("sdp_close_session: laddr %s", bt_ntoa(&sess->laddr, NULL)); + + if (sess->req != NULL) { + free(sess->req); + sess->req = NULL; + } + + if (sess->rsp != NULL) { + free(sess->rsp); + sess->rsp = NULL; + } + + close(sess->fd); + sess->fd = -1; + sess->state = SDP_SESSION_CLOSED; +} + +void +sdp_delete_session(struct sdp_session *sess) +{ + struct sdp_state *sdp = sess->sdp; + + sdp_close_session(sess); + + TAILQ_REMOVE(&sdp->sessions, sess, entry); + + free(sess); +} + +void +sdp_eventcb(int fd, short evflags, void *arg) +{ } diff --git a/usr.sbin/btd/sdp.h b/usr.sbin/btd/sdp.h new file mode 100644 index 00000000000..93d41cb2d29 --- /dev/null +++ b/usr.sbin/btd/sdp.h @@ -0,0 +1,564 @@ +/* $NetBSD: sdp.h,v 1.4 2008/08/06 14:21:33 plunky Exp $ */ + +/*- + * Copyright (c) 2006 Itronix Inc. + * All rights reserved. + * + * Written by Iain Hibbert for Itronix Inc. + * + * 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. + * 3. The name of Itronix Inc. may not be used to endorse + * or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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. + */ +/* + * sdp.h + * + * Copyright (c) 2001-2003, 2008 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * 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 AUTHOR 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 AUTHOR 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. + * + * $Id: sdp.h,v 1.1 2008/11/26 21:48:30 uwe Exp $ + * $FreeBSD: src/lib/libsdp/sdp.h,v 1.5 2005/05/27 19:11:33 emax Exp $ + */ + +#ifndef _SDP_H_ +#define _SDP_H_ + +#include <sys/queue.h> +#include <event.h> +#include <string.h> + +__BEGIN_DECLS + +/* + * Data representation (page 349) + */ + +/* Nil, the null type */ +#define SDP_DATA_NIL 0x00 + +/* Unsigned integer */ +#define SDP_DATA_UINT8 0x08 +#define SDP_DATA_UINT16 0x09 +#define SDP_DATA_UINT32 0x0A +#define SDP_DATA_UINT64 0x0B +#define SDP_DATA_UINT128 0x0C + +/* Signed two's-complement integer */ +#define SDP_DATA_INT8 0x10 +#define SDP_DATA_INT16 0x11 +#define SDP_DATA_INT32 0x12 +#define SDP_DATA_INT64 0x13 +#define SDP_DATA_INT128 0x14 + +/* UUID, a universally unique identifier */ +#define SDP_DATA_UUID16 0x19 +#define SDP_DATA_UUID32 0x1A +#define SDP_DATA_UUID128 0x1C + +/* Text string */ +#define SDP_DATA_STR8 0x25 +#define SDP_DATA_STR16 0x26 +#define SDP_DATA_STR32 0x27 + +/* Boolean */ +#define SDP_DATA_BOOL 0x28 + +/* + * Data element sequence. + * A data element whose data field is a sequence of data elements + */ +#define SDP_DATA_SEQ8 0x35 +#define SDP_DATA_SEQ16 0x36 +#define SDP_DATA_SEQ32 0x37 + +/* + * Data element alternative. + * A data element whose data field is a sequence of data elements from + * which one data element is to be selected. + */ +#define SDP_DATA_ALT8 0x3D +#define SDP_DATA_ALT16 0x3E +#define SDP_DATA_ALT32 0x3F + +/* URL, a uniform resource locator */ +#define SDP_DATA_URL8 0x45 +#define SDP_DATA_URL16 0x46 +#define SDP_DATA_URL32 0x47 + +/* + * Protocols UUID (short) http://www.bluetoothsig.org/assigned-numbers/sdp.htm + * BASE UUID 00000000-0000-1000-8000-00805F9B34FB + */ + +#define SDP_UUID_PROTOCOL_SDP 0x0001 +#define SDP_UUID_PROTOCOL_UDP 0x0002 +#define SDP_UUID_PROTOCOL_RFCOMM 0x0003 +#define SDP_UUID_PROTOCOL_TCP 0x0004 +#define SDP_UUID_PROTOCOL_TCS_BIN 0x0005 +#define SDP_UUID_PROTOCOL_TCS_AT 0x0006 +#define SDP_UUID_PROTOCOL_OBEX 0x0008 +#define SDP_UUID_PROTOCOL_IP 0x0009 +#define SDP_UUID_PROTOCOL_FTP 0x000A +#define SDP_UUID_PROTOCOL_HTTP 0x000C +#define SDP_UUID_PROTOCOL_WSP 0x000E +#define SDP_UUID_PROTOCOL_BNEP 0x000F +#define SDP_UUID_PROTOCOL_UPNP 0x0010 +#define SDP_UUID_PROTOCOL_HIDP 0x0011 +#define SDP_UUID_PROTOCOL_HARDCOPY_CONTROL_CHANNEL 0x0012 +#define SDP_UUID_PROTOCOL_HARDCOPY_DATA_CHANNEL 0x0014 +#define SDP_UUID_PROTOCOL_HARDCOPY_NOTIFICATION 0x0016 +#define SDP_UUID_PROTOCOL_AVCTP 0x0017 +#define SDP_UUID_PROTOCOL_AVDTP 0x0019 +#define SDP_UUID_PROTOCOL_CMPT 0x001B +#define SDP_UUID_PROTOCOL_UDI_C_PLANE 0x001D +#define SDP_UUID_PROTOCOL_L2CAP 0x0100 + +/* + * Service class IDs http://www.bluetoothsig.org/assigned-numbers/sdp.htm + */ + +#define SDP_SERVICE_CLASS_SERVICE_DISCOVERY_SERVER 0x1000 +#define SDP_SERVICE_CLASS_BROWSE_GROUP_DESCRIPTOR 0x1001 +#define SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP 0x1002 +#define SDP_SERVICE_CLASS_SERIAL_PORT 0x1101 +#define SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP 0x1102 +#define SDP_SERVICE_CLASS_DIALUP_NETWORKING 0x1103 +#define SDP_SERVICE_CLASS_IR_MC_SYNC 0x1104 +#define SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH 0x1105 +#define SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER 0x1106 +#define SDP_SERVICE_CLASS_IR_MC_SYNC_COMMAND 0x1107 +#define SDP_SERVICE_CLASS_HEADSET 0x1108 +#define SDP_SERVICE_CLASS_CORDLESS_TELEPHONY 0x1109 +#define SDP_SERVICE_CLASS_AUDIO_SOURCE 0x110A +#define SDP_SERVICE_CLASS_AUDIO_SINK 0x110B +#define SDP_SERVICE_CLASS_AV_REMOTE_CONTROL_TARGET 0x110C +#define SDP_SERVICE_CLASS_ADVANCED_AUDIO_DISTRIBUTION 0x110D +#define SDP_SERVICE_CLASS_AV_REMOTE_CONTROL 0x110E +#define SDP_SERVICE_CLASS_VIDEO_CONFERENCING 0x110F +#define SDP_SERVICE_CLASS_INTERCOM 0x1110 +#define SDP_SERVICE_CLASS_FAX 0x1111 +#define SDP_SERVICE_CLASS_HEADSET_AUDIO_GATEWAY 0x1112 +#define SDP_SERVICE_CLASS_WAP 0x1113 +#define SDP_SERVICE_CLASS_WAP_CLIENT 0x1114 +#define SDP_SERVICE_CLASS_PANU 0x1115 +#define SDP_SERVICE_CLASS_NAP 0x1116 +#define SDP_SERVICE_CLASS_GN 0x1117 +#define SDP_SERVICE_CLASS_DIRECT_PRINTING 0x1118 +#define SDP_SERVICE_CLASS_REFERENCE_PRINTING 0x1119 +#define SDP_SERVICE_CLASS_IMAGING 0x111A +#define SDP_SERVICE_CLASS_IMAGING_RESPONDER 0x111B +#define SDP_SERVICE_CLASS_IMAGING_AUTOMATIC_ARCHIVE 0x111C +#define SDP_SERVICE_CLASS_IMAGING_REFERENCED_OBJECTS 0x111D +#define SDP_SERVICE_CLASS_HANDSFREE 0x111E +#define SDP_SERVICE_CLASS_HANDSFREE_AUDIO_GATEWAY 0x111F +#define SDP_SERVICE_CLASS_DIRECT_PRINTING_REFERENCE_OBJECTS 0x1120 +#define SDP_SERVICE_CLASS_REFLECTED_UI 0x1121 +#define SDP_SERVICE_CLASS_BASIC_PRINTING 0x1122 +#define SDP_SERVICE_CLASS_PRINTING_STATUS 0x1123 +#define SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE 0x1124 +#define SDP_SERVICE_CLASS_HARDCOPY_CABLE_REPLACEMENT 0x1125 +#define SDP_SERVICE_CLASS_HCR_PRINT 0x1126 +#define SDP_SERVICE_CLASS_HCR_SCAN 0x1127 +#define SDP_SERVICE_CLASS_COMMON_ISDN_ACCESS 0x1128 +#define SDP_SERVICE_CLASS_VIDEO_CONFERENCING_GW 0x1129 +#define SDP_SERVICE_CLASS_UDI_MT 0x112A +#define SDP_SERVICE_CLASS_UDI_TA 0x112B +#define SDP_SERVICE_CLASS_AUDIO_VIDEO 0x112C +#define SDP_SERVICE_CLASS_SIM_ACCESS 0x112D +#define SDP_SERVICE_CLASS_PNP_INFORMATION 0x1200 +#define SDP_SERVICE_CLASS_GENERIC_NETWORKING 0x1201 +#define SDP_SERVICE_CLASS_GENERIC_FILE_TRANSFER 0x1202 +#define SDP_SERVICE_CLASS_GENERIC_AUDIO 0x1203 +#define SDP_SERVICE_CLASS_GENERIC_TELEPHONY 0x1204 +#define SDP_SERVICE_CLASS_UPNP 0x1205 +#define SDP_SERVICE_CLASS_UPNP_IP 0x1206 +#define SDP_SERVICE_CLASS_ESDP_UPNP_IP_PAN 0x1300 +#define SDP_SERVICE_CLASS_ESDP_UPNP_IP_LAP 0x1301 +#define SDP_SERVICE_CLASS_ESDP_UPNP_L2CAP 0x1302 + +/* + * Universal attribute definitions (page 366) and + * http://www.bluetoothsig.org/assigned-numbers/sdp.htm + */ + +#define SDP_ATTR_RANGE(lo, hi) \ + (uint32_t)(((uint16_t)(lo) << 16) | ((uint16_t)(hi))) + +#define SDP_ATTR_SERVICE_RECORD_HANDLE 0x0000 +#define SDP_ATTR_SERVICE_CLASS_ID_LIST 0x0001 +#define SDP_ATTR_SERVICE_RECORD_STATE 0x0002 +#define SDP_ATTR_SERVICE_ID 0x0003 +#define SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST 0x0004 +#define SDP_ATTR_BROWSE_GROUP_LIST 0x0005 +#define SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST 0x0006 +#define SDP_ATTR_SERVICE_INFO_TIME_TO_LIVE 0x0007 +#define SDP_ATTR_SERVICE_AVAILABILITY 0x0008 +#define SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST 0x0009 +#define SDP_ATTR_DOCUMENTATION_URL 0x000A +#define SDP_ATTR_CLIENT_EXECUTABLE_URL 0x000B +#define SDP_ATTR_ICON_URL 0x000C +#define SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS 0x000D +#define SDP_ATTR_GROUP_ID 0x0200 +#define SDP_ATTR_IP_SUBNET 0x0200 +#define SDP_ATTR_VERSION_NUMBER_LIST 0x0200 +#define SDP_ATTR_SERVICE_DATABASE_STATE 0x0201 +#define SDP_ATTR_SERVICE_VERSION 0x0300 +#define SDP_ATTR_EXTERNAL_NETWORK 0x0301 +#define SDP_ATTR_NETWORK 0x0301 +#define SDP_ATTR_SUPPORTED_DATA_STORES_LIST 0x0301 +#define SDP_ATTR_FAX_CLASS1_SUPPORT 0x0302 +#define SDP_ATTR_REMOTE_AUDIO_VOLUME_CONTROL 0x0302 +#define SDP_ATTR_FAX_CLASS20_SUPPORT 0x0303 +#define SDP_ATTR_SUPPORTED_FORMATS_LIST 0x0303 +#define SDP_ATTR_FAX_CLASS2_SUPPORT 0x0304 +#define SDP_ATTR_AUDIO_FEEDBACK_SUPPORT 0x0305 +#define SDP_ATTR_NETWORK_ADDRESS 0x0306 +#define SDP_ATTR_WAP_GATEWAY 0x0307 +#define SDP_ATTR_HOME_PAGE_URL 0x0308 +#define SDP_ATTR_WAP_STACK_TYPE 0x0309 +#define SDP_ATTR_SECURITY_DESCRIPTION 0x030A +#define SDP_ATTR_NET_ACCESS_TYPE 0x030B +#define SDP_ATTR_MAX_NET_ACCESS_RATE 0x030C +#define SDP_ATTR_IPV4_SUBNET 0x030D +#define SDP_ATTR_IPV6_SUBNET 0x030E +#define SDP_ATTR_SUPPORTED_CAPABALITIES 0x0310 +#define SDP_ATTR_SUPPORTED_FEATURES 0x0311 +#define SDP_ATTR_SUPPORTED_FUNCTIONS 0x0312 +#define SDP_ATTR_TOTAL_IMAGING_DATA_CAPACITY 0x0313 + +/* + * The offset must be added to the attribute ID base (contained in the + * LANGUAGE_BASE_ATTRIBUTE_ID_LIST attribute) in order to compute the + * attribute ID for these attributes. + */ + +#define SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID 0x0100 +#define SDP_ATTR_SERVICE_NAME_OFFSET 0x0000 +#define SDP_ATTR_SERVICE_DESCRIPTION_OFFSET 0x0001 +#define SDP_ATTR_PROVIDER_NAME_OFFSET 0x0002 + +/* + * Protocol data unit (PDU) format (page 352) + */ + +#define SDP_PDU_ERROR_RESPONSE 0x01 +#define SDP_PDU_SERVICE_SEARCH_REQUEST 0x02 +#define SDP_PDU_SERVICE_SEARCH_RESPONSE 0x03 +#define SDP_PDU_SERVICE_ATTRIBUTE_REQUEST 0x04 +#define SDP_PDU_SERVICE_ATTRIBUTE_RESPONSE 0x05 +#define SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST 0x06 +#define SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_RESPONSE 0x07 + +struct sdp_pdu { + uint8_t pid; /* PDU ID - SDP_PDU_xxx */ + uint16_t tid; /* transaction ID */ + uint16_t len; /* parameters length (in bytes) */ +} __attribute__ ((packed)); +typedef struct sdp_pdu sdp_pdu_t; +typedef struct sdp_pdu * sdp_pdu_p; + +/* + * Error codes for SDP_PDU_ERROR_RESPONSE + */ + +#define SDP_ERROR_CODE_INVALID_SDP_VERSION 0x0001 +#define SDP_ERROR_CODE_INVALID_SERVICE_RECORD_HANDLE 0x0002 +#define SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX 0x0003 +#define SDP_ERROR_CODE_INVALID_PDU_SIZE 0x0004 +#define SDP_ERROR_CODE_INVALID_CONTINUATION_STATE 0x0005 +#define SDP_ERROR_CODE_INSUFFICIENT_RESOURCES 0x0006 + +/* + * SDP int128/uint128 parameter + */ + +struct int128 { + int8_t b[16]; +}; +typedef struct int128 int128_t; +typedef struct int128 uint128_t; + +/* + * SDP attribute + */ + +struct sdp_attr { + uint16_t flags; +#define SDP_ATTR_OK (0 << 0) +#define SDP_ATTR_INVALID (1 << 0) +#define SDP_ATTR_TRUNCATED (1 << 1) + uint16_t attr; /* SDP_ATTR_xxx */ + uint32_t vlen; /* length of the value[] in bytes */ + uint8_t *value; /* base pointer */ +}; +typedef struct sdp_attr sdp_attr_t; +typedef struct sdp_attr * sdp_attr_p; + +/****************************************************************************** + * User interface + *****************************************************************************/ + +/* Inline versions of get/put byte/short/long. Pointer is advanced */ +#define SDP_GET8(b, cp) do { \ + (b) = *(const uint8_t *)(cp); \ + (cp) += sizeof(uint8_t); \ +} while (/* CONSTCOND */0) + +#define SDP_GET16(s, cp) do { \ + (s) = be16dec(cp); \ + (cp) += sizeof(uint16_t); \ +} while (/* CONSTCOND */0) + +#define SDP_GET32(l, cp) do { \ + (l) = be32dec(cp); \ + (cp) += sizeof(uint32_t); \ +} while (/* CONSTCOND */0) + +#define SDP_GET64(l, cp) do { \ + (l) = be64dec(cp); \ + (cp) += sizeof(uint64_t); \ +} while (/* CONSTCOND */0) + +#if BYTE_ORDER == LITTLE_ENDIAN +#define SDP_GET128(l, cp) do { \ + register const uint8_t *t_cp = (const uint8_t *)(cp); \ + (l)->b[15] = *t_cp++; \ + (l)->b[14] = *t_cp++; \ + (l)->b[13] = *t_cp++; \ + (l)->b[12] = *t_cp++; \ + (l)->b[11] = *t_cp++; \ + (l)->b[10] = *t_cp++; \ + (l)->b[9] = *t_cp++; \ + (l)->b[8] = *t_cp++; \ + (l)->b[7] = *t_cp++; \ + (l)->b[6] = *t_cp++; \ + (l)->b[5] = *t_cp++; \ + (l)->b[4] = *t_cp++; \ + (l)->b[3] = *t_cp++; \ + (l)->b[2] = *t_cp++; \ + (l)->b[1] = *t_cp++; \ + (l)->b[0] = *t_cp++; \ + (cp) += 16; \ +} while (/* CONSTCOND */0) + +#define SDP_GET_UUID128(l, cp) do { \ + memcpy(&((l)->b), (cp), 16); \ + (cp) += 16; \ +} while (/* CONSTCOND */0) +#elif BYTE_ORDER == BIG_ENDIAN +#define SDP_GET128(l, cp) do { \ + memcpy(&((l)->b), (cp), 16); \ + (cp) += 16; \ +} while (/* CONSTCOND */0) + +#define SDP_GET_UUID128(l, cp) SDP_GET128(l, cp) +#else +#error "Unsupported BYTE_ORDER" +#endif /* BYTE_ORDER */ + +#define SDP_PUT8(b, cp) do { \ + *(uint8_t *)(cp) = (b); \ + (cp) += sizeof(uint8_t); \ +} while (/* CONSTCOND */0) + +#define SDP_PUT16(s, cp) do { \ + be16enc((cp), (s)); \ + (cp) += sizeof(uint16_t); \ +} while (/* CONSTCOND */0) + +#define SDP_PUT32(s, cp) do { \ + be32enc((cp), (s)); \ + (cp) += sizeof(uint32_t); \ +} while (/* CONSTCOND */0) + +#define SDP_PUT64(s, cp) do { \ + be64enc((cp), (s)); \ + (cp) += sizeof(uint64_t); \ +} while (/* CONSTCOND */0) + +#if BYTE_ORDER == LITTLE_ENDIAN +#define SDP_PUT128(l, cp) do { \ + register const uint8_t *t_cp = (const uint8_t *)(cp); \ + *t_cp++ = (l)->b[15]; \ + *t_cp++ = (l)->b[14]; \ + *t_cp++ = (l)->b[13]; \ + *t_cp++ = (l)->b[12]; \ + *t_cp++ = (l)->b[11]; \ + *t_cp++ = (l)->b[10]; \ + *t_cp++ = (l)->b[9]; \ + *t_cp++ = (l)->b[8]; \ + *t_cp++ = (l)->b[7]; \ + *t_cp++ = (l)->b[6]; \ + *t_cp++ = (l)->b[5]; \ + *t_cp++ = (l)->b[4]; \ + *t_cp++ = (l)->b[3]; \ + *t_cp++ = (l)->b[2]; \ + *t_cp++ = (l)->b[1]; \ + *t_cp = (l)->b[0]; \ + (cp) += 16; \ +} while (/* CONSTCOND */0) + +#define SDP_PUT_UUID128(l, cp) do { \ + memcpy((cp), &((l)->b), 16); \ + (cp) += 16; \ +} while (/* CONSTCOND */0) +#elif BYTE_ORDER == BIG_ENDIAN +#define SDP_PUT128(l, cp) do { \ + memcpy((cp), &((l)->b), 16); \ + (cp) += 16; \ +} while (/* CONSTCOND */0) + +#define SDP_PUT_UUID128(l, cp) SDP_PUT128(l, cp) +#else +#error "Unsupported BYTE_ORDER" +#endif /* BYTE_ORDER */ + +void * sdp_open (bdaddr_t const *l, bdaddr_t const *r); +void * sdp_open_local (char const *control); +int32_t sdp_close (void *xs); +int32_t sdp_error (void *xs); + +int32_t sdp_search (void *xs, + uint32_t plen, uint16_t const *pp, + uint32_t alen, uint32_t const *ap, + uint32_t vlen, sdp_attr_t *vp); + +char const * sdp_attr2desc (uint16_t attr); +char const * sdp_uuid2desc (uint16_t uuid); +void sdp_print (uint32_t level, uint8_t *start, + uint8_t const *end); + +struct sdp_session; +struct btdev_attach_args; + +int sdp_query(struct sdp_session *, struct btdev_attach_args *, bdaddr_t *, + bdaddr_t *, const char *); + +/****************************************************************************** + * internal interface and utility functions + *****************************************************************************/ + +enum sdp_session_state { + SDP_SESSION_CLOSED, + SDP_SESSION_OPEN, + SDP_SESSION_FINISHED +}; + +struct sdp_session { + TAILQ_ENTRY(sdp_session) entry; + struct sdp_state *sdp; + enum sdp_session_state state; + bdaddr_t laddr; + bdaddr_t raddr; + uint16_t omtu; + uint16_t imtu; + uint8_t *req; + uint8_t *rsp; + int fd; + struct event ev; + /* for legacy SDP code */ + uint8_t *req_e; + uint8_t *rsp_e; + uint16_t tid; + uint8_t cs[16]; + uint32_t cslen; + int error; +}; + +static __inline void +be16enc(void *buf, uint16_t u) +{ + uint8_t *p = (uint8_t *)buf; + + p[0] = ((unsigned)u >> 8) & 0xff; + p[1] = u & 0xff; +} + +static __inline uint16_t +be16dec(const void *buf) +{ + const uint8_t *p = (const uint8_t *)buf; + + return ((p[0] << 8) | p[1]); +} + +static __inline void +be32enc(void *buf, uint32_t u) +{ + uint8_t *p = (uint8_t *)buf; + + p[0] = (u >> 24) & 0xff; + p[1] = (u >> 16) & 0xff; + p[2] = (u >> 8) & 0xff; + p[3] = u & 0xff; +} + +static __inline uint32_t +be32dec(const void *buf) +{ + const uint8_t *p = (const uint8_t *)buf; + + return ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static __inline void +be64enc(void *buf, uint64_t u) +{ + uint8_t *p = (uint8_t *)buf; + + be32enc(p, (uint32_t)(u >> 32)); + be32enc(p + 4, (uint32_t)(u & 0xffffffffULL)); +} + +static __inline uint64_t +be64dec(const void *buf) +{ + const uint8_t *p = (const uint8_t *)buf; + + return (((uint64_t)be32dec(p) << 32) | be32dec(p + 4)); +} + +__END_DECLS + +#endif /* ndef _SDP_H_ */ diff --git a/usr.sbin/btd/sdp_query.c b/usr.sbin/btd/sdp_query.c new file mode 100644 index 00000000000..e6bde48e28e --- /dev/null +++ b/usr.sbin/btd/sdp_query.c @@ -0,0 +1,730 @@ +/* $OpenBSD: sdp_query.c,v 1.1 2008/11/26 21:48:30 uwe Exp $ */ +/* $NetBSD: sdp.c,v 1.5 2008/04/20 19:34:23 plunky Exp $ */ + +/*- + * Copyright (c) 2006 Itronix Inc. + * All rights reserved. + * + * 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. + * 3. The name of Itronix Inc. may not be used to endorse + * or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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. + */ +/* + * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * 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 AUTHOR 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 AUTHOR 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/types.h> + +#include <dev/bluetooth/btdev.h> +#include <dev/usb/usb.h> +#include <dev/usb/usbhid.h> + +#include <err.h> +#include <errno.h> +#include <sdp.h> +#include <stdlib.h> +#include <usbhid.h> + +#include "btd.h" + +static int hid_mode(uint8_t *, int32_t); + +static int32_t parse_l2cap_psm(sdp_attr_t *); +static int32_t parse_rfcomm_channel(sdp_attr_t *); +static int32_t parse_hid_descriptor(sdp_attr_t *); +static int32_t parse_boolean(sdp_attr_t *); + +static int config_hid(struct btdev_attach_args *); +static int config_hset(struct btdev_attach_args *); +static int config_hf(struct btdev_attach_args *); + +uint16_t hid_services[] = { + SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE +}; + +uint32_t hid_attrs[] = { + SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, + SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST), + SDP_ATTR_RANGE( SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS, + SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS), + SDP_ATTR_RANGE( 0x0205, /* HIDReconnectInitiate */ + 0x0206), /* HIDDescriptorList */ + SDP_ATTR_RANGE( 0x0209, /* HIDBatteryPower */ + 0x0209), + SDP_ATTR_RANGE( 0x020d, /* HIDNormallyConnectable */ + 0x020d) +}; + +uint16_t hset_services[] = { + SDP_SERVICE_CLASS_HEADSET +}; + +uint32_t hset_attrs[] = { + SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, + SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST), +}; + +uint16_t hf_services[] = { + SDP_SERVICE_CLASS_HANDSFREE_AUDIO_GATEWAY +}; + +uint32_t hf_attrs[] = { + SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, + SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST), +}; + +#define NUM(v) (sizeof(v) / sizeof(v[0])) + +static struct { + const char *name; + int (*handler)(struct btdev_attach_args *); + const char *description; + uint16_t *services; + int nservices; + uint32_t *attrs; + int nattrs; +} cfgtype[] = { + { + "HID", config_hid, "Human Interface Device", + hid_services, NUM(hid_services), + hid_attrs, NUM(hid_attrs), + }, + { + "HSET", config_hset, "Headset", + hset_services, NUM(hset_services), + hset_attrs, NUM(hset_attrs), + }, + { + "HF", config_hf, "Handsfree", + hf_services, NUM(hf_services), + hf_attrs, NUM(hf_attrs), + }, +}; + +static sdp_attr_t values[8]; +static uint8_t buffer[NUM(values)][512]; + +int +sdp_query(struct sdp_session *sess, struct btdev_attach_args *dict, + bdaddr_t *laddr, bdaddr_t *raddr, const char *service) +{ + int rv, i; + + for (i = 0 ; i < NUM(values) ; i++) { + values[i].flags = SDP_ATTR_INVALID; + values[i].attr = 0; + values[i].vlen = sizeof(buffer[i]); + values[i].value = buffer[i]; + } + + for (i = 0 ; i < NUM(cfgtype) ; i++) { + if (strcasecmp(service, cfgtype[i].name) == 0) { + rv = sdp_search(sess, + cfgtype[i].nservices, cfgtype[i].services, + cfgtype[i].nattrs, cfgtype[i].attrs, + NUM(values), values); + + if (rv != 0) { + errno = sess->error; + return -1; + } + + rv = (*cfgtype[i].handler)(dict); + if (rv != 0) + return -1; + + return 0; + } + } + + fatalx("bad device type"); + /* NOTREACHED */ + return -1; +} + +/* + * Configure HID results + */ +static int +config_hid(struct btdev_attach_args *dict) +{ + int32_t control_psm, interrupt_psm, + reconnect_initiate, battery_power, + normally_connectable, hid_length; + uint8_t *hid_descriptor; + int i; + + control_psm = -1; + interrupt_psm = -1; + reconnect_initiate = -1; + normally_connectable = 0; + battery_power = 0; + hid_descriptor = NULL; + hid_length = -1; + + for (i = 0; i < NUM(values) ; i++) { + if (values[i].flags != SDP_ATTR_OK) + continue; + + switch (values[i].attr) { + case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST: + control_psm = parse_l2cap_psm(&values[i]); + break; + + case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS: + interrupt_psm = parse_l2cap_psm(&values[i]); + break; + + case 0x0205: /* HIDReconnectInitiate */ + reconnect_initiate = parse_boolean(&values[i]); + break; + + case 0x0206: /* HIDDescriptorList */ + if (parse_hid_descriptor(&values[i]) == 0) { + hid_descriptor = values[i].value; + hid_length = values[i].vlen; + } + break; + + case 0x0209: /* HIDBatteryPower */ + battery_power = parse_boolean(&values[i]); + break; + + case 0x020d: /* HIDNormallyConnectable */ + normally_connectable = parse_boolean(&values[i]); + break; + } + } + + if (control_psm == -1 + || interrupt_psm == -1 + || reconnect_initiate == -1 + || hid_descriptor == NULL + || hid_length == -1) + return ENOATTR; + + dict->bd_type = BTDEV_HID; + dict->bd_hid.hid_ctl = control_psm; + dict->bd_hid.hid_int = interrupt_psm; + dict->bd_hid.hid_desc = hid_descriptor; + dict->bd_hid.hid_dlen = hid_length; + dict->bd_mode = hid_mode(hid_descriptor, hid_length); + + if (!reconnect_initiate) + dict->bd_hid.hid_flags |= BTHID_INITIATE; + + return 0; +} + +/* + * Configure HSET results + */ +static int +config_hset(struct btdev_attach_args *dict) +{ + uint32_t channel; + int i; + + channel = -1; + + for (i = 0; i < NUM(values) ; i++) { + if (values[i].flags != SDP_ATTR_OK) + continue; + + switch (values[i].attr) { + case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST: + channel = parse_rfcomm_channel(&values[i]); + break; + } + } + + if (channel == -1) + return ENOATTR; + + dict->bd_type = BTDEV_HSET; + dict->bd_hset.hset_channel = channel; + + return 0; +} + +/* + * Configure HF results + */ +static int +config_hf(struct btdev_attach_args *dict) +{ + uint32_t channel; + int i; + + channel = -1; + + for (i = 0 ; i < NUM(values) ; i++) { + if (values[i].flags != SDP_ATTR_OK) + continue; + + switch (values[i].attr) { + case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST: + channel = parse_rfcomm_channel(&values[i]); + break; + } + } + + if (channel == -1) + return ENOATTR; + + dict->bd_type = BTDEV_HF; + dict->bd_hf.hf_listen = 1; + dict->bd_hf.hf_channel = channel; + return 0; +} + +/* + * Parse [additional] protocol descriptor list for L2CAP PSM + * + * seq8 len8 2 + * seq8 len8 2 + * uuid16 value16 3 L2CAP + * uint16 value16 3 PSM + * seq8 len8 2 + * uuid16 value16 3 HID Protocol + * === + * 15 + */ + +static int32_t +parse_l2cap_psm(sdp_attr_t *a) +{ + uint8_t *ptr = a->value; + uint8_t *end = a->value + a->vlen; + int32_t type, len, uuid, psm; + + if (end - ptr < 15) + return (-1); + + if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) { + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_SEQ8: + SDP_GET8(len, ptr); + break; + + case SDP_DATA_SEQ16: + SDP_GET16(len, ptr); + break; + + case SDP_DATA_SEQ32: + SDP_GET32(len, ptr); + break; + + default: + return (-1); + } + if (ptr + len > end) + return (-1); + } + + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_SEQ8: + SDP_GET8(len, ptr); + break; + + case SDP_DATA_SEQ16: + SDP_GET16(len, ptr); + break; + + case SDP_DATA_SEQ32: + SDP_GET32(len, ptr); + break; + + default: + return (-1); + } + if (ptr + len > end) + return (-1); + + /* Protocol */ + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_SEQ8: + SDP_GET8(len, ptr); + break; + + case SDP_DATA_SEQ16: + SDP_GET16(len, ptr); + break; + + case SDP_DATA_SEQ32: + SDP_GET32(len, ptr); + break; + + default: + return (-1); + } + if (ptr + len > end) + return (-1); + + /* UUID */ + if (ptr + 3 > end) + return (-1); + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_UUID16: + SDP_GET16(uuid, ptr); + if (uuid != SDP_UUID_PROTOCOL_L2CAP) + return (-1); + break; + + case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */ + case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */ + default: + return (-1); + } + + /* PSM */ + if (ptr + 3 > end) + return (-1); + SDP_GET8(type, ptr); + if (type != SDP_DATA_UINT16) + return (-1); + SDP_GET16(psm, ptr); + + return (psm); +} + +/* + * Parse HID descriptor string + * + * seq8 len8 2 + * seq8 len8 2 + * uint8 value8 2 + * str value 3 + * === + * 9 + */ + +static int32_t +parse_hid_descriptor(sdp_attr_t *a) +{ + uint8_t *ptr = a->value; + uint8_t *end = a->value + a->vlen; + int32_t type, len, descriptor_type; + + if (end - ptr < 9) + return (-1); + + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_SEQ8: + SDP_GET8(len, ptr); + break; + + case SDP_DATA_SEQ16: + SDP_GET16(len, ptr); + break; + + case SDP_DATA_SEQ32: + SDP_GET32(len, ptr); + break; + + default: + return (-1); + } + if (ptr + len > end) + return (-1); + + while (ptr < end) { + /* Descriptor */ + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_SEQ8: + if (ptr + 1 > end) + return (-1); + SDP_GET8(len, ptr); + break; + + case SDP_DATA_SEQ16: + if (ptr + 2 > end) + return (-1); + SDP_GET16(len, ptr); + break; + + case SDP_DATA_SEQ32: + if (ptr + 4 > end) + return (-1); + SDP_GET32(len, ptr); + break; + + default: + return (-1); + } + + /* Descripor type */ + if (ptr + 1 > end) + return (-1); + SDP_GET8(type, ptr); + if (type != SDP_DATA_UINT8 || ptr + 1 > end) + return (-1); + SDP_GET8(descriptor_type, ptr); + + /* Descriptor value */ + if (ptr + 1 > end) + return (-1); + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_STR8: + if (ptr + 1 > end) + return (-1); + SDP_GET8(len, ptr); + break; + + case SDP_DATA_STR16: + if (ptr + 2 > end) + return (-1); + SDP_GET16(len, ptr); + break; + + case SDP_DATA_STR32: + if (ptr + 4 > end) + return (-1); + SDP_GET32(len, ptr); + break; + + default: + return (-1); + } + if (ptr + len > end) + return (-1); + + if (descriptor_type == UDESC_REPORT && len > 0) { + a->value = ptr; + a->vlen = len; + + return (0); + } + + ptr += len; + } + + return (-1); +} + +/* + * Parse boolean value + * + * bool8 int8 + */ + +static int32_t +parse_boolean(sdp_attr_t *a) +{ + if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL) + return (-1); + + return (a->value[1]); +} + +/* + * Parse protocol descriptor list for the RFCOMM channel + * + * seq8 len8 2 + * seq8 len8 2 + * uuid16 value16 3 L2CAP + * seq8 len8 2 + * uuid16 value16 3 RFCOMM + * uint8 value8 2 channel + * === + * 14 + */ + +static int32_t +parse_rfcomm_channel(sdp_attr_t *a) +{ + uint8_t *ptr = a->value; + uint8_t *end = a->value + a->vlen; + int32_t type, len, uuid, channel; + + if (end - ptr < 14) + return (-1); + + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_SEQ8: + SDP_GET8(len, ptr); + break; + + case SDP_DATA_SEQ16: + SDP_GET16(len, ptr); + break; + + case SDP_DATA_SEQ32: + SDP_GET32(len, ptr); + break; + + default: + return (-1); + } + if (ptr + len > end) + return (-1); + + /* Protocol */ + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_SEQ8: + SDP_GET8(len, ptr); + break; + + case SDP_DATA_SEQ16: + SDP_GET16(len, ptr); + break; + + case SDP_DATA_SEQ32: + SDP_GET32(len, ptr); + break; + + default: + return (-1); + } + if (ptr + len > end) + return (-1); + + /* UUID */ + if (ptr + 3 > end) + return (-1); + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_UUID16: + SDP_GET16(uuid, ptr); + if (uuid != SDP_UUID_PROTOCOL_L2CAP) + return (-1); + break; + + case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */ + case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */ + default: + return (-1); + } + + /* Protocol */ + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_SEQ8: + SDP_GET8(len, ptr); + break; + + case SDP_DATA_SEQ16: + SDP_GET16(len, ptr); + break; + + case SDP_DATA_SEQ32: + SDP_GET32(len, ptr); + break; + + default: + return (-1); + } + if (ptr + len > end) + return (-1); + + /* UUID */ + if (ptr + 3 > end) + return (-1); + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_UUID16: + SDP_GET16(uuid, ptr); + if (uuid != SDP_UUID_PROTOCOL_RFCOMM) + return (-1); + break; + + case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */ + case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */ + default: + return (-1); + } + + /* channel */ + if (ptr + 2 > end) + return (-1); + + SDP_GET8(type, ptr); + if (type != SDP_DATA_UINT8) + return (-1); + + SDP_GET8(channel, ptr); + + return (channel); +} + +/* + * return appropriate mode for HID descriptor + */ +static int +hid_mode(uint8_t *desc, int32_t dlen) +{ + report_desc_t r; + hid_data_t d; + struct hid_item h; + int mode; + + mode = BTDEV_MODE_AUTH; /* default */ + + r = hid_use_report_desc(desc, dlen); + if (r == NULL) + err(EXIT_FAILURE, "hid_use_report_desc"); + + d = hid_start_parse(r, ~0, -1); + while (hid_get_item(d, &h) > 0) { + if (h.kind == hid_collection + && HID_PAGE(h.usage) == HUP_GENERIC_DESKTOP + && HID_USAGE(h.usage) == HUG_KEYBOARD) + mode = BTDEV_MODE_ENCRYPT; + } + + hid_end_parse(d); + hid_dispose_report_desc(r); + + return mode; +} diff --git a/usr.sbin/btd/sdp_search.c b/usr.sbin/btd/sdp_search.c new file mode 100644 index 00000000000..a0ca443510f --- /dev/null +++ b/usr.sbin/btd/sdp_search.c @@ -0,0 +1,422 @@ +/* $NetBSD: search.c,v 1.3 2007/12/15 16:03:29 perry Exp $ */ + +/* + * search.c + * + * Copyright (c) 2001-2003 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * 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 AUTHOR 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 AUTHOR 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. + * + * $Id: sdp_search.c,v 1.1 2008/11/26 21:48:30 uwe Exp $ + * $FreeBSD: src/lib/libsdp/search.c,v 1.8 2007/11/16 15:13:12 emax Exp $ + */ + +#include <sys/types.h> +#include <sys/uio.h> +#include <netbt/bluetooth.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "sdp.h" + +int32_t +sdp_search(void *xss, + uint32_t plen, uint16_t const *pp, + uint32_t alen, uint32_t const *ap, + uint32_t vlen, sdp_attr_t *vp) +{ + struct sdp_xpdu { + sdp_pdu_t pdu; + uint16_t len; + } __packed xpdu; + + struct sdp_session *ss = xss; + uint8_t *req = NULL, *rsp = NULL, *rsp_tmp = NULL; + int32_t t, len; + uint16_t lo, hi; + + if (ss == NULL) + return (-1); + + if (ss->req == NULL || ss->rsp == NULL || + plen == 0 || pp == NULL || alen == 0 || ap == NULL) { + ss->error = EINVAL; + return (-1); + } + + req = ss->req; + + /* Calculate ServiceSearchPattern length */ + plen = plen * (sizeof(pp[0]) + 1); + + /* Calculate AttributeIDList length */ + for (len = 0, t = 0; t < alen; t ++) { + lo = (uint16_t) (ap[t] >> 16); + hi = (uint16_t) (ap[t]); + + if (lo > hi) { + ss->error = EINVAL; + return (-1); + } + + if (lo != hi) + len += (sizeof(ap[t]) + 1); + else + len += (sizeof(lo) + 1); + } + alen = len; + + /* Calculate length of the request */ + len = plen + sizeof(uint8_t) + sizeof(uint16_t) + + /* ServiceSearchPattern */ + sizeof(uint16_t) + + /* MaximumAttributeByteCount */ + alen + sizeof(uint8_t) + sizeof(uint16_t); + /* AttributeIDList */ + + if (ss->req_e - req < len) { + ss->error = ENOBUFS; + return (-1); + } + + /* Put ServiceSearchPattern */ + SDP_PUT8(SDP_DATA_SEQ16, req); + SDP_PUT16(plen, req); + for (; plen > 0; pp ++, plen -= (sizeof(pp[0]) + 1)) { + SDP_PUT8(SDP_DATA_UUID16, req); + SDP_PUT16(*pp, req); + } + + /* Put MaximumAttributeByteCount */ + SDP_PUT16(0xffff, req); + + /* Put AttributeIDList */ + SDP_PUT8(SDP_DATA_SEQ16, req); + SDP_PUT16(alen, req); + for (; alen > 0; ap ++) { + lo = (uint16_t) (*ap >> 16); + hi = (uint16_t) (*ap); + + if (lo != hi) { + /* Put attribute range */ + SDP_PUT8(SDP_DATA_UINT32, req); + SDP_PUT32(*ap, req); + alen -= (sizeof(ap[0]) + 1); + } else { + /* Put attribute */ + SDP_PUT8(SDP_DATA_UINT16, req); + SDP_PUT16(lo, req); + alen -= (sizeof(lo) + 1); + } + } + + /* Submit ServiceSearchAttributeRequest and wait for response */ + ss->cslen = 0; + rsp = ss->rsp; + + do { + struct iovec iov[2]; + uint8_t *req_cs = req; + + /* Add continuation state (if any) */ + if (ss->req_e - req_cs < ss->cslen + 1) { + ss->error = ENOBUFS; + return (-1); + } + + SDP_PUT8(ss->cslen, req_cs); + if (ss->cslen > 0) { + memcpy(req_cs, ss->cs, ss->cslen); + req_cs += ss->cslen; + } + + /* Prepare SDP PDU header */ + xpdu.pdu.pid = SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST; + xpdu.pdu.tid = htons(ss->tid); + xpdu.pdu.len = htons(req_cs - ss->req); + + /* Submit request */ + iov[0].iov_base = (void *) &xpdu; + iov[0].iov_len = sizeof(xpdu.pdu); + iov[1].iov_base = (void *) ss->req; + iov[1].iov_len = req_cs - ss->req; + + do { + len = writev(ss->fd, iov, sizeof(iov)/sizeof(iov[0])); + } while (len < 0 && errno == EINTR); + + if (len < 0) { + ss->error = errno; + return (-1); + } + + /* Read response */ + iov[0].iov_base = (void *) &xpdu; + iov[0].iov_len = sizeof(xpdu); + iov[1].iov_base = (void *) rsp; + iov[1].iov_len = ss->imtu; + + do { + len = readv(ss->fd, iov, sizeof(iov)/sizeof(iov[0])); + } while (len < 0 && errno == EINTR); + + if (len < 0) { + ss->error = errno; + return (-1); + } + if (len < sizeof(xpdu)) { + ss->error = ENOMSG; + return (-1); + } + + xpdu.pdu.tid = ntohs(xpdu.pdu.tid); + xpdu.pdu.len = ntohs(xpdu.pdu.len); + xpdu.len = ntohs(xpdu.len); + + if (xpdu.pdu.pid == SDP_PDU_ERROR_RESPONSE || + xpdu.pdu.tid != ss->tid || + xpdu.pdu.len > len || + xpdu.len > xpdu.pdu.len) { + ss->error = EIO; + return (-1); + } + + rsp += xpdu.len; + ss->tid ++; + + /* Save continuation state (if any) */ + ss->cslen = rsp[0]; + if (ss->cslen > 0) { + if (ss->cslen > sizeof(ss->cs)) { + ss->error = ENOBUFS; + return (-1); + } + + memcpy(ss->cs, rsp + 1, ss->cslen); + + /* + * Ensure that we always have ss->imtu bytes + * available in the ss->rsp buffer + */ + + if (ss->rsp_e - rsp <= ss->imtu) { + uint32_t size, offset; + + size = ss->rsp_e - ss->rsp + ss->imtu; + offset = rsp - ss->rsp; + + rsp_tmp = realloc(ss->rsp, size); + if (rsp_tmp == NULL) { + ss->error = ENOMEM; + return (-1); + } + + ss->rsp = rsp_tmp; + ss->rsp_e = ss->rsp + size; + rsp = ss->rsp + offset; + } + } + } while (ss->cslen > 0); + + /* + * If we got here then we have completed SDP transaction and now + * we must populate attribute values into vp array. At this point + * ss->rsp points to the beginning of the response and rsp points + * to the end of the response. + * + * From Bluetooth v1.1 spec page 364 + * + * The AttributeLists is a data element sequence where each element + * in turn is a data element sequence representing an attribute list. + * Each attribute list contains attribute IDs and attribute values + * from one service record. The first element in each attribute list + * contains the attribute ID of the first attribute to be returned for + * that service record. The second element in each attribute list + * contains the corresponding attribute value. Successive pairs of + * elements in each attribute list contain additional attribute ID + * and value pairs. Only attributes that have non-null values within + * the service record and whose attribute IDs were specified in the + * SDP_ServiceSearchAttributeRequest are contained in the AttributeLists + * Neither an attribute ID nor attribute value is placed in + * AttributeLists for attributes in the service record that have no + * value. Within each attribute list, the attributes are listed in + * ascending order of attribute ID value. + */ + + if (vp == NULL) + goto done; + + rsp_tmp = ss->rsp; + + /* Skip the first SEQ */ + SDP_GET8(t, rsp_tmp); + switch (t) { + case SDP_DATA_SEQ8: + SDP_GET8(len, rsp_tmp); + break; + + case SDP_DATA_SEQ16: + SDP_GET16(len, rsp_tmp); + break; + + case SDP_DATA_SEQ32: + SDP_GET32(len, rsp_tmp); + break; + + default: + ss->error = ENOATTR; + return (-1); + /* NOT REACHED */ + } + + for (; rsp_tmp < rsp && vlen > 0; ) { + /* Get set of attributes for the next record */ + SDP_GET8(t, rsp_tmp); + switch (t) { + case SDP_DATA_SEQ8: + SDP_GET8(len, rsp_tmp); + break; + + case SDP_DATA_SEQ16: + SDP_GET16(len, rsp_tmp); + break; + + case SDP_DATA_SEQ32: + SDP_GET32(len, rsp_tmp); + break; + + default: + ss->error = ENOATTR; + return (-1); + /* NOT REACHED */ + } + + /* Now rsp_tmp points to list of (attr,value) pairs */ + for (; len > 0 && vlen > 0; vp ++, vlen --) { + /* Attribute */ + SDP_GET8(t, rsp_tmp); + if (t != SDP_DATA_UINT16) { + ss->error = ENOATTR; + return (-1); + } + SDP_GET16(vp->attr, rsp_tmp); + + /* Attribute value */ + switch (rsp_tmp[0]) { + case SDP_DATA_NIL: + alen = 0; + break; + + case SDP_DATA_UINT8: + case SDP_DATA_INT8: + case SDP_DATA_BOOL: + alen = sizeof(uint8_t); + break; + + case SDP_DATA_UINT16: + case SDP_DATA_INT16: + case SDP_DATA_UUID16: + alen = sizeof(uint16_t); + break; + + case SDP_DATA_UINT32: + case SDP_DATA_INT32: + case SDP_DATA_UUID32: + alen = sizeof(uint32_t); + break; + + case SDP_DATA_UINT64: + case SDP_DATA_INT64: + alen = sizeof(uint64_t); + break; + + case SDP_DATA_UINT128: + case SDP_DATA_INT128: + case SDP_DATA_UUID128: + alen = sizeof(uint128_t); + break; + + case SDP_DATA_STR8: + case SDP_DATA_URL8: + case SDP_DATA_SEQ8: + case SDP_DATA_ALT8: + alen = rsp_tmp[1] + sizeof(uint8_t); + break; + + case SDP_DATA_STR16: + case SDP_DATA_URL16: + case SDP_DATA_SEQ16: + case SDP_DATA_ALT16: + alen = ((uint16_t)rsp_tmp[1] << 8) + | ((uint16_t)rsp_tmp[2]); + alen += sizeof(uint16_t); + break; + + case SDP_DATA_STR32: + case SDP_DATA_URL32: + case SDP_DATA_SEQ32: + case SDP_DATA_ALT32: + alen = ((uint32_t)rsp_tmp[1] << 24) + | ((uint32_t)rsp_tmp[2] << 16) + | ((uint32_t)rsp_tmp[3] << 8) + | ((uint32_t)rsp_tmp[4]); + alen += sizeof(uint32_t); + break; + + default: + ss->error = ENOATTR; + return (-1); + /* NOT REACHED */ + } + + alen += sizeof(uint8_t); + + if (vp->value != NULL) { + if (alen <= vp->vlen) { + vp->flags = SDP_ATTR_OK; + vp->vlen = alen; + } else + vp->flags = SDP_ATTR_TRUNCATED; + + memcpy(vp->value, rsp_tmp, vp->vlen); + } else + vp->flags = SDP_ATTR_INVALID; + + len -= ( + sizeof(uint8_t) + sizeof(uint16_t) + + alen + ); + + rsp_tmp += alen; + } + } +done: + ss->error = 0; + + return (0); +} |