dnsproxy: Release all the fields of the request after forward dns reply
[framework/connectivity/connman.git] / src / dnsproxy.c
index f56c30c..9a4fa47 100644 (file)
@@ -24,6 +24,7 @@
 #endif
 
 #include <errno.h>
+#include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 #include <stdint.h>
@@ -183,10 +184,14 @@ static int cache_refcount;
 static GSList *server_list = NULL;
 static GSList *request_list = NULL;
 static GSList *request_pending_list = NULL;
-static guint16 request_id = 0x0000;
 static GHashTable *listener_table = NULL;
 static time_t next_refresh;
 
+static guint16 get_id()
+{
+       return random();
+}
+
 static int protocol_offset(int protocol)
 {
        switch (protocol) {
@@ -1505,6 +1510,17 @@ static int ns_resolv(struct server_data *server, struct request_data *req,
        return 0;
 }
 
+static void destroy_request_data(struct request_data *req)
+{
+       if (req->timeout > 0)
+               g_source_remove(req->timeout);
+
+       g_free(req->resp);
+       g_free(req->request);
+       g_free(req->name);
+       g_free(req);
+}
+
 static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                                struct server_data *data)
 {
@@ -1565,7 +1581,11 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                         * name. In this case we end up in this branch.
                         */
                        if (domain_len > 0) {
-                               memcpy(ptr + host_len + 1,
+                               /*
+                                * Note that we must use memmove() here,
+                                * because the memory areas can overlap.
+                                */
+                               memmove(ptr + host_len + 1,
                                        ptr + host_len + domain_len + 1,
                                        reply_len - (ptr - reply + domain_len));
 
@@ -1589,9 +1609,6 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
        if (hdr->rcode > 0 && req->numresp < req->numserv)
                return -EINVAL;
 
-       if (req->timeout > 0)
-               g_source_remove(req->timeout);
-
        request_list = g_slist_remove(request_list, req);
 
        if (protocol == IPPROTO_UDP) {
@@ -1604,8 +1621,7 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                close(sk);
        }
 
-       g_free(req->resp);
-       g_free(req);
+       destroy_request_data(req);
 
        return err;
 }
@@ -1663,7 +1679,7 @@ static void destroy_server(struct server_data *server)
        g_io_channel_unref(server->channel);
 
        if (server->protocol == IPPROTO_UDP)
-               connman_info("Removing DNS server %s", server->server);
+               DBG("Removing DNS server %s", server->server);
 
        g_free(server->incoming_reply);
        g_free(server->server);
@@ -1771,6 +1787,7 @@ hangup:
        if ((condition & G_IO_OUT) && !server->connected) {
                GSList *list;
                GList *domains;
+               int no_request_sent = TRUE;
                struct server_data *udp_server;
 
                udp_server = find_server(server->interface, server->server,
@@ -1796,29 +1813,48 @@ hangup:
                        server->timeout = 0;
                }
 
-               for (list = request_list; list; list = list->next) {
+               for (list = request_list; list; ) {
                        struct request_data *req = list->data;
+                       int status;
 
-                       if (req->protocol == IPPROTO_UDP)
+                       if (req->protocol == IPPROTO_UDP) {
+                               list = list->next;
                                continue;
+                       }
 
                        DBG("Sending req %s over TCP", (char *)req->name);
 
+                       status = ns_resolv(server, req,
+                                               req->request, req->name);
+                       if (status > 0) {
+                               /*
+                                * A cached result was sent,
+                                * so the request can be released
+                                */
+                               list = list->next;
+                               request_list = g_slist_remove(request_list, req);
+                               destroy_request_data(req);
+                               continue;
+                       }
+
+                       if (status < 0) {
+                               list = list->next;
+                               continue;
+                       }
+
+                       no_request_sent = FALSE;
+
                        if (req->timeout > 0)
                                g_source_remove(req->timeout);
 
                        req->timeout = g_timeout_add_seconds(30,
                                                request_timeout, req);
-                       if (ns_resolv(server, req, req->request,
-                                       req->name) > 0) {
-                               /* We sent cached result so no need for timeout
-                                * handler.
-                                */
-                               if (req->timeout > 0) {
-                                       g_source_remove(req->timeout);
-                                       req->timeout = 0;
-                               }
-                       }
+                       list = list->next;
+               }
+
+               if (no_request_sent == TRUE) {
+                       destroy_server(server);
+                       return FALSE;
                }
 
        } else if (condition & G_IO_IN) {
@@ -2034,14 +2070,12 @@ static struct server_data *create_server(const char *interface,
        if (protocol == IPPROTO_UDP) {
                /* Enable new servers by default */
                data->enabled = TRUE;
-               connman_info("Adding DNS server %s", data->server);
+               DBG("Adding DNS server %s", data->server);
 
                server_list = g_slist_append(server_list, data);
-
-               return data;
        }
 
-       return NULL;
+       return data;
 }
 
 static gboolean resolv(struct request_data *req,
@@ -2196,12 +2230,12 @@ static void dnsproxy_offline_mode(connman_bool_t enabled)
                struct server_data *data = list->data;
 
                if (enabled == FALSE) {
-                       connman_info("Enabling DNS server %s", data->server);
+                       DBG("Enabling DNS server %s", data->server);
                        data->enabled = TRUE;
                        cache_invalidate();
                        cache_refresh();
                } else {
-                       connman_info("Disabling DNS server %s", data->server);
+                       DBG("Disabling DNS server %s", data->server);
                        data->enabled = FALSE;
                        cache_invalidate();
                }
@@ -2232,10 +2266,10 @@ static void dnsproxy_default_changed(struct connman_service *service)
                struct server_data *data = list->data;
 
                if (g_strcmp0(data->interface, interface) == 0) {
-                       connman_info("Enabling DNS server %s", data->server);
+                       DBG("Enabling DNS server %s", data->server);
                        data->enabled = TRUE;
                } else {
-                       connman_info("Disabling DNS server %s", data->server);
+                       DBG("Disabling DNS server %s", data->server);
                        data->enabled = FALSE;
                }
        }
@@ -2330,12 +2364,12 @@ static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition,
        unsigned char buf[768];
        char query[512];
        struct request_data *req;
-       struct server_data *server;
        int sk, client_sk, len, err;
        struct sockaddr_in6 client_addr;
        socklen_t client_addr_len = sizeof(client_addr);
        GSList *list;
        struct listener_data *ifdata = user_data;
+       int waiting_for_connect = FALSE;
 
        DBG("condition 0x%x", condition);
 
@@ -2379,13 +2413,9 @@ static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition,
        req->client_sk = client_sk;
        req->protocol = IPPROTO_TCP;
 
-       request_id += 2;
-       if (request_id == 0x0000 || request_id == 0xffff)
-               request_id += 2;
-
        req->srcid = buf[2] | (buf[3] << 8);
-       req->dstid = request_id;
-       req->altid = request_id + 1;
+       req->dstid = get_id();
+       req->altid = get_id();
        req->request_len = len;
 
        buf[2] = req->dstid & 0xff;
@@ -2394,63 +2424,53 @@ static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition,
        req->numserv = 0;
        req->ifdata = (struct listener_data *) ifdata;
        req->append_domain = FALSE;
-       request_list = g_slist_append(request_list, req);
 
        for (list = server_list; list; list = list->next) {
                struct server_data *data = list->data;
-               GList *domains;
 
                if (data->protocol != IPPROTO_UDP || data->enabled == FALSE)
                        continue;
 
-               server = create_server(data->interface, NULL,
-                                       data->server, IPPROTO_TCP);
-
-               /*
-                * If server is NULL, we're not connected yet.
-                * Copy the relevant buffers and continue with
-                * the next nameserver.
-                * The request will actually be sent once we're
-                * properly connected over TCP to this nameserver.
-                */
-               if (server == NULL) {
-                       req->request = g_try_malloc0(req->request_len);
-                       if (req->request == NULL)
-                               return TRUE;
-
-                       memcpy(req->request, buf, req->request_len);
-
-                       req->name = g_try_malloc0(sizeof(query));
-                       if (req->name == NULL) {
-                               g_free(req->request);
-                               return TRUE;
-                       }
-                       memcpy(req->name, query, sizeof(query));
-
+               if(create_server(data->interface, NULL,
+                                       data->server, IPPROTO_TCP) == NULL)
                        continue;
-               }
 
-               if (req->timeout > 0)
-                       g_source_remove(req->timeout);
-
-               for (domains = data->domains; domains;
-                               domains = domains->next) {
-                       char *dom = domains->data;
+               waiting_for_connect = TRUE;
+       }
 
-                       DBG("Adding domain %s to %s", dom, server->server);
+       if (waiting_for_connect == FALSE) {
+               /* No server is waiting for connect */
+               send_response(client_sk, buf, len, NULL, 0, IPPROTO_TCP);
+               g_free(req);
+               return TRUE;
+       }
 
-                       server->domains = g_list_append(server->domains,
-                                               g_strdup(dom));
-               }
+       /*
+        * The server is not connected yet.
+        * Copy the relevant buffers.
+        * The request will actually be sent once we're
+        * properly connected over TCP to the nameserver.
+        */
+       req->request = g_try_malloc0(req->request_len);
+       if (req->request == NULL) {
+               send_response(client_sk, buf, len, NULL, 0, IPPROTO_TCP);
+               g_free(req);
+               return TRUE;
+       }
+       memcpy(req->request, buf, req->request_len);
 
-               req->timeout = g_timeout_add_seconds(30, request_timeout, req);
-               if (ns_resolv(server, req, buf, query) > 0) {
-                       if (req->timeout > 0) {
-                               g_source_remove(req->timeout);
-                               req->timeout = 0;
-                       }
-               }
+       req->name = g_try_malloc0(sizeof(query));
+       if (req->name == NULL) {
+               send_response(client_sk, buf, len, NULL, 0, IPPROTO_TCP);
+               g_free(req->request);
+               g_free(req);
+               return TRUE;
        }
+       memcpy(req->name, query, sizeof(query));
+
+       req->timeout = g_timeout_add_seconds(30, request_timeout, req);
+
+       request_list = g_slist_append(request_list, req);
 
        return TRUE;
 }
@@ -2498,13 +2518,9 @@ static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition,
        req->client_sk = 0;
        req->protocol = IPPROTO_UDP;
 
-       request_id += 2;
-       if (request_id == 0x0000 || request_id == 0xffff)
-               request_id += 2;
-
        req->srcid = buf[0] | (buf[1] << 8);
-       req->dstid = request_id;
-       req->altid = request_id + 1;
+       req->dstid = get_id();
+       req->altid = get_id();
        req->request_len = len;
 
        buf[0] = req->dstid & 0xff;
@@ -2673,17 +2689,6 @@ static int create_listener(struct listener_data *ifdata)
        return 0;
 }
 
-static void destroy_request_data(struct request_data *req)
-{
-       if (req->timeout > 0)
-               g_source_remove(req->timeout);
-
-       g_free(req->resp);
-       g_free(req->request);
-       g_free(req->name);
-       g_free(req);
-}
-
 static void destroy_listener(struct listener_data *ifdata)
 {
        GSList *list;
@@ -2782,6 +2787,8 @@ int __connman_dnsproxy_init(void)
 
        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");