service: Support automatically added nameservers
authorJukka Rissanen <jukka.rissanen@linux.intel.com>
Wed, 23 Nov 2011 15:06:20 +0000 (17:06 +0200)
committerSamuel Ortiz <sameo@linux.intel.com>
Mon, 28 Nov 2011 10:51:35 +0000 (11:51 +0100)
IPv6 autoconfigured nameservers can be added to resolver
via netlink messages in rtnl.c. Because of this they are
not seen in service object so we need to get those auto
added nameserver to be added in service.c so that
service can show them to user if necessary.

Fixes BMC#24196

src/connman.h
src/dhcp.c
src/network.c
src/provider.c
src/resolver.c
src/service.c

index 906483a..bd815a0 100644 (file)
@@ -498,9 +498,9 @@ void __connman_service_provision_changed(const char *ident);
 const char *__connman_service_type2string(enum connman_service_type type);
 
 int __connman_service_nameserver_append(struct connman_service *service,
-                                       const char *nameserver);
+                               const char *nameserver, gboolean is_auto);
 int __connman_service_nameserver_remove(struct connman_service *service,
-                                       const char *nameserver);
+                               const char *nameserver, gboolean is_auto);
 void __connman_service_nameserver_clear(struct connman_service *service);
 void __connman_service_nameserver_add_routes(struct connman_service *service,
                                                const char *gw);
index c6df4fa..a6263bd 100644 (file)
@@ -99,7 +99,7 @@ static void dhcp_invalidate(struct connman_dhcp *dhcp, connman_bool_t callback)
        if (dhcp->nameservers != NULL) {
                for (i = 0; dhcp->nameservers[i] != NULL; i++) {
                        __connman_service_nameserver_remove(service,
-                                                       dhcp->nameservers[i]);
+                                               dhcp->nameservers[i], FALSE);
                }
        }
 
@@ -275,7 +275,7 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
                if (dhcp->nameservers != NULL) {
                        for (i = 0; dhcp->nameservers[i] != NULL; i++) {
                                __connman_service_nameserver_remove(service,
-                                                       dhcp->nameservers[i]);
+                                               dhcp->nameservers[i], FALSE);
                        }
                        g_strfreev(dhcp->nameservers);
                }
