Blob Blame Raw
From e1132a3a41d2ff5a9479bbb629d73db4856a5f34 Mon Sep 17 00:00:00 2001
From: Tomas Hozza <thozza@redhat.com>
Date: Tue, 11 Oct 2016 15:38:58 +0200
Subject: [PATCH] Add cache-max-negative-ttl option

https://github.com/thozza/unbound/commit/0ef133ea5819fbf7518de9c8492c5e05c95ac8ce

git-svn-id: http://unbound.nlnetlabs.nl/svn/trunk@3431 be551aaa-1e26-0410-a405-d3ace91eadb9
Signed-off-by: Tomas Hozza <thozza@redhat.com>
---
 doc/example.conf.in                    |  3 +++
 doc/unbound-control.8.in               |  2 +-
 doc/unbound.conf.5.in                  |  4 ++++
 testcode/unitmsgparse.c                |  3 +++
 testdata/iter_domain_sale.rpl          |  3 ++-
 testdata/iter_domain_sale_nschange.rpl |  6 ++++--
 util/config_file.c                     |  5 +++++
 util/config_file.h                     |  2 ++
 util/configlexer.lex                   |  1 +
 util/configparser.y                    | 12 +++++++++++-
 util/data/msgparse.h                   |  2 ++
 util/data/msgreply.c                   | 29 ++++++++++++++++++++++++++---
 12 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/doc/example.conf.in b/doc/example.conf.in
index aa9a7f7..52cad67 100644
--- a/doc/example.conf.in
+++ b/doc/example.conf.in
@@ -125,6 +125,9 @@ server:
 	# cache. Items are not cached for longer. In seconds.
 	# cache-max-ttl: 86400
 
+	# the time to live (TTL) value cap for negative responses in the cache
+	# cache-max-negative-ttl: 3600
+
 	# the time to live (TTL) value for cached roundtrip times, lameness and
 	# EDNS version information for hosts. In seconds.
 	# infra-host-ttl: 900
diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in
index 669e81d..f2c76eb 100644
--- a/doc/unbound-control.8.in
+++ b/doc/unbound-control.8.in
@@ -170,7 +170,7 @@ harden\-glue, harden\-dnssec\-stripped, harden\-below\-nxdomain,
 harden\-referral\-path, prefetch, prefetch\-key, log\-queries,
 hide\-identity, hide\-version, identity, version, val\-log\-level,
 val\-log\-squelch, ignore\-cd\-flag, add\-holddown, del\-holddown,
-keep\-missing, tcp\-upstream, ssl\-upstream.
+keep\-missing, tcp\-upstream, ssl\-upstream, cache\-max\-negative\-ttl.
 .TP
 .B get_option \fIopt
 Get the value of the option.  Give the option name without a trailing ':'.
diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in
index 6dd0216..7485345 100644
--- a/doc/unbound.conf.5.in
+++ b/doc/unbound.conf.5.in
@@ -267,6 +267,10 @@ Zero makes sure the data in the cache is as the domain owner intended,
 higher values, especially more than an hour or so, can lead to trouble as 
 the data in the cache does not match up with the actual data any more.
 .TP
+.B cache\-max\-negative\-ttl: \fI<seconds>
+Time to live maximum for negative responses, these have a SOA in the
+authority section that is limited in time.  Default is 3600.
+.TP
 .B infra\-host\-ttl: \fI<seconds>
 Time to live for entries in the host cache. The host cache contains 
 roundtrip timing, lameness and EDNS support information. Default is 900.
