server.c revision dcada8eed787ddcca19634f5a71a4dd0a2dc1856
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#include <bluetooth/sdp.h>
40
41#include <glib.h>
42#include <dbus/dbus.h>
43#include <gdbus.h>
44
45#include "logging.h"
46
47#include "device.h"
48#include "server.h"
49#include "storage.h"
50#include "dbus-service.h"
51#include "glib-helper.h"
52
53static const char* HID_UUID = "00001124-0000-1000-8000-00805f9b34fb";
54
55static DBusConnection *connection = NULL;
56
57static void cancel_authorization(const char *addr)
58{
59	DBusMessage *msg;
60
61	msg = dbus_message_new_method_call("org.bluez", "/org/bluez",
62						"org.bluez.Database",
63						"CancelAuthorizationRequest");
64	if (!msg) {
65		error("Unable to allocate new method call");
66		return;
67	}
68
69	dbus_message_append_args(msg,
70			DBUS_TYPE_STRING, &addr,
71			DBUS_TYPE_STRING, &HID_UUID,
72			DBUS_TYPE_INVALID);
73
74	send_message_and_unref(connection, msg);
75}
76
77struct authorization_data {
78	bdaddr_t src;
79	bdaddr_t dst;
80};
81
82static void authorization_callback(DBusPendingCall *pcall, void *data)
83{
84	struct authorization_data *auth = data;
85	DBusMessage *reply = dbus_pending_call_steal_reply(pcall);
86	DBusError derr;
87
88	dbus_error_init(&derr);
89	if (dbus_set_error_from_message(&derr, reply) != TRUE) {
90		dbus_message_unref(reply);
91		input_device_connadd(&auth->src, &auth->dst);
92		return;
93	}
94
95	error("Authorization denied: %s", derr.message);
96	if (dbus_error_has_name(&derr, DBUS_ERROR_NO_REPLY)) {
97		char addr[18];
98		memset(addr, 0, sizeof(addr));
99		ba2str(&auth->dst, addr);
100		cancel_authorization(addr);
101	}
102
103	input_device_close_channels(&auth->src, &auth->dst);
104
105	dbus_error_free(&derr);
106	dbus_message_unref(reply);
107}
108
109static void auth_callback(DBusError *derr, void *user_data)
110{
111	struct authorization_data *auth = user_data;
112
113	if (derr) {
114		error("Access denied: %s", derr->message);
115		if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY))
116			service_cancel_auth(&auth->dst);
117
118		input_device_close_channels(&auth->src, &auth->dst);
119	} else
120		input_device_connadd(&auth->src, &auth->dst);
121
122	g_free(auth);
123}
124
125static int authorize_device(const bdaddr_t *src, const bdaddr_t *dst)
126{
127	struct authorization_data *auth;
128	DBusMessage *msg;
129	DBusPendingCall *pending;
130	char addr[18];
131	const char *paddr = addr;
132	int retval;
133
134	auth = g_new0(struct authorization_data, 1);
135	bacpy(&auth->src, src);
136	bacpy(&auth->dst, dst);
137
138	retval = service_req_auth(src, dst, HID_UUID,
139				auth_callback, auth);
140	if (retval < 0)
141		goto fallback;
142
143	return retval;
144
145fallback:
146	msg = dbus_message_new_method_call("org.bluez", "/org/bluez",
147				"org.bluez.Database", "RequestAuthorization");
148	if (!msg) {
149		error("Unable to allocate new RequestAuthorization method call");
150		return -ENOMEM;
151	}
152
153	memset(addr, 0, sizeof(addr));
154	ba2str(dst, addr);
155	dbus_message_append_args(msg,
156			DBUS_TYPE_STRING, &paddr,
157			DBUS_TYPE_STRING, &HID_UUID,
158			DBUS_TYPE_INVALID);
159
160	if (dbus_connection_send_with_reply(connection,
161				msg, &pending, -1) == FALSE)
162		return -EACCES;
163
164	dbus_pending_call_set_notify(pending, authorization_callback, auth, g_free);
165	dbus_pending_call_unref(pending);
166	dbus_message_unref(msg);
167
168	return 0;
169}
170
171static void connect_event_cb(GIOChannel *chan, int err, const bdaddr_t *src,
172				const bdaddr_t *dst, gpointer data)
173{
174	int sk, psm = GPOINTER_TO_UINT(data);
175
176	if (err < 0) {
177		error("accept: %s (%d)", strerror(-err), -err);
178		return;
179	}
180
181	sk = g_io_channel_unix_get_fd(chan);
182
183	debug("Incoming connection on PSM %d", psm);
184
185	if (input_device_set_channel(src, dst, psm, sk) < 0) {
186		/* Send unplug virtual cable to unknown devices */
187		if (psm == L2CAP_PSM_HIDP_CTRL) {
188			unsigned char unplug[] = { 0x15 };
189			int err;
190			err = write(sk, unplug, sizeof(unplug));
191		}
192		close(sk);
193		return;
194	}
195
196	if ((psm == L2CAP_PSM_HIDP_INTR) && (authorize_device(src, dst) < 0))
197		input_device_close_channels(src, dst);
198
199	return;
200}
201
202static GIOChannel *ctrl_io = NULL;
203static GIOChannel *intr_io = NULL;
204
205int server_start(DBusConnection *conn)
206{
207	ctrl_io = bt_l2cap_listen(BDADDR_ANY, L2CAP_PSM_HIDP_CTRL, 0, 0,
208				connect_event_cb,
209				GUINT_TO_POINTER(L2CAP_PSM_HIDP_CTRL));
210	if (!ctrl_io) {
211		error("Failed to listen on control channel");
212		return -1;
213	}
214	g_io_channel_set_close_on_unref(ctrl_io, TRUE);
215
216	intr_io = bt_l2cap_listen(BDADDR_ANY, L2CAP_PSM_HIDP_INTR, 0, 0,
217				connect_event_cb,
218				GUINT_TO_POINTER(L2CAP_PSM_HIDP_INTR));
219	if (!intr_io) {
220		error("Failed to listen on interrupt channel");
221		g_io_channel_unref(ctrl_io);
222		ctrl_io = NULL;
223	}
224	g_io_channel_set_close_on_unref(intr_io, TRUE);
225
226	connection = conn;
227
228	return 0;
229}
230
231void server_stop(void)
232{
233	if (intr_io)
234		g_io_channel_unref(intr_io);
235
236	if (ctrl_io)
237		g_io_channel_unref(ctrl_io);
238}
239