provider: Set nameservers if we receive them from vpnd
[platform/upstream/connman.git] / plugins / vpn.c
index c7f59a9..d33d7c1 100644 (file)
@@ -47,7 +47,6 @@
 static DBusConnection *connection;
 
 static GHashTable *vpn_connections = NULL;
-static gboolean starting_vpnd = TRUE;
 static guint watch;
 static guint added_watch;
 static guint removed_watch;
@@ -82,6 +81,7 @@ struct connection_data {
        char **host_ip;
        char *domain;
        char **nameservers;
+       gboolean immutable;
 
        GHashTable *server_routes;
        GHashTable *user_routes;
@@ -252,6 +252,8 @@ static void set_provider_state(struct connection_data *data)
        enum connman_provider_state state = CONNMAN_PROVIDER_STATE_UNKNOWN;
        int err = 0;
 
+       DBG("provider %p new state %s", data->provider, data->state);
+
        if (g_str_equal(data->state, "ready") == TRUE) {
                state = CONNMAN_PROVIDER_STATE_READY;
                goto set;
@@ -301,6 +303,7 @@ 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) {
                        connman_provider_set_index(data->provider,
                                                        data->index);
@@ -450,11 +453,22 @@ static int extract_nameservers(DBusMessageIter *array,
        return 0;
 }
 
+static int errorstr2val(const char *error) {
+       if (g_strcmp0(error, CONNMAN_ERROR_INTERFACE ".InProgress") == 0)
+               return -EINPROGRESS;
+
+       if (g_strcmp0(error, CONNMAN_ERROR_INTERFACE ".AlreadyConnected") == 0)
+               return -EISCONN;
+
+       return -ECONNREFUSED;
+}
+
 static void connect_reply(DBusPendingCall *call, void *user_data)
 {
        DBusMessage *reply;
        DBusError error;
-       struct config_create_data *cb_data = 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)
                return;
@@ -466,16 +480,17 @@ static void connect_reply(DBusPendingCall *call, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, reply) == TRUE) {
-               if (dbus_error_has_name(&error, CONNMAN_ERROR_INTERFACE
-                                               ".InProgress") == FALSE) {
+               int err = errorstr2val(error.name);
+               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) {
-                               cb_data->callback(cb_data->message,
-                                               ECONNREFUSED, NULL);
+                               cb_data->callback(cb_data->message, err, NULL);
                                free_config_cb_data(cb_data);
+                               data->cb_data = NULL;
                        }
                        goto done;
                }
@@ -502,6 +517,8 @@ static int connect_provider(struct connection_data *data, void *user_data)
 
        DBG("data %p user %p path %s", data, cb_data, data->path);
 
+       data->connect_pending = FALSE;
+
        message = dbus_message_new_method_call(VPN_SERVICE, data->path,
                                        VPN_CONNECTION_INTERFACE,
                                        VPN_CONNECT);
@@ -526,7 +543,7 @@ static int connect_provider(struct connection_data *data, void *user_data)
                cb_data->path = g_strdup(data->path);
        }
 
-       dbus_pending_call_set_notify(call, connect_reply, cb_data, NULL);
+       dbus_pending_call_set_notify(call, connect_reply, data, NULL);
 
        dbus_message_unref(message);
 