diff --git a/testcode/unitmsgparse.c b/testcode/unitmsgparse.c
index 4342395..08f3b50 100644
--- a/testcode/unitmsgparse.c
+++ b/testcode/unitmsgparse.c
@@ -582,9 +582,11 @@ testfromdrillfile(ldns_buffer* pkt, struct alloc_cache* alloc,
 
 void msgparse_test(void)
 {
+	uint32_t origttl = MAX_NEG_TTL;
 	ldns_buffer* pkt = ldns_buffer_new(65553);
 	ldns_buffer* out = ldns_buffer_new(65553);
 	struct alloc_cache super_a, alloc;
+	MAX_NEG_TTL = 86400;
 	/* init */
 	alloc_init(&super_a, NULL, 0);
 	alloc_init(&alloc, &super_a, 2);
@@ -621,4 +623,5 @@ void msgparse_test(void)
 	alloc_clear(&super_a);
 	ldns_buffer_free(pkt);
 	ldns_buffer_free(out);
+	MAX_NEG_TTL = origttl;
 }
diff --git a/testdata/iter_domain_sale.rpl b/testdata/iter_domain_sale.rpl
index 724b51d..ff61278 100644
--- a/testdata/iter_domain_sale.rpl
+++ b/testdata/iter_domain_sale.rpl
@@ -238,7 +238,8 @@ SECTION QUESTION
 nx1.example.com. IN A
 SECTION ANSWER
 SECTION AUTHORITY
-example.com.	3600 IN SOA	a. b. 1 2 3 4 5
+; at TTL 5 because TTL is capped at min-ttl of 5 in rdata of SOA
+example.com.	5 IN SOA	a. b. 1 2 3 4 5
 example.com.	1800 IN NS	ns.example.com.
 SECTION ADDITIONAL
 ns.example.com.	1800 	IN 	A	1.2.3.4
diff --git a/testdata/iter_domain_sale_nschange.rpl b/testdata/iter_domain_sale_nschange.rpl
index a7d9f11..bc396f6 100644
--- a/testdata/iter_domain_sale_nschange.rpl
+++ b/testdata/iter_domain_sale_nschange.rpl
@@ -285,7 +285,8 @@ SECTION QUESTION
 nx1.example.com. IN A
 SECTION ANSWER
 SECTION AUTHORITY
-example.com.	3600 IN SOA	a. b. 1 2 3 4 5
+; at TTL 5 because TTL capped at ttl of minttl in rdata of SOA.
+example.com.	5 IN SOA	a. b. 1 2 3 4 5
 example.com.	3600 IN NS	nsb.example.com.
 SECTION ADDITIONAL
 nsb.example.com.	3600 	IN 	A	1.2.3.4
@@ -306,7 +307,8 @@ SECTION QUESTION
 nx1.example.com. IN A
 SECTION ANSWER
 SECTION AUTHORITY
-example.com.	3600 IN SOA	a. b. 1 2 3 4 5
+; at TTL 5 because TTL capped at ttl of minttl in rdata of SOA.
+example.com.	5 IN SOA	a. b. 1 2 3 4 5
 example.com.	1800 IN NS	nsb.example.com.
 SECTION ADDITIONAL
 nsb.example.com.	3600 	IN 	A	1.2.3.4
diff --git a/util/config_file.c b/util/config_file.c
index b946f0d..4e0fbe7 100644
--- a/util/config_file.c
+++ b/util/config_file.c
@@ -128,6 +128,7 @@ config_create(void)
 	cfg->bogus_ttl = 60;
 	cfg->min_ttl = 0;
 	cfg->max_ttl = 3600 * 24;
+	cfg->max_negative_ttl = 3600;
 	cfg->prefetch = 0;
 	cfg->prefetch_key = 0;
 	cfg->infra_cache_slabs = 4;
@@ -359,6 +360,8 @@ int config_set_option(struct config_file* cfg, const char* opt,
 	else S_YNO("prefetch:", prefetch)
 	else S_YNO("prefetch-key:", prefetch_key)
 	else S_NUMBER_OR_ZERO("cache-max-ttl:", max_ttl)
+	else if(strcmp(opt, "cache-max-negative-ttl:") == 0)
+	{ IS_NUMBER_OR_ZERO; cfg->max_negative_ttl = atoi(val); MAX_NEG_TTL=cfg->max_negative_ttl;}
 	else S_NUMBER_OR_ZERO("infra-host-ttl:", host_ttl)
 	else S_POW2("infra-cache-slabs:", infra_cache_slabs)
 	else S_SIZET_NONZERO("infra-cache-numhosts:", infra_cache_numhosts)
@@ -593,6 +596,7 @@ config_get_option(struct config_file* cfg, const char* opt,
 	else O_YNO(opt, "prefetch-key", prefetch_key)
 	else O_YNO(opt, "prefetch", prefetch)
 	else O_DEC(opt, "cache-max-ttl", max_ttl)
+	else O_DEC(opt, "cache-max-negative-ttl", max_negative_ttl)
 	else O_DEC(opt, "infra-host-ttl", host_ttl)
 	else O_DEC(opt, "infra-cache-slabs", infra_cache_slabs)
 	else O_MEM(opt, "infra-cache-numhosts", infra_cache_numhosts)
@@ -1149,6 +1153,7 @@ config_apply(struct config_file* config)
 {
 	MAX_TTL = (uint32_t)config->max_ttl;
 	MIN_TTL = (uint32_t)config->min_ttl;
+	MAX_NEG_TTL = (uint32_t)config->max_negative_ttl;
 	EDNS_ADVERTISED_SIZE = (uint16_t)config->edns_buffer_size;
 	MINIMAL_RESPONSES = config->minimal_responses;
 	RRSET_ROUNDROBIN = config->rrset_roundrobin;
diff --git a/util/config_file.h b/util/config_file.h
index 69595cb..4d493b8 100644
--- a/util/config_file.h
+++ b/util/config_file.h
@@ -179,6 +179,8 @@ struct config_file {
 	int max_ttl;
 	/** the number of seconds minimum TTL used for RRsets and messages */
 	int min_ttl;
+	/** the number of seconds maximal negative TTL for SOA in auth */
+	int max_negative_ttl;
 	/** if prefetching of messages should be performed. */
 	int prefetch;
 	/** if prefetching of DNSKEYs should be performed. */
diff --git a/util/configlexer.lex b/util/configlexer.lex
index 4694cdd..079f195 100644
--- a/util/configlexer.lex
+++ b/util/configlexer.lex
@@ -208,6 +208,7 @@ msg-cache-slabs{COLON}		{ YDVAR(1, VAR_MSG_CACHE_SLABS) }
 rrset-cache-size{COLON}		{ YDVAR(1, VAR_RRSET_CACHE_SIZE) }
 rrset-cache-slabs{COLON}	{ YDVAR(1, VAR_RRSET_CACHE_SLABS) }
 cache-max-ttl{COLON}     	{ YDVAR(1, VAR_CACHE_MAX_TTL) }
+cache-max-negative-ttl{COLON}   { YDVAR(1, VAR_CACHE_MAX_NEGATIVE_TTL) }
 cache-min-ttl{COLON}     	{ YDVAR(1, VAR_CACHE_MIN_TTL) }
 infra-host-ttl{COLON}		{ YDVAR(1, VAR_INFRA_HOST_TTL) }
 infra-lame-ttl{COLON}		{ YDVAR(1, VAR_INFRA_LAME_TTL) }
diff --git a/util/configparser.y b/util/configparser.y
index 0dbee2b..7d7147d 100644
--- a/util/configparser.y
+++ b/util/configparser.y
@@ -105,6 +105,7 @@ extern struct config_parser_state* cfg_parser;
 %token VAR_IGNORE_CD_FLAG VAR_LOG_QUERIES VAR_TCP_UPSTREAM VAR_SSL_UPSTREAM
 %token VAR_SSL_SERVICE_KEY VAR_SSL_SERVICE_PEM VAR_SSL_PORT VAR_FORWARD_FIRST
 %token VAR_STUB_FIRST VAR_MINIMAL_RESPONSES VAR_RRSET_ROUNDROBIN
+%token VAR_CACHE_MAX_NEGATIVE_TTL
 
 %%
 toplevelvars: /* empty */ | toplevelvars toplevelvar ;
@@ -161,7 +162,7 @@ content_server: server_num_threads | server_verbosity | server_port |
 	server_so_sndbuf | server_harden_below_nxdomain | server_ignore_cd_flag |
 	server_log_queries | server_tcp_upstream | server_ssl_upstream |
 	server_ssl_service_key | server_ssl_service_pem | server_ssl_port |
-	server_minimal_responses | server_rrset_roundrobin
+	server_minimal_responses | server_rrset_roundrobin | server_cache_max_negative_ttl
 	;
 stubstart: VAR_STUB_ZONE
 	{
@@ -934,6 +935,15 @@ server_cache_max_ttl: VAR_CACHE_MAX_TTL STRING_ARG
 		free($2);
 	}
 	;
+server_cache_max_negative_ttl: VAR_CACHE_MAX_NEGATIVE_TTL STRING_ARG
+	{
+		OUTYY(("P(server_cache_max_negative_ttl:%s)\n", $2));
+		if(atoi($2) == 0 && strcmp($2, "0") != 0)
+			yyerror("number expected");
+		else cfg_parser->cfg->max_negative_ttl = atoi($2);
+		free($2);
+	}
+	;
 server_cache_min_ttl: VAR_CACHE_MIN_TTL STRING_ARG
 	{
 		OUTYY(("P(server_cache_min_ttl:%s)\n", $2));
diff --git a/util/data/msgparse.h b/util/data/msgparse.h
index 830d68e..825d368 100644
--- a/util/data/msgparse.h
+++ b/util/data/msgparse.h
@@ -74,6 +74,8 @@ struct regional;
 extern uint32_t MAX_TTL;
 /** Minimum TTL that is allowed. */
 extern uint32_t MIN_TTL;
+/** Maximum Negative TTL that is allowed */
+extern uint32_t MAX_NEG_TTL;
 /** Negative cache time (for entries without any RRs.) */
 #define NORR_TTL 5 /* seconds */
 
diff --git a/util/data/msgreply.c b/util/data/msgreply.c
index 6d711ff..dce724d 100644
--- a/util/data/msgreply.c
+++ b/util/data/msgreply.c
@@ -56,6 +56,8 @@
 uint32_t MAX_TTL = 3600 * 24 * 10; /* ten days */
 /** MIN TTL default for messages and rrsets */
 uint32_t MIN_TTL = 0;
+/** MAX Negative TTL, for SOA records in authority section */
+uint32_t MAX_NEG_TTL = 3600; /* one hour */
 
 /** allocate qinfo, return 0 on error */
 static int
@@ -151,10 +153,23 @@ repinfo_alloc_rrset_keys(struct reply_info* rep, struct alloc_cache* alloc,
 	return 1;
 }
 
+/** find the minimumttl in the rdata of SOA record */
+static uint32_t
+soa_find_minttl(struct rr_parse* rr)
+{
+	uint16_t rlen = ldns_read_uint16(rr->ttl_data+4);
+	if(rlen < 20)
+		return 0; /* rdata too small for SOA (dname, dname, 5*32bit) */
+	/* minimum TTL is the last 32bit value in the rdata of the record */
+	/* at position ttl_data + 4(ttl) + 2(rdatalen) + rdatalen - 4(timeval)*/
+	return ldns_read_uint32(rr->ttl_data+6+rlen-4);
+}
+
 /** do the rdata copy */
 static int
 rdata_copy(ldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to, 
-	struct rr_parse* rr, uint32_t* rr_ttl, uint16_t type)
+	struct rr_parse* rr, uint32_t* rr_ttl, uint16_t type,
+	ldns_pkt_section section)
 {
 	uint16_t pkt_len;
 	const ldns_rr_descriptor* desc;
@@ -163,6 +178,14 @@ rdata_copy(ldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to,
 	/* RFC 2181 Section 8. if msb of ttl is set treat as if zero. */
 	if(*rr_ttl & 0x80000000U)
 		*rr_ttl = 0;
+	if(type == LDNS_RR_TYPE_SOA && section == LDNS_SECTION_AUTHORITY) {
+		/* negative response. see if TTL of SOA record larger than the
+		 * minimum-ttl in the rdata of the SOA record */
+		if(*rr_ttl > soa_find_minttl(rr))
+			*rr_ttl = soa_find_minttl(rr);
+		if(*rr_ttl > MAX_NEG_TTL)
+			*rr_ttl = MAX_NEG_TTL;
+	}
 	if(*rr_ttl < MIN_TTL)
 		*rr_ttl = MIN_TTL;
 	if(*rr_ttl < data->ttl)
@@ -252,7 +275,7 @@ parse_rr_copy(ldns_buffer* pkt, struct rrset_parse* pset,
 		data->rr_data[i] = nextrdata;
 		nextrdata += rr->size;
 		if(!rdata_copy(pkt, data, data->rr_data[i], rr, 
-			&data->rr_ttl[i], pset->type))
+			&data->rr_ttl[i], pset->type, pset->section))
 			return 0;
 		rr = rr->next;
 	}
@@ -263,7 +286,7 @@ parse_rr_copy(ldns_buffer* pkt, struct rrset_parse* pset,
 		data->rr_data[i] = nextrdata;
 		nextrdata += rr->size;
 		if(!rdata_copy(pkt, data, data->rr_data[i], rr, 
-			&data->rr_ttl[i], LDNS_RR_TYPE_RRSIG))
+			&data->rr_ttl[i], LDNS_RR_TYPE_RRSIG, pset->section))
 			return 0;
 		rr = rr->next;
 	}
-- 
2.7.4