service: Simplify nameserver route adding and removing
[framework/connectivity/connman.git] / plugins / openvpn.c
index f4544fe..8df8a3f 100644 (file)
 #include <connman/log.h>
 #include <connman/task.h>
 #include <connman/dbus.h>
+#include <connman/ipconfig.h>
 
 #include "vpn.h"
 
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
 static DBusConnection *connection;
 
-struct ov_route {
-       char *host;
-       char *netmask;
-       char *gateway;
+struct {
+       const char *cm_opt;
+       const char *ov_opt;
+       char       has_value;
+} ov_options[] = {
+       { "Host", "--remote", 1 },
+       { "OpenVPN.CACert", "--ca", 1 },
+       { "OpenVPN.Cert", "--cert", 1 },
+       { "OpenVPN.Key", "--key", 1 },
+       { "OpenVPN.MTU", "--mtu", 1 },
+       { "OpenVPN.Proto", "--proto", 1 },
+       { "OpenVPN.Port", "--port", 1 },
+       { "OpenVPN.AuthUserPass", "--auth-user-pass", 1 },
+       { "OpenVPN.TLSRemote", "--tls-remote", 1 },
+       { "OpenVPN.Cipher", "--cipher", 1 },
+       { "OpenVPN.Auth", "--auth", 1 },
+       { "OpenVPN.CompLZO", "--comp-lzo", 0 },
+       { "OpenVPN.RemoteCertTls", "--remote-cert-tls", 1 },
 };
 
-static void destroy_route(gpointer user_data)
-{
-       struct ov_route *route = user_data;
-
-       g_free(route->host);
-       g_free(route->netmask);
-       g_free(route->gateway);
-       g_free(route);
-}
-
-static void ov_provider_append_routes(gpointer key, gpointer value,
-                                       gpointer user_data)
-{
-       struct ov_route *route = value;
-       struct connman_provider *provider = user_data;
-
-       connman_provider_append_route(provider, route->host, route->netmask,
-                                       route->gateway);
-}
-
-static struct ov_route *ov_route_lookup(const char *key, const char *prefix_key,
-                                       GHashTable *routes)
-{
-       if (g_str_has_prefix(key, prefix_key)) {
-               unsigned long idx;
-               const char *start;
-               char *end;
-               struct ov_route *route;
-
-               start = key + strlen(prefix_key);
-               idx = g_ascii_strtoull(start, &end, 10);
-
-               if (idx == 0 && start == end) {
-                       connman_error("string conversion failed %s", start);
-                       return NULL;
-               }
-
-               route = g_hash_table_lookup(routes, GINT_TO_POINTER(idx));
-               if (route == NULL) {
-                       route = g_try_new0(struct ov_route, 1);
-                       if (route == NULL) {
-                               connman_error("out of memory");
-                               return NULL;
-                       }
-
-                       g_hash_table_replace(routes, GINT_TO_POINTER(idx),
-                                               route);
-               }
-
-               return  route;
-       }
-
-       return NULL;
-}
-
-static void ov_append_route(const char *key, const char *value, GHashTable *routes)
-{
-       struct ov_route *route;
-
-       /*
-        * OpenVPN pushes routing tupples (host, nw, gw) as several
-        * environment values, e.g.
-        *
-        * route_gateway_2 = 10.242.2.13
-        * route_netmask_2 = 255.255.0.0
-        * route_network_2 = 192.168.0.0
-        * route_gateway_1 = 10.242.2.13
-        * route_netmask_1 = 255.255.255.255
-        * route_network_1 = 10.242.2.1
-        *
-        * The hash table is used to group the separate environment
-        * variables together. It also makes sure all tupples are
-        * complete even when OpenVPN pushes the information in a
-        * wrong order (unlikely).
-        */
-
-       route = ov_route_lookup(key, "route_network_", routes);
-       if (route != NULL) {
-               route->host = g_strdup(value);
-               return;
-       }
-
-       route = ov_route_lookup(key, "route_netmask_", routes);
-       if (route != NULL) {
-               route->netmask = g_strdup(value);
-               return;
-       }
-
-       route = ov_route_lookup(key, "route_gateway_", routes);
-       if (route != NULL)
-               route->gateway = g_strdup(value);
-}
-
 static void ov_append_dns_entries(const char *key, const char *value,
                                        char **dns_entries)
 {
-       if (g_str_has_prefix(key, "foreign_option_")) {
-               gchar **options;
+       gchar **options;
 
-               options = g_strsplit(value, " ", 3);
-               if (options[0] != NULL &&
-                       !strcmp(options[0], "dhcp-option") &&
+       if (g_str_has_prefix(key, "foreign_option_") == FALSE)
+               return;
+
+       options = g_strsplit(value, " ", 3);
+       if (options[0] != NULL &&
+               !strcmp(options[0], "dhcp-option") &&
                        options[1] != NULL &&
                        !strcmp(options[1], "DNS") &&
-                       options[2] != NULL) {
+                               options[2] != NULL) {
 
-                       if (*dns_entries != NULL) {
-                               char *tmp;
+               if (*dns_entries != NULL) {
+                       char *tmp;
 
-                               tmp = g_strjoin(" ", *dns_entries,
+                       tmp = g_strjoin(" ", *dns_entries,
                                                options[2], NULL);
-                               g_free(*dns_entries);
-                               *dns_entries = tmp;
-                       } else {
-                               *dns_entries = g_strdup(options[2]);
-                       }
+                       g_free(*dns_entries);
+                       *dns_entries = tmp;
+               } else {
+                       *dns_entries = g_strdup(options[2]);
                }
-
-               g_strfreev(options);
        }
+
+       g_strfreev(options);
 }
 
 static int ov_notify(DBusMessage *msg, struct connman_provider *provider)
 {
        DBusMessageIter iter, dict;
        const char *reason, *key, *value;
-       const char *domain = NULL;
-       char *dns_entries = NULL;
-       GHashTable *routes;
-
-       dbus_message_iter_init(msg, &iter);
-
-       dbus_message_iter_get_basic(&iter, &reason);
-       dbus_message_iter_next(&iter);
+       char *nameservers = NULL;
+       char *address = NULL, *gateway = NULL, *peer = NULL;
+       struct connman_ipaddress *ipaddress;
 
        dbus_message_iter_init(msg, &iter);
 
@@ -196,13 +116,8 @@ static int ov_notify(DBusMessage *msg, struct connman_provider *provider)
        if (strcmp(reason, "up"))
                return VPN_STATE_DISCONNECT;
 
-       domain = connman_provider_get_string(provider, "VPN.Domain");
-
        dbus_message_iter_recurse(&iter, &dict);
 
-       routes = g_hash_table_new_full(g_direct_hash, g_direct_equal,
-                                       NULL, destroy_route);
-
        while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
                DBusMessageIter entry;
 
@@ -213,88 +128,109 @@ static int ov_notify(DBusMessage *msg, struct connman_provider *provider)
 
                DBG("%s = %s", key, value);
 
-               if (!strcmp(key, "trusted_ip"))
+               if (!strcmp(key, "trusted_ip")) {
                        connman_provider_set_string(provider, "Gateway", value);
+                       gateway = g_strdup(value);
+               }
 
-               if (!strcmp(key, "ifconfig_local"))
+               if (!strcmp(key, "ifconfig_local")) {
                        connman_provider_set_string(provider, "Address", value);
+                       address = g_strdup(value);
+               }
 
-               if (!strcmp(key, "ifconfig_remote"))
+               if (!strcmp(key, "ifconfig_remote")) {
                        connman_provider_set_string(provider, "Peer", value);
+                       peer = g_strdup(value);
+               }
 
-               ov_append_route(key, value, routes);
+               if (g_str_has_prefix(key, "route_") == TRUE)
+                       connman_provider_append_route(provider, key, value);
 
-               ov_append_dns_entries(key, value, &dns_entries);
+               ov_append_dns_entries(key, value, &nameservers);
 
                dbus_message_iter_next(&dict);
        }
 
-       if (dns_entries != NULL) {
-               connman_provider_set_string(provider, "DNS", dns_entries);
-               g_free(dns_entries);
+       ipaddress = connman_ipaddress_alloc(AF_INET);
+       if (ipaddress == NULL) {
+               g_free(nameservers);
+               g_free(address);
+               g_free(gateway);
+               g_free(peer);
+
+               return VPN_STATE_FAILURE;
        }
 
-       g_hash_table_foreach(routes, ov_provider_append_routes, provider);
+       connman_ipaddress_set_ipv4(ipaddress, address, NULL, gateway);
+       connman_ipaddress_set_peer(ipaddress, peer);
+       connman_provider_set_ipaddress(provider, ipaddress);
+
+       connman_provider_set_nameservers(provider, nameservers);
 
-       g_hash_table_destroy(routes);
+       g_free(nameservers);
+       g_free(address);
+       g_free(gateway);
+       g_free(peer);
+       connman_ipaddress_free(ipaddress);
 
        return VPN_STATE_CONNECT;
 }
 
+static int ov_save(struct connman_provider *provider, GKeyFile *keyfile)
+{
+       const char *option;
+       int i;
+
+       for (i = 0; i < (int)ARRAY_SIZE(ov_options); i++) {
+               if (strncmp(ov_options[i].cm_opt, "OpenVPN.", 8) == 0) {
+                       option = connman_provider_get_string(provider,
+                                                       ov_options[i].cm_opt);
+                       if (option == NULL)
+                               continue;
+
+                       g_key_file_set_string(keyfile,
+                                       connman_provider_get_save_group(provider),
+                                       ov_options[i].cm_opt, option);
+               }
+       }
+       return 0;
+}
+
+static int task_append_config_data(struct connman_provider *provider,
+                                       struct connman_task *task)
+{
+       const char *option;
+       int i;
+
+       for (i = 0; i < (int)ARRAY_SIZE(ov_options); i++) {
+               option = connman_provider_get_string(provider,
+                                       ov_options[i].cm_opt);
+               if (option == NULL)
+                       continue;
+
+               if (connman_task_add_argument(task,
+                                       ov_options[i].ov_opt,
+                                       ov_options[i].has_value ? option : NULL) < 0) {
+                       return -EIO;
+               }
+       }
+
+       return 0;
+}
+
 static int ov_connect(struct connman_provider *provider,
                struct connman_task *task, const char *if_name)
 {
-       const char *vpnhost, *cafile, *mtu, *certfile, *keyfile;
-       const char *proto, *port, *auth_user_pass;
-       const char *tls_remote, *cipher, *auth, *comp_lzo;
+       const char *option;
        int err, fd;
 
-       vpnhost = connman_provider_get_string(provider, "Host");
-       if (!vpnhost) {
+       option = connman_provider_get_string(provider, "Host");
+       if (option == NULL) {
                connman_error("Host not set; cannot enable VPN");
                return -EINVAL;
        }
 
-       cafile = connman_provider_get_string(provider, "OpenVPN.CACert");
-       certfile = connman_provider_get_string(provider, "OpenVPN.Cert");
-       keyfile = connman_provider_get_string(provider, "OpenVPN.Key");
-       mtu = connman_provider_get_string(provider, "VPN.MTU");
-       proto = connman_provider_get_string(provider, "OpenVPN.Proto");
-       port = connman_provider_get_string(provider, "OpenVPN.Port");
-       auth_user_pass = connman_provider_get_string(provider,
-                                                       "OpenVPN.AuthUserPass");
-       tls_remote = connman_provider_get_string(provider, "OpenVPN.TLSRemote");
-       cipher = connman_provider_get_string(provider, "OpenVPN.Cipher");
-       auth = connman_provider_get_string(provider, "OpenVPN.Auth");
-       comp_lzo = connman_provider_get_string(provider, "OpenVPN.CompLZO");
-
-       if (mtu != NULL)
-               connman_task_add_argument(task, "--mtu", (char *)mtu);
-
-       if (proto != NULL)
-               connman_task_add_argument(task, "--proto", (char *)proto);
-
-       if (port != NULL)
-               connman_task_add_argument(task, "--port", (char *)port);
-
-       if (auth_user_pass != NULL) {
-               connman_task_add_argument(task, "--auth-user-pass",
-                                               (char *)auth_user_pass);
-       }
-
-       if (tls_remote != NULL) {
-               connman_task_add_argument(task, "--tls-remote",
-                                               (char *)tls_remote);
-       }
-
-       if (cipher != NULL)
-               connman_task_add_argument(task, "--cipher", (char *)cipher);
-
-       if (auth != NULL)
-               connman_task_add_argument(task, "--auth", (char *)auth);
-
-       if (comp_lzo)
-               connman_task_add_argument(task, "--comp-lzo", (char *)comp_lzo);
+       task_append_config_data(provider, task);
 
        connman_task_add_argument(task, "--syslog", NULL);
 
@@ -320,7 +256,6 @@ static int ov_connect(struct connman_provider *provider,
        connman_task_add_argument(task, "--dev-type", "tun");
 
        connman_task_add_argument(task, "--tls-client", NULL);
-       connman_task_add_argument(task, "--remote", (char *)vpnhost);
        connman_task_add_argument(task, "--nobind", NULL);
        connman_task_add_argument(task, "--persist-key", NULL);
        connman_task_add_argument(task, "--persist-tun", NULL);
@@ -340,21 +275,6 @@ static int ov_connect(struct connman_provider *provider,
 
        connman_task_add_argument(task, "--client", NULL);
 
-       if (cafile) {
-               connman_task_add_argument(task, "--ca",
-                                               (char *)cafile);
-       }
-
-       if (certfile) {
-               connman_task_add_argument(task, "--cert",
-                                               (char *)certfile);
-       }
-
-       if (keyfile) {
-               connman_task_add_argument(task, "--key",
-                                               (char *)keyfile);
-       }
-
        fd = fileno(stderr);
        err = connman_task_run(task, vpn_died, provider,
                        NULL, &fd, &fd);
@@ -369,6 +289,7 @@ static int ov_connect(struct connman_provider *provider,
 static struct vpn_driver vpn_driver = {
        .notify = ov_notify,
        .connect        = ov_connect,
+       .save           = ov_save,
 };
 
 static int openvpn_init(void)