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;  } | 
