Base Code merged to SPIN 2.4
[platform/upstream/connman.git] / src / dnsproxy.c
old mode 100644 (file)
new mode 100755 (executable)
index 7232b98..185d6f1
 
 #include "connman.h"
 
+#if defined TIZEN_EXT
+#include <sys/smack.h>
+#endif
+
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 struct domain_hdr {
        uint16_t id;
@@ -192,7 +196,11 @@ struct domain_rr {
  * By default the TTL (time-to-live) of the DNS response is used
  * when setting the cache entry life time. The value is in seconds.
  */
+#if defined TIZEN_EXT
+#define MAX_CACHE_TTL (60 * 60)
+#else
 #define MAX_CACHE_TTL (60 * 30)
+#endif
 /*
  * Also limit the other end, cache at least for 30 seconds.
  */
@@ -211,14 +219,29 @@ static int cache_size;
 static GHashTable *cache;
 static int cache_refcount;
 static GSList *server_list = NULL;
+#if defined TIZEN_EXT
+static GSList *server_list_sec = NULL;
+#endif
 static GSList *request_list = NULL;
 static GHashTable *listener_table = NULL;
 static time_t next_refresh;
 static GHashTable *partial_tcp_req_table;
+static guint cache_timer = 0;
+
+#if defined TIZEN_EXT
+static void destroy_server_sec(struct server_data *server);
+static struct server_data *create_server_sec(int index,
+               const char *domain, const char *server,
+               int protocol);
+#endif
 
 static guint16 get_id(void)
 {
-       return random();
+       uint64_t rand;
+
+       __connman_util_get_random(&rand);
+
+       return rand;
 }
 
 static int protocol_offset(int protocol)
@@ -356,8 +379,7 @@ static int dns_name_length(unsigned char *buf)
 static void update_cached_ttl(unsigned char *buf, int len, int new_ttl)
 {
        unsigned char *c;
-       uint32_t *i;
-       uint16_t *w;
+       uint16_t w;
        int l;
 
        /* skip the header */
@@ -387,17 +409,19 @@ static void update_cached_ttl(unsigned char *buf, int len, int new_ttl)
                        break;
 
                /* now the 4 byte TTL field */
-               i = (uint32_t *)c;
-               *i = htonl(new_ttl);
+               c[0] = new_ttl >> 24 & 0xff;
+               c[1] = new_ttl >> 16 & 0xff;
+               c[2] = new_ttl >> 8 & 0xff;
+               c[3] = new_ttl & 0xff;
                c += 4;
                len -= 4;
                if (len < 0)
                        break;
 
                /* now the 2 byte rdlen field */
-               w = (uint16_t *)c;
-               c += ntohs(*w) + 2;
-               len -= ntohs(*w) + 2;
+               w = c[0] << 8 | c[1];
+               c += w + 2;
+               len -= w + 2;
        }
 }
 
@@ -435,7 +459,7 @@ static void send_cached_response(int sk, unsigned char *buf, int len,
 
        hdr->id = id;
        hdr->qr = 1;
-       hdr->rcode = 0;
+       hdr->rcode = ns_r_noerror;
        hdr->ancount = htons(answers);
        hdr->nscount = 0;
        hdr->arcount = 0;
@@ -482,7 +506,7 @@ static void send_response(int sk, unsigned char *buf, int len,
        DBG("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode);
 
        hdr->qr = 1;
-       hdr->rcode = 2;
+       hdr->rcode = ns_r_servfail;
 
        hdr->ancount = 0;
        hdr->nscount = 0;
@@ -525,6 +549,8 @@ static void destroy_request_data(struct request_data *req)
 static gboolean request_timeout(gpointer user_data)
 {
        struct request_data *req = user_data;
+       struct sockaddr *sa;
+       int sk;
 
        if (!req)
                return FALSE;
@@ -532,47 +558,38 @@ static gboolean request_timeout(gpointer user_data)
        DBG("id 0x%04x", req->srcid);
 
        request_list = g_slist_remove(request_list, req);
-       req->numserv--;
 
-       if (req->resplen > 0 && req->resp) {
-               int sk, err;
+       if (req->protocol == IPPROTO_UDP) {
+               sk = get_req_udp_socket(req);
+               sa = &req->sa;
+       } else if (req->protocol == IPPROTO_TCP) {
+               sk = req->client_sk;
+               sa = NULL;
+       } else
+               goto out;
 
-               if (req->protocol == IPPROTO_UDP) {
-                       sk = get_req_udp_socket(req);
-                       if (sk < 0)
-                               return FALSE;
+       if (req->resplen > 0 && req->resp) {
+               /*
+                * Here we have received at least one reply (probably telling
+                * "not found" result), so send that back to client instead
+                * of more fatal server failed error.
+                */
+               if (sk >= 0)
+                       sendto(sk, req->resp, req->resplen, MSG_NOSIGNAL,
+                               sa, req->sa_len);
 
-                       err = sendto(sk, req->resp, req->resplen, MSG_NOSIGNAL,
-                               &req->sa, req->sa_len);
-               } else {
-                       sk = req->client_sk;
-                       err = send(sk, req->resp, req->resplen, MSG_NOSIGNAL);
-                       if (err < 0)
-                               close(sk);
-               }
-               if (err < 0)
-                       return FALSE;
-       } else if (req->request && req->numserv == 0) {
+       } else if (req->request) {
+               /*
+                * There was not reply from server at all.
+                */
                struct domain_hdr *hdr;
 
-               if (req->protocol == IPPROTO_TCP) {
-                       hdr = (void *) (req->request + 2);
-                       hdr->id = req->srcid;
-                       send_response(req->client_sk, req->request,
-                               req->request_len, NULL, 0, IPPROTO_TCP);
-
-               } else if (req->protocol == IPPROTO_UDP) {
-                       int sk;
-
-                       hdr = (void *) (req->request);
-                       hdr->id = req->srcid;
+               hdr = (void *)(req->request + protocol_offset(req->protocol));
+               hdr->id = req->srcid;
 
-                       sk = get_req_udp_socket(req);
-                       if (sk >= 0)
-                               send_response(sk, req->request,
-                                       req->request_len, &req->sa,
-                                       req->sa_len, IPPROTO_UDP);
-               }
+               if (sk >= 0)
+                       send_response(sk, req->request, req->request_len,
+                               sa, req->sa_len, req->protocol);
        }
 
        /*
@@ -585,6 +602,7 @@ static gboolean request_timeout(gpointer user_data)
                                GINT_TO_POINTER(req->client_sk));
        }
 
+out:
        req->timeout = 0;
        destroy_request_data(req);
 
@@ -763,6 +781,8 @@ static void cache_element_destroy(gpointer value)
 
 static gboolean try_remove_cache(gpointer user_data)
 {
+       cache_timer = 0;
+
        if (__sync_fetch_and_sub(&cache_refcount, 1) == 1) {
                DBG("No cache users, removing it.");
 
@@ -1344,7 +1364,6 @@ static void cache_refresh(void)
 static int reply_query_type(unsigned char *msg, int len)
 {
        unsigned char *c;
-       uint16_t *w;
        int l;
        int type;
 
@@ -1358,8 +1377,7 @@ static int reply_query_type(unsigned char *msg, int len)
        /* now the query, which is a name and 2 16 bit words */
        l = dns_name_length(c) + 1;
        c += l;
-       w = (uint16_t *) c;
-       type = ntohs(*w);
+       type = c[0] << 8 | c[1];
 
        return type;
 }
@@ -1401,7 +1419,7 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
        DBG("offset %d hdr %p msg %p rcode %d", offset, hdr, msg, hdr->rcode);
 
        /* Continue only if response code is 0 (=ok) */
-       if (hdr->rcode != 0)
+       if (hdr->rcode != ns_r_noerror)
                return 0;
 
        if (!cache)
@@ -1636,6 +1654,31 @@ static int ns_resolv(struct server_data *server, struct request_data *req,
                }
        }
 
+#if defined TIZEN_EXT
+       if (server->protocol == IPPROTO_UDP) {
+               GList *domains;
+               struct server_data *new_server = NULL;
+
+               new_server = create_server_sec(server->index, NULL,
+                                               server->server, IPPROTO_UDP);
+
+               if (new_server != NULL) {
+                       for (domains = server->domains; domains;
+                                               domains = domains->next) {
+                               char *dom = domains->data;
+
+                               DBG("Adding domain %s to %s",
+                                               dom, new_server->server);
+
+                               new_server->domains = g_list_append(
+                                               new_server->domains,
+                                                       g_strdup(dom));
+                       }
+
+                       server = new_server;
+               }
+       }
+#endif
        sk = g_io_channel_unix_get_fd(server->channel);
 
        err = sendto(sk, request, req->request_len, MSG_NOSIGNAL,
@@ -1760,14 +1803,11 @@ static char *uncompress(int16_t field_count, char *start, char *end,
                int pos;                /* position in compressed string */
                char name[NS_MAXLABEL]; /* tmp label */
                uint16_t dns_type, dns_class;
+               int comp_pos;
 
-               pos = dn_expand((const u_char *)start, (u_char *)end,
-                               (u_char *)ptr, name, NS_MAXLABEL);
-               if (pos < 0) {
-                       DBG("uncompress error [%d/%s]", errno,
-                               strerror(errno));
+               if (!convert_label(start, end, ptr, name, NS_MAXLABEL,
+                                       &pos, &comp_pos))
                        goto out;
-               }
 
                /*
                 * Copy the uncompressed resource record, type, class and \0 to
@@ -1775,7 +1815,6 @@ static char *uncompress(int16_t field_count, char *start, char *end,
                 */
 
                ulen = strlen(name);
-               *uptr++ = ulen;
                strncpy(uptr, name, uncomp_len - (uptr - uncompressed));
 
                DBG("pos %d ulen %d left %d name %s", pos, ulen,
@@ -1807,8 +1846,6 @@ static char *uncompress(int16_t field_count, char *start, char *end,
                 * so we need to uncompress it also when necessary.
                 */
                if (dns_type == ns_t_cname) {
-                       int comp_pos;
-
                        if (!convert_label(start, end, ptr, uptr,
                                        uncomp_len - (uptr - uncompressed),
                                                &pos, &comp_pos))
@@ -1833,7 +1870,6 @@ static char *uncompress(int16_t field_count, char *start, char *end,
                        ptr += dlen;
 
                } else if (dns_type == ns_t_soa) {
-                       int comp_pos;
                        int total_len = 0;
                        char *len_ptr;
 
@@ -1884,6 +1920,45 @@ out:
        return NULL;
 }
 
+static int strip_domains(char *name, char *answers, int maxlen)
+{
+       uint16_t data_len;
+       int name_len = strlen(name);
+       char *ptr, *start = answers, *end = answers + maxlen;
+
+       while (maxlen > 0) {
+               ptr = strstr(answers, name);
+               if (ptr) {
+                       char *domain = ptr + name_len;
+
+                       if (*domain) {
+                               int domain_len = strlen(domain);
+
+                               memmove(answers + name_len,
+                                       domain + domain_len,
+                                       end - (domain + domain_len));
+
+                               end -= domain_len;
+                               maxlen -= domain_len;
+                       }
+               }
+
+               answers += strlen(answers) + 1;
+               answers += 2 + 2 + 4;  /* skip type, class and ttl fields */
+
+               data_len = answers[0] << 8 | answers[1];
+               answers += 2; /* skip the length field */
+
+               if (answers + data_len > end)
+                       return -EINVAL;
+
+               answers += data_len;
+               maxlen -= answers - ptr;
+       }
+
+       return end - start;
+}
+
 static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                                struct server_data *data)
 {
@@ -1911,7 +1986,7 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
 
        req->numresp++;
 
-       if (hdr->rcode == 0 || !req->resp) {
+       if (hdr->rcode == ns_r_noerror || !req->resp) {
                unsigned char *new_reply = NULL;
 
                /*
@@ -1979,6 +2054,8 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                         */
                        if (domain_len > 0) {
                                int len = host_len + 1;
+                               int new_len, fixed_len;
+                               char *answers;
 
                                /*
                                 * First copy host (without domain name) into
@@ -2001,6 +2078,8 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                                 */
                                ptr += NS_QFIXEDSZ;
                                uptr += NS_QFIXEDSZ;
+                               answers = uptr;
+                               fixed_len = answers - uncompressed;
 
                                /*
                                 * We then uncompress the result to buffer
@@ -2032,22 +2111,39 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                                        goto out;
 
                                /*
+                                * The uncompressed buffer now contains almost
+                                * valid response. Final step is to get rid of
+                                * the domain name because at least glibc
+                                * gethostbyname() implementation does extra
+                                * checks and expects to find an answer without
+                                * domain name if we asked a query without
+                                * domain part. Note that glibc getaddrinfo()
+                                * works differently and accepts FQDN in answer
+                                */
+                               new_len = strip_domains(uncompressed, answers,
+                                                       uptr - answers);
+                               if (new_len < 0) {
+                                       DBG("Corrupted packet");
+                                       return -EINVAL;
+                               }
+
+                               /*
                                 * Because we have now uncompressed the answers
-                                * we must create a bigger buffer to hold all
-                                * that data.
+                                * we might have to create a bigger buffer to
+                                * hold all that data.
                                 */
 
-                               new_reply = g_try_malloc(header_len +
-                                                       uptr - uncompressed);
+                               reply_len = header_len + new_len + fixed_len;
+
+                               new_reply = g_try_malloc(reply_len);
                                if (!new_reply)
                                        return -ENOMEM;
 
                                memcpy(new_reply, reply, header_len);
                                memcpy(new_reply + header_len, uncompressed,
-                                       uptr - uncompressed);
+                                       new_len + fixed_len);
 
                                reply = new_reply;
-                               reply_len = header_len + uptr - uncompressed;
                        }
                }
 
@@ -2068,8 +2164,13 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
        }
 
 out:
-       if (hdr->rcode > 0 && req->numresp < req->numserv)
-               return -EINVAL;
+       if (req->numresp < req->numserv) {
+               if (hdr->rcode > ns_r_noerror) {
+                       return -EINVAL;
+               } else if (hdr->ancount == 0 && req->append_domain) {
+                       return -EINVAL;
+               }
+       }
 
        request_list = g_slist_remove(request_list, req);
 
@@ -2147,7 +2248,8 @@ static void destroy_server(struct server_data *server)
         * without any good reason. The small delay allows the new RDNSS to
         * create a new DNS server instance and the refcount does not go to 0.
         */
-       g_timeout_add_seconds(3, try_remove_cache, NULL);
+       if (cache && !cache_timer)
+               cache_timer = g_timeout_add_seconds(3, try_remove_cache, NULL);
 
        g_free(server);
 }
@@ -2175,6 +2277,19 @@ static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition,
        if (err < 0)
                return TRUE;
 
+#if defined TIZEN_EXT
+       GSList *list;
+
+       for (list = server_list_sec; list; list = list->next) {
+               struct server_data *new_data = list->data;
+
+               if (new_data == data) {
+                       destroy_server_sec(data);
+                       return TRUE;
+               }
+       }
+#endif
+
        return TRUE;
 }
 
@@ -2464,6 +2579,169 @@ static int server_create_socket(struct server_data *data)
        return 0;
 }
 
+#if defined TIZEN_EXT
+
+static void destroy_server_sec(struct server_data *server)
+{
+       GList *list;
+
+       DBG("index %d server %s sock %d", server->index, server->server,
+                       server->channel != NULL ?
+                       g_io_channel_unix_get_fd(server->channel): -1);
+
+       server_list_sec = g_slist_remove(server_list_sec, server);
+       close(g_io_channel_unix_get_fd(server->channel));
+       server_destroy_socket(server);
+
+       if (server->protocol == IPPROTO_UDP && server->enabled)
+               DBG("Removing DNS server %s", server->server);
+
+       g_free(server->server);
+       for (list = server->domains; list; list = list->next) {
+               char *domain = list->data;
+
+               server->domains = g_list_remove(server->domains, domain);
+               g_free(domain);
+       }
+       g_free(server->server_addr);
+
+       /*
+        * We do not remove cache right away but delay it few seconds.
+        * The idea is that when IPv6 DNS server is added via RDNSS, it has a
+        * lifetime. When the lifetime expires we decrease the refcount so it
+        * is possible that the cache is then removed. Because a new DNS server
+        * is usually created almost immediately we would then loose the cache
+        * without any good reason. The small delay allows the new RDNSS to
+        * create a new DNS server instance and the refcount does not go to 0.
+        */
+       /* TODO: Need to check this */
+       /* g_timeout_add_seconds(3, try_remove_cache, NULL); */
+
+       g_free(server);
+}
+
+static void destroy_all_server_sec()
+{
+       GSList *list;
+
+       DBG("remove all dns server");
+
+       for (list = server_list_sec; list; list = list->next) {
+               struct server_data *server = list->data;
+               destroy_server_sec(server);
+       }
+       server_list_sec = NULL;
+}
+
+static gboolean sec_udp_idle_timeout(gpointer user_data)
+{
+       struct server_data *server = user_data;
+
+       DBG("");
+
+       if (server == NULL)
+               return FALSE;
+
+       destroy_server_sec(server);
+
+       return FALSE;
+}
+
+static struct server_data *create_server_sec(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_sec(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_sec(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_sec(data);
+               return NULL;
+       }
+       memcpy(data->server_addr, rp->ai_addr, rp->ai_addrlen);
+       freeaddrinfo(rp);
+
+       if (server_create_socket(data) != 0) {
+               destroy_server_sec(data);
+               return NULL;
+       }
+
+       if (protocol == IPPROTO_UDP) {
+               /* Enable new servers by default */
+               data->enabled = TRUE;
+               DBG("Adding DNS server %s", data->server);
+
+               data->timeout = g_timeout_add_seconds(30, sec_udp_idle_timeout,
+                                                               data);
+
+               server_list_sec = g_slist_append(server_list_sec, data);
+       }
+
+       return data;
+}
+#endif
+
 static struct server_data *create_server(int index,
                                        const char *domain, const char *server,
                                        int protocol)
@@ -2628,6 +2906,34 @@ static void append_domain(int index, const char *domain)
        }
 }
 
+static void flush_requests(struct server_data *server)
+{
+       GSList *list;
+
+       list = request_list;
+       while (list) {
+               struct request_data *req = list->data;
+
+               list = list->next;
+
+               if (ns_resolv(server, req, req->request, req->name)) {
+                       /*
+                        * A cached result was sent,
+                        * so the request can be released
+                        */
+                       request_list =
+                               g_slist_remove(request_list, req);
+                       destroy_request_data(req);
+                       continue;
+               }
+
+               if (req->timeout > 0)
+                       g_source_remove(req->timeout);
+
+               req->timeout = g_timeout_add_seconds(5, request_timeout, req);
+       }
+}
+
 int __connman_dnsproxy_append(int index, const char *domain,
                                                        const char *server)
 {
@@ -2660,6 +2966,8 @@ int __connman_dnsproxy_append(int index, const char *domain,
        if (!data)
                return -EIO;
 
+       flush_requests(data);
+
        return 0;
 }
 
@@ -2692,34 +3000,11 @@ int __connman_dnsproxy_remove(int index, const char *domain,
        remove_server(index, domain, server, IPPROTO_UDP);
        remove_server(index, domain, server, IPPROTO_TCP);
 
-       return 0;
-}
-
-void __connman_dnsproxy_flush(void)
-{
-       GSList *list;
-
-       list = request_list;
-       while (list) {
-               struct request_data *req = list->data;
-
-               list = list->next;
-
-               if (resolv(req, req->request, req->name)) {
-                       /*
-                        * A cached result was sent,
-                        * so the request can be released
-                        */
-                       request_list =
-                               g_slist_remove(request_list, req);
-                       destroy_request_data(req);
-                       continue;
-               }
+#if defined TIZEN_EXT
+       destroy_all_server_sec();
+#endif
 
-               if (req->timeout > 0)
-                       g_source_remove(req->timeout);
-               req->timeout = g_timeout_add_seconds(5, request_timeout, req);
-       }
+       return 0;
 }
 
 static void dnsproxy_offline_mode(bool enabled)
@@ -2832,6 +3117,21 @@ static int parse_request(unsigned char *buf, int len,
                remain -= label_len + 1;
        }
 
+#if defined TIZEN_EXT
+       /* parse DNS query type either A or AAAA
+        * enforce to drop AAAA temporarily (IPv6 not supported)
+        */
+       if (last_label != NULL) {
+               uint16_t *type_p = (uint16_t *)last_label;
+               uint16_t type = ntohs(*type_p);
+
+               if (type == 0x1c) {
+                       DBG("query %s is type AAAA(0x%x)", name, type);
+                       return -ENOENT;
+               }
+       }
+#endif
+
        if (last_label && arcount && remain >= 9 && last_label[4] == 0 &&
                                !memcmp(last_label + 5, opt_edns0_type, 2)) {
                uint16_t edns0_bufsize;
@@ -3162,6 +3462,23 @@ static gboolean client_timeout(gpointer user_data)
        return FALSE;
 }
 
+#if defined TIZEN_EXT
+static void recover_listener(GIOChannel *channel, struct listener_data *ifdata)
+{
+       int sk, index;
+
+       index = ifdata->index;
+
+       sk = g_io_channel_unix_get_fd(channel);
+       close(sk);
+
+       __connman_dnsproxy_remove_listener(index);
+
+       if (__connman_dnsproxy_add_listener(index) == 0)
+               DBG("listener %d successfully recovered", index);
+}
+#endif
+
 static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition,
                                struct listener_data *ifdata, int family,
                                guint *listener_watch)
@@ -3182,11 +3499,17 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition,
                condition, channel, ifdata, family);
 
        if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+#if defined TIZEN_EXT
+               connman_error("Error %d with TCP listener channel", condition);
+
+               recover_listener(channel, ifdata);
+#else
                if (*listener_watch > 0)
                        g_source_remove(*listener_watch);
                *listener_watch = 0;
 
                connman_error("Error with TCP listener channel");
+#endif
 
                return false;
        }
@@ -3300,7 +3623,11 @@ static bool tcp_listener_event(GIOChannel *channel, GIOCondition condition,
         * The packet length bytes do not contain the total message length,
         * that is the reason to -2 below.
         */
+#if defined TIZEN_EXT
+       if (msg_len > (unsigned int)(len - 2)) {
+#else
        if (msg_len != (unsigned int)(len - 2)) {
+#endif
                DBG("client %d sent %d bytes but expecting %u pending %d",
                        client_sk, len, msg_len + 2, msg_len + 2 - len);
 
@@ -3329,6 +3656,41 @@ static gboolean tcp6_listener_event(GIOChannel *channel, GIOCondition condition,
                                &ifdata->tcp6_listener_watch);
 }
 
+#if defined TIZEN_EXT
+/* Temporarily disable AAAA type to enhance performance (IPv6 not supported) */
+static void __send_response_not_implemented(int sk, unsigned char *buf, int len,
+                               const struct sockaddr *to, socklen_t tolen,
+                               int protocol)
+{
+       struct domain_hdr *hdr;
+       int err, offset = protocol_offset(protocol);
+
+       DBG("sk %d", sk);
+
+       if (offset < 0)
+               return;
+
+       if (len < 12)
+               return;
+
+       hdr = (void *) (buf + offset);
+
+       DBG("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode);
+
+       hdr->qr = 1;
+       hdr->rcode = 4;
+
+       hdr->ancount = 0;
+       hdr->nscount = 0;
+       hdr->arcount = 0;
+
+       err = sendto(sk, buf, len, MSG_NOSIGNAL, to, tolen);
+       if (err < 0)
+               connman_error("Failed to send DNS response to %d: %s",
+                               sk, strerror(errno));
+}
+#endif
+
 static bool udp_listener_event(GIOChannel *channel, GIOCondition condition,
                                struct listener_data *ifdata, int family,
                                guint *listener_watch)
@@ -3345,8 +3707,14 @@ static bool udp_listener_event(GIOChannel *channel, GIOCondition condition,
        int sk, err, len;
 
        if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+#if defined TIZEN_EXT
+               connman_error("Error %d with UDP listener channel", condition);
+
+               recover_listener(channel, ifdata);
+#else
                connman_error("Error with UDP listener channel");
                *listener_watch = 0;
+#endif
                return false;
        }
 
@@ -3369,6 +3737,16 @@ static bool udp_listener_event(GIOChannel *channel, GIOCondition condition,
 
        err = parse_request(buf, len, query, sizeof(query));
        if (err < 0 || (g_slist_length(server_list) == 0)) {
+#if defined TIZEN_EXT
+               if (err == -ENOENT) {
+                       /* Temporarily disable AAAA type to enhance performance
+                        * (IPv6 not supported)
+                        */
+                       __send_response_not_implemented(sk, buf, len, client_addr,
+                                                                       *client_addr_len, IPPROTO_UDP);
+                       return TRUE;
+               }
+#endif
                send_response(sk, buf, len, client_addr,
                                *client_addr_len, IPPROTO_UDP);
                return true;
@@ -3402,7 +3780,15 @@ static bool udp_listener_event(GIOChannel *channel, GIOCondition condition,
                return true;
        }
 
+       req->name = g_strdup(query);
+       req->request = g_malloc(len);
+       memcpy(req->request, buf, len);
+#if defined TIZEN_EXT
+       DBG("req %p dstid 0x%04x altid 0x%04x", req, req->dstid, req->altid);
+       req->timeout = g_timeout_add_seconds(30, request_timeout, req);
+#else
        req->timeout = g_timeout_add_seconds(5, request_timeout, req);
+#endif
        request_list = g_slist_append(request_list, req);
 
        return true;
@@ -3437,7 +3823,12 @@ static GIOChannel *get_listener(int family, int protocol, int index)
        } s;
        socklen_t slen;
        int sk, type;
+#if !defined TIZEN_EXT
        char *interface;
+#endif
+#if defined TIZEN_EXT
+       int option;
+#endif
 
        DBG("family %d protocol %d index %d", family, protocol, index);
 
@@ -3467,6 +3858,10 @@ static GIOChannel *get_listener(int family, int protocol, int index)
                return NULL;
        }
 
+#if !defined TIZEN_EXT
+       /* ConnMan listens DNS from multiple interfaces
+        * E.g. various technology based and tethering interfaces
+        */
        interface = connman_inet_ifname(index);
        if (!interface || setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
                                        interface,
@@ -3480,13 +3875,16 @@ static GIOChannel *get_listener(int family, int protocol, int index)
                return NULL;
        }
        g_free(interface);
+#endif
 
        if (family == AF_INET6) {
                memset(&s.sin6, 0, sizeof(s.sin6));
                s.sin6.sin6_family = AF_INET6;
                s.sin6.sin6_port = htons(53);
                slen = sizeof(s.sin6);
-
+#if defined TIZEN_EXT
+               s.sin6.sin6_addr = in6addr_any;
+#else
                if (__connman_inet_get_interface_address(index,
                                                AF_INET6,
                                                &s.sin6.sin6_addr) < 0) {
@@ -3497,24 +3895,41 @@ static GIOChannel *get_listener(int family, int protocol, int index)
                        close(sk);
                        return NULL;
                }
+#endif
 
        } else if (family == AF_INET) {
                memset(&s.sin, 0, sizeof(s.sin));
                s.sin.sin_family = AF_INET;
                s.sin.sin_port = htons(53);
                slen = sizeof(s.sin);
-
+#if defined TIZEN_EXT
+               s.sin.sin_addr.s_addr = htonl(INADDR_ANY);
+#else
                if (__connman_inet_get_interface_address(index,
                                                AF_INET,
                                                &s.sin.sin_addr) < 0) {
                        close(sk);
                        return NULL;
                }
+#endif
        } else {
                close(sk);
                return NULL;
        }
 
+#if defined TIZEN_EXT
+       if (smack_fsetlabel(sk, "system::use_internet", SMACK_LABEL_IPOUT) != 0)
+               connman_error("Failed to label system::use_internet");
+
+       if (smack_fsetlabel(sk, "system::use_internet", SMACK_LABEL_IPIN) != 0)
+               connman_error("Failed to label system::use_internet");
+#endif
+#if defined TIZEN_EXT
+       /* When ConnMan crashed,
+        * probably DNS listener cannot bind existing address */
+       option = 1;
+       setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
+#endif
        if (bind(sk, &s.sa, slen) < 0) {
                connman_error("Failed to bind %s listener socket", proto);
                close(sk);
@@ -3562,40 +3977,68 @@ static int create_dns_listener(int protocol, struct listener_data *ifdata)
                ifdata->tcp4_listener_channel = get_listener(AF_INET, protocol,
                                                        ifdata->index);
                if (ifdata->tcp4_listener_channel)
+#if defined TIZEN_EXT
+                       ifdata->tcp4_listener_watch =
+                               g_io_add_watch(ifdata->tcp4_listener_channel,
+                                       G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       tcp4_listener_event, (gpointer)ifdata);
+#else
                        ifdata->tcp4_listener_watch =
                                g_io_add_watch(ifdata->tcp4_listener_channel,
                                        G_IO_IN, tcp4_listener_event,
                                        (gpointer)ifdata);
+#endif
                else
                        ret |= TCP_IPv4_FAILED;
 
                ifdata->tcp6_listener_channel = get_listener(AF_INET6, protocol,
                                                        ifdata->index);
                if (ifdata->tcp6_listener_channel)
+#if defined TIZEN_EXT
+                       ifdata->tcp6_listener_watch =
+                               g_io_add_watch(ifdata->tcp6_listener_channel,
+                                       G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       tcp6_listener_event, (gpointer)ifdata);
+#else
                        ifdata->tcp6_listener_watch =
                                g_io_add_watch(ifdata->tcp6_listener_channel,
                                        G_IO_IN, tcp6_listener_event,
                                        (gpointer)ifdata);
+#endif
                else
                        ret |= TCP_IPv6_FAILED;
        } else {
                ifdata->udp4_listener_channel = get_listener(AF_INET, protocol,
                                                        ifdata->index);
                if (ifdata->udp4_listener_channel)
+#if defined TIZEN_EXT
+                       ifdata->udp4_listener_watch =
+                               g_io_add_watch(ifdata->udp4_listener_channel,
+                                       G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       udp4_listener_event, (gpointer)ifdata);
+#else
                        ifdata->udp4_listener_watch =
                                g_io_add_watch(ifdata->udp4_listener_channel,
                                        G_IO_IN, udp4_listener_event,
                                        (gpointer)ifdata);
+#endif
                else
                        ret |= UDP_IPv4_FAILED;
 
                ifdata->udp6_listener_channel = get_listener(AF_INET6, protocol,
                                                        ifdata->index);
                if (ifdata->udp6_listener_channel)
+#if defined TIZEN_EXT
+                       ifdata->udp6_listener_watch =
+                               g_io_add_watch(ifdata->udp6_listener_channel,
+                                       G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       udp6_listener_event, (gpointer)ifdata);
+#else
                        ifdata->udp6_listener_watch =
                                g_io_add_watch(ifdata->udp6_listener_channel,
                                        G_IO_IN, udp6_listener_event,
                                        (gpointer)ifdata);
+#endif
                else
                        ret |= UDP_IPv6_FAILED;
        }
@@ -3771,8 +4214,6 @@ int __connman_dnsproxy_init(void)
 
        DBG("");
 
-       srandom(time(NULL));
-
        listener_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
                                                        NULL, g_free);
 
@@ -3804,6 +4245,16 @@ void __connman_dnsproxy_cleanup(void)
 {
        DBG("");
 
+       if (cache_timer) {
+               g_source_remove(cache_timer);
+               cache_timer = 0;
+       }
+
+       if (cache) {
+               g_hash_table_destroy(cache);
+               cache = NULL;
+       }
+
        connman_notifier_unregister(&dnsproxy_notifier);
 
        g_hash_table_foreach(listener_table, remove_listener, NULL);