X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=plugins%2Fvpn.c;h=d708d1ffd25a223232fe77b6c737c01fad55cb38;hb=5479dad2b3b5f342f51ca6e7ec8a5a501820bd55;hp=038a833815e903a895623dff2cd351e4112f5572;hpb=76e97ed5506da5b88111a9b2a3cdcaa4f6d8c43d;p=platform%2Fupstream%2Fconnman.git diff --git a/plugins/vpn.c b/plugins/vpn.c old mode 100644 new mode 100755 index 038a833..d708d1f --- a/plugins/vpn.c +++ b/plugins/vpn.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2012 Intel Corporation. All rights reserved. + * Copyright (C) 2012-2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -71,8 +72,10 @@ struct connection_data { struct connman_provider *provider; int index; DBusPendingCall *call; - connman_bool_t connect_pending; + DBusPendingCall *disconnect_call; + bool connect_pending; struct config_create_data *cb_data; + char *service_ident; char *state; char *type; @@ -81,7 +84,8 @@ struct connection_data { char **host_ip; char *domain; char **nameservers; - gboolean immutable; + bool immutable; + bool default_route_set; GHashTable *server_routes; GHashTable *user_routes; @@ -91,6 +95,7 @@ struct connection_data { GResolv *resolv; guint resolv_id; + guint remove_resolv_id; }; static int set_string(struct connman_provider *provider, @@ -99,22 +104,22 @@ static int set_string(struct connman_provider *provider, struct connection_data *data; data = connman_provider_get_data(provider); - if (data == NULL) + if (!data) return -EINVAL; DBG("data %p provider %p key %s value %s", data, provider, key, value); - if (g_str_equal(key, "Type") == TRUE) { + if (g_str_equal(key, "Type")) { g_free(data->type); data->type = g_strdup(value); - } else if (g_str_equal(key, "Name") == TRUE) { + } else if (g_str_equal(key, "Name")) { g_free(data->name); data->name = g_strdup(value); - } else if (g_str_equal(key, "Host") == TRUE) { + } else if (g_str_equal(key, "Host")) { g_free(data->host); data->host = g_strdup(value); - } else if (g_str_equal(key, "VPN.Domain") == TRUE || - g_str_equal(key, "Domain") == TRUE) { + } else if (g_str_equal(key, "VPN.Domain") || + g_str_equal(key, "Domain")) { g_free(data->domain); data->domain = g_strdup(value); } else @@ -129,25 +134,27 @@ static const char *get_string(struct connman_provider *provider, struct connection_data *data; data = connman_provider_get_data(provider); - if (data == NULL) + if (!data) return NULL; DBG("data %p provider %p key %s", data, provider, key); - if (g_str_equal(key, "Type") == TRUE) + if (g_str_equal(key, "Type")) return data->type; - else if (g_str_equal(key, "Name") == TRUE) + else if (g_str_equal(key, "Name")) return data->name; - else if (g_str_equal(key, "Host") == TRUE) + else if (g_str_equal(key, "Host")) return data->host; - else if (g_str_equal(key, "HostIP") == TRUE) { - if (data->host_ip == NULL || - data->host_ip[0] == NULL) + else if (g_str_equal(key, "HostIP")) { + if (!data->host_ip || + !data->host_ip[0]) return data->host; else return data->host_ip[0]; - } else if (g_str_equal(key, "VPN.Domain") == TRUE) + } else if (g_str_equal(key, "VPN.Domain")) return data->domain; + else if (g_str_equal(key, "Transport")) + return data->service_ident; return g_hash_table_lookup(data->setting_strings, key); } @@ -160,7 +167,7 @@ static char *get_ident(const char *path) return NULL; pos = strrchr(path, '/'); - if (pos == NULL) + if (!pos) return NULL; return pos + 1; @@ -168,10 +175,15 @@ static char *get_ident(const char *path) static void cancel_host_resolv(struct connection_data *data) { - if (data->resolv_id != 0) + + if (data->remove_resolv_id) + g_source_remove(data->remove_resolv_id); + + if (data->resolv && data->resolv_id) g_resolv_cancel_lookup(data->resolv, data->resolv_id); data->resolv_id = 0; + data->remove_resolv_id = 0; g_resolv_unref(data->resolv); data->resolv = NULL; @@ -193,32 +205,34 @@ static void resolv_result(GResolvResultStatus status, DBG("status %d", status); - if (status == G_RESOLV_RESULT_STATUS_SUCCESS && results != NULL && - g_strv_length(results) > 0) + if (status == G_RESOLV_RESULT_STATUS_SUCCESS && results && + g_strv_length(results) > 0) { + g_strfreev(data->host_ip); data->host_ip = g_strdupv(results); + } /* * We cannot unref the resolver here as resolv struct is manipulated * by gresolv.c after we return from this callback. */ - g_timeout_add_seconds(0, remove_resolv, data); + data->remove_resolv_id = g_idle_add(remove_resolv, data); data->resolv_id = 0; } static void resolv_host_addr(struct connection_data *data) { - if (data->host == NULL) + if (!data->host) return; if (connman_inet_check_ipaddress(data->host) > 0) return; - if (data->host_ip != NULL) + if (data->host_ip) return; data->resolv = g_resolv_new(0); - if (data->resolv == NULL) { + if (!data->resolv) { DBG("Cannot resolv %s", data->host); return; } @@ -231,13 +245,13 @@ static void resolv_host_addr(struct connection_data *data) static void free_config_cb_data(struct config_create_data *cb_data) { - if (cb_data == NULL) + if (!cb_data) return; g_free(cb_data->path); cb_data->path = NULL; - if (cb_data->message != NULL) { + if (cb_data->message) { dbus_message_unref(cb_data->message); cb_data->message = NULL; } @@ -247,35 +261,44 @@ static void free_config_cb_data(struct config_create_data *cb_data) g_free(cb_data); } +static bool provider_is_connected(struct connection_data *data) +{ + return data && (g_str_equal(data->state, "ready") || + g_str_equal(data->state, "configuration")); +} + static void set_provider_state(struct connection_data *data) { enum connman_provider_state state = CONNMAN_PROVIDER_STATE_UNKNOWN; + bool connected; int err = 0; DBG("provider %p new state %s", data->provider, data->state); - if (g_str_equal(data->state, "ready") == TRUE) { + connected = provider_is_connected(data); + + if (g_str_equal(data->state, "ready")) { state = CONNMAN_PROVIDER_STATE_READY; goto set; - } else if (g_str_equal(data->state, "configuration") == TRUE) { + } else if (g_str_equal(data->state, "configuration")) { state = CONNMAN_PROVIDER_STATE_CONNECT; - } else if (g_str_equal(data->state, "idle") == TRUE) { + } else if (g_str_equal(data->state, "idle")) { state = CONNMAN_PROVIDER_STATE_IDLE; - } else if (g_str_equal(data->state, "disconnect") == TRUE) { + } else if (g_str_equal(data->state, "disconnect")) { err = ECONNREFUSED; state = CONNMAN_PROVIDER_STATE_DISCONNECT; goto set; - } else if (g_str_equal(data->state, "failure") == TRUE) { + } else if (g_str_equal(data->state, "failure")) { err = ECONNREFUSED; state = CONNMAN_PROVIDER_STATE_FAILURE; goto set; } connman_provider_set_state(data->provider, state); - return; + goto free; set: - if (data->cb_data != NULL) + if (data->cb_data) data->cb_data->callback(data->cb_data->message, err, data->ident); @@ -283,6 +306,12 @@ set: free_config_cb_data(data->cb_data); data->cb_data = NULL; + +free: + if (!connected) { + g_free(data->service_ident); + data->service_ident = NULL; + } } static int create_provider(struct connection_data *data, void *user_data) @@ -293,7 +322,7 @@ static int create_provider(struct connection_data *data, void *user_data) DBG("%s", data->path); data->provider = connman_provider_get(data->ident); - if (data->provider == NULL) + if (!data->provider) return -ENOMEM; DBG("provider %p name %s", data->provider, data->name); @@ -304,10 +333,10 @@ static int create_provider(struct connection_data *data, void *user_data) err = connman_provider_create_service(data->provider); if (err == 0) { connman_provider_set_immutable(data->provider, data->immutable); - if (g_str_equal(data->state, "ready") == TRUE) { + if (g_str_equal(data->state, "ready")) { connman_provider_set_index(data->provider, data->index); - if (data->ip != NULL) + if (data->ip) connman_provider_set_ipaddress(data->provider, data->ip); } @@ -333,7 +362,7 @@ static struct connection_data *create_connection_data(const char *path) struct connection_data *data; data = g_try_new0(struct connection_data, 1); - if (data == NULL) + if (!data) return NULL; DBG("path %s", path); @@ -375,19 +404,19 @@ static int extract_ip(DBusMessageIter *array, int family, dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); - if (g_str_equal(key, "Address") == TRUE) { + if (g_str_equal(key, "Address")) { dbus_message_iter_get_basic(&value, &address); DBG("address %s", address); - } else if (g_str_equal(key, "Netmask") == TRUE) { + } else if (g_str_equal(key, "Netmask")) { dbus_message_iter_get_basic(&value, &netmask); DBG("netmask %s", netmask); - } else if (g_str_equal(key, "PrefixLength") == TRUE) { + } else if (g_str_equal(key, "PrefixLength")) { dbus_message_iter_get_basic(&value, &netmask); DBG("prefix length %s", netmask); - } else if (g_str_equal(key, "Peer") == TRUE) { + } else if (g_str_equal(key, "Peer")) { dbus_message_iter_get_basic(&value, &peer); DBG("peer %s", peer); - } else if (g_str_equal(key, "Gateway") == TRUE) { + } else if (g_str_equal(key, "Gateway")) { dbus_message_iter_get_basic(&value, &gateway); DBG("gateway %s", gateway); } @@ -395,8 +424,9 @@ static int extract_ip(DBusMessageIter *array, int family, dbus_message_iter_next(&dict); } + connman_ipaddress_free(data->ip); data->ip = connman_ipaddress_alloc(family); - if (data->ip == NULL) + if (!data->ip) return -ENOMEM; switch (family) { @@ -414,6 +444,7 @@ static int extract_ip(DBusMessageIter *array, int family, } connman_ipaddress_set_peer(data->ip, peer); + connman_ipaddress_set_p2p(data->ip, true); return 0; } @@ -433,13 +464,13 @@ static int extract_nameservers(DBusMessageIter *array, dbus_message_iter_get_basic(&entry, &nameserver); nameservers = g_try_renew(char *, nameservers, i + 2); - if (nameservers == NULL) + if (!nameservers) return -ENOMEM; DBG("[%d] %s", i, nameserver); nameservers[i] = g_strdup(nameserver); - if (nameservers[i] == NULL) + if (!nameservers[i]) return -ENOMEM; nameservers[++i] = NULL; @@ -460,6 +491,9 @@ static int errorstr2val(const char *error) { if (g_strcmp0(error, CONNMAN_ERROR_INTERFACE ".AlreadyConnected") == 0) return -EISCONN; + if (g_strcmp0(error, CONNMAN_ERROR_INTERFACE ".OperationCanceled") == 0) + return -ECANCELED; + return -ECONNREFUSED; } @@ -470,30 +504,52 @@ static void connect_reply(DBusPendingCall *call, void *user_data) struct connection_data *data = user_data; struct config_create_data *cb_data = data->cb_data; - if (dbus_pending_call_get_completed(call) == FALSE) + DBG(""); + + if (!dbus_pending_call_get_completed(call)) { + connman_warn("vpn connect reply pending call incomplete"); + goto out; + } + + if (call != data->call) { + connman_error("invalid call %p to VPN connect_reply data %p " + " call %p ", call, data, data->call); + dbus_pending_call_unref(call); return; + } DBG("user_data %p path %s", user_data, cb_data ? cb_data->path : NULL); reply = dbus_pending_call_steal_reply(call); + if (!reply) + goto out; dbus_error_init(&error); - if (dbus_set_error_from_message(&error, reply) == TRUE) { + if (dbus_set_error_from_message(&error, reply)) { int err = errorstr2val(error.name); - if (err != -EINPROGRESS) { + + /* + * ECANCELED means that user has canceled authentication + * dialog. That's not really an error, it's part of a normal + * workflow. We also take it as a request to turn autoconnect + * off, in case if it was on. + */ + if (err == -ECANCELED) { + DBG("%s connect canceled", data->path); + connman_provider_set_autoconnect(data->provider, false); + } else if (err != -EINPROGRESS) { connman_error("Connect reply: %s (%s)", error.message, error.name); - dbus_error_free(&error); - DBG("data %p cb_data %p", data, cb_data); - if (cb_data != NULL) { + + if (cb_data) { cb_data->callback(cb_data->message, err, NULL); free_config_cb_data(cb_data); data->cb_data = NULL; } - goto done; } + dbus_error_free(&error); } @@ -503,46 +559,90 @@ static void connect_reply(DBusPendingCall *call, void *user_data) * state. */ -done: dbus_message_unref(reply); - dbus_pending_call_unref(call); +out: + dbus_pending_call_unref(data->call); + + data->call = NULL; + data->connect_pending = false; } -static int connect_provider(struct connection_data *data, void *user_data) +static int connect_provider(struct connection_data *data, void *user_data, + const char *dbus_sender) { DBusPendingCall *call; DBusMessage *message; struct config_create_data *cb_data = user_data; + struct connman_service *transport = connman_service_get_default(); - DBG("data %p user %p path %s", data, cb_data, data->path); + DBG("data %p user %p path %s sender %s", data, cb_data, data->path, + dbus_sender); - data->connect_pending = FALSE; + if (!transport) { + DBG("no default service, refusing to connect"); + return -EINVAL; + } + if (data->connect_pending && data->call) { + connman_info("connect already pending"); + return -EALREADY; + } + + /* We need to pass original dbus sender to connman-vpnd, + * use a Connect2 method for that if the original dbus sender is set. + * Connect method requires no parameter, Connect2 requires dbus sender + * name to be set. + */ message = dbus_message_new_method_call(VPN_SERVICE, data->path, VPN_CONNECTION_INTERFACE, - VPN_CONNECT); - if (message == NULL) + dbus_sender && *dbus_sender ? + VPN_CONNECT2 : VPN_CONNECT); + if (!message) return -ENOMEM; - if (dbus_connection_send_with_reply(connection, message, - &call, DBUS_TIMEOUT) == FALSE) { + if (dbus_sender && *dbus_sender) + dbus_message_append_args(message, DBUS_TYPE_STRING, + &dbus_sender, NULL); + else + dbus_sender = ""; + + if (!dbus_connection_send_with_reply(connection, message, + &call, DBUS_TIMEOUT)) { connman_error("Unable to call %s.%s()", - VPN_CONNECTION_INTERFACE, VPN_CONNECT); + VPN_CONNECTION_INTERFACE, dbus_sender && *dbus_sender ? + VPN_CONNECT2 : VPN_CONNECT); dbus_message_unref(message); return -EINVAL; } - if (call == NULL) { + if (!call) { dbus_message_unref(message); return -EINVAL; } - if (cb_data != NULL) { + if (data->call) { + dbus_pending_call_cancel(data->call); + dbus_pending_call_unref(data->call); + } + + data->call = call; + data->connect_pending = true; + + if (cb_data) { g_free(cb_data->path); cb_data->path = g_strdup(data->path); } + /* + * This is the service which (most likely) will be used + * as a transport for VPN connection. + */ + g_free(data->service_ident); + data->service_ident = + g_strdup(connman_service_get_identifier(transport)); + DBG("transport %s", data->service_ident); + dbus_pending_call_set_notify(call, connect_reply, data, NULL); dbus_message_unref(message); @@ -556,22 +656,22 @@ static void add_connection(const char *path, DBusMessageIter *properties, struct connection_data *data; int err; char *ident = get_ident(path); - connman_bool_t found = FALSE; + bool found = false; data = g_hash_table_lookup(vpn_connections, ident); - if (data != NULL) { + if (data) { /* * We might have a dummy connection struct here that * was created by configuration_create_reply() so in * that case just continue. */ - if (data->connect_pending == FALSE) + if (!data->connect_pending) return; - found = TRUE; + found = true; } else { data = create_connection_data(path); - if (data == NULL) + if (!data) return; } @@ -589,35 +689,39 @@ static void add_connection(const char *path, DBusMessageIter *properties, dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); - if (g_str_equal(key, "State") == TRUE) { + if (g_str_equal(key, "State")) { dbus_message_iter_get_basic(&value, &str); DBG("state %s -> %s", data->state, str); data->state = g_strdup(str); - } else if (g_str_equal(key, "IPv4") == TRUE) { + } else if (g_str_equal(key, "IPv4")) { extract_ip(&value, AF_INET, data); - } else if (g_str_equal(key, "IPv6") == TRUE) { + } else if (g_str_equal(key, "IPv6")) { extract_ip(&value, AF_INET6, data); - } else if (g_str_equal(key, "Name") == TRUE) { + } else if (g_str_equal(key, "Name")) { dbus_message_iter_get_basic(&value, &str); data->name = g_strdup(str); - } else if (g_str_equal(key, "Type") == TRUE) { + } else if (g_str_equal(key, "Type")) { dbus_message_iter_get_basic(&value, &str); data->type = g_strdup(str); - } else if (g_str_equal(key, "Immutable") == TRUE) { - dbus_message_iter_get_basic(&value, &data->immutable); - } else if (g_str_equal(key, "Host") == TRUE) { + } else if (g_str_equal(key, "Immutable")) { + dbus_bool_t immutable; + + dbus_message_iter_get_basic(&value, &immutable); + data->immutable = immutable; + } else if (g_str_equal(key, "Host")) { dbus_message_iter_get_basic(&value, &str); data->host = g_strdup(str); - } else if (g_str_equal(key, "Domain") == TRUE) { + } else if (g_str_equal(key, "Domain")) { dbus_message_iter_get_basic(&value, &str); + g_free(data->domain); data->domain = g_strdup(str); - } else if (g_str_equal(key, "Nameservers") == TRUE) { + } else if (g_str_equal(key, "Nameservers")) { extract_nameservers(&value, data); - } else if (g_str_equal(key, "Index") == TRUE) { + } else if (g_str_equal(key, "Index")) { dbus_message_iter_get_basic(&value, &data->index); - } else if (g_str_equal(key, "ServerRoutes") == TRUE) { + } else if (g_str_equal(key, "ServerRoutes")) { /* Ignored */ - } else if (g_str_equal(key, "UserRoutes") == TRUE) { + } else if (g_str_equal(key, "UserRoutes")) { /* Ignored */ } else { if (dbus_message_iter_get_arg_type(&value) == @@ -633,7 +737,7 @@ static void add_connection(const char *path, DBusMessageIter *properties, dbus_message_iter_next(properties); } - if (found == FALSE) + if (!found) g_hash_table_insert(vpn_connections, g_strdup(data->ident), data); @@ -643,8 +747,23 @@ static void add_connection(const char *path, DBusMessageIter *properties, resolv_host_addr(data); - if (data->connect_pending == TRUE) - connect_provider(data, data->cb_data); + if (data->nameservers) + connman_provider_set_nameservers(data->provider, + data->nameservers); + + if (data->domain) + connman_provider_set_domain(data->provider, + data->domain); + + if (data->connect_pending) { + const char *dbus_sender = NULL; + + if (data->cb_data && data->cb_data->message) { + dbus_sender = + dbus_message_get_sender(data->cb_data->message); + } + connect_provider(data, data->cb_data, dbus_sender); + } return; @@ -668,29 +787,33 @@ static void get_connections_reply(DBusPendingCall *call, void *user_data) DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_STRUCT_END_CHAR_AS_STRING; - if (dbus_pending_call_get_completed(call) == FALSE) - return; - DBG(""); + if (!dbus_pending_call_get_completed(call)) { + connman_warn("get connections reply pending call incomplete"); + goto out; + } + reply = dbus_pending_call_steal_reply(call); + if (!reply) + goto out; dbus_error_init(&error); - if (dbus_set_error_from_message(&error, reply) == TRUE) { + if (dbus_set_error_from_message(&error, reply)) { connman_error("%s", error.message); dbus_error_free(&error); goto done; } - if (dbus_message_has_signature(reply, signature) == FALSE) { + if (!dbus_message_has_signature(reply, signature)) { connman_error("vpnd signature \"%s\" does not match " "expected \"%s\"", dbus_message_get_signature(reply), signature); goto done; } - if (dbus_message_iter_init(reply, &array) == FALSE) + if (!dbus_message_iter_init(reply, &array)) goto done; dbus_message_iter_recurse(&array, &dict); @@ -713,6 +836,7 @@ static void get_connections_reply(DBusPendingCall *call, void *user_data) done: dbus_message_unref(reply); +out: dbus_pending_call_unref(call); } @@ -726,18 +850,18 @@ static int get_connections(void *user_data) message = dbus_message_new_method_call(VPN_SERVICE, "/", VPN_MANAGER_INTERFACE, GET_CONNECTIONS); - if (message == NULL) + if (!message) return -ENOMEM; - if (dbus_connection_send_with_reply(connection, message, - &call, DBUS_TIMEOUT) == FALSE) { + if (!dbus_connection_send_with_reply(connection, message, + &call, DBUS_TIMEOUT)) { connman_error("Unable to call %s.%s()", VPN_MANAGER_INTERFACE, GET_CONNECTIONS); dbus_message_unref(message); return -EINVAL; } - if (call == NULL) { + if (!call) { dbus_message_unref(message); return -EINVAL; } @@ -760,22 +884,26 @@ static void remove_connection_reply(DBusPendingCall *call, void *user_data) DBusMessage *reply; DBusError error; - if (dbus_pending_call_get_completed(call) == FALSE) - return; - DBG(""); + if (!dbus_pending_call_get_completed(call)) { + connman_warn("remove connection reply pending call incomplete"); + goto out; + } + reply = dbus_pending_call_steal_reply(call); + if (!reply) + goto out; dbus_error_init(&error); - if (dbus_set_error_from_message(&error, reply) == TRUE) { + if (dbus_set_error_from_message(&error, reply)) { /* * If the returned error is NotFound, it means that we * have actually removed the provider in vpnd already. */ - if (dbus_error_has_name(&error, CONNMAN_ERROR_INTERFACE - ".NotFound") == FALSE) + if (!dbus_error_has_name(&error, + CONNMAN_ERROR_INTERFACE".NotFound")) connman_error("%s", error.message); dbus_error_free(&error); @@ -783,6 +911,7 @@ static void remove_connection_reply(DBusPendingCall *call, void *user_data) dbus_message_unref(reply); +out: dbus_pending_call_unref(call); } @@ -796,7 +925,7 @@ static int provider_remove(struct connman_provider *provider) DBG("provider %p data %p", provider, data); - if (data == NULL) { + if (!data) { /* * This means the provider is already removed, * just ignore the dbus in this case. @@ -815,21 +944,21 @@ static int provider_remove(struct connman_provider *provider) message = dbus_message_new_method_call(VPN_SERVICE, "/", VPN_MANAGER_INTERFACE, VPN_REMOVE); - if (message == NULL) + if (!message) return -ENOMEM; dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &data->path, NULL); - if (dbus_connection_send_with_reply(connection, message, - &call, DBUS_TIMEOUT) == FALSE) { + if (!dbus_connection_send_with_reply(connection, message, + &call, DBUS_TIMEOUT)) { connman_error("Unable to call %s.%s()", VPN_MANAGER_INTERFACE, VPN_REMOVE); dbus_message_unref(message); return -EINVAL; } - if (call == NULL) { + if (!call) { dbus_message_unref(message); return -EINVAL; } @@ -842,32 +971,31 @@ static int provider_remove(struct connman_provider *provider) return 0; } -static int provider_connect(struct connman_provider *provider) +static int provider_connect(struct connman_provider *provider, + const char *dbus_sender) { struct connection_data *data; data = connman_provider_get_data(provider); - if (data == NULL) + if (!data) return -EINVAL; - return connect_provider(data, NULL); + return connect_provider(data, NULL, dbus_sender); } static void disconnect_reply(DBusPendingCall *call, void *user_data) { + struct connection_data *data = user_data; DBusMessage *reply; DBusError error; - if (dbus_pending_call_get_completed(call) == FALSE) - return; - DBG("user %p", user_data); reply = dbus_pending_call_steal_reply(call); dbus_error_init(&error); - if (dbus_set_error_from_message(&error, reply) == TRUE) { + if (dbus_set_error_from_message(&error, reply)) { connman_error("%s", error.message); dbus_error_free(&error); goto done; @@ -875,67 +1003,76 @@ static void disconnect_reply(DBusPendingCall *call, void *user_data) done: dbus_message_unref(reply); - dbus_pending_call_unref(call); + data->disconnect_call = NULL; } static int disconnect_provider(struct connection_data *data) { - DBusPendingCall *call; + bool sent; DBusMessage *message; DBG("data %p path %s", data, data->path); + if (data->disconnect_call) { + DBG("already disconnecting"); + return -EINVAL; + } + message = dbus_message_new_method_call(VPN_SERVICE, data->path, VPN_CONNECTION_INTERFACE, VPN_DISCONNECT); - if (message == NULL) + if (!message) return -ENOMEM; - if (dbus_connection_send_with_reply(connection, message, - &call, DBUS_TIMEOUT) == FALSE) { + sent = dbus_connection_send_with_reply(connection, message, + &data->disconnect_call, DBUS_TIMEOUT); + dbus_message_unref(message); + + if (!sent || !data->disconnect_call) { connman_error("Unable to call %s.%s()", VPN_CONNECTION_INTERFACE, VPN_DISCONNECT); - dbus_message_unref(message); return -EINVAL; } - if (call == NULL) { - dbus_message_unref(message); - return -EINVAL; - } - - dbus_pending_call_set_notify(call, disconnect_reply, NULL, NULL); + dbus_pending_call_set_notify(data->disconnect_call, disconnect_reply, + data, NULL); - dbus_message_unref(message); + data->default_route_set = false; connman_provider_set_state(data->provider, CONNMAN_PROVIDER_STATE_DISCONNECT); - /* - * We return 0 here instead of -EINPROGRESS because - * __connman_service_disconnect() needs to return something - * to gdbus so that gdbus will not call Disconnect() more - * than once. This way we do not need to pass the dbus reply - * message around the code. - */ - return 0; + + g_free(data->service_ident); + data->service_ident = NULL; + + return -EINPROGRESS; } static int provider_disconnect(struct connman_provider *provider) { + int err = 0; + struct connection_data *data; DBG("provider %p", provider); data = connman_provider_get_data(provider); - if (data == NULL) + if (!data) return -EINVAL; - if (g_str_equal(data->state, "ready") == TRUE || - g_str_equal(data->state, "configuration") == TRUE) - return disconnect_provider(data); + if (provider_is_connected(data)) + err = disconnect_provider(data); - return 0; + if (data->call) { + dbus_pending_call_cancel(data->call); + dbus_pending_call_unref(data->call); + data->call = NULL; + } + + data->connect_pending = false; + + return err; } static void configuration_create_reply(DBusPendingCall *call, void *user_data) @@ -949,29 +1086,33 @@ static void configuration_create_reply(DBusPendingCall *call, void *user_data) struct connection_data *data; struct config_create_data *cb_data = user_data; - if (dbus_pending_call_get_completed(call) == FALSE) - return; - DBG("user %p", cb_data); + if (!dbus_pending_call_get_completed(call)) { + connman_warn("configuration create reply pending call incomplete"); + goto out; + } + reply = dbus_pending_call_steal_reply(call); + if (!reply) + goto out; dbus_error_init(&error); - if (dbus_set_error_from_message(&error, reply) == TRUE) { + if (dbus_set_error_from_message(&error, reply)) { connman_error("dbus error: %s", error.message); dbus_error_free(&error); goto done; } - if (dbus_message_has_signature(reply, signature) == FALSE) { + if (!dbus_message_has_signature(reply, signature)) { connman_error("vpn configuration signature \"%s\" does not " "match expected \"%s\"", dbus_message_get_signature(reply), signature); goto done; } - if (dbus_message_iter_init(reply, &iter) == FALSE) + if (!dbus_message_iter_init(reply, &iter)) goto done; dbus_message_iter_get_basic(&iter, &path); @@ -982,16 +1123,16 @@ static void configuration_create_reply(DBusPendingCall *call, void *user_data) ident = get_ident(path); data = g_hash_table_lookup(vpn_connections, ident); - if (data == NULL) { + if (!data) { /* * Someone removed the data. We cannot really continue. */ DBG("Pending data not found for %s, cannot continue!", ident); } else { data->call = NULL; - data->connect_pending = TRUE; + data->connect_pending = true; - if (data->cb_data == NULL) + if (!data->cb_data) data->cb_data = cb_data; else DBG("Connection callback data already in use!"); @@ -1008,6 +1149,7 @@ static void configuration_create_reply(DBusPendingCall *call, void *user_data) done: dbus_message_unref(reply); +out: dbus_pending_call_unref(call); } @@ -1033,37 +1175,37 @@ static struct vpn_route *parse_user_route(const char *user_route) int family = PF_UNSPEC; char **elems = g_strsplit(user_route, "/", 0); - if (elems == NULL) + if (!elems) return NULL; network = elems[0]; - if (network == NULL || *network == '\0') { + if (!network || *network == '\0') { DBG("no network/netmask set"); goto out; } netmask = elems[1]; - if (netmask != NULL && *netmask == '\0') { + if (netmask && *netmask == '\0') { DBG("no netmask set"); goto out; } - if (g_strrstr(network, ":") != NULL) + if (g_strrstr(network, ":")) family = AF_INET6; - else if (g_strrstr(network, ".") != NULL) { + else if (g_strrstr(network, ".")) { family = AF_INET; - if (g_strrstr(netmask, ".") == NULL) { + if (!g_strrstr(netmask, ".")) { /* We have netmask length */ in_addr_t addr; struct in_addr netmask_in; unsigned char prefix_len = 32; - if (netmask != NULL) { + if (netmask) { char *ptr; long int value = strtol(netmask, &ptr, 10); if (ptr != netmask && *ptr == '\0' && - value <= 32) + value && value <= 32) prefix_len = value; } @@ -1076,7 +1218,7 @@ static struct vpn_route *parse_user_route(const char *user_route) } route = g_try_new(struct vpn_route, 1); - if (route == NULL) + if (!route) goto out; route->network = g_strdup(network); @@ -1103,7 +1245,7 @@ static GSList *get_user_networks(DBusMessageIter *array) dbus_message_iter_get_basic(&entry, &val); route = parse_user_route(val); - if (route != NULL) + if (route) list = g_slist_prepend(list, route); dbus_message_iter_next(&entry); @@ -1120,7 +1262,7 @@ static void append_route(DBusMessageIter *iter, void *user_data) connman_dbus_dict_open(iter, &item); - if (route == NULL) + if (!route) goto empty_dict; if (route->family == AF_INET) @@ -1132,15 +1274,15 @@ static void append_route(DBusMessageIter *iter, void *user_data) connman_dbus_dict_append_basic(&item, "ProtocolFamily", DBUS_TYPE_INT32, &family); - if (route->network != NULL) + if (route->network) connman_dbus_dict_append_basic(&item, "Network", DBUS_TYPE_STRING, &route->network); - if (route->netmask != NULL) + if (route->netmask) connman_dbus_dict_append_basic(&item, "Netmask", DBUS_TYPE_STRING, &route->netmask); - if (route->gateway != NULL) + if (route->gateway) connman_dbus_dict_append_basic(&item, "Gateway", DBUS_TYPE_STRING, &route->gateway); @@ -1154,7 +1296,7 @@ static void append_routes(DBusMessageIter *iter, void *user_data) DBG("routes %p", routes); - for (list = routes; list != NULL; list = g_slist_next(list)) { + for (list = routes; list; list = g_slist_next(list)) { DBusMessageIter dict; struct vpn_route *route = list->data; @@ -1210,24 +1352,23 @@ static int create_configuration(DBusMessage *msg, connection_ready_cb callback) case DBUS_TYPE_STRING: dbus_message_iter_get_basic(&value, &item_value); - if (g_str_equal(key, "Type") == TRUE) { + if (g_str_equal(key, "Type")) type = (const char *)item_value; - } else if (g_str_equal(key, "Name") == TRUE) { + else if (g_str_equal(key, "Name")) name = (const char *)item_value; - } else if (g_str_equal(key, "Host") == TRUE) { + else if (g_str_equal(key, "Host")) host = (const char *)item_value; - } else if (g_str_equal(key, "VPN.Domain") == TRUE) { + else if (g_str_equal(key, "VPN.Domain")) domain = (const char *)item_value; - } DBG("%s %s", key, (char *)item_value); - if (item_value != NULL) + if (item_value) connman_dbus_dict_append_basic(&new_dict, key, value_type, &item_value); break; case DBUS_TYPE_ARRAY: - if (g_str_equal(key, "Networks") == TRUE) { + if (g_str_equal(key, "Networks")) { networks = get_user_networks(&value); connman_dbus_dict_append_array(&new_dict, "UserRoutes", @@ -1246,12 +1387,12 @@ static int create_configuration(DBusMessage *msg, connection_ready_cb callback) DBG("VPN type %s name %s host %s domain %s networks %p", type, name, host, domain, networks); - if (host == NULL || domain == NULL) { + if (!host || !domain) { err = -EINVAL; goto done; } - if (type == NULL || name == NULL) { + if (!type || !name) { err = -EOPNOTSUPP; goto done; } @@ -1262,8 +1403,8 @@ static int create_configuration(DBusMessage *msg, connection_ready_cb callback) DBG("ident %s", ident); data = g_hash_table_lookup(vpn_connections, ident); - if (data != NULL) { - if (data->call != NULL || data->cb_data != NULL) { + if (data) { + if (data->call || data->cb_data) { DBG("create configuration call already pending"); err = -EINPROGRESS; goto done; @@ -1274,7 +1415,7 @@ static int create_configuration(DBusMessage *msg, connection_ready_cb callback) data = create_connection_data(path); g_free(path); - if (data == NULL) { + if (!data) { err = -ENOMEM; goto done; } @@ -1295,7 +1436,7 @@ static int create_configuration(DBusMessage *msg, connection_ready_cb callback) dbus_message_set_member(new_msg, "Create"); user_data = g_try_new0(struct config_create_data, 1); - if (user_data == NULL) { + if (!user_data) { err = -ENOMEM; goto done; } @@ -1308,7 +1449,7 @@ static int create_configuration(DBusMessage *msg, connection_ready_cb callback) result = dbus_connection_send_with_reply(connection, new_msg, &call, DBUS_TIMEOUT); - if (result == FALSE || call == NULL) { + if (!result || !call) { err = -EIO; goto done; } @@ -1318,29 +1459,29 @@ static int create_configuration(DBusMessage *msg, connection_ready_cb callback) data->call = call; done: - if (new_msg != NULL) + if (new_msg) dbus_message_unref(new_msg); - if (networks != NULL) + if (networks) g_slist_free_full(networks, destroy_route); g_free(me); return err; } -static connman_bool_t check_host(char **hosts, char *host) +static bool check_host(char **hosts, char *host) { int i; - if (hosts == NULL) - return FALSE; + if (!hosts) + return false; - for (i = 0; hosts[i] != NULL; i++) { + for (i = 0; hosts[i]; i++) { if (g_strcmp0(hosts[i], host) == 0) - return TRUE; + return true; } - return FALSE; + return false; } static void set_route(struct connection_data *data, struct vpn_route *route) @@ -1350,12 +1491,23 @@ static void set_route(struct connection_data *data, struct vpn_route *route) * VPN server, then we must discard that because the * server cannot be contacted via VPN tunnel. */ - if (check_host(data->host_ip, route->network) == TRUE) { + if (check_host(data->host_ip, route->network)) { DBG("Discarding VPN route to %s via %s at index %d", route->network, route->gateway, data->index); return; } + DBG("set route provider %p %s/%s/%s", data->provider, + route->network, route->gateway, + route->netmask); + + /* Do not add default route for split routed VPNs.*/ + if (connman_provider_is_split_routing(data->provider) && + connman_inet_is_default_route(route->family, + route->network, route->gateway, + route->netmask)) + return; + if (route->family == AF_INET6) { unsigned char prefix_len = atoi(route->netmask); @@ -1368,6 +1520,126 @@ static void set_route(struct connection_data *data, struct vpn_route *route) route->gateway, route->netmask); } + + if (connman_inet_is_default_route(route->family, route->network, + route->gateway, route->netmask)) + data->default_route_set = true; +} + +static int save_route(GHashTable *routes, int family, const char *network, + const char *netmask, const char *gateway); + +static int add_network_route(struct connection_data *data) +{ + struct vpn_route rt = { 0, }; + int err; + + if (!data) + return -EINVAL; + + rt.family = connman_provider_get_family(data->provider); + switch (rt.family) { + case PF_INET: + err = connman_inet_get_route_addresses(data->index, + &rt.network, &rt.netmask, &rt.gateway); + break; + case PF_INET6: + err = connman_inet_ipv6_get_route_addresses(data->index, + &rt.network, &rt.netmask, &rt.gateway); + break; + default: + connman_error("invalid protocol family %d", rt.family); + return -EINVAL; + } + + DBG("network %s gateway %s netmask %s for provider %p", + rt.network, rt.gateway, rt.netmask, + data->provider); + + if (err) { + connman_error("cannot get network/gateway/netmask for %p", + data->provider); + goto out; + } + + err = save_route(data->server_routes, rt.family, rt.network, rt.netmask, + rt.gateway); + if (err) { + connman_warn("failed to add network route for provider" + "%p", data->provider); + goto out; + } + + set_route(data, &rt); + +out: + g_free(rt.network); + g_free(rt.netmask); + g_free(rt.gateway); + + return 0; +} + +static bool is_valid_route_table(struct connman_provider *provider, + GHashTable *table) +{ + GHashTableIter iter; + gpointer value, key; + struct vpn_route *route; + size_t table_size; + + if (!table) + return false; + + table_size = g_hash_table_size(table); + + /* Non-split routed may have only the default route */ + if (table_size > 0 && !connman_provider_is_split_routing(provider)) + return true; + + /* Split routed has more than the default route */ + if (table_size > 1) + return true; + + /* + * Only one route for split routed VPN, which should not be the + * default route. + */ + g_hash_table_iter_init(&iter, table); + if (!g_hash_table_iter_next(&iter, &key, &value)) /* First and only */ + return false; + + route = value; + if (!route) + return false; + + DBG("check route %d %s/%s/%s", route->family, route->network, + route->gateway, route->netmask); + + if (!connman_inet_is_default_route(route->family, route->network, + route->gateway, route->netmask)) + return true; + + return false; +} + +static bool check_routes(struct connman_provider *provider) +{ + struct connection_data *data;; + + DBG("provider %p", provider); + + data = connman_provider_get_data(provider); + if (!data) + return false; + + if (is_valid_route_table(provider, data->user_routes)) + return true; + + if (is_valid_route_table(provider, data->server_routes)) + return true; + + return false; } static int set_routes(struct connman_provider *provider, @@ -1380,14 +1652,14 @@ static int set_routes(struct connman_provider *provider, DBG("provider %p", provider); data = connman_provider_get_data(provider); - if (data == NULL) + if (!data) return -EINVAL; if (type == CONNMAN_PROVIDER_ROUTE_ALL || type == CONNMAN_PROVIDER_ROUTE_USER) { g_hash_table_iter_init(&iter, data->user_routes); - while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) + while (g_hash_table_iter_next(&iter, &key, &value)) set_route(data, value); } @@ -1395,32 +1667,34 @@ static int set_routes(struct connman_provider *provider, type == CONNMAN_PROVIDER_ROUTE_SERVER) { g_hash_table_iter_init(&iter, data->server_routes); - while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) + while (g_hash_table_iter_next(&iter, &key, &value)) set_route(data, value); } - return 0; -} + /* If non-split routed VPN does not have a default route, add it */ + if (!connman_provider_is_split_routing(provider) && + !data->default_route_set) { + int family = connman_provider_get_family(provider); + const char *ipaddr_any = family == AF_INET6 ? + "::" : "0.0.0.0"; + struct vpn_route def_route = {family, (char*) ipaddr_any, + (char*) ipaddr_any, NULL}; -static connman_bool_t check_routes(struct connman_provider *provider) -{ - struct connection_data *data; - - DBG("provider %p", provider); - - data = connman_provider_get_data(provider); - if (data == NULL) - return FALSE; - - if (data->user_routes != NULL && - g_hash_table_size(data->user_routes) > 0) - return TRUE; + set_route(data, &def_route); + } - if (data->server_routes != NULL && - g_hash_table_size(data->server_routes) > 0) - return TRUE; + /* Split routed VPN must have at least one route to the network */ + if (connman_provider_is_split_routing(provider) && + !check_routes(provider)) { + int err = add_network_route(data); + if (err) { + connman_warn("cannot add network route provider %p", + provider); + return err; + } + } - return FALSE; + return 0; } static struct connman_provider_driver provider_driver = { @@ -1441,17 +1715,11 @@ static void destroy_provider(struct connection_data *data) { DBG("data %p", data); - if (g_str_equal(data->state, "ready") == TRUE || - g_str_equal(data->state, "configuration") == TRUE) + if (provider_is_connected(data)) connman_provider_disconnect(data->provider); - if (data->call != NULL) - dbus_pending_call_cancel(data->call); - connman_provider_set_data(data->provider, NULL); - connman_provider_remove(data->provider); - data->provider = NULL; } @@ -1461,15 +1729,27 @@ static void connection_destroy(gpointer hash_data) DBG("data %p", data); - if (data->provider != NULL) + if (data->provider) destroy_provider(data); + if (data->call) { + dbus_pending_call_cancel(data->call); + dbus_pending_call_unref(data->call); + } + + if (data->disconnect_call) { + dbus_pending_call_cancel(data->disconnect_call); + dbus_pending_call_unref(data->disconnect_call); + } + + g_free(data->service_ident); g_free(data->path); g_free(data->ident); g_free(data->state); g_free(data->type); g_free(data->name); g_free(data->host); + g_strfreev(data->host_ip); g_free(data->domain); g_hash_table_destroy(data->server_routes); g_hash_table_destroy(data->user_routes); @@ -1510,7 +1790,7 @@ static gboolean connection_removed(DBusConnection *conn, DBusMessage *message, const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING; struct connection_data *data; - if (dbus_message_has_signature(message, signature) == FALSE) { + if (!dbus_message_has_signature(message, signature)) { connman_error("vpn removed signature \"%s\" does not match " "expected \"%s\"", dbus_message_get_signature(message), signature); @@ -1521,7 +1801,7 @@ static gboolean connection_removed(DBusConnection *conn, DBusMessage *message, DBUS_TYPE_INVALID); data = g_hash_table_lookup(vpn_connections, get_ident(path)); - if (data != NULL) + if (data) remove_connection(conn, path); return TRUE; @@ -1539,7 +1819,7 @@ static gboolean connection_added(DBusConnection *conn, DBusMessage *message, DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING; - if (dbus_message_has_signature(message, signature) == FALSE) { + if (!dbus_message_has_signature(message, signature)) { connman_error("vpn ConnectionAdded signature \"%s\" does not " "match expected \"%s\"", dbus_message_get_signature(message), signature); @@ -1548,7 +1828,7 @@ static gboolean connection_added(DBusConnection *conn, DBusMessage *message, DBG(""); - if (dbus_message_iter_init(message, &iter) == FALSE) + if (!dbus_message_iter_init(message, &iter)) return TRUE; dbus_message_iter_get_basic(&iter, &path); @@ -1570,9 +1850,9 @@ static int save_route(GHashTable *routes, int family, const char *network, DBG("family %d network %s netmask %s", family, network, netmask); route = g_hash_table_lookup(routes, key); - if (route == NULL) { + if (!route) { route = g_try_new0(struct vpn_route, 1); - if (route == NULL) { + if (!route) { connman_error("out of memory"); return -ENOMEM; } @@ -1583,12 +1863,52 @@ static int save_route(GHashTable *routes, int family, const char *network, route->gateway = g_strdup(gateway); g_hash_table_replace(routes, key, route); - } else + } else { g_free(key); + return -EALREADY; + } return 0; } +static void change_provider_split_routing(struct connman_provider *provider, + bool split_routing) +{ + struct connection_data *data; + int err; + + if (!provider) + return; + + if (connman_provider_is_split_routing(provider) == split_routing) + return; + + data = connman_provider_get_data(provider); + if (split_routing && data && provider_is_connected(data) && + !check_routes(provider)) { + err = add_network_route(data); + if (err) { + connman_warn("cannot add network route provider %p", + provider); + return; + } + } + + err = connman_provider_set_split_routing(provider, split_routing); + switch (err) { + case 0: + /* fall through */ + case -EALREADY: + break; + case -EINVAL: + /* fall through */ + case -EOPNOTSUPP: + connman_warn("cannot change split routing %d", err); + default: + break; + } +} + static int read_route_dict(GHashTable *routes, DBusMessageIter *dicts) { DBusMessageIter dict; @@ -1611,7 +1931,7 @@ static int read_route_dict(GHashTable *routes, DBusMessageIter *dicts) dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); - if (g_str_equal(key, "ProtocolFamily") == TRUE) { + if (g_str_equal(key, "ProtocolFamily")) { int pf; dbus_message_iter_get_basic(&value, &pf); switch (pf) { @@ -1623,13 +1943,13 @@ static int read_route_dict(GHashTable *routes, DBusMessageIter *dicts) break; } DBG("family %d", family); - } else if (g_str_equal(key, "Netmask") == TRUE) { + } else if (g_str_equal(key, "Netmask")) { dbus_message_iter_get_basic(&value, &netmask); DBG("netmask %s", netmask); - } else if (g_str_equal(key, "Network") == TRUE) { + } else if (g_str_equal(key, "Network")) { dbus_message_iter_get_basic(&value, &network); DBG("host %s", network); - } else if (g_str_equal(key, "Gateway") == TRUE) { + } else if (g_str_equal(key, "Gateway")) { dbus_message_iter_get_basic(&value, &gateway); DBG("gateway %s", gateway); } @@ -1637,7 +1957,7 @@ static int read_route_dict(GHashTable *routes, DBusMessageIter *dicts) dbus_message_iter_next(&dict); } - if (netmask == NULL || network == NULL || gateway == NULL) { + if (!netmask || !network || !gateway) { DBG("Value missing."); return -EINVAL; } @@ -1689,14 +2009,14 @@ static gboolean property_changed(DBusConnection *conn, const char *path = dbus_message_get_path(message); struct connection_data *data = NULL; DBusMessageIter iter, value; - connman_bool_t ip_set = FALSE; + bool ip_set = false; int err; char *str; const char *key; const char *signature = DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING; - if (dbus_message_has_signature(message, signature) == FALSE) { + if (!dbus_message_has_signature(message, signature)) { connman_error("vpn property signature \"%s\" does not match " "expected \"%s\"", dbus_message_get_signature(message), signature); @@ -1704,10 +2024,10 @@ static gboolean property_changed(DBusConnection *conn, } data = g_hash_table_lookup(vpn_connections, get_ident(path)); - if (data == NULL) + if (!data) return TRUE; - if (dbus_message_iter_init(message, &iter) == FALSE) + if (!dbus_message_iter_init(message, &iter)) return TRUE; dbus_message_iter_get_basic(&iter, &key); @@ -1717,28 +2037,28 @@ static gboolean property_changed(DBusConnection *conn, DBG("key %s", key); - if (g_str_equal(key, "State") == TRUE) { + if (g_str_equal(key, "State")) { dbus_message_iter_get_basic(&value, &str); DBG("%s %s -> %s", data->path, data->state, str); - if (g_str_equal(data->state, str) == TRUE) + if (g_str_equal(data->state, str)) return TRUE; g_free(data->state); data->state = g_strdup(str); set_provider_state(data); - } else if (g_str_equal(key, "Index") == TRUE) { + } else if (g_str_equal(key, "Index")) { dbus_message_iter_get_basic(&value, &data->index); connman_provider_set_index(data->provider, data->index); - } else if (g_str_equal(key, "IPv4") == TRUE) { + } else if (g_str_equal(key, "IPv4")) { err = extract_ip(&value, AF_INET, data); - ip_set = TRUE; - } else if (g_str_equal(key, "IPv6") == TRUE) { + ip_set = true; + } else if (g_str_equal(key, "IPv6")) { err = extract_ip(&value, AF_INET6, data); - ip_set = TRUE; - } else if (g_str_equal(key, "ServerRoutes") == TRUE) { + ip_set = true; + } else if (g_str_equal(key, "ServerRoutes")) { err = routes_changed(&value, data->server_routes); /* * Note that the vpnd will delay the route sending a bit @@ -1750,16 +2070,28 @@ static gboolean property_changed(DBusConnection *conn, if (err == 0) set_routes(data->provider, CONNMAN_PROVIDER_ROUTE_SERVER); - } else if (g_str_equal(key, "UserRoutes") == TRUE) { + } else if (g_str_equal(key, "UserRoutes")) { err = routes_changed(&value, data->user_routes); if (err == 0) set_routes(data->provider, CONNMAN_PROVIDER_ROUTE_USER); - } else if (g_str_equal(key, "Nameservers") == TRUE) { - extract_nameservers(&value, data); + } else if (g_str_equal(key, "Nameservers")) { + if (extract_nameservers(&value, data) == 0 && + data->nameservers) + connman_provider_set_nameservers(data->provider, + data->nameservers); + } else if (g_str_equal(key, "Domain")) { + dbus_message_iter_get_basic(&value, &str); + g_free(data->domain); + data->domain = g_strdup(str); + connman_provider_set_domain(data->provider, data->domain); + } else if (g_str_equal(key, "SplitRouting")) { + dbus_bool_t split_routing; + dbus_message_iter_get_basic(&value, &split_routing); + change_provider_split_routing(data->provider, split_routing); } - if (ip_set == TRUE && err == 0) { + if (ip_set && err == 0) { err = connman_provider_set_ipaddress(data->provider, data->ip); if (err < 0) DBG("setting provider IP address failed (%s/%d)", @@ -1769,12 +2101,131 @@ static gboolean property_changed(DBusConnection *conn, return TRUE; } +static int vpn_find_online_transport_cb(struct connman_service *service, + void *user_data) +{ + if (connman_service_get_type(service) != CONNMAN_SERVICE_TYPE_VPN) { + switch (connman_service_get_state(service)) { + case CONNMAN_SERVICE_STATE_ONLINE: + *((struct connman_service**)user_data) = service; + return 1; + default: + break; + } + } + + return 0; +} + +static struct connman_service *vpn_find_online_transport() +{ + struct connman_service *service = NULL; + + connman_service_iterate_services(vpn_find_online_transport_cb, + &service); + return service; +} + +static bool vpn_is_valid_transport(struct connman_service *transport) +{ + if (transport) { + struct connman_service *online; + + switch (connman_service_get_state(transport)) { + case CONNMAN_SERVICE_STATE_READY: + online = vpn_find_online_transport(); + + /* Stay connected if there are no online services */ + if (!online) + return true; + + DBG("%s is ready, %s is online, disconnecting", + connman_service_get_identifier(transport), + connman_service_get_identifier(online)); + break; + + case CONNMAN_SERVICE_STATE_ONLINE: + online = vpn_find_online_transport(); + + /* Check if our transport is still the default */ + if (online == transport) + return true; + + DBG("%s is replaced by %s as default, disconnecting", + connman_service_get_identifier(transport), + connman_service_get_identifier(online)); + break; + + default: + break; + } + } else { + DBG("transport gone"); + } + + return false; +} + +static void vpn_disconnect_check_provider(struct connection_data *data) +{ + if (provider_is_connected(data)) { + /* With NULL service ident NULL is returned immediately */ + struct connman_service *service = + connman_service_lookup_from_identifier + (data->service_ident); + + if (!vpn_is_valid_transport(service)) { + connman_provider_disconnect(data->provider); + } + + /* VPN moved to be split routed, default route is not set */ + if (connman_provider_is_split_routing(data->provider)) + data->default_route_set = false; + } +} + +static void vpn_disconnect_check() +{ + GHashTableIter iter; + gpointer value; + + DBG(""); + g_hash_table_iter_init(&iter, vpn_connections); + while (g_hash_table_iter_next(&iter, NULL, &value)) + vpn_disconnect_check_provider(value); +} + +static void vpn_service_add(struct connman_service *service, const char *name) +{ + vpn_disconnect_check(); +} + +static void vpn_service_list_changed(struct connman_service *service) +{ + vpn_disconnect_check(); +} + +static void vpn_service_state_changed(struct connman_service *service, + enum connman_service_state state) +{ + vpn_disconnect_check(); +} + +static const struct connman_notifier vpn_notifier = { + .name = "vpn", + .priority = CONNMAN_NOTIFIER_PRIORITY_DEFAULT, + .default_changed = vpn_service_list_changed, + .service_add = vpn_service_add, + .service_remove = vpn_service_list_changed, + .service_state_changed = vpn_service_state_changed +}; + static int vpn_init(void) { int err; connection = connman_dbus_get_connection(); - if (connection == NULL) + if (!connection) return -EIO; watch = g_dbus_add_service_watch(connection, VPN_SERVICE, @@ -1809,6 +2260,7 @@ static int vpn_init(void) vpnd_created(connection, &provider_driver); } + connman_notifier_register(&vpn_notifier); return err; remove: @@ -1829,9 +2281,10 @@ static void vpn_exit(void) g_dbus_remove_watch(connection, removed_watch); g_dbus_remove_watch(connection, property_watch); + connman_notifier_unregister(&vpn_notifier); connman_provider_driver_unregister(&provider_driver); - if (vpn_connections != NULL) + if (vpn_connections) g_hash_table_destroy(vpn_connections); dbus_connection_unref(connection);