diff options
author | Claudio Takahasi <claudio.takahasi@openbossa.org> | 2006-03-16 22:47:01 +0000 |
---|---|---|
committer | Claudio Takahasi <claudio.takahasi@openbossa.org> | 2006-03-16 22:47:01 +0000 |
commit | ad51f992f0c529a928edad39a045864a02e641af (patch) | |
tree | e7a76bbed664bca8e2f4e4a6c6ff0316cd396de9 | |
parent | 4a77b36ae82117567bd7045693a45b139055557b (diff) |
Initial create bonding service
-rw-r--r-- | hcid/dbus-adapter.c | 194 | ||||
-rw-r--r-- | hcid/dbus.c | 202 | ||||
-rw-r--r-- | hcid/dbus.h | 12 | ||||
-rw-r--r-- | hcid/hcid.h | 34 | ||||
-rw-r--r-- | hcid/security.c | 4 |
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) |