neard: Add neard plugin with tethering info sharing support
[platform/upstream/connman.git] / plugins / neard.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2013  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <stdio.h>
28 #include <stdint.h>
29 #include <string.h>
30 #include <arpa/inet.h>
31
32 #include <glib.h>
33
34 #define CONNMAN_API_SUBJECT_TO_CHANGE
35 #include <connman/plugin.h>
36 #include <connman/dbus.h>
37 #include <connman/technology.h>
38
39 #include <gdbus.h>
40
41 #define NEARD_SERVICE "org.neard"
42 #define NEARD_PATH "/"
43 #define NEARD_MANAGER_INTERFACE "org.neard.Manager"
44 #define NEARD_AGENT_INTERFACE "org.neard.HandoverAgent"
45 #define NEARD_ERROR_INTERFACE "org.neard.HandoverAgent.Error"
46
47 #define AGENT_PATH "/net/connman/neard_handover_agent"
48 #define AGENT_TYPE "wifi"
49
50 struct data_elements {
51         unsigned int value;
52         unsigned int length;
53         gboolean fixed_length;
54 };
55
56 typedef enum {
57         DE_AUTHENTICATION_TYPE = 0,
58         DE_NETWORK_KEY         = 1,
59         DE_SSID                = 2,
60         DE_MAX                 = 3,
61 } DEid;
62
63 static const struct data_elements  DEs[DE_MAX] = {
64         { 0x1003, 2,  TRUE  },
65         { 0x1027, 64, FALSE },
66         { 0x1045, 32, FALSE },
67 };
68
69 #define DE_VAL_OPEN                 0x0001
70 #define DE_VAL_PSK                  0x0022
71
72
73 static DBusConnection *connection = NULL;
74 DBusPendingCall *register_call = NULL;
75 gboolean agent_registered = FALSE;
76 static guint watch_id = 0;
77
78 static int set_2b_into_tlv(uint8_t *tlv_msg, uint16_t val)
79 {
80         uint16_t ne_val;
81         uint8_t *ins;
82
83         ins = (uint8_t *) &ne_val;
84
85         ne_val = htons(val);
86         tlv_msg[0] = ins[0];
87         tlv_msg[1] = ins[1];
88
89         return 2;
90 }
91
92 static int set_byte_array_into_tlv(uint8_t *tlv_msg,
93                                         uint8_t *array, int length)
94 {
95         memcpy((void *)tlv_msg, (void *)array, length);
96         return length;
97 }
98
99 static uint8_t *encode_to_tlv(const char *ssid, const char *psk, int *length)
100 {
101         uint16_t ssid_len, psk_len;
102         uint8_t *tlv_msg;
103         int pos = 0;
104
105         if (ssid == NULL || length == NULL)
106                 return NULL;
107
108         ssid_len = strlen(ssid);
109
110         *length = 6 + 4 + ssid_len;
111         if (psk != NULL) {
112                 psk_len = strlen(psk);
113                 *length += 4 + psk_len;
114         } else
115                 psk_len = 0;
116
117         tlv_msg = g_try_malloc0(sizeof(uint8_t) * (*length));
118         if (tlv_msg == NULL)
119                 return NULL;
120
121         pos += set_2b_into_tlv(tlv_msg+pos, DEs[DE_SSID].value);
122         pos += set_2b_into_tlv(tlv_msg+pos, ssid_len);
123         pos += set_byte_array_into_tlv(tlv_msg+pos, (uint8_t *)ssid, ssid_len);
124
125         pos += set_2b_into_tlv(tlv_msg+pos, DEs[DE_AUTHENTICATION_TYPE].value);
126         pos += set_2b_into_tlv(tlv_msg+pos,
127                                         DEs[DE_AUTHENTICATION_TYPE].length);
128         if (psk != NULL) {
129                 pos += set_2b_into_tlv(tlv_msg+pos, DE_VAL_PSK);
130                 pos += set_2b_into_tlv(tlv_msg+pos, DEs[DE_NETWORK_KEY].value);
131                 pos += set_2b_into_tlv(tlv_msg+pos, psk_len);
132                 pos += set_byte_array_into_tlv(tlv_msg+pos,
133                                                 (uint8_t *)psk, psk_len);
134         } else
135                 pos += set_2b_into_tlv(tlv_msg+pos, DE_VAL_OPEN);
136
137         return tlv_msg;
138 }
139
140 static int parse_request_oob_params(DBusMessage *message)
141 {
142         DBusMessageIter iter;
143         DBusMessageIter array;
144
145         dbus_message_iter_init(message, &iter);
146
147         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
148                 return -EINVAL;
149
150         dbus_message_iter_recurse(&iter, &array);
151         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_DICT_ENTRY)
152                 return -EINVAL;
153
154         return 0;
155 }
156
157 static DBusMessage *create_request_oob_reply(DBusMessage *message)
158 {
159         DBusMessage *reply;
160         DBusMessageIter iter;
161         DBusMessageIter dict;
162         const char *ssid, *psk;
163         uint8_t *tlv_msg;
164         int length;
165
166         if (connman_technology_get_wifi_tethering(&ssid, &psk) == FALSE)
167                 return g_dbus_create_error(message,
168                                         NEARD_ERROR_INTERFACE ".NotSupported",
169                                         "Operation is not supported");
170
171         tlv_msg = encode_to_tlv(ssid, psk, &length);
172         if (tlv_msg == NULL)
173                 return g_dbus_create_error(message,
174                                         NEARD_ERROR_INTERFACE ".NotSupported",
175                                         "Operation is not supported");
176
177         reply = dbus_message_new_method_return(message);
178         if (reply == NULL)
179                 goto out;
180
181         dbus_message_iter_init_append(reply, &iter);
182
183         connman_dbus_dict_open(&iter, &dict);
184
185         connman_dbus_dict_append_fixed_array(&dict, "WSC",
186                                         DBUS_TYPE_BYTE, &tlv_msg, length);
187
188         dbus_message_iter_close_container(&iter, &dict);
189
190 out:
191         g_free(tlv_msg);
192
193         return reply;
194 }
195
196 static DBusMessage *request_oob_method(DBusConnection *dbus_conn,
197                                         DBusMessage *message, void *user_data)
198 {
199         DBG("");
200
201         if (parse_request_oob_params(message) != 0)
202                 return g_dbus_create_error(message,
203                                         NEARD_ERROR_INTERFACE ".Failed",
204                                         "Invalid parameters");
205
206         return create_request_oob_reply(message);
207 }
208
209 static DBusMessage *push_oob_method(DBusConnection *dbus_conn,
210                                         DBusMessage *message, void *user_data)
211 {
212         DBG("");
213
214         return g_dbus_create_error(message,
215                                 NEARD_ERROR_INTERFACE ".NotSupported",
216                                 "Operation is not supported");
217 }
218
219 static DBusMessage *release_method(DBusConnection *dbus_conn,
220                                         DBusMessage *message, void *user_data)
221 {
222         DBG("");
223
224         agent_registered = FALSE;
225         g_dbus_unregister_interface(connection,
226                                         AGENT_PATH, NEARD_AGENT_INTERFACE);
227
228         return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
229 }
230
231 static const GDBusMethodTable neard_methods[] = {
232 { GDBUS_ASYNC_METHOD("RequestOOB",
233                 GDBUS_ARGS({ "data", "a{sv}" }),
234                 GDBUS_ARGS({ "data", "a{sv}" }), request_oob_method) },
235         { GDBUS_ASYNC_METHOD("PushOOB",
236                 GDBUS_ARGS({ "data", "a{sv}"}), NULL, push_oob_method) },
237         { GDBUS_METHOD("Release", NULL, NULL, release_method) },
238         { },
239 };
240
241 static void cleanup_register_call(void)
242 {
243         if (register_call != NULL) {
244                 dbus_pending_call_cancel(register_call);
245                 dbus_pending_call_unref(register_call);
246                 register_call = NULL;
247         }
248 }
249
250 static void register_agent_cb(DBusPendingCall *pending, void *user_data)
251 {
252         DBusMessage *reply;
253
254         if (dbus_pending_call_get_completed(pending) == FALSE)
255                 return;
256
257         register_call = NULL;
258
259         reply = dbus_pending_call_steal_reply(pending);
260         if (reply == NULL)
261                 goto out;
262
263         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
264                 g_dbus_unregister_interface(connection,
265                                         AGENT_PATH, NEARD_AGENT_INTERFACE);
266         } else
267                 agent_registered = TRUE;
268
269         dbus_message_unref(reply);
270 out:
271         dbus_pending_call_unref(pending);
272 }
273
274 static void register_agent(void)
275 {
276         const char *path = AGENT_PATH;
277         const char *type = AGENT_TYPE;
278         DBusMessage *message;
279
280         message = dbus_message_new_method_call(NEARD_SERVICE, NEARD_PATH,
281                                                 NEARD_MANAGER_INTERFACE,
282                                                 "RegisterHandoverAgent");
283         if (message == NULL)
284                 return;
285
286         dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH,
287                         &path, DBUS_TYPE_STRING, &type, DBUS_TYPE_INVALID);
288
289         if (dbus_connection_send_with_reply(connection, message,
290                         &register_call, DBUS_TIMEOUT_USE_DEFAULT) == FALSE) {
291                 dbus_message_unref(message);
292                 goto out;
293         }
294
295         if (dbus_pending_call_set_notify(register_call, register_agent_cb,
296                                                         NULL, NULL) == FALSE)
297                 cleanup_register_call();
298
299 out:
300         dbus_message_unref(message);
301 }
302
303 static void unregister_agent(void)
304 {
305         const char *path = AGENT_PATH;
306         const char *type = AGENT_TYPE;
307         DBusMessage *message;
308
309         if (agent_registered == FALSE)
310                 return cleanup_register_call();
311
312         agent_registered = FALSE;
313
314         message = dbus_message_new_method_call(NEARD_SERVICE, NEARD_PATH,
315                                                 NEARD_MANAGER_INTERFACE,
316                                                 "UnregisterHandoverAgent");
317         if (message != NULL) {
318                 dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH,
319                         &path, DBUS_TYPE_STRING, &type, DBUS_TYPE_INVALID);
320                 g_dbus_send_message(connection, message);
321         }
322
323         g_dbus_unregister_interface(connection,
324                                         AGENT_PATH, NEARD_AGENT_INTERFACE);
325 }
326
327 static void neard_is_present(DBusConnection *conn, void *user_data)
328 {
329         DBG("");
330
331         if (agent_registered == TRUE)
332                 return;
333
334         if (g_dbus_register_interface(connection, AGENT_PATH,
335                                         NEARD_AGENT_INTERFACE, neard_methods,
336                                         NULL, NULL, NULL, NULL) == TRUE)
337                 register_agent();
338 }
339
340 static void neard_is_out(DBusConnection *conn, void *user_data)
341 {
342         DBG("");
343
344         if (agent_registered == TRUE) {
345                 g_dbus_unregister_interface(connection,
346                                         AGENT_PATH, NEARD_AGENT_INTERFACE);
347                 agent_registered = FALSE;
348         }
349
350         cleanup_register_call();
351 }
352
353 static int neard_init(void)
354 {
355         connection = connman_dbus_get_connection();
356         if (connection == NULL)
357                 return -EIO;
358
359         watch_id = g_dbus_add_service_watch(connection, NEARD_SERVICE,
360                                                 neard_is_present, neard_is_out,
361                                                 NULL, NULL);
362         if (watch_id == 0) {
363                 dbus_connection_unref(connection);
364                 return -ENOMEM;
365         }
366
367         return 0;
368 }
369
370 static void neard_exit(void)
371 {
372         unregister_agent();
373
374         if (watch_id != 0)
375                 g_dbus_remove_watch(connection, watch_id);
376         if (connection != NULL)
377                 dbus_connection_unref(connection);
378 }
379
380 CONNMAN_PLUGIN_DEFINE(neard, "Neard handover plugin", VERSION,
381                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, neard_init, neard_exit)