diff options
author | Marcel Holtmann <marcel@holtmann.org> | 2006-09-15 14:44:22 +0000 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2006-09-15 14:44:22 +0000 |
commit | 0ab6b68b07ee96bf4bd4cf0fe7d4b68582b5caf5 (patch) | |
tree | 77d374eb81c90b66d9994c9cd058812d49209a16 /hcid/dbus-adapter.c | |
parent | 87123c20dcf8baf2b952709ff2aacb07a21ca1b3 (diff) |
Use L2CAP raw sockets for HCI connection creation
Diffstat (limited to 'hcid/dbus-adapter.c')
-rw-r--r-- | hcid/dbus-adapter.c | 355 |
1 files changed, 202 insertions, 153 deletions
diff --git a/hcid/dbus-adapter.c b/hcid/dbus-adapter.c index bfe7ae9a..907b81db 100644 --- a/hcid/dbus-adapter.c +++ b/hcid/dbus-adapter.c @@ -27,14 +27,16 @@ #include <stdio.h> #include <errno.h> -#include <unistd.h> #include <ctype.h> +#include <fcntl.h> +#include <unistd.h> #include <sys/param.h> #include <sys/socket.h> #include <bluetooth/bluetooth.h> #include <bluetooth/hci.h> #include <bluetooth/hci_lib.h> +#include <bluetooth/l2cap.h> #include <dbus/dbus.h> @@ -201,7 +203,8 @@ static int check_address(const char *addr) return 0; } -static struct bonding_request_info *bonding_request_new(bdaddr_t *peer) +static struct bonding_request_info *bonding_request_new(bdaddr_t *peer, DBusConnection *conn, + DBusMessage *msg) { struct bonding_request_info *bonding; @@ -214,6 +217,9 @@ static struct bonding_request_info *bonding_request_new(bdaddr_t *peer) bacpy(&bonding->bdaddr, peer); + bonding->conn = dbus_connection_ref(conn); + bonding->rq = dbus_message_ref(msg); + return bonding; } @@ -1690,19 +1696,164 @@ static DBusHandlerResult handle_dev_disconnect_remote_device_req(DBusConnection } -static DBusHandlerResult handle_dev_create_bonding_req(DBusConnection *conn, DBusMessage *msg, void *data) +static int l2raw_connect(const char *local, const bdaddr_t *remote) +{ + struct sockaddr_l2 addr; + long arg; + int sk; + + sk = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP); + if (sk < 0) { + error("Can't create socket: %s (%d)", strerror(errno), errno); + return sk; + } + + memset(&addr, 0, sizeof(addr)); + addr.l2_family = AF_BLUETOOTH; + str2ba(local, &addr.l2_bdaddr); + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + error("Can't bind socket: %s (%d)", strerror(errno), errno); + goto failed; + } + + arg = fcntl(sk, F_GETFL); + if (arg < 0) { + error("Can't get file flags: %s (%d)", strerror(errno), errno); + goto failed; + } + + arg |= O_NONBLOCK; + if (fcntl(sk, F_SETFL, arg) < 0) { + error("Can't set file flags: %s (%d)", strerror(errno), errno); + goto failed; + } + + memset(&addr, 0, sizeof(addr)); + addr.l2_family = AF_BLUETOOTH; + bacpy(&addr.l2_bdaddr, remote); + + if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + if (errno == EAGAIN || errno == EINPROGRESS) + return sk; + error("Can't connect socket: %s (%d)", strerror(errno), errno); + goto failed; + } + + return sk; + +failed: + close(sk); + return -1; +} + +static gboolean create_bonding_conn_complete(GIOChannel *io, GIOCondition cond, + struct hci_dbus_data *pdata) { - char filename[PATH_MAX + 1]; struct hci_request rq; - create_conn_cp cc_cp; - auth_requested_cp ar_cp; + auth_requested_cp cp; evt_cmd_status rp; + struct l2cap_conninfo cinfo; + socklen_t len; + int sk, dd, ret; + + if (!pdata->bonding) { + /* If we come here it implies a bug somewhere */ + debug("create_bonding_conn_complete: no pending bonding!"); + g_io_channel_close(io); + g_io_channel_unref(io); + return FALSE; + } + + if (cond & G_IO_NVAL) { + error_authentication_canceled(pdata->bonding->conn, pdata->bonding->rq); + goto cleanup; + } + + sk = g_io_channel_unix_get_fd(io); + + len = sizeof(ret); + if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { + error("Can't get socket error: %s (%d)", strerror(errno), errno); + error_failed(pdata->bonding->conn, pdata->bonding->rq, errno); + goto failed; + } + + if (ret != 0) { + error_connection_attempt_failed(pdata->bonding->conn, pdata->bonding->rq, ret); + goto failed; + } + + len = sizeof(cinfo); + if (getsockopt(sk, SOL_L2CAP, L2CAP_CONNINFO, &cinfo, &len) < 0) { + error("Can't get connection info: %s (%d)", strerror(errno), errno); + error_failed(pdata->bonding->conn, pdata->bonding->rq, errno); + goto failed; + } + + dd = hci_open_dev(pdata->dev_id); + if (dd < 0) { + error_no_such_adapter(pdata->bonding->conn, pdata->bonding->rq); + goto failed; + } + + memset(&rp, 0, sizeof(rp)); + + memset(&cp, 0, sizeof(cp)); + cp.handle = cinfo.hci_handle; + + memset(&rq, 0, sizeof(rq)); + rq.ogf = OGF_LINK_CTL; + rq.ocf = OCF_AUTH_REQUESTED; + rq.event = EVT_CMD_STATUS; + rq.cparam = &cp; + rq.clen = AUTH_REQUESTED_CP_SIZE; + rq.rparam = &rp; + rq.rlen = EVT_CMD_STATUS_SIZE; + + if (hci_send_req(dd, &rq, 100) < 0) { + error("Unable to send HCI request: %s (%d)", + strerror(errno), errno); + error_failed(pdata->bonding->conn, pdata->bonding->rq, errno); + hci_close_dev(dd); + goto failed; + } + + if (rp.status) { + error("HCI_Authentication_Requested failed with status 0x%02x", + rp.status); + error_failed(pdata->bonding->conn, pdata->bonding->rq, bt_error(rp.status)); + hci_close_dev(dd); + goto failed; + } + + hci_close_dev(dd); + + pdata->bonding->io_id = 0; + + return FALSE; + +failed: + g_io_channel_close(io); + +cleanup: + name_listener_remove(pdata->bonding->conn, dbus_message_get_sender(pdata->bonding->rq), + (name_cb_t) create_bond_req_exit, pdata); + + bonding_request_free(pdata->bonding); + pdata->bonding = NULL; + + return FALSE; +} + +static DBusHandlerResult handle_dev_create_bonding_req(DBusConnection *conn, DBusMessage *msg, void *data) +{ + char filename[PATH_MAX + 1]; DBusError err; char *str, *peer_addr = NULL; struct hci_dbus_data *dbus_data = data; - struct slist *l; bdaddr_t peer_bdaddr; - int dd, disconnect; + int sk; if (!dbus_data->up) return error_not_ready(conn, msg); @@ -1723,14 +1874,16 @@ static DBusHandlerResult handle_dev_create_bonding_req(DBusConnection *conn, DBu str2ba(peer_addr, &peer_bdaddr); - /* check if there is a pending bonding request */ - if (dbus_data->bonding) - return error_bonding_in_progress(conn, msg); - /* check if there is a pending discover: requested by D-Bus/non clients */ if (dbus_data->discover_state != STATE_IDLE || dbus_data->discovery_requestor) return error_discover_in_progress(conn, msg); + if (dbus_data->bonding) + return error_bonding_in_progress(conn, msg); + + if (slist_find(dbus_data->pin_reqs, &peer_bdaddr, pin_req_cmp)) + return error_bonding_in_progress(conn, msg); + /* check if a link key already exists */ create_name(filename, PATH_MAX, STORAGEDIR, dbus_data->address, "linkkeys"); @@ -1740,83 +1893,36 @@ static DBusHandlerResult handle_dev_create_bonding_req(DBusConnection *conn, DBu return error_bonding_already_exists(conn, msg); } - dd = hci_open_dev(dbus_data->dev_id); - if (dd < 0) - return error_no_such_adapter(conn, msg); + sk = l2raw_connect(dbus_data->address, &peer_bdaddr); + if (sk < 0) + return error_connection_attempt_failed(conn, msg, 0); - memset(&rq, 0, sizeof(rq)); - memset(&rp, 0, sizeof(rp)); - - rq.ogf = OGF_LINK_CTL; - rq.event = EVT_CMD_STATUS; - rq.rparam = &rp; - rq.rlen = EVT_CMD_STATUS_SIZE; - - /* check if there is an active connection */ - l = slist_find(dbus_data->active_conn, &peer_bdaddr, active_conn_find_by_bdaddr); - - if (!l) { - memset(&cc_cp, 0, sizeof(cc_cp)); - /* create a new connection */ - bacpy(&cc_cp.bdaddr, &peer_bdaddr); - cc_cp.pkt_type = htobs(HCI_DM1); - cc_cp.pscan_rep_mode = 0x02; - cc_cp.clock_offset = htobs(0x0000); - cc_cp.role_switch = 0x01; - - rq.ocf = OCF_CREATE_CONN; - rq.cparam = &cc_cp; - rq.clen = CREATE_CONN_CP_SIZE; - disconnect = 1; - } else { - struct active_conn_info *dev = l->data; - - memset(&ar_cp, 0, sizeof(ar_cp)); - - ar_cp.handle = dev->handle; - rq.ocf = OCF_AUTH_REQUESTED; - rq.cparam = &ar_cp; - rq.clen = AUTH_REQUESTED_CP_SIZE; - disconnect = 0; - } - - if (hci_send_req(dd, &rq, 100) < 0) { - int err = errno; - error("Unable to send the HCI request: %s (%d)", - strerror(errno), errno); - hci_close_dev(dd); - return error_failed(conn, msg, err); - } - - if (rp.status) { - error("%s failed with status 0x%02x", rq.ocf == OCF_CREATE_CONN ? - "HCI_Create_Connection" : "HCI_Authentication_Requested", - rp.status); - hci_close_dev(dd); - return error_failed(conn, msg, bt_error(rp.status)); + dbus_data->bonding = bonding_request_new(&peer_bdaddr, conn, msg); + if (!dbus_data->bonding) { + close(sk); + return DBUS_HANDLER_RESULT_NEED_MEMORY; } - dbus_data->bonding = bonding_request_new(&peer_bdaddr); - dbus_data->bonding->disconnect = disconnect; - dbus_data->bonding->rq = dbus_message_ref(msg); + dbus_data->bonding->io = g_io_channel_unix_new(sk); + dbus_data->bonding->io_id = g_io_add_watch(dbus_data->bonding->io, + G_IO_OUT | G_IO_NVAL, + (GIOFunc) create_bonding_conn_complete, + dbus_data); name_listener_add(conn, dbus_message_get_sender(msg), (name_cb_t) create_bond_req_exit, dbus_data); - hci_close_dev(dd); - return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult handle_dev_cancel_bonding_req(DBusConnection *conn, DBusMessage *msg, void *data) { struct hci_dbus_data *dbus_data = data; - struct slist *la; DBusMessage *reply; DBusError err; bdaddr_t peer_bdaddr; const char *peer_addr; - int dd = -1; + struct slist *l; if (!dbus_data->up) return error_not_ready(conn, msg); @@ -1837,103 +1943,46 @@ static DBusHandlerResult handle_dev_cancel_bonding_req(DBusConnection *conn, DBu str2ba(peer_addr, &peer_bdaddr); - /* check if there is a pending bonding request */ - if (!dbus_data->bonding || bacmp(&dbus_data->bonding->bdaddr, &peer_bdaddr)) { - error("No bonding request pending."); + if (!dbus_data->bonding || bacmp(&dbus_data->bonding->bdaddr, &peer_bdaddr)) return error_bonding_not_in_progress(conn, msg); - } if (strcmp(dbus_message_get_sender(dbus_data->bonding->rq), dbus_message_get_sender(msg))) return error_not_authorized(conn, msg); - dd = hci_open_dev(dbus_data->dev_id); - if (dd < 0) - return error_no_such_adapter(conn, msg); - dbus_data->bonding->cancel = 1; - la = slist_find(dbus_data->active_conn, &peer_bdaddr, active_conn_find_by_bdaddr); - - if (!la) { - /* connection request is pending */ - struct hci_request rq; - create_conn_cancel_cp cp; - evt_cmd_status rp; - - memset(&rq, 0, sizeof(rq)); - memset(&cp, 0, sizeof(cp)); - memset(&rp, 0, sizeof(rp)); + g_io_channel_close(dbus_data->bonding->io); - bacpy(&cp.bdaddr, &dbus_data->bonding->bdaddr); + l = slist_find(dbus_data->pin_reqs, &peer_bdaddr, pin_req_cmp); + if (l) { + struct pending_pin_info *pin_req = l->data; + + if (pin_req->replied) { + /* + * If disconnect can't be applied and the PIN Code Request + * was already replied it doesn't make sense cancel the + * remote passkey: return not authorized. + */ + return error_not_authorized(conn, msg); + } else { + int dd = hci_open_dev(dbus_data->dev_id); + if (dd < 0) { + error("Can't open hci%d: %s (%d)", + dbus_data->dev_id, strerror(errno), errno); + return DBUS_HANDLER_RESULT_HANDLED; + } - rq.ogf = OGF_LINK_CTL; - rq.ocf = OCF_CREATE_CONN_CANCEL; - rq.rparam = &rp; - rq.rlen = EVT_CMD_STATUS_SIZE; - rq.event = EVT_CMD_STATUS; - rq.cparam = &cp; - rq.clen = CREATE_CONN_CANCEL_CP_SIZE; + hci_send_cmd(dd, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY, 6, &peer_bdaddr); - if (hci_send_req(dd, &rq, 100) < 0) { - int err = errno; - error("Cancel bonding - unable to send the HCI request: %s (%d)", - strerror(errno), errno); hci_close_dev(dd); - return error_failed(conn, msg, err); - } - - if (rp.status) { - error("Cancel bonding - Failed with status 0x%02x", rp.status); - hci_close_dev(dd); - return error_failed(conn, msg, bt_error(rp.status)); - } + } - /* - * if the HCI doesn't support cancel create connection cmd let - * the create connection complete event arrives with page timeout. - * Bonding in progress will be returned to requestors. - */ - - } else { - struct slist *lb; - struct active_conn_info *cinfo = la->data; - - /* - * It is already connected, search in the pending passkey requests to - * figure out the current stage(waiting host passkey/remote passkey) - */ - lb = slist_find(dbus_data->pending_bondings, &peer_bdaddr, pending_bonding_cmp); - if (lb) { - struct pending_bonding_info *pb = lb->data; - /* 0: waiting host passkey 1: waiting remote passkey */ - if (pb->step) { - if (dbus_data->bonding->disconnect) { - - /* disconnect and let disconnect handler reply create bonding */ - if (hci_disconnect(dd, htobs(cinfo->handle), HCI_AUTHENTICATION_FAILURE, 1000) < 0) - error("Disconnect failed"); - } else { - /* - * If disconnect can't be applied and the PIN Code Request - * was already replied it doesn't make sense cancel the - * remote passkey: return not authorized. - */ - - error_not_authorized(conn, msg); - goto failed; - } - } else { - - /* for unlock PIN Code Request */ - hci_send_cmd(dd, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY, 6, &peer_bdaddr); - } - } + dbus_data->pin_reqs = slist_remove(dbus_data->pin_reqs, pin_req); + free(pin_req); } reply = dbus_message_new_method_return(msg); send_reply_and_unref(conn, reply); -failed: - hci_close_dev(dd); return DBUS_HANDLER_RESULT_HANDLED; } |