diff options
| author | Claudio Takahasi <claudio.takahasi@openbossa.org> | 2008-04-29 23:02:37 +0000 | 
|---|---|---|
| committer | Claudio Takahasi <claudio.takahasi@openbossa.org> | 2008-04-29 23:02:37 +0000 | 
| commit | a19826fc877b376b9456e612b1a82daba8661724 (patch) | |
| tree | bb197ccb8c0304f75aa985856ebc0a8c173072ad /network/server.c | |
| parent | 18da7d861808385a5e8c74213beac74599520348 (diff) | |
network: cleanup
Diffstat (limited to 'network/server.c')
| -rw-r--r-- | network/server.c | 393 | 
1 files changed, 222 insertions, 171 deletions
| diff --git a/network/server.c b/network/server.c index 88ff533f..f3e6a995 100644 --- a/network/server.c +++ b/network/server.c @@ -66,9 +66,12 @@ struct setup_session {  	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 */ +	guint		watch;		/* BNEP socket watch */ +}; + +struct timeout { +	guint	id;		/* Timeout id */ +	guint	watch;		/* BNEP socket watch */  };  /* Main server structure */ @@ -86,10 +89,49 @@ struct network_server {  static GIOChannel *bnep_io = NULL;  static DBusConnection *connection = NULL; -static GSList *setup_sessions = NULL; +static struct setup_session *setup = NULL; +static GSList *servers = NULL;  static const char *prefix = NULL;  static gboolean security = TRUE; +static struct setup_session *setup_session_new(gchar *address, +		uint16_t dst_role, uint16_t src_role, int nsk, guint watch) +{ +	struct setup_session *setup; + +	setup = g_new0(struct setup_session, 1); +	setup->address = g_strdup(address); +	setup->dst_role = dst_role; +	setup->src_role = src_role; +	setup->nsk = nsk; +	setup->watch = watch; + +	return setup; +} + +static void setup_session_free(struct setup_session *setup) +{ +	g_source_remove(setup->watch); +	g_free(setup->address); +	g_free(setup); +} + +static struct network_server *server_find(bdaddr_t *src, uint16_t role) +{ +	struct network_server *ns; +	GSList *l; + +	for (l = servers; l; l = l->next) { +		ns = l->data; +		if (bacmp(&ns->src, src) != 0) +			continue; +		if (ns->id == role) +			return ns; +	} + +	return NULL; +} +  static int store_property(bdaddr_t *src, uint16_t id,  			const char *key, const char *value)  { @@ -107,17 +149,6 @@ static int store_property(bdaddr_t *src, uint16_t id,  	return textfile_put(filename, key, value);  } -static void setup_free(struct setup_session *s) -{ -	g_free(s->address); -	g_free(s); -} - -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)  {  	sdp_lang_attr_t base_lang; @@ -257,18 +288,18 @@ static sdp_record_t *server_record_new(const char *name, uint16_t id)  	return record;  } -static ssize_t send_bnep_ctrl_rsp(int sk, uint16_t response) +static ssize_t send_bnep_ctrl_rsp(int sk, uint16_t val)  {  	struct bnep_control_rsp rsp;  	rsp.type = BNEP_CONTROL;  	rsp.ctrl = BNEP_SETUP_CONN_RSP; -	rsp.resp = htons(response); +	rsp.resp = htons(val);  	return send(sk, &rsp, sizeof(rsp), 0);  } -static void cancel_authorization(struct setup_session *s) +static void cancel_authorization_old()  {  	DBusMessage *msg;  	const char *uuid; @@ -281,39 +312,34 @@ static void cancel_authorization(struct setup_session *s)  		return;  	} -	uuid = bnep_uuid(s->dst_role); +	uuid = bnep_uuid(setup->dst_role);  	dbus_message_append_args(msg, -			DBUS_TYPE_STRING, &s->address, +			DBUS_TYPE_STRING, &setup->address,  			DBUS_TYPE_STRING, &uuid,  			DBUS_TYPE_INVALID);  	send_message_and_unref(connection, msg);  } -static void connection_setup(struct setup_session *s) +static int server_connadd(struct network_server *ns, int nsk, +			const gchar *address, uint16_t dst_role)  { -	struct network_server *ns = NULL; -	char path[MAX_PATH_LENGTH], devname[16]; -	uint16_t response = BNEP_CONN_NOT_ALLOWED; +	char devname[16];  	const char *bridge; - -	if (!g_slist_find(setup_sessions, s)) -		return; - -	snprintf(path, MAX_PATH_LENGTH, NETWORK_PATH"/%s", bnep_name(s->dst_role)); -	dbus_connection_get_object_user_data(connection, path, (void *) &ns); +	int err;  	/* Server can be disabled in the meantime */ -	if (ns == NULL || ns->enable == FALSE) -		goto failed; +	if (ns->enable == FALSE) +		return -EPERM;  	memset(devname, 0, 16);  	strncpy(devname, prefix, strlen(prefix)); -	if (bnep_connadd(s->nsk, s->dst_role, devname) < 0) -		goto failed; +	err = bnep_connadd(nsk, dst_role, devname); +	if (err < 0) +		return err; -	info("Authorization succedded. New connection: %s", devname); +	info("Added new connection: %s", devname);  	bridge = bridge_get_name(ns->id);  	if (bridge) { @@ -321,83 +347,85 @@ static void connection_setup(struct setup_session *s)  			error("Can't add %s to the bridge %s: %s(%d)",  					devname, bridge, strerror(errno),  					errno); -			goto failed; +			return -EPERM;  		}  		bnep_if_up(devname, 0);  	} else  		bnep_if_up(devname, ns->id); -	response = BNEP_SUCCESS; - -	ns->clients = g_slist_append(ns->clients, g_strdup(s->address)); - -failed: -	send_bnep_ctrl_rsp(s->nsk, response); -} - -static void setup_watch_destroy(void *data) -{ -	struct setup_session *s; -	GSList *l; - -	/* -	 * Remote initiated: socket HUP -	 * Authorization: denied/accepted -	 */ -	l = g_slist_find(setup_sessions, data); -	if (!l) -		return; - -	s = l->data; +	ns->clients = g_slist_append(ns->clients, g_strdup(address)); -	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); +	return 0;  }  static void req_auth_cb_old(DBusPendingCall *pcall, void *user_data)  { +	struct network_server *ns = user_data;  	DBusMessage *reply = dbus_pending_call_steal_reply(pcall); -	struct setup_session *s = user_data;  	DBusError derr; +	uint16_t val; + +	if (!setup) { +		info("Authorization cancelled: Client exited"); +		return; +	}  	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)) { -			cancel_authorization(s); +			cancel_authorization_old();  		}  		dbus_error_free(&derr); -	} else -		connection_setup(s); +		val = BNEP_CONN_NOT_ALLOWED; +		goto done; +	} + +	if (server_connadd(ns, setup->nsk, +			setup->address, setup->dst_role) < 0) +		val = BNEP_CONN_NOT_ALLOWED; +	else +		val = BNEP_SUCCESS; +done: +	send_bnep_ctrl_rsp(setup->nsk, val);  	dbus_message_unref(reply); +	setup_session_free(setup); +	setup = NULL;  }  static void req_auth_cb(DBusError *derr, void *user_data)  { -	struct setup_session *s = user_data; +	struct network_server *ns = user_data; +	uint16_t val; -	if (!g_slist_find(setup_sessions, s)) +	if (!setup) { +		info("Authorization cancelled: Client exited");  		return; +	}  	if (derr) {  		error("Access denied: %s", derr->message);  		if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY)) {  			bdaddr_t dst; -			str2ba(s->address, &dst); +			str2ba(setup->address, &dst);  			plugin_cancel_auth(&dst);  		} -	} else  -		connection_setup(s); +		val = BNEP_CONN_NOT_ALLOWED; +		goto done; +	} + +	if (server_connadd(ns, setup->nsk, +			setup->address, setup->dst_role) < 0) +		val = BNEP_CONN_NOT_ALLOWED; +	else +		val = BNEP_SUCCESS; -	setup_watch_destroy(s); +done: +	send_bnep_ctrl_rsp(setup->nsk, val); +	setup_session_free(setup); +	setup = NULL;  }  static int req_auth_old(const char *address, const char *uuid, void *user_data) @@ -427,30 +455,30 @@ static int req_auth_old(const char *address, const char *uuid, void *user_data)  	}  	dbus_pending_call_set_notify(pending, -			req_auth_cb_old, user_data, setup_watch_destroy); +			req_auth_cb_old, user_data, NULL);  	dbus_pending_call_unref(pending);  	dbus_message_unref(msg);  	return 0;  } -static int authorize_connection(bdaddr_t *src, struct setup_session *s) +static int authorize_connection(struct network_server *ns, const char *address)  {  	const char *uuid;  	bdaddr_t dst;  	int ret_val; -	uuid =  bnep_uuid(s->dst_role); -	str2ba(s->address, &dst); +	uuid =  bnep_uuid(ns->id); +	str2ba(address, &dst); -	ret_val = plugin_req_auth(src, &dst, uuid, req_auth_cb, s); +	ret_val = plugin_req_auth(&ns->src, &dst, uuid, req_auth_cb, ns);  	if (ret_val < 0) -		return req_auth_old(s->address, uuid, s); +		return req_auth_old(address, uuid, ns);  	else  		return ret_val;  } -static uint16_t inline chk_role(uint16_t dst_role, uint16_t src_role) +static uint16_t inline bnep_setup_chk(uint16_t dst_role, uint16_t src_role)  {  	/* Allowed PAN Profile scenarios */  	switch (dst_role) { @@ -471,108 +499,145 @@ static uint16_t inline chk_role(uint16_t dst_role, uint16_t src_role)  	return BNEP_CONN_INVALID_DST;  } -static gboolean connect_setup_event(GIOChannel *chan, -					GIOCondition cond, gpointer data) +static uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req, +				uint16_t *dst_role, uint16_t *src_role)  { -	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 response; -	uint8_t *pservice; -	ssize_t r; -	int sk; +	uint8_t *dest, *source; + +	dest = req->service; +	source = req->service + req->uuid_size; + +	switch (req->uuid_size) { +	case 2: /* UUID16 */ +		*dst_role = ntohs(bt_get_unaligned((uint16_t *) dest)); +		*src_role = ntohs(bt_get_unaligned((uint16_t *) source)); +		break; +	case 4: /* UUID32 */ +	case 16: /* UUID128 */ +		*dst_role = ntohl(bt_get_unaligned((uint32_t *) dest)); +		*src_role = ntohl(bt_get_unaligned((uint32_t *) source)); +		break; +	default: +		return BNEP_CONN_INVALID_SVC; +	} + +	return 0; +} + +static gboolean bnep_setup(GIOChannel *chan, +			GIOCondition cond, gpointer user_data) +{ +	struct timeout *to = user_data; +	struct network_server *ns; +	uint8_t packet[BNEP_MTU]; +	struct bnep_setup_conn_req *req = (void *) packet; +	struct sockaddr_l2 sa; +	socklen_t size; +	char address[18]; +	uint16_t rsp, src_role, dst_role; +	int n, sk;  	if (cond & G_IO_NVAL)  		return FALSE;  	if (cond & (G_IO_ERR | G_IO_HUP)) {  		error("Hangup or error on BNEP socket"); -		/* 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)); -	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 || r != (sizeof(*req) + req->uuid_size * 2)) { -		error("Invalid BNEP packet size"); -		response = BNEP_CONN_INVALID_SVC; -		goto reply; +	/* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */ +	n = read(sk, packet, sizeof(packet)); +	if (n < 0) { +		error("read(): %s(%d)", strerror(errno), errno); +		return FALSE;  	} -	if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ) { -		error("Invalid BNEP control packet content"); +	if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ)  		return FALSE; -	} -	pservice = req->service; -	/* Getting destination service: considering 2 bytes size */ -	s->dst_role = ntohs(bt_get_unaligned((uint16_t *) pservice)); -	pservice += req->uuid_size; -	/* Getting source service: considering 2 bytes size */ -	s->src_role = ntohs(bt_get_unaligned((uint16_t *) pservice)); +	rsp = bnep_setup_decode(req, &dst_role, &src_role); +	if (rsp) +		goto reply; -	response = chk_role(s->src_role, s->dst_role); -	if (response) +	rsp = bnep_setup_chk(dst_role, src_role); +	if (rsp)  		goto reply; -	snprintf(path, MAX_PATH_LENGTH, NETWORK_PATH"/%s", bnep_name(s->dst_role)); -	dbus_connection_get_object_user_data(connection, path, (void *) &ns); +	size = sizeof(sa); +	if (getsockname(sk, (struct sockaddr *) &sa, &size) < 0) { +		rsp = BNEP_CONN_NOT_ALLOWED; +		goto reply; +	} -	if (ns == NULL || ns->enable == FALSE) { -		response = BNEP_CONN_NOT_ALLOWED; +	ba2str(&sa.l2_bdaddr, address); +	ns = server_find(&sa.l2_bdaddr, dst_role); +	if (!ns || ns->enable == FALSE) { +		error("Server unavailable: %s (0x%x)", address, dst_role); +		rsp = BNEP_CONN_NOT_ALLOWED;  		goto reply;  	} -	if (s->timeout) { -		g_source_remove(s->timeout); -		s->timeout = 0; +	if (getpeername(sk, (struct sockaddr *) &sa, &size) < 0) { +		rsp = BNEP_CONN_NOT_ALLOWED; +		goto reply;  	} -	if (++s->attempts > MAX_SETUP_ATTEMPTS) { -		/* Retransmission */ -		response = BNEP_CONN_NOT_ALLOWED; +	ba2str(&sa.l2_bdaddr, address); + +	if (setup) { +		error("Connection rejected: Pending authorization"); +		rsp = BNEP_CONN_NOT_ALLOWED;  		goto reply;  	}  	/* Wait authorization before reply success */ -	if (authorize_connection(&ns->src, s) < 0) { -		response = BNEP_CONN_NOT_ALLOWED; +	if (authorize_connection(ns, address) < 0) { +		rsp = BNEP_CONN_NOT_ALLOWED;  		goto reply;  	} +	setup = setup_session_new(address, dst_role, src_role, sk, to->watch); + +	g_source_remove(to->id); +	to->id = 0; +  	return TRUE; +  reply: -	send_bnep_ctrl_rsp(sk, response); +	send_bnep_ctrl_rsp(sk, rsp); +  	return FALSE;  } -static gboolean setup_timeout(void *data) +static void setup_destroy(void *user_data)  { -	setup_watch_destroy(data); +	struct timeout *to = user_data; + +	if (to->id) +		g_source_remove(to->id); + +	g_free(to); +} + +static gboolean timeout_cb(void *user_data) +{ +	struct timeout *to = user_data; + +	to->id = 0; +	g_source_remove(to->watch); +  	return FALSE;  }  static gboolean connect_event(GIOChannel *chan, -				GIOCondition cond, gpointer data) +				GIOCondition cond, gpointer user_data)  {  	struct sockaddr_l2 addr; -	struct setup_session *s; +	struct timeout *to;  	GIOChannel *io;  	socklen_t addrlen; -	char peer[18]; -	bdaddr_t dst; -	unsigned short psm;  	int sk, nsk;  	if (cond & G_IO_NVAL) @@ -590,38 +655,25 @@ static gboolean connect_event(GIOChannel *chan,  	addrlen = sizeof(addr);  	nsk = accept(sk, (struct sockaddr *) &addr, &addrlen); -	if (nsk < 0) -		return TRUE; - -	bacpy(&dst, &addr.l2_bdaddr); -	psm = btohs(addr.l2_psm); -	ba2str(&dst, peer); - -	info("Connection from: %s on PSM %d", peer, psm); - -	if (g_slist_find_custom(setup_sessions, peer, -				(GCompareFunc) setup_cmp)) { -		error("Pending connection setup session"); -		close(nsk); +	if (nsk < 0) { +		error("accept(): %s(%d)", strerror(errno), errno);  		return TRUE;  	} -	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 */ -	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, 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); +	/* +	 * BNEP_SETUP_CONNECTION_REQUEST_MSG shall be received and +	 * user shall authorize the incomming connection before +	 * the time expires. +	 */ +	to = g_malloc0(sizeof(struct timeout)); +	to->id = g_timeout_add(SETUP_TIMEOUT, timeout_cb, to); +	to->watch = g_io_add_watch_full(io, G_PRIORITY_DEFAULT, +				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, +				bnep_setup, to, setup_destroy); +	g_io_channel_unref(io);  	return TRUE;  } @@ -708,12 +760,6 @@ 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); @@ -1034,6 +1080,7 @@ static void server_unregister(DBusConnection *conn, void *data)  	info("Unregistered server path:%s", ns->path); +	servers = g_slist_remove(servers, ns);  	server_free(ns);  } @@ -1094,6 +1141,8 @@ int server_register(const char *path, bdaddr_t *src, uint16_t id)  	ns->id = id;  	bacpy(&ns->src, src); +	servers = g_slist_append(servers, ns); +  	info("Registered server path:%s", path);  	return 0; @@ -1149,6 +1198,8 @@ int server_register_from_file(const char *path, const bdaddr_t *src,  		return -1;  	} +	servers = g_slist_append(servers, ns); +  	info("Registered server path:%s", path);  	return 0; | 
