1/*
2 *
3 *  BlueZ - Bluetooth protocol stack for Linux
4 *
5 *  Copyright (C) 2011  ST-Ericsson SA
6 *
7 *  Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
8 *
9 *
10 *  This program is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License as published by
12 *  the Free Software Foundation; either version 2 of the License, or
13 *  (at your option) any later version.
14 *
15 *  This program is distributed in the hope that it will be useful,
16 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 *  GNU General Public License for more details.
19 *
20 *  You should have received a copy of the GNU General Public License
21 *  along with this program; if not, write to the Free Software
22 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23 *
24 */
25
26#ifdef HAVE_CONFIG_H
27#include <config.h>
28#endif
29
30#include <errno.h>
31#include <gdbus.h>
32
33#include <bluetooth/bluetooth.h>
34#include <bluetooth/hci.h>
35#include <bluetooth/sdp.h>
36
37#include "plugin.h"
38#include "log.h"
39#include "adapter.h"
40#include "device.h"
41#include "manager.h"
42#include "dbus-common.h"
43#include "event.h"
44#include "error.h"
45#include "oob.h"
46
47#define OOB_INTERFACE	"org.bluez.OutOfBand"
48
49struct oob_request {
50	struct btd_adapter *adapter;
51	DBusMessage *msg;
52};
53
54static GSList *oob_requests = NULL;
55static DBusConnection *connection = NULL;
56
57static gint oob_request_cmp(gconstpointer a, gconstpointer b)
58{
59	const struct oob_request *data = a;
60	const struct btd_adapter *adapter = b;
61
62	return data->adapter != adapter;
63}
64
65static struct oob_request *find_oob_request(struct btd_adapter *adapter)
66{
67	GSList *match;
68
69	match = g_slist_find_custom(oob_requests, adapter, oob_request_cmp);
70
71	if (match)
72		return match->data;
73
74	return NULL;
75}
76
77static void read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
78				uint8_t *randomizer)
79{
80	struct DBusMessage *reply;
81	struct oob_request *oob_request;
82
83	oob_request = find_oob_request(adapter);
84	if (!oob_request)
85		return;
86
87	if (hash && randomizer)
88		reply = g_dbus_create_reply(oob_request->msg,
89			DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash, 16,
90			DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer, 16,
91			DBUS_TYPE_INVALID);
92	else
93		reply = btd_error_failed(oob_request->msg,
94					"Failed to read local OOB data.");
95
96	oob_requests = g_slist_remove(oob_requests, oob_request);
97	dbus_message_unref(oob_request->msg);
98	g_free(oob_request);
99
100	if (!reply) {
101		error("Couldn't allocate D-Bus message");
102		return;
103	}
104
105	if (!g_dbus_send_message(connection, reply))
106		error("D-Bus send failed");
107}
108
109static DBusMessage *read_local_data(DBusConnection *conn, DBusMessage *msg,
110								void *data)
111{
112	struct btd_adapter *adapter = data;
113	struct oob_request *oob_request;
114
115	if (find_oob_request(adapter))
116		return btd_error_in_progress(msg);
117
118	if (btd_adapter_read_local_oob_data(adapter))
119		return btd_error_failed(msg, "Request failed.");
120
121	oob_request = g_new(struct oob_request, 1);
122	oob_request->adapter = adapter;
123	oob_requests = g_slist_append(oob_requests, oob_request);
124	oob_request->msg = dbus_message_ref(msg);
125
126	return NULL;
127}
128
129static DBusMessage *add_remote_data(DBusConnection *conn, DBusMessage *msg,
130								void *data)
131{
132	struct btd_adapter *adapter = data;
133	uint8_t *hash, *randomizer;
134	int32_t hlen, rlen;
135	const char *addr;
136	bdaddr_t bdaddr;
137
138	if (!dbus_message_get_args(msg, NULL,
139			DBUS_TYPE_STRING, &addr,
140			DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash, &hlen,
141			DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer, &rlen,
142			DBUS_TYPE_INVALID))
143		return btd_error_invalid_args(msg);
144
145	if (hlen != 16 || rlen != 16 || bachk(addr))
146		return btd_error_invalid_args(msg);
147
148	str2ba(addr, &bdaddr);
149
150	if (btd_adapter_add_remote_oob_data(adapter, &bdaddr, hash, randomizer))
151		return btd_error_failed(msg, "Request failed");
152
153	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
154}
155
156static DBusMessage *remove_remote_data(DBusConnection *conn, DBusMessage *msg,
157								void *data)
158{
159	struct btd_adapter *adapter = data;
160	const char *addr;
161	bdaddr_t bdaddr;
162
163	if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr,
164			DBUS_TYPE_INVALID))
165		return btd_error_invalid_args(msg);
166
167	if (bachk(addr))
168		return btd_error_invalid_args(msg);
169
170	str2ba(addr, &bdaddr);
171
172	if (btd_adapter_remove_remote_oob_data(adapter, &bdaddr))
173		return btd_error_failed(msg, "Request failed");
174
175	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
176}
177
178static GDBusMethodTable oob_methods[] = {
179	{"AddRemoteData",	"sayay",	"",	add_remote_data},
180	{"RemoveRemoteData",	"s",		"",	remove_remote_data},
181	{"ReadLocalData",	"",		"ayay",	read_local_data,
182						G_DBUS_METHOD_FLAG_ASYNC},
183	{}
184};
185
186static int oob_probe(struct btd_adapter *adapter)
187{
188	const char *path = adapter_get_path(adapter);
189
190	if (!g_dbus_register_interface(connection, path, OOB_INTERFACE,
191				oob_methods, NULL, NULL, adapter, NULL)) {
192			error("OOB interface init failed on path %s", path);
193			return -EIO;
194		}
195
196	return 0;
197}
198
199static void oob_remove(struct btd_adapter *adapter)
200{
201	read_local_data_complete(adapter, NULL, NULL);
202
203	g_dbus_unregister_interface(connection, adapter_get_path(adapter),
204							OOB_INTERFACE);
205}
206
207static struct btd_adapter_driver oob_driver = {
208	.name	= "oob",
209	.probe	= oob_probe,
210	.remove	= oob_remove,
211};
212
213static int dbusoob_init(void)
214{
215	DBG("Setup dbusoob plugin");
216
217	connection = get_dbus_connection();
218
219	oob_register_cb(read_local_data_complete);
220
221	return btd_register_adapter_driver(&oob_driver);
222}
223
224static void dbusoob_exit(void)
225{
226	DBG("Cleanup dbusoob plugin");
227
228	manager_foreach_adapter((adapter_cb) oob_remove, NULL);
229
230	btd_unregister_adapter_driver(&oob_driver);
231}
232
233BLUETOOTH_PLUGIN_DEFINE(dbusoob, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
234						dbusoob_init, dbusoob_exit)
235