]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
3577. [bug] Handle zero TTL values better. [RT #33411]
authorMark Andrews <marka@isc.org>
Wed, 29 May 2013 08:10:11 +0000 (18:10 +1000)
committerMark Andrews <marka@isc.org>
Wed, 29 May 2013 08:10:11 +0000 (18:10 +1000)
13 files changed:
CHANGES
bin/tests/system/conf.sh.in
bin/tests/system/inline/tests.sh
bin/tests/system/zero/clean.sh [new file with mode: 0644]
bin/tests/system/zero/ns1/named.conf [new file with mode: 0644]
bin/tests/system/zero/ns1/root.db [new file with mode: 0644]
bin/tests/system/zero/ns2/named.conf [new file with mode: 0644]
bin/tests/system/zero/ns3/named.conf [new file with mode: 0644]
bin/tests/system/zero/ns3/root.hint [new file with mode: 0644]
bin/tests/system/zero/ns4/named.conf [new file with mode: 0644]
bin/tests/system/zero/setup.sh [new file with mode: 0644]
bin/tests/system/zero/tests.sh [new file with mode: 0644]
lib/dns/rbtdb.c

diff --git a/CHANGES b/CHANGES
index 4a411d69bf4b0252f73daf9c4b4bab2064c975af..24a9afb14005d6f64ec24b9947f3f89c340c0ca6 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,5 @@
+3577.  [bug]           Handle zero TTL values better. [RT #33411]
+
 3576.  [bug]           Address a shutdown race when validating. [RT #33573]
 
 3575.  [func]          Changed the logging category for RRL events from
index 3093512d60f0d146dcc6ea7624e888b6dfd3bc5c..60f22f8501e72e3d4a03245f0d6b04b7dabffae6 100644 (file)
@@ -66,7 +66,7 @@ SUBDIRS="acl additional allow_query addzone autosign builtin
         notify nsupdate pending pkcs11 redirect resolver rndc rpz
         rrl rrsetorder rsabigexponent smartsign sortlist spf staticstub
         statistics stub tkey tsig tsiggss unknown upforwd verify
-        views wildcard xfer xferquota zonechecks"
+        views wildcard xfer xferquota zero zonechecks"
 
 # PERL will be an empty string if no perl interpreter was found.
 PERL=@PERL@
index 30190e3b716b3e01af1320c3b2d0435c0c4ed5c6..b81b92f367d10ae9856721ea09a190fea5f8ed32 100644 (file)
@@ -103,6 +103,7 @@ status=`expr $status + $ret`
 n=`expr $n + 1`
 echo "I:checking negative private type response was properly signed ($n)"
 ret=0
+sleep 1
 $DIG $DIGOPTS @10.53.0.6 -p 5300 bits TYPE65534 > dig.out.ns6.test$n
 grep "status: NOERROR" dig.out.ns6.test$n > /dev/null || ret=1
 grep "ANSWER: 0," dig.out.ns6.test$n > /dev/null || ret=1
diff --git a/bin/tests/system/zero/clean.sh b/bin/tests/system/zero/clean.sh
new file mode 100644 (file)
index 0000000..554577b
--- /dev/null
@@ -0,0 +1,19 @@
+# Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+rm -f */named.run
+rm -f ns2/example.db
+rm -f ns4/example.bk
+rm -f dig.out*
+rm -f query.list
diff --git a/bin/tests/system/zero/ns1/named.conf b/bin/tests/system/zero/ns1/named.conf
new file mode 100644 (file)
index 0000000..1c8aabc
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: named.conf,v 1.15 2009/05/29 23:47:49 tbox Exp $ */
+
+controls { /* empty */ };
+
+options {
+       query-source address 10.53.0.1;
+       notify-source 10.53.0.1;
+       transfer-source 10.53.0.1;
+       port 5300;
+       pid-file "named.pid";
+       listen-on { 10.53.0.1; };
+       listen-on-v6 { none; };
+       recursion no;
+       acache-enable yes;
+};
+
+zone "." {
+       type master;
+       file "root.db";
+};
+
diff --git a/bin/tests/system/zero/ns1/root.db b/bin/tests/system/zero/ns1/root.db
new file mode 100644 (file)
index 0000000..69aca86
--- /dev/null
@@ -0,0 +1,24 @@
+; Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+$TTL 300
+@      SOA ns1. hostmaster.warn.example. (
+           1 3600 1200 604800 3600 )
+       NS  ns1.
+ns1. A 10.53.0.1
+;
+example. NS ns2.example.
+ns2.example. A 10.53.0.2
+example. NS ns4.example.
+ns4.example. A 10.53.0.4
diff --git a/bin/tests/system/zero/ns2/named.conf b/bin/tests/system/zero/ns2/named.conf
new file mode 100644 (file)
index 0000000..86673b2
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: named.conf,v 1.15 2009/05/29 23:47:49 tbox Exp $ */
+
+controls { /* empty */ };
+
+options {
+       query-source address 10.53.0.2;
+       notify-source 10.53.0.2;
+       transfer-source 10.53.0.2;
+       port 5300;
+       pid-file "named.pid";
+       listen-on { 10.53.0.2; };
+       listen-on-v6 { none; };
+       recursion no;
+       acache-enable yes;
+};
+
+zone "example" {
+       type master;
+       file "example.db";
+};
+
diff --git a/bin/tests/system/zero/ns3/named.conf b/bin/tests/system/zero/ns3/named.conf
new file mode 100644 (file)
index 0000000..3af9fd9
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: named.conf,v 1.15 2009/05/29 23:47:49 tbox Exp $ */
+
+controls { /* empty */ };
+
+options {
+       query-source address 10.53.0.3;
+       notify-source 10.53.0.3;
+       transfer-source 10.53.0.3;
+       port 5300;
+       pid-file "named.pid";
+       listen-on { 10.53.0.3; };
+       listen-on-v6 { none; };
+       recursion yes;
+       acache-enable yes;
+};
+
+zone "." {
+       type hint;
+       file "root.hint";
+};
+
diff --git a/bin/tests/system/zero/ns3/root.hint b/bin/tests/system/zero/ns3/root.hint
new file mode 100644 (file)
index 0000000..b67101d
--- /dev/null
@@ -0,0 +1,16 @@
+; Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+. NS ns1.
+ns1. A 10.53.0.1
diff --git a/bin/tests/system/zero/ns4/named.conf b/bin/tests/system/zero/ns4/named.conf
new file mode 100644 (file)
index 0000000..bceeb23
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: named.conf,v 1.15 2009/05/29 23:47:49 tbox Exp $ */
+
+controls { /* empty */ };
+
+options {
+       query-source address 10.53.0.4;
+       notify-source 10.53.0.4;
+       transfer-source 10.53.0.4;
+       port 5300;
+       pid-file "named.pid";
+       listen-on { 10.53.0.4; };
+       listen-on-v6 { none; };
+       recursion no;
+       acache-enable yes;
+};
+
+zone "example" {
+       type slave;
+       masters { 10.53.0.2; };
+       file "example.bk";
+};
+
diff --git a/bin/tests/system/zero/setup.sh b/bin/tests/system/zero/setup.sh
new file mode 100644 (file)
index 0000000..f5dd9c5
--- /dev/null
@@ -0,0 +1,15 @@
+# Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+sh ../genzone.sh 2 4 | sed -e 's/^$TTL 3600$/$TTL 0 ; force TTL to zero/' -e 's/86400.IN SOA/0 SOA/' > ns2/example.db
diff --git a/bin/tests/system/zero/tests.sh b/bin/tests/system/zero/tests.sh
new file mode 100644 (file)
index 0000000..15c2906
--- /dev/null
@@ -0,0 +1,48 @@
+# Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+status=0
+echo "I:check lookups against zero TTL records"
+i=0
+passes=10
+$DIG -p 5300 @10.53.0.2 axfr example | 
+awk '$2 == "0" { print "-q", $1, $4; print "-q", "zzz"$1, $4;}' > query.list
+while [ $i -lt $passes ]
+do
+       ret=0
+       $DIG -p 5300 @10.53.0.3 -f query.list > dig.out$i.1 &
+       $DIG -p 5300 @10.53.0.3 -f query.list > dig.out$i.2 &
+       $DIG -p 5300 @10.53.0.3 -f query.list > dig.out$i.3 &
+       $DIG -p 5300 @10.53.0.3 -f query.list > dig.out$i.4 &
+       $DIG -p 5300 @10.53.0.3 -f query.list > dig.out$i.5 &
+       $DIG -p 5300 @10.53.0.3 -f query.list > dig.out$i.6 &
+       wait
+       grep "status: SERVFAIL" dig.out$i.1 && ret=1
+       grep "status: SERVFAIL" dig.out$i.2 && ret=1
+       grep "status: SERVFAIL" dig.out$i.3 && ret=1
+       grep "status: SERVFAIL" dig.out$i.5 && ret=1
+       grep "status: SERVFAIL" dig.out$i.6 && ret=1
+       grep "status: SERVFAIL" dig.out$i.6 && ret=1
+       [ $ret = 1 ] && break
+       i=`expr $i + 1`
+       echo "I: successfully completed pass $i of $passes"
+done
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:exit status: $status"
+exit $status
index 52cd8e28804e867b1d66f9548ced33787a0ccaa8..eb7221b521f7f3f0f607519cb450abbcc2ac8e7e 100644 (file)
@@ -4316,7 +4316,7 @@ cache_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) {
        header_prev = NULL;
        for (header = node->data; header != NULL; header = header_next) {
                header_next = header->next;
-               if (header->rdh_ttl <= search->now) {
+               if (header->rdh_ttl <  search->now) {
                        /*
                         * This rdataset is stale.  If no one else is
                         * using the node, we can clean it up right
@@ -4324,7 +4324,7 @@ cache_zonecut_callback(dns_rbtnode_t *node, dns_name_t *name, void *arg) {
                         * the node as dirty, so it will get cleaned
                         * up later.
                         */
-                       if ((header->rdh_ttl <= search->now - RBTDB_VIRTUAL) &&
+                       if ((header->rdh_ttl <  search->now - RBTDB_VIRTUAL) &&
                            (locktype == isc_rwlocktype_write ||
                             NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
                                /*
@@ -4439,7 +4439,7 @@ find_deepest_zonecut(rbtdb_search_t *search, dns_rbtnode_t *node,
                     header != NULL;
                     header = header_next) {
                        header_next = header->next;
-                       if (header->rdh_ttl <= search->now) {
+                       if (header->rdh_ttl <  search->now) {
                                /*
                                 * This rdataset is stale.  If no one else is
                                 * using the node, we can clean it up right
@@ -4447,7 +4447,7 @@ find_deepest_zonecut(rbtdb_search_t *search, dns_rbtnode_t *node,
                                 * the node as dirty, so it will get cleaned
                                 * up later.
                                 */
-                               if ((header->rdh_ttl <= search->now -
+                               if ((header->rdh_ttl <  search->now -
                                                    RBTDB_VIRTUAL) &&
                                    (locktype == isc_rwlocktype_write ||
                                     NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
@@ -4615,7 +4615,7 @@ find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
                     header != NULL;
                     header = header_next) {
                        header_next = header->next;
-                       if (header->rdh_ttl <= now) {
+                       if (header->rdh_ttl <  now) {
                                /*
                                 * This rdataset is stale.  If no one else is
                                 * using the node, we can clean it up right
@@ -4623,7 +4623,7 @@ find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
                                 * node as dirty, so it will get cleaned up
                                 * later.
                                 */
-                               if ((header->rdh_ttl <= now - RBTDB_VIRTUAL) &&
+                               if ((header->rdh_ttl <  now - RBTDB_VIRTUAL) &&
                                    (locktype == isc_rwlocktype_write ||
                                     NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
                                        /*
@@ -4836,14 +4836,14 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
        header_prev = NULL;
        for (header = node->data; header != NULL; header = header_next) {
                header_next = header->next;
-               if (header->rdh_ttl <= now) {
+               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) &&
+                       if ((header->rdh_ttl <  now - RBTDB_VIRTUAL) &&
                            (locktype == isc_rwlocktype_write ||
                             NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
                                /*
@@ -5142,14 +5142,14 @@ cache_findzonecut(dns_db_t *db, dns_name_t *name, unsigned int options,
        header_prev = NULL;
        for (header = node->data; header != NULL; header = header_next) {
                header_next = header->next;
-               if (header->rdh_ttl <= now) {
+               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) &&
+                       if ((header->rdh_ttl < now - RBTDB_VIRTUAL) &&
                            (locktype == isc_rwlocktype_write ||
                             NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
                                /*
@@ -5627,8 +5627,8 @@ cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
 
        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) &&
+               if (header->rdh_ttl < now) {
+                       if ((header->rdh_ttl < now - RBTDB_VIRTUAL) &&
                            (locktype == isc_rwlocktype_write ||
                             NODE_TRYUPGRADE(lock) == ISC_R_SUCCESS)) {
                                /*
@@ -5935,7 +5935,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
                                        }
                        }
                        if (topheader != NULL && EXISTS(topheader) &&
-                           topheader->rdh_ttl > now) {
+                           topheader->rdh_ttl >= now) {
                                /*
                                 * Found one.
                                 */
@@ -6000,7 +6000,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
                 * has no effect, provided that the cache data isn't stale.
                 */
                if (rbtversion == NULL && trust < header->trust &&
-                   (header->rdh_ttl > now || header_nx)) {
+                   (header->rdh_ttl >= now || header_nx)) {
                        free_rdataset(rbtdb, rbtdb->common.mctx, newheader);
                        if (addedrdataset != NULL)
                                bind_rdataset(rbtdb, rbtnode, header, now,
@@ -6071,7 +6071,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
                 * Don't lower trust of existing record if the
                 * update is forced.
                 */
-               if (IS_CACHE(rbtdb) && header->rdh_ttl > now &&
+               if (IS_CACHE(rbtdb) && header->rdh_ttl >= now &&
                    header->type == dns_rdatatype_ns &&
                    !header_nx && !newheader_nx &&
                    header->trust >= newheader->trust &&
@@ -6107,7 +6107,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
                 * to be no more than the current NS RRset's TTL.  This
                 * ensures the delegations that are withdrawn are honoured.
                 */
-               if (IS_CACHE(rbtdb) && header->rdh_ttl > now &&
+               if (IS_CACHE(rbtdb) && header->rdh_ttl >= now &&
                    header->type == dns_rdatatype_ns &&
                    !header_nx && !newheader_nx &&
                    header->trust <= newheader->trust) {
@@ -6115,7 +6115,7 @@ add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, rbtdb_version_t *rbtversion,
                                newheader->rdh_ttl = header->rdh_ttl;
                        }
                }
-               if (IS_CACHE(rbtdb) && header->rdh_ttl > now &&
+               if (IS_CACHE(rbtdb) && header->rdh_ttl >= now &&
                    (header->type == dns_rdatatype_a ||
                     header->type == dns_rdatatype_aaaa ||
                     header->type == dns_rdatatype_ds ||
@@ -6517,7 +6517,7 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
                        cleanup_dead_nodes(rbtdb, rbtnode->locknum);
 
                header = isc_heap_element(rbtdb->heaps[rbtnode->locknum], 1);
-               if (header && header->rdh_ttl <= now - RBTDB_VIRTUAL)
+               if (header && header->rdh_ttl < now - RBTDB_VIRTUAL)
                        expire_header(rbtdb, header, tree_locked,
                                      expire_ttl);
 
@@ -9576,7 +9576,7 @@ overmem_purge(dns_rbtdb_t *rbtdb, unsigned int locknum_start,
                          isc_rwlocktype_write);
 
                header = isc_heap_element(rbtdb->heaps[locknum], 1);
-               if (header && header->rdh_ttl <= now - RBTDB_VIRTUAL) {
+               if (header && header->rdh_ttl < now - RBTDB_VIRTUAL) {
                        expire_header(rbtdb, header, tree_locked,
                                      expire_ttl);
                        purgecount--;