diff -Nurp bind9-9.7.0.dfsg.P1.orig/bin/named/config.c bind9-9.7.0.dfsg.P1/bin/named/config.c
--- bind9-9.7.0.dfsg.P1.orig/bin/named/config.c	2012-09-25 17:27:09.000000000 -0700
+++ bind9-9.7.0.dfsg.P1/bin/named/config.c	2012-09-25 17:27:11.000000000 -0700
@@ -143,6 +143,11 @@ options {\n\
 	max-cache-ttl 604800; /* 1 week */\n\
 	min-ncache-ttl 0; /* 0 hours */\n\
 	min-cache-ttl 0; /* 0 seconds */\n\
+	max-stale-ttl 0; /* 0 seconds = serve stale feature disabled */\n\
+	stale-answer-ttl 1; /* 1 second */\n\
+	resolver-max-time 30000; /* in milliseconds */\n\
+	resolver-retry-interval 800; /* in milliseconds */\n\
+	resolver-nonbackoff-tries 3;\n\
 	transfer-format many-answers;\n\
 	max-cache-size 0;\n\
 	check-names master fail;\n\
diff -Nurp bind9-9.7.0.dfsg.P1.orig/bin/named/include/named/server.h bind9-9.7.0.dfsg.P1/bin/named/include/named/server.h
--- bind9-9.7.0.dfsg.P1.orig/bin/named/include/named/server.h	2012-09-25 17:27:09.000000000 -0700
+++ bind9-9.7.0.dfsg.P1/bin/named/include/named/server.h	2012-09-25 17:27:11.000000000 -0700
@@ -166,7 +166,10 @@ enum {
 	dns_nsstatscounter_updatefail = 34,
 	dns_nsstatscounter_updatebadprereq = 35,
 
-	dns_nsstatscounter_max = 36
+	dns_nsstatscounter_trystale = 36,
+	dns_nsstatscounter_usedstale = 37,
+
+	dns_nsstatscounter_max = 38
 };
 
 void
diff -Nurp bind9-9.7.0.dfsg.P1.orig/bin/named/query.c bind9-9.7.0.dfsg.P1/bin/named/query.c
--- bind9-9.7.0.dfsg.P1.orig/bin/named/query.c	2012-09-25 17:27:09.000000000 -0700
+++ bind9-9.7.0.dfsg.P1/bin/named/query.c	2012-09-25 17:27:11.000000000 -0700
@@ -3196,6 +3196,8 @@ query_recurse(ns_client_t *client, dns_r
 	dns_rdataset_t *rdataset, *sigrdataset;
 	isc_sockaddr_t *peeraddr;
 
+	CTRACE("query_recurse");
+
 	if (!resuming)
 		inc_stats(client, dns_nsstatscounter_recursion);
 
@@ -3717,6 +3719,7 @@ query_find(ns_client_t *client, dns_fetc
 	dns_rdata_t rdata = DNS_RDATA_INIT;
 	dns_rdatasetiter_t *rdsiter;
 	isc_boolean_t want_restart, authoritative, is_zone, need_wildcardproof;
+	isc_boolean_t want_stale;
 	unsigned int n, nlabels;
 	dns_namereln_t namereln;
 	int order;
@@ -3762,6 +3765,7 @@ query_find(ns_client_t *client, dns_fetc
 	options = 0;
 	resuming = ISC_FALSE;
 	is_zone = ISC_FALSE;
+	want_stale = ISC_FALSE;
 
 	if (event != NULL) {
 		/*
@@ -3955,6 +3959,56 @@ query_find(ns_client_t *client, dns_fetc
 			     client->query.dboptions, client->now,
 			     &node, fname, rdataset, sigrdataset);
 
+	if (want_stale) {
+		/*
+		 * Tried another cache lookup.  If it found stale data,
+		 * this might be good enough to make forward progress even
+		 * if it wasn't immediately the answer that was sought,
+		 * but that effort should try for fresh data.
+		 */
+		isc_boolean_t success;
+		client->query.dboptions &= ~DNS_DBFIND_STALEOK;
+		want_stale = ISC_FALSE;
+
+		if (dns_rdataset_isassociated(rdataset) &&
+		    dns_rdataset_count(rdataset) > 0 &&
+		    (isc_int32_t)rdataset->ttl < 1) {
+			/*
+			 * A TTL less than 0 means something stale was found.
+			 * (Is this true for NXDOMAIN/NODATA as well? XXX)
+			 * Can't responsibly return the negative value as a
+			 * huge unsigned TTL though, so set it to a configured
+			 * value, expected to be 1 second by default.
+			 */
+			rdataset->ttl = client->view->staleanswerttl;
+			inc_stats(client, dns_nsstatscounter_usedstale);
+			success = ISC_TRUE;
+		} else {
+			/*
+			 * If something with a sane TTL > 0 was found then
+			 * it is almost always the case that it was the same
+			 * thing that got found the first time and was found
+			 * to be not useful enough.  It is conceivable, though,
+			 * that it was something closer to the answer that
+			 * got cached, but still not enough that the resolver
+			 * could finish its job properly.
+			 */
+			success = ISC_FALSE;
+		}
+
+		char namebuf[DNS_NAME_FORMATSIZE];
+		dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
+		isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+			      NS_LOGMODULE_QUERY, ISC_LOG_INFO,
+			      "%s resolver failure, stale answer %s",
+			      namebuf, success ? "used" : "unavailable");
+
+		if (! success) {
+			QUERY_ERROR(DNS_R_SERVFAIL);
+			goto cleanup;
+		}
+	}
+
  resume:
 	CTRACE("query_find: resume");
 	switch (result) {
@@ -4143,6 +4197,9 @@ query_find(ns_client_t *client, dns_fetc
 				goto db_find;
 			}
 		} else {
+			/*
+			 * ! is_zone, therefor result is from the cache.
+			 */
 			if (zfname != NULL &&
 			    !dns_name_issubdomain(fname, zfname)) {
 				/*
@@ -4625,6 +4682,15 @@ query_find(ns_client_t *client, dns_fetc
 		/*
 		 * Something has gone wrong.
 		 */
+		if (resuming) {
+			/*
+			 * Just came back from the resolver.
+			 * Maybe we have some cached stale data to use.
+			 */
+			want_stale = ISC_TRUE;
+			goto cleanup;
+		}
+
 		QUERY_ERROR(DNS_R_SERVFAIL);
 		goto cleanup;
 	}
@@ -4994,6 +5060,33 @@ query_find(ns_client_t *client, dns_fetc
 		goto restart;
 	}
 
+	/*
+	 * The resolver failed to find an answer.  Make one last attempt to
+	 * find something useful in the cache, even if it is expired.
+	 */
+	if (want_stale) {
+		dns_ttl_t stale_ttl = 0;
+
+		db = NULL;
+		dns_db_attach(client->view->cachedb, &db);
+
+		if (dns_db_getservestalettl(db, &stale_ttl) == ISC_R_SUCCESS &&
+		    stale_ttl > 0) {
+			client->query.dboptions |= DNS_DBFIND_STALEOK;
+
+			inc_stats(client, dns_nsstatscounter_trystale);
+
+			if (client->query.fetch != NULL)
+				dns_resolver_destroyfetch(&client->query.fetch);
+			goto db_find;
+		}
+
+		dns_db_detach(&db);
+		want_stale = ISC_FALSE;
+		QUERY_ERROR(DNS_R_SERVFAIL);
+		goto cleanup;
+	}
+
 	if (eresult != ISC_R_SUCCESS &&
 	    (!PARTIALANSWER(client) || WANTRECURSION(client))) {
 		if (eresult == DNS_R_DUPLICATE || eresult == DNS_R_DROP) {
diff -Nurp bind9-9.7.0.dfsg.P1.orig/bin/named/server.c bind9-9.7.0.dfsg.P1/bin/named/server.c
--- bind9-9.7.0.dfsg.P1.orig/bin/named/server.c	2012-09-25 17:27:09.000000000 -0700
+++ bind9-9.7.0.dfsg.P1/bin/named/server.c	2012-09-25 17:27:11.000000000 -0700
@@ -1233,7 +1233,8 @@ static isc_boolean_t
 cache_sharable(dns_view_t *originview, dns_view_t *view,
 	       isc_boolean_t new_zero_no_soattl,
 	       unsigned int new_cleaning_interval,
-	       isc_uint32_t new_max_cache_size)
+	       isc_uint32_t new_max_cache_size,
+	       isc_uint32_t new_stale_ttl)
 {
 	/*
 	 * If the cache cannot even reused for the same view, it cannot be
@@ -1248,6 +1249,7 @@ cache_sharable(dns_view_t *originview, d
 	 */
 	if (dns_cache_getcleaninginterval(originview->cache) !=
 	    new_cleaning_interval ||
+	    dns_cache_getservestalettl(originview->cache) != new_stale_ttl ||
 	    dns_cache_getcachesize(originview->cache) != new_max_cache_size) {
 		return (ISC_FALSE);
 	}
@@ -1293,6 +1295,7 @@ configure_view(dns_view_t *view, const c
 	isc_uint32_t max_cache_size;
 	isc_uint32_t max_acache_size;
 	isc_uint32_t lame_ttl;
+	isc_uint32_t stale_ttl;
 	dns_tsig_keyring_t *ring = NULL;
 	dns_view_t *pview = NULL;	/* Production view */
 	isc_mem_t *cmctx;
@@ -1540,6 +1543,11 @@ configure_view(dns_view_t *view, const c
 	if (view->maxncachettl > 7 * 24 * 3600)
 		view->maxncachettl = 7 * 24 * 3600;
 
+	obj = NULL;
+	result = ns_config_get(maps, "max-stale-ttl", &obj);
+	INSIST(result == ISC_R_SUCCESS);
+	stale_ttl = cfg_obj_asuint32(obj);
+
 	/*
 	 * Configure the view's cache.
 	 *
@@ -1573,7 +1581,8 @@ configure_view(dns_view_t *view, const c
 	nsc = cachelist_find(cachelist, cachename);
 	if (nsc != NULL) {
 		if (!cache_sharable(nsc->primaryview, view, zero_no_soattl,
-				    cleaning_interval, max_cache_size)) {
+				    cleaning_interval, max_cache_size,
+				    stale_ttl)) {
 			isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
 				      NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
 				      "views %s and %s can't share the cache "
@@ -1656,16 +1665,63 @@ configure_view(dns_view_t *view, const c
 	obj = NULL;
 	result = ns_config_get(maps, "cache-file", &obj);
 	if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") != 0) {
-		CHECK(dns_cache_setfilename(cache, cfg_obj_asstring(obj)));
-		if (!reused_cache && !shared_cache)
-			CHECK(dns_cache_load(cache));
+	    const char* cachefile = cfg_obj_asstring(obj);
+		CHECK(dns_cache_setfilename(cache, cachefile));
+		if (!reused_cache && !shared_cache) {
+			if (isc_file_exists(cachefile)) {
+				char load[ISC_DIR_PATHMAX];
+
+				if (strlcpy(load, cachefile,
+					    sizeof(load)) >= sizeof(load) ||
+				    strlcat(load, ".old",
+					    sizeof(load)) >= sizeof(load))
+					result = ISC_R_INVALIDFILE;
+				else
+					result = isc_file_rename(cachefile,
+								 load);
+
+				if (result != ISC_R_SUCCESS)
+					isc_log_write(ns_g_lctx,
+						      NS_LOGCATEGORY_GENERAL,
+						      NS_LOGMODULE_SERVER,
+						      ISC_LOG_ERROR,
+						      "rename(%s, %s): %s",
+						      cachefile, load,
+						    isc_result_totext(result));
+				else {
+				    /*
+				     * Failure to load the cache should not
+				     * prevent named from starting.
+				     */
+				    CHECK(dns_cache_setfilename(cache, load));
+				    result = dns_cache_load(cache);
+				}
+
+				if (result != ISC_R_SUCCESS)
+					isc_log_write(ns_g_lctx,
+						      NS_LOGCATEGORY_GENERAL,
+						      NS_LOGMODULE_SERVER,
+						      ISC_LOG_ERROR,
+						      "loading old cache %s: "
+						      "%s", load,
+						    isc_result_totext(result));
+
+				CHECK(dns_cache_setfilename(cache, cachefile));
+			}
+		}
 	}
 
 	dns_cache_setcleaninginterval(cache, cleaning_interval);
 	dns_cache_setcachesize(cache, max_cache_size);
+	dns_cache_setservestalettl(cache, stale_ttl);
 
 	dns_cache_detach(&cache);
 
+	obj = NULL;
+	result = ns_config_get(maps, "stale-answer-ttl", &obj);
+	INSIST(result == ISC_R_SUCCESS);
+	view->staleanswerttl = ISC_MAX(cfg_obj_asuint32(obj), 1);
+
 	/*
 	 * Resolver.
 	 *
@@ -1760,6 +1816,28 @@ configure_view(dns_view_t *view, const c
 	view->maxudp = udpsize;
 
 	/*
+	 * Set resolver retry parameters.
+	 */
+	unsigned int resolver_param;
+	obj = NULL;
+	CHECK(ns_config_get(maps, "resolver-max-time", &obj));
+	resolver_param = cfg_obj_asuint32(obj);
+	if (resolver_param > 0)
+	    dns_resolver_setmaxtime(view->resolver, resolver_param);
+
+	obj = NULL;
+	CHECK(ns_config_get(maps, "resolver-retry-interval", &obj));
+	resolver_param = cfg_obj_asuint32(obj);
+	if (resolver_param > 0)
+	    dns_resolver_setretryinterval(view->resolver, resolver_param);
+
+	obj = NULL;
+	CHECK(ns_config_get(maps, "resolver-nonbackoff-tries", &obj));
+	resolver_param = cfg_obj_asuint32(obj);
+	if (resolver_param > 0)
+	    dns_resolver_setnonbackofftries(view->resolver, resolver_param);
+
+	/*
 	 * Set supported DNSSEC algorithms.
 	 */
 	dns_resolver_reset_algorithms(view->resolver);
diff -Nurp bind9-9.7.0.dfsg.P1.orig/bin/named/statschannel.c bind9-9.7.0.dfsg.P1/bin/named/statschannel.c
--- bind9-9.7.0.dfsg.P1.orig/bin/named/statschannel.c	2012-09-25 17:27:09.000000000 -0700
+++ bind9-9.7.0.dfsg.P1/bin/named/statschannel.c	2012-09-25 17:27:11.000000000 -0700
@@ -198,6 +198,12 @@ init_desc(void) {
 	SET_NSSTATDESC(updatebadprereq,
 		       "updates rejected due to prerequisite failure",
 		       "UpdateBadPrereq");
+	SET_NSSTATDESC(trystale,
+		       "attempts to use stale cache data after lookup failure",
+		       "QryTryStale");
+	SET_NSSTATDESC(usedstale,
+		    "successful uses of stale cache data after lookup failure",
+		       "QryUsedStale");
 	INSIST(i == dns_nsstatscounter_max);
 
 	/* Initialize resolver statistics */
diff -Nurp bind9-9.7.0.dfsg.P1.orig/debian/changelog bind9-9.7.0.dfsg.P1/debian/changelog
--- bind9-9.7.0.dfsg.P1.orig/debian/changelog	2012-09-25 17:27:10.000000000 -0700
+++ bind9-9.7.0.dfsg.P1/debian/changelog	2012-09-25 17:27:34.000000000 -0700
@@ -1,3 +1,9 @@
+bind9 (1:9.7.0.dfsg.P1-1ubuntu0.7aka8) akamai; urgency=low
+
+  * CR52806: preserve stale cache records and serve if cannot be refreshed.
+
+ -- David Lawrence <tale@akamai.com>  Thu, 7 Apr 2011 11:41:02 +0000
+
 bind9 (1:9.7.0.dfsg.P1-1ubuntu0.7aka7) akamai; urgency=low
 
   * Restrict the source port range to 32768-65535
diff -Nurp bind9-9.7.0.dfsg.P1.orig/lib/dns/cache.c bind9-9.7.0.dfsg.P1/lib/dns/cache.c
--- bind9-9.7.0.dfsg.P1.orig/lib/dns/cache.c	2012-09-25 17:27:10.000000000 -0700
+++ bind9-9.7.0.dfsg.P1/lib/dns/cache.c	2012-09-25 17:27:11.000000000 -0700
@@ -134,6 +134,7 @@ struct dns_cache {
 	int			db_argc;
 	char			**db_argv;
 	isc_uint32_t		size;
+	dns_ttl_t		serve_stale_ttl;
 
 	/* Locked by 'filelock'. */
 	char			*filename;
@@ -162,9 +163,13 @@ overmem_cleaning_action(isc_task_t *task
 
 static inline isc_result_t
 cache_create_db(dns_cache_t *cache, dns_db_t **db) {
-	return (dns_db_create(cache->mctx, cache->db_type, dns_rootname,
-			      dns_dbtype_cache, cache->rdclass,
-			      cache->db_argc, cache->db_argv, db));
+	isc_result_t result;
+	result = dns_db_create(cache->mctx, cache->db_type, dns_rootname,
+			       dns_dbtype_cache, cache->rdclass,
+			       cache->db_argc, cache->db_argv, db);
+	if (result == ISC_R_SUCCESS)
+		dns_db_setservestalettl(*db, cache->serve_stale_ttl);
+	return (result);
 }
 
 isc_result_t
@@ -1044,6 +1049,32 @@ dns_cache_getcachesize(dns_cache_t *cach
 	return (size);
 }
 
+void
+dns_cache_setservestalettl(dns_cache_t *cache, dns_ttl_t ttl) {
+	REQUIRE(VALID_CACHE(cache));
+
+	LOCK(&cache->lock);
+	cache->serve_stale_ttl = ttl;
+	UNLOCK(&cache->lock);
+
+	(void)dns_db_setservestalettl(cache->db, ttl);
+}
+
+dns_ttl_t
+dns_cache_getservestalettl(dns_cache_t *cache) {
+	dns_ttl_t ttl;
+	isc_result_t result;
+
+	REQUIRE(VALID_CACHE(cache));
+
+	/*
+	 * Could get it straight from the dns_cache_t, but use db
+	 * to confirm the value that the db is really using.
+	 */
+	result = dns_db_getservestalettl(cache->db, &ttl);
+	return result == ISC_R_SUCCESS ? ttl : 0;
+}
+
 /*
  * The cleaner task is shutting down; do the necessary cleanup.
  */
diff -Nurp bind9-9.7.0.dfsg.P1.orig/lib/dns/db.c bind9-9.7.0.dfsg.P1/lib/dns/db.c
--- bind9-9.7.0.dfsg.P1.orig/lib/dns/db.c	2012-09-25 17:27:10.000000000 -0700
+++ bind9-9.7.0.dfsg.P1/lib/dns/db.c	2012-09-25 17:27:11.000000000 -0700
@@ -944,3 +944,25 @@ dns_db_resigned(dns_db_t *db, dns_rdatas
 	if (db->methods->resigned != NULL)
 		(db->methods->resigned)(db, rdataset, version);
 }
+
+isc_result_t
+dns_db_setservestalettl(dns_db_t *db, dns_ttl_t ttl)
+{
+	REQUIRE(DNS_DB_VALID(db));
+	REQUIRE((db->attributes & DNS_DBATTR_CACHE) != 0);
+
+	if (db->methods->setservestalettl != NULL)
+		return ((db->methods->setservestalettl)(db, ttl));
+	return (ISC_R_NOTIMPLEMENTED);
+}
+
+isc_result_t
+dns_db_getservestalettl(dns_db_t *db, dns_ttl_t *ttl)
+{
+	REQUIRE(DNS_DB_VALID(db));
+	REQUIRE((db->attributes & DNS_DBATTR_CACHE) != 0);
+
+	if (db->methods->getservestalettl != NULL)
+		return ((db->methods->getservestalettl)(db, ttl));
+	return (ISC_R_NOTIMPLEMENTED);
+}
diff -Nurp bind9-9.7.0.dfsg.P1.orig/lib/dns/include/dns/cache.h bind9-9.7.0.dfsg.P1/lib/dns/include/dns/cache.h
--- bind9-9.7.0.dfsg.P1.orig/lib/dns/include/dns/cache.h	2012-09-25 17:27:10.000000000 -0700
+++ bind9-9.7.0.dfsg.P1/lib/dns/include/dns/cache.h	2012-09-25 17:27:11.000000000 -0700
@@ -256,6 +256,27 @@ dns_cache_getcachesize(dns_cache_t *cach
  * Get the maximum cache size.
  */
 
+void
+dns_cache_setservestalettl(dns_cache_t *cache, dns_ttl_t ttl);
+/*%<
+ * Sets the length of time that cached answers are retained past their normal
+ * TTL, for use if the resolver fails to obtain a fresher answer.  Default
+ * value for the library is 0, disabling the use of stale data.  Akamai's
+ * named uses a non-zero default value, set in bin/named/conig.c
+ *
+ * Requires:
+ *\li	'cache' to be valid.
+ */
+
+dns_ttl_t
+dns_cache_getservestalettl(dns_cache_t *cache);
+/*%<
+ * Gets the length of time that cached answers are kept past normal expiry.
+ *
+ * Requires:
+ *\li	'cache' to be valid.
+ */
+
 isc_result_t
 dns_cache_flush(dns_cache_t *cache);
 /*%<
diff -Nurp bind9-9.7.0.dfsg.P1.orig/lib/dns/include/dns/db.h bind9-9.7.0.dfsg.P1/lib/dns/include/dns/db.h
--- bind9-9.7.0.dfsg.P1.orig/lib/dns/include/dns/db.h	2012-09-25 17:27:10.000000000 -0700
+++ bind9-9.7.0.dfsg.P1/lib/dns/include/dns/db.h	2012-09-25 17:27:11.000000000 -0700
@@ -170,6 +170,8 @@ typedef struct dns_dbmethods {
 					   dns_dbversion_t *version);
 	isc_boolean_t	(*isdnssec)(dns_db_t *db);
 	dns_stats_t	*(*getrrsetstats)(dns_db_t *db);
+	isc_result_t	(*setservestalettl)(dns_db_t *db, dns_ttl_t ttl);
+	isc_result_t	(*getservestalettl)(dns_db_t *db, dns_ttl_t *ttl);
 } dns_dbmethods_t;
 
 typedef isc_result_t
@@ -217,6 +219,7 @@ struct dns_db {
 #define DNS_DBFIND_COVERINGNSEC		0x0040
 #define DNS_DBFIND_FORCENSEC3		0x0080
 #define DNS_DBFIND_ADDITIONALOK		0x0100
+#define DNS_DBFIND_STALEOK		0x2000
 /*@}*/
 
 /*@{*/
@@ -1487,6 +1490,36 @@ dns_db_getrrsetstats(dns_db_t *db);
  *	dns_rdatasetstats_create(); otherwise NULL.
  */
 
+isc_result_t
+dns_db_setservestalettl(dns_db_t *db, dns_ttl_t ttl);
+/*%<
+ * Sets the length of time that cached answers are retained past their normal
+ * TTL, for use if the resolver fails to obtain a fresher answer.  Default
+ * value for the library is 0, disabling the use of stale data.  Akamai's
+ * named uses a non-zero default value, set in bin/named/config.c.
+ *
+ * Requires:
+ * \li	'db' is a valid cache database.
+ * \li	'ttl' is the number of seconds to retain data past its normal expiry.
+ *
+ * Returns:
+ * \li	#ISC_R_SUCCESS
+ * \li	#ISC_R_NOTIMPLEMENTED - Not supported by this DB implementation.
+ */
+
+isc_result_t
+dns_db_getservestalettl(dns_db_t *db, dns_ttl_t *ttl);
+/* Gets length of time that cached answers are kept past normal TTL expiration.
+ *
+ * Requires:
+ * \li	'db' is a valid cache database.
+ * \li	'ttl' is the number of seconds to retain data past its normal expiry.
+ *
+ * Returns:
+ * \li	#ISC_R_SUCCESS
+ * \li	#ISC_R_NOTIMPLEMENTED - Not supported by this DB implementation.
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_DB_H */
diff -Nurp bind9-9.7.0.dfsg.P1.orig/lib/dns/include/dns/resolver.h bind9-9.7.0.dfsg.P1/lib/dns/include/dns/resolver.h
--- bind9-9.7.0.dfsg.P1.orig/lib/dns/include/dns/resolver.h	2012-09-25 17:27:10.000000000 -0700
+++ bind9-9.7.0.dfsg.P1/lib/dns/include/dns/resolver.h	2012-09-25 17:27:11.000000000 -0700
@@ -507,6 +507,53 @@ void
 dns_resolver_setzeronosoattl(dns_resolver_t *resolver, isc_boolean_t state);
 
 unsigned int
+dns_resolver_getmaxtime(dns_resolver_t *resolver);
+
+void
+dns_resolver_setmaxtime(dns_resolver_t *resolver, unsigned int maxtime);
+/*%<
+ * Sets the maximum amount of milliseconds the resolver will spend on a query.
+ * Defaults to 30,000 milliseconds (30 seconds); silently capped at 30 seconds.
+ *
+ * Requires:
+ * \li	resolver to be valid.
+ * \li  maxtime > 0.
+ */
+
+unsigned int
+dns_resolver_getretryinterval(dns_resolver_t *resolver);
+
+void
+dns_resolver_setretryinterval(dns_resolver_t *resolver, unsigned int interval);
+/*%<
+ * Sets the amount of time, in millseconds, that is waited for a reply
+ * to a server before another server is tried.  Interacts with the
+ * value of dns_resolver_getnonbackofftries() by trying that number of times
+ * at this interval, before doing exponential backoff and doubling the interval
+ * on each subsequent try, to a maximum of 10 seconds.  Defaults to 800 ms;
+ * silently capped at 2000 ms.
+ *
+ * Requires:
+ * \li	resolver to be valid.
+ * \li  interval > 0.
+ */
+
+unsigned int
+dns_resolver_getnonbackofftries(dns_resolver_t *resolver);
+
+void
+dns_resolver_setnonbackofftries(dns_resolver_t *resolver, unsigned int tries);
+/*%<
+ * Sets the number of failures of getting a reply from remote servers for
+ * a query before backing off by doubling the retry interval for each
+ * subsequent request sent.  Defaults to 3.
+ *
+ * Requires:
+ * \li	resolver to be valid.
+ * \li  tries > 0.
+ */
+
+unsigned int
 dns_resolver_getoptions(dns_resolver_t *resolver);
 
 void
diff -Nurp bind9-9.7.0.dfsg.P1.orig/lib/dns/include/dns/view.h bind9-9.7.0.dfsg.P1/lib/dns/include/dns/view.h
--- bind9-9.7.0.dfsg.P1.orig/lib/dns/include/dns/view.h	2012-09-25 17:27:10.000000000 -0700
+++ bind9-9.7.0.dfsg.P1/lib/dns/include/dns/view.h	2012-09-25 17:27:11.000000000 -0700
@@ -155,6 +155,7 @@ struct dns_view {
 	dns_name_t *			dlv;
 	dns_fixedname_t			dlv_fixed;
 	isc_uint16_t			maxudp;
+        dns_ttl_t			staleanswerttl;
 #ifdef ALLOW_FILTER_AAAA_ON_V4
 	dns_v4_aaaa_t			v4_aaaa;
 #endif
diff -Nurp bind9-9.7.0.dfsg.P1.orig/lib/dns/master.c bind9-9.7.0.dfsg.P1/lib/dns/master.c
--- bind9-9.7.0.dfsg.P1.orig/lib/dns/master.c	2012-09-25 17:27:10.000000000 -0700
+++ bind9-9.7.0.dfsg.P1/lib/dns/master.c	2012-09-25 17:27:11.000000000 -0700
@@ -1870,11 +1870,16 @@ load_text(dns_loadctx_t *lctx) {
 		if ((lctx->options & DNS_MASTER_AGETTL) != 0) {
 			/*
 			 * Adjust the TTL for $DATE.  If the RR has already
-			 * expired, ignore it.
+			 * expired, set its TTL to 0.  This should be okay
+			 * even if the Akamai serve stale feature is not
+			 * in effect, because it will just be quickly expired
+			 * by the cache, and the way this was written before
+			 * the patch it could potentially add 0 TTLs anyway.
 			 */
 			if (lctx->ttl < ttl_offset)
-				continue;
-			lctx->ttl -= ttl_offset;
+			    lctx->ttl = 0;
+			else
+			    lctx->ttl -= ttl_offset;
 		}
 
 		/*
diff -Nurp bind9-9.7.0.dfsg.P1.orig/lib/dns/masterdump.c bind9-9.7.0.dfsg.P1/lib/dns/masterdump.c
--- bind9-9.7.0.dfsg.P1.orig/lib/dns/masterdump.c	2012-09-25 17:27:10.000000000 -0700
+++ bind9-9.7.0.dfsg.P1/lib/dns/masterdump.c	2012-09-25 17:27:11.000000000 -0700
@@ -97,6 +97,7 @@ typedef struct dns_totext_ctx {
 	dns_fixedname_t		origin_fixname;
 	isc_uint32_t 		current_ttl;
 	isc_boolean_t 		current_ttl_valid;
+	dns_ttl_t		serve_stale_ttl;
 } dns_totext_ctx_t;
 
 LIBDNS_EXTERNAL_DATA const dns_master_style_t
@@ -315,6 +316,7 @@ totext_ctx_init(const dns_master_style_t
 	ctx->neworigin = NULL;
 	ctx->current_ttl = 0;
 	ctx->current_ttl_valid = ISC_FALSE;
+	ctx->serve_stale_ttl = 0;
 
 	return (ISC_R_SUCCESS);
 }
@@ -901,6 +903,8 @@ dump_rdatasets_text(isc_mem_t *mctx, dns
 		    (ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0) {
 			/* Omit negative cache entries */
 		} else {
+			if (rds->ttl < ctx->serve_stale_ttl)
+				fprintf(f, "; stale\n");
 			isc_result_t result =
 				dump_rdataset(mctx, name, rds, ctx,
 					       buffer, f);
@@ -1299,6 +1303,15 @@ dumpctx_create(isc_mem_t *mctx, dns_db_t
 	dns_db_attach(db, &dctx->db);
 
 	dctx->do_date = dns_db_iscache(dctx->db);
+	if (dctx->do_date) {
+	    /*
+	     * Adjust the date backwards by the serve-stale TTL, if any.
+	     * This is so the TTL will be loaded correctly when next started.
+	     */
+	    (void)dns_db_getservestalettl(dctx->db,
+					  &dctx->tctx.serve_stale_ttl);
+	    dctx->now -= dctx->tctx.serve_stale_ttl;
+	}
 
 	if (dctx->format == dns_masterformat_text &&
 	    (dctx->tctx.style.flags & DNS_STYLEFLAG_REL_OWNER) != 0) {
@@ -1366,6 +1379,10 @@ dumptostreaminc(dns_dumpctx_t *dctx) {
 			 * it in the zone case.
 			 */
 			if (dctx->do_date) {
+				fprintf(dctx->f,
+					"; using a %d second stale ttl\n",
+					dctx->tctx.serve_stale_ttl);
+
 				result = dns_time32_totext(dctx->now, &buffer);
 				RUNTIME_CHECK(result == ISC_R_SUCCESS);
 				isc_buffer_usedregion(&buffer, &r);
diff -Nurp bind9-9.7.0.dfsg.P1.orig/lib/dns/rbtdb.c bind9-9.7.0.dfsg.P1/lib/dns/rbtdb.c
--- bind9-9.7.0.dfsg.P1.orig/lib/dns/rbtdb.c	2012-09-25 17:27:10.000000000 -0700
+++ bind9-9.7.0.dfsg.P1/lib/dns/rbtdb.c	2012-09-25 17:27:11.000000000 -0700
@@ -271,6 +271,7 @@ typedef ISC_LIST(rdatasetheader_t)      
 typedef ISC_LIST(dns_rbtnode_t)         rbtnodelist_t;
 
 #define RDATASET_ATTR_NONEXISTENT       0x0001
+/* Akamai semantic: TTL expired but still usable, if KEEPSTALE. */
 #define RDATASET_ATTR_STALE             0x0002
 #define RDATASET_ATTR_IGNORE            0x0004
 #define RDATASET_ATTR_RETAIN            0x0008
@@ -279,6 +280,8 @@ typedef ISC_LIST(dns_rbtnode_t)         
 #define RDATASET_ATTR_STATCOUNT         0x0040
 #define RDATASET_ATTR_OPTOUT		0x0080
 #define RDATASET_ATTR_NEGATIVE          0x0100
+/* Akakmai semantic: TTL really expired. */
+#define RDATASET_ATTR_ANCIENT		0x0200
 
 typedef struct acache_cbarg {
 	dns_rdatasetadditional_t        type;
@@ -319,6 +322,10 @@ struct acachectl {
 	(((header)->attributes & RDATASET_ATTR_OPTOUT) != 0)
 #define NEGATIVE(header) \
 	(((header)->attributes & RDATASET_ATTR_NEGATIVE) != 0)
+#define STALE(header) \
+	(((header)->attributes & RDATASET_ATTR_STALE) != 0)
+#define ANCIENT(header) \
+	(((header)->attributes & RDATASET_ATTR_ANCIENT) != 0)
 
 #define DEFAULT_NODE_LOCK_COUNT         7       /*%< Should be prime. */
 
@@ -364,6 +371,17 @@ typedef enum {
 	dns_db_secure
 } dns_db_secure_t;
 
+typedef enum {
+	rdataset_ttl_fresh,
+	rdataset_ttl_stale,
+	rdataset_ttl_ancient
+} rdataset_ttl_t;
+
+#define OLDDATA(search, ttl) \
+	(rdsttl == rdataset_ttl_ancient || \
+	 (rdsttl == rdataset_ttl_stale && \
+	  ((search)->options & DNS_DBFIND_STALEOK) == 0))
+
 typedef struct rbtdb_version {
 	/* Not locked */
 	rbtdb_serial_t                  serial;
@@ -420,6 +438,12 @@ typedef struct {
 	dns_dbnode_t                    *nsnode;
 
 	/*
+	 * Akamai: maximum length of time to keep using a stale answer
+	 * past its normal TTL expiry.
+	*/
+	dns_ttl_t			serve_stale_ttl;
+
+	/*
 	 * This is a linked list used to implement the LRU cache.  There will
 	 * be node_lock_count linked lists here.  Nodes in bucket 1 will be
 	 * placed on the linked list rdatasets[1].
@@ -449,6 +473,8 @@ typedef struct {
 #define RBTDB_ATTR_LOADED               0x01
 #define RBTDB_ATTR_LOADING              0x02
 
+#define KEEPSTALE(rbtdb) ((rbtdb)->serve_stale_ttl > 0)
+
 /*%
  * Search Context
  */
@@ -1343,8 +1369,8 @@ clean_cache_node(dns_rbtdb_t *rbtdb, dns
 		/*
 		 * If current is nonexistent or stale, we can clean it up.
 		 */
-		if ((current->attributes &
-		     (RDATASET_ATTR_NONEXISTENT|RDATASET_ATTR_STALE)) != 0) {
+		if (NONEXISTENT(current) || ANCIENT(current) ||
+		    (STALE(current) && ! KEEPSTALE(rbtdb))) {
 			if (top_prev != NULL)
 				top_prev->next = current->next;
 			else
@@ -1578,6 +1604,78 @@ cleanup_dead_nodes(dns_rbtdb_t *rbtdb, i
 	}
 }
 
+static void
+clean_now_or_later(dns_rbtnode_t *node, dns_rbtdb_t *rbtdb,
+		   rdatasetheader_t *header, rdatasetheader_t **header_prevp)
+{
+	if (dns_rbtnode_refcurrent(node) == 0) {
+		isc_mem_t *mctx;
+
+		/*
+		 * header->down can be non-NULL if the refcount has just
+		 * decremented to 0 but decrement_reference() has not performed
+		 * clean_cache_node(), in which case we need to purge the stale
+		 * headers first.
+		 */
+		mctx = rbtdb->common.mctx;
+		clean_stale_headers(rbtdb, mctx, header);
+		if (*header_prevp != NULL)
+			(*header_prevp)->next = header->next;
+		else
+			node->data = header->next;
+		free_rdataset(rbtdb, mctx, header);
+	} else {
+		header->attributes |= RDATASET_ATTR_STALE |
+		                      RDATASET_ATTR_ANCIENT;
+		node->dirty = 1;
+		*header_prevp = header;
+	}
+}
+
+static rdataset_ttl_t
+check_ttl(dns_rbtnode_t *node, rbtdb_search_t *search,
+	  rdatasetheader_t *header, rdatasetheader_t **header_prevp,
+	  nodelock_t *lock, isc_rwlocktype_t *locktype)
+{
+	dns_rbtdb_t *rbtdb = search->rbtdb;
+
+	if (header->rdh_ttl > search->now)
+		return rdataset_ttl_fresh;
+
+	/*
+	 * This rdataset is stale, but perhaps still usable.
+	 */
+	if (KEEPSTALE(rbtdb) &&
+	    header->rdh_ttl + rbtdb->serve_stale_ttl > search->now) {
+		header->attributes |= RDATASET_ATTR_STALE;
+		/* Doesn't set dirty because it doesn't need removal. */
+		return rdataset_ttl_stale;
+	}
+
+	/*
+	 * This rdataset is so stale it is no longer usable, even with
+	 * KEEPSTALE.  If no one else is using the node, we can clean it up
+	 * right now, otherwise we mark it as ancient, and the node as dirty,
+	 * so it will get cleaned up later.
+	 */
+	if ((header->rdh_ttl <= search->now - RBTDB_VIRTUAL) &&
+	    (*locktype == isc_rwlocktype_write ||
+	     NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
+		/*
+		 * We update the node's status only when we can get write
+		 * access; otherwise, we leave others to this work.  Periodical
+		 * cleaning will eventually take the job as the last resort.
+		 * We won't downgrade the lock, since other rdatasets are
+		 * probably stale, too.
+		 */
+		*locktype = isc_rwlocktype_write;
+		clean_now_or_later(node, rbtdb, header, header_prevp);
+	} else
+		*header_prevp = header;
+
+	return rdataset_ttl_ancient;
+}
+
 /*
  * Caller must be holding the node lock if its reference must be protected
  * by the lock.
@@ -4063,6 +4161,7 @@ cache_zonecut_callback(dns_rbtnode_t *no
 	isc_result_t result;
 	nodelock_t *lock;
 	isc_rwlocktype_t locktype;
+	rdataset_ttl_t rdsttl;
 
 	/* XXX comment */
 
@@ -4085,58 +4184,10 @@ cache_zonecut_callback(dns_rbtnode_t *no
 	header_prev = NULL;
 	for (header = node->data; header != NULL; header = header_next) {
 		header_next = header->next;
-		if (header->rdh_ttl <= search->now) {
-			/*
-			 * This rdataset is stale.  If no one else is
-			 * using the node, we can clean it up right
-			 * now, otherwise we mark it as stale, and
-			 * the node as dirty, so it will get cleaned
-			 * up later.
-			 */
-			if ((header->rdh_ttl <= search->now - RBTDB_VIRTUAL) &&
-			    (locktype == isc_rwlocktype_write ||
-			     NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
-				/*
-				 * We update the node's status only when we
-				 * can get write access; otherwise, we leave
-				 * others to this work.  Periodical cleaning
-				 * will eventually take the job as the last
-				 * resort.
-				 * We won't downgrade the lock, since other
-				 * rdatasets are probably stale, too.
-				 */
-				locktype = isc_rwlocktype_write;
-
-				if (dns_rbtnode_refcurrent(node) == 0) {
-					isc_mem_t *mctx;
-
-					/*
-					 * header->down can be non-NULL if the
-					 * refcount has just decremented to 0
-					 * but decrement_reference() has not
-					 * performed clean_cache_node(), in
-					 * which case we need to purge the
-					 * stale headers first.
-					 */
-					mctx = search->rbtdb->common.mctx;
-					clean_stale_headers(search->rbtdb,
-							    mctx,
-							    header);
-					if (header_prev != NULL)
-						header_prev->next =
-							header->next;
-					else
-						node->data = header->next;
-					free_rdataset(search->rbtdb, mctx,
-						      header);
-				} else {
-					header->attributes |=
-						RDATASET_ATTR_STALE;
-					node->dirty = 1;
-					header_prev = header;
-				}
-			} else
-				header_prev = header;
+		rdsttl = check_ttl(node, search, header, &header_prev,
+				   lock, &locktype);
+		if (OLDDATA(search, rdsttl)) {
+			/* rdataset is too old to be useful */ ;
 		} else if (header->type == dns_rdatatype_dname &&
 			   EXISTS(header)) {
 			dname_header = header;
@@ -4186,6 +4237,7 @@ find_deepest_zonecut(rbtdb_search_t *sea
 	isc_boolean_t done;
 	nodelock_t *lock;
 	isc_rwlocktype_t locktype;
+	rdataset_ttl_t rdsttl;
 
 	/*
 	 * Caller must be holding the tree lock.
@@ -4209,48 +4261,10 @@ find_deepest_zonecut(rbtdb_search_t *sea
 		     header != NULL;
 		     header = header_next) {
 			header_next = header->next;
-			if (header->rdh_ttl <= search->now) {
-				/*
-				 * This rdataset is stale.  If no one else is
-				 * using the node, we can clean it up right
-				 * now, otherwise we mark it as stale, and
-				 * the node as dirty, so it will get cleaned
-				 * up later.
-				 */
-				if ((header->rdh_ttl <= search->now -
-						    RBTDB_VIRTUAL) &&
-				    (locktype == isc_rwlocktype_write ||
-				     NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
-					/*
-					 * We update the node's status only
-					 * when we can get write access.
-					 */
-					locktype = isc_rwlocktype_write;
-
-					if (dns_rbtnode_refcurrent(node)
-					    == 0) {
-						isc_mem_t *m;
-
-						m = search->rbtdb->common.mctx;
-						clean_stale_headers(
-							search->rbtdb,
-							m, header);
-						if (header_prev != NULL)
-							header_prev->next =
-								header->next;
-						else
-							node->data =
-								header->next;
-						free_rdataset(rbtdb, m,
-							      header);
-					} else {
-						header->attributes |=
-							RDATASET_ATTR_STALE;
-						node->dirty = 1;
-						header_prev = header;
-					}
-				} else
-					header_prev = header;
+			rdsttl = check_ttl(node, search, header, &header_prev,
+					   lock, &locktype);
+			if (OLDDATA(search, rdsttl)) {
+			    	/* rdataset is too old to be useful */ ;
 			} else if (EXISTS(header)) {
 				/*
 				 * We've found an extant rdataset.  See if
@@ -4359,6 +4373,7 @@ find_coveringnsec(rbtdb_search_t *search
 	rbtdb_rdatatype_t matchtype, sigmatchtype;
 	nodelock_t *lock;
 	isc_rwlocktype_t locktype;
+	rdataset_ttl_t rdsttl;
 
 	matchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_nsec, 0);
 	sigmatchtype = RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig,
@@ -4385,47 +4400,11 @@ find_coveringnsec(rbtdb_search_t *search
 		     header != NULL;
 		     header = header_next) {
 			header_next = header->next;
-			if (header->rdh_ttl <= now) {
-				/*
-				 * This rdataset is stale.  If no one else is
-				 * using the node, we can clean it up right
-				 * now, otherwise we mark it as stale, and the
-				 * node as dirty, so it will get cleaned up
-				 * later.
-				 */
-				if ((header->rdh_ttl <= now - RBTDB_VIRTUAL) &&
-				    (locktype == isc_rwlocktype_write ||
-				     NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
-					/*
-					 * We update the node's status only
-					 * when we can get write access.
-					 */
-					locktype = isc_rwlocktype_write;
-
-					if (dns_rbtnode_refcurrent(node)
-					    == 0) {
-						isc_mem_t *m;
-
-						m = search->rbtdb->common.mctx;
-						clean_stale_headers(
-							search->rbtdb,
-							m, header);
-						if (header_prev != NULL)
-							header_prev->next =
-								header->next;
-						else
-							node->data = header->next;
-						free_rdataset(search->rbtdb, m,
-							      header);
-					} else {
-						header->attributes |=
-							RDATASET_ATTR_STALE;
-						node->dirty = 1;
-						header_prev = header;
-					}
-				} else
-					header_prev = header;
-				continue;
+			rdsttl = check_ttl(node, search, header, &header_prev,
+					   lock, &locktype);
+			if (OLDDATA(search, rdsttl)) {
+			    /* rdataset is too old to be useful. */
+			    continue;
 			}
 			if (NONEXISTENT(header) ||
 			    RBTDB_RDATATYPE_BASE(header->type) == 0) {
@@ -4481,6 +4460,7 @@ cache_find(dns_db_t *db, dns_name_t *nam
 	rdatasetheader_t *foundsig, *nssig, *cnamesig;
 	rdatasetheader_t *update, *updatesig;
 	rbtdb_rdatatype_t sigtype, negtype;
+	rdataset_ttl_t rdsttl;
 
 	UNUSED(version);
 
@@ -4567,43 +4547,13 @@ cache_find(dns_db_t *db, dns_name_t *nam
 	header_prev = NULL;
 	for (header = node->data; header != NULL; header = header_next) {
 		header_next = header->next;
-		if (header->rdh_ttl <= now) {
+		rdsttl = check_ttl(node, &search, header, &header_prev,
+				   lock, &locktype);
+		if (OLDDATA(&search, rdsttl)) {
 			/*
-			 * This rdataset is stale.  If no one else is using the
-			 * node, we can clean it up right now, otherwise we
-			 * mark it as stale, and the node as dirty, so it will
-			 * get cleaned up later.
+			 * rdataset is too old to be useful.
 			 */
-			if ((header->rdh_ttl <= now - RBTDB_VIRTUAL) &&
-			    (locktype == isc_rwlocktype_write ||
-			     NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
-				/*
-				 * We update the node's status only when we
-				 * can get write access.
-				 */
-				locktype = isc_rwlocktype_write;
-
-				if (dns_rbtnode_refcurrent(node) == 0) {
-					isc_mem_t *mctx;
-
-					mctx = search.rbtdb->common.mctx;
-					clean_stale_headers(search.rbtdb, mctx,
-							    header);
-					if (header_prev != NULL)
-						header_prev->next =
-							header->next;
-					else
-						node->data = header->next;
-					free_rdataset(search.rbtdb, mctx,
-						      header);
-				} else {
-					header->attributes |=
-						RDATASET_ATTR_STALE;
-					node->dirty = 1;
-					header_prev = header;
-				}
-			} else
-				header_prev = header;
+			;
 		} else if (EXISTS(header)) {
 			/*
 			 * We now know that there is at least one active
@@ -4827,6 +4777,7 @@ cache_findzonecut(dns_db_t *db, dns_name
 	rdatasetheader_t *found, *foundsig;
 	unsigned int rbtoptions = DNS_RBTFIND_EMPTYDATA;
 	isc_rwlocktype_t locktype;
+	rdataset_ttl_t rdsttl;
 
 	search.rbtdb = (dns_rbtdb_t *)db;
 
@@ -4878,43 +4829,10 @@ cache_findzonecut(dns_db_t *db, dns_name
 	header_prev = NULL;
 	for (header = node->data; header != NULL; header = header_next) {
 		header_next = header->next;
-		if (header->rdh_ttl <= now) {
-			/*
-			 * This rdataset is stale.  If no one else is using the
-			 * node, we can clean it up right now, otherwise we
-			 * mark it as stale, and the node as dirty, so it will
-			 * get cleaned up later.
-			 */
-			if ((header->rdh_ttl <= now - RBTDB_VIRTUAL) &&
-			    (locktype == isc_rwlocktype_write ||
-			     NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
-				/*
-				 * We update the node's status only when we
-				 * can get write access.
-				 */
-				locktype = isc_rwlocktype_write;
-
-				if (dns_rbtnode_refcurrent(node) == 0) {
-					isc_mem_t *mctx;
-
-					mctx = search.rbtdb->common.mctx;
-					clean_stale_headers(search.rbtdb, mctx,
-							    header);
-					if (header_prev != NULL)
-						header_prev->next =
-							header->next;
-					else
-						node->data = header->next;
-					free_rdataset(search.rbtdb, mctx,
-						      header);
-				} else {
-					header->attributes |=
-						RDATASET_ATTR_STALE;
-					node->dirty = 1;
-					header_prev = header;
-				}
-			} else
-				header_prev = header;
+		rdsttl = check_ttl(node, &search, header, &header_prev,
+				   lock, &locktype);
+		if (OLDDATA(&search, rdsttl)) {
+			/* rdataset is too old to be useful */ ;
 		} else if (EXISTS(header)) {
 			/*
 			 * If we found a type we were looking for, remember
@@ -5110,23 +5028,37 @@ expirenode(dns_db_t *db, dns_dbnode_t *n
 		  isc_rwlocktype_write);
 
 	for (header = rbtnode->data; header != NULL; header = header->next)
-		if (header->rdh_ttl <= now - RBTDB_VIRTUAL) {
+		if (KEEPSTALE(rbtdb) &&
+		    header->rdh_ttl < now &&
+		    header->rdh_ttl + rbtdb->serve_stale_ttl > now) {
+			header->attributes |= RDATASET_ATTR_STALE;
+			/*
+			 * Not dirty because it doesn't need removal.
+			 */
+		} else if (header->rdh_ttl <= now - RBTDB_VIRTUAL) {
 			/*
 			 * We don't check if refcurrent(rbtnode) == 0 and try
 			 * to free like we do in cache_find(), because
 			 * refcurrent(rbtnode) must be non-zero.  This is so
 			 * because 'node' is an argument to the function.
 			 */
-			header->attributes |= RDATASET_ATTR_STALE;
+			header->attributes |= RDATASET_ATTR_ANCIENT;
 			rbtnode->dirty = 1;
 			if (log)
 				isc_log_write(dns_lctx, category, module,
 					      level, "overmem cache: stale %s",
 					      printname);
 		} else if (force_expire) {
+		    	/*
+			 * Akamai: no serve-stale consideration of TTL is done
+			 * here because it force_expire only happens if the
+			 * cache goes over its limits, which we don't set.
+			 * If we were to set it, then to honor it stale
+			 * rdatasets would need to be blown away anyway.
+			 */
 			if (! RETAIN(header)) {
 				set_ttl(rbtdb, header, 0);
-				header->attributes |= RDATASET_ATTR_STALE;
+				header->attributes |= RDATASET_ATTR_ANCIENT;
 				rbtnode->dirty = 1;
 			} else if (log) {
 				isc_log_write(dns_lctx, category, module,
@@ -5364,7 +5296,21 @@ cache_findrdataset(dns_db_t *db, dns_dbn
 	for (header = rbtnode->data; header != NULL; header = header_next) {
 		header_next = header->next;
 		if (header->rdh_ttl <= now) {
-			if ((header->rdh_ttl <= now - RBTDB_VIRTUAL) &&
+			/*
+			 * Stale data.  The Akamai serve stale feature isn't
+			 * relevant here as (currently) the only use of
+			 * cache_findrdataset is to fill in authority/additional
+			 * records without doing any further recursion
+			 * if data has expired.  Without the potential for
+			 * recursion that use therefore also doesn't support
+			 * the notion of "see if any fresh data is cached,
+			 * if not then try to resolve, and if that fails then
+			 * use any stale data."  Still, this block should not
+			 * trigger data expiry if the serve stale feature
+			 * is in use, so don't mark the node as stale/ancient.
+			 */
+			if (! KEEPSTALE(rbtdb) &&
+			    (header->rdh_ttl <= now - RBTDB_VIRTUAL) &&
 			    (locktype == isc_rwlocktype_write ||
 			     NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
 				/*
@@ -5380,7 +5326,7 @@ cache_findrdataset(dns_db_t *db, dns_dbn
 				 * non-zero.  This is so because 'node' is an
 				 * argument to the function.
 				 */
-				header->attributes |= RDATASET_ATTR_STALE;
+				header->attributes |= RDATASET_ATTR_ANCIENT;
 				rbtnode->dirty = 1;
 			}
 		} else if (EXISTS(header)) {
@@ -5642,7 +5588,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *r
 				if (covers == dns_rdatatype_any) {
 					set_ttl(rbtdb, topheader, 0);
 					topheader->attributes |=
-						RDATASET_ATTR_STALE;
+						RDATASET_ATTR_ANCIENT;
 					rbtnode->dirty = 1;
 				} else if (topheader->type == sigtype)
 					sigheader = topheader;
@@ -5688,7 +5634,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *r
 				 * NXDOMAIN/NODATA(QTYPE=ANY).
 				 */
 				set_ttl(rbtdb, topheader, 0);
-				topheader->attributes |= RDATASET_ATTR_STALE;
+				topheader->attributes |= RDATASET_ATTR_ANCIENT;
 				rbtnode->dirty = 1;
 				topheader = NULL;
 				goto find_header;
@@ -5729,6 +5675,8 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *r
 		/*
 		 * Trying to add an rdataset with lower trust to a cache DB
 		 * has no effect, provided that the cache data isn't stale.
+		 * Akamai: if the cache data is stale, new lower trust data
+		 * will supersede it below.  Unclear what best policy is here.
 		 */
 		if (rbtversion == NULL && trust < header->trust &&
 		    (header->rdh_ttl > now || header_nx)) {
@@ -5757,6 +5705,10 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *r
 
 			if ((options & DNS_DBADD_EXACT) != 0)
 				flags |= DNS_RDATASLAB_EXACT;
+			/*
+			 * Akamai: TTL use here is irrelevant to the cache;
+			 * merge is only done with zonedbs.
+			 */
 			if ((options & DNS_DBADD_EXACTTTL) != 0 &&
 			     newheader->rdh_ttl != header->rdh_ttl)
 					result = DNS_R_NOTEXACT;
@@ -5800,6 +5752,8 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *r
 		 * prevents named being locked to old servers.
 		 * Don't lower trust of existing record if the
 		 * update is forced.
+		 * Akamai: nothing special to be done w.r.t stale data;
+		 * it gets replaced normally further down.
 		 */
 		if (IS_CACHE(rbtdb) && header->rdh_ttl > now &&
 		    header->type == dns_rdatatype_ns &&
@@ -5899,11 +5853,11 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *r
 				changed->dirty = ISC_TRUE;
 			if (rbtversion == NULL) {
 				set_ttl(rbtdb, header, 0);
-				header->attributes |= RDATASET_ATTR_STALE;
+				header->attributes |= RDATASET_ATTR_ANCIENT;
 				if (sigheader != NULL) {
 					set_ttl(rbtdb, sigheader, 0);
 					sigheader->attributes |=
-						 RDATASET_ATTR_STALE;
+						 RDATASET_ATTR_ANCIENT;
 				}
 			}
 			idx = newheader->node->locknum;
@@ -7083,6 +7037,30 @@ getrrsetstats(dns_db_t *db) {
 	return (rbtdb->rrsetstats);
 }
 
+static isc_result_t
+setservestalettl(dns_db_t *db, dns_ttl_t ttl) {
+	dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+
+	REQUIRE(VALID_RBTDB(rbtdb));
+	REQUIRE(IS_CACHE(rbtdb));
+
+	/* currently no bounds checking.  0 means disable. */
+	rbtdb->serve_stale_ttl = ttl;
+	return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+getservestalettl(dns_db_t *db, dns_ttl_t *ttl) {
+	dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+
+	REQUIRE(VALID_RBTDB(rbtdb));
+	REQUIRE(IS_CACHE(rbtdb));
+
+	*ttl = rbtdb->serve_stale_ttl;
+	return ISC_R_SUCCESS;
+}
+
+
 static dns_dbmethods_t zone_methods = {
 	attach,
 	detach,
@@ -7119,7 +7097,9 @@ static dns_dbmethods_t zone_methods = {
 	getsigningtime,
 	resigned,
 	isdnssec,
-	NULL
+	NULL,
+	NULL,			/* setservestalettl */
+	NULL			/* getservestalettl */
 };
 
 static dns_dbmethods_t cache_methods = {
@@ -7158,7 +7138,9 @@ static dns_dbmethods_t cache_methods = {
 	NULL,
 	NULL,
 	isdnssec,
-	getrrsetstats
+	getrrsetstats,
+	setservestalettl,
+	getservestalettl
 };
 
 isc_result_t
@@ -7390,6 +7372,7 @@ dns_rbtdb_create
 	rbtdb->attributes = 0;
 	rbtdb->overmem = ISC_FALSE;
 	rbtdb->task = NULL;
+	rbtdb->serve_stale_ttl = 0;
 
 	/*
 	 * Version Initialization.
@@ -7785,7 +7768,8 @@ rdatasetiter_first(dns_rdatasetiter_t *i
 				 * rdatasets to work.
 				 */
 				if (NONEXISTENT(header) ||
-				    (now != 0 && now > header->rdh_ttl))
+				    (now != 0 && now > header->rdh_ttl
+						     + rbtdb->serve_stale_ttl))
 					header = NULL;
 				break;
 			} else
@@ -7861,7 +7845,9 @@ rdatasetiter_next(dns_rdatasetiter_t *it
 					 */
 					if ((header->attributes &
 					     RDATASET_ATTR_NONEXISTENT) != 0 ||
-					    (now != 0 && now > header->rdh_ttl))
+					    (now != 0 &&
+					     now > header->rdh_ttl
+						 + rbtdb->serve_stale_ttl))
 						header = NULL;
 					break;
 				} else
@@ -8931,7 +8917,7 @@ expire_header(dns_rbtdb_t *rbtdb, rdatas
 	      isc_boolean_t tree_locked)
 {
 	set_ttl(rbtdb, header, 0);
-	header->attributes |= RDATASET_ATTR_STALE;
+	header->attributes |= RDATASET_ATTR_ANCIENT;
 	header->node->dirty = 1;
 
 	/*
diff -Nurp bind9-9.7.0.dfsg.P1.orig/lib/dns/resolver.c bind9-9.7.0.dfsg.P1/lib/dns/resolver.c
--- bind9-9.7.0.dfsg.P1.orig/lib/dns/resolver.c	2012-09-25 17:27:10.000000000 -0700
+++ bind9-9.7.0.dfsg.P1/lib/dns/resolver.c	2012-09-25 17:27:11.000000000 -0700
@@ -386,6 +386,11 @@ struct dns_resolver {
 	isc_timer_t *			spillattimer;
 	isc_boolean_t			zero_no_soa_ttl;
 
+	/* Akamai additions for serve-stale feature. */
+	unsigned int			maxtime;       /* in milliseconds */
+	unsigned int			retryinterval; /* in milliseconds */
+	unsigned int			nonbackofftries;
+
 	/* Locked by lock. */
 	unsigned int			references;
 	isc_boolean_t			exiting;
@@ -1312,14 +1317,12 @@ fctx_setretryinterval(fetchctx_t *fctx, 
 	unsigned int seconds;
 	unsigned int us;
 
+	us = fctx->res->retryinterval * 1000;
 	/*
-	 * We retry every .8 seconds the first two times through the address
-	 * list, and then we do exponential back-off.
+	 * Exponential backoff after the first few tries.
 	 */
-	if (fctx->restarts < 3)
-		us = 800000;
-	else
-		us = (800000 << (fctx->restarts - 2));
+	if (fctx->restarts >= fctx->res->nonbackofftries)
+		us <<= fctx->restarts - 2;
 
 	/*
 	 * Double the round-trip time.
@@ -3621,7 +3624,8 @@ fctx_create(dns_resolver_t *res, dns_nam
 	/*
 	 * Compute an expiration time for the entire fetch.
 	 */
-	isc_interval_set(&interval, 30, 0);             /* XXXRTH constant */
+	isc_interval_set(&interval, res->maxtime / 1000,
+			 res->maxtime % 1000 * 1000000);
 	iresult = isc_time_nowplusinterval(&fctx->expires, &interval);
 	if (iresult != ISC_R_SUCCESS) {
 		UNEXPECTED_ERROR(__FILE__, __LINE__,
@@ -4439,7 +4443,7 @@ cache_name(fetchctx_t *fctx, dns_name_t 
 		 */
 		if (rdataset->ttl > res->view->maxcachettl)
 			rdataset->ttl = res->view->maxcachettl;
-		
+
 		/*
 		 * Enforce configured minimum cache TTL.
 		 */
@@ -7322,6 +7326,9 @@ dns_resolver_create(dns_view_t *view,
 	res->spillatmax = 100;
 	res->spillattimer = NULL;
 	res->zero_no_soa_ttl = ISC_FALSE;
+	res->maxtime = 30000;
+	res->retryinterval = 800;
+	res->nonbackofftries = 3;
 	res->ndisps = 0;
 	res->nextdisp = 0; /* meaningless at this point, but init it */
 	res->nbuckets = ntasks;
@@ -8646,3 +8653,49 @@ dns_resolver_getoptions(dns_resolver_t *
 
 	return (resolver->options);
 }
+
+unsigned int
+dns_resolver_getmaxtime(dns_resolver_t *resolver) {
+	REQUIRE(VALID_RESOLVER(resolver));
+
+	return (resolver->maxtime);
+}
+
+void
+dns_resolver_setmaxtime(dns_resolver_t *resolver, unsigned int maxtime) {
+	REQUIRE(VALID_RESOLVER(resolver));
+	REQUIRE(maxtime > 0);
+
+	resolver->maxtime = ISC_MIN(maxtime, 30000);
+}
+
+unsigned int
+dns_resolver_getretryinterval(dns_resolver_t *resolver) {
+	REQUIRE(VALID_RESOLVER(resolver));
+
+	return (resolver->retryinterval);
+}
+
+void
+dns_resolver_setretryinterval(dns_resolver_t *resolver, unsigned int interval)
+{
+	REQUIRE(VALID_RESOLVER(resolver));
+	REQUIRE(interval > 0);
+
+	resolver->retryinterval = ISC_MIN(interval, 2000);
+}
+
+unsigned int
+dns_resolver_getnonbackofftries(dns_resolver_t *resolver) {
+	REQUIRE(VALID_RESOLVER(resolver));
+
+	return (resolver->nonbackofftries);
+}
+
+void
+dns_resolver_setnonbackofftries(dns_resolver_t *resolver, unsigned int tries) {
+	REQUIRE(VALID_RESOLVER(resolver));
+	REQUIRE(tries > 0);
+
+	resolver->nonbackofftries = tries;
+}
diff -Nurp bind9-9.7.0.dfsg.P1.orig/lib/dns/view.c bind9-9.7.0.dfsg.P1/lib/dns/view.c
--- bind9-9.7.0.dfsg.P1.orig/lib/dns/view.c	2012-09-25 17:27:10.000000000 -0700
+++ bind9-9.7.0.dfsg.P1/lib/dns/view.c	2012-09-25 17:27:11.000000000 -0700
@@ -177,6 +177,7 @@ dns_view_create(isc_mem_t *mctx, dns_rda
 	view->flush = ISC_FALSE;
 	view->dlv = NULL;
 	view->maxudp = 0;
+	view->staleanswerttl = 1;
 #ifdef ALLOW_FILTER_AAAA_ON_V4
 	view->v4_aaaa = dns_v4_aaaa_ok;
 #endif
diff -Nurp bind9-9.7.0.dfsg.P1.orig/lib/isccfg/namedconf.c bind9-9.7.0.dfsg.P1/lib/isccfg/namedconf.c
--- bind9-9.7.0.dfsg.P1.orig/lib/isccfg/namedconf.c	2012-09-25 17:27:10.000000000 -0700
+++ bind9-9.7.0.dfsg.P1/lib/isccfg/namedconf.c	2012-09-25 17:27:11.000000000 -0700
@@ -1028,6 +1028,11 @@ view_clauses[] = {
 	{ "minimal-responses", &cfg_type_boolean, 0 },
 	{ "preferred-glue", &cfg_type_astring, 0 },
 	{ "provide-ixfr", &cfg_type_boolean, 0 },
+	{ "max-stale-ttl", &cfg_type_uint32, 0 },
+	{ "stale-answer-ttl", &cfg_type_uint32, 0 },
+	{ "resolver-max-time", &cfg_type_uint32, 0 },
+	{ "resolver-retry-interval", &cfg_type_uint32, 0 },
+	{ "resolver-nonbackoff-tries", &cfg_type_uint32, 0 },
 	/*
 	 * Note that the query-source option syntax is different
 	 * from the other -source options.
