dnsproxy: Save DNS packet in cache always in TCP format
authorJukka Rissanen <jukka.rissanen@linux.intel.com>
Wed, 20 Jun 2012 10:38:35 +0000 (13:38 +0300)
committerPatrik Flykt <patrik.flykt@linux.intel.com>
Wed, 20 Jun 2012 11:11:00 +0000 (14:11 +0300)
The cached DNS packet will always contain two prefix bytes.
For UDP we do not send these two extra bytes. This way we do
not need to know if the cached message is from UDP or TCP query.

src/dnsproxy.c

index ee7cbf3..03d334f 100644 (file)
@@ -376,15 +376,32 @@ static void send_cached_response(int sk, unsigned char *buf, int len,
                                int protocol, int id, uint16_t answers, int ttl)
 {
        struct domain_hdr *hdr;
-       int err, offset = protocol_offset(protocol);
+       unsigned char *ptr = buf;
+       int err, offset, dns_len, adj_len = len - 2;
 
-       if (offset < 0)
+       /*
+        * The cached packet contains always the TCP offset (two bytes)
+        * so skip them for UDP.
+        */
+       switch (protocol) {
+       case IPPROTO_UDP:
+               ptr += 2;
+               len -= 2;
+               dns_len = len;
+               offset = 0;
+               break;
+       case IPPROTO_TCP:
+               offset = 2;
+               dns_len = ptr[0] * 256 + ptr[1];
+               break;
+       default:
                return;
+       }
 
        if (len < 12)
                return;
 
-       hdr = (void *) (buf + offset);
+       hdr = (void *) (ptr + offset);
 
        hdr->id = id;
        hdr->qr = 1;
@@ -397,16 +414,22 @@ static void send_cached_response(int sk, unsigned char *buf, int len,
        if (answers == 0)
                hdr->aa = 1;
        else
-               update_cached_ttl(buf, len, ttl);
+               update_cached_ttl((unsigned char *)hdr, adj_len, ttl);
 
-       DBG("id 0x%04x answers %d", hdr->id, answers);
+       DBG("sk %d id 0x%04x answers %d ptr %p length %d dns %d",
+               sk, hdr->id, answers, ptr, len, dns_len);
 
-       err = sendto(sk, buf, len, MSG_NOSIGNAL, to, tolen);
+       err = sendto(sk, ptr, len, MSG_NOSIGNAL, to, tolen);
        if (err < 0) {
                connman_error("Cannot send cached DNS response: %s",
                                strerror(errno));
                return;
        }
+
+       if (err != len || (dns_len != (len - 2) && protocol == IPPROTO_TCP) ||
+                               (dns_len != len && protocol == IPPROTO_UDP))
+               DBG("Packet length mismatch, sent %d wanted %d dns %d",
+                       err, len, dns_len);
 }
 
 static void send_response(int sk, unsigned char *buf, int len,
@@ -1354,7 +1377,12 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
        data->type = type;
        data->answers = answers;
        data->timeout = ttl;
-       data->data_len = 12 + qlen + 1 + 2 + 2 + rsplen;
+       /*
+        * The "2" in start of the length is the TCP offset. We allocate it
+        * here even for UDP packet because it simplifies the sending
+        * of cached packet.
+        */
+       data->data_len = 2 + 12 + qlen + 1 + 2 + 2 + rsplen;
        data->data = ptr = g_malloc(data->data_len);
        data->valid_until = current_time + ttl;
 
@@ -1374,13 +1402,24 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
                return -ENOMEM;
        }
 
-       memcpy(ptr, msg, 12);
-       memcpy(ptr + 12, question, qlen + 1); /* copy also the \0 */
+       /*
+        * We cache the two extra bytes at the start of the message
+        * in a TCP packet. When sending UDP packet, we skip the first
+        * two bytes. This way we do not need to know the format
+        * (UDP/TCP) of the cached message.
+        */
+       ptr[0] = (data->data_len - 2) / 256;
+       ptr[1] = (data->data_len - 2) - ptr[0] * 256;
+       if (srv->protocol == IPPROTO_UDP)
+               ptr += 2;
+
+       memcpy(ptr, msg, offset + 12);
+       memcpy(ptr + offset + 12, question, qlen + 1); /* copy also the \0 */
 
-       q = (void *) (ptr + 12 + qlen + 1);
+       q = (void *) (ptr + offset + 12 + qlen + 1);
        q->type = htons(type);
        q->class = htons(class);
-       memcpy(ptr + 12 + qlen + 1 + sizeof(struct domain_question),
+       memcpy(ptr + offset + 12 + qlen + 1 + sizeof(struct domain_question),
                response, rsplen);
 
        if (new_entry == TRUE) {
@@ -1388,10 +1427,15 @@ static int cache_update(struct server_data *srv, unsigned char *msg,
                cache_size++;
        }
 
-       DBG("cache %d %squestion \"%s\" type %d ttl %d size %zd",
+       DBG("cache %d %squestion \"%s\" type %d ttl %d size %zd packet %u "
+                                                               "dns len %u",
                cache_size, new_entry ? "new " : "old ",
                question, type, ttl,
-               sizeof(*entry) + sizeof(*data) + data->data_len + qlen);
+               sizeof(*entry) + sizeof(*data) + data->data_len + qlen,
+               data->data_len,
+               srv->protocol == IPPROTO_TCP ?
+                       (unsigned int)(data->data[0] * 256 + data->data[1]) :
+                       data->data_len);
 
        return 0;
 }