@@ -284,7 +284,7 @@ static void lease_available_cb(GDHCPClient *dhcp_client, gpointer user_data)
 
                for (i = 0; dhcp->nameservers[i] != NULL; i++) {
                        __connman_service_nameserver_append(service,
-                                                       dhcp->nameservers[i]);
+                                               dhcp->nameservers[i], FALSE);
                }
        } else {
                g_strfreev(nameservers);
index 1bb1a6e..50d1f8b 100644 (file)
@@ -1410,7 +1410,7 @@ int connman_network_set_nameservers(struct connman_network *network,
 
        for (i = 0; nameservers_array[i] != NULL; i++) {
                __connman_service_nameserver_append(service,
-                                               nameservers_array[i]);
+                                               nameservers_array[i], FALSE);
        }
 
        g_strfreev(nameservers_array);
index 27b0902..1ff6bc4 100644 (file)
@@ -841,7 +841,7 @@ int connman_provider_set_nameservers(struct connman_provider *provider,
 
        for (i = 0; nameservers_array[i] != NULL; i++) {
                __connman_service_nameserver_append(provider->vpn_service,
-                                                       nameservers_array[i]);
+                                               nameservers_array[i], FALSE);
        }
 
        g_strfreev(nameservers_array);
index 68fa4f7..29871e1 100644 (file)
@@ -232,11 +232,22 @@ static gboolean resolver_expire_cb(gpointer user_data)
 {
        struct entry_data *entry = user_data;
        GSList *list;
+       int index;
 
        DBG("interface %s domain %s server %s",
                        entry->interface, entry->domain, entry->server);
 
        list = g_slist_append(NULL, entry);
+
+       index = connman_inet_ifindex(entry->interface);
+       if (index >= 0) {
+               struct connman_service *service;
+               service = __connman_service_lookup_from_index(index);
+               if (service != NULL)
+                       __connman_service_nameserver_remove(service,
+                                                       entry->server, TRUE);
+       }
+
        remove_entries(list);
 
        return FALSE;
@@ -262,10 +273,24 @@ static int append_resolver(const char *interface, const char *domain,
        entry->domain = g_strdup(domain);
        entry->server = g_strdup(server);
        entry->flags = flags;
-       if (lifetime)
+       if (lifetime) {
+               int index;
                entry->timeout = g_timeout_add_seconds(lifetime,
                                                resolver_expire_cb, entry);
 
+               /*
+                * We update the service only for those nameservers
+                * that are automagically added via netlink (lifetime > 0)
+                */
+               index = connman_inet_ifindex(interface);
+               if (index >= 0) {
+                       struct connman_service *service;
+                       service = __connman_service_lookup_from_index(index);
+                       if (service != NULL)
+                               __connman_service_nameserver_append(service,
+                                                               server, TRUE);
+               }
+       }
        entry_list = g_slist_append(entry_list, entry);
 
        if (dnsproxy_enabled == TRUE)
@@ -287,8 +312,28 @@ static int append_resolver(const char *interface, const char *domain,
 int connman_resolver_append(const char *interface, const char *domain,
                                                const char *server)
 {
+       GSList *list, *matches = NULL;
+
        DBG("interface %s domain %s server %s", interface, domain, server);
 
+       if (server == NULL)
+               return -EINVAL;
+
+       for (list = entry_list; list; list = list->next) {
+               struct entry_data *entry = list->data;
+
+               if (entry->timeout > 0 ||
+                               g_strcmp0(entry->interface, interface) != 0 ||
+                               g_strcmp0(entry->domain, domain) != 0 ||
+                               g_strcmp0(entry->server, server) != 0)
+                       continue;
+
+               matches = g_slist_append(matches, entry);
+       }
+
+       if (matches != NULL)
+               remove_entries(matches);
+
        return append_resolver(interface, domain, server, 0, 0);
 }
 
@@ -322,6 +367,12 @@ int connman_resolver_append_lifetime(const char *interface, const char *domain,
                        continue;
 
                g_source_remove(entry->timeout);
+
+               if (lifetime == 0) {
+                       resolver_expire_cb(entry);
+                       return 0;
+               }
+
                entry->timeout = g_timeout_add_seconds(lifetime,
                                                resolver_expire_cb, entry);
                return 0;
index c453f9e..bb01e6f 100644 (file)
@@ -87,6 +87,7 @@ struct connman_service {
        struct connman_provider *provider;
        char **nameservers;
        char **nameservers_config;
+       char **nameservers_auto;
        char **domains;
        char *domainname;
        char **timeservers;
@@ -836,8 +837,6 @@ static void update_nameservers(struct connman_service *service)
                break;
        }
 
-       connman_resolver_remove_all(ifname);
-
        if (service->nameservers_config != NULL) {
                int i;
 
@@ -866,63 +865,95 @@ static void update_nameservers(struct connman_service *service)
        connman_resolver_flush();
 }
 
+/*
+ * The is_auto variable is set to true when IPv6 autoconf nameservers are
+ * inserted to resolver via netlink message (see rtnl.c:rtnl_newnduseropt()
+ * for details) and not through service.c
+ */
 int __connman_service_nameserver_append(struct connman_service *service,
-                                               const char *nameserver)
+                               const char *nameserver, gboolean is_auto)
 {
-       int len;
+       char **nameservers;
+       int len, i;
 
-       DBG("service %p nameserver %s", service, nameserver);
+       DBG("service %p nameserver %s auto %d", service, nameserver, is_auto);
 
        if (nameserver == NULL)
                return -EINVAL;
 
-       if (service->nameservers != NULL) {
-               int i;
+       if (is_auto == TRUE)
+               nameservers = service->nameservers_auto;
+       else
+               nameservers = service->nameservers;
 
-               for (i = 0; service->nameservers[i] != NULL; i++)
-                       if (g_strcmp0(service->nameservers[i], nameserver) == 0)
-                               return -EEXIST;
+       for (i = 0; nameservers != NULL && nameservers[i] != NULL; i++)
+               if (g_strcmp0(nameservers[i], nameserver) == 0)
+                       return -EEXIST;
 
-               len = g_strv_length(service->nameservers);
-               service->nameservers = g_try_renew(char *, service->nameservers,
-                                                       len + 2);
+       if (nameservers != NULL) {
+               len = g_strv_length(nameservers);
+               nameservers = g_try_renew(char *, nameservers, len + 2);
        } else {
                len = 0;
-               service->nameservers = g_try_new0(char *, len + 2);
+               nameservers = g_try_new0(char *, len + 2);
        }
 
-       if (service->nameservers == NULL)
+       if (nameservers == NULL)
                return -ENOMEM;
 
-       service->nameservers[len] = g_strdup(nameserver);
-       service->nameservers[len + 1] = NULL;
+       nameservers[len] = g_strdup(nameserver);
+       if (nameservers[len] == NULL)
+               return -ENOMEM;
 
-       update_nameservers(service);
+       nameservers[len + 1] = NULL;
+
+       if (is_auto == TRUE) {
+               service->nameservers_auto = nameservers;
+       } else {
+               service->nameservers = nameservers;
+               update_nameservers(service);
+       }
 
        return 0;
 }
 
 int __connman_service_nameserver_remove(struct connman_service *service,
-                                               const char *nameserver)
+                               const char *nameserver, gboolean is_auto)
 {
-       char **servers;
+       char **servers, **nameservers;
+       gboolean found = FALSE;
        int len, i, j;
 
-       DBG("service %p nameserver %s", service, nameserver);
+       DBG("service %p nameserver %s auto %d", service, nameserver, is_auto);
 
        if (nameserver == NULL)
                return -EINVAL;
 
-       if (service->nameservers == NULL)
+       if (is_auto == TRUE)
+               nameservers = service->nameservers_auto;
+       else
+               nameservers = service->nameservers;
+
+       if (nameservers == NULL)
                return 0;
 
-       len = g_strv_length(service->nameservers);
-       if (len == 1) {
-               if (g_strcmp0(service->nameservers[0], nameserver) != 0)
-                       return 0;
+       for (i = 0; nameservers != NULL && nameservers[i] != NULL; i++)
+               if (g_strcmp0(nameservers[i], nameserver) == 0) {
+                       found = TRUE;
+                       break;
+               }
 
-               g_strfreev(service->nameservers);
-               service->nameservers = NULL;
+       if (found == FALSE)
+               return 0;
+
+       len = g_strv_length(nameservers);
+
+       if (len == 1) {
+               g_strfreev(nameservers);
+               if (is_auto == TRUE)
+                       service->nameservers_auto = NULL;
+               else
+                       service->nameservers = NULL;
 
                return 0;
        }
@@ -932,17 +963,24 @@ int __connman_service_nameserver_remove(struct connman_service *service,
                return -ENOMEM;
 
        for (i = 0, j = 0; i < len; i++) {
-               if (g_strcmp0(service->nameservers[i], nameserver) != 0) {
-                       servers[j] = g_strdup(service->nameservers[i]);
+               if (g_strcmp0(nameservers[i], nameserver) != 0) {
+                       servers[j] = g_strdup(nameservers[i]);
+                       if (servers[j] == NULL)
+                               return -ENOMEM;
                        j++;
                }
        }
        servers[len - 1] = NULL;
 
-       g_strfreev(service->nameservers);
-       service->nameservers = servers;
+       g_strfreev(nameservers);
+       nameservers = servers;
 
-       update_nameservers(service);
+       if (is_auto == TRUE) {
+               service->nameservers_auto = nameservers;
+       } else {
+               service->nameservers = nameservers;
+               update_nameservers(service);
+       }
 
        return 0;
 }
@@ -1422,9 +1460,12 @@ static void append_dns(DBusMessageIter *iter, void *user_data)
        if (service->nameservers_config != NULL) {
                append_nameserver(iter, &service->nameservers_config);
                return;
-       } else if (service->nameservers != NULL) {
-               append_nameserver(iter, &service->nameservers);
-               return;
+       } else {
+               if (service->nameservers != NULL)
+                       append_nameserver(iter, &service->nameservers);
+
+               if (service->nameservers_auto != NULL)
+                       append_nameserver(iter, &service->nameservers_auto);
        }
 }
 
@@ -3422,6 +3463,7 @@ static void service_free(gpointer user_data)
 
        g_strfreev(service->nameservers);
        g_strfreev(service->nameservers_config);
+       g_strfreev(service->nameservers_auto);
        g_strfreev(service->domains);
        g_strfreev(service->proxies);
        g_strfreev(service->excludes);