diff options
| -rw-r--r-- | hcid/dbus-adapter.c | 4 | ||||
| -rw-r--r-- | hcid/dbus-error.c | 16 | ||||
| -rw-r--r-- | hcid/dbus-sdp.c | 501 | ||||
| -rw-r--r-- | hcid/dbus.h | 17 | 
4 files changed, 537 insertions, 1 deletions
| diff --git a/hcid/dbus-adapter.c b/hcid/dbus-adapter.c index 039b0672..513d875c 100644 --- a/hcid/dbus-adapter.c +++ b/hcid/dbus-adapter.c @@ -2214,6 +2214,8 @@ DBusHandlerResult msg_func_device(DBusConnection *conn, DBusMessage *msg, void *  		return handle_security_method(conn, msg, data);  	else if (!strcmp(RFCOMM_INTERFACE, iface))  		return handle_rfcomm_method(conn, msg, data); -	else +	else if (!strcmp(SDP_INTERFACE, iface)) +		return handle_sdp_method(conn, msg, data); +	else   		return error_not_implemented(conn, msg);  } diff --git a/hcid/dbus-error.c b/hcid/dbus-error.c index 5407da15..bcd1b109 100644 --- a/hcid/dbus-error.c +++ b/hcid/dbus-error.c @@ -208,3 +208,19 @@ DBusHandlerResult error_binding_does_not_exist(DBusConnection *conn, DBusMessage  {  	return error_does_not_exist(conn, msg, "Binding does not exist");  } + +DBusHandlerResult error_service_already_exists(DBusConnection *conn, DBusMessage *msg) +{ +	return error_already_exists(conn, msg, "Service already exists"); +} + +DBusHandlerResult error_service_does_not_exist(DBusConnection *conn, DBusMessage *msg) +{ +	return error_does_not_exist(conn, msg, "Service does not exist"); +} + +DBusHandlerResult error_service_search_in_progress(DBusConnection *conn, DBusMessage *msg) +{ +	return error_in_progress(conn, msg, "Service search in progress"); +} + diff --git a/hcid/dbus-sdp.c b/hcid/dbus-sdp.c index f3bf47c4..e2a46fb8 100644 --- a/hcid/dbus-sdp.c +++ b/hcid/dbus-sdp.c @@ -27,3 +27,504 @@  #include <stdio.h>  #include <errno.h> +#include <unistd.h> + +#include <fcntl.h> + +#include <sys/types.h> +#include <sys/socket.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> +#include <bluetooth/l2cap.h> +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h> + +#include <dbus/dbus.h> + +#include "dbus.h" +#include "hcid.h" +#include "textfile.h" + +struct dbus_sdp_record { +	char *owner;		/* null for remote services or unique name if local */ +	bdaddr_t provider;	/* remote Bluetooth address or local address */ +	char *name;		/* service name */ +	uint32_t identifier; +	uint16_t uuid; +	uint8_t channel; +}; + +/* list of remote and local service records */ +static struct slist *sdp_records = NULL; + +struct search_request_info *search_request_info_new(bdaddr_t *dba, DBusMessage *msg) +{ +	struct search_request_info *search = malloc(sizeof(struct search_request_info)); +	if (!search) +		return NULL; + +	memset(search, 0, sizeof(*search)); + +	bacpy(&search->bdaddr, dba); +	search->rq = dbus_message_ref(msg); + +	return search; +} +void search_request_info_free(struct search_request_info *search) +{ +	if (search->rq) +		dbus_message_unref(search->rq); +	if (search->session) +		free(search->session); +	if (search->io) +		free(search->io); + +	free(search); +} + +void dbus_sdp_record_free(struct dbus_sdp_record *rec) +{ +	if (rec->owner) +		free(rec->owner); +	if (rec->name) +		free(rec->name); + +	free(rec); +} + +static void id2str(uint32_t id, char *dst) +{ +	snprintf(dst, 9, "%2.2X%2.2X%2.2X%2.2X", (id >> 24) & 0xFF, +			(id >> 16) & 0xFF, (id >> 8) & 0xFF, id & 0xFF); +	debug ("%s, identifier:%s", __PRETTY_FUNCTION__, dst); +} + +static uint32_t str2id(const char *id) +{ +	return atoi(id); +} + + +static uint32_t generate_new_id(const bdaddr_t *provider, uint8_t channel) +{ +	/* FIXME: generate the pseudo random id */ +	return 1; +} + +struct dbus_sdp_record *dbus_sdp_record_new(const char *owner, bdaddr_t *provider, +						const char *name, uint32_t uuid, uint8_t channel) +{ +	struct dbus_sdp_record *rec; + +	/* FIXME: validate the arguments */ + +	rec = malloc(sizeof(struct dbus_sdp_record)); +	if (!rec) +		return NULL; + +	memset(rec, 0, sizeof(*rec)); +	if (owner) { +		rec->owner = strdup(owner); +		if (!rec->owner) +			goto mem_fail; +	} + +	bacpy(&rec->provider, provider); + +	rec->name = strdup(name); +	if(!rec->name) +		goto mem_fail; +	 +	rec->uuid = uuid; +	rec->channel = channel; +	rec->identifier = generate_new_id(provider, channel); + +	return rec; + +mem_fail: +	dbus_sdp_record_free(rec); +	return NULL; +} + +static void owner_exited(const char *owner, struct hci_dbus_data *dbus_data) +{ +	struct slist *cur, *next; + +	debug("SDP provider owner %s exited", owner); + +	for (cur = sdp_records; cur != NULL; cur = next) { +		struct dbus_sdp_record *rec = cur->data; + +		next = cur->next; + +		if(!rec->owner) +			continue; + +		if (strcmp(rec->owner, owner)) +			continue; + +		sdp_records = slist_remove(sdp_records, rec); +		dbus_sdp_record_free(rec); +	} +} + +static int record_cmp(const struct dbus_sdp_record *a, const struct dbus_sdp_record *b) +{ +	int ret; +	 +	if (b->owner) { +		if (!a->owner) +			return -1; +		ret = strcmp(a->owner, b->owner); +		if (ret) +			return ret; +	} + +	if (bacmp(&b->provider, BDADDR_ANY)) { +		if (!bacmp(&a->provider, BDADDR_ANY)) +			return -1; +		ret = bacmp(&a->provider, &b->provider); +		if (ret) +			return ret; +	} + +	if (b->uuid) { +		ret = (a->uuid - b->uuid); +		if (ret) +			return ret; +	} + +	if (b->channel) { +		ret = (a->channel - b->channel); +		if (ret) +			return ret; +	} + +	return 0; +} + +static gboolean sdp_client_connection_cb(GIOChannel *chan, GIOCondition cond, struct hci_dbus_data *dbus_data) +{ +	debug("%s, line:%d condition:%d", __PRETTY_FUNCTION__, __LINE__, cond); +	/* FIXME: send the request */ +	return FALSE; +} + +int dbus_sdp_connect(struct hci_dbus_data *dbus_data, const bdaddr_t *sba, +			const bdaddr_t *dba, uint32_t flags, int *err) +{ +	struct sockaddr_l2 sa; +	sdp_session_t *session = malloc(sizeof(sdp_session_t)); +	if (!session) { +		if (err) +			*err = ENOMEM; +		return -1; +	} + +	memset(session, 0, sizeof(*session)); +	session->flags = flags; + +	// create L2CAP connection +	session->sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); +	session->local = 0; +	if (session->sock >= 0) { +		sa.l2_family = AF_BLUETOOTH; +		sa.l2_psm = 0; +		if (bacmp(sba, BDADDR_ANY) != 0) { +			sa.l2_bdaddr = *sba; +			if (bind(session->sock, (struct sockaddr *) &sa, sizeof(sa)) < 0) +				goto fail; +		} +		if (flags & SDP_WAIT_ON_CLOSE) { +			struct linger l = { .l_onoff = 1, .l_linger = 1 }; +			setsockopt(session->sock, SOL_SOCKET, SO_LINGER, &l, sizeof(l)); +		} +		sa.l2_psm = htobs(SDP_PSM); +		sa.l2_bdaddr = *dba; + +		debug("%s, line:%d connecting ...", __PRETTY_FUNCTION__, __LINE__); + +		dbus_data->search->io = g_io_channel_unix_new(session->sock); + +		fcntl(session->sock, F_SETFL, fcntl(session->sock, F_GETFL, 0)|O_NONBLOCK); +		if (connect(session->sock, (struct sockaddr *) &sa, sizeof(sa)) < 0) { +			if ( !(errno == EAGAIN || errno == EINPROGRESS)) { +				error("connect() failed:%s (%d)", strerror(errno), errno); +				goto fail; +			} +			g_io_add_watch(dbus_data->search->io, G_IO_OUT, +					(GIOFunc)sdp_client_connection_cb, dbus_data); +		} else { +			debug("Connect completed in the first attempt"); +			sdp_client_connection_cb(dbus_data->search->io, G_IO_OUT, dbus_data); +		} + +		dbus_data->search->session = session; +		return 0; +	} +fail: +	if (err) +		*err = errno; + +	if (session->sock >= 0) +		close(session->sock); +	free(session); +	errno = *err; + +	return -1; +} + +static DBusHandlerResult get_identifiers(DBusConnection *conn, +						DBusMessage *msg, void *data) +{ +	char filename[PATH_MAX + 1]; +	struct hci_dbus_data *dbus_data = data; +	struct dbus_sdp_record *rec; +	struct slist *l; +	const char *peer; +	char *str; +	DBusMessage *reply; +	DBusMessageIter iter, array_iter; +	DBusError err; +	bdaddr_t sba, dba; +	uint32_t flags = 0; +	int conn_err, found = 0; + +	dbus_error_init(&err); + +	dbus_message_get_args(msg, &err, +			DBUS_TYPE_STRING, &peer, +			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); +	} + +	str2ba(peer, &dba); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +	/* check the cache */ +	dbus_message_iter_init_append(reply, &iter); +	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, +						DBUS_TYPE_STRING_AS_STRING, &array_iter); + +	for (l = sdp_records; l; l = l->next) { +		char id_str[9]; +		char *id_ptr = id_str; + +		rec = l->data; +		if (bacmp(&rec->provider, &dba)) +			continue; +		id2str(rec->identifier, id_ptr); +		dbus_message_iter_append_basic(&array_iter, +					DBUS_TYPE_STRING, &id_ptr); +		found = 1; +	} + +	dbus_message_iter_close_container(&iter, &array_iter); +	 +	if (found) +		return send_reply_and_unref(conn, reply); + +	dbus_message_unref(reply); + +	if (dbus_data->search) +		return error_service_search_in_progress(conn, msg); + +	/* check if it is a unknown address */ +	snprintf(filename, PATH_MAX, "%s/%s/lastseen", STORAGEDIR, dbus_data->address); + +	str = textfile_get(filename, peer); +	if (!str) +		return error_unknown_address(conn, msg); + +	free(str); + +	/* FIXME: if found, when it is invalid/expired? */ +	 +	/* FIXME: check if there is an active connection */ +	 +	/* Check if there is an inquiry/bonding in progress */ + +	/* Background search */ +	dbus_data->search = search_request_info_new(&dba, msg); +	if (!dbus_data->search) +		return DBUS_HANDLER_RESULT_NEED_MEMORY; + +	hci_devba(dbus_data->dev_id, &sba); + +	if (dbus_sdp_connect(dbus_data, &sba, &dba, flags, &conn_err) < 0) { +		search_request_info_free(dbus_data->search); +		dbus_data->search = NULL; +		return error_failed(conn, msg, conn_err); +	} + +	return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult get_identifiers_by_service(DBusConnection *conn, +						DBusMessage *msg, void *data) +{ +	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult get_uuid(DBusConnection *conn, +					 DBusMessage *msg, void *data) +{ +	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult get_name(DBusConnection *conn, +					DBusMessage *msg, void *data) +{ +	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + + +static DBusHandlerResult register_rfcomm(DBusConnection *conn, +						DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	struct dbus_sdp_record *rec, ref; +	DBusMessage *reply; +	DBusError err; +	const char *owner, *name; +	bdaddr_t provider; +	char id_str[9]; +	char *id_ptr = id_str; +	uint8_t channel; + +	owner = dbus_message_get_sender(msg); + +	dbus_error_init(&err); +	dbus_message_get_args(msg, &err, +			DBUS_TYPE_STRING, &name, +			DBUS_TYPE_BYTE, &channel, +			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); +	} + +	hci_devba(dbus_data->dev_id, &provider); +	 +	rec = dbus_sdp_record_new(owner, &provider, name, RFCOMM_UUID, channel); +	if (!rec) +		return DBUS_HANDLER_RESULT_NEED_MEMORY; +	 +	if (slist_find(sdp_records, &rec, (cmp_func_t)record_cmp)) { +		dbus_sdp_record_free(rec); +		return error_service_already_exists(conn, msg); +	} + +	id2str(rec->identifier, id_ptr); +	reply = dbus_message_new_method_return(msg); +	dbus_message_append_args(msg, +			DBUS_TYPE_STRING, &id_ptr, +			DBUS_TYPE_INVALID); +	if (!reply) { +		dbus_sdp_record_free(rec); +		return DBUS_HANDLER_RESULT_NEED_MEMORY; +	} + +	/* FIXME: register the service */ + +	/* Only add a D-Bus unique name listener if there isn't one already registered */ +	memset(&ref, 0, sizeof(ref)); +	bacpy(&ref.provider, BDADDR_ANY); + +	if (!slist_find(sdp_records, &ref, (cmp_func_t)record_cmp)) +		name_listener_add(conn, rec->owner, (name_cb_t)owner_exited, dbus_data); + +	sdp_records = slist_append(sdp_records, rec); + +	return send_reply_and_unref(conn, reply); +} + +static DBusHandlerResult unregister_rfcomm(DBusConnection *conn, +						DBusMessage *msg, void *data) +{ +	struct hci_dbus_data *dbus_data = data; +	struct dbus_sdp_record *rec, ref; +	struct slist *match; +	DBusMessage *reply; +	DBusError err; +	const char *owner, *identifier; + +	owner = dbus_message_get_sender(msg); + +	dbus_error_init(&err); +	dbus_message_get_args(msg, &err, +			DBUS_TYPE_STRING, &identifier, +			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); +	} + +	memset(&ref, 0, sizeof(ref)); +	 +	ref.uuid = RFCOMM_UUID; +	ref.identifier = str2id(identifier); +	hci_devba(dbus_data->dev_id, &ref.provider); + +	match = slist_find(sdp_records, &ref, (cmp_func_t)record_cmp); +	if (!match) +		return error_service_does_not_exist(conn, msg); + +	rec = match->data; +	 +	if (strcmp(rec->owner, owner)) +		return error_not_authorized(conn, msg); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return DBUS_HANDLER_RESULT_NEED_MEMORY; + +	/* FIXME: unregister the service */ + +	sdp_records = slist_remove(sdp_records, rec); +	dbus_sdp_record_free(rec); + +	bacpy(&ref.provider, BDADDR_ANY); +	ref.uuid = 0x0000; + +	/* Only remove the D-Bus unique name listener if there are no more record using this name */ +	if (!slist_find(sdp_records, &ref, (cmp_func_t)record_cmp)) +		name_listener_remove(conn, ref.name, (name_cb_t)owner_exited, dbus_data); + +	return send_reply_and_unref(conn, reply); +} + +static struct service_data sdp_services[] = { +	{ "GetIdentifiers",		get_identifiers			}, +	{ "GetIdentifiersByService",	get_identifiers_by_service	}, +	{ "GetUUID",			get_uuid			}, +	{ "GetName",			get_name			}, +	{ "RegisterRFCOMM",		register_rfcomm			}, +	{ "UnregisterRFCOMM",		unregister_rfcomm		}, +	{ NULL, NULL } +}; + +DBusHandlerResult handle_sdp_method(DBusConnection *conn, DBusMessage *msg, void *data) +{ +	service_handler_func_t handler; + +	handler = find_service_handler(sdp_services, msg); + +	if (handler) +		return handler(conn, msg, data); + +	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} diff --git a/hcid/dbus.h b/hcid/dbus.h index da6eeba7..aab6f354 100644 --- a/hcid/dbus.h +++ b/hcid/dbus.h @@ -27,7 +27,9 @@  #include <stdint.h>  #include <dbus/dbus.h>  #include <bluetooth/bluetooth.h> +#include <bluetooth/sdp.h>  #include "list.h" +#include "glib-ectomy.h"  #define BASE_PATH		"/org/bluez"  #define BASE_INTERFACE		"org.bluez" @@ -44,6 +46,8 @@  #define RFCOMM_INTERFACE	BASE_INTERFACE ".RFCOMM" +#define SDP_INTERFACE		BASE_INTERFACE ".SDP" +  #define MANAGER_PATH_MASK	(1 << 15)  #define ADAPTER_PATH_MASK	(1 << 14) @@ -97,6 +101,13 @@ struct bonding_request_info {  	int disconnect; /* disconnect after finish */  }; +struct search_request_info { +	bdaddr_t bdaddr; +	DBusMessage *rq; +	GIOChannel *io; +	sdp_session_t *session; +}; +  struct active_conn_info {  	bdaddr_t bdaddr;  	uint16_t handle; @@ -115,6 +126,7 @@ struct hci_dbus_data {  	char *requestor_name;	           /* requestor unique name */  	struct slist *passkey_agents;  	struct bonding_request_info *bonding; +	struct search_request_info *search;  	struct slist *active_conn;  	int pairing_active;  }; @@ -166,6 +178,9 @@ DBusHandlerResult error_record_does_not_exist(DBusConnection *conn, DBusMessage  DBusHandlerResult error_passkey_agent_already_exists(DBusConnection *conn, DBusMessage *msg);  DBusHandlerResult error_passkey_agent_does_not_exist(DBusConnection *conn, DBusMessage *msg);  DBusHandlerResult error_binding_does_not_exist(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult error_service_already_exists(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult error_service_does_not_exist(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult error_service_search_in_progress(DBusConnection *conn, DBusMessage *msg);  DBusHandlerResult error_connect_canceled(DBusConnection *conn, DBusMessage *msg);  typedef void (*name_cb_t)(const char *name, void *user_data); @@ -179,6 +194,8 @@ DBusHandlerResult handle_security_method(DBusConnection *conn, DBusMessage *msg,  DBusHandlerResult handle_rfcomm_method(DBusConnection *conn, DBusMessage *msg, void *data); +DBusHandlerResult handle_sdp_method(DBusConnection *conn, DBusMessage *msg, void *data); +  service_handler_func_t find_service_handler(struct service_data *services, DBusMessage *msg);  int handle_passkey_request(int dev, const char *path, bdaddr_t *sba, bdaddr_t *dba); | 
