service: Disconnect the connecting service when needed
[platform/upstream/connman.git] / src / dnsproxy.c
index 719c170..7a9ca91 100644 (file)
@@ -83,10 +83,10 @@ struct partial_reply {
 };
 
 struct server_data {
-       char *interface;
+       int index;
        GList *domains;
        char *server;
-       struct sockaddr server_addr;
+       struct sockaddr *server_addr;
        socklen_t server_addr_len;
        int protocol;
        GIOChannel *channel;
@@ -122,7 +122,7 @@ struct request_data {
 };
 
 struct listener_data {
-       char *ifname;
+       int index;
        GIOChannel *udp_listener_channel;
        guint udp_listener_watch;
        GIOChannel *tcp_listener_channel;
@@ -243,27 +243,27 @@ static struct request_data *find_request(guint16 id)
        return NULL;
 }
 
-static struct server_data *find_server(const char *interface,
+static struct server_data *find_server(int index,
                                        const char *server,
                                                int protocol)
 {
        GSList *list;
 
-       DBG("interface %s server %s", interface, server);
+       DBG("index %d server %s proto %d", index, server, protocol);
 
        for (list = server_list; list; list = list->next) {
                struct server_data *data = list->data;
 
-               if (interface == NULL && data->interface == NULL &&
+               if (index < 0 && data->index < 0 &&
                                g_str_equal(data->server, server) == TRUE &&
                                data->protocol == protocol)
                        return data;
 
-               if (interface == NULL ||
-                               data->interface == NULL || data->server == NULL)
+               if (index < 0 ||
+                               data->index < 0 || data->server == NULL)
                        continue;
 
-               if (g_str_equal(data->interface, interface) == TRUE &&
+               if (data->index == index &&
                                g_str_equal(data->server, server) == TRUE &&
                                data->protocol == protocol)
                        return data;
@@ -1307,7 +1307,7 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
                                return -ENOMEM;
                        data->inserted = entry->ipv4->inserted;
                        data->type = type;
-                       data->answers = hdr->ancount;
+                       data->answers = ntohs(hdr->ancount);
                        data->timeout = entry->ipv4->timeout;
                        if (srv->protocol == IPPROTO_UDP)
                                cache_offset = 2;
@@ -1508,7 +1508,7 @@ static int ns_resolv(struct server_data *server, struct request_data *req,
        sk = g_io_channel_unix_get_fd(server->channel);
 
        err = sendto(sk, request, req->request_len, MSG_NOSIGNAL,
-                       &server->server_addr, server->server_addr_len);
+                       server->server_addr, server->server_addr_len);
        if (err < 0) {
                DBG("Cannot send message to server %s sock %d "
                        "protocol %d (%s/%d)",
@@ -1570,6 +1570,9 @@ static int ns_resolv(struct server_data *server, struct request_data *req,
                        alt[1] = req_len & 0xff;
                }
 
+               DBG("req %p dstid 0x%04x altid 0x%04x", req, req->dstid,
+                               req->altid);
+
                err = send(sk, alt, req->request_len + domlen, MSG_NOSIGNAL);
                if (err < 0)
                        return -EIO;
@@ -1611,7 +1614,8 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
        if (req == NULL)
                return -EINVAL;
 
-       DBG("id 0x%04x rcode %d", hdr->id, hdr->rcode);
+       DBG("req %p dstid 0x%04x altid 0x%04x rcode %d",
+                       req, req->dstid, req->altid, hdr->rcode);
 
        ifdata = req->ifdata;
 
@@ -1627,17 +1631,25 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                 * remove it before forwarding the reply.
                 */
                if (req->append_domain == TRUE) {
+                       unsigned int domain_len = 0;
                        unsigned char *ptr;
                        uint8_t host_len;
-                       unsigned int domain_len;
+                       unsigned int header_len;
 
                        /*
                         * ptr points to the first char of the hostname.
                         * ->hostname.domain.net
                         */
-                       ptr = reply + offset + sizeof(struct domain_hdr);
+                       header_len = offset + sizeof(struct domain_hdr);
+                       ptr = reply + header_len;
                        host_len = *ptr;
-                       domain_len = strlen((const char *)ptr + host_len + 1);
+                       if (host_len > 0)
+                               domain_len = strnlen((const char *)ptr + 1 +
+                                               host_len,
+                                               reply_len - header_len);
+
+
+                       DBG("host len %d domain len %d", host_len, domain_len);
 
                        /*
                         * Remove the domain name and replace it by the end
@@ -1657,7 +1669,7 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                                 */
                                memmove(ptr + host_len + 1,
                                        ptr + host_len + domain_len + 1,
-                                       reply_len - (ptr - reply + domain_len));
+                                       reply_len - header_len - domain_len);
 
                                reply_len = reply_len - domain_len;
                        }
@@ -1738,27 +1750,45 @@ static gboolean try_remove_cache(gpointer user_data)
        return FALSE;
 }
 
-static void destroy_server(struct server_data *server)
+static void server_destroy_socket(struct server_data *data)
 {
-       GList *list;
+       DBG("index %d server %s proto %d", data->index,
+                                       data->server, data->protocol);
 
-       DBG("interface %s server %s sock %d", server->interface, server->server,
-               g_io_channel_unix_get_fd(server->channel));
+       if (data->watch > 0) {
+               g_source_remove(data->watch);
+               data->watch = 0;
+       }
 
-       server_list = g_slist_remove(server_list, server);
+       if (data->timeout > 0) {
+               g_source_remove(data->timeout);
+               data->timeout = 0;
+       }
 
-       if (server->watch > 0)
-               g_source_remove(server->watch);
+       if (data->channel != NULL) {
+               g_io_channel_shutdown(data->channel, TRUE, NULL);
+               g_io_channel_unref(data->channel);
+               data->channel = NULL;
+       }
+
+       g_free(data->incoming_reply);
+       data->incoming_reply = NULL;
+}
+
+static void destroy_server(struct server_data *server)
+{
+       GList *list;
 
-       if (server->timeout > 0)
-               g_source_remove(server->timeout);
+       DBG("index %d server %s sock %d", server->index, server->server,
+                       server->channel != NULL ?
+                       g_io_channel_unix_get_fd(server->channel): -1);
 
-       g_io_channel_unref(server->channel);
+       server_list = g_slist_remove(server_list, server);
+       server_destroy_socket(server);
 
-       if (server->protocol == IPPROTO_UDP)
+       if (server->protocol == IPPROTO_UDP && server->enabled)
                DBG("Removing DNS server %s", server->server);
 
-       g_free(server->incoming_reply);
        g_free(server->server);
        for (list = server->domains; list; list = list->next) {
                char *domain = list->data;
@@ -1766,7 +1796,7 @@ static void destroy_server(struct server_data *server)
                server->domains = g_list_remove(server->domains, domain);
                g_free(domain);
        }
-       g_free(server->interface);
+       g_free(server->server_addr);
 
        /*
         * We do not remove cache right away but delay it few seconds.
@@ -1791,7 +1821,7 @@ static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition,
 
        if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
                connman_error("Error with UDP server %s", data->server);
-               data->watch = 0;
+               server_destroy_socket(data);
                return FALSE;
        }
 
@@ -1867,7 +1897,7 @@ hangup:
                int no_request_sent = TRUE;
                struct server_data *udp_server;
 
-               udp_server = find_server(server->interface, server->server,
+               udp_server = find_server(server->index, server->server,
                                                                IPPROTO_UDP);
                if (udp_server != NULL) {
                        for (domains = udp_server->domains; domains;
@@ -2015,84 +2045,56 @@ static gboolean tcp_idle_timeout(gpointer user_data)
        return FALSE;
 }
 
-static struct server_data *create_server(const char *interface,
-                                       const char *domain, const char *server,
-                                       int protocol)
+static int server_create_socket(struct server_data *data)
 {
-       struct addrinfo hints, *rp;
-       struct server_data *data;
-       int sk, ret;
-
-       DBG("interface %s server %s", interface, server);
-
-       memset(&hints, 0, sizeof(hints));
-
-       switch (protocol) {
-       case IPPROTO_UDP:
-               hints.ai_socktype = SOCK_DGRAM;
-               break;
-
-       case IPPROTO_TCP:
-               hints.ai_socktype = SOCK_STREAM;
-               break;
-
-       default:
-               return NULL;
-       }
-       hints.ai_family = AF_UNSPEC;
-       hints.ai_flags = AI_NUMERICSERV | AI_NUMERICHOST;
+       int sk, err;
+       char *interface;
 
-       ret = getaddrinfo(server, "53", &hints, &rp);
-       if (ret) {
-               connman_error("Failed to parse server %s address: %s\n",
-                             server, gai_strerror(ret));
-               return NULL;
-       }
-       /* Do not blindly copy this code elsewhere; it doesn't loop over the
-          results using ->ai_next as it should. That's OK in *this* case
-          because it was a numeric lookup; we *know* there's only one. */
+       DBG("index %d server %s proto %d", data->index,
+                                       data->server, data->protocol);
 
-       sk = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+       sk = socket(data->server_addr->sa_family,
+               data->protocol == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM,
+               data->protocol);
        if (sk < 0) {
-               connman_error("Failed to create server %s socket", server);
-               freeaddrinfo(rp);
-               return NULL;
+               err = errno;
+               connman_error("Failed to create server %s socket",
+                                                       data->server);
+               server_destroy_socket(data);
+               return -err;
        }
 
        DBG("sk %d", sk);
 
+       interface = connman_inet_ifname(data->index);
        if (interface != NULL) {
                if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
-                               interface, strlen(interface) + 1) < 0) {
+                                       interface,
+                                       strlen(interface) + 1) < 0) {
+                       err = errno;
                        connman_error("Failed to bind server %s "
                                                "to interface %s",
-                                                       server, interface);
-                       freeaddrinfo(rp);
+                                               data->server, interface);
                        close(sk);
-                       return NULL;
+                       server_destroy_socket(data);
+                       g_free(interface);
+                       return -err;
                }
-       }
-
-       data = g_try_new0(struct server_data, 1);
-       if (data == NULL) {
-               connman_error("Failed to allocate server %s data", server);
-               freeaddrinfo(rp);
-               close(sk);
-               return NULL;
+               g_free(interface);
        }
 
        data->channel = g_io_channel_unix_new(sk);
        if (data->channel == NULL) {
-               connman_error("Failed to create server %s channel", server);
-               freeaddrinfo(rp);
+               connman_error("Failed to create server %s channel",
+                                                       data->server);
                close(sk);
-               g_free(data);
-               return NULL;
+               server_destroy_socket(data);
+               return -ENOMEM;
        }
 
        g_io_channel_set_close_on_unref(data->channel, TRUE);
 
-       if (protocol == IPPROTO_TCP) {
+       if (data->protocol == IPPROTO_TCP) {
                g_io_channel_set_flags(data->channel, G_IO_FLAG_NONBLOCK, NULL);
                data->watch = g_io_add_watch(data->channel,
                        G_IO_OUT | G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
@@ -2104,41 +2106,16 @@ static struct server_data *create_server(const char *interface,
                        G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
                                                udp_server_event, data);
 
-       data->interface = g_strdup(interface);
-       if (domain)
-               data->domains = g_list_append(data->domains, g_strdup(domain));
-       data->server = g_strdup(server);
-       data->protocol = protocol;
-       data->server_addr_len = rp->ai_addrlen;
-       memcpy(&data->server_addr, rp->ai_addr, rp->ai_addrlen);
+       if (connect(sk, data->server_addr, data->server_addr_len) < 0) {
+               err = errno;
 
-       ret = connect(sk, rp->ai_addr, rp->ai_addrlen);
-       freeaddrinfo(rp);
-       if (ret < 0) {
-               if ((protocol == IPPROTO_TCP && errno != EINPROGRESS) ||
-                               protocol == IPPROTO_UDP) {
-                       GList *list;
-
-                       connman_error("Failed to connect to server %s", server);
-                       if (data->watch > 0)
-                               g_source_remove(data->watch);
-                       if (data->timeout > 0)
-                               g_source_remove(data->timeout);
-
-                       g_io_channel_unref(data->channel);
-                       close(sk);
-
-                       g_free(data->server);
-                       g_free(data->interface);
-                       for (list = data->domains; list; list = list->next) {
-                               char *tmp_domain = list->data;
+               if ((data->protocol == IPPROTO_TCP && errno != EINPROGRESS) ||
+                               data->protocol == IPPROTO_UDP) {
 
-                               data->domains = g_list_remove(data->domains,
-                                                               tmp_domain);
-                               g_free(tmp_domain);
-                       }
-                       g_free(data);
-                       return NULL;
+                       connman_error("Failed to connect to server %s",
+                                                               data->server);
+                       server_destroy_socket(data);
+                       return -err;
                }
        }
 
@@ -2148,6 +2125,89 @@ static struct server_data *create_server(const char *interface,
                                        NULL,
                                        cache_element_destroy);
 
+       return 0;
+}
+
+static struct server_data *create_server(int index,
+                                       const char *domain, const char *server,
+                                       int protocol)
+{
+       struct server_data *data;
+       struct addrinfo hints, *rp;
+       int ret;
+
+       DBG("index %d server %s", index, server);
+
+       data = g_try_new0(struct server_data, 1);
+       if (data == NULL) {
+               connman_error("Failed to allocate server %s data", server);
+               return NULL;
+       }
+
+       data->index = index;
+       if (domain)
+               data->domains = g_list_append(data->domains, g_strdup(domain));
+       data->server = g_strdup(server);
+       data->protocol = protocol;
+
+       memset(&hints, 0, sizeof(hints));
+
+       switch (protocol) {
+       case IPPROTO_UDP:
+               hints.ai_socktype = SOCK_DGRAM;
+               break;
+
+       case IPPROTO_TCP:
+               hints.ai_socktype = SOCK_STREAM;
+               break;
+
+       default:
+               destroy_server(data);
+               return NULL;
+       }
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_flags = AI_NUMERICSERV | AI_NUMERICHOST;
+
+       ret = getaddrinfo(data->server, "53", &hints, &rp);
+       if (ret) {
+               connman_error("Failed to parse server %s address: %s\n",
+                             data->server, gai_strerror(ret));
+               destroy_server(data);
+               return NULL;
+       }
+
+       /* Do not blindly copy this code elsewhere; it doesn't loop over the
+          results using ->ai_next as it should. That's OK in *this* case
+          because it was a numeric lookup; we *know* there's only one. */
+
+       data->server_addr_len = rp->ai_addrlen;
+
+       switch (rp->ai_family) {
+       case AF_INET:
+               data->server_addr = (struct sockaddr *)
+                                       g_try_new0(struct sockaddr_in, 1);
+               break;
+       case AF_INET6:
+               data->server_addr = (struct sockaddr *)
+                                       g_try_new0(struct sockaddr_in6, 1);
+               break;
+       default:
+               connman_error("Wrong address family %d", rp->ai_family);
+               break;
+       }
+       if (data->server_addr == NULL) {
+               freeaddrinfo(rp);
+               destroy_server(data);
+               return NULL;
+       }
+       memcpy(data->server_addr, rp->ai_addr, rp->ai_addrlen);
+       freeaddrinfo(rp);
+
+       if (server_create_socket(data) != 0) {
+               destroy_server(data);
+               return NULL;
+       }
+
        if (protocol == IPPROTO_UDP) {
                /* Enable new servers by default */
                data->enabled = TRUE;
@@ -2167,15 +2227,22 @@ static gboolean resolv(struct request_data *req,
        for (list = server_list; list; list = list->next) {
                struct server_data *data = list->data;
 
+               if (data->protocol == IPPROTO_TCP) {
+                       DBG("server %s ignored proto TCP", data->server);
+                       continue;
+               }
+
                DBG("server %s enabled %d", data->server, data->enabled);
 
                if (data->enabled == FALSE)
                        continue;
 
-               if (data->watch == 0 && data->protocol == IPPROTO_UDP)
-                       data->watch = g_io_add_watch(data->channel,
-                               G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
-                                               udp_server_event, data);
+               if (data->channel == NULL && data->protocol == IPPROTO_UDP) {
+                       if (server_create_socket(data) < 0) {
+                               DBG("socket creation failed while resolving");
+                               continue;
+                       }
+               }
 
                if (ns_resolv(data, req, request, name) > 0)
                        return TRUE;
@@ -2184,11 +2251,11 @@ static gboolean resolv(struct request_data *req,
        return FALSE;
 }
 
-static void append_domain(const char *interface, const char *domain)
+static void append_domain(int index, const char *domain)
 {
        GSList *list;
 
-       DBG("interface %s domain %s", interface, domain);
+       DBG("index %d domain %s", index, domain);
 
        if (domain == NULL)
                return;
@@ -2199,10 +2266,10 @@ static void append_domain(const char *interface, const char *domain)
                char *dom;
                gboolean dom_found = FALSE;
 
-               if (data->interface == NULL)
+               if (data->index < 0)
                        continue;
 
-               if (g_str_equal(data->interface, interface) == FALSE)
+               if (data->index != index)
                        continue;
 
                for (dom_list = data->domains; dom_list;
@@ -2222,18 +2289,18 @@ static void append_domain(const char *interface, const char *domain)
        }
 }
 
-int __connman_dnsproxy_append(const char *interface, const char *domain,
+int __connman_dnsproxy_append(int index, const char *domain,
                                                        const char *server)
 {
        struct server_data *data;
 
-       DBG("interface %s server %s", interface, server);
+       DBG("index %d server %s", index, server);
 
        if (server == NULL && domain == NULL)
                return -EINVAL;
 
        if (server == NULL) {
-               append_domain(interface, domain);
+               append_domain(index, domain);
 
                return 0;
        }
@@ -2241,35 +2308,35 @@ int __connman_dnsproxy_append(const char *interface, const char *domain,
        if (g_str_equal(server, "127.0.0.1") == TRUE)
                return -ENODEV;
 
-       data = find_server(interface, server, IPPROTO_UDP);
+       data = find_server(index, server, IPPROTO_UDP);
        if (data != NULL) {
-               append_domain(interface, domain);
+               append_domain(index, domain);
                return 0;
        }
 
-       data = create_server(interface, domain, server, IPPROTO_UDP);
+       data = create_server(index, domain, server, IPPROTO_UDP);
        if (data == NULL)
                return -EIO;
 
        return 0;
 }
 
-static void remove_server(const char *interface, const char *domain,
+static void remove_server(int index, const char *domain,
                        const char *server, int protocol)
 {
        struct server_data *data;
 
-       data = find_server(interface, server, protocol);
+       data = find_server(index, server, protocol);
        if (data == NULL)
                return;
 
        destroy_server(data);
 }
 
-int __connman_dnsproxy_remove(const char *interface, const char *domain,
+int __connman_dnsproxy_remove(int index, const char *domain,
                                                        const char *server)
 {
-       DBG("interface %s server %s", interface, server);
+       DBG("index %d server %s", index, server);
 
        if (server == NULL)
                return -EINVAL;
@@ -2277,8 +2344,8 @@ int __connman_dnsproxy_remove(const char *interface, const char *domain,
        if (g_str_equal(server, "127.0.0.1") == TRUE)
                return -ENODEV;
 
-       remove_server(interface, domain, server, IPPROTO_UDP);
-       remove_server(interface, domain, server, IPPROTO_TCP);
+       remove_server(index, domain, server, IPPROTO_UDP);
+       remove_server(index, domain, server, IPPROTO_TCP);
 
        return 0;
 }
@@ -2335,7 +2402,7 @@ static void dnsproxy_offline_mode(connman_bool_t enabled)
 static void dnsproxy_default_changed(struct connman_service *service)
 {
        GSList *list;
-       char *interface;
+       int index;
 
        DBG("service %p", service);
 
@@ -2348,14 +2415,14 @@ static void dnsproxy_default_changed(struct connman_service *service)
                return;
        }
 
-       interface = connman_service_get_interface(service);
-       if (interface == NULL)
+       index = __connman_service_get_index(service);
+       if (index < 0)
                return;
 
        for (list = server_list; list; list = list->next) {
                struct server_data *data = list->data;
 
-               if (g_strcmp0(data->interface, interface) == 0) {
+               if (data->index == index) {
                        DBG("Enabling DNS server %s", data->server);
                        data->enabled = TRUE;
                } else {
@@ -2364,7 +2431,6 @@ static void dnsproxy_default_changed(struct connman_service *service)
                }
        }
 
-       g_free(interface);
        cache_refresh();
 }
 
@@ -2552,7 +2618,7 @@ static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition,
                if (data->protocol != IPPROTO_UDP || data->enabled == FALSE)
                        continue;
 
-               if(create_server(data->interface, NULL,
+               if(create_server(data->index, NULL,
                                        data->server, IPPROTO_TCP) == NULL)
                        continue;
 
@@ -2675,9 +2741,9 @@ static int create_dns_listener(int protocol, struct listener_data *ifdata)
        socklen_t slen;
        int sk, type, v6only = 0;
        int family = AF_INET6;
+       char *interface;
 
-
-       DBG("interface %s", ifdata->ifname);
+       DBG("index %d", ifdata->index);
 
        switch (protocol) {
        case IPPROTO_UDP:
@@ -2705,13 +2771,17 @@ static int create_dns_listener(int protocol, struct listener_data *ifdata)
                return -EIO;
        }
 
-       if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
-                                       ifdata->ifname,
-                                       strlen(ifdata->ifname) + 1) < 0) {
+       interface = connman_inet_ifname(ifdata->index);
+       if (interface == NULL || setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
+                                       interface,
+                                       strlen(interface) + 1) < 0) {
                connman_error("Failed to bind %s listener interface", proto);
                close(sk);
+               g_free(interface);
                return -EIO;
        }
+       g_free(interface);
+
        /* Ensure it accepts Legacy IP connections too */
        if (family == AF_INET6 &&
                        setsockopt(sk, SOL_IPV6, IPV6_V6ONLY,
@@ -2772,7 +2842,7 @@ static int create_dns_listener(int protocol, struct listener_data *ifdata)
 
 static void destroy_udp_listener(struct listener_data *ifdata)
 {
-       DBG("interface %s", ifdata->ifname);
+       DBG("index %d", ifdata->index);
 
        if (ifdata->udp_listener_watch > 0)
                g_source_remove(ifdata->udp_listener_watch);
@@ -2782,7 +2852,7 @@ static void destroy_udp_listener(struct listener_data *ifdata)
 
 static void destroy_tcp_listener(struct listener_data *ifdata)
 {
-       DBG("interface %s", ifdata->ifname);
+       DBG("index %d", ifdata->index);
 
        if (ifdata->tcp_listener_watch > 0)
                g_source_remove(ifdata->tcp_listener_watch);
@@ -2792,7 +2862,7 @@ static void destroy_tcp_listener(struct listener_data *ifdata)
 
 static int create_listener(struct listener_data *ifdata)
 {
-       int err;
+       int err, index;
 
        err = create_dns_listener(IPPROTO_UDP, ifdata);
        if (err < 0)
@@ -2804,18 +2874,21 @@ static int create_listener(struct listener_data *ifdata)
                return err;
        }
 
-       if (g_strcmp0(ifdata->ifname, "lo") == 0)
-               __connman_resolvfile_append("lo", NULL, "127.0.0.1");
+       index = connman_inet_ifindex("lo");
+       if (ifdata->index == index)
+               __connman_resolvfile_append(index, NULL, "127.0.0.1");
 
        return 0;
 }
 
 static void destroy_listener(struct listener_data *ifdata)
 {
+       int index;
        GSList *list;
 
-       if (g_strcmp0(ifdata->ifname, "lo") == 0)
-               __connman_resolvfile_remove("lo", NULL, "127.0.0.1");
+       index = connman_inet_ifindex("lo");
+       if (ifdata->index == index)
+               __connman_resolvfile_remove(index, NULL, "127.0.0.1");
 
        for (list = request_list; list; list = list->next) {
                struct request_data *req = list->data;
@@ -2833,21 +2906,27 @@ static void destroy_listener(struct listener_data *ifdata)
        destroy_udp_listener(ifdata);
 }
 
-int __connman_dnsproxy_add_listener(const char *interface)
+int __connman_dnsproxy_add_listener(int index)
 {
        struct listener_data *ifdata;
        int err;
 
-       DBG("interface %s", interface);
+       DBG("index %d", index);
+
+       if (index < 0)
+               return -EINVAL;
+
+       if (listener_table == NULL)
+               return 0;
 
-       if (g_hash_table_lookup(listener_table, interface) != NULL)
+       if (g_hash_table_lookup(listener_table, GINT_TO_POINTER(index)) != NULL)
                return 0;
 
        ifdata = g_try_new0(struct listener_data, 1);
        if (ifdata == NULL)
                return -ENOMEM;
 
-       ifdata->ifname = g_strdup(interface);
+       ifdata->index = index;
        ifdata->udp_listener_channel = NULL;
        ifdata->udp_listener_watch = 0;
        ifdata->tcp_listener_channel = NULL;
@@ -2855,52 +2934,57 @@ int __connman_dnsproxy_add_listener(const char *interface)
 
        err = create_listener(ifdata);
        if (err < 0) {
-               connman_error("Couldn't create listener for %s err %d",
-                               interface, err);
-               g_free(ifdata->ifname);
+               connman_error("Couldn't create listener for index %d err %d",
+                               index, err);
                g_free(ifdata);
                return err;
        }
-       g_hash_table_insert(listener_table, ifdata->ifname, ifdata);
+       g_hash_table_insert(listener_table, GINT_TO_POINTER(ifdata->index),
+                       ifdata);
        return 0;
 }
 
-void __connman_dnsproxy_remove_listener(const char *interface)
+void __connman_dnsproxy_remove_listener(int index)
 {
        struct listener_data *ifdata;
 
-       DBG("interface %s", interface);
+       DBG("index %d", index);
 
-       ifdata = g_hash_table_lookup(listener_table, interface);
+       if (listener_table == NULL)
+               return;
+
+       ifdata = g_hash_table_lookup(listener_table, GINT_TO_POINTER(index));
        if (ifdata == NULL)
                return;
 
        destroy_listener(ifdata);
 
-       g_hash_table_remove(listener_table, interface);
+       g_hash_table_remove(listener_table, GINT_TO_POINTER(index));
 }
 
 static void remove_listener(gpointer key, gpointer value, gpointer user_data)
 {
-       const char *interface = key;
+       int index = GPOINTER_TO_INT(key);
        struct listener_data *ifdata = value;
 
-       DBG("interface %s", interface);
+       DBG("index %d", index);
 
        destroy_listener(ifdata);
 }
 
 int __connman_dnsproxy_init(void)
 {
-       int err;
+       int err, index;
 
        DBG("");
 
        srandom(time(NULL));
 
-       listener_table = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                                       g_free, g_free);
-       err = __connman_dnsproxy_add_listener("lo");
+       listener_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       NULL, g_free);
+
+       index = connman_inet_ifindex("lo");
+       err = __connman_dnsproxy_add_listener(index);
        if (err < 0)
                return err;
 
@@ -2911,7 +2995,7 @@ int __connman_dnsproxy_init(void)
        return 0;
 
 destroy:
-       __connman_dnsproxy_remove_listener("lo");
+       __connman_dnsproxy_remove_listener(index);
        g_hash_table_destroy(listener_table);
 
        return err;