dnsproxy: Do not copy two extra bytes if using TCP
[framework/connectivity/connman.git] / src / dnsproxy.c
index a56e312..21512c6 100644 (file)
@@ -113,6 +113,7 @@ struct request_data {
        gpointer resp;
        gsize resplen;
        struct listener_data *ifdata;
+       gboolean append_domain;
 };
 
 struct listener_data {
@@ -278,6 +279,7 @@ static int append_query(unsigned char *buf, unsigned int size,
 {
        unsigned char *ptr = buf;
        char *offset;
+       int len;
 
        DBG("query %s domain %s", query, domain);
 
@@ -287,11 +289,12 @@ static int append_query(unsigned char *buf, unsigned int size,
 
                tmp = strchr(offset, '.');
                if (tmp == NULL) {
-                       if (strlen(offset) == 0)
+                       len = strlen(offset);
+                       if (len == 0)
                                break;
-                       *ptr = strlen(offset);
-                       memcpy(ptr + 1, offset, strlen(offset));
-                       ptr += strlen(offset) + 1;
+                       *ptr = len;
+                       memcpy(ptr + 1, offset, len);
+                       ptr += len + 1;
                        break;
                }
 
@@ -308,11 +311,12 @@ static int append_query(unsigned char *buf, unsigned int size,
 
                tmp = strchr(offset, '.');
                if (tmp == NULL) {
-                       if (strlen(offset) == 0)
+                       len = strlen(offset);
+                       if (len == 0)
                                break;
-                       *ptr = strlen(offset);
-                       memcpy(ptr + 1, offset, strlen(offset));
-                       ptr += strlen(offset) + 1;
+                       *ptr = len;
+                       memcpy(ptr + 1, offset, len);
+                       ptr += len + 1;
                        break;
                }
 
@@ -346,6 +350,9 @@ static int ns_resolv(struct server_data *server, struct request_data *req,
        if (dot != NULL && dot != lookup + strlen(lookup) - 1)
                return 0;
 
+       if (server->domains != NULL && server->domains->data != NULL)
+               req->append_domain = TRUE;
+
        for (list = server->domains; list; list = list->next) {
                char *domain;
                unsigned char alt[1024];
@@ -380,16 +387,16 @@ static int ns_resolv(struct server_data *server, struct request_data *req,
 
                memcpy(alt + offset + altlen,
                        request + offset + altlen - domlen,
-                               req->request_len - altlen + domlen);
+                               req->request_len - altlen - offset + domlen);
 
                if (server->protocol == IPPROTO_TCP) {
-                       int req_len = req->request_len + domlen - 1;
+                       int req_len = req->request_len + domlen - 2;
 
                        alt[0] = (req_len >> 8) & 0xff;
                        alt[1] = req_len & 0xff;
                }
 
-               err = send(sk, alt, req->request_len + domlen + 1, 0);
+               err = send(sk, alt, req->request_len + domlen, 0);
                if (err < 0)
                        return -EIO;
 
@@ -428,6 +435,35 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol)
        req->numresp++;
 
        if (hdr->rcode == 0 || req->resp == NULL) {
+
+               /*
+                * If the domain name was append
+                * remove it before forwarding the reply.
+                */
+               if (req->append_domain == TRUE) {
+                       unsigned char *ptr;
+                       uint8_t host_len;
+                       unsigned int domain_len;
+
+                       /*
+                        * ptr points to the first char of the hostname.
+                        * ->hostname.domain.net
+                        */
+                       ptr = reply + offset + sizeof(struct domain_hdr);
+                       host_len = *ptr;
+                       domain_len = strlen((const char *)ptr) - host_len - 1;
+
+                       /*
+                        * remove the domain name and replaced it by the end
+                        * of reply.
+                        */
+                       memmove(ptr + host_len + 1,
+                               ptr + host_len + domain_len + 1,
+                               reply_len - (ptr - reply + domain_len));
+
+                       reply_len = reply_len - domain_len;
+               }
+
                g_free(req->resp);
                req->resplen = 0;
 
@@ -1176,6 +1212,7 @@ 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) {
@@ -1290,12 +1327,13 @@ static gboolean udp_listener_event(GIOChannel *channel, GIOCondition condition,
        req->numserv = 0;
        req->ifdata = (struct listener_data *) ifdata;
        req->timeout = g_timeout_add_seconds(5, request_timeout, req);
+       req->append_domain = FALSE;
        request_list = g_slist_append(request_list, req);
 
        return resolv(req, buf, query);
 }
 
-static int create_dns_listener(int protocol, const char *ifname)
+static int create_dns_listener(int protocol, struct listener_data *ifdata)
 {
        GIOChannel *channel;
        const char *proto;
@@ -1307,23 +1345,19 @@ static int create_dns_listener(int protocol, const char *ifname)
        socklen_t slen;
        int sk, type, v6only = 0;
        int family = AF_INET6;
-       struct listener_data *ifdata;
 
-       DBG("interface %s", ifname);
 
-       ifdata = g_hash_table_lookup(listener_table, ifname);
-       if (ifdata == NULL)
-               return -ENODEV;
+       DBG("interface %s", ifdata->ifname);
 
        switch (protocol) {
        case IPPROTO_UDP:
                proto = "UDP";
-               type = SOCK_DGRAM;
+               type = SOCK_DGRAM | SOCK_CLOEXEC;
                break;
 
        case IPPROTO_TCP:
                proto = "TCP";
-               type = SOCK_STREAM;
+               type = SOCK_STREAM | SOCK_CLOEXEC;
                break;
 
        default:
@@ -1342,7 +1376,8 @@ static int create_dns_listener(int protocol, const char *ifname)
        }
 
        if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
-                                       ifname, strlen(ifname) + 1) < 0) {
+                                       ifdata->ifname,
+                                       strlen(ifdata->ifname) + 1) < 0) {
                connman_error("Failed to bind %s listener interface", proto);
                close(sk);
                return -EIO;
@@ -1405,15 +1440,9 @@ static int create_dns_listener(int protocol, const char *ifname)
        return 0;
 }
 
-static void destroy_udp_listener(const char *interface)
+static void destroy_udp_listener(struct listener_data *ifdata)
 {
-       struct listener_data *ifdata;
-
-       DBG("interface %s", interface);
-
-       ifdata = g_hash_table_lookup(listener_table, interface);
-       if (ifdata == NULL)
-               return;
+       DBG("interface %s", ifdata->ifname);
 
        if (ifdata->udp_listener_watch > 0)
                g_source_remove(ifdata->udp_listener_watch);
@@ -1421,15 +1450,9 @@ static void destroy_udp_listener(const char *interface)
        g_io_channel_unref(ifdata->udp_listener_channel);
 }
 
-static void destroy_tcp_listener(const char *interface)
+static void destroy_tcp_listener(struct listener_data *ifdata)
 {
-       struct listener_data *ifdata;
-
-       DBG("interface %s", interface);
-
-       ifdata = g_hash_table_lookup(listener_table, interface);
-       if (ifdata == NULL)
-               return;
+       DBG("interface %s", ifdata->ifname);
 
        if (ifdata->tcp_listener_watch > 0)
                g_source_remove(ifdata->tcp_listener_watch);
@@ -1437,34 +1460,31 @@ static void destroy_tcp_listener(const char *interface)
        g_io_channel_unref(ifdata->tcp_listener_channel);
 }
 
-static int create_listener(const char *interface)
+static int create_listener(struct listener_data *ifdata)
 {
        int err;
 
-       err = create_dns_listener(IPPROTO_UDP, interface);
+       err = create_dns_listener(IPPROTO_UDP, ifdata);
        if (err < 0)
                return err;
 
-       err = create_dns_listener(IPPROTO_TCP, interface);
+       err = create_dns_listener(IPPROTO_TCP, ifdata);
        if (err < 0) {
-               destroy_udp_listener(interface);
+               destroy_udp_listener(ifdata);
                return err;
        }
 
-       if (g_strcmp0(interface, "lo") == 0)
+       if (g_strcmp0(ifdata->ifname, "lo") == 0)
                __connman_resolvfile_append("lo", NULL, "127.0.0.1");
 
        return 0;
 }
 
-static void destroy_listener(const char *interface)
+static void destroy_listener(struct listener_data *ifdata)
 {
        GSList *list;
 
-       if (interface == NULL)
-               return;
-
-       if (g_strcmp0(interface, "lo") == 0)
+       if (g_strcmp0(ifdata->ifname, "lo") == 0)
                __connman_resolvfile_remove("lo", NULL, "127.0.0.1");
 
        for (list = request_pending_list; list; list = list->next) {
@@ -1499,8 +1519,8 @@ static void destroy_listener(const char *interface)
        g_slist_free(request_list);
        request_list = NULL;
 
-       destroy_tcp_listener(interface);
-       destroy_udp_listener(interface);
+       destroy_tcp_listener(ifdata);
+       destroy_udp_listener(ifdata);
 }
 
 int __connman_dnsproxy_add_listener(const char *interface)
@@ -1523,7 +1543,7 @@ int __connman_dnsproxy_add_listener(const char *interface)
        ifdata->tcp_listener_channel = NULL;
        ifdata->tcp_listener_watch = 0;
 
-       err = create_listener(interface);
+       err = create_listener(ifdata);
        if (err < 0) {
                connman_error("Couldn't create listener for %s err %d",
                                interface, err);
@@ -1537,9 +1557,15 @@ int __connman_dnsproxy_add_listener(const char *interface)
 
 void __connman_dnsproxy_remove_listener(const char *interface)
 {
+       struct listener_data *ifdata;
+
        DBG("interface %s", interface);
 
-       destroy_listener(interface);
+       ifdata = g_hash_table_lookup(listener_table, interface);
+       if (ifdata == NULL)
+               return;
+
+       destroy_listener(ifdata);
 
        g_hash_table_remove(listener_table, interface);
 }