diff options
| author | Claudio Takahasi <claudio.takahasi@openbossa.org> | 2007-08-31 14:52:14 +0000 | 
|---|---|---|
| committer | Claudio Takahasi <claudio.takahasi@openbossa.org> | 2007-08-31 14:52:14 +0000 | 
| commit | 1778b4d041e6251d7411467a1db978730f8fdce0 (patch) | |
| tree | f6f27183bb2f1ff9abf06a9bc4d357bc64d6e5a9 /network/server.c | |
| parent | edce261bbbcaa3bca4d68289e4f244c1f709c0c9 (diff) | |
network: added setup msg timeout and retransmissions
Diffstat (limited to 'network/server.c')
| -rw-r--r-- | network/server.c | 216 | 
1 files changed, 127 insertions, 89 deletions
| diff --git a/network/server.c b/network/server.c index 672fd555..5b741edd 100644 --- a/network/server.c +++ b/network/server.c @@ -53,6 +53,8 @@  #include "dbus-helper.h"  #define NETWORK_SERVER_INTERFACE "org.bluez.network.Server" +#define SETUP_TIMEOUT		1000 +#define MAX_SETUP_ATTEMPTS	3  #include "bridge.h"  #include "common.h" @@ -60,9 +62,14 @@  #include "server.h"  /* Pending Authorization */ -struct pending_auth { -	char			*addr;		/* Bluetooth Address */ -	GIOChannel		*io;		/* BNEP connection setup io channel */ +struct setup_session { +	char		*address;	/* Remote Bluetooth Address */ +	uint16_t	dst_role;	/* Destination role */ +	uint16_t	src_role;	/* Source role */ +	int		nsk;		/* L2CAP socket */ +	int		attempts;	/* Setup msg received */ +	guint		watch;		/* BNEP setup watch */ +	guint		timeout;	/* Max setup time */  };  /* Main server structure */ @@ -82,7 +89,7 @@ struct network_server {  static char netdev[16] = "bnep%d";  static GIOChannel *bnep_io = NULL;  static DBusConnection *connection = NULL; -static struct pending_auth *pending_auth = NULL; +static GSList *setup_sessions = NULL;  static int store_property(bdaddr_t *src, uint16_t id,  			const char *key, const char *value) @@ -101,18 +108,15 @@ static int store_property(bdaddr_t *src, uint16_t id,  	return textfile_put(filename, key, value);  } -static void pending_auth_free(struct pending_auth *pauth) +static void setup_free(struct setup_session *s)  { -	if (!pauth) -		return; +	g_free(s->address); +	g_free(s); +} -	if (pauth->addr) -		g_free(pauth->addr); -	if (pauth->io) { -		g_io_channel_close(pauth->io); -		g_io_channel_unref(pauth->io); -	} -	g_free(pauth); +static int setup_cmp(const struct setup_session *s, const char *addr) +{ +	return strcmp(s->address, addr);  }  static void add_lang_attr(sdp_record_t *r) @@ -210,7 +214,7 @@ static int create_server_record(sdp_buf_t *buf, const char *name,  	/* Supported protocols */  	{ -		uint16_t ptype[] = {  +		uint16_t ptype[] = {  			0x0800,  /* IPv4 */  			0x0806,  /* ARP */  		}; @@ -258,25 +262,21 @@ static int create_server_record(sdp_buf_t *buf, const char *name,  	return ret;  } -static int send_bnep_ctrl_rsp(GIOChannel *chan, uint16_t response) +static ssize_t send_bnep_ctrl_rsp(int sk, uint16_t response)  {  	struct bnep_control_rsp rsp; -	GIOError gerr; -	gsize n;  	rsp.type = BNEP_CONTROL;  	rsp.ctrl = BNEP_SETUP_CONN_RSP;  	rsp.resp = htons(response); -	gerr = g_io_channel_write(chan, (gchar *)&rsp, sizeof(rsp), &n); - -	return -gerr; +	return send(sk, &rsp, sizeof(rsp), 0);  } -static void cancel_authorization(const char *address) +static void cancel_authorization(struct setup_session *s)  {  	DBusMessage *msg; -	const char *uuid = ""; +	const char *uuid;  	msg = dbus_message_new_method_call("org.bluez", "/org/bluez",  						"org.bluez.Database", @@ -286,8 +286,9 @@ static void cancel_authorization(const char *address)  		return;  	} +	uuid = bnep_uuid(s->dst_role);  	dbus_message_append_args(msg, -			DBUS_TYPE_STRING, &address, +			DBUS_TYPE_STRING, &s->address,  			DBUS_TYPE_STRING, &uuid,  			DBUS_TYPE_INVALID); @@ -296,29 +297,32 @@ static void cancel_authorization(const char *address)  static void authorization_callback(DBusPendingCall *pcall, void *data)  { -	struct network_server *ns = data; +	struct setup_session *s = data; +	struct network_server *ns = NULL;  	DBusMessage *reply = dbus_pending_call_steal_reply(pcall); -	char devname[16]; +	char path[MAX_PATH_LENGTH], devname[16]; +	uint16_t response = BNEP_CONN_NOT_ALLOWED;  	DBusError derr; -	uint16_t response; -	int sk; -	if (!pending_auth) { +	if (!g_slist_find(setup_sessions, s)) {  		dbus_message_unref(reply); -		dbus_pending_call_unref(pcall);  		return;  	} -	sk = g_io_channel_unix_get_fd(pending_auth->io); +	snprintf(path, MAX_PATH_LENGTH, NETWORK_PATH"/%s", bnep_name(s->dst_role)); +	dbus_connection_get_object_user_data(connection, path, (void *) &ns); + +	/* Server can be disabled in the meantime */ +	if (ns == NULL || ns->enable == FALSE) +		goto failed;  	dbus_error_init(&derr);  	if (dbus_set_error_from_message(&derr, reply)) {  		error("Access denied: %s", derr.message);  		if (dbus_error_has_name(&derr, DBUS_ERROR_NO_REPLY)) {  			debug("Canceling authorization request"); -			cancel_authorization(pending_auth->addr); +			cancel_authorization(s);  		} -		response = BNEP_CONN_NOT_ALLOWED;  		dbus_error_free(&derr);  		goto failed;  	} @@ -326,10 +330,8 @@ static void authorization_callback(DBusPendingCall *pcall, void *data)  	memset(devname, 0, 16);  	strncpy(devname, netdev, 16); -	if (bnep_connadd(sk, ns->id, devname) < 0) { -		response = BNEP_CONN_NOT_ALLOWED; +	if (bnep_connadd(s->nsk, s->dst_role, devname) < 0)  		goto failed; -	}  	info("Authorization succedded. New connection: %s", devname);  	response = BNEP_SUCCESS; @@ -337,36 +339,53 @@ static void authorization_callback(DBusPendingCall *pcall, void *data)  	if (bridge_add_interface("pan0", devname) < 0) {  		error("Can't add %s to the bridge: %s(%d)",  				devname, strerror(errno), errno); -		response = BNEP_CONN_NOT_ALLOWED;  		goto failed;  	}  	bnep_if_up(devname, TRUE);  	bnep_if_up("pan0", TRUE); -	ns->clients = g_slist_append(ns->clients, g_strdup(pending_auth->addr)); +	ns->clients = g_slist_append(ns->clients, g_strdup(s->address));  	/* FIXME: Enable routing if applied */  	/* FIXME: send the D-Bus message to notify the new bnep iface */  failed: -	send_bnep_ctrl_rsp(pending_auth->io, response); +	send_bnep_ctrl_rsp(s->nsk, response); +	dbus_message_unref(reply); +} -	pending_auth_free(pending_auth); -	pending_auth = NULL; +static void setup_watch_destroy(void *data) +{ +	struct setup_session *s; +	GSList *l; -	close(sk); +	/* +	 * Remote initiated: socket HUP +	 * Authorization: denied/accepted +	 */ +	l = g_slist_find(setup_sessions, data); +	if (!l) +		return; -	dbus_message_unref(reply); -	dbus_pending_call_unref(pcall); +	s = l->data; + +	setup_sessions = g_slist_remove(setup_sessions, s); + +	/* Remove active watches */ +	if (s->watch) +		g_source_remove(s->watch); +	if (s->timeout) +		g_source_remove(s->timeout); +	setup_free(s);  } -static int authorize_connection(const char *address, struct network_server *ns) +static int authorize_connection(struct setup_session *s)  {  	DBusMessage *msg;  	DBusPendingCall *pending; -	const char *uuid = ""; /* FIXME: */ +	const char *uuid;  	msg = dbus_message_new_method_call("org.bluez", "/org/bluez",  				"org.bluez.Database", "RequestAuthorization"); @@ -375,10 +394,11 @@ static int authorize_connection(const char *address, struct network_server *ns)  		return -ENOMEM;  	} -	debug("Requesting authorization for %s UUID:%s", address, uuid); +	uuid = bnep_uuid(s->dst_role); +	debug("Requesting authorization for %s UUID:%s", s->address, uuid);  	dbus_message_append_args(msg, -			DBUS_TYPE_STRING, &address, +			DBUS_TYPE_STRING, &s->address,  			DBUS_TYPE_STRING, &uuid,  			DBUS_TYPE_INVALID); @@ -389,7 +409,9 @@ static int authorize_connection(const char *address, struct network_server *ns)  		return -EACCES;  	} -	dbus_pending_call_set_notify(pending, authorization_callback, ns, NULL); +	dbus_pending_call_set_notify(pending, +			authorization_callback, s, setup_watch_destroy); +	dbus_pending_call_unref(pending);  	dbus_message_unref(msg);  	return 0; @@ -419,38 +441,39 @@ static uint16_t inline chk_role(uint16_t dst_role, uint16_t src_role)  static gboolean connect_setup_event(GIOChannel *chan,  					GIOCondition cond, gpointer data)  { +	struct setup_session *s = data;  	struct network_server *ns = NULL;  	struct bnep_setup_conn_req *req;  	unsigned char pkt[BNEP_MTU];  	char path[MAX_PATH_LENGTH]; -	uint16_t dst_role, src_role, response; +	uint16_t response;  	uint8_t *pservice; -	GIOError gerr; -	gsize n; +	ssize_t r; +	int sk;  	if (cond & G_IO_NVAL)  		return FALSE;  	if (cond & (G_IO_ERR | G_IO_HUP)) {  		error("Hangup or error on BNEP socket"); -		cancel_authorization(pending_auth->addr); +		/* If there is a pending authorization */ +		if (s->attempts) +			cancel_authorization(s);  		return FALSE;  	} +	sk = g_io_channel_unix_get_fd(chan);  	memset(pkt, 0, sizeof(pkt)); -	n = 0; -	gerr = g_io_channel_read(chan, (gchar *)pkt, sizeof(pkt) - 1, &n); -	if (gerr != G_IO_ERROR_NONE) -		return FALSE; +	r = recv(sk, pkt, sizeof(pkt) - 1, 0);  	req = (struct bnep_setup_conn_req *) pkt; -	/*  +	/*  	 * FIXME: According to BNEP SPEC the UUID size can be  	 * 2-16 bytes. Currently only 2 bytes size is supported  	 */ -	if (req->uuid_size != 2 || n != (sizeof(*req) + req->uuid_size * 2)) { +	if (req->uuid_size != 2 || r != (sizeof(*req) + req->uuid_size * 2)) {  		error("Invalid BNEP packet size"); -		response = BNEP_CONN_INVALID_SVC;  +		response = BNEP_CONN_INVALID_SVC;  		goto reply;  	} @@ -461,16 +484,16 @@ static gboolean connect_setup_event(GIOChannel *chan,  	pservice = req->service;  	/* Getting destination service: considering 2 bytes size */ -	dst_role = ntohs(bt_get_unaligned((uint16_t *) pservice)); +	s->dst_role = ntohs(bt_get_unaligned((uint16_t *) pservice));  	pservice += req->uuid_size;  	/* Getting source service: considering 2 bytes size */ -	src_role = ntohs(bt_get_unaligned((uint16_t *) pservice)); +	s->src_role = ntohs(bt_get_unaligned((uint16_t *) pservice)); -	response = chk_role(src_role, dst_role); +	response = chk_role(s->src_role, s->dst_role);  	if (response)  		goto reply; -	snprintf(path, MAX_PATH_LENGTH, NETWORK_PATH"/%s", bnep_name(dst_role)); +	snprintf(path, MAX_PATH_LENGTH, NETWORK_PATH"/%s", bnep_name(s->dst_role));  	dbus_connection_get_object_user_data(connection, path, (void *) &ns);  	if (ns == NULL || ns->enable == FALSE) { @@ -478,35 +501,41 @@ static gboolean connect_setup_event(GIOChannel *chan,  		goto reply;  	} -	/* -	 * FIXME: Check if the connection already exists. Check if the -	 * BNEP SPEC allows return "connection not allowed" for this case -	 */ +	if (s->timeout) { +		g_source_remove(s->timeout); +		s->timeout = 0; +	} + +	if (++s->attempts > MAX_SETUP_ATTEMPTS) { +		/* Retransmission */ +		response = BNEP_CONN_NOT_ALLOWED; +		goto reply; +	}  	/* Wait authorization before reply success */ -	if (authorize_connection(pending_auth->addr, ns) < 0) { +	if (authorize_connection(s) < 0) {  		response = BNEP_CONN_NOT_ALLOWED;  		goto reply;  	}  	return TRUE;  reply: -	send_bnep_ctrl_rsp(chan, response); +	send_bnep_ctrl_rsp(sk, response);  	return FALSE;  } -static void connect_setup_destroy(gpointer data) +static gboolean setup_timeout(void *data)  { -	if (pending_auth) { -		pending_auth_free(pending_auth); -		pending_auth = NULL; -	} +	setup_watch_destroy(data); +	return FALSE;  }  static gboolean connect_event(GIOChannel *chan,  				GIOCondition cond, gpointer data)  {  	struct sockaddr_l2 addr; +	struct setup_session *s; +	GIOChannel *io;  	socklen_t addrlen;  	char peer[18];  	bdaddr_t dst; @@ -537,26 +566,29 @@ static gboolean connect_event(GIOChannel *chan,  	info("Connection from: %s on PSM %d", peer, psm); -	/* Only one authorization at same time */ -	if (pending_auth) { -		GIOChannel *io; -		error("Rejecting %s(pending authorization)", peer); -		io = g_io_channel_unix_new(nsk); -		send_bnep_ctrl_rsp(io, BNEP_CONN_NOT_ALLOWED); -		g_io_channel_close(io); -		g_io_channel_unref(io); +	if (g_slist_find_custom(setup_sessions, peer, +				(GCompareFunc) setup_cmp)) { +		error("Pending connection setup session"); +		close(nsk);  		return TRUE;  	} -	pending_auth = g_new0(struct pending_auth, 1); -	pending_auth->addr = g_strdup(peer); -	pending_auth->io = g_io_channel_unix_new(nsk); -	g_io_channel_set_close_on_unref(pending_auth->io, FALSE); +	s = g_new0(struct setup_session, 1); +	s->address = g_strdup(peer); +	s->nsk = nsk; +	io = g_io_channel_unix_new(nsk); +	g_io_channel_set_close_on_unref(io, TRUE);  	/* New watch for BNEP setup */ -	g_io_add_watch_full(pending_auth->io, G_PRIORITY_DEFAULT, +	s->watch = g_io_add_watch_full(io, G_PRIORITY_DEFAULT,  			G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, -			connect_setup_event, pending_auth, &connect_setup_destroy); +			connect_setup_event, s, &setup_watch_destroy); +	g_io_channel_unref(io); + +	/* Remove the timeout at the first valid msg */ +	s->timeout = g_timeout_add(SETUP_TIMEOUT, setup_timeout, s); + +	setup_sessions = g_slist_append(setup_sessions, s);  	return TRUE;  } @@ -637,6 +669,12 @@ fail:  void server_exit()  { +	if (setup_sessions) { +		g_slist_foreach(setup_sessions, (GFunc) setup_free, NULL); +		g_slist_free(setup_sessions); +		setup_sessions = NULL; +	} +  	if (bnep_io != NULL) {  		g_io_channel_close(bnep_io);  		g_io_channel_unref(bnep_io); | 
