summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@nokia.com>2010-05-17 17:32:26 +0200
committerJohan Hedberg <johan.hedberg@nokia.com>2010-05-17 17:32:26 +0200
commit1e4d4fe0ed499adaf3e61b1e593dccd3c0928158 (patch)
treeb990d40549df8e7609176c7a9b0b024137084d25
parent38a7ef119c1ee1637a4f334dd055b8a171cdf148 (diff)
Add new "Blocked" property to device objects
This patch adds a new "Blocked" property to device objects. It maps directly to the HCIBLOCKDEV and HCIUNBLOCKDEV ioctl's which enable control of incoming connection acceptance on the kernel side.
-rw-r--r--doc/device-api.txt7
-rw-r--r--src/adapter.c38
-rw-r--r--src/device.c213
-rw-r--r--src/storage.c34
-rw-r--r--src/storage.h3
-rwxr-xr-xtest/test-device20
6 files changed, 279 insertions, 36 deletions
diff --git a/doc/device-api.txt b/doc/device-api.txt
index efd37660..b8182997 100644
--- a/doc/device-api.txt
+++ b/doc/device-api.txt
@@ -154,6 +154,13 @@ Properties string Address [readonly]
Indicates if the remote is seen as trusted. This
setting can be changed by the application.
+ boolean Blocked [readwrite]
+
+ If set to true any incoming connections from the
+ device will be immediately rejected. Any device
+ drivers will also be removed and no new ones will
+ be probed as long as the device is blocked.
+
string Alias [readwrite]
The name alias for the remote device. The alias can
diff --git a/src/adapter.c b/src/adapter.c
index f246a792..fb401716 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -2022,6 +2022,23 @@ static void create_stored_device_from_linkkeys(char *key, char *value,
}
}
+static void create_stored_device_from_blocked(char *key, char *value,
+ void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ struct btd_device *device;
+
+ if (g_slist_find_custom(adapter->devices,
+ key, (GCompareFunc) device_address_cmp))
+ return;
+
+ device = device_create(connection, adapter, key);
+ if (device) {
+ device_set_temporary(device, FALSE);
+ adapter->devices = g_slist_append(adapter->devices, device);
+ }
+}
+
static void load_devices(struct btd_adapter *adapter)
{
char filename[PATH_MAX + 1];
@@ -2036,6 +2053,26 @@ static void load_devices(struct btd_adapter *adapter)
create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "linkkeys");
textfile_foreach(filename, create_stored_device_from_linkkeys,
adapter);
+
+ create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "blocked");
+ textfile_foreach(filename, create_stored_device_from_blocked, adapter);
+}
+
+static void clear_blocked(struct btd_adapter *adapter)
+{
+ int dd;
+
+ dd = hci_open_dev(adapter->dev_id);
+ if (dd < 0) {
+ error("hci_open_dev(hci%d): %s (%d)", adapter->dev_id,
+ strerror(errno), errno);
+ return;
+ }
+
+ if (ioctl(dd, HCIUNBLOCKADDR, BDADDR_ANY) < 0)
+ error("ioctl(HCIUNBLOCKADDR): %s (%d)", strerror(errno), errno);
+
+ hci_close_dev(dd);
}
static void probe_driver(gpointer data, gpointer user_data)
@@ -2205,6 +2242,7 @@ proceed:
if (adapter->initialized == FALSE) {
load_drivers(adapter);
+ clear_blocked(adapter);
load_devices(adapter);
/* retrieve the active connections: address the scenario where
diff --git a/src/device.c b/src/device.c
index 6ba16122..3d91ced1 100644
--- a/src/device.c
+++ b/src/device.c
@@ -31,6 +31,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
+#include <sys/ioctl.h>
#include <errno.h>
#include <bluetooth/bluetooth.h>
@@ -137,6 +138,7 @@ struct btd_device {
gboolean trusted;
gboolean paired;
+ gboolean blocked;
gboolean renewed_key;
gboolean authorizing;
@@ -336,6 +338,10 @@ static DBusMessage *get_properties(DBusConnection *conn,
boolean = device_is_trusted(device);
dict_append_entry(&dict, "Trusted", DBUS_TYPE_BOOLEAN, &boolean);
+ /* Blocked */
+ boolean = device->blocked;
+ dict_append_entry(&dict, "Blocked", DBUS_TYPE_BOOLEAN, &boolean);
+
/* Connected */
boolean = (device->handle != 0);
dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN,
@@ -424,6 +430,156 @@ static DBusMessage *set_trust(DBusConnection *conn, DBusMessage *msg,
return dbus_message_new_method_return(msg);
}
+static void driver_remove(struct btd_driver_data *driver_data,
+ struct btd_device *device)
+{
+ struct btd_device_driver *driver = driver_data->driver;
+
+ driver->remove(device);
+}
+
+static void driver_free(struct btd_driver_data *driver_data,
+ struct btd_device *device)
+{
+ struct btd_device_driver *driver = driver_data->driver;
+
+ driver->remove(device);
+
+ g_free(driver);
+ g_free(driver_data);
+}
+
+static gboolean do_disconnect(gpointer user_data)
+{
+ struct btd_device *device = user_data;
+ disconnect_cp cp;
+ int dd;
+ uint16_t dev_id = adapter_get_dev_id(device->adapter);
+
+ device->disconn_timer = 0;
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0)
+ goto fail;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = htobs(device->handle);
+ cp.reason = HCI_OE_USER_ENDED_CONNECTION;
+
+ hci_send_cmd(dd, OGF_LINK_CTL, OCF_DISCONNECT,
+ DISCONNECT_CP_SIZE, &cp);
+
+ close(dd);
+
+fail:
+ return FALSE;
+}
+
+static int device_block(DBusConnection *conn, struct btd_device *device)
+{
+ int dev_id, dd, err;
+ bdaddr_t src;
+
+ if (device->blocked)
+ return 0;
+
+ dev_id = adapter_get_dev_id(device->adapter);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0)
+ return -errno;
+
+ if (device->handle)
+ do_disconnect(device);
+
+ g_slist_foreach(device->drivers, (GFunc) driver_remove, device);
+
+ if (ioctl(dd, HCIBLOCKADDR, &device->bdaddr) < 0) {
+ err = -errno;
+ hci_close_dev(dd);
+ return err;
+ }
+
+ hci_close_dev(dd);
+
+ device->blocked = TRUE;
+
+ adapter_get_address(device->adapter, &src);
+
+ err = write_blocked(&src, &device->bdaddr, TRUE);
+ if (err < 0)
+ error("write_blocked(): %s (%d)", strerror(-err), -err);
+
+ device_set_temporary(device, FALSE);
+
+ emit_property_changed(conn, device->path, DEVICE_INTERFACE, "Blocked",
+ DBUS_TYPE_BOOLEAN, &device->blocked);
+
+ return 0;
+}
+
+static int device_unblock(DBusConnection *conn, struct btd_device *device)
+{
+ int dev_id, dd, err;
+ bdaddr_t src;
+
+ if (!device->blocked)
+ return 0;
+
+ dev_id = adapter_get_dev_id(device->adapter);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0)
+ return -errno;
+
+ if (ioctl(dd, HCIUNBLOCKADDR, &device->bdaddr) < 0) {
+ err = -errno;
+ hci_close_dev(dd);
+ return err;
+ }
+
+ hci_close_dev(dd);
+
+ device->blocked = FALSE;
+
+ adapter_get_address(device->adapter, &src);
+
+ err = write_blocked(&src, &device->bdaddr, FALSE);
+ if (err < 0)
+ error("write_blocked(): %s (%d)", strerror(-err), -err);
+
+ emit_property_changed(conn, device->path, DEVICE_INTERFACE, "Blocked",
+ DBUS_TYPE_BOOLEAN, &device->blocked);
+
+ device_probe_drivers(device, device->uuids);
+
+ return 0;
+}
+
+static DBusMessage *set_blocked(DBusConnection *conn, DBusMessage *msg,
+ gboolean value, void *data)
+{
+ struct btd_device *device = data;
+ int err;
+
+ if (value)
+ err = device_block(conn, device);
+ else
+ err = device_unblock(conn, device);
+
+ switch (-err) {
+ case 0:
+ return dbus_message_new_method_return(msg);
+ case EINVAL:
+ return g_dbus_create_error(msg,
+ ERROR_INTERFACE ".NotSupported",
+ "Kernel lacks blacklist support");
+ default:
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+ "%s", strerror(-err));
+ }
+}
+
static inline DBusMessage *invalid_args(DBusMessage *msg)
{
return g_dbus_create_error(msg,
@@ -453,7 +609,6 @@ static DBusMessage *set_property(DBusConnection *conn,
if (g_str_equal("Trusted", property)) {
dbus_bool_t value;
-
if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
return invalid_args(msg);
dbus_message_iter_get_basic(&sub, &value);
@@ -467,6 +622,15 @@ static DBusMessage *set_property(DBusConnection *conn,
dbus_message_iter_get_basic(&sub, &alias);
return set_alias(conn, msg, alias, data);
+ } else if (g_str_equal("Blocked", property)) {
+ dbus_bool_t value;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
+ return invalid_args(msg);
+
+ dbus_message_iter_get_basic(&sub, &value);
+
+ return set_blocked(conn, msg, value, data);
}
return invalid_args(msg);
@@ -631,32 +795,6 @@ static DBusMessage *cancel_discover(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}
-static gboolean do_disconnect(gpointer user_data)
-{
- struct btd_device *device = user_data;
- disconnect_cp cp;
- int dd;
- uint16_t dev_id = adapter_get_dev_id(device->adapter);
-
- device->disconn_timer = 0;
-
- dd = hci_open_dev(dev_id);
- if (dd < 0)
- goto fail;
-
- memset(&cp, 0, sizeof(cp));
- cp.handle = htobs(device->handle);
- cp.reason = HCI_OE_USER_ENDED_CONNECTION;
-
- hci_send_cmd(dd, OGF_LINK_CTL, OCF_DISCONNECT,
- DISCONNECT_CP_SIZE, &cp);
-
- close(dd);
-
-fail:
- return FALSE;
-}
-
static void bonding_request_cancel(struct bonding_req *bonding)
{
if (!bonding->io)
@@ -898,6 +1036,9 @@ struct btd_device *device_create(DBusConnection *conn,
device->alias = g_strdup(alias);
device->trusted = read_trust(&src, address, GLOBAL_TRUST);
+ if (read_blocked(&src, &device->bdaddr))
+ device_block(conn, device);
+
device->auth = 0xff;
if (read_link_key(&src, &device->bdaddr, NULL, NULL) == 0)
@@ -978,8 +1119,6 @@ static void device_remove_stored(struct btd_device *device)
void device_remove(struct btd_device *device, gboolean remove_stored)
{
- GSList *list;
- struct btd_device_driver *driver;
debug("Removing device %s", device->path);
@@ -995,13 +1134,9 @@ void device_remove(struct btd_device *device, gboolean remove_stored)
if (remove_stored)
device_remove_stored(device);
- for (list = device->drivers; list; list = list->next) {
- struct btd_driver_data *driver_data = list->data;
- driver = driver_data->driver;
-
- driver->remove(device);
- g_free(driver_data);
- }
+ g_slist_foreach(device->drivers, (GFunc) driver_free, device);
+ g_slist_free(device->drivers);
+ device->drivers = NULL;
btd_device_unref(device);
}
@@ -1096,6 +1231,11 @@ void device_probe_drivers(struct btd_device *device, GSList *profiles)
GSList *list;
int err;
+ if (device->blocked) {
+ debug("Skipping drivers for blocked device %s", device->path);
+ goto add_uuids;
+ }
+
debug("Probe drivers for %s", device->path);
for (list = device_drivers; list; list = list->next) {
@@ -1125,6 +1265,7 @@ void device_probe_drivers(struct btd_device *device, GSList *profiles)
g_slist_free(probe_uuids);
}
+add_uuids:
for (list = profiles; list; list = list->next) {
GSList *l = g_slist_find_custom(device->uuids, list->data,
(GCompareFunc) strcasecmp);
diff --git a/src/storage.c b/src/storage.c
index 981c7440..ed1e5ec4 100644
--- a/src/storage.c
+++ b/src/storage.c
@@ -1205,3 +1205,37 @@ int read_device_pairable(bdaddr_t *bdaddr, gboolean *mode)
return 0;
}
+
+gboolean read_blocked(const bdaddr_t *local, const bdaddr_t *remote)
+{
+ char filename[PATH_MAX + 1], *str, addr[18];
+
+ create_filename(filename, PATH_MAX, local, "blocked");
+
+ ba2str(remote, addr);
+
+ str = textfile_caseget(filename, addr);
+ if (!str)
+ return FALSE;
+
+ free(str);
+
+ return TRUE;
+}
+
+int write_blocked(const bdaddr_t *local, const bdaddr_t *remote,
+ gboolean blocked)
+{
+ char filename[PATH_MAX + 1], addr[18];
+
+ create_filename(filename, PATH_MAX, local, "blocked");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(remote, addr);
+
+ if (blocked == FALSE)
+ return textfile_casedel(filename, addr);
+
+ return textfile_caseput(filename, addr, "");
+}
diff --git a/src/storage.h b/src/storage.h
index ed507342..eee71d39 100644
--- a/src/storage.h
+++ b/src/storage.h
@@ -75,6 +75,9 @@ int read_device_id(const gchar *src, const gchar *dst,
uint16_t *product, uint16_t *version);
int write_device_pairable(bdaddr_t *local, gboolean mode);
int read_device_pairable(bdaddr_t *local, gboolean *mode);
+gboolean read_blocked(const bdaddr_t *local, const bdaddr_t *remote);
+int write_blocked(const bdaddr_t *local, const bdaddr_t *remote,
+ gboolean blocked);
#define PNP_UUID "00001200-0000-1000-8000-00805f9b34fb"
diff --git a/test/test-device b/test/test-device
index f8f2d14a..2322122c 100755
--- a/test/test-device
+++ b/test/test-device
@@ -124,5 +124,25 @@ if (sys.argv[1] == "trusted"):
device.SetProperty("Trusted", value)
sys.exit(0)
+if (sys.argv[1] == "blocked"):
+ if (len(sys.argv) < 3):
+ print "Need address parameter"
+ else:
+ path = adapter.FindDevice(sys.argv[2])
+ device = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Device")
+ if (len(sys.argv) < 4):
+ properties = device.GetProperties()
+ print properties["Blocked"]
+ else:
+ if (sys.argv[3] == "yes"):
+ value = dbus.Boolean(1)
+ elif (sys.argv[3] == "no"):
+ value = dbus.Boolean(0)
+ else:
+ value = dbus.Boolean(sys.argv[3])
+ device.SetProperty("Blocked", value)
+ sys.exit(0)
+
print "Unknown command"
sys.exit(1)