Cache negative DNS results.
Find the TTL for the negative results using the minimum of
the SOA records TTL or the MINIMUM-TTL field (RFC-2308).
bug:5926539
Change-Id: I6d39c9fb558afcb7a4a5bc014d97dab4a85c0d4f
diff --git a/libc/netbsd/resolv/res_cache.c b/libc/netbsd/resolv/res_cache.c
index 915fdf0..afc9a36 100644
--- a/libc/netbsd/resolv/res_cache.c
+++ b/libc/netbsd/resolv/res_cache.c
@@ -572,8 +572,6 @@
static int
_dnsPacket_checkQR( DnsPacket* packet )
{
- int len;
-
if (!_dnsPacket_checkQName(packet))
return 0;
@@ -832,8 +830,6 @@
static unsigned
_dnsPacket_hashQR( DnsPacket* packet, unsigned hash )
{
- int len;
-
hash = _dnsPacket_hashQName(packet, hash);
hash = _dnsPacket_hashBytes(packet, 4, hash); /* TYPE and CLASS */
return hash;
@@ -1020,8 +1016,58 @@
} Entry;
/**
- * Parse the answer records and find the smallest
- * TTL among the answer records.
+ * Find the TTL for a negative DNS result. This is defined as the minimum
+ * of the SOA records TTL and the MINIMUM-TTL field (RFC-2308).
+ *
+ * Return 0 if not found.
+ */
+static u_long
+answer_getNegativeTTL(ns_msg handle) {
+ int n, nscount;
+ u_long result = 0;
+ ns_rr rr;
+
+ nscount = ns_msg_count(handle, ns_s_ns);
+ for (n = 0; n < nscount; n++) {
+ if ((ns_parserr(&handle, ns_s_ns, n, &rr) == 0) && (ns_rr_type(rr) == ns_t_soa)) {
+ const u_char *rdata = ns_rr_rdata(rr); // find the data
+ const u_char *edata = rdata + ns_rr_rdlen(rr); // add the len to find the end
+ int len;
+ u_long ttl, rec_result = ns_rr_ttl(rr);
+
+ // find the MINIMUM-TTL field from the blob of binary data for this record
+ // skip the server name
+ len = dn_skipname(rdata, edata);
+ if (len == -1) continue; // error skipping
+ rdata += len;
+
+ // skip the admin name
+ len = dn_skipname(rdata, edata);
+ if (len == -1) continue; // error skipping
+ rdata += len;
+
+ if (edata - rdata != 5*NS_INT32SZ) continue;
+ // skip: serial number + refresh interval + retry interval + expiry
+ rdata += NS_INT32SZ * 4;
+ // finally read the MINIMUM TTL
+ ttl = ns_get32(rdata);
+ if (ttl < rec_result) {
+ rec_result = ttl;
+ }
+ // Now that the record is read successfully, apply the new min TTL
+ if (n == 0 || rec_result < result) {
+ result = rec_result;
+ }
+ }
+ }
+ return result;
+}
+
+/**
+ * Parse the answer records and find the appropriate
+ * smallest TTL among the records. This might be from
+ * the answer records if found or from the SOA record
+ * if it's a negative result.
*
* The returned TTL is the number of seconds to
* keep the answer in the cache.
@@ -1041,14 +1087,20 @@
if (ns_initparse(answer, answerlen, &handle) >= 0) {
// get number of answer records
ancount = ns_msg_count(handle, ns_s_an);
- for (n = 0; n < ancount; n++) {
- if (ns_parserr(&handle, ns_s_an, n, &rr) == 0) {
- ttl = ns_rr_ttl(rr);
- if (n == 0 || ttl < result) {
- result = ttl;
+
+ if (ancount == 0) {
+ // a response with no answers? Cache this negative result.
+ result = answer_getNegativeTTL(handle);
+ } else {
+ for (n = 0; n < ancount; n++) {
+ if (ns_parserr(&handle, ns_s_an, n, &rr) == 0) {
+ ttl = ns_rr_ttl(rr);
+ if (n == 0 || ttl < result) {
+ result = ttl;
+ }
+ } else {
+ XLOG("ns_parserr failed ancount no = %d. errno = %s\n", n, strerror(errno));
}
- } else {
- XLOG("ns_parserr failed ancount no = %d. errno = %s\n", n, strerror(errno));
}
}
} else {
@@ -1253,7 +1305,7 @@
} else {
struct timespec ts = {0,0};
ts.tv_sec = _time_now() + PENDING_REQUEST_TIMEOUT;
- int rv = pthread_cond_timedwait(&ri->cond, &cache->lock, &ts);
+ pthread_cond_timedwait(&ri->cond, &cache->lock, &ts);
}
}
@@ -1307,7 +1359,6 @@
_cache_flush_locked( Cache* cache )
{
int nn;
- time_t now = _time_now();
for (nn = 0; nn < cache->max_entries; nn++)
{
@@ -1443,6 +1494,7 @@
remove("/data/reslog.txt");
}
else {
+ errno = 0; // else debug is introducing error signals
XLOG("_dump_answer: can't open file\n");
}
}
@@ -1578,9 +1630,7 @@
int answersize,
int *answerlen )
{
- DnsPacket pack[1];
Entry key[1];
- int index;
Entry** lookup;
Entry* e;
time_t now;
@@ -1753,8 +1803,6 @@
static struct resolv_cache* _find_named_cache_locked(const char* ifname);
/* gets a resolv_cache_info associated with an interface name, or NULL if not found */
static struct resolv_cache_info* _find_cache_info_locked(const char* ifname);
-/* free dns name server list of a resolv_cache_info structure */
-static void _free_nameservers(struct resolv_cache_info* cache_info);
/* look up the named cache, and creates one if needed */
static struct resolv_cache* _get_res_cache_for_iface_locked(const char* ifname);
/* empty the named cache */