summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClaudio Takahasi <claudio.takahasi@openbossa.org>2006-03-16 22:47:01 +0000
committerClaudio Takahasi <claudio.takahasi@openbossa.org>2006-03-16 22:47:01 +0000
commitad51f992f0c529a928edad39a045864a02e641af (patch)
treee7a76bbed664bca8e2f4e4a6c6ff0316cd396de9
parent4a77b36ae82117567bd7045693a45b139055557b (diff)
Initial create bonding service
-rw-r--r--hcid/dbus-adapter.c194
-rw-r--r--hcid/dbus.c202
-rw-r--r--hcid/dbus.h12
-rw-r--r--hcid/hcid.h34
-rw-r--r--hcid/security.c4
5 files changed, 358 insertions, 88 deletions
diff --git a/hcid/dbus-adapter.c b/hcid/dbus-adapter.c
index 2ff247de..fffb6fd4 100644
--- a/hcid/dbus-adapter.c
+++ b/hcid/dbus-adapter.c
@@ -73,6 +73,62 @@ static const char *phone_minor_cls[] = {
"isdn"
};
+int find_connection_handle(int dd, bdaddr_t *peer)
+{
+ struct hci_conn_info_req *cr;
+ int handle = -1;
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr)
+ return -1;
+
+ bacpy(&cr->bdaddr, peer);
+ cr->type = ACL_LINK;
+
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ free(cr);
+ return -1;
+ }
+
+ handle = cr->conn_info->handle;
+ free(cr);
+
+ return handle;
+}
+
+static int bonding_requests_find(const void *a, const void *b)
+{
+ const struct bonding_request_info *dev = a;
+ const bdaddr_t *peer = b;
+
+ if (!dev)
+ return -1;
+
+ if (memcmp(dev->addr, peer, sizeof(*peer)) == 0) {
+ return 0;
+ }
+
+ return -1;
+}
+
+static int bonding_requests_append(struct slist **list, bdaddr_t *addr, DBusMessage *msg, bonding_state_t bonding_state)
+{
+ struct bonding_request_info *data;
+
+ data = malloc(sizeof(*data));
+ if (!data)
+ return -1;
+
+ data->addr = malloc(sizeof(*data->addr));
+ memcpy(data->addr, addr, sizeof(*data->addr));
+ data->msg = msg;
+ data->bonding_state = bonding_state;
+
+ *list = slist_append(*list, data);
+
+ return 0;
+}
+
static DBusHandlerResult handle_dev_get_address_req(DBusConnection *conn, DBusMessage *msg, void *data)
{
struct hci_dbus_data *dbus_data = data;
@@ -888,75 +944,125 @@ static DBusHandlerResult handle_dev_last_used_req(DBusConnection *conn, DBusMess
static DBusHandlerResult handle_dev_create_bonding_req(DBusConnection *conn, DBusMessage *msg, void *data)
{
+ char filename[PATH_MAX + 1];
+ char local_addr[18];
struct hci_request rq;
- auth_requested_cp cp;
evt_cmd_status rp;
- DBusMessage *reply;
- char *str_bdaddr;
+ DBusError err;
+ char *peer_addr;
+ char *str;
struct hci_dbus_data *dbus_data = data;
- struct hci_conn_info_req *cr;
- bdaddr_t bdaddr;
- int dd, dev_id;
-
- dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &str_bdaddr,
- DBUS_TYPE_INVALID);
-
- str2ba(str_bdaddr, &bdaddr);
+ struct slist *tmp_list;
+ bdaddr_t peer_bdaddr;
+ int dd, handle;
+ bonding_state_t bonding_state;
+
+ dbus_error_init(&err);
+ dbus_message_get_args(msg, &err,
+ DBUS_TYPE_STRING, &peer_addr,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_error_is_set(&err)) {
+ error("Can't extract message arguments:%s", err.message);
+ dbus_error_free(&err);
+ return error_invalid_arguments(conn, msg);
+ }
- dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ str2ba(peer_addr, &peer_bdaddr);
+
+ /* check if there is a pending bonding request */
+ tmp_list = slist_find(dbus_data->bonding_requests, &peer_bdaddr, bonding_requests_find);
- if (dev_id < 0)
- return error_failed(conn, msg, ENOTCONN);
+ if (tmp_list)
+ return error_bonding_in_progress(conn, msg);
- if (dbus_data->dev_id != dev_id)
- return error_failed(conn, msg, ENOTCONN);
+ get_device_address(dbus_data->dev_id, local_addr, sizeof(local_addr));
- dd = hci_open_dev(dev_id);
- if (dd < 0)
- return error_no_such_adapter(conn, msg);
+ /* check if a link key already exists */
+ snprintf(filename, PATH_MAX, "%s/%s/linkkeys", STORAGEDIR, local_addr);
- cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
- if (!cr) {
- hci_close_dev(dd);
- return error_out_of_memory(conn, msg);
+ str = textfile_get(filename, peer_addr);
+ if (str) {
+ free(str);
+ return error_bonding_already_exists(conn, msg);
}
- bacpy(&cr->bdaddr, &bdaddr);
- cr->type = ACL_LINK;
+ /* check if the address belongs to the last seen cache */
+ snprintf(filename, PATH_MAX, "%s/%s/lastseen", STORAGEDIR, local_addr);
+ str = textfile_get(filename, peer_addr);
+ if (!str)
+ return error_unknown_address(conn, msg);
- if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
- hci_close_dev(dd);
- free(cr);
- return error_failed(conn, msg, errno);
- }
+ free(str);
- memset(&cp, 0, sizeof(cp));
- cp.handle = cr->conn_info->handle;
+ dd = hci_open_dev(dbus_data->dev_id);
+ if (dd < 0)
+ return error_no_such_adapter(conn, msg);
+
+ /* check if there is an active connection */
+ //dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &peer_bdaddr);
+ handle = find_connection_handle(dd, &peer_bdaddr);
memset(&rq, 0, sizeof(rq));
+ memset(&rp, 0, sizeof(rp));
+
rq.ogf = OGF_LINK_CTL;
- rq.ocf = OCF_AUTH_REQUESTED;
- rq.cparam = &cp;
- rq.clen = AUTH_REQUESTED_CP_SIZE;
+ rq.event = EVT_CMD_STATUS;
rq.rparam = &rp;
- rq.rlen = EVT_CMD_STATUS_SIZE;
- rq.event = EVT_CMD_STATUS;
+ rq.rlen = EVT_CMD_STATUS_SIZE;
+
+ if (handle < 0 ) {
+ create_conn_cp cp;
+
+ memset(&cp, 0, sizeof(cp));
+ /* create a new connection */
+ bonding_state = CONNECTING;
+
+ bacpy(&cp.bdaddr, &peer_bdaddr);
+ cp.pkt_type = htobs(HCI_DM1 | HCI_DM3 | HCI_DM5 | HCI_DH1 | HCI_DH3 | HCI_DH5);
+ cp.pscan_rep_mode = 0x02;
+ cp.clock_offset = htobs(0x0000);
+ cp.role_switch = 0x01;
+
+ rq.ocf = OCF_CREATE_CONN;
+ rq.cparam = &cp;
+ rq.clen = CREATE_CONN_CP_SIZE;
+ } else {
+ /* connection found */
+ auth_requested_cp cp;
+
+ memset(&cp, 0, sizeof(cp));
+
+ /* active connection found */
+ bonding_state = PAIRING;
+
+ cp.handle = handle;
+
+ rq.ocf = OCF_AUTH_REQUESTED;
+ rq.cparam = &cp;
+ rq.clen = AUTH_REQUESTED_CP_SIZE;
+ }
if (hci_send_req(dd, &rq, 100) < 0) {
- error("Unable to send authentication request: %s (%d)",
- strerror(errno), errno);
+ error("Unable to send the HCI request: %s (%d)",
+ strerror(errno), errno);
hci_close_dev(dd);
- free(cr);
return error_failed(conn, msg, errno);
}
- reply = dbus_message_new_method_return(msg);
+ if (rp.status) {
+ error("Failed with status 0x%02x", rp.status);
+ hci_close_dev(dd);
+ return error_failed(conn, msg, rp.status);
+ }
- free(cr);
+ /* add in the bonding requests list */
+ bonding_requests_append(&dbus_data->bonding_requests, &peer_bdaddr, msg, bonding_state);
- close(dd);
+ dbus_message_ref(msg);
+ hci_close_dev(dd);
- return send_reply_and_unref(conn, reply);
+ return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_dev_remove_bonding_req(DBusConnection *conn, DBusMessage *msg, void *data)
diff --git a/hcid/dbus.c b/hcid/dbus.c
index 54bc54b4..64f0ad18 100644
--- a/hcid/dbus.c
+++ b/hcid/dbus.c
@@ -101,6 +101,7 @@ static const char *phone_minor_cls[] = {
"isdn"
};
+
void discovered_device_free(void *data, void *user_data)
{
struct discovered_dev_info *dev = data;
@@ -179,6 +180,63 @@ static int remote_name_remove(struct slist **list, bdaddr_t *addr)
return ret_val;
}
+static int bonding_requests_find(const void *a, const void *b)
+{
+ const struct bonding_request_info *dev = a;
+ const bdaddr_t *peer = b;
+
+ if (!dev)
+ return -1;
+
+ if (memcmp(dev->addr, peer, sizeof(*peer)) == 0)
+ return 0;
+
+ return -1;
+}
+
+static DBusMessage *dbus_msg_new_authentication_return(DBusMessage *msg, uint8_t status)
+{
+
+ switch (status) {
+ case 0x00: /* success */
+ return dbus_message_new_method_return(msg);
+
+ case 0x04: /* page timeout */
+ case 0x08: /* connection timeout */
+ case 0x10: /* connection accept timeout */
+ case 0x22: /* LMP response timeout */
+ case 0x28: /* instant passed - is this a timeout? */
+ return dbus_message_new_error(msg, ERROR_INTERFACE".AuthenticationTimeout",
+ "Authentication Timeout");
+ case 0x17: /* too frequent pairing attempts */
+ return dbus_message_new_error(msg, ERROR_INTERFACE".RepeatedAttemps",
+ "Repeated Attempts");
+
+ case 0x18: /* pairing not allowed (e.g. gw rejected attempt) */
+ return dbus_message_new_error(msg, ERROR_INTERFACE".AuthenticationRejected",
+ "Authentication Rejected");
+
+ case 0x07: /* memory capacity */
+ case 0x09: /* connection limit */
+ case 0x0a: /* synchronous connection limit */
+ case 0x0d: /* limited resources */
+ case 0x14: /* terminated due to low resources */
+ return dbus_message_new_error(msg, ERROR_INTERFACE".AuthenticationCanceled",
+ "Authentication Canceled");
+
+ case 0x05: /* authentication failure */
+ case 0x06: /* pin missing */
+ case 0x0E: /* rejected due to security reasons - is this auth failure? */
+ case 0x25: /* encryption mode not acceptable - is this auth failure? */
+ case 0x26: /* link key cannot be changed - is this auth failure? */
+ case 0x29: /* pairing with unit key unsupported - is this auth failure? */
+ case 0x2f: /* insufficient security - is this auth failure? */
+ default:
+ return dbus_message_new_error(msg, ERROR_INTERFACE".AuthenticationFailed",
+ "Authentication Failed");
+ }
+}
+
/*
* Timeout functions Protypes
*/
@@ -463,7 +521,9 @@ void hcid_dbus_request_pin(int dev, bdaddr_t *sba, struct hci_conn_info *ci)
void hcid_dbus_bonding_created_complete(bdaddr_t *local, bdaddr_t *peer, const uint8_t status)
{
- DBusMessage *message = NULL;
+ struct hci_dbus_data *pdata;
+ DBusMessage *msg_reply = NULL;
+ DBusMessage *msg_signal = NULL;
char *local_addr, *peer_addr;
const char *name;
bdaddr_t tmp;
@@ -486,28 +546,53 @@ void hcid_dbus_bonding_created_complete(bdaddr_t *local, bdaddr_t *peer, const u
* 0x01-0x0F: authentication request failed
*/
name = status ? "BondingFailed" : "BondingCreated";
+ /* authentication signal */
+ msg_signal = dbus_message_new_signal(path, ADAPTER_INTERFACE, name);
- message = dbus_message_new_signal(path, ADAPTER_INTERFACE, name);
-
- if (message == NULL) {
+ if (msg_signal == NULL) {
error("Can't allocate D-Bus remote name message");
goto failed;
}
- dbus_message_append_args(message,
+ dbus_message_append_args(msg_signal,
DBUS_TYPE_STRING, &peer_addr,
DBUS_TYPE_INVALID);
- if (dbus_connection_send(connection, message, NULL) == FALSE) {
- error("Can't send D-Bus remote name message");
+ if (dbus_connection_send(connection, msg_signal, NULL) == FALSE) {
+ error("Can't send D-Bus bonding created signal");
goto failed;
}
dbus_connection_flush(connection);
+ /* create the authentication reply */
+ if (dbus_connection_get_object_path_data(connection, path, (void *) &pdata)) {
+ struct slist *l;
+
+ l = slist_find(pdata->bonding_requests, peer, bonding_requests_find);
+
+ if (l) {
+ struct bonding_request_info *dev = l->data;
+
+ msg_reply = dbus_msg_new_authentication_return(dev->msg, status);
+ if (dbus_connection_send(connection, msg_reply, NULL) == FALSE) {
+ error("Can't send D-Bus reply for create bonding request");
+ goto failed;
+ }
+
+ dbus_message_unref(dev->msg);
+ pdata->bonding_requests = slist_remove(pdata->bonding_requests, dev);
+ free(dev->addr);
+ free(dev);
+ }
+ }
+
failed:
- if (message)
- dbus_message_unref(message);
+ if (msg_signal)
+ dbus_message_unref(msg_signal);
+
+ if (msg_reply)
+ dbus_message_unref(msg_reply);
bt_free(local_addr);
bt_free(peer_addr);
@@ -877,8 +962,105 @@ failed:
bt_free(peer_addr);
}
-void hcid_dbus_conn_complete(bdaddr_t *local, bdaddr_t *peer)
+void hcid_dbus_conn_complete(bdaddr_t *local, uint8_t status, uint16_t handle, bdaddr_t *peer)
{
+ char path[MAX_PATH_LENGTH];
+ struct hci_request rq;
+ evt_cmd_status rp;
+ auth_requested_cp cp;
+ struct hci_dbus_data *pdata = NULL;
+ const struct slist *l;
+ struct bonding_request_info *dev = NULL;
+ char *local_addr, *peer_addr;
+ bdaddr_t tmp;
+ int dd, id;
+
+ baswap(&tmp, local); local_addr = batostr(&tmp);
+ baswap(&tmp, peer); peer_addr = batostr(&tmp);
+
+ id = hci_devid(local_addr);
+ if (id < 0) {
+ error("No matching device id for %s", local_addr);
+ goto failed;
+ }
+
+ snprintf(path, sizeof(path), "%s/hci%d", ADAPTER_PATH, id);
+
+ if (!dbus_connection_get_object_path_data(connection, path, (void *) &pdata))
+ goto failed;
+
+ l = slist_find(pdata->bonding_requests, peer, bonding_requests_find);
+
+ /*
+ * Connections can be requested by other applications, profiles and bonding
+ * For now it's necessary check only if there a pending bonding request
+ */
+ if (!l)
+ goto failed;
+
+ dev = l->data;
+
+ /* connection failed */
+ if (status) {
+ error_connection_attempt_failed(connection, dev->msg, status);
+ goto failed;
+ }
+
+ if (dev->bonding_state != CONNECTING)
+ goto failed; /* FIXME: is it possible? */
+
+ dd = hci_open_dev(pdata->dev_id);
+ if (dd < 0) {
+ error_no_such_adapter(connection, dev->msg);
+ goto failed;
+ }
+
+ /* request authentication */
+ memset(&rq, 0, sizeof(rq));
+ memset(&rp, 0, sizeof(rp));
+ memset(&cp, 0, sizeof(cp));
+
+ cp.handle = handle;
+
+ rq.ogf = OGF_LINK_CTL;
+ rq.event = EVT_CMD_STATUS;
+ rq.rparam = &rp;
+ rq.rlen = EVT_CMD_STATUS_SIZE;
+
+ rq.ocf = OCF_AUTH_REQUESTED;
+ rq.cparam = &cp;
+ rq.clen = AUTH_REQUESTED_CP_SIZE;
+
+ if (hci_send_req(dd, &rq, 100) < 0) {
+ error("Unable to send the HCI request: %s (%d)",
+ strerror(errno), errno);
+ error_failed(connection, dev->msg, errno);
+ goto failed;
+ }
+
+ if (rp.status) {
+ error("Failed with status 0x%02x", rp.status);
+ error_failed(connection, dev->msg, rp.status);
+ goto failed;
+ }
+ /* request sent properly */
+ dev->bonding_state = PAIRING;
+
+failed:
+ /* remove from the list if the HCI pairing request was not sent */
+ if (dev) {
+ if (dev->bonding_state != PAIRING) {
+ dbus_message_unref(dev->msg);
+ pdata->bonding_requests = slist_remove(pdata->bonding_requests, dev);
+ free(dev->addr);
+ free(dev);
+ }
+ }
+
+ hci_close_dev(dd);
+
+ bt_free(local_addr);
+ bt_free(peer_addr);
}
void hcid_dbus_disconn_complete(bdaddr_t *local, bdaddr_t *peer, uint8_t reason)
diff --git a/hcid/dbus.h b/hcid/dbus.h
index 7635b122..71d436b8 100644
--- a/hcid/dbus.h
+++ b/hcid/dbus.h
@@ -89,6 +89,17 @@ struct discovered_dev_info {
name_status_t name_status;
};
+typedef enum {
+ CONNECTING,
+ PAIRING
+} bonding_state_t;
+
+struct bonding_request_info {
+ bdaddr_t *addr;
+ DBusMessage *msg;
+ bonding_state_t bonding_state;
+};
+
struct hci_dbus_data {
uint16_t dev_id;
uint16_t path_id;
@@ -100,6 +111,7 @@ struct hci_dbus_data {
struct slist *discovered_devices;
char *requestor_name; /* requestor unique name */
struct slist *passkey_agents;
+ struct slist *bonding_requests;
};
struct passkey_agent {
diff --git a/hcid/hcid.h b/hcid/hcid.h
index ca39bc1d..db2133c3 100644
--- a/hcid/hcid.h
+++ b/hcid/hcid.h
@@ -116,7 +116,7 @@ void hcid_dbus_inquiry_start(bdaddr_t *local);
void hcid_dbus_inquiry_complete(bdaddr_t *local);
void hcid_dbus_inquiry_result(bdaddr_t *local, bdaddr_t *peer, uint32_t class, int8_t rssi);
void hcid_dbus_remote_name(bdaddr_t *local, bdaddr_t *peer, uint8_t status, char *name);
-void hcid_dbus_conn_complete(bdaddr_t *local, bdaddr_t *peer);
+void hcid_dbus_conn_complete(bdaddr_t *local, uint8_t status, uint16_t handle, bdaddr_t *peer);
void hcid_dbus_disconn_complete(bdaddr_t *local, bdaddr_t *peer, uint8_t reason);
void hcid_dbus_bonding_created_complete(bdaddr_t *local, bdaddr_t *peer, const uint8_t status);
void hcid_dbus_setname_complete(bdaddr_t *local);
@@ -126,7 +126,7 @@ static inline void hcid_dbus_inquiry_start(bdaddr_t *local) {}
static inline void hcid_dbus_inquiry_complete(bdaddr_t *local) {}
static inline void hcid_dbus_inquiry_result(bdaddr_t *local, bdaddr_t *peer, uint32_t class, int8_t rssi) {}
static inline void hcid_dbus_remote_name(bdaddr_t *local, bdaddr_t *peer, uint8_t status, char *name) {}
-static inline void hcid_dbus_conn_complete(bdaddr_t *local, bdaddr_t *peer) {}
+static inline void hcid_dbus_conn_complete(bdaddr_t *local, uint8_t status, uint16_t handle, bdaddr_t *peer) {}
static inline void hcid_dbus_disconn_complete(bdaddr_t *local, bdaddr_t *peer, uint8_t reason) {}
static inline void hcid_dbus_bonding_created_complete(bdaddr_t *local, bdaddr_t *peer, const uint8_t status) {}
static inline void hcid_dbus_setname_complete(bdaddr_t *local) {}
@@ -174,33 +174,3 @@ void enable_debug();
void disable_debug();
void start_logging(const char *ident, const char *message);
void stop_logging(void);
-
-static inline int find_conn(int dd, int dev_id, long arg)
-{
- struct hci_conn_list_req *cl;
- struct hci_conn_info *ci;
- int i;
-
- cl = malloc(10 * sizeof(*ci) + sizeof(*cl));
- if (!cl) {
- error("Can't allocate memory");
- return 0;
- }
-
- cl->dev_id = dev_id;
- cl->conn_num = 10;
- ci = cl->conn_info;
-
- if (ioctl(dd, HCIGETCONNLIST, (void *) cl)) {
- error("Can't get connection list");
- return 0;
- }
-
- for (i = 0; i < cl->conn_num; i++, ci++)
- if (!bacmp((bdaddr_t *) arg, &ci->bdaddr))
- return 1;
-
- free(cl);
-
- return 0;
-}
diff --git a/hcid/security.c b/hcid/security.c
index f3c03ef2..f42738a5 100644
--- a/hcid/security.c
+++ b/hcid/security.c
@@ -465,14 +465,14 @@ static inline void conn_complete(int dev, bdaddr_t *sba, void *ptr)
{
evt_conn_complete *evt = ptr;
+ hcid_dbus_conn_complete(sba, evt->status, evt->handle, &evt->bdaddr);
+
if (evt->status)
return;
update_lastused(sba, &evt->bdaddr);
name_resolve(dev, &evt->bdaddr);
-
- hcid_dbus_conn_complete(sba, &evt->bdaddr);
}
static inline void disconn_complete(int dev, bdaddr_t *sba, void *ptr)