server.c revision 098cd10838193272c669348c5ffd0410218e1bcc
1/*
2 *
3 *  BlueZ - Bluetooth protocol stack for Linux
4 *
5 *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org>
6 *
7 *
8 *  This program is free software; you can redistribute it and/or modify
9 *  it under the terms of the GNU General Public License as published by
10 *  the Free Software Foundation; either version 2 of the License, or
11 *  (at your option) any later version.
12 *
13 *  This program is distributed in the hope that it will be useful,
14 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *  GNU General Public License for more details.
17 *
18 *  You should have received a copy of the GNU General Public License
19 *  along with this program; if not, write to the Free Software
20 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 *
22 */
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <errno.h>
29#include <unistd.h>
30#include <stdlib.h>
31#include <sys/ioctl.h>
32#include <sys/socket.h>
33
34#include <bluetooth/bluetooth.h>
35#include <bluetooth/l2cap.h>
36#include <bluetooth/hidp.h>
37#include <bluetooth/hci.h>
38#include <bluetooth/hci_lib.h>
39
40#include <glib.h>
41
42#include "logging.h"
43#include "dbus.h"
44
45#include "device.h"
46#include "server.h"
47#include "storage.h"
48
49#include "plugin.h"
50
51static const char* HID_UUID = "00001124-0000-1000-8000-00805f9b34fb";
52
53static DBusConnection *connection = NULL;
54
55static void cancel_authorization(const char *addr)
56{
57	DBusMessage *msg;
58
59	msg = dbus_message_new_method_call("org.bluez", "/org/bluez",
60						"org.bluez.Database",
61						"CancelAuthorizationRequest");
62	if (!msg) {
63		error("Unable to allocate new method call");
64		return;
65	}
66
67	dbus_message_append_args(msg,
68			DBUS_TYPE_STRING, &addr,
69			DBUS_TYPE_STRING, &HID_UUID,
70			DBUS_TYPE_INVALID);
71
72	send_message_and_unref(connection, msg);
73}
74
75struct authorization_data {
76	bdaddr_t src;
77	bdaddr_t dst;
78};
79
80static void authorization_callback(DBusPendingCall *pcall, void *data)
81{
82	struct authorization_data *auth = data;
83	DBusMessage *reply = dbus_pending_call_steal_reply(pcall);
84	DBusError derr;
85
86	dbus_error_init(&derr);
87	if (dbus_set_error_from_message(&derr, reply) != TRUE) {
88		dbus_message_unref(reply);
89		input_device_connadd(&auth->src, &auth->dst);
90		return;
91	}
92
93	error("Authorization denied: %s", derr.message);
94	if (dbus_error_has_name(&derr, DBUS_ERROR_NO_REPLY)) {
95		char addr[18];
96		memset(addr, 0, sizeof(addr));
97		ba2str(&auth->dst, addr);
98		cancel_authorization(addr);
99	}
100
101	input_device_close_channels(&auth->src, &auth->dst);
102
103	dbus_error_free(&derr);
104	dbus_message_unref(reply);
105}
106
107static void auth_callback(DBusError *derr, void *user_data)
108{
109	struct authorization_data *auth = user_data;
110
111	if (derr) {
112		error("Access denied: %s", derr->message);
113		if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY))
114			plugin_cancel_auth(&auth->dst);
115
116		input_device_close_channels(&auth->src, &auth->dst);
117	} else
118		input_device_connadd(&auth->src, &auth->dst);
119
120	g_free(auth);
121}
122
123static int authorize_device(bdaddr_t *src, bdaddr_t *dst)
124{
125	struct authorization_data *auth;
126	DBusMessage *msg;
127	DBusPendingCall *pending;
128	char addr[18];
129	const char *paddr = addr;
130	int retval;
131
132	auth = g_new0(struct authorization_data, 1);
133	bacpy(&auth->src, src);
134	bacpy(&auth->dst, dst);
135
136	retval = plugin_req_auth(src, dst, HID_UUID,
137				auth_callback, auth);
138	if (retval < 0)
139		goto fallback;
140
141	return retval;
142
143fallback:
144	msg = dbus_message_new_method_call("org.bluez", "/org/bluez",
145				"org.bluez.Database", "RequestAuthorization");
146	if (!msg) {
147		error("Unable to allocate new RequestAuthorization method call");
148		return -ENOMEM;
149	}
150
151	memset(addr, 0, sizeof(addr));
152	ba2str(dst, addr);
153	dbus_message_append_args(msg,
154			DBUS_TYPE_STRING, &paddr,
155			DBUS_TYPE_STRING, &HID_UUID,
156			DBUS_TYPE_INVALID);
157
158	if (dbus_connection_send_with_reply(connection,
159				msg, &pending, -1) == FALSE)
160		return -EACCES;
161
162	dbus_pending_call_set_notify(pending, authorization_callback, auth, g_free);
163	dbus_pending_call_unref(pending);
164	dbus_message_unref(msg);
165
166	return 0;
167}
168
169static gboolean connect_event(GIOChannel *chan, GIOCondition cond, gpointer data)
170{
171	struct sockaddr_l2 addr;
172	socklen_t addrlen;
173	bdaddr_t src, dst;
174	unsigned char psm;
175	int sk, nsk;
176
177	sk = g_io_channel_unix_get_fd(chan);
178
179	memset(&addr, 0, sizeof(addr));
180	addrlen = sizeof(addr);
181
182	nsk = accept(sk, (struct sockaddr *) &addr, &addrlen);
183	if (nsk < 0)
184		return TRUE;
185
186	bacpy(&dst, &addr.l2_bdaddr);
187	psm = btohs(addr.l2_psm);
188
189	memset(&addr, 0, sizeof(addr));
190	addrlen = sizeof(addr);
191
192	if (getsockname(nsk, (struct sockaddr *) &addr, &addrlen) < 0) {
193		close(nsk);
194		return TRUE;
195	}
196
197	bacpy(&src, &addr.l2_bdaddr);
198
199	debug("Incoming connection on PSM %d", psm);
200
201	if (input_device_set_channel(&src, &dst, psm, nsk) < 0) {
202		/* Send unplug virtual cable to unknown devices */
203		if (psm == L2CAP_PSM_HIDP_CTRL) {
204			unsigned char unplug[] = { 0x15 };
205			int err;
206			err = write(nsk, unplug, sizeof(unplug));
207		}
208		close(nsk);
209		return TRUE;
210	}
211
212	if ((psm == L2CAP_PSM_HIDP_INTR) && (authorize_device(&src, &dst) < 0))
213		input_device_close_channels(&src, &dst);
214
215	return TRUE;
216}
217
218static GIOChannel *setup_l2cap(unsigned int psm)
219{
220	GIOChannel *io;
221	struct sockaddr_l2 addr;
222	int sk;
223
224	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
225	if (sk < 0)
226		return NULL;
227
228	memset(&addr, 0, sizeof(addr));
229	addr.l2_family = AF_BLUETOOTH;
230	bacpy(&addr.l2_bdaddr, BDADDR_ANY);
231	addr.l2_psm = htobs(psm);
232
233	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
234		close(sk);
235		return NULL;
236	}
237
238	if (listen(sk, 10) < 0) {
239		close(sk);
240		return NULL;
241	}
242
243	io = g_io_channel_unix_new(sk);
244	g_io_channel_set_close_on_unref(io, TRUE);
245
246	g_io_add_watch(io, G_IO_IN, connect_event, NULL);
247
248	return io;
249}
250
251static GIOChannel *ctrl_io = NULL;
252static GIOChannel *intr_io = NULL;
253
254int server_start(DBusConnection *conn)
255{
256	ctrl_io = setup_l2cap(L2CAP_PSM_HIDP_CTRL);
257	if (!ctrl_io) {
258		error("Failed to listen on control channel");
259		return -1;
260	}
261	g_io_channel_set_close_on_unref(ctrl_io, TRUE);
262
263	intr_io = setup_l2cap(L2CAP_PSM_HIDP_INTR);
264	if (!intr_io) {
265		error("Failed to listen on interrupt channel");
266		g_io_channel_unref(ctrl_io);
267		ctrl_io = NULL;
268	}
269	g_io_channel_set_close_on_unref(intr_io, TRUE);
270
271	connection = conn;
272
273	return 0;
274}
275
276void server_stop(void)
277{
278	if (intr_io)
279		g_io_channel_unref(intr_io);
280
281	if (ctrl_io)
282		g_io_channel_unref(ctrl_io);
283}
284