summaryrefslogtreecommitdiff
path: root/usr.sbin/btd
diff options
context:
space:
mode:
authorUwe Stuehler <uwe@cvs.openbsd.org>2008-11-24 23:34:43 +0000
committerUwe Stuehler <uwe@cvs.openbsd.org>2008-11-24 23:34:43 +0000
commitb76ea0f34f6d26d8711d402b22cc4aa21d8bac6c (patch)
treeb8b93e3d3e731a3dd6da39f8b778856970f8b23f /usr.sbin/btd
parentb6679679f13b731ba42b47d62e0c1c9c441021fe (diff)
Bluetooth daemon and contrl utility, one for all, work in progress
Diffstat (limited to 'usr.sbin/btd')
-rw-r--r--usr.sbin/btd/Makefile14
-rw-r--r--usr.sbin/btd/bt.c154
-rw-r--r--usr.sbin/btd/btd.c243
-rw-r--r--usr.sbin/btd/btd.h161
-rw-r--r--usr.sbin/btd/conf.c123
-rw-r--r--usr.sbin/btd/control.c208
-rw-r--r--usr.sbin/btd/db.c203
-rw-r--r--usr.sbin/btd/devinfo.c100
-rw-r--r--usr.sbin/btd/hci.c498
-rw-r--r--usr.sbin/btd/log.c175
-rw-r--r--usr.sbin/btd/parse.y521
-rw-r--r--usr.sbin/btd/sdp.c738
-rw-r--r--usr.sbin/btd/util.c33
13 files changed, 3171 insertions, 0 deletions
diff --git a/usr.sbin/btd/Makefile b/usr.sbin/btd/Makefile
new file mode 100644
index 00000000000..2fabc09fb19
--- /dev/null
+++ b/usr.sbin/btd/Makefile
@@ -0,0 +1,14 @@
+# $OpenBSD: Makefile,v 1.1 2008/11/24 23:34:41 uwe Exp $
+
+PROG= btd
+SRCS= bt.c btd.c conf.c control.c db.c devinfo.c \
+ hci.c log.c sdp.c util.c
+NOMAN=
+
+LDADD+= -levent -lbluetooth -lsdp -lusbhid
+LDFLAGS+= -L/usr/local/lib
+CPPFLAGS+= -I${.CURDIR} -I${.CURDIR}/../btctl -I/usr/local/include
+COPTS+= -Wall -Werror
+DEBUG= -g
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/btd/bt.c b/usr.sbin/btd/bt.c
new file mode 100644
index 00000000000..aac9cc66247
--- /dev/null
+++ b/usr.sbin/btd/bt.c
@@ -0,0 +1,154 @@
+/* $OpenBSD: bt.c,v 1.1 2008/11/24 23:34:41 uwe Exp $ */
+
+/*
+ * Copyright (c) 2008 Uwe Stuehler <uwe@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#include <dev/bluetooth/btdev.h>
+
+#include <bluetooth.h>
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <usbhid.h>
+
+#include "btd.h"
+
+void bt_sighdlr(int, short, void *);
+
+struct event ev_sigint;
+struct event ev_sigterm;
+struct event ev_siginfo;
+
+struct bufferevent *ev_priv;
+void bt_priv_readcb(struct bufferevent *, void *);
+void bt_priv_writecb(struct bufferevent *, void *);
+void bt_priv_errorcb(struct bufferevent *, short, void *);
+
+void
+bt_sighdlr(int sig, short ev, void *arg)
+{
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ (void)event_loopexit(NULL);
+ break;
+
+ case SIGINFO:
+ /* report status */
+ break;
+ }
+}
+
+pid_t
+bt_main(int pipe_prnt[2], struct btd *env, struct passwd *pw)
+{
+ struct stat stb;
+ pid_t pid;
+
+ switch (pid = fork()) {
+ case -1:
+ fatal("cannot fork");
+ break;
+ case 0:
+ break;
+ default:
+ return pid;
+ }
+
+ setproctitle("bt engine");
+
+ event_init();
+
+ hid_init(NULL);
+
+ db_open(BTD_DB, &env->db);
+
+ if (hci_init(env))
+ fatalx("can't set up HCI listeners");
+
+ control_init(env);
+
+ if (stat(pw->pw_dir, &stb) == -1)
+ fatal("stat");
+ if (stb.st_uid != 0 || (stb.st_mode & (S_IWGRP|S_IWOTH)) != 0)
+ fatalx("bad privsep dir permissions");
+ if (chroot(pw->pw_dir) == -1)
+ fatal("chroot");
+ if (chdir("/") == -1)
+ fatal("chdir(\"/\")");
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ fatal("can't drop privileges");
+
+ close(pipe_prnt[0]);
+
+ if ((ev_priv = bufferevent_new(pipe_prnt[1], bt_priv_readcb,
+ bt_priv_writecb, bt_priv_errorcb, env)) == NULL)
+ fatalx("bufferevent_new ev_priv");
+
+ bufferevent_enable(ev_priv, EV_READ);
+
+ signal_set(&ev_sigint, SIGINT, bt_sighdlr, NULL);
+ signal_set(&ev_sigterm, SIGTERM, bt_sighdlr, NULL);
+ signal_set(&ev_siginfo, SIGINFO, bt_sighdlr, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_siginfo, NULL);
+
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGCHLD, SIG_DFL);
+
+ log_info("bt engine ready");
+
+ (void)event_dispatch();
+
+ log_info("bt engine exiting");
+ exit(0);
+}
+
+void
+bt_priv_readcb(struct bufferevent *ev, void *arg)
+{
+ log_debug(__func__);
+}
+
+void
+bt_priv_writecb(struct bufferevent *ev, void *arg)
+{
+ /* nothing to do */
+ log_debug(__func__);
+}
+
+void
+bt_priv_errorcb(struct bufferevent *ev, short what, void *arg)
+{
+ log_warnx("priv pipe error, what=%#x", what);
+ exit(0);
+}
diff --git a/usr.sbin/btd/btd.c b/usr.sbin/btd/btd.c
new file mode 100644
index 00000000000..f2c04fec282
--- /dev/null
+++ b/usr.sbin/btd/btd.c
@@ -0,0 +1,243 @@
+/* $OpenBSD: btd.c,v 1.1 2008/11/24 23:34:42 uwe Exp $ */
+
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+#include <dev/bluetooth/btdev.h>
+
+#include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "btd.h"
+
+void sighdlr(int, short, void *);
+__dead void usage(void);
+int check_child(pid_t, const char *);
+
+static const char *progname;
+static int quit = 0;
+static int reconfig = 0;
+static int sigchld = 0;
+
+static struct event ev_sigchld;
+static struct event ev_sighup;
+static struct event ev_sigint;
+static struct event ev_sigterm;
+static struct bufferevent *ev_bt;
+
+static void readcb(struct bufferevent *, void *);
+static void writecb(struct bufferevent *, void *);
+static void errorcb(struct bufferevent *, short, void *);
+
+void
+sighdlr(int sig, short what, void *arg)
+{
+ switch (sig) {
+ case SIGTERM:
+ case SIGINT:
+ quit = 1;
+ break;
+ case SIGCHLD:
+ sigchld = 1;
+ break;
+ case SIGHUP:
+ reconfig = 1;
+ break;
+ }
+}
+
+__dead void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-d]\n", progname);
+ exit(2);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct btd env;
+ pid_t chld_pid, pid;
+ int pipe_chld[2];
+ struct passwd *pw;
+ int ch;
+
+ progname = basename(argv[0]);
+ bzero(&env, sizeof(env));
+ TAILQ_INIT(&env.interfaces);
+ TAILQ_INIT(&env.devices);
+
+ while ((ch = getopt(argc, argv, "d")) != -1) {
+ switch (ch) {
+ case 'd':
+ env.debug = 1;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ if (argc > 0) {
+ usage();
+ /* NOTREACHED */
+ }
+
+ if (geteuid() != 0)
+ errx(1, "need root privileges");
+
+ if ((pw = getpwnam(BTD_USER)) == NULL)
+ errx(1, "unknown user %s", BTD_USER);
+
+ endpwent();
+
+ log_init(env.debug);
+
+ if (!env.debug && daemon(1, 0))
+ fatal("daemon");
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_chld) == -1)
+ fatal("socketpair");
+
+ /* fork child process */
+ sigchld = 1;
+ chld_pid = bt_main(pipe_chld, &env, pw);
+
+ setproctitle("[priv]");
+
+ /* only after daemon() */
+ event_init();
+
+ signal_set(&ev_sigchld, SIGCHLD, sighdlr, &env);
+ signal_set(&ev_sighup, SIGHUP, sighdlr, &env);
+ signal_set(&ev_sigint, SIGINT, sighdlr, &env);
+ signal_set(&ev_sigterm, SIGTERM, sighdlr, &env);
+ signal_add(&ev_sigchld, NULL);
+ signal_add(&ev_sighup, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+
+ close(pipe_chld[1]);
+
+ if ((ev_bt = bufferevent_new(pipe_chld[0], readcb,
+ writecb, errorcb, &env)) == NULL)
+ fatalx("bufferevent_new ev_bt");
+
+ bufferevent_enable(ev_bt, EV_READ);
+
+ while (quit == 0) {
+ if (!event_loop(EVLOOP_ONCE))
+ quit = 1;
+
+ if (sigchld) {
+ if (check_child(chld_pid, "child")) {
+ quit = 1;
+ chld_pid = 0;
+ }
+ sigchld = 0;
+ }
+ }
+
+ signal_del(&ev_sigchld);
+
+ if (chld_pid)
+ kill(chld_pid, SIGTERM);
+
+ do {
+ if ((pid = wait(NULL)) == -1 &&
+ errno != EINTR && errno != ECHILD)
+ fatal("wait");
+ } while (pid != -1 || (pid == -1 && errno == EINTR));
+
+ log_info("Terminating");
+ control_cleanup();
+ return 0;
+}
+
+int
+check_child(pid_t pid, const char *pname)
+{
+ int status, sig;
+ char *signame;
+
+ if (waitpid(pid, &status, WNOHANG) > 0) {
+ if (WIFEXITED(status)) {
+ log_warnx("Lost child: %s exited", pname);
+ return (1);
+ }
+ if (WIFSIGNALED(status)) {
+ sig = WTERMSIG(status);
+ signame = strsignal(sig) ? strsignal(sig) : "unknown";
+ log_warnx("Lost child: %s terminated; signal %d (%s)",
+ pname, sig, signame);
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+static void
+readcb(struct bufferevent *ev, void *arg)
+{
+ log_warnx("readcb");
+}
+
+static void
+writecb(struct bufferevent *ev, void *arg)
+{
+ /* nothing to do here */
+ log_warnx("writecb");
+}
+
+static void
+errorcb(struct bufferevent *ev, short what, void *arg)
+{
+ log_warnx("Pipe error");
+ quit = 1;
+}
+
+#ifdef notyet
+void
+btd_devctl(struct imsg *imsg)
+{
+ struct btdev_attach_args baa;
+ char buf[sizeof(int) + sizeof(bdaddr_t)];
+ unsigned long cmd;
+ int res;
+ int fd;
+
+ if (devinfo_load_attach_args(&baa, imsg->data,
+ imsg->hdr.len - IMSG_HEADER_SIZE))
+ fatalx("invalid IMSG_ATTACH/DETACH received");
+
+ if ((fd = open(BTHUB_PATH, O_WRONLY, 0)) == -1) {
+ res = errno;
+ log_warn("can't open %s", BTHUB_PATH);
+ goto ret;
+ }
+
+ cmd = imsg->hdr.type == IMSG_ATTACH ? BTDEV_ATTACH : BTDEV_DETACH;
+ if (ioctl(fd, cmd, &baa) == -1)
+ res = errno;
+
+ res = 0;
+ (void)close(fd);
+ret:
+ devinfo_unload_attach_args(&baa);
+
+ memcpy(buf, &res, sizeof(res));
+ memcpy((int *)buf + 1, &baa.bd_raddr, sizeof(bdaddr_t));
+ /* send reply */
+}
+#endif
diff --git a/usr.sbin/btd/btd.h b/usr.sbin/btd/btd.h
new file mode 100644
index 00000000000..1e88ff00546
--- /dev/null
+++ b/usr.sbin/btd/btd.h
@@ -0,0 +1,161 @@
+/* $OpenBSD: btd.h,v 1.1 2008/11/24 23:34:42 uwe Exp $ */
+
+/*
+ * Copyright (c) 2008 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <dev/bluetooth/btdev.h>
+#include <netbt/bluetooth.h>
+
+#include <db.h>
+#include <event.h>
+#include <pwd.h>
+#include <stdarg.h>
+
+#define DEFAULT_INTERFACE_NAME "OpenBSD"
+
+#define BTD_DB "/var/db/btd.db"
+#define BTD_SOCKET "/var/run/btd.sock"
+#define BTD_USER "_btd"
+
+/* XXX there is no need for more than one bthub device! */
+#define BTHUB_PATH "/dev/bthub0"
+
+#define READ_BUF_SIZE 8192
+
+struct btd;
+
+struct bt_interface {
+ TAILQ_ENTRY(bt_interface) entry;
+ struct event ev;
+ struct btd *env;
+ const char *xname;
+ bdaddr_t addr;
+ const char *name;
+ int disabled;
+ int fd;
+};
+
+struct btd_db {
+ DB *dbh;
+};
+
+struct btd_hci {
+ struct bt_interface *inquiry_interface;
+ int inquiry_running;
+ time_t last_inquiry; /* start time */
+};
+
+struct bt_devinfo {
+ /* XXX don't tie it to a kernel structure */
+ struct btdev_attach_args baa;
+};
+
+struct bt_device {
+ TAILQ_ENTRY(bt_device) entry;
+ bdaddr_t addr;
+ uint16_t type;
+ uint8_t *pin; /* HCI_PIN_SIZE when not NULL */
+ time_t last_seen; /* last response to an inquiry */
+ int flags;
+ struct bt_devinfo info;
+};
+
+#define BTDF_VISIBLE 0x0001 /* device is discoverable */
+#define BTDF_ATTACH 0x0002 /* attempt to attach a driver */
+
+struct btd {
+ int debug;
+ struct btd_db db;
+ struct btd_hci hci;
+ TAILQ_HEAD(interfaces, bt_interface) interfaces;
+ TAILQ_HEAD(devices, bt_device) devices;
+};
+
+/* ipc messages */
+
+enum imsg_type {
+ IMSG_NONE,
+ IMSG_CONFIG_BEGIN,
+ IMSG_CONFIG_INTERFACE,
+ IMSG_CONFIG_DEVICE,
+ IMSG_CONFIG_COMMIT,
+ IMSG_CONFIG_ROLLBACK,
+ IMSG_ATTACH,
+ IMSG_DETACH
+};
+
+/* prototypes */
+
+/* bt.c */
+pid_t bt_main(int[2], struct btd *, struct passwd *);
+
+/* conf.c */
+struct bt_device *conf_add_device(struct btd *,
+ const bdaddr_t *);
+struct bt_device *conf_find_device(const struct btd *,
+ const bdaddr_t *);
+struct bt_interface *conf_add_interface(struct btd *,
+ const bdaddr_t *);
+struct bt_interface *conf_find_interface(const struct btd *,
+ const bdaddr_t *);
+const uint8_t *conf_lookup_pin(const struct btd *, const bdaddr_t *);
+
+/* control.c */
+void control_init(struct btd *);
+void control_cleanup(void);
+
+/* db.c */
+void db_open(const char *, struct btd_db *);
+int db_put_link_key(struct btd_db *, const bdaddr_t *, const uint8_t *);
+int db_get_link_key(struct btd_db *, const bdaddr_t *, uint8_t *);
+int db_put_devinfo(struct btd_db *, const bdaddr_t *,
+ const struct bt_devinfo *);
+int db_get_devinfo(struct btd_db *, const bdaddr_t *, struct bt_devinfo *);
+void db_dump(struct btd_db *);
+
+/* devinfo.c */
+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 *);
+int devinfo_load_attach_args(struct btdev_attach_args *, void *, size_t);
+void devinfo_unload_attach_args(struct btdev_attach_args *);
+void devinfo_dump(const struct bt_devinfo *);
+
+/* hci.c */
+int hci_init(struct btd *);
+time_t hci_ping(struct btd *);
+int hci_dispatch(int, struct btd *);
+
+/* log.c */
+extern int debug;
+void log_init(int);
+void vlog(int, const char *, va_list);
+void log_warn(const char *, ...);
+void log_warnx(const char *, ...);
+void log_info(const char *, ...);
+void log_debug(const char *, ...);
+void log_packet(const bdaddr_t *, const bdaddr_t *, const char *, ...);
+void fatal(const char *);
+void fatalx(const char *);
+
+/* sdp.c */
+int sdp_query(struct btdev_attach_args *, bdaddr_t *, bdaddr_t *, const char *);
+
+/* util.c */
+time_t getmonotime(void);
diff --git a/usr.sbin/btd/conf.c b/usr.sbin/btd/conf.c
new file mode 100644
index 00000000000..7ed0f67f5a4
--- /dev/null
+++ b/usr.sbin/btd/conf.c
@@ -0,0 +1,123 @@
+#include <sys/types.h>
+
+#include <netbt/hci.h>
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "btd.h"
+
+struct bt_device *
+conf_add_device(struct btd *conf, const bdaddr_t *addr)
+{
+ struct bt_device *btdev;
+ struct bt_device *defaults;
+
+ assert(conf_find_device(conf, addr) == NULL);
+
+ btdev = calloc(1, sizeof(*btdev));
+ if (btdev == NULL) {
+ log_warn("conf_add_device");
+ return NULL;
+ }
+
+ bdaddr_copy(&btdev->addr, addr);
+
+ defaults = bdaddr_any(addr) ? NULL :
+ conf_find_device(conf, BDADDR_ANY);
+
+ if (defaults != NULL) {
+ btdev->type = defaults->type;
+ btdev->flags = defaults->flags;
+
+ if (defaults->pin != NULL) {
+ if ((btdev->pin = malloc(HCI_KEY_SIZE)) == NULL) {
+ log_warn("conf_add_device malloc");
+ TAILQ_REMOVE(&conf->devices, btdev, entry);
+ return NULL;
+ }
+ memcpy(btdev->pin, defaults->pin, HCI_KEY_SIZE);
+ }
+ }
+
+ TAILQ_INSERT_TAIL(&conf->devices, btdev, entry);
+
+ return btdev;
+}
+
+struct bt_interface *
+conf_add_interface(struct btd *conf, const bdaddr_t *addr)
+{
+ struct bt_interface *iface;
+ struct bt_interface *defaults;
+
+ assert(conf_find_interface(conf, addr) == NULL);
+
+ iface = calloc(1, sizeof(*iface));
+ if (iface == NULL) {
+ log_warn("conf_add_interface");
+ return NULL;
+ }
+
+ bdaddr_copy(&iface->addr, addr);
+ iface->env = conf;
+ iface->fd = -1;
+
+ defaults = bdaddr_any(addr) ? NULL :
+ conf_find_interface(conf, BDADDR_ANY);
+
+ if (defaults != NULL) {
+ if (defaults->name != NULL &&
+ (iface->name = strdup(defaults->name)) == NULL) {
+ log_warn("conf_add_interface strdup");
+ TAILQ_REMOVE(&conf->interfaces, iface, entry);
+ free(iface);
+ return NULL;
+ }
+
+ iface->disabled = defaults->disabled;
+ }
+
+ TAILQ_INSERT_TAIL(&conf->interfaces, iface, entry);
+
+ return iface;
+}
+
+struct bt_device *
+conf_find_device(const struct btd *conf, const bdaddr_t *addr)
+{
+ struct bt_device *btdev;
+
+ TAILQ_FOREACH(btdev, &conf->devices, entry) {
+ if (bdaddr_same(&btdev->addr, addr))
+ return btdev;
+ }
+
+ return NULL;
+}
+
+struct bt_interface *
+conf_find_interface(const struct btd *conf, const bdaddr_t *addr)
+{
+ struct bt_interface *iface;
+
+ TAILQ_FOREACH(iface, &conf->interfaces, entry) {
+ if (bdaddr_same(&iface->addr, addr))
+ return iface;
+ }
+
+ return NULL;
+}
+
+const uint8_t *
+conf_lookup_pin(const struct btd *conf, const bdaddr_t *addr)
+{
+ struct bt_device *btdev;
+
+ if ((btdev = conf_find_device(conf, addr)) == NULL &&
+ (btdev = conf_find_device(conf, BDADDR_ANY)) == NULL)
+ return NULL;
+
+ return btdev->pin;
+}
diff --git a/usr.sbin/btd/control.c b/usr.sbin/btd/control.c
new file mode 100644
index 00000000000..f81961e2b7f
--- /dev/null
+++ b/usr.sbin/btd/control.c
@@ -0,0 +1,208 @@
+/* $OpenBSD: control.c,v 1.1 2008/11/24 23:34:42 uwe Exp $ */
+
+/*
+ * Copyright (c) 2008 Uwe Stuehler <uwe@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "btd.h"
+#include "btctl.h"
+
+#define CONTROL_BACKLOG 5
+
+void control_acceptcb(int, short, void *);
+void control_readcb(struct bufferevent *, void *);
+void control_errorcb(struct bufferevent *, short, void *);
+
+void socket_blockmode(int, int);
+
+struct control_connection {
+ TAILQ_ENTRY(control_connection) entry;
+ struct bufferevent *ev;
+ int fd;
+};
+
+static struct event ev_control;
+static TAILQ_HEAD(, control_connection) connections =
+ TAILQ_HEAD_INITIALIZER(connections);
+
+void
+control_init(struct btd *env)
+{
+ struct sockaddr_un sun;
+ mode_t old_umask;
+ int fd;
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ fatal("control_init: socket");
+
+ sun.sun_family = AF_UNIX;
+ if (strlcpy(sun.sun_path, BTD_SOCKET,
+ sizeof(sun.sun_path)) >= sizeof(sun.sun_path))
+ fatalx("control_init: socket name too long");
+
+ if (unlink(BTD_SOCKET) == -1)
+ if (errno != ENOENT)
+ fatal("control_init: unlink");
+
+ old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
+ if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
+ fatal("control_init: bind");
+ (void)umask(old_umask);
+
+ if (chmod(BTD_SOCKET, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
+ log_warn("control_init: chmod");
+ close(fd);
+ (void)unlink(BTD_SOCKET);
+ exit(1);
+ }
+
+ if (listen(fd, CONTROL_BACKLOG) == -1)
+ fatal("control_init: listen");
+
+ socket_blockmode(fd, 0);
+
+ event_set(&ev_control, fd, EV_READ | EV_PERSIST,
+ control_acceptcb, env);
+ event_add(&ev_control, NULL);
+}
+
+void
+control_cleanup(void)
+{
+ if (unlink(BTD_SOCKET) == -1)
+ fatal("control_init: unlink");
+}
+
+void
+control_acceptcb(int fd, short evflags, void *arg)
+{
+ struct control_connection *conn;
+ struct sockaddr_storage sa;
+ socklen_t salen = sizeof(sa);
+ int new_fd;
+
+ if ((new_fd = accept(fd, (struct sockaddr *)&sa, &salen)) == -1) {
+ log_warn("control_eventcb: accept");
+ return;
+ }
+
+ if ((conn = calloc(1, sizeof(*conn))) == NULL)
+ fatal("control_eventcb: calloc");
+
+ conn->fd = new_fd;
+
+ if ((conn->ev = bufferevent_new(new_fd, control_readcb, NULL,
+ control_errorcb, conn)) == NULL)
+ fatal("control_errorcb: bufferevent_new");
+
+ TAILQ_INSERT_TAIL(&connections, conn, entry);
+ bufferevent_enable(conn->ev, EV_READ);
+}
+
+void
+control_readcb(struct bufferevent *ev, void *arg)
+{
+ btctl_interface_stmt interface_stmt;
+ btctl_attach_stmt attach_stmt;
+ struct control_connection *conn = arg;
+ enum btctl_stmt_type stmt_type;
+ size_t stmt_size = 0;
+ int err;
+
+ if (EVBUFFER_LENGTH(EVBUFFER_INPUT(ev)) < sizeof(stmt_type))
+ return;
+
+ memcpy(&stmt_type, EVBUFFER_DATA(EVBUFFER_INPUT(ev)),
+ sizeof(stmt_type));
+
+ switch (stmt_type) {
+ case BTCTL_CONFIG:
+ case BTCTL_COMMIT:
+ case BTCTL_ROLLBACK:
+ break;
+ case BTCTL_INTERFACE_STMT:
+ stmt_size += sizeof(btctl_interface_stmt);
+ break;
+ case BTCTL_ATTACH_STMT:
+ stmt_size += sizeof(btctl_attach_stmt);
+ break;
+ }
+
+ if (EVBUFFER_LENGTH(EVBUFFER_INPUT(ev)) <= stmt_size)
+ return;
+
+ bufferevent_read(ev, &stmt_type, sizeof(stmt_type));
+ log_debug("stmt %#x size %lu", stmt_type, stmt_size);
+
+ switch (stmt_type) {
+ case BTCTL_CONFIG:
+ case BTCTL_COMMIT:
+ case BTCTL_ROLLBACK:
+ break;
+ case BTCTL_INTERFACE_STMT:
+ bufferevent_read(ev, &interface_stmt, stmt_size);
+ break;
+ case BTCTL_ATTACH_STMT:
+ bufferevent_read(ev, &attach_stmt, stmt_size);
+ break;
+ default:
+ log_warnx("Invalid control packet of type %#x", stmt_type);
+ close(conn->fd);
+ return;
+ }
+
+ err = 0;
+ bufferevent_write(ev, &err, sizeof(err));
+}
+
+void
+control_errorcb(struct bufferevent *ev, short what, void *arg)
+{
+ struct control_connection *conn = arg;
+
+ TAILQ_REMOVE(&connections, conn, entry);
+ bufferevent_free(conn->ev);
+ close(conn->fd);
+ free(conn);
+}
+
+void
+socket_blockmode(int fd, int block)
+{
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
+ fatal("fcntl F_GETFL");
+
+ if (block)
+ flags &= ~O_NONBLOCK;
+ else
+ flags |= O_NONBLOCK;
+
+ if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
+ fatal("fcntl F_SETFL");
+}
diff --git a/usr.sbin/btd/db.c b/usr.sbin/btd/db.c
new file mode 100644
index 00000000000..e147b072b8d
--- /dev/null
+++ b/usr.sbin/btd/db.c
@@ -0,0 +1,203 @@
+#include <sys/stat.h>
+
+#include <netbt/hci.h>
+
+#include <assert.h>
+#include <bluetooth.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "btd.h"
+
+typedef enum {
+ DB_DEVINFO,
+ DB_LINK_KEY
+} db_type;
+
+typedef struct {
+ bdaddr_t addr;
+ db_type type;
+} db_key;
+
+int db_put(struct btd_db *, db_key *, const void *, size_t);
+int db_get_raw(struct btd_db *, db_key *, void **, size_t *);
+int db_get_exact(struct btd_db *, db_key *, void *, size_t);
+
+void
+db_open(const char *file, struct btd_db *db)
+{
+ assert(db->dbh == NULL);
+
+ db->dbh = dbopen(file, O_CREAT | O_EXLOCK | O_RDWR,
+ S_IRUSR | S_IWUSR, DB_HASH, NULL);
+ if (db->dbh == NULL)
+ fatal(file);
+}
+
+int
+db_put(struct btd_db *db, db_key *key, const void *data, size_t size)
+{
+ DB *dbh = db->dbh;
+ DBT dbk, dbd;
+
+ memset(&dbk, 0, sizeof(dbk));
+ dbk.size = sizeof(db_key);
+ dbk.data = (void *)key;
+ memset(&dbd, 0, sizeof(dbd));
+ dbd.size = size;
+ dbd.data = (void*)data;
+
+ if (dbh->put(dbh, &dbk, &dbd, 0)) {
+ log_warn("db_put");
+ return -1;
+ }
+
+ if (dbh->sync(dbh, 0)) {
+ log_warn("db_sync");
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+db_get_raw(struct btd_db *db, db_key *key, void **data, size_t *size)
+{
+ DB *dbh = db->dbh;
+ DBT dbk, dbd;
+ int res;
+
+ memset(&dbk, 0, sizeof(dbk));
+ dbk.size = sizeof(db_key);
+ dbk.data = (void *)key;
+ memset(&dbd, 0, sizeof(dbd));
+
+ if ((res = dbh->get(dbh, &dbk, &dbd, 0)) != 0) {
+ if (res == -1)
+ log_warn("db_get");
+ return res;
+ }
+
+ *data = dbd.data;
+ *size = dbd.size;
+ return 0;
+}
+
+int
+db_get_exact(struct btd_db *db, db_key *key, void *data, size_t size)
+{
+ void *d;
+ size_t dlen;
+ int res;
+
+ if ((res = db_get_raw(db, key, &d, &dlen)) == 0) {
+ if (dlen != size) {
+ log_warnx("data size mismatch for key type %#x"
+ " (%u != %u)", key->type, dlen, size);
+ return -1;
+ }
+ memcpy(data, d, size);
+ }
+
+ return res;
+}
+
+int
+db_put_link_key(struct btd_db *db, const bdaddr_t *addr,
+ const uint8_t *link_key)
+{
+ db_key key;
+
+ memset(&key, 0, sizeof(key));
+ bdaddr_copy(&key.addr, addr);
+ key.type = DB_LINK_KEY;
+
+ return db_put(db, &key, link_key, HCI_KEY_SIZE);
+}
+
+int
+db_get_link_key(struct btd_db *db, const bdaddr_t *addr, uint8_t *link_key)
+{
+ db_key key;
+
+ memset(&key, 0, sizeof(key));
+ bdaddr_copy(&key.addr, addr);
+ key.type = DB_LINK_KEY;
+
+ return db_get_exact(db, &key, link_key, HCI_KEY_SIZE);
+}
+
+int
+db_put_devinfo(struct btd_db *db, const bdaddr_t *addr,
+ const struct bt_devinfo *info)
+{
+ db_key key;
+ void *data;
+ size_t size;
+ int res;
+
+ memset(&key, 0, sizeof(key));
+ bdaddr_copy(&key.addr, addr);
+ key.type = DB_DEVINFO;
+
+ if (devinfo_store(info, &data, &size))
+ return -1;
+
+ res = db_put(db, &key, data, size);
+ free(data);
+ return res;
+}
+
+int
+db_get_devinfo(struct btd_db *db, const bdaddr_t *addr,
+ struct bt_devinfo *info)
+{
+ db_key key;
+ void *data;
+ size_t size;
+ int res;
+
+ memset(&key, 0, sizeof(key));
+ bdaddr_copy(&key.addr, addr);
+ key.type = DB_DEVINFO;
+
+ if ((res = db_get_raw(db, &key, &data, &size)) == 0 &&
+ devinfo_load(info, data, size))
+ res = -1;
+
+ return res;
+}
+
+void
+db_dump(struct btd_db *db)
+{
+ struct bt_devinfo info;
+ DB *dbh = db->dbh;
+ DBT dbk, dbd;
+
+ if (dbh->seq(dbh, &dbk, &dbd, R_FIRST) != 0)
+ return;
+ do {
+ db_key *key = dbk.data;
+
+ if (dbk.size != sizeof(db_key))
+ fatalx("invalid db key");
+
+ switch (key->type) {
+ case DB_LINK_KEY:
+ log_info("%s link_key", bt_ntoa(&key->addr, NULL));
+ break;
+ case DB_DEVINFO:
+ log_info("%s devinfo", bt_ntoa(&key->addr, NULL));
+ if (db_get_devinfo(db, &key->addr, &info) == 0) {
+ devinfo_dump(&info);
+ devinfo_unload(&info);
+ }
+ break;
+ default:
+ fatalx("invalid db key type");
+ }
+ }
+ while (dbh->seq(dbh, &dbk, &dbd, R_NEXT) == 0);
+}
diff --git a/usr.sbin/btd/devinfo.c b/usr.sbin/btd/devinfo.c
new file mode 100644
index 00000000000..5471b2bbe4a
--- /dev/null
+++ b/usr.sbin/btd/devinfo.c
@@ -0,0 +1,100 @@
+#include <bluetooth.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "btd.h"
+
+int
+devinfo_store(const struct bt_devinfo *info, void **data, size_t *datalen)
+{
+ *datalen = sizeof(info->baa);
+ if (info->baa.bd_type == BTDEV_HID)
+ *datalen += info->baa.bd_hid.hid_dlen;
+
+ if ((*data = malloc(*datalen)) == NULL) {
+ log_warn("devinfo_store");
+ return -1;
+ }
+
+ memcpy(*data, &info->baa, sizeof(info->baa));
+ if (info->baa.bd_type == BTDEV_HID &&
+ info->baa.bd_hid.hid_dlen > 0)
+ memcpy((uint8_t *)*data + sizeof(info->baa),
+ info->baa.bd_hid.hid_desc,
+ info->baa.bd_hid.hid_dlen);
+
+ return 0;
+}
+
+int
+devinfo_load(struct bt_devinfo *info, void *data, size_t datalen)
+{
+ return devinfo_load_attach_args(&info->baa, data, datalen);
+}
+
+void
+devinfo_unload(struct bt_devinfo *info)
+{
+ return devinfo_unload_attach_args(&info->baa);
+}
+
+int
+devinfo_load_attach_args(struct btdev_attach_args *baa, void *data,
+ size_t datalen)
+{
+ if (datalen < sizeof(*baa)) {
+ log_warnx("devinfo data too short");
+ memset(baa, 0, sizeof(*baa));
+ return -1;
+ }
+
+ memcpy(baa, data, sizeof(*baa));
+ data = (struct btdev_attach_args *)data + 1;
+ datalen -= sizeof(*baa);
+
+ if (baa->bd_type == BTDEV_HID) {
+ uint16_t dlen = baa->bd_hid.hid_dlen;
+ void *desc = NULL;
+
+ if (datalen != dlen) {
+ log_warnx("bad devinfo data length (HID)");
+ return -1;
+ }
+
+ if (dlen > 0) {
+ if ((desc = malloc(dlen)) == NULL) {
+ log_warn("devinfo_load_attach_args");
+ return -1;
+ }
+ memcpy(desc, data, dlen);
+ }
+
+ baa->bd_hid.hid_desc = desc;
+ } else if (datalen > 0) {
+ log_warnx("devinfo data too long");
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+devinfo_unload_attach_args(struct btdev_attach_args *baa)
+{
+ if (baa->bd_type == BTDEV_HID && baa->bd_hid.hid_desc != NULL) {
+ free(baa->bd_hid.hid_desc);
+ baa->bd_hid.hid_desc = NULL;
+ baa->bd_hid.hid_dlen = 0;
+ }
+}
+
+void
+devinfo_dump(const struct bt_devinfo *info)
+{
+ const struct btdev_attach_args *baa = &info->baa;
+
+ log_info("laddr %s", bt_ntoa(&baa->bd_laddr, NULL));
+ log_info("raddr %s", bt_ntoa(&baa->bd_raddr, NULL));
+ log_info("type %#x", baa->bd_type);
+ log_info("mode %d", baa->bd_mode);
+}
diff --git a/usr.sbin/btd/hci.c b/usr.sbin/btd/hci.c
new file mode 100644
index 00000000000..93d42318b9c
--- /dev/null
+++ b/usr.sbin/btd/hci.c
@@ -0,0 +1,498 @@
+/* $OpenBSD: hci.c,v 1.1 2008/11/24 23:34:42 uwe Exp $ */
+/* $NetBSD: btconfig.c,v 1.13 2008/07/21 13:36:57 lukem Exp $ */
+
+/*-
+ * Copyright (c) 2008 Uwe Stuehler <uwe@openbsd.org>
+ * 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.
+ */
+
+#include <sys/ioctl.h>
+
+#include <assert.h>
+#include <bluetooth.h>
+#include <errno.h>
+#include <event.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "btd.h"
+
+int hci_init_config(struct bt_interface *);
+
+void hci_eventcb(int, short, void *);
+
+int hci_process_pin_code_req(struct bt_interface *,
+ struct sockaddr_bt *, const bdaddr_t *);
+int hci_process_link_key_req(struct bt_interface *,
+ struct sockaddr_bt *, const bdaddr_t *);
+int hci_process_link_key_notification(struct bt_interface *, struct sockaddr_bt *,
+ const hci_link_key_notification_ep *);
+int hci_req(int, uint16_t, uint8_t , void *, size_t, void *, size_t);
+int hci_send_cmd(int, struct sockaddr_bt *, uint16_t, size_t, void *);
+
+#define hci_write(iface, opcode, wbuf, wlen) \
+ hci_req(iface->fd, opcode, 0, wbuf, wlen, NULL, 0)
+#define hci_read(iface, opcode, rbuf, rlen) \
+ hci_req(iface->fd, opcode, 0, NULL, 0, rbuf, rlen)
+#define hci_cmd(iface, opcode, cbuf, clen) \
+ hci_req(iface->fd, opcode, 0, cbuf, clen, NULL, 0)
+
+int
+hci_init(struct btd *env)
+{
+ struct btreq btr;
+ struct bt_interface *defaults;
+ struct bt_interface *iface;
+ int hci;
+
+ hci = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+ if (hci == -1)
+ fatal("could not open raw HCI socket");
+
+ defaults = conf_find_interface(env, BDADDR_ANY);
+ iface = NULL;
+
+ bzero(&btr, sizeof(btr));
+ for (;;) {
+ int flags;
+
+ if (ioctl(hci, SIOCNBTINFO, &btr) == -1)
+ break;
+
+ /*
+ * Interfaces must be up, just to determine their
+ * bdaddr. Grrrr...
+ */
+ if (!((flags = btr.btr_flags) & BTF_UP)) {
+ btr.btr_flags |= BTF_UP;
+ if (ioctl(hci, SIOCSBTFLAGS, &btr) == -1) {
+ log_warn("%s: SIOCSBTFLAGS", btr.btr_name);
+ continue;
+ }
+ if (ioctl(hci, SIOCGBTINFO, &btr) == -1) {
+ log_warn("%s: SIOCGBTINFO", btr.btr_name);
+ continue;
+ }
+ }
+
+ if (!(btr.btr_flags & BTF_UP) ||
+ bdaddr_any(&btr.btr_bdaddr)) {
+ log_warn("could not enable %s", btr.btr_name);
+ goto redisable;
+ }
+
+ /*
+ * Discover device driver unit names for explicitly
+ * configured interfaces.
+ */
+ iface = conf_find_interface(env, &btr.btr_bdaddr);
+ if (iface != NULL) {
+ assert(iface->xname == NULL);
+ iface->xname = strdup(btr.btr_name);
+ if (iface->xname == NULL)
+ fatal("hci_init strdup 1");
+ goto redisable;
+ }
+
+ /*
+ * Add interfaces not explicitly configured.
+ */
+ iface = conf_add_interface(env, &btr.btr_bdaddr);
+ if (iface == NULL)
+ fatalx("hci_init add_interface");
+
+ iface->xname = strdup(btr.btr_name);
+ if (iface->xname == NULL)
+ fatal("hci_init strdup 2");
+
+ redisable:
+ /* See above. Grrrr... */
+ if (!(flags & BTF_UP) &&
+ (iface == NULL || iface->disabled)) {
+ btr.btr_flags &= ~BTF_UP;
+ if (ioctl(hci, SIOCSBTFLAGS, &btr) == -1)
+ fatal("hci_init SIOCSBTFLAGS");
+ }
+ }
+
+ close(hci);
+
+ TAILQ_FOREACH(iface, &env->interfaces, entry) {
+ struct sockaddr_bt sa;
+ struct hci_filter filter;
+
+ if (bdaddr_any(&iface->addr))
+ continue;
+
+ /* Disable interfaces not present in the system. */
+ if (iface->xname == NULL) {
+ iface->disabled = 1;
+ log_info("interface disabled: %s (not present)",
+ bt_ntoa(&iface->addr, NULL));
+ continue;
+ }
+
+ /* Skip disabled interfaces. */
+ if (iface->disabled) {
+ log_info("interface disabled: %s (%s)",
+ bt_ntoa(&iface->addr, NULL), iface->xname);
+ continue;
+ }
+
+ log_info("listening on %s (%s)",
+ bt_ntoa(&iface->addr, NULL), iface->xname);
+
+ iface->fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+ if (iface->fd == -1)
+ fatal("socket");
+
+ bzero(&sa, sizeof(sa));
+ sa.bt_len = sizeof(sa);
+ sa.bt_family = AF_BLUETOOTH;
+ bdaddr_copy(&sa.bt_bdaddr, &iface->addr);
+ if (bind(iface->fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
+ fatal("bind");
+
+ if (connect(iface->fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
+ fatal("connect");
+
+ if (hci_init_config(iface) == -1)
+ fatalx("hci_init_config");
+
+ bzero(&filter, sizeof(filter));
+ hci_filter_set(HCI_EVENT_PIN_CODE_REQ, &filter);
+ hci_filter_set(HCI_EVENT_LINK_KEY_REQ, &filter);
+ hci_filter_set(HCI_EVENT_LINK_KEY_NOTIFICATION, &filter);
+ if (setsockopt(iface->fd, BTPROTO_HCI, SO_HCI_EVT_FILTER,
+ (const void *)&filter, sizeof(filter)) < 0)
+ fatal("setsockopt");
+
+ event_set(&iface->ev, iface->fd, EV_READ | EV_PERSIST,
+ hci_eventcb, iface);
+ event_add(&iface->ev, NULL);
+ }
+
+ /*
+ * Run device discovery on the first available
+ * interface. This should be configurable.
+ */
+ TAILQ_FOREACH(iface, &env->interfaces, entry) {
+ if (!bdaddr_any(&iface->addr) && !iface->disabled) {
+ env->hci.inquiry_interface = iface;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int
+hci_init_config(struct bt_interface *iface)
+{
+ struct btreq btr;
+ struct btd *env = iface->env;
+ struct bt_interface *defaults;
+ const char *name;
+ uint8_t val;
+
+ defaults = conf_find_interface(env, BDADDR_ANY);
+
+ if (iface->name != NULL)
+ name = iface->name;
+ else if (defaults != NULL && defaults->name != NULL)
+ name = defaults->name;
+ else
+ name = NULL;
+
+ if (name != NULL && hci_write(iface, HCI_CMD_WRITE_LOCAL_NAME,
+ (char *)name, HCI_UNIT_NAME_SIZE))
+ return -1;
+
+ if (hci_read(iface, HCI_CMD_READ_SCAN_ENABLE, &val, sizeof(val)))
+ return -1;
+
+ val |= HCI_PAGE_SCAN_ENABLE;
+ val |= HCI_INQUIRY_SCAN_ENABLE;
+
+ if (hci_write(iface, HCI_CMD_WRITE_SCAN_ENABLE, &val, sizeof(val)))
+ return -1;
+
+ bdaddr_copy(&btr.btr_bdaddr, &iface->addr);
+ if (ioctl(iface->fd, SIOCGBTINFOA, &btr) < 0) {
+ log_warn("SIOCGBTINFOA");
+ return -1;
+ }
+
+ val = btr.btr_link_policy;
+ val |= HCI_LINK_POLICY_ENABLE_ROLE_SWITCH;
+ btr.btr_link_policy = val;
+
+ if (ioctl(iface->fd, SIOCSBTPOLICY, &btr) < 0) {
+ log_warn("SIOCSBTPOLICY");
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+hci_eventcb(int fd, short evflags, void *arg)
+{
+ char buf[HCI_EVENT_PKT_SIZE];
+ hci_event_hdr_t *event = (hci_event_hdr_t *)buf;
+ struct bt_interface *iface = arg;
+ struct sockaddr_bt sa;
+ socklen_t size;
+ bdaddr_t *addr;
+ void *ep;
+ int n;
+
+ if (iface == NULL)
+ fatal("HCI event on closed socket?");
+
+ size = sizeof(sa);
+ n = recvfrom(iface->fd, buf, sizeof(buf), 0,
+ (struct sockaddr *)&sa, &size);
+ if (n < 0) {
+ log_warn("could not receive from HCI socket");
+ return;
+ }
+
+ if (event->type != HCI_EVENT_PKT) {
+ log_packet(&sa.bt_bdaddr, &iface->addr,
+ "unexpected HCI packet, type=%#x", event->type);
+ return;
+ }
+
+ addr = (bdaddr_t *)(event + 1);
+ ep = (bdaddr_t *)(event + 1);
+
+ switch (event->event) {
+ case HCI_EVENT_PIN_CODE_REQ:
+ hci_process_pin_code_req(iface, &sa, addr);
+ break;
+
+ case HCI_EVENT_LINK_KEY_REQ:
+ hci_process_link_key_req(iface, &sa, addr);
+ break;
+
+ case HCI_EVENT_LINK_KEY_NOTIFICATION:
+ hci_process_link_key_notification(iface, &sa, ep);
+ break;
+
+ default:
+ log_packet(&sa.bt_bdaddr, &iface->addr,
+ "unexpected HCI event, event=%#x", event->event);
+ break;
+ }
+}
+
+int
+hci_process_pin_code_req(struct bt_interface *iface,
+ struct sockaddr_bt *sa, const bdaddr_t *addr)
+{
+ const uint8_t *pin;
+
+ pin = conf_lookup_pin(iface->env, addr);
+
+ if (pin == NULL) {
+ log_info("%s: PIN code not found", bt_ntoa(addr, NULL));
+ return hci_send_cmd(iface->fd, sa, HCI_CMD_PIN_CODE_NEG_REP,
+ sizeof(bdaddr_t), (void *)addr);
+ } else {
+ hci_pin_code_rep_cp cp;
+ int n;
+
+ bdaddr_copy(&cp.bdaddr, addr);
+ memcpy(cp.pin, pin, HCI_PIN_SIZE);
+
+ n = HCI_PIN_SIZE;
+ while (n > 0 && pin[n - 1] == 0)
+ n--;
+ cp.pin_size = n;
+
+ log_info("%s: PIN code found", bt_ntoa(addr, NULL));
+ return hci_send_cmd(iface->fd, sa, HCI_CMD_PIN_CODE_REP,
+ sizeof(cp), &cp);
+ }
+}
+
+int
+hci_process_link_key_req(struct bt_interface *iface,
+ struct sockaddr_bt *sa, const bdaddr_t *addr)
+{
+ hci_link_key_rep_cp cp;
+
+ if (db_get_link_key(&iface->env->db, addr, cp.key)) {
+ log_info("%s: link key not found", bt_ntoa(addr, NULL));
+ return hci_send_cmd(iface->fd, sa, HCI_CMD_LINK_KEY_NEG_REP,
+ sizeof(bdaddr_t), (void *)addr);
+ }
+
+ log_info("%s: link key found", bt_ntoa(addr, NULL));
+ bdaddr_copy(&cp.bdaddr, addr);
+
+ return hci_send_cmd(iface->fd, sa, HCI_CMD_LINK_KEY_REP,
+ sizeof(cp), &cp);
+}
+
+int
+hci_process_link_key_notification(struct bt_interface *iface,
+ struct sockaddr_bt *sa, const hci_link_key_notification_ep *ep)
+{
+ const bdaddr_t *addr = &ep->bdaddr;
+ int res;
+
+ if ((res = db_put_link_key(&iface->env->db, addr, ep->key)) == 0)
+ log_info("%s: link key stored", bt_ntoa(addr, NULL));
+ else
+ log_info("%s: link key not stored", bt_ntoa(addr, NULL));
+
+ return res;
+}
+
+/*
+ * Basic HCI cmd request function with argument return.
+ *
+ * Normally, this will return on COMMAND_STATUS or COMMAND_COMPLETE
+ * for the given opcode, but if event is given then it will ignore
+ * COMMAND_STATUS (unless error) and wait for the specified event.
+ *
+ * If rbuf/rlen is given, results will be copied into the result
+ * buffer for COMMAND_COMPLETE/event responses.
+ */
+int
+hci_req(int hci, uint16_t opcode, uint8_t event, void *cbuf, size_t clen,
+ void *rbuf, size_t rlen)
+{
+ uint8_t msg[sizeof(hci_cmd_hdr_t) + HCI_CMD_PKT_SIZE];
+ hci_event_hdr_t *ep;
+ hci_cmd_hdr_t *cp;
+
+ cp = (hci_cmd_hdr_t *)msg;
+ cp->type = HCI_CMD_PKT;
+ cp->opcode = opcode = htole16(opcode);
+ cp->length = clen = MIN(clen, sizeof(msg) - sizeof(hci_cmd_hdr_t));
+
+ if (clen)
+ memcpy((cp + 1), cbuf, clen);
+
+ if (send(hci, msg, sizeof(hci_cmd_hdr_t) + clen, 0) < 0) {
+ log_warn("HCI send");
+ return -1;
+ }
+
+ ep = (hci_event_hdr_t *)msg;
+ for(;;) {
+ if (recv(hci, msg, sizeof(msg), 0) < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+
+ log_warn("HCI recv");
+ return -1;
+ }
+
+ if (ep->event == HCI_EVENT_COMMAND_STATUS) {
+ hci_command_status_ep *cs;
+
+ cs = (hci_command_status_ep *)(ep + 1);
+ if (cs->opcode != opcode)
+ continue;
+
+ if (cs->status) {
+ log_warnx("HCI cmd (%4.4x) failed (status %d)",
+ opcode, cs->status);
+ return -1;
+ }
+
+ if (event == 0)
+ break;
+
+ continue;
+ }
+
+ if (ep->event == HCI_EVENT_COMMAND_COMPL) {
+ hci_command_compl_ep *cc;
+ uint8_t *ptr;
+
+ cc = (hci_command_compl_ep *)(ep + 1);
+ if (cc->opcode != opcode)
+ continue;
+
+ if (rbuf == NULL)
+ break;
+
+ ptr = (uint8_t *)(cc + 1);
+ if (*ptr) {
+ log_warn("HCI cmd (%4.4x) failed (status %d)",
+ opcode, *ptr);
+ return -1;
+ }
+
+ memcpy(rbuf, ++ptr, rlen);
+ break;
+ }
+
+ if (ep->event == event) {
+ if (rbuf == NULL)
+ break;
+
+ memcpy(rbuf, (ep + 1), rlen);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* Send HCI Command Packet to socket */
+int
+hci_send_cmd(int sock, struct sockaddr_bt *sa, uint16_t opcode, size_t len,
+ void *buf)
+{
+ char msg[HCI_CMD_PKT_SIZE];
+ hci_cmd_hdr_t *h = (hci_cmd_hdr_t *)msg;
+
+ h->type = HCI_CMD_PKT;
+ h->opcode = htole16(opcode);
+ h->length = len;
+
+ if (len > 0)
+ memcpy(msg + sizeof(hci_cmd_hdr_t), buf, len);
+
+ if (sendto(sock, msg, sizeof(hci_cmd_hdr_t) + len, 0,
+ (struct sockaddr *)sa, sizeof(*sa)) == -1) {
+ log_warn("HCI send command");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/btd/log.c b/usr.sbin/btd/log.c
new file mode 100644
index 00000000000..8346fe08cb7
--- /dev/null
+++ b/usr.sbin/btd/log.c
@@ -0,0 +1,175 @@
+/* $OpenBSD: log.c,v 1.1 2008/11/24 23:34:42 uwe Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <bluetooth.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+
+#include "btd.h"
+
+int debug = 1;
+
+void logit(int, const char *, ...);
+
+void
+log_init(int n_debug)
+{
+ extern char *__progname;
+
+ debug = n_debug;
+
+ if (!debug)
+ openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
+
+ tzset();
+}
+
+void
+logit(int pri, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vlog(pri, fmt, ap);
+ va_end(ap);
+}
+
+void
+vlog(int pri, const char *fmt, va_list ap)
+{
+ char *nfmt;
+
+ if (debug) {
+ /* best effort in out of mem situations */
+ if (asprintf(&nfmt, "%s\n", fmt) == -1) {
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ } else {
+ vfprintf(stderr, nfmt, ap);
+ free(nfmt);
+ }
+ fflush(stderr);
+ } else
+ vsyslog(pri, fmt, ap);
+}
+
+
+void
+log_warn(const char *emsg, ...)
+{
+ char *nfmt;
+ va_list ap;
+
+ /* best effort to even work in out of memory situations */
+ if (emsg == NULL)
+ logit(LOG_CRIT, "%s", strerror(errno));
+ else {
+ va_start(ap, emsg);
+
+ if (asprintf(&nfmt, "%s: %s", emsg, strerror(errno)) == -1) {
+ /* we tried it... */
+ vlog(LOG_CRIT, emsg, ap);
+ logit(LOG_CRIT, "%s", strerror(errno));
+ } else {
+ vlog(LOG_CRIT, nfmt, ap);
+ free(nfmt);
+ }
+ va_end(ap);
+ }
+}
+
+void
+log_warnx(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_CRIT, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_info(const char *emsg, ...)
+{
+ va_list ap;
+
+ va_start(ap, emsg);
+ vlog(LOG_INFO, emsg, ap);
+ va_end(ap);
+}
+
+void
+log_debug(const char *emsg, ...)
+{
+ va_list ap;
+
+ if (debug) {
+ va_start(ap, emsg);
+ vlog(LOG_DEBUG, emsg, ap);
+ va_end(ap);
+ }
+}
+
+void
+log_packet(const bdaddr_t *srcn, const bdaddr_t *dstn, const char *emsg, ...)
+{
+ char srca[18], dsta[18];
+ char *nfmt;
+ va_list ap;
+
+ if (debug) {
+ bt_ntoa(srcn, srca);
+ bt_ntoa(dstn, dsta);
+
+ va_start(ap, emsg);
+ if (asprintf(&nfmt, "%s > %s: %s", srca, dsta, emsg) == -1)
+ vlog(LOG_DEBUG, emsg, ap);
+ else {
+ vlog(LOG_DEBUG, nfmt, ap);
+ free(nfmt);
+ }
+ va_end(ap);
+ }
+}
+
+void
+fatal(const char *emsg)
+{
+ if (emsg == NULL)
+ logit(LOG_CRIT, "fatal: %s", strerror(errno));
+ else
+ if (errno)
+ logit(LOG_CRIT, "fatal: %s: %s",
+ emsg, strerror(errno));
+ else
+ logit(LOG_CRIT, "fatal: %s", emsg);
+
+ exit(1);
+}
+
+void
+fatalx(const char *emsg)
+{
+ errno = 0;
+ fatal(emsg);
+}
diff --git a/usr.sbin/btd/parse.y b/usr.sbin/btd/parse.y
new file mode 100644
index 00000000000..bef3126cc54
--- /dev/null
+++ b/usr.sbin/btd/parse.y
@@ -0,0 +1,521 @@
+/* $OpenBSD: parse.y,v 1.1 2008/11/24 23:34:42 uwe Exp $ */
+
+/*
+ * Copyright (c) 2008 Uwe Stuehler <uwe@openbsd.org>
+ * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+%{
+#include <sys/limits.h>
+
+#include <dev/bluetooth/btdev.h>
+
+#include <bluetooth.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <syslog.h>
+
+#include "btd.h"
+
+TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
+
+static struct file {
+ TAILQ_ENTRY(file) entry;
+ FILE *stream;
+ char *name;
+ int lineno;
+ int errors;
+} *file, *topfile;
+
+struct file *pushfile(const char *);
+int popfile(void);
+int yyparse(void);
+int yylex(void);
+int yyerror(const char *, ...);
+int kw_cmp(const void *, const void *);
+int lookup(char *);
+int lgetc(int);
+int lungetc(int);
+int findeol(void);
+
+typedef struct {
+ union {
+ int64_t number;
+ char *string;
+ bdaddr_t bdaddr;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+struct btd *conf;
+
+%}
+
+%token INTERFACE NAME DISABLED
+%token ATTACH TYPE HID HSET NONE HF PIN
+%token ERROR
+%token <v.string> STRING
+%token <v.number> NUMBER
+%type <v.bdaddr> address
+%type <v.string> name_opt
+%type <v.number> disabled_opt
+%type <v.number> type_opt
+%type <v.string> pin_opt
+%%
+
+grammar : /* empty */
+ | grammar '\n'
+ | grammar main '\n'
+ | grammar error '\n'
+ {
+ file->errors++;
+ }
+ ;
+
+main : INTERFACE address name_opt disabled_opt
+ {
+ struct bt_interface *iface;
+
+ if (conf_find_interface(conf, &$2)) {
+ yyerror("interface %s is already defined",
+ bdaddr_any(&$2) ? "*" : bt_ntoa(&$2, NULL));
+ YYERROR;
+ }
+
+ iface = conf_add_interface(conf, &$2);
+ if (iface == NULL) {
+ yyerror("could not add interface");
+ YYERROR;
+ }
+
+ iface->name = $3;
+ iface->disabled = $4;
+ }
+ | ATTACH address type_opt pin_opt
+ {
+ struct bt_device *btdev;
+
+ if (conf_find_device(conf, &$2)) {
+ yyerror("device %s is already defined",
+ bdaddr_any(&$2) ? "*" : bt_ntoa(&$2, NULL));
+ YYERROR;
+ }
+
+ btdev = conf_add_device(conf, &$2);
+ if (btdev == NULL) {
+ yyerror("could not add device");
+ YYERROR;
+ }
+
+ btdev->pin = $4;
+ btdev->flags |= BTDF_ATTACH;
+ btdev_set_type(btdev, $3);
+ }
+ ;
+
+name_opt
+ : /* empty */
+ { $$ = NULL; }
+ | NAME STRING
+ { $$ = $2; }
+ ;
+
+disabled_opt
+ : /* empty */
+ { $$ = 0; }
+ | DISABLED
+ { $$ = 1; }
+ ;
+
+type_opt
+ : /* empty */
+ { $$ = BTDEV_NONE; }
+ | TYPE NONE
+ { $$ = BTDEV_NONE; }
+ | TYPE HID
+ { $$ = BTDEV_HID; }
+ | TYPE HSET
+ { $$ = BTDEV_HSET; }
+ | TYPE HF
+ { $$ = BTDEV_HF; }
+ ;
+
+pin_opt
+ : /* empty */
+ { $$ = NULL; }
+ | PIN STRING
+ {
+ if (($$ = calloc(HCI_PIN_SIZE, sizeof(uint8_t))) == NULL)
+ fatal("pin_opt calloc");
+ strlcpy($$, $2, HCI_PIN_SIZE);
+ free($2);
+ }
+ ;
+
+address : STRING
+ {
+ if (strcmp($1, "*")) {
+ bt_aton($1, &$$);
+
+ if (bdaddr_any(&$$)) {
+ /* 0:0:0:0:0:0 could be misinterpreted */
+ yyerror("invalid address '%s'", $1);
+ free($1);
+ YYERROR;
+ }
+
+ free($1);
+ } else
+ bdaddr_copy(&$$, BDADDR_ANY);
+ }
+ ;
+
+%%
+
+struct keywords {
+ const char *k_name;
+ int k_val;
+};
+
+int
+yyerror(const char *fmt, ...)
+{
+ va_list ap;
+ char *nfmt;
+
+ file->errors++;
+ va_start(ap, fmt);
+ if (asprintf(&nfmt, "%s:%d: %s", file->name, yylval.lineno, fmt) == -1)
+ fatalx("yyerror asprintf");
+ vlog(LOG_CRIT, nfmt, ap);
+ va_end(ap);
+ free(nfmt);
+ return (0);
+}
+
+int
+kw_cmp(const void *k, const void *e)
+{
+ return (strcmp(k, ((const struct keywords *)e)->k_name));
+}
+
+int
+lookup(char *s)
+{
+ /* this has to be sorted always */
+ static const struct keywords keywords[] = {
+ { "attach", ATTACH},
+ { "disabled", DISABLED},
+ { "hf", HF},
+ { "hid", HID},
+ { "hset", HSET},
+ { "interface", INTERFACE},
+ { "name", NAME},
+ { "none", NONE},
+ { "pin", PIN},
+ { "type", TYPE}
+ };
+ const struct keywords *p;
+
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p)
+ return (p->k_val);
+ else
+ return (STRING);
+}
+
+#define MAXPUSHBACK 128
+
+char *parsebuf;
+int parseindex;
+char pushback_buffer[MAXPUSHBACK];
+int pushback_index = 0;
+
+int
+lgetc(int quotec)
+{
+ int c, next;
+
+ if (parsebuf) {
+ /* Read character from the parsebuffer instead of input. */
+ if (parseindex >= 0) {
+ c = parsebuf[parseindex++];
+ if (c != '\0')
+ return (c);
+ parsebuf = NULL;
+ } else
+ parseindex++;
+ }
+
+ if (pushback_index)
+ return (pushback_buffer[--pushback_index]);
+
+ if (quotec) {
+ if ((c = getc(file->stream)) == EOF) {
+ yyerror("reached end of file while parsing "
+ "quoted string");
+ if (file == topfile || popfile() == EOF)
+ return (EOF);
+ return (quotec);
+ }
+ return (c);
+ }
+
+ while ((c = getc(file->stream)) == '\\') {
+ next = getc(file->stream);
+ if (next != '\n') {
+ c = next;
+ break;
+ }
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+
+ while (c == EOF) {
+ if (file == topfile || popfile() == EOF)
+ return (EOF);
+ c = getc(file->stream);
+ }
+ return (c);
+}
+
+int
+lungetc(int c)
+{
+ if (c == EOF)
+ return (EOF);
+ if (parsebuf) {
+ parseindex--;
+ if (parseindex >= 0)
+ return (c);
+ }
+ if (pushback_index < MAXPUSHBACK-1)
+ return (pushback_buffer[pushback_index++] = c);
+ else
+ return (EOF);
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ parsebuf = NULL;
+
+ /* skip to either EOF or the first real EOL */
+ while (1) {
+ if (pushback_index)
+ c = pushback_buffer[--pushback_index];
+ else
+ c = lgetc(0);
+ if (c == '\n') {
+ file->lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return (ERROR);
+}
+
+int
+yylex(void)
+{
+ char buf[8096];
+ char *p;
+ int quotec, next, c;
+ int token;
+
+ p = buf;
+ while ((c = lgetc(0)) == ' ' || c == '\t')
+ ; /* nothing */
+
+ yylval.lineno = file->lineno;
+ if (c == '#')
+ while ((c = lgetc(0)) != '\n' && c != EOF)
+ ; /* nothing */
+
+ switch (c) {
+ case '\'':
+ case '"':
+ quotec = c;
+ while (1) {
+ if ((c = lgetc(quotec)) == EOF)
+ return (0);
+ if (c == '\n') {
+ file->lineno++;
+ continue;
+ } else if (c == '\\') {
+ if ((next = lgetc(quotec)) == EOF)
+ return (0);
+ if (next == quotec || c == ' ' || c == '\t')
+ c = next;
+ else if (next == '\n')
+ continue;
+ else
+ lungetc(next);
+ } else if (c == quotec) {
+ *p = '\0';
+ break;
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ *p++ = (char)c;
+ }
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL)
+ fatal("yylex: strdup");
+ return (STRING);
+ }
+
+#define allowed_to_end_number(x) \
+ (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
+
+ if (c == '-' || isdigit(c)) {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(0)) != EOF && isdigit(c));
+ lungetc(c);
+ if (p == buf + 1 && buf[0] == '-')
+ goto nodigits;
+ if (c == EOF || allowed_to_end_number(c)) {
+ const char *errstr = NULL;
+
+ *p = '\0';
+ yylval.v.number = strtonum(buf, LLONG_MIN,
+ LLONG_MAX, &errstr);
+ if (errstr) {
+ yyerror("\"%s\" invalid number: %s",
+ buf, errstr);
+ return (findeol());
+ }
+ return (NUMBER);
+ } else {
+nodigits:
+ while (p > buf + 1)
+ lungetc(*--p);
+ c = *--p;
+ if (c == '-')
+ return (c);
+ }
+ }
+
+#define allowed_in_string(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && x != '<' && x != '>' && \
+ x != '!' && x != '=' && x != '/' && x != '#' && \
+ x != ','))
+
+ if (isalnum(c) || c == ':' || c == '_' || c == '*') {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
+ lungetc(c);
+ *p = '\0';
+ if ((token = lookup(buf)) == STRING)
+ if ((yylval.v.string = strdup(buf)) == NULL)
+ fatal("yylex: strdup");
+ return (token);
+ }
+ if (c == '\n') {
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+ if (c == EOF)
+ return (0);
+ return (c);
+}
+
+struct file *
+pushfile(const char *name)
+{
+ struct file *nfile;
+
+ if ((nfile = calloc(1, sizeof(struct file))) == NULL ||
+ (nfile->name = strdup(name)) == NULL) {
+ log_warn("malloc");
+ return (NULL);
+ }
+ if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
+ log_warn("%s", nfile->name);
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ }
+ nfile->lineno = 1;
+ TAILQ_INSERT_TAIL(&files, nfile, entry);
+ return (nfile);
+}
+
+int
+popfile(void)
+{
+ struct file *prev;
+
+ if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
+ prev->errors += file->errors;
+
+ TAILQ_REMOVE(&files, file, entry);
+ fclose(file->stream);
+ free(file->name);
+ free(file);
+ file = prev;
+ return (file ? 0 : EOF);
+}
+
+int
+parse_config(const char *filename, struct btd *xconf)
+{
+ int errors = 0;
+
+ conf = xconf;
+ TAILQ_INIT(&conf->interfaces);
+ TAILQ_INIT(&conf->devices);
+
+ if ((file = pushfile(filename)) == NULL)
+ return (-1);
+
+ topfile = file;
+
+ yyparse();
+ errors = file->errors;
+ popfile();
+
+ if (!errors && conf_find_interface(conf, BDADDR_ANY) == NULL)
+ if (conf_add_interface(conf, BDADDR_ANY) == NULL)
+ fatalx("parse_config add_interface");
+
+ if (!errors && conf_find_device(conf, BDADDR_ANY) == NULL)
+ if (conf_add_device(conf, BDADDR_ANY) == NULL)
+ fatalx("parse_config add_device");
+
+ return (errors ? -1 : 0);
+}
diff --git a/usr.sbin/btd/sdp.c b/usr.sbin/btd/sdp.c
new file mode 100644
index 00000000000..6ff2fbfd0d3
--- /dev/null
+++ b/usr.sbin/btd/sdp.c
@@ -0,0 +1,738 @@
+/* $OpenBSD: sdp.c,v 1.1 2008/11/24 23:34:42 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 <bluetooth.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 btdev_attach_args *dict,
+ bdaddr_t *laddr, bdaddr_t *raddr, const char *service)
+{
+ void *ss;
+ 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) {
+ ss = sdp_open(laddr, raddr);
+
+ if (ss == NULL || (errno = sdp_error(ss)) != 0)
+ return -1;
+
+ rv = sdp_search(ss,
+ cfgtype[i].nservices, cfgtype[i].services,
+ cfgtype[i].nattrs, cfgtype[i].attrs,
+ NUM(values), values);
+
+ if (rv != 0) {
+ errno = sdp_error(ss);
+ return -1;
+ }
+ sdp_close(ss);
+
+ 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/util.c b/usr.sbin/btd/util.c
new file mode 100644
index 00000000000..0117a3d27c3
--- /dev/null
+++ b/usr.sbin/btd/util.c
@@ -0,0 +1,33 @@
+/* $OpenBSD: util.c,v 1.1 2008/11/24 23:34:42 uwe Exp $ */
+
+/*
+ * Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/time.h>
+#include <limits.h>
+
+#include "btd.h"
+
+time_t
+getmonotime(void)
+{
+ struct timespec ts;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
+ fatal("clock_gettime");
+
+ return (ts.tv_sec);
+}