@@ -586,6 +603,8 @@ static void add_connection(const char *path, DBusMessageIter *properties,
                } else if (g_str_equal(key, "Type") == TRUE) {
                        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) {
                        dbus_message_iter_get_basic(&value, &str);
                        data->host = g_strdup(str);
@@ -596,6 +615,10 @@ static void add_connection(const char *path, DBusMessageIter *properties,
                        extract_nameservers(&value, data);
                } else if (g_str_equal(key, "Index") == TRUE) {
                        dbus_message_iter_get_basic(&value, &data->index);
+               } else if (g_str_equal(key, "ServerRoutes") == TRUE) {
+                       /* Ignored */
+               } else if (g_str_equal(key, "UserRoutes") == TRUE) {
+                       /* Ignored */
                } else {
                        if (dbus_message_iter_get_arg_type(&value) ==
                                                        DBUS_TYPE_STRING) {
@@ -620,6 +643,10 @@ static void add_connection(const char *path, DBusMessageIter *properties,
 
        resolv_host_addr(data);
 
+       if (data->nameservers != NULL)
+               connman_provider_set_nameservers(data->provider,
+                                               data->nameservers);
+
        if (data->connect_pending == TRUE)
                connect_provider(data, data->cb_data);
 
@@ -773,6 +800,14 @@ static int provider_remove(struct connman_provider *provider)
 
        DBG("provider %p data %p", provider, data);
 
+       if (data == NULL) {
+               /*
+                * This means the provider is already removed,
+                * just ignore the dbus in this case.
+                */
+               return -EALREADY;
+       }
+
        /*
         * When provider.c:provider_remove() calls this function,
         * it will remove the provider itself after the call.
@@ -1232,8 +1267,8 @@ static int create_configuration(DBusMessage *msg, connection_ready_cb callback)
 
        data = g_hash_table_lookup(vpn_connections, ident);
        if (data != NULL) {
-               if (data->call != NULL) {
-                       connman_error("Dbus call already pending");
+               if (data->call != NULL || data->cb_data != NULL) {
+                       DBG("create configuration call already pending");
                        err = -EINPROGRESS;
                        goto done;
                }
@@ -1263,6 +1298,18 @@ static int create_configuration(DBusMessage *msg, connection_ready_cb callback)
        dbus_message_set_sender(new_msg, me);
        dbus_message_set_member(new_msg, "Create");
 
+       user_data = g_try_new0(struct config_create_data, 1);
+       if (user_data == NULL) {
+               err = -ENOMEM;
+               goto done;
+       }
+
+       user_data->callback = callback;
+       user_data->message = dbus_message_ref(msg);
+       user_data->path = NULL;
+
+       DBG("cb %p msg %p", user_data, msg);
+
        result = dbus_connection_send_with_reply(connection, new_msg,
                                                &call, DBUS_TIMEOUT);
        if (result == FALSE || call == NULL) {
@@ -1270,20 +1317,6 @@ static int create_configuration(DBusMessage *msg, connection_ready_cb callback)
                goto done;
        }
 
-       if (data->cb_data == NULL) {
-               user_data = g_try_new(struct config_create_data, 1);
-               if (user_data != NULL) {
-                       user_data->callback = callback;
-                       user_data->message = dbus_message_ref(msg);
-                       user_data->path = NULL;
-
-                       DBG("cb %p msg %p", user_data, msg);
-               }
-       } else {
-               DBG("Configuration callback data already pending, "
-                       "discarding new data.");
-       }
-
        dbus_pending_call_set_notify(call, configuration_create_reply,
                                                        user_data, NULL);
        data->call = call;
@@ -1419,7 +1452,9 @@ static void destroy_provider(struct connection_data *data)
        if (data->call != NULL)
                dbus_pending_call_cancel(data->call);
 
-       connman_provider_put(data->provider);
+       connman_provider_set_data(data->provider, NULL);
+
+       connman_provider_remove(data->provider);
 
        data->provider = NULL;
 }
@@ -1455,22 +1490,14 @@ static void vpnd_created(DBusConnection *conn, void *user_data)
 {
        DBG("connection %p", conn);
 
-       if (starting_vpnd == TRUE) {
-               vpn_connections = g_hash_table_new_full(g_str_hash,
-                                               g_str_equal,
-                                               g_free, connection_destroy);
-               get_connections(user_data);
-               starting_vpnd = FALSE;
-       }
+       get_connections(user_data);
 }
 
 static void vpnd_removed(DBusConnection *conn, void *user_data)
 {
        DBG("connection %p", conn);
 
-       g_hash_table_destroy(vpn_connections);
-       vpn_connections = NULL;
-       starting_vpnd = TRUE;
+       g_hash_table_remove_all(vpn_connections);
 }
 
 static void remove_connection(DBusConnection *conn, const char *path)
@@ -1485,6 +1512,7 @@ static gboolean connection_removed(DBusConnection *conn, DBusMessage *message,
 {
        const char *path;
        const char *signature = DBUS_TYPE_OBJECT_PATH_AS_STRING;
+       struct connection_data *data;
 
        if (dbus_message_has_signature(message, signature) == FALSE) {
                connman_error("vpn removed signature \"%s\" does not match "
@@ -1495,7 +1523,11 @@ static gboolean connection_removed(DBusConnection *conn, DBusMessage *message,
 
        dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
                                DBUS_TYPE_INVALID);
-       remove_connection(conn, path);
+
+       data = g_hash_table_lookup(vpn_connections, get_ident(path));
+       if (data != NULL)
+               remove_connection(conn, path);
+
        return TRUE;
 }
 
@@ -1728,7 +1760,10 @@ static gboolean property_changed(DBusConnection *conn,
                        set_routes(data->provider,
                                                CONNMAN_PROVIDER_ROUTE_USER);
        } else if (g_str_equal(key, "Nameservers") == TRUE) {
-               extract_nameservers(&value, data);
+               if (extract_nameservers(&value, data) == 0 &&
+                                               data->nameservers != NULL)
+                       connman_provider_set_nameservers(data->provider,
+                                                       data->nameservers);
        }
 
        if (ip_set == TRUE && err == 0) {
@@ -1773,8 +1808,13 @@ static int vpn_init(void)
        }
 
        err = connman_provider_driver_register(&provider_driver);
-       if (err == 0)
+       if (err == 0) {
+               vpn_connections = g_hash_table_new_full(g_str_hash,
+                                               g_str_equal,
+                                               g_free, connection_destroy);
+
                vpnd_created(connection, &provider_driver);
+       }
 
        return err;
 
@@ -1798,7 +1838,8 @@ static void vpn_exit(void)
 
        connman_provider_driver_unregister(&provider_driver);
 
-       g_hash_table_destroy(vpn_connections);
+       if (vpn_connections != NULL)
+               g_hash_table_destroy(vpn_connections);
 
        dbus_connection_unref(connection);
 }