Disconnect VPNs when no physical services are left
[platform/upstream/connman.git] / src / provider.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  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 <stdio.h>
27 #include <string.h>
28 #include <gdbus.h>
29
30 #include "connman.h"
31
32 static DBusConnection *connection = NULL;
33
34 static GHashTable *provider_hash = NULL;
35
36 static GSList *driver_list = NULL;
37
38 struct connman_provider {
39         struct connman_element element;
40         struct connman_service *vpn_service;
41         char *identifier;
42         char *name;
43         char *type;
44         char *host;
45         char *dns;
46         char *domain;
47         struct connman_provider_driver *driver;
48         void *driver_data;
49 };
50
51 void __connman_provider_append_properties(struct connman_provider *provider,
52                                                         DBusMessageIter *iter)
53 {
54         if (provider->host != NULL)
55                 connman_dbus_dict_append_basic(iter, "Host",
56                                         DBUS_TYPE_STRING, &provider->host);
57
58         if (provider->domain != NULL)
59                 connman_dbus_dict_append_basic(iter, "Domain",
60                                         DBUS_TYPE_STRING, &provider->domain);
61
62         if (provider->type != NULL)
63                 connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING,
64                                                  &provider->type);
65 }
66
67 static struct connman_provider *connman_provider_lookup(const char *identifier)
68 {
69         struct connman_provider *provider = NULL;
70
71         provider = g_hash_table_lookup(provider_hash, identifier);
72
73         return provider;
74 }
75
76 static int connman_provider_setup_vpn_ipv4(struct connman_provider *provider,
77                                                 struct connman_element *element)
78 {
79         if (element == NULL || provider == NULL)
80                 return -EINVAL;
81
82         DBG("set vpn type %d", element->type);
83
84         g_free(element->ipv4.address);
85         element->ipv4.address = g_strdup(provider->element.ipv4.address);
86
87         g_free(element->ipv4.netmask);
88         element->ipv4.netmask = g_strdup(provider->element.ipv4.netmask);
89
90         g_free(element->ipv4.gateway);
91         element->ipv4.gateway = g_strdup(provider->element.ipv4.gateway);
92
93         g_free(element->ipv4.broadcast);
94         element->ipv4.broadcast = g_strdup(provider->element.ipv4.broadcast);
95
96         g_free(element->ipv4.pac);
97         element->ipv4.pac = g_strdup(provider->element.ipv4.pac);
98
99         return connman_element_register(element, &provider->element);
100 }
101
102 struct connman_provider *connman_provider_ref(struct connman_provider *provider)
103 {
104         DBG("provider %p", provider);
105
106         if (connman_element_ref(&provider->element) == NULL)
107                 return NULL;
108
109         return provider;
110 }
111
112 void connman_provider_unref(struct connman_provider *provider)
113 {
114         DBG("provider %p", provider);
115
116         connman_element_unref(&provider->element);
117 }
118
119 static gboolean match_driver(struct connman_provider *provider,
120                                 struct connman_provider_driver *driver)
121 {
122         if (g_strcmp0(driver->name, provider->type) == 0)
123                 return TRUE;
124
125         return FALSE;
126 }
127
128 static int provider_probe(struct connman_provider *provider)
129 {
130         GSList *list;
131
132         DBG("provider %p name %s", provider, provider->name);
133
134         if (provider->driver != NULL)
135                 return -EALREADY;
136
137         for (list = driver_list; list; list = list->next) {
138                 struct connman_provider_driver *driver = list->data;
139
140                 if (match_driver(provider, driver) == FALSE)
141                         continue;
142
143                 DBG("driver %p name %s", driver, driver->name);
144
145                 if (driver->probe != NULL && driver->probe(provider) == 0) {
146                         provider->driver = driver;
147                         break;
148                 }
149         }
150
151         if (provider->driver == NULL)
152                 return -ENODEV;
153
154         return 0;
155 }
156
157 int __connman_provider_disconnect(struct connman_provider *provider)
158 {
159         int err;
160
161         DBG("provider %p", provider);
162
163         if (provider->driver != NULL && provider->driver->disconnect != NULL)
164                 err = provider->driver->disconnect(provider);
165         else
166                 return -EOPNOTSUPP;
167
168         __connman_service_indicate_state(provider->vpn_service,
169                                         CONNMAN_SERVICE_STATE_DISCONNECT);
170         if (err < 0) {
171                 if (err != -EINPROGRESS)
172                         return err;
173
174                 return -EINPROGRESS;
175         }
176
177         return 0;
178 }
179
180 int __connman_provider_connect(struct connman_provider *provider)
181 {
182         int err;
183
184         DBG("provider %p", provider);
185
186         g_free(provider->element.ipv4.address);
187         g_free(provider->element.ipv4.netmask);
188         g_free(provider->element.ipv4.gateway);
189         g_free(provider->element.ipv4.broadcast);
190         g_free(provider->element.ipv4.pac);
191
192         provider->element.ipv4.address = NULL;
193         provider->element.ipv4.netmask = NULL;
194         provider->element.ipv4.gateway = NULL;
195         provider->element.ipv4.broadcast = NULL;
196         provider->element.ipv4.pac = NULL;
197
198         if (provider->driver != NULL && provider->driver->connect != NULL)
199                 err = provider->driver->connect(provider);
200         else
201                 return -EOPNOTSUPP;
202
203         if (err < 0) {
204                 if (err != -EINPROGRESS)
205                         return err;
206
207                 __connman_service_indicate_state(provider->vpn_service,
208                                         CONNMAN_SERVICE_STATE_ASSOCIATION);
209                 return -EINPROGRESS;
210         }
211
212         return 0;
213 }
214
215 int __connman_provider_remove(const char *path)
216 {
217         struct connman_provider *provider;
218
219         DBG("path %s", path);
220
221         provider = g_hash_table_lookup(provider_hash, path);
222         if (provider == NULL) {
223                 DBG("patch %s not found", path);
224                 return -ENXIO;
225         }
226
227         g_hash_table_remove(provider_hash, path);
228
229         return 0;
230 }
231
232 static int set_connected(struct connman_provider *provider,
233                                         connman_bool_t connected)
234 {
235         struct connman_service *service = provider->vpn_service;
236
237         if (service == NULL)
238                 return -ENODEV;
239
240         if (connected == TRUE) {
241                 enum connman_element_type type = CONNMAN_ELEMENT_TYPE_UNKNOWN;
242                 struct connman_element *element;
243                 char *nameservers = NULL, *name = NULL;
244                 const char *value, *first;
245                 int err;
246
247                 __connman_service_indicate_state(provider->vpn_service,
248                                         CONNMAN_SERVICE_STATE_CONFIGURATION);
249
250                 type = CONNMAN_ELEMENT_TYPE_IPV4;
251
252                 element = connman_element_create(NULL);
253                 if (element == NULL)
254                         return -ENOMEM;
255
256                 element->type  = type;
257                 element->index = provider->element.index;
258
259                 err = connman_provider_setup_vpn_ipv4(provider, element);
260                 if (err < 0) {
261                         connman_element_unref(element);
262
263                         __connman_service_indicate_state(service,
264                                                 CONNMAN_SERVICE_STATE_FAILURE);
265
266                         return err;
267                 }
268
269                 __connman_service_indicate_state(service,
270                                                 CONNMAN_SERVICE_STATE_READY);
271
272                 __connman_service_set_domainname(service, provider->domain);
273
274                 nameservers = g_strdup(provider->dns);
275                 value = nameservers;
276                 first = strchr(value, ' ');
277                 __connman_service_append_nameserver(service, first);
278                 name = connman_inet_ifname(provider->element.index);
279                 while (value) {
280                         char *next = strchr(value, ' ');
281                         if (next)
282                                 *(next++) = 0;
283
284                         connman_resolver_append(name, provider->domain, value);
285                         value = next;
286                 }
287
288                 g_free(nameservers);
289                 g_free(name);
290
291         } else {
292                 connman_element_unregister_children(&provider->element);
293                 __connman_service_indicate_state(service,
294                                                 CONNMAN_SERVICE_STATE_IDLE);
295         }
296
297         return 0;
298 }
299
300 int connman_provider_set_state(struct connman_provider *provider,
301                                         enum connman_provider_state state)
302 {
303         if (provider == NULL || provider->vpn_service == NULL)
304                 return -EINVAL;
305
306         switch (state) {
307         case CONNMAN_PROVIDER_STATE_UNKNOWN:
308                 return -EINVAL;
309         case CONNMAN_PROVIDER_STATE_IDLE:
310                 return set_connected(provider, FALSE);
311         case CONNMAN_PROVIDER_STATE_CONNECT:
312                 return __connman_service_indicate_state(provider->vpn_service,
313                                         CONNMAN_SERVICE_STATE_ASSOCIATION);
314         case CONNMAN_PROVIDER_STATE_READY:
315                 return set_connected(provider, TRUE);
316         case CONNMAN_PROVIDER_STATE_DISCONNECT:
317                 return __connman_service_indicate_state(provider->vpn_service,
318                                         CONNMAN_SERVICE_STATE_DISCONNECT);
319         case CONNMAN_PROVIDER_STATE_FAILURE:
320                 return __connman_service_indicate_state(provider->vpn_service,
321                                         CONNMAN_SERVICE_STATE_FAILURE);
322         }
323
324         return -EINVAL;
325 }
326
327 static void unregister_provider(gpointer data)
328 {
329         struct connman_provider *provider = data;
330
331         DBG("provider %p", provider);
332
333         __connman_service_put(provider->vpn_service);
334
335         connman_element_unregister(&provider->element);
336         connman_provider_unref(provider);
337 }
338
339 static void provider_destruct(struct connman_element *element)
340 {
341         struct connman_provider *provider = element->private;
342
343         DBG("provider %p", provider);
344
345         g_free(provider->name);
346         g_free(provider->type);
347         g_free(provider->domain);
348         g_free(provider->identifier);
349         g_free(provider->dns);
350 }
351
352 static void provider_initialize(struct connman_provider *provider)
353 {
354         DBG("provider %p", provider);
355
356         __connman_element_initialize(&provider->element);
357
358         provider->element.private = provider;
359         provider->element.destruct = provider_destruct;
360
361         provider->element.ipv4.address = NULL;
362         provider->element.ipv4.netmask = NULL;
363         provider->element.ipv4.gateway = NULL;
364         provider->element.ipv4.broadcast = NULL;
365         provider->element.ipv4.pac = NULL;
366
367         provider->name = NULL;
368         provider->type = NULL;
369         provider->dns = NULL;
370         provider->domain = NULL;
371         provider->identifier = NULL;
372 }
373
374 static struct connman_provider *connman_provider_new(void)
375 {
376         struct connman_provider *provider;
377
378         provider = g_try_new0(struct connman_provider, 1);
379         if (provider == NULL)
380                 return NULL;
381
382         DBG("provider %p", provider);
383         provider_initialize(provider);
384
385         return provider;
386 }
387
388 static struct connman_provider *connman_provider_get(const char *identifier)
389 {
390         struct connman_provider *provider;
391
392         provider = g_hash_table_lookup(provider_hash, identifier);
393         if (provider != NULL)
394                 return provider;
395
396         provider = connman_provider_new();
397         if (provider == NULL)
398                 return NULL;
399
400         DBG("provider %p", provider);
401
402         provider->identifier = g_strdup(identifier);
403
404         g_hash_table_insert(provider_hash, provider->identifier, provider);
405
406         provider->element.name = g_strdup(identifier);
407         connman_element_register(&provider->element, NULL);
408
409         return provider;
410 }
411
412 static void provider_dbus_ident(char *ident)
413 {
414         int i, len = strlen(ident);
415
416         for (i = 0; i < len; i++)
417                 if (ident[i] == '.')
418                         ident[i] = '_';
419 }
420
421 int __connman_provider_create_and_connect(DBusMessage *msg)
422 {
423         struct connman_provider *provider;
424         DBusMessageIter iter, array;
425         const char *type = NULL, *name = NULL, *service_path = NULL;
426         const char *host = NULL, *domain = NULL;
427         char *ident;
428         gboolean created = FALSE;
429         int err;
430
431         dbus_message_iter_init(msg, &iter);
432         dbus_message_iter_recurse(&iter, &array);
433
434         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
435                 DBusMessageIter entry, value;
436                 const char *key;
437
438                 dbus_message_iter_recurse(&array, &entry);
439                 dbus_message_iter_get_basic(&entry, &key);
440
441                 dbus_message_iter_next(&entry);
442                 dbus_message_iter_recurse(&entry, &value);
443
444                 switch (dbus_message_iter_get_arg_type(&value)) {
445                 case DBUS_TYPE_STRING:
446                         if (g_str_equal(key, "Type") == TRUE)
447                                 dbus_message_iter_get_basic(&value, &type);
448                         else if (g_str_equal(key, "Name") == TRUE)
449                                 dbus_message_iter_get_basic(&value, &name);
450                         else if (g_str_equal(key, "Host") == TRUE)
451                                 dbus_message_iter_get_basic(&value, &host);
452                         else if (g_str_equal(key, "VPN.Domain") == TRUE)
453                                 dbus_message_iter_get_basic(&value, &domain);
454                         break;
455                 }
456
457                 dbus_message_iter_next(&array);
458         }
459
460         if (host == NULL || domain == NULL) {
461                 err = -EINVAL;
462                 goto failed;
463         }
464
465         DBG("Type %s name %s", type, name);
466
467         if (type == NULL || name == NULL) {
468                 err = -EOPNOTSUPP;
469                 goto failed;
470         }
471
472         ident = g_strdup_printf("%s_%s", host, domain);
473         provider_dbus_ident(ident);
474
475         DBG("ident %s", ident);
476
477         provider = connman_provider_lookup(ident);
478
479         if (provider == NULL) {
480                 created = TRUE;
481                 provider = connman_provider_get(ident);
482                 if (provider) {
483                         provider->host = g_strdup(host);
484                         provider->domain = g_strdup(domain);
485                         provider->name = g_strdup(name);
486                         provider->type = g_strdup(type);
487                 }
488         }
489
490         if (provider == NULL) {
491                 DBG("can not create provider");
492                 err = -EOPNOTSUPP;
493                 goto failed;
494         }
495         dbus_message_iter_init(msg, &iter);
496         dbus_message_iter_recurse(&iter, &array);
497
498         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
499                 DBusMessageIter entry, value;
500                 const char *key, *str;
501
502                 dbus_message_iter_recurse(&array, &entry);
503                 dbus_message_iter_get_basic(&entry, &key);
504
505                 dbus_message_iter_next(&entry);
506                 dbus_message_iter_recurse(&entry, &value);
507
508                 switch (dbus_message_iter_get_arg_type(&value)) {
509                 case DBUS_TYPE_STRING:
510                         dbus_message_iter_get_basic(&value, &str);
511                         connman_provider_set_string(provider, key, str);
512                         break;
513                 }
514
515                 dbus_message_iter_next(&array);
516         }
517
518         g_free(ident);
519
520         if (provider == NULL) {
521                 err = -EOPNOTSUPP;
522                 goto failed;
523         }
524
525         if (created == TRUE)
526                 provider_probe(provider);
527
528         provider->vpn_service =
529                         __connman_service_create_from_provider(provider);
530         if (provider->vpn_service == NULL) {
531                 err = -EOPNOTSUPP;
532                 goto failed;
533         }
534
535         err = __connman_service_connect(provider->vpn_service);
536         if (err < 0 && err != -EINPROGRESS)
537                 goto failed;
538
539         service_path = __connman_service_get_path(provider->vpn_service);
540         g_dbus_send_reply(connection, msg,
541                                 DBUS_TYPE_OBJECT_PATH, &service_path,
542                                                         DBUS_TYPE_INVALID);
543         return 0;
544
545 failed:
546         if (provider != NULL && created == TRUE) {
547                 DBG("can not connect delete provider");
548                 connman_provider_unref(provider);
549
550                 if (provider->vpn_service != NULL)
551                         __connman_service_put(provider->vpn_service);
552         }
553
554         return err;
555 }
556
557 const char * __connman_provider_get_ident(struct connman_provider *provider)
558 {
559         if (provider == NULL)
560                 return NULL;
561
562         return provider->identifier;
563 }
564
565 int connman_provider_set_string(struct connman_provider *provider,
566                                         const char *key, const char *value)
567 {
568         DBG("provider %p key %s value %s", provider, key, value);
569
570         if (g_str_equal(key, "Type") == TRUE) {
571                 g_free(provider->type);
572                 provider->type = g_strdup(value);
573         } else if (g_str_equal(key, "Name") == TRUE) {
574                 g_free(provider->name);
575                 provider->name = g_strdup(value);
576         } else if (g_str_equal(key, "Gateway") == TRUE) {
577                 g_free(provider->element.ipv4.gateway);
578                 provider->element.ipv4.gateway = g_strdup(value);
579         } else if (g_str_equal(key, "Address") == TRUE) {
580                 g_free(provider->element.ipv4.address);
581                 provider->element.ipv4.address = g_strdup(value);
582         } else if (g_str_equal(key, "Netmask") == TRUE) {
583                 g_free(provider->element.ipv4.netmask);
584                 provider->element.ipv4.netmask = g_strdup(value);
585         } else if (g_str_equal(key, "PAC") == TRUE) {
586                 g_free(provider->element.ipv4.pac);
587                 provider->element.ipv4.pac = g_strdup(value);
588                 __connman_service_set_proxy_autoconfig(provider->vpn_service,
589                                                                         value);
590         } else if (g_str_equal(key, "DNS") == TRUE) {
591                 g_free(provider->dns);
592                 provider->dns = g_strdup(value);
593         } else if (g_str_equal(key, "Domain") == TRUE) {
594                 g_free(provider->domain);
595                 provider->domain = g_strdup(value);
596         }
597
598         return connman_element_set_string(&provider->element, key, value);
599 }
600
601 const char *connman_provider_get_string(struct connman_provider *provider,
602                                                         const char *key)
603 {
604         DBG("provider %p key %s", provider, key);
605
606         if (g_str_equal(key, "Type") == TRUE)
607                 return provider->type;
608         else if (g_str_equal(key, "Name") == TRUE)
609                 return provider->name;
610
611         return connman_element_get_string(&provider->element, key);
612 }
613
614 void *connman_provider_get_data(struct connman_provider *provider)
615 {
616         return provider->driver_data;
617 }
618
619 void connman_provider_set_data(struct connman_provider *provider, void *data)
620 {
621         provider->driver_data = data;
622 }
623
624 void connman_provider_set_index(struct connman_provider *provider, int index)
625 {
626         struct connman_service *service = provider->vpn_service;
627         struct connman_ipconfig *ipconfig;
628
629         DBG("");
630
631         ipconfig = __connman_service_get_ipconfig(service);
632
633         if (ipconfig == NULL) {
634                 __connman_service_create_ipconfig(service, index);
635
636                 ipconfig = __connman_service_get_ipconfig(service);
637                 if (ipconfig == NULL) {
638                         DBG("Couldnt create ipconfig");
639                         goto done;
640                 }
641         }
642
643         connman_ipconfig_set_method(ipconfig, CONNMAN_IPCONFIG_METHOD_FIXED);
644         __connman_ipconfig_set_index(ipconfig, index);
645
646
647 done:
648         provider->element.index = index;
649 }
650
651 int connman_provider_get_index(struct connman_provider *provider)
652 {
653         return provider->element.index;
654 }
655
656 static gint compare_priority(gconstpointer a, gconstpointer b)
657 {
658         return 0;
659 }
660
661 static void clean_provider(gpointer key, gpointer value, gpointer user_data)
662 {
663         struct connman_provider *provider = value;
664
665         if (provider->driver != NULL && provider->driver->remove)
666                 provider->driver->remove(provider);
667 }
668
669 int connman_provider_driver_register(struct connman_provider_driver *driver)
670 {
671         DBG("driver %p name %s", driver, driver->name);
672
673         driver_list = g_slist_insert_sorted(driver_list, driver,
674                                                         compare_priority);
675         return 0;
676 }
677
678 void connman_provider_driver_unregister(struct connman_provider_driver *driver)
679 {
680         DBG("driver %p name %s", driver, driver->name);
681
682         driver_list = g_slist_remove(driver_list, driver);
683 }
684
685 static void provider_disconnect(gpointer key, gpointer value,
686                                                 gpointer user_data)
687 {
688         struct connman_provider *provider = value;
689
690         __connman_provider_disconnect(provider);
691 }
692
693 static void provider_offline_mode(connman_bool_t enabled)
694 {
695         DBG("enabled %d", enabled);
696
697         if (enabled == TRUE)
698                 g_hash_table_foreach(provider_hash, provider_disconnect, NULL);
699
700 }
701
702 static struct connman_notifier provider_notifier = {
703         .name                   = "provider",
704         .offline_mode           = provider_offline_mode,
705 };
706
707 int __connman_provider_init(void)
708 {
709         int err;
710
711         DBG("");
712
713         connection = connman_dbus_get_connection();
714
715         provider_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
716                                                 NULL, unregister_provider);
717
718         err = connman_notifier_register(&provider_notifier);
719         if (err < 0) {
720                 g_hash_table_destroy(provider_hash);
721                 dbus_connection_unref(connection);
722         }
723
724         return err;
725 }
726
727 void __connman_provider_cleanup(void)
728 {
729         DBG("");
730
731         connman_notifier_unregister(&provider_notifier);
732
733         g_hash_table_foreach(provider_hash, clean_provider, NULL);
734
735         g_hash_table_destroy(provider_hash);
736         provider_hash = NULL;
737
738         dbus_connection_unref(connection);
739 }