From 28c0f56aaf3f74c78f16b9443de4333ffdc398d6 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 20 Jun 2012 13:38:35 +0300 Subject: [PATCH] dnsproxy: Save DNS packet in cache always in TCP format 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 | 70 +++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/src/dnsproxy.c b/src/dnsproxy.c index ee7cbf3..03d334f 100644 --- a/src/dnsproxy.c +++ b/src/dnsproxy.c @@ -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; } -- 2.7.4