main: Use 'NetworkInterfaceBlacklist' string everywhere
[framework/connectivity/connman.git] / src / dnsproxy.c
index 03d334f..82c5c1f 100644 (file)
@@ -439,7 +439,7 @@ static void send_response(int sk, unsigned char *buf, int len,
        struct domain_hdr *hdr;
        int err, offset = protocol_offset(protocol);
 
-       DBG("");
+       DBG("sk %d", sk);
 
        if (offset < 0)
                return;
@@ -460,8 +460,8 @@ static void send_response(int sk, unsigned char *buf, int len,
 
        err = sendto(sk, buf, len, MSG_NOSIGNAL, to, tolen);
        if (err < 0) {
-               connman_error("Failed to send DNS response: %s",
-                               strerror(errno));
+               connman_error("Failed to send DNS response to %d: %s",
+                               sk, strerror(errno));
                return;
        }
 }
@@ -665,13 +665,22 @@ static uint16_t cache_check_validity(char *question, uint16_t type,
        return type;
 }
 
-static struct cache_entry *cache_check(gpointer request, int *qtype)
+static struct cache_entry *cache_check(gpointer request, int *qtype, int proto)
 {
-       char *question = request + 12;
+       char *question;
        struct cache_entry *entry;
        struct domain_question *q;
        uint16_t type;
-       int offset;
+       int offset, proto_offset;
+
+       if (request == NULL)
+               return NULL;
+
+       proto_offset = protocol_offset(proto);
+       if (proto_offset < 0)
+               return NULL;
+
+       question = request + proto_offset + 12;
 
        offset = strlen(question) + 1;
        q = (void *) (question + offset);
@@ -1237,6 +1246,7 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
        int offset = protocol_offset(srv->protocol);
        int err, qlen, ttl = 0;
        uint16_t answers = 0, type = 0, class = 0;
+       struct domain_hdr *hdr = (void *)(msg + offset);
        struct domain_question *q;
        struct cache_entry *entry;
        struct cache_data *data;
@@ -1261,12 +1271,13 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
                next_refresh = current_time + 30;
        }
 
-
-       /* Continue only if response code is 0 (=ok) */
-       if (msg[3] & 0x0f)
+       if (offset < 0)
                return 0;
 
-       if (offset < 0)
+       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)
                return 0;
 
        rsplen = sizeof(response) - 1;
@@ -1283,21 +1294,30 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
         * to cache the negative response.
         */
        if ((err == -ENOMSG || err == -ENOBUFS) &&
-                       reply_query_type(msg, msg_len) == 28) {
+                       reply_query_type(msg + offset,
+                                       msg_len - offset) == 28) {
                entry = g_hash_table_lookup(cache, question);
                if (entry && entry->ipv4 && entry->ipv6 == NULL) {
+                       int cache_offset = 0;
+
                        data = g_try_new(struct cache_data, 1);
                        if (data == NULL)
                                return -ENOMEM;
                        data->inserted = entry->ipv4->inserted;
                        data->type = type;
-                       data->answers = msg[5];
+                       data->answers = hdr->ancount;
                        data->timeout = entry->ipv4->timeout;
-                       data->data_len = msg_len;
-                       data->data = ptr = g_malloc(msg_len);
+                       if (srv->protocol == IPPROTO_UDP)
+                               cache_offset = 2;
+                       data->data_len = msg_len + cache_offset;
+                       data->data = ptr = g_malloc(data->data_len);
+                       ptr[0] = (data->data_len - 2) / 256;
+                       ptr[1] = (data->data_len - 2) - ptr[0] * 256;
+                       if (srv->protocol == IPPROTO_UDP)
+                               ptr += 2;
                        data->valid_until = entry->ipv4->valid_until;
                        data->cache_until = entry->ipv4->cache_until;
-                       memcpy(data->data, msg, msg_len);
+                       memcpy(ptr, msg, msg_len);
                        entry->ipv6 = data;
                        /*
                         * we will get a "hit" when we serve the response
@@ -1448,7 +1468,7 @@ static int ns_resolv(struct server_data *server, struct request_data *req,
        char *dot, *lookup = (char *) name;
        struct cache_entry *entry;
 
-       entry = cache_check(request, &type);
+       entry = cache_check(request, &type, req->protocol);
        if (entry != NULL) {
                int ttl_left = 0;
                struct cache_data *data;
@@ -1664,6 +1684,12 @@ static int forward_dns_reply(unsigned char *reply, int reply_len, int protocol,
                close(sk);
        }
 
+       if (err < 0)
+               DBG("Cannot send msg, sk %d proto %d errno %d/%s", sk,
+                       protocol, errno, strerror(errno));
+       else
+               DBG("proto %d sent %d bytes to %d", protocol, err, sk);
+
        destroy_request_data(req);
 
        return err;
@@ -1709,7 +1735,8 @@ static void destroy_server(struct server_data *server)
 {
        GList *list;
 
-       DBG("interface %s server %s", server->interface, server->server);
+       DBG("interface %s server %s sock %d", server->interface, server->server,
+               g_io_channel_unix_get_fd(server->channel));
 
        server_list = g_slist_remove(server_list, server);
 
@@ -1787,7 +1814,7 @@ static gboolean tcp_server_event(GIOChannel *channel, GIOCondition condition,
        if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
                GSList *list;
 hangup:
-               DBG("TCP server channel closed");
+               DBG("TCP server channel closed, sk %d", sk);
 
                /*
                 * Discard any partial response which is buffered; better
@@ -1924,7 +1951,7 @@ hangup:
                        reply_len = reply_len_buf[1] | reply_len_buf[0] << 8;
                        reply_len += 2;
 
-                       DBG("TCP reply %d bytes", reply_len);
+                       DBG("TCP reply %d bytes from %d", reply_len, sk);
 
                        reply = g_try_malloc(sizeof(*reply) + reply_len + 2);
                        if (!reply)
@@ -2025,6 +2052,8 @@ static struct server_data *create_server(const char *interface,
                return NULL;
        }
 
+       DBG("sk %d", sk);
+
        if (interface != NULL) {
                if (setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE,
                                interface, strlen(interface) + 1) < 0) {
@@ -2421,7 +2450,8 @@ static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition,
        socklen_t client_addr_len = sizeof(client_addr);
        GSList *list;
        struct listener_data *ifdata = user_data;
-       int waiting_for_connect = FALSE;
+       int waiting_for_connect = FALSE, qtype = 0;
+       struct cache_entry *entry;
 
        DBG("condition 0x%x", condition);
 
@@ -2448,7 +2478,8 @@ static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition,
        if (len < 2)
                return TRUE;
 
-       DBG("Received %d bytes (id 0x%04x)", len, buf[2] | buf[3] << 8);
+       DBG("Received %d bytes (id 0x%04x) from %d", len,
+               buf[2] | buf[3] << 8, client_sk);
 
        err = parse_request(buf + 2, len - 2, query, sizeof(query));
        if (err < 0 || (g_slist_length(server_list) == 0)) {
@@ -2477,6 +2508,35 @@ static gboolean tcp_listener_event(GIOChannel *channel, GIOCondition condition,
        req->ifdata = (struct listener_data *) ifdata;
        req->append_domain = FALSE;
 
+       /*
+        * Check if the answer is found in the cache before
+        * creating sockets to the server.
+        */
+       entry = cache_check(buf, &qtype, IPPROTO_TCP);
+       if (entry != NULL) {
+               int ttl_left = 0;
+               struct cache_data *data;
+
+               DBG("cache hit %s type %s", query, qtype == 1 ? "A" : "AAAA");
+               if (qtype == 1)
+                       data = entry->ipv4;
+               else
+                       data = entry->ipv6;
+
+               if (data != NULL) {
+                       ttl_left = data->valid_until - time(NULL);
+                       entry->hits++;
+
+                       send_cached_response(client_sk, data->data,
+                                       data->data_len, NULL, 0, IPPROTO_TCP,
+                                       req->srcid, data->answers, ttl_left);
+
+                       g_free(req);
+                       return TRUE;
+               } else
+                       DBG("data missing, ignoring cache for this query");
+       }
+
        for (list = server_list; list; list = list->next) {
                struct server_data *data = list->data;