plugin: Add VPN plugin that will interact with vpnd
[platform/upstream/connman.git] / plugins / vpn.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2012  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 <string.h>
27 #include <errno.h>
28 #include <sys/socket.h>
29 #include <stdlib.h>
30 #include <glib.h>
31
32 #include <gdbus.h>
33
34 #define CONNMAN_API_SUBJECT_TO_CHANGE
35 #include <connman/technology.h>
36 #include <connman/plugin.h>
37 #include <connman/log.h>
38 #include <connman/dbus.h>
39 #include <connman/provider.h>
40 #include <connman/ipaddress.h>
41 #include <connman/vpn-dbus.h>
42
43 #define DBUS_TIMEOUT 10000
44
45 static DBusConnection *connection;
46
47 static GHashTable *vpn_connections = NULL;
48 static gboolean starting_vpnd = TRUE;
49 static guint watch;
50 static guint added_watch;
51 static guint removed_watch;
52 static guint property_watch;
53
54 struct connection_data {
55         char *path;
56         struct connman_provider *provider;
57         int index;
58         DBusPendingCall *call;
59
60         char *state;
61         char *type;
62         char *name;
63         char *host;
64         char *domain;
65         char **nameservers;
66
67         GHashTable *setting_strings;
68
69         struct connman_ipaddress *ip;
70 };
71
72 static int set_string(struct connman_provider *provider,
73                                         const char *key, const char *value)
74 {
75         struct connection_data *data;
76
77         data = connman_provider_get_data(provider);
78         if (data == NULL)
79                 return -EINVAL;
80
81         DBG("data %p provider %p key %s value %s", data, provider, key, value);
82
83         if (g_str_equal(key, "Type") == TRUE) {
84                 g_free(data->type);
85                 data->type = g_strdup(value);
86         } else if (g_str_equal(key, "Name") == TRUE) {
87                 g_free(data->name);
88                 data->name = g_strdup(value);
89         } else if (g_str_equal(key, "Host") == TRUE) {
90                 g_free(data->host);
91                 data->host = g_strdup(value);
92         } else if (g_str_equal(key, "VPN.Domain") == TRUE ||
93                                 g_str_equal(key, "Domain") == TRUE) {
94                 g_free(data->domain);
95                 data->domain = g_strdup(value);
96         } else
97                 g_hash_table_replace(data->setting_strings,
98                                 g_strdup(key), g_strdup(value));
99         return 0;
100 }
101
102 static const char *get_string(struct connman_provider *provider,
103                                                         const char *key)
104 {
105         struct connection_data *data;
106
107         data = connman_provider_get_data(provider);
108         if (data == NULL)
109                 return NULL;
110
111         DBG("data %p provider %p key %s", data, provider, key);
112
113         if (g_str_equal(key, "Type") == TRUE)
114                 return data->type;
115         else if (g_str_equal(key, "Name") == TRUE)
116                 return data->name;
117         else if (g_str_equal(key, "Host") == TRUE)
118                 return data->host;
119         else if (g_str_equal(key, "VPN.Domain") == TRUE)
120                 return data->domain;
121
122         return g_hash_table_lookup(data->setting_strings, key);
123 }
124
125 static char *get_ident(const char *path)
126 {
127         char *pos;
128
129         if (*path != '/')
130                 return NULL;
131
132         pos = strrchr(path, '/');
133         if (pos == NULL)
134                 return NULL;
135
136         return pos + 1;
137 }
138
139 static void set_provider_state(struct connection_data *data)
140 {
141         if (g_str_equal(data->state, "ready") == TRUE)
142                 connman_provider_set_state(data->provider,
143                                         CONNMAN_PROVIDER_STATE_READY);
144         else if (g_str_equal(data->state, "configuration") == TRUE)
145                 connman_provider_set_state(data->provider,
146                                         CONNMAN_PROVIDER_STATE_CONNECT);
147         else if (g_str_equal(data->state, "idle") == TRUE)
148                 connman_provider_set_state(data->provider,
149                                         CONNMAN_PROVIDER_STATE_IDLE);
150         else if (g_str_equal(data->state, "disconnect") == TRUE)
151                 connman_provider_set_state(data->provider,
152                                         CONNMAN_PROVIDER_STATE_DISCONNECT);
153         else if (g_str_equal(data->state, "failure") == TRUE)
154                 connman_provider_set_state(data->provider,
155                                         CONNMAN_PROVIDER_STATE_FAILURE);
156         else
157                 connman_provider_set_state(data->provider,
158                                         CONNMAN_PROVIDER_STATE_UNKNOWN);
159 }
160
161 static int create_provider(struct connection_data *data, void *user_data)
162 {
163         struct connman_provider_driver *driver = user_data;
164         char *ident;
165         int err = 0;
166
167         DBG("%s", data->path);
168
169         ident = g_strdup(get_ident(data->path));
170
171         data->provider = connman_provider_get(ident);
172         if (data->provider == NULL) {
173                 err = -ENOMEM;
174                 goto out;
175         }
176
177         DBG("provider %p name %s", data->provider, data->name);
178
179         connman_provider_set_data(data->provider, data);
180         connman_provider_set_driver(data->provider, driver);
181
182         err = connman_provider_create_service(data->provider);
183         if (err == 0) {
184                 if (g_str_equal(data->state, "ready") == TRUE) {
185                         connman_provider_set_index(data->provider,
186                                                         data->index);
187                         if (data->ip != NULL)
188                                 connman_provider_set_ipaddress(data->provider,
189                                                                 data->ip);
190                 }
191
192                 set_provider_state(data);
193         }
194
195 out:
196         g_free(ident);
197         return err;
198 }
199
200 static struct connection_data *create_connection_data(const char *path)
201 {
202         struct connection_data *data;
203
204         data = g_try_new0(struct connection_data, 1);
205         if (data == NULL)
206                 return NULL;
207
208         DBG("path %s", path);
209
210         data->path = g_strdup(path);
211         data->index = -1;
212
213         data->setting_strings = g_hash_table_new_full(g_str_hash,
214                                                 g_str_equal, g_free, g_free);
215
216         return data;
217 }
218
219 static int extract_ip(DBusMessageIter *array, int family,
220                                                 struct connection_data *data)
221 {
222         DBusMessageIter dict;
223         char *address = NULL, *gateway = NULL, *netmask = NULL, *peer = NULL;
224         unsigned char prefix_len;
225
226         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
227                 return -EINVAL;
228
229         dbus_message_iter_recurse(array, &dict);
230
231         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
232                 DBusMessageIter entry, value;
233                 const char *key;
234
235                 dbus_message_iter_recurse(&dict, &entry);
236                 dbus_message_iter_get_basic(&entry, &key);
237
238                 dbus_message_iter_next(&entry);
239                 dbus_message_iter_recurse(&entry, &value);
240
241                 if (g_str_equal(key, "Address") == TRUE) {
242                         dbus_message_iter_get_basic(&value, &address);
243                         DBG("address %s", address);
244                 } else if (g_str_equal(key, "Netmask") == TRUE) {
245                         dbus_message_iter_get_basic(&value, &netmask);
246                         DBG("netmask %s", netmask);
247                 } else if (g_str_equal(key, "PrefixLength") == TRUE) {
248                         dbus_message_iter_get_basic(&value, &netmask);
249                         DBG("prefix length %s", netmask);
250                 } else if (g_str_equal(key, "Peer") == TRUE) {
251                         dbus_message_iter_get_basic(&value, &peer);
252                         DBG("peer %s", peer);
253                 } else if (g_str_equal(key, "Gateway") == TRUE) {
254                         dbus_message_iter_get_basic(&value, &gateway);
255                         DBG("gateway %s", gateway);
256                 }
257
258                 dbus_message_iter_next(&dict);
259         }
260
261         data->ip = connman_ipaddress_alloc(family);
262         if (data->ip == NULL)
263                 return -ENOMEM;
264
265         switch (family) {
266         case AF_INET:
267                 connman_ipaddress_set_ipv4(data->ip, address, netmask,
268                                                                 gateway);
269                 break;
270         case AF_INET6:
271                 prefix_len = atoi(netmask);
272                 connman_ipaddress_set_ipv6(data->ip, address, prefix_len,
273                                                                 gateway);
274                 break;
275         default:
276                 return -EINVAL;
277         }
278
279         connman_ipaddress_set_peer(data->ip, peer);
280
281         return 0;
282 }
283
284 static int extract_nameservers(DBusMessageIter *array,
285                                                 struct connection_data *data)
286 {
287         DBusMessageIter entry;
288         char **nameservers = NULL;
289         int i = 0;
290
291         dbus_message_iter_recurse(array, &entry);
292
293         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
294                 const char *nameserver;
295
296                 dbus_message_iter_get_basic(&entry, &nameserver);
297
298                 nameservers = g_try_renew(char *, nameservers, i + 2);
299                 if (nameservers == NULL)
300                         return -ENOMEM;
301
302                 DBG("[%d] %s", i, nameserver);
303
304                 nameservers[i] = g_strdup(nameserver);
305                 if (nameservers[i] == NULL)
306                         return -ENOMEM;
307
308                 nameservers[++i] = NULL;
309
310                 dbus_message_iter_next(&entry);
311         }
312
313         g_strfreev(data->nameservers);
314         data->nameservers = nameservers;
315
316         return 0;
317 }
318
319 static void connect_reply(DBusPendingCall *call, void *user_data)
320 {
321         DBusMessage *reply;
322         DBusError error;
323
324         if (dbus_pending_call_get_completed(call) == FALSE)
325                 return;
326
327         DBG("user_data %p", user_data);
328
329         reply = dbus_pending_call_steal_reply(call);
330
331         dbus_error_init(&error);
332
333         if (dbus_set_error_from_message(&error, reply) == TRUE) {
334                 if (dbus_error_has_name(&error, CONNMAN_ERROR_INTERFACE
335                                                 ".InProgress") == FALSE) {
336                         connman_error("Connect reply: %s (%s)", error.message,
337                                                                 error.name);
338                         dbus_error_free(&error);
339                         goto done;
340                 }
341                 dbus_error_free(&error);
342         }
343
344         /*
345          * The vpn connection is up when we get a "ready" state
346          * property so at this point we do nothing for the provider
347          * state.
348          */
349
350 done:
351         dbus_message_unref(reply);
352
353         dbus_pending_call_unref(call);
354 }
355
356 static int connect_provider(struct connection_data *data, void *user_data)
357 {
358         DBusPendingCall *call;
359         DBusMessage *message;
360
361         DBG("data %p", data);
362
363         message = dbus_message_new_method_call(VPN_SERVICE, data->path,
364                                         VPN_CONNECTION_INTERFACE,
365                                         VPN_CONNECT);
366         if (message == NULL)
367                 return -ENOMEM;
368
369         if (dbus_connection_send_with_reply(connection, message,
370                                         &call, DBUS_TIMEOUT) == FALSE) {
371                 connman_error("Unable to call %s.%s()",
372                         VPN_CONNECTION_INTERFACE, VPN_CONNECT);
373                 dbus_message_unref(message);
374                 return -EINVAL;
375         }
376
377         if (call == NULL) {
378                 dbus_message_unref(message);
379                 return -EINVAL;
380         }
381
382         dbus_pending_call_set_notify(call, connect_reply, NULL, NULL);
383
384         dbus_message_unref(message);
385
386         return -EINPROGRESS;
387 }
388
389 static void add_connection(const char *path, DBusMessageIter *properties,
390                         void *user_data)
391 {
392         struct connection_data *data;
393         int err;
394
395         data = g_hash_table_lookup(vpn_connections, path);
396         if (data != NULL)
397                 return;
398
399         data = create_connection_data(path);
400         if (data == NULL)
401                 return;
402
403         DBG("data %p path %s", data, path);
404
405         while (dbus_message_iter_get_arg_type(properties) ==
406                         DBUS_TYPE_DICT_ENTRY) {
407                 DBusMessageIter entry, value;
408                 const char *key;
409                 char *str;
410
411                 dbus_message_iter_recurse(properties, &entry);
412                 dbus_message_iter_get_basic(&entry, &key);
413
414                 dbus_message_iter_next(&entry);
415                 dbus_message_iter_recurse(&entry, &value);
416
417                 if (g_str_equal(key, "State") == TRUE) {
418                         dbus_message_iter_get_basic(&value, &str);
419                         DBG("state %s -> %s", data->state, str);
420                         data->state = g_strdup(str);
421                 } else if (g_str_equal(key, "IPv4") == TRUE) {
422                         extract_ip(&value, AF_INET, data);
423                 } else if (g_str_equal(key, "IPv6") == TRUE) {
424                         extract_ip(&value, AF_INET6, data);
425                 } else if (g_str_equal(key, "Name") == TRUE) {
426                         dbus_message_iter_get_basic(&value, &str);
427                         data->name = g_strdup(str);
428                 } else if (g_str_equal(key, "Type") == TRUE) {
429                         dbus_message_iter_get_basic(&value, &str);
430                         data->type = g_strdup(str);
431                 } else if (g_str_equal(key, "Host") == TRUE) {
432                         dbus_message_iter_get_basic(&value, &str);
433                         data->host = g_strdup(str);
434                 } else if (g_str_equal(key, "Domain") == TRUE) {
435                         dbus_message_iter_get_basic(&value, &str);
436                         data->domain = g_strdup(str);
437                 } else if (g_str_equal(key, "Nameservers") == TRUE) {
438                         extract_nameservers(&value, data);
439                 } else if (g_str_equal(key, "Index") == TRUE) {
440                         dbus_message_iter_get_basic(&value, &data->index);
441                 } else {
442                         if (dbus_message_iter_get_arg_type(&value) ==
443                                                         DBUS_TYPE_STRING) {
444                                 dbus_message_iter_get_basic(&value, &str);
445                                 g_hash_table_replace(data->setting_strings,
446                                                 g_strdup(key), g_strdup(str));
447                         } else {
448                                 DBG("unknown key %s", key);
449                         }
450                 }
451
452                 dbus_message_iter_next(properties);
453         }
454
455         g_hash_table_insert(vpn_connections, g_strdup(path), data);
456
457         err = create_provider(data, user_data);
458         if (err < 0)
459                 goto out;
460
461         return;
462
463 out:
464         DBG("removing %s", path);
465         g_hash_table_remove(vpn_connections, path);
466 }
467
468 static void get_connections_reply(DBusPendingCall *call, void *user_data)
469 {
470         DBusMessage *reply;
471         DBusError error;
472         DBusMessageIter array, dict;
473         const char *signature = DBUS_TYPE_ARRAY_AS_STRING
474                 DBUS_STRUCT_BEGIN_CHAR_AS_STRING
475                 DBUS_TYPE_OBJECT_PATH_AS_STRING
476                 DBUS_TYPE_ARRAY_AS_STRING
477                 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
478                 DBUS_TYPE_STRING_AS_STRING
479                 DBUS_TYPE_VARIANT_AS_STRING
480                 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
481                 DBUS_STRUCT_END_CHAR_AS_STRING;
482
483         DBG("");
484
485         reply = dbus_pending_call_steal_reply(call);
486
487         if (dbus_message_has_signature(reply, signature) == FALSE) {
488                 connman_error("vpnd signature \"%s\" does not match "
489                                                         "expected \"%s\"",
490                         dbus_message_get_signature(reply), signature);
491                 goto done;
492         }
493
494         dbus_error_init(&error);
495
496         if (dbus_set_error_from_message(&error, reply) == TRUE) {
497                 connman_error("%s", error.message);
498                 dbus_error_free(&error);
499                 goto done;
500         }
501
502         if (dbus_message_iter_init(reply, &array) == FALSE)
503                 goto done;
504
505         dbus_message_iter_recurse(&array, &dict);
506
507         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_STRUCT) {
508                 DBusMessageIter value, properties;
509                 const char *path;
510
511                 dbus_message_iter_recurse(&dict, &value);
512                 dbus_message_iter_get_basic(&value, &path);
513
514                 dbus_message_iter_next(&value);
515                 dbus_message_iter_recurse(&value, &properties);
516
517                 add_connection(path, &properties, user_data);
518
519                 dbus_message_iter_next(&dict);
520         }
521
522 done:
523         dbus_message_unref(reply);
524
525         dbus_pending_call_unref(call);
526 }
527
528 static int get_connections(void *user_data)
529 {
530         DBusPendingCall *call;
531         DBusMessage *message;
532
533         DBG("");
534
535         message = dbus_message_new_method_call(VPN_SERVICE, "/",
536                                         VPN_MANAGER_INTERFACE,
537                                         GET_CONNECTIONS);
538         if (message == NULL)
539                 return -ENOMEM;
540
541         if (dbus_connection_send_with_reply(connection, message,
542                                         &call, DBUS_TIMEOUT) == FALSE) {
543                 connman_error("Unable to call %s.%s()", VPN_MANAGER_INTERFACE,
544                                                         GET_CONNECTIONS);
545                 dbus_message_unref(message);
546                 return -EINVAL;
547         }
548
549         if (call == NULL) {
550                 dbus_message_unref(message);
551                 return -EINVAL;
552         }
553
554         dbus_pending_call_set_notify(call, get_connections_reply,
555                                                         user_data, NULL);
556
557         dbus_message_unref(message);
558
559         return -EINPROGRESS;
560 }
561
562 static int provider_probe(struct connman_provider *provider)
563 {
564         return 0;
565 }
566
567 static void remove_connection_reply(DBusPendingCall *call, void *user_data)
568 {
569         DBusMessage *reply;
570         DBusError error;
571
572         DBG("");
573
574         reply = dbus_pending_call_steal_reply(call);
575
576         dbus_error_init(&error);
577
578         if (dbus_set_error_from_message(&error, reply) == TRUE) {
579                 /*
580                  * If the returned error is NotFound, it means that we
581                  * have actually removed the provider in vpnd already.
582                  */
583                 if (dbus_error_has_name(&error, CONNMAN_ERROR_INTERFACE
584                                                 ".NotFound") == FALSE)
585                         connman_error("%s", error.message);
586
587                 dbus_error_free(&error);
588         }
589
590         dbus_message_unref(reply);
591
592         dbus_pending_call_unref(call);
593 }
594
595 static int provider_remove(struct connman_provider *provider)
596 {
597         DBusPendingCall *call;
598         DBusMessage *message;
599         struct connection_data *data;
600
601         data = connman_provider_get_data(provider);
602
603         DBG("provider %p data %p", provider, data);
604
605         /*
606          * When provider.c:provider_remove() calls this function,
607          * it will remove the provider itself after the call.
608          * This means that we cannot use the provider pointer later
609          * as it is no longer valid.
610          */
611         data->provider = NULL;
612
613         message = dbus_message_new_method_call(VPN_SERVICE, "/",
614                                         VPN_MANAGER_INTERFACE,
615                                         VPN_REMOVE);
616         if (message == NULL)
617                 return -ENOMEM;
618
619         dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &data->path,
620                                 NULL);
621
622         if (dbus_connection_send_with_reply(connection, message,
623                                         &call, DBUS_TIMEOUT) == FALSE) {
624                 connman_error("Unable to call %s.%s()", VPN_MANAGER_INTERFACE,
625                                                         VPN_REMOVE);
626                 dbus_message_unref(message);
627                 return -EINVAL;
628         }
629
630         if (call == NULL) {
631                 dbus_message_unref(message);
632                 return -EINVAL;
633         }
634
635         dbus_pending_call_set_notify(call, remove_connection_reply,
636                                                         NULL, NULL);
637
638         dbus_message_unref(message);
639
640         return 0;
641 }
642
643 static int provider_connect(struct connman_provider *provider)
644 {
645         struct connection_data *data;
646
647         data = connman_provider_get_data(provider);
648         if (data == NULL)
649                 return -EINVAL;
650
651         return connect_provider(data, NULL);
652
653 }
654
655 static void disconnect_reply(DBusPendingCall *call, void *user_data)
656 {
657         DBusMessage *reply;
658         DBusError error;
659
660         DBG("");
661
662         reply = dbus_pending_call_steal_reply(call);
663
664         dbus_error_init(&error);
665
666         if (dbus_set_error_from_message(&error, reply) == TRUE) {
667                 connman_error("%s", error.message);
668                 dbus_error_free(&error);
669                 goto done;
670         }
671
672 done:
673         dbus_message_unref(reply);
674
675         dbus_pending_call_unref(call);
676 }
677
678 static int disconnect_provider(struct connection_data *data)
679 {
680         DBusPendingCall *call;
681         DBusMessage *message;
682
683         DBG("data %p path %s", data, data->path);
684
685         message = dbus_message_new_method_call(VPN_SERVICE, data->path,
686                                         VPN_CONNECTION_INTERFACE,
687                                         VPN_DISCONNECT);
688         if (message == NULL)
689                 return -ENOMEM;
690
691         if (dbus_connection_send_with_reply(connection, message,
692                                         &call, DBUS_TIMEOUT) == FALSE) {
693                 connman_error("Unable to call %s.%s()",
694                         VPN_CONNECTION_INTERFACE, VPN_DISCONNECT);
695                 dbus_message_unref(message);
696                 return -EINVAL;
697         }
698
699         if (call == NULL) {
700                 dbus_message_unref(message);
701                 return -EINVAL;
702         }
703
704         dbus_pending_call_set_notify(call, disconnect_reply, NULL, NULL);
705
706         dbus_message_unref(message);
707
708         connman_provider_set_state(data->provider,
709                                         CONNMAN_PROVIDER_STATE_DISCONNECT);
710         /*
711          * We return 0 here instead of -EINPROGRESS because
712          * __connman_service_disconnect() needs to return something
713          * to gdbus so that gdbus will not call Disconnect() more
714          * than once. This way we do not need to pass the dbus reply
715          * message around the code.
716          */
717         return 0;
718 }
719
720 static int provider_disconnect(struct connman_provider *provider)
721 {
722         struct connection_data *data;
723
724         DBG("provider %p", provider);
725
726         data = connman_provider_get_data(provider);
727         if (data == NULL)
728                 return -EINVAL;
729
730         if (g_str_equal(data->state, "ready") == TRUE ||
731                         g_str_equal(data->state, "configuration") == TRUE)
732                 return disconnect_provider(data);
733
734         return 0;
735 }
736
737 static void configuration_create_reply(DBusPendingCall *call, void *user_data)
738 {
739         DBusMessage *reply;
740         DBusError error;
741         DBusMessageIter iter;
742         const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
743         const char *path;
744
745         DBG("user %p", user_data);
746
747         reply = dbus_pending_call_steal_reply(call);
748
749         if (dbus_message_has_signature(reply, signature) == FALSE) {
750                 connman_error("vpn configuration signature \"%s\" does not "
751                                                 "match expected \"%s\"",
752                         dbus_message_get_signature(reply), signature);
753                 goto done;
754         }
755
756         dbus_error_init(&error);
757
758         if (dbus_set_error_from_message(&error, reply) == TRUE) {
759                 connman_error("dbus error: %s", error.message);
760                 dbus_error_free(&error);
761                 goto done;
762         }
763
764         if (dbus_message_iter_init(reply, &iter) == FALSE)
765                 goto done;
766
767         dbus_message_iter_get_basic(&iter, &path);
768
769         /*
770          * Then try to connect the VPN as expected by ConnectProvider API
771          */
772         // XXX:
773
774 done:
775         dbus_message_unref(reply);
776
777         dbus_pending_call_unref(call);
778 }
779
780 static void set_dbus_ident(char *ident)
781 {
782         int i, len = strlen(ident);
783
784         for (i = 0; i < len; i++) {
785                 if (ident[i] >= '0' && ident[i] <= '9')
786                         continue;
787                 if (ident[i] >= 'a' && ident[i] <= 'z')
788                         continue;
789                 if (ident[i] >= 'A' && ident[i] <= 'Z')
790                         continue;
791                 ident[i] = '_';
792         }
793 }
794
795 static int create_configuration(DBusMessage *msg)
796 {
797         DBusMessage *new_msg;
798         DBusPendingCall *call;
799         DBusMessageIter iter, array;
800         const char *type = NULL, *name = NULL;
801         const char *host = NULL, *domain = NULL;
802         char *ident, *me;
803         int err;
804         dbus_bool_t result;
805         struct connection_data *data;
806
807         dbus_message_iter_init(msg, &iter);
808         dbus_message_iter_recurse(&iter, &array);
809
810         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
811                 DBusMessageIter entry, value;
812                 const char *key;
813
814                 dbus_message_iter_recurse(&array, &entry);
815                 dbus_message_iter_get_basic(&entry, &key);
816
817                 dbus_message_iter_next(&entry);
818                 dbus_message_iter_recurse(&entry, &value);
819
820                 switch (dbus_message_iter_get_arg_type(&value)) {
821                 case DBUS_TYPE_STRING:
822                         if (g_str_equal(key, "Type") == TRUE)
823                                 dbus_message_iter_get_basic(&value, &type);
824                         else if (g_str_equal(key, "Name") == TRUE)
825                                 dbus_message_iter_get_basic(&value, &name);
826                         else if (g_str_equal(key, "Host") == TRUE)
827                                 dbus_message_iter_get_basic(&value, &host);
828                         else if (g_str_equal(key, "VPN.Domain") == TRUE)
829                                 dbus_message_iter_get_basic(&value, &domain);
830                         break;
831                 }
832
833                 dbus_message_iter_next(&array);
834         }
835
836         DBG("VPN type %s name %s host %s domain %s", type, name, host, domain);
837
838         if (host == NULL || domain == NULL)
839                 return -EINVAL;
840
841         if (type == NULL || name == NULL)
842                 return -EOPNOTSUPP;
843
844         ident = g_strdup_printf("%s_%s", host, domain);
845         set_dbus_ident(ident);
846
847         DBG("ident %s", ident);
848
849         data = g_hash_table_lookup(vpn_connections, ident);
850         if (data != NULL) {
851                 if (data->call != NULL) {
852                         connman_error("Dbus call already pending");
853                         return -EINPROGRESS;
854                 }
855         } else {
856                 data = create_connection_data(ident);
857                 if (data == NULL)
858                         return -ENOMEM;
859
860                 g_hash_table_insert(vpn_connections, g_strdup(ident), data);
861         }
862
863         /*
864          * User called net.connman.Manager.ConnectProvider if we are here.
865          * The config dict is already there in the original message so use it.
866          */
867         me = g_strdup(dbus_message_get_destination(msg));
868
869         new_msg = dbus_message_copy(msg);
870
871         dbus_message_set_interface(new_msg, VPN_MANAGER_INTERFACE);
872         dbus_message_set_path(new_msg, "/");
873         dbus_message_set_destination(new_msg, VPN_SERVICE);
874         dbus_message_set_sender(new_msg, me);
875         dbus_message_set_member(new_msg, "Create");
876
877         result = dbus_connection_send_with_reply(connection, new_msg,
878                                                 &call, DBUS_TIMEOUT);
879         if (result == FALSE || call == NULL) {
880                 err = -EIO;
881                 goto done;
882         }
883
884         dbus_pending_call_set_notify(call, configuration_create_reply,
885                                                                 NULL, NULL);
886         data->call = call;
887
888 done:
889         dbus_message_unref(new_msg);
890
891         g_free(me);
892         return err;
893 }
894
895 static struct connman_provider_driver provider_driver = {
896         .name = "VPN",
897         .type = CONNMAN_PROVIDER_TYPE_VPN,
898         .probe = provider_probe,
899         .remove = provider_remove,
900         .connect = provider_connect,
901         .disconnect = provider_disconnect,
902         .set_property = set_string,
903         .get_property = get_string,
904         .create = create_configuration,
905 };
906
907 static void destroy_provider(struct connection_data *data)
908 {
909         DBG("data %p", data);
910
911         if (g_str_equal(data->state, "ready") == TRUE ||
912                         g_str_equal(data->state, "configuration") == TRUE)
913                 connman_provider_disconnect(data->provider);
914
915         if (data->call != NULL)
916                 dbus_pending_call_cancel(data->call);
917
918         connman_provider_put(data->provider);
919
920         data->provider = NULL;
921 }
922
923 static void connection_destroy(gpointer hash_data)
924 {
925         struct connection_data *data = hash_data;
926
927         DBG("data %p", data);
928
929         if (data->provider != NULL)
930                 destroy_provider(data);
931
932         g_free(data->path);
933         g_free(data->state);
934         g_free(data->type);
935         g_free(data->name);
936         g_free(data->host);
937         g_free(data->domain);
938         g_strfreev(data->nameservers);
939         g_hash_table_destroy(data->setting_strings);
940         connman_ipaddress_free(data->ip);
941
942         g_free(data);
943 }
944
945 static void vpnd_created(DBusConnection *conn, void *user_data)
946 {
947         DBG("connection %p", conn);
948
949         if (starting_vpnd == TRUE) {
950                 vpn_connections = g_hash_table_new_full(g_str_hash,
951                                                 g_str_equal,
952                                                 g_free, connection_destroy);
953                 get_connections(user_data);
954                 starting_vpnd = FALSE;
955         }
956 }
957
958 static void vpnd_removed(DBusConnection *conn, void *user_data)
959 {
960         DBG("connection %p", conn);
961
962         g_hash_table_destroy(vpn_connections);
963         vpn_connections = NULL;
964         starting_vpnd = TRUE;
965 }
966
967 static void remove_connection(DBusConnection *conn, const char *path)
968 {
969         DBG("path %s", path);
970
971         g_hash_table_remove(vpn_connections, path);
972 }
973
974 static gboolean connection_removed(DBusConnection *conn, DBusMessage *message,
975                                 void *user_data)
976 {
977         const char *path;
978         const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
979
980         if (dbus_message_has_signature(message, signature) == FALSE) {
981                 connman_error("vpn removed signature \"%s\" does not match "
982                                                         "expected \"%s\"",
983                         dbus_message_get_signature(message), signature);
984                 return TRUE;
985         }
986
987         dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
988                                 DBUS_TYPE_INVALID);
989         remove_connection(conn, path);
990         return TRUE;
991 }
992
993 static gboolean connection_added(DBusConnection *conn, DBusMessage *message,
994                                 void *user_data)
995 {
996         DBusMessageIter iter, properties;
997         const char *path;
998         const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING
999                 DBUS_TYPE_ARRAY_AS_STRING
1000                 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1001                 DBUS_TYPE_STRING_AS_STRING
1002                 DBUS_TYPE_VARIANT_AS_STRING
1003                 DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
1004
1005         if (dbus_message_has_signature(message, signature) == FALSE) {
1006                 connman_error("vpn ConnectionAdded signature \"%s\" does not "
1007                                                 "match expected \"%s\"",
1008                         dbus_message_get_signature(message), signature);
1009                 return TRUE;
1010         }
1011
1012         DBG("");
1013
1014         if (dbus_message_iter_init(message, &iter) == FALSE)
1015                 return TRUE;
1016
1017         dbus_message_iter_get_basic(&iter, &path);
1018
1019         dbus_message_iter_next(&iter);
1020         dbus_message_iter_recurse(&iter, &properties);
1021
1022         add_connection(path, &properties, user_data);
1023
1024         return TRUE;
1025 }
1026
1027 static gboolean property_changed(DBusConnection *conn,
1028                                 DBusMessage *message,
1029                                 void *user_data)
1030 {
1031         const char *path = dbus_message_get_path(message);
1032         struct connection_data *data = NULL;
1033         DBusMessageIter iter, value;
1034         connman_bool_t ip_set = FALSE;
1035         int err;
1036         char *str;
1037         const char *key;
1038         const char *signature = DBUS_TYPE_STRING_AS_STRING
1039                 DBUS_TYPE_VARIANT_AS_STRING;
1040
1041         if (dbus_message_has_signature(message, signature) == FALSE) {
1042                 connman_error("vpn property signature \"%s\" does not match "
1043                                                         "expected \"%s\"",
1044                         dbus_message_get_signature(message), signature);
1045                 return TRUE;
1046         }
1047
1048         data = g_hash_table_lookup(vpn_connections, path);
1049         if (data == NULL)
1050                 return TRUE;
1051
1052         if (dbus_message_iter_init(message, &iter) == FALSE)
1053                 return TRUE;
1054
1055         dbus_message_iter_get_basic(&iter, &key);
1056
1057         dbus_message_iter_next(&iter);
1058         dbus_message_iter_recurse(&iter, &value);
1059
1060         DBG("key %s", key);
1061
1062         if (g_str_equal(key, "State") == TRUE) {
1063                 dbus_message_iter_get_basic(&value, &str);
1064
1065                 DBG("%s %s -> %s", data->path, data->state, str);
1066
1067                 if (g_str_equal(data->state, str) == TRUE)
1068                         return TRUE;
1069
1070                 g_free(data->state);
1071                 data->state = g_strdup(str);
1072
1073                 set_provider_state(data);
1074         } else if (g_str_equal(key, "Index") == TRUE) {
1075                 dbus_message_iter_get_basic(&value, &data->index);
1076                 connman_provider_set_index(data->provider, data->index);
1077         } else if (g_str_equal(key, "IPv4") == TRUE) {
1078                 err = extract_ip(&value, AF_INET, data);
1079                 ip_set = TRUE;
1080         } else if (g_str_equal(key, "IPv6") == TRUE) {
1081                 err = extract_ip(&value, AF_INET6, data);
1082                 ip_set = TRUE;
1083         } else if (g_str_equal(key, "ServerRoutes") == TRUE) {
1084                 /* XXX: TBD */
1085         } else if (g_str_equal(key, "UserRoutes") == TRUE) {
1086                 /* XXX: TBD */
1087         } else if (g_str_equal(key, "Nameservers") == TRUE) {
1088                 extract_nameservers(&value, data);
1089         }
1090
1091         if (ip_set == TRUE && err == 0) {
1092                 err = connman_provider_set_ipaddress(data->provider, data->ip);
1093                 if (err < 0)
1094                         DBG("setting provider IP address failed (%s/%d)",
1095                                 strerror(-err), -err);
1096         }
1097
1098         return TRUE;
1099 }
1100
1101 static int vpn_init(void)
1102 {
1103         int err;
1104
1105         connection = connman_dbus_get_connection();
1106         if (connection == NULL)
1107                 return -EIO;
1108
1109         watch = g_dbus_add_service_watch(connection, VPN_SERVICE,
1110                         vpnd_created, vpnd_removed, &provider_driver, NULL);
1111
1112         added_watch = g_dbus_add_signal_watch(connection, VPN_SERVICE, NULL,
1113                                         VPN_MANAGER_INTERFACE,
1114                                         CONNECTION_ADDED, connection_added,
1115                                         &provider_driver, NULL);
1116
1117         removed_watch = g_dbus_add_signal_watch(connection, VPN_SERVICE, NULL,
1118                                         VPN_MANAGER_INTERFACE,
1119                                         CONNECTION_REMOVED, connection_removed,
1120                                         NULL, NULL);
1121
1122         property_watch = g_dbus_add_signal_watch(connection, VPN_SERVICE, NULL,
1123                                         VPN_CONNECTION_INTERFACE,
1124                                         PROPERTY_CHANGED, property_changed,
1125                                         NULL, NULL);
1126
1127         if (added_watch == 0 || removed_watch == 0 || property_watch == 0) {
1128                 err = -EIO;
1129                 goto remove;
1130         }
1131
1132         err = connman_provider_driver_register(&provider_driver);
1133         if (err == 0)
1134                 vpnd_created(connection, &provider_driver);
1135
1136         return err;
1137
1138 remove:
1139         g_dbus_remove_watch(connection, watch);
1140         g_dbus_remove_watch(connection, added_watch);
1141         g_dbus_remove_watch(connection, removed_watch);
1142         g_dbus_remove_watch(connection, property_watch);
1143
1144         dbus_connection_unref(connection);
1145
1146         return err;
1147 }
1148
1149 static void vpn_exit(void)
1150 {
1151         g_dbus_remove_watch(connection, watch);
1152         g_dbus_remove_watch(connection, added_watch);
1153         g_dbus_remove_watch(connection, removed_watch);
1154         g_dbus_remove_watch(connection, property_watch);
1155
1156         connman_provider_driver_unregister(&provider_driver);
1157
1158         g_hash_table_destroy(vpn_connections);
1159
1160         dbus_connection_unref(connection);
1161 }
1162
1163 CONNMAN_PLUGIN_DEFINE(vpn, "VPN plugin", VERSION,
1164                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, vpn_init, vpn_exit)