+3140. [func] New command "rndc flushtree <name>" clears the
+ specified name from the server cache along with
+ all names under it. [RT #19970]
+
3139. [test] Added tests from RFC 6234, RFC 2202, and RFC 1321
for the hashing algorithms (md5, sha1 - sha512, and
their hmac counterparts). [RT #25067]
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: control.c,v 1.43 2011/03/21 23:47:21 tbox Exp $ */
+/* $Id: control.c,v 1.44 2011/08/02 20:36:11 each Exp $ */
/*! \file */
} else if (command_compare(command, NS_COMMAND_FLUSH)) {
result = ns_server_flushcache(ns_g_server, command);
} else if (command_compare(command, NS_COMMAND_FLUSHNAME)) {
- result = ns_server_flushname(ns_g_server, command);
+ result = ns_server_flushnode(ns_g_server, command, ISC_FALSE);
+ } else if (command_compare(command, NS_COMMAND_FLUSHTREE)) {
+ result = ns_server_flushnode(ns_g_server, command, ISC_TRUE);
} else if (command_compare(command, NS_COMMAND_STATUS)) {
result = ns_server_status(ns_g_server, text);
} else if (command_compare(command, NS_COMMAND_TSIGLIST)) {
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: control.h,v 1.33 2011/03/21 23:47:21 tbox Exp $ */
+/* $Id: control.h,v 1.34 2011/08/02 20:36:12 each Exp $ */
#ifndef NAMED_CONTROL_H
#define NAMED_CONTROL_H 1
#define NS_COMMAND_NOTRACE "notrace"
#define NS_COMMAND_FLUSH "flush"
#define NS_COMMAND_FLUSHNAME "flushname"
+#define NS_COMMAND_FLUSHTREE "flushtree"
#define NS_COMMAND_STATUS "status"
#define NS_COMMAND_TSIGLIST "tsig-list"
#define NS_COMMAND_TSIGDELETE "tsig-delete"
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: server.h,v 1.112 2011/03/21 23:47:21 tbox Exp $ */
+/* $Id: server.h,v 1.113 2011/08/02 20:36:12 each Exp $ */
#ifndef NAMED_SERVER_H
#define NAMED_SERVER_H 1
ns_server_flushcache(ns_server_t *server, char *args);
/*%
- * Flush a particular name from the server's cache(s)
+ * Flush a particular name from the server's cache. If 'tree' is false,
+ * also flush the name from the ADB and badcache. If 'tree' is true, also
+ * flush all the names under the specified name.
*/
isc_result_t
-ns_server_flushname(ns_server_t *server, char *args);
+ns_server_flushnode(ns_server_t *server, char *args, isc_boolean_t tree);
/*%
* Report the server's status.
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: server.c,v 1.615 2011/07/28 03:18:17 each Exp $ */
+/* $Id: server.c,v 1.616 2011/08/02 20:36:11 each Exp $ */
/*! \file */
}
isc_result_t
-ns_server_flushname(ns_server_t *server, char *args) {
+ns_server_flushnode(ns_server_t *server, char *args, isc_boolean_t tree) {
char *ptr, *target, *viewname;
dns_view_t *view;
isc_boolean_t flushed;
* if some of the views share a single cache. But since the
* operation is lightweight we prefer simplicity here.
*/
- result = dns_view_flushname(view, name);
+ result = dns_view_flushnode(view, name, tree);
if (result != ISC_R_SUCCESS) {
flushed = ISC_FALSE;
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
- "flushing name '%s' in cache view '%s' "
- "failed: %s", target, view->name,
+ "flushing %s '%s' in cache view '%s' "
+ "failed: %s",
+ tree ? "tree" : "name",
+ target, view->name,
isc_result_totext(result));
}
}
if (viewname != NULL)
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_INFO,
- "flushing name '%s' in cache view '%s' "
- "succeeded", target, viewname);
+ "flushing %s '%s' in cache view '%s' "
+ "succeeded",
+ tree ? "tree" : "name",
+ target, viewname);
else
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_INFO,
- "flushing name '%s' in all cache views "
- "succeeded", target);
+ "flushing %s '%s' in all cache views "
+ "succeeded",
+ tree ? "tree" : "name",
+ target);
result = ISC_R_SUCCESS;
} else {
if (!found)
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
- "flushing name '%s' in cache view '%s' "
- "failed: view not found", target,
- viewname);
+ "flushing %s '%s' in cache view '%s' "
+ "failed: view not found",
+ tree ? "tree" : "name",
+ target, viewname);
result = ISC_R_FAILURE;
}
isc_task_endexclusive(server->task);
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: named.conf,v 1.9 2007/06/19 23:47:01 tbox Exp $ */
+/* $Id: named.conf,v 1.10 2011/08/02 20:36:12 each Exp $ */
controls { /* empty */ };
type master;
file "example.db";
};
+
+zone "flushtest.example" {
+ type master;
+ file "flushtest.db";
+};
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: named.conf,v 1.8 2007/06/19 23:47:01 tbox Exp $ */
+/* $Id: named.conf,v 1.9 2011/08/02 20:36:12 each Exp $ */
controls { /* empty */ };
disable-empty-zone 127.IN-ADDR.ARPA;
};
+key rndc_key {
+ secret "1234abcd8765";
+ algorithm hmac-md5;
+};
+
+controls {
+ inet 10.53.0.2 port 9953 allow { any; } keys { rndc_key; };
+};
+
zone "." {
type hint;
file "../../common/root.hint";
};
+
+zone "flushtest.example" {
+ type forward;
+ forwarders { 10.53.0.1; };
+};
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
-# $Id: tests.sh,v 1.5 2007/06/19 23:47:00 tbox Exp $
+# $Id: tests.sh,v 1.6 2011/08/02 20:36:12 each Exp $
SYSTEMTESTTOP=..
. $SYSTEMTESTTOP/conf.sh
status=0
-$DIG +nosea +nocomm +nocmd +noquest +noadd +noauth +nocomm +nostat \
- -f dig.batch -p 5300 @10.53.0.2 > dig.out.ns2 || status=1
+RNDCOPTS="-c ../common/rndc.conf -s 10.53.0.2 -p 9953"
+DIGOPTS="+nosea +nocomm +nocmd +noquest +noadd +noauth +nocomm \
+ +nostat @10.53.0.2 -p 5300"
+
+# fill the cache with nodes from flushtest.example zone
+load_cache () {
+ # empty all existing cache data
+ $RNDC $RNDCOPTS flush
+
+ # load the positive cache entries
+ $DIG $DIGOPTS txt top1.flushtest.example > /dev/null 2>1
+ $DIG $DIGOPTS txt second1.top1.flushtest.example > /dev/null 2>1
+ $DIG $DIGOPTS txt third1.second1.top1.flushtest.example > /dev/null 2>1
+ $DIG $DIGOPTS txt third2.second1.top1.flushtest.example > /dev/null 2>1
+ $DIG $DIGOPTS txt second2.top1.flushtest.example > /dev/null 2>1
+ $DIG $DIGOPTS txt second3.top1.flushtest.example > /dev/null 2>1
+ $DIG $DIGOPTS txt second1.top2.flushtest.example > /dev/null 2>1
+ $DIG $DIGOPTS txt second2.top2.flushtest.example > /dev/null 2>1
+ $DIG $DIGOPTS txt second3.top2.flushtest.example > /dev/null 2>1
+ $DIG $DIGOPTS txt top3.flushtest.example > /dev/null 2>1
+ $DIG $DIGOPTS txt second1.top3.flushtest.example > /dev/null 2>1
+ $DIG $DIGOPTS txt third1.second1.top3.flushtest.example > /dev/null 2>1
+ $DIG $DIGOPTS txt third2.second1.top3.flushtest.example > /dev/null 2>1
+ $DIG $DIGOPTS txt third1.second2.top3.flushtest.example > /dev/null 2>1
+ $DIG $DIGOPTS txt third2.second2.top3.flushtest.example > /dev/null 2>1
+ $DIG $DIGOPTS txt second3.top3.flushtest.example > /dev/null 2>1
+
+ # load the negative cache entries
+ # nxrrset:
+ $DIG $DIGOPTS a third1.second1.top1.flushtest.example > /dev/null
+ # nxdomain:
+ $DIG $DIGOPTS txt top4.flushtest.example > /dev/null
+ # empty nonterminal:
+ $DIG $DIGOPTS txt second2.top3.flushtest.example > /dev/null
+
+ # sleep one second ensure the TTLs will be lower on cached data
+ sleep 1
+}
+
+dump_cache () {
+ rm -f ns2/named_dump.db
+ $RNDC $RNDCOPTS dumpdb -cache
+ sleep 1
+}
+
+clear_cache () {
+ $RNDC $RNDCOPTS flush
+}
+
+in_cache () {
+ ttl=`$DIG $DIGOPTS "$@" | awk '{print $2}'`
+ [ -z "$ttl" ] && {
+ ttl=`$DIG $DIGOPTS +noanswer +auth "$@" | awk '{print $2}'`
+ [ "$ttl" -eq 3600 ] && return 1
+ return 0
+ }
+ [ "$ttl" -eq 3600 ] && return 1
+ return 0
+}
+
+echo "I:check correctness of routine cache cleaning"
+$DIG $DIGOPTS -f dig.batch > dig.out.ns2 || status=1
grep ";" dig.out.ns2
$PERL ../digcomp.pl dig.out.ns2 knowngood.dig.out || status=1
+echo "I:reset and check that records are correctly cached initially"
+ret=0
+load_cache
+dump_cache
+nrecords=`grep flushtest.example ns2/named_dump.db | grep -v '^;' | wc -l`
+[ $nrecords -eq 20 ] || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:check flushing of the full cache"
+ret=0
+clear_cache
+dump_cache
+nrecords=`grep flushtest.example ns2/named_dump.db | grep -v '^;' | wc -l`
+[ $nrecords -eq 0 ] || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:check flushing of individual nodes"
+ret=0
+clear_cache
+load_cache
+# interior node
+in_cache txt top1.flushtest.example || ret=1
+$RNDC $RNDCOPTS flushname top1.flushtest.example
+in_cache txt top1.flushtest.example && ret=1
+
+# leaf node, under the interior node (should still exist)
+in_cache txt third2.second1.top1.flushtest.example || ret=1
+$RNDC $RNDCOPTS flushname third2.second1.top1.flushtest.example
+in_cache txt third2.second1.top1.flushtest.example && ret=1
+
+# another leaf node, with both positive and negative cache entries
+in_cache a third1.second1.top1.flushtest.example || ret=1
+in_cache txt third1.second1.top1.flushtest.example || ret=1
+$RNDC $RNDCOPTS flushname third1.second1.top1.flushtest.example
+in_cache a third1.second1.top1.flushtest.example && ret=1
+in_cache txt third1.second1.top1.flushtest.example && ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:check flushing of namespaces"
+ret=0
+clear_cache
+load_cache
+# flushing leaf node should leave the interior node:
+in_cache txt third1.second1.top1.flushtest.example || ret=1
+in_cache txt top1.flushtest.example || ret=1
+$RNDC $RNDCOPTS flushtree third1.second1.top1.flushtest.example
+in_cache txt third1.second1.top1.flushtest.example && ret=1
+in_cache txt top1.flushtest.example || ret=1
+in_cache txt second1.top1.flushtest.example || ret=1
+in_cache txt third2.second1.top1.flushtest.example || ret=1
+$RNDC $RNDCOPTS flushtree second1.top1.flushtest.example
+in_cache txt top1.flushtest.example || ret=1
+in_cache txt second1.top1.flushtest.example && ret=1
+in_cache txt third2.second1.top1.flushtest.example && ret=1
+
+# flushing from an empty node should still remove all its children
+in_cache txt second1.top2.flushtest.example || ret=1
+$RNDC $RNDCOPTS flushtree top2.flushtest.example
+in_cache txt second1.top2.flushtest.example && ret=1
+in_cache txt second2.top2.flushtest.example && ret=1
+in_cache txt second3.top2.flushtest.example && ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:check the number of cached records remaining"
+ret=0
+dump_cache
+nrecords=`grep flushtest.example ns2/named_dump.db | grep -v '^;' | wc -l`
+[ $nrecords -eq 19 ] || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
echo "I:exit status: $status"
exit $status
- PERFORMANCE OF THIS SOFTWARE.
-->
-<!-- File: $Id: Bv9ARM-book.xml,v 1.498 2011/07/28 03:18:17 each Exp $ -->
+<!-- File: $Id: Bv9ARM-book.xml,v 1.499 2011/08/02 20:36:12 each Exp $ -->
<book xmlns:xi="http://www.w3.org/2001/XInclude">
<title>BIND 9 Administrator Reference Manual</title>
</varlistentry>
<varlistentry>
- <term><userinput>flushname</userinput> <replaceable>name</replaceable></term>
+ <term><userinput>flushname</userinput>
+ <replaceable>name</replaceable>
+ <optional><replaceable>view<replaceable></optional>
+ </term>
<listitem>
<para>
- Flushes the given name from the server's cache.
+ Flushes the given name from the server's DNS cache,
+ and from the server's nameserver address database
+ if applicable.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><userinput>flushtree</userinput>
+ <replaceable>name</replaceable>
+ <optional><replaceable>view<replaceable></optional>
+ </term>
+ <listitem>
+ <para>
+ Flushes the given name, and all of its subdomains,
+ from the server's DNS cache. (The server's
+ nameserver address database is not affected.)
</para>
</listitem>
</varlistentry>
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: cache.c,v 1.89 2011/03/03 23:47:32 tbox Exp $ */
+/* $Id: cache.c,v 1.90 2011/08/02 20:36:12 each Exp $ */
/*! \file */
return (ISC_R_SUCCESS);
}
-isc_result_t
-dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) {
+static isc_result_t
+clearnode(dns_db_t *db, dns_dbnode_t *node) {
isc_result_t result;
dns_rdatasetiter_t *iter = NULL;
- dns_dbnode_t *node = NULL;
- dns_db_t *db = NULL;
-
- LOCK(&cache->lock);
- if (cache->db != NULL)
- dns_db_attach(cache->db, &db);
- UNLOCK(&cache->lock);
- if (db == NULL)
- return (ISC_R_SUCCESS);
- result = dns_db_findnode(cache->db, name, ISC_FALSE, &node);
- if (result == ISC_R_NOTFOUND) {
- result = ISC_R_SUCCESS;
- goto cleanup_db;
- }
- if (result != ISC_R_SUCCESS)
- goto cleanup_db;
- result = dns_db_allrdatasets(cache->db, node, NULL,
- (isc_stdtime_t)0, &iter);
+ result = dns_db_allrdatasets(db, node, NULL, (isc_stdtime_t)0, &iter);
if (result != ISC_R_SUCCESS)
- goto cleanup_node;
+ return (result);
for (result = dns_rdatasetiter_first(iter);
result == ISC_R_SUCCESS;
dns_rdataset_init(&rdataset);
dns_rdatasetiter_current(iter, &rdataset);
- result = dns_db_deleterdataset(cache->db, node, NULL,
+ result = dns_db_deleterdataset(db, node, NULL,
rdataset.type, rdataset.covers);
dns_rdataset_disassociate(&rdataset);
if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED)
break;
}
+
if (result == ISC_R_NOMORE)
result = ISC_R_SUCCESS;
dns_rdatasetiter_destroy(&iter);
+ return (result);
+}
- cleanup_node:
- dns_db_detachnode(cache->db, &node);
+static isc_result_t
+cleartree(dns_db_t *db, dns_name_t *name) {
+ isc_result_t result;
+ dns_dbiterator_t *iter = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_fixedname_t fnodename;
+ dns_name_t *nodename;
+
+ dns_fixedname_init(&fnodename);
+ nodename = dns_fixedname_name(&fnodename);
+
+ result = dns_db_createiterator(db, 0, &iter);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dns_dbiterator_seek(iter, name);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ while (result == ISC_R_SUCCESS) {
+ result = dns_dbiterator_current(iter, &node, nodename);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN)
+ goto cleanup;
+ if (! dns_name_issubdomain(nodename, name))
+ goto cleanup;
+
+ result = clearnode(db, node);
+ dns_db_detachnode(db, &node);
+ result = dns_dbiterator_next(iter);
+ }
+
+ cleanup:
+ if (result == ISC_R_NOMORE || result == ISC_R_NOTFOUND)
+ result = ISC_R_SUCCESS;
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (iter != NULL)
+ dns_dbiterator_destroy(&iter);
+
+ return (result);
+}
+
+isc_result_t
+dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) {
+ return (dns_cache_flushnode(cache, name, ISC_FALSE));
+}
+
+isc_result_t
+dns_cache_flushnode(dns_cache_t *cache, dns_name_t *name,
+ isc_boolean_t tree)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_db_t *db = NULL;
+
+ if (dns_name_equal(name, dns_rootname))
+ return (dns_cache_flush(cache));
+
+ LOCK(&cache->lock);
+ if (cache->db != NULL)
+ dns_db_attach(cache->db, &db);
+ UNLOCK(&cache->lock);
+ if (db == NULL)
+ return (ISC_R_SUCCESS);
+
+ if (tree) {
+ result = cleartree(cache->db, name);
+ } else {
+ result = dns_db_findnode(cache->db, name, ISC_FALSE, &node);
+ if (result == ISC_R_NOTFOUND) {
+ result = ISC_R_SUCCESS;
+ goto cleanup_db;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_db;
+ result = clearnode(cache->db, node);
+ dns_db_detachnode(cache->db, &node);
+ }
cleanup_db:
dns_db_detach(&db);
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: cache.h,v 1.30 2011/03/03 23:47:32 tbox Exp $ */
+/* $Id: cache.h,v 1.31 2011/08/02 20:36:13 each Exp $ */
#ifndef DNS_CACHE_H
#define DNS_CACHE_H 1
*\li #ISC_R_NOMEMORY
*/
+isc_result_t
+dns_cache_flushnode(dns_cache_t *cache, dns_name_t *name,
+ isc_boolean_t tree);
+/*
+ * Flush a given name from the cache. If 'tree' is true, then
+ * also flush all names under 'name'.
+ *
+ * Requires:
+ *\li 'cache' to be valid.
+ *\li 'name' to be valid.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ *\li #ISC_R_NOMEMORY
+ *\li other error returns.
+ */
+
isc_result_t
dns_cache_flushname(dns_cache_t *cache, dns_name_t *name);
/*
- * Flushes a given name from the cache.
+ * Flush a given name from the cache. Equivalent to
+ * dns_cache_flushpartial(cache, name, ISC_FALSE).
*
* Requires:
*\li 'cache' to be valid.
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: view.h,v 1.133 2011/02/23 03:08:11 marka Exp $ */
+/* $Id: view.h,v 1.134 2011/08/02 20:36:13 each Exp $ */
#ifndef DNS_VIEW_H
#define DNS_VIEW_H 1
*/
isc_result_t
-dns_view_flushname(dns_view_t *view, dns_name_t *);
+dns_view_flushnode(dns_view_t *view, dns_name_t *name, isc_boolean_t tree);
/*%<
- * Flush the given name from the view's cache (and ADB).
+ * Flush the given name from the view's cache (and optionally ADB/badcache).
+ *
+ * If 'tree' is true, flush 'name' and all names below it
+ * from the cache, but do not flush ADB.
+ *
+ * If 'tree' is false, flush 'name' frmo both the cache and ADB,
+ * but do not touch any other nodes.
+ *
+ * Requires:
+ *\li 'view' is valid.
+ *\li 'name' is valid.
+ *
+ * Returns:
+ *\li #ISC_R_SUCCESS
+ * other returns are failures.
+ */
+
+isc_result_t
+dns_view_flushname(dns_view_t *view, dns_name_t *name);
+/*%<
+ * Flush the given name from the view's cache, ADB and badcache.
+ * Equivalent to dns_view_flushnode(view, name, ISC_FALSE).
+ *
*
* Requires:
*\li 'view' is valid.
/*%<
* Add the given name to the delegation only table.
*
- *
* Requires:
*\li 'view' is valid.
*\li 'name' is valid.
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: view.c,v 1.180 2011/03/11 06:11:25 marka Exp $ */
+/* $Id: view.c,v 1.181 2011/08/02 20:36:12 each Exp $ */
/*! \file */
isc_result_t
dns_view_flushname(dns_view_t *view, dns_name_t *name) {
+ return (dns_view_flushnode(view, name, ISC_FALSE));
+}
+
+isc_result_t
+dns_view_flushnode(dns_view_t *view, dns_name_t *name, isc_boolean_t tree) {
REQUIRE(DNS_VIEW_VALID(view));
- if (view->adb != NULL)
- dns_adb_flushname(view->adb, name);
- if (view->cache == NULL)
- return (ISC_R_SUCCESS);
- if (view->resolver != NULL)
- dns_resolver_flushbadcache(view->resolver, name);
- return (dns_cache_flushname(view->cache, name));
+ if (!tree) {
+ if (view->adb != NULL)
+ dns_adb_flushname(view->adb, name);
+ if (view->cache == NULL)
+ return (ISC_R_SUCCESS);
+ if (view->resolver != NULL)
+ dns_resolver_flushbadcache(view->resolver, name);
+ }
+ return (dns_cache_flushnode(view->cache, name, tree));
}
isc_result_t