From c008187195ccf10b70c5aba319b0fc73932db1f2 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Mon, 9 Jan 2012 16:08:37 -0800 Subject: [PATCH] dnsproxy: Cache negative IPv6 entries for the case where we have an IPv4 entry already It's very common for a client to ask for both IPv4 and IPv6 entries for a name, however until now, Connman would only cache the IPv4 entry. The result is that the client app still has the full latency of a lookup, the one for the AAAA record. This patch will cause Connman to cache negative IPv6 entries, but only if a valid IPv4 entry is already in the cache. The TTL for this negative entry is set to the TTL of the existing IPv4 entry, with the rationale that these entries go together anyway. --- src/dnsproxy.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/src/dnsproxy.c b/src/dnsproxy.c index c8b3229..c373ae1 100644 --- a/src/dnsproxy.c +++ b/src/dnsproxy.c @@ -337,7 +337,11 @@ static void send_cached_response(int sk, unsigned char *buf, int len, hdr->nscount = 0; hdr->arcount = 0; - update_cached_ttl(buf, len, ttl); + /* if this is a negative reply, we are authorative */ + if (answers == 0) + hdr->aa = 1; + else + update_cached_ttl(buf, len, ttl); DBG("id 0x%04x answers %d", hdr->id, answers); @@ -995,12 +999,35 @@ static void cache_cleanup(void) max_timeout = 0; } +static int reply_query_type(unsigned char *msg, int len) +{ + unsigned char *c; + uint16_t *w; + int l; + int type; + + /* skip the header */ + c = msg + sizeof(struct domain_hdr); + len -= sizeof(struct domain_hdr); + + if (len < 0) + return 0; + + /* now the query, which is a name and 2 16 bit words */ + l = dns_name_length(c) + 1; + c += l; + len -= l; + w = (uint16_t *) c; + type = ntohs(*w); + return type; +} + static int cache_update(struct server_data *srv, unsigned char *msg, unsigned int msg_len) { int offset = protocol_offset(srv->protocol); int err, qlen, ttl = 0; - uint16_t answers, type = 0, class = 0; + uint16_t answers = 0, type = 0, class = 0; struct domain_question *q; struct cache_entry *entry; struct cache_data *data; @@ -1031,6 +1058,33 @@ static int cache_update(struct server_data *srv, unsigned char *msg, question, sizeof(question) - 1, &type, &class, &ttl, response, &rsplen, &answers); + + /* + * special case: if we do a ipv6 lookup and get no result + * for a record that's already in our ipv4 cache.. we want + * to cache the negative response. + */ + if ((err == -ENOMSG || err == -ENOBUFS) && + reply_query_type(msg, msg_len) == 28) { + entry = g_hash_table_lookup(cache, question); + if (entry && entry->ipv4 && entry->ipv6 == NULL) { + 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->timeout = entry->ipv4->timeout; + data->data_len = msg_len; + data->data = ptr = g_malloc(msg_len); + data->valid_until = entry->ipv4->valid_until; + data->cache_until = entry->ipv4->cache_until; + memcpy(data->data, msg, msg_len); + entry->ipv6 = data; + return 0; + } + } + if (err < 0 || ttl == 0) return 0; -- 2.7.4