server.c revision 9491a544f622e40453265c30f24ce44a61440cc1
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#include "dbus-service.h"
49
50static const char* HID_UUID = "00001124-0000-1000-8000-00805f9b34fb";
51
52static DBusConnection *connection = NULL;
53
54static void cancel_authorization(const char *addr)
55{
56	DBusMessage *msg;
57
58	msg = dbus_message_new_method_call("org.bluez", "/org/bluez",
59						"org.bluez.Database",
60						"CancelAuthorizationRequest");
61	if (!msg) {
62		error("Unable to allocate new method call");
63		return;
64	}
65
66	dbus_message_append_args(msg,
67			DBUS_TYPE_STRING, &addr,
68			DBUS_TYPE_STRING, &HID_UUID,
69			DBUS_TYPE_INVALID);
70
71	send_message_and_unref(connection, msg);
72}
73
74struct authorization_data {
75	bdaddr_t src;
76	bdaddr_t dst;
77};
78
79static void authorization_callback(DBusPendingCall *pcall, void *data)
80{
81	struct authorization_data *auth = data;
82	DBusMessage *reply = dbus_pending_call_steal_reply(pcall);
83	DBusError derr;
84
85	dbus_error_init(&derr);
86	if (dbus_set_error_from_message(&derr, reply) != TRUE) {
87		dbus_message_unref(reply);
88		input_device_connadd(&auth->src, &auth->dst);
89		return;
90	}
91
92	error("Authorization denied: %s", derr.message);
93	if (dbus_error_has_name(&derr, DBUS_ERROR_NO_REPLY)) {
94		char addr[18];
95		memset(addr, 0, sizeof(addr));
96		ba2str(&auth->dst, addr);
97		cancel_authorization(addr);
98	}
99
100	input_device_close_channels(&auth->src, &auth->dst);
101
102	dbus_error_free(&derr);
103	dbus_message_unref(reply);
104}
105
106static void auth_callback(DBusError *derr, void *user_data)
107{
108	struct authorization_data *auth = user_data;
109
110	if (derr) {
111		error("Access denied: %s", derr->message);
112		if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY))
113			service_cancel_auth(&auth->dst);
114
115		input_device_close_channels(&auth->src, &auth->dst);
116	} else
117		input_device_connadd(&auth->src, &auth->dst);
118
119	g_free(auth);
120}
121
122static int authorize_device(bdaddr_t *src, bdaddr_t *dst)
123{
124	struct authorization_data *auth;
125	DBusMessage *msg;
126	DBusPendingCall *pending;
127	char addr[18];
128	const char *paddr = addr;
129	int retval;
130
131	auth = g_new0(struct authorization_data, 1);
132	bacpy(&auth->src, src);
133	bacpy(&auth->dst, dst);
134
135	retval = service_req_auth(src, dst, HID_UUID,
136				auth_callback, auth);
137	if (retval < 0)
138		goto fallback;
139
140	return retval;
141
142fallback:
143	msg = dbus_message_new_method_call("org.bluez", "/org/bluez",
144				"org.bluez.Database", "RequestAuthorization");
145	if (!msg) {
146		error("Unable to allocate new RequestAuthorization method call");
147		return -ENOMEM;
148	}
149
150	memset(addr, 0, sizeof(addr));
151	ba2str(dst, addr);
152	dbus_message_append_args(msg,
153			DBUS_TYPE_STRING, &paddr,
154			DBUS_TYPE_STRING, &HID_UUID,
155			DBUS_TYPE_INVALID);
156
157	if (dbus_connection_send_with_reply(connection,
158				msg, &pending, -1) == FALSE)
159		return -EACCES;
160
161	dbus_pending_call_set_notify(pending, authorization_callback, auth, g_free);
162	dbus_pending_call_unref(pending);
163	dbus_message_unref(msg);
164
165	return 0;
166}
167
168static gboolean connect_event(GIOChannel *chan, GIOCondition cond, gpointer data)
169{
170	struct sockaddr_l2 addr;
171	socklen_t addrlen;
172	bdaddr_t src, dst;
173	unsigned char psm;
174	int sk, nsk;
175
176	sk = g_io_channel_unix_get_fd(chan);
177
178	memset(&addr, 0, sizeof(addr));
179	addrlen = sizeof(addr);
180
181	nsk = accept(sk, (struct sockaddr *) &addr, &addrlen);
182	if (nsk < 0)
183		return TRUE;
184
185	bacpy(&dst, &addr.l2_bdaddr);
186	psm = btohs(addr.l2_psm);
187
188	memset(&addr, 0, sizeof(addr));
189	addrlen = sizeof(addr);
190
191	if (getsockname(nsk, (struct sockaddr *) &addr, &addrlen) < 0) {
192		close(nsk);
193		return TRUE;
194	}
195
196	bacpy(&src, &addr.l2_bdaddr);
197
198	debug("Incoming connection on PSM %d", psm);
199
200	if (input_device_set_channel(&src, &dst, psm, nsk) < 0) {
201		/* Send unplug virtual cable to unknown devices */
202		if (psm == L2CAP_PSM_HIDP_CTRL) {
203			unsigned char unplug[] = { 0x15 };
204			int err;
205			err = write(nsk, unplug, sizeof(unplug));
206		}
207		close(nsk);
208		return TRUE;
209	}
210
211	if ((psm == L2CAP_PSM_HIDP_INTR) && (authorize_device(&src, &dst) < 0))
212		input_device_close_channels(&src, &dst);
213
214	return TRUE;
215}
216
217static GIOChannel *setup_l2cap(unsigned int psm)
218{
219	GIOChannel *io;
220	struct sockaddr_l2 addr;
221	int sk;
222
223	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
224	if (sk < 0)
225		return NULL;
226
227	memset(&addr, 0, sizeof(addr));
228	addr.l2_family = AF_BLUETOOTH;
229	bacpy(&addr.l2_bdaddr, BDADDR_ANY);
230	addr.l2_psm = htobs(psm);
231
232	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
233		close(sk);
234		return NULL;
235	}
236
237	if (listen(sk, 10) < 0) {
238		close(sk);
239		return NULL;
240	}
241
242	io = g_io_channel_unix_new(sk);
243	g_io_channel_set_close_on_unref(io, TRUE);
244
245	g_io_add_watch(io, G_IO_IN, connect_event, NULL);
246
247	return io;
248}
249
250static GIOChannel *ctrl_io = NULL;
251static GIOChannel *intr_io = NULL;
252
253int server_start(DBusConnection *conn)
254{
255	ctrl_io = setup_l2cap(L2CAP_PSM_HIDP_CTRL);
256	if (!ctrl_io) {
257		error("Failed to listen on control channel");
258		return -1;
259	}
260	g_io_channel_set_close_on_unref(ctrl_io, TRUE);
261
262	intr_io = setup_l2cap(L2CAP_PSM_HIDP_INTR);
263	if (!intr_io) {
264		error("Failed to listen on interrupt channel");
265		g_io_channel_unref(ctrl_io);
266		ctrl_io = NULL;
267	}
268	g_io_channel_set_close_on_unref(intr_io, TRUE);
269
270	connection = conn;
271
272	return 0;
273}
274
275void server_stop(void)
276{
277	if (intr_io)
278		g_io_channel_unref(intr_io);
279
280	if (ctrl_io)
281		g_io_channel_unref(ctrl_io);
282}
283