mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-09-01 06:55:30 +00:00
[9.20] fix: usr: Recently expired records could be returned with timestamp in future
Under rare circumstances, the RRSet that expired at the time of the query could be returned with TTL far in the future. This has been fixed. As a side-effect, the expiration time of expired RRSets are no longer printed out in the cache dump. Closes #5094 Backport of MR !10048 Merge branch 'backport-5094-fix-timestamp-in-ttl-9.20' into 'bind-9.20' See merge request isc-projects/bind9!10059
This commit is contained in:
@@ -1649,7 +1649,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
|
|||||||
status=$((status + ret))
|
status=$((status + ret))
|
||||||
# Check that expired records are not dumped.
|
# Check that expired records are not dumped.
|
||||||
ret=0
|
ret=0
|
||||||
grep "; expired since .* (awaiting cleanup)" ns5/named_dump.db.test$n && ret=1
|
grep "; expired (awaiting cleanup)" ns5/named_dump.db.test$n && ret=1
|
||||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||||
status=$((status + ret))
|
status=$((status + ret))
|
||||||
|
|
||||||
@@ -1665,13 +1665,13 @@ status=$((status + ret))
|
|||||||
echo_i "check rndc dump expired data.example ($n)"
|
echo_i "check rndc dump expired data.example ($n)"
|
||||||
ret=0
|
ret=0
|
||||||
awk '/; expired/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \
|
awk '/; expired/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \
|
||||||
| grep "; expired since .* (awaiting cleanup) data\.example\..*A text record with a 2 second ttl" >/dev/null 2>&1 || ret=1
|
| grep "; expired (awaiting cleanup) data\.example\..*A text record with a 2 second ttl" >/dev/null 2>&1 || ret=1
|
||||||
awk '/; expired/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \
|
awk '/; expired/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \
|
||||||
| grep "; expired since .* (awaiting cleanup) nodata\.example\." >/dev/null 2>&1 || ret=1
|
| grep "; expired (awaiting cleanup) nodata\.example\." >/dev/null 2>&1 || ret=1
|
||||||
awk '/; expired/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \
|
awk '/; expired/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \
|
||||||
| grep "; expired since .* (awaiting cleanup) nxdomain\.example\." >/dev/null 2>&1 || ret=1
|
| grep "; expired (awaiting cleanup) nxdomain\.example\." >/dev/null 2>&1 || ret=1
|
||||||
awk '/; expired/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \
|
awk '/; expired/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \
|
||||||
| grep "; expired since .* (awaiting cleanup) othertype\.example\." >/dev/null 2>&1 || ret=1
|
| grep "; expired (awaiting cleanup) othertype\.example\." >/dev/null 2>&1 || ret=1
|
||||||
# Also make sure the not expired data does not have an expired comment.
|
# Also make sure the not expired data does not have an expired comment.
|
||||||
awk '/; authanswer/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \
|
awk '/; authanswer/ { x=$0; getline; print x, $0}' ns5/named_dump.db.test$n \
|
||||||
| grep "; authanswer longttl\.example.*A text record with a 600 second ttl" >/dev/null 2>&1 || ret=1
|
| grep "; authanswer longttl\.example.*A text record with a 600 second ttl" >/dev/null 2>&1 || ret=1
|
||||||
|
@@ -1164,15 +1164,7 @@ again:
|
|||||||
if (STALE(rds)) {
|
if (STALE(rds)) {
|
||||||
fprintf(f, "; stale\n");
|
fprintf(f, "; stale\n");
|
||||||
} else if (ANCIENT(rds)) {
|
} else if (ANCIENT(rds)) {
|
||||||
isc_buffer_t b;
|
fprintf(f, "; expired (awaiting cleanup)\n");
|
||||||
char buf[sizeof("YYYYMMDDHHMMSS")];
|
|
||||||
memset(buf, 0, sizeof(buf));
|
|
||||||
isc_buffer_init(&b, buf, sizeof(buf) - 1);
|
|
||||||
dns_time64_totext((uint64_t)rds->ttl, &b);
|
|
||||||
fprintf(f,
|
|
||||||
"; expired since %s "
|
|
||||||
"(awaiting cleanup)\n",
|
|
||||||
buf);
|
|
||||||
}
|
}
|
||||||
result = dump_rdataset(mctx, name, rds, ctx, buffer, f);
|
result = dump_rdataset(mctx, name, rds, ctx, buffer, f);
|
||||||
if (result != ISC_R_SUCCESS) {
|
if (result != ISC_R_SUCCESS) {
|
||||||
|
@@ -954,15 +954,20 @@ setttl(dns_slabheader_t *header, dns_ttl_t newttl) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mark_ancient(dns_slabheader_t *header) {
|
||||||
|
setttl(header, 0);
|
||||||
|
mark(header, DNS_SLABHEADERATTR_ANCIENT);
|
||||||
|
HEADERNODE(header)->dirty = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Caller must hold the node (write) lock.
|
* Caller must hold the node (write) lock.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
expireheader(dns_slabheader_t *header, isc_rwlocktype_t *nlocktypep,
|
expireheader(dns_slabheader_t *header, isc_rwlocktype_t *nlocktypep,
|
||||||
isc_rwlocktype_t *tlocktypep, dns_expire_t reason DNS__DB_FLARG) {
|
isc_rwlocktype_t *tlocktypep, dns_expire_t reason DNS__DB_FLARG) {
|
||||||
setttl(header, 0);
|
mark_ancient(header);
|
||||||
mark(header, DNS_SLABHEADERATTR_ANCIENT);
|
|
||||||
HEADERNODE(header)->dirty = 1;
|
|
||||||
|
|
||||||
if (isc_refcount_current(&HEADERNODE(header)->erefs) == 0) {
|
if (isc_refcount_current(&HEADERNODE(header)->erefs) == 0) {
|
||||||
qpcache_t *qpdb = (qpcache_t *)header->db;
|
qpcache_t *qpdb = (qpcache_t *)header->db;
|
||||||
@@ -1058,7 +1063,7 @@ bindrdataset(qpcache_t *qpdb, qpcnode_t *node, dns_slabheader_t *header,
|
|||||||
* (these records should not be cached anyway).
|
* (these records should not be cached anyway).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (KEEPSTALE(qpdb) && stale_ttl > now) {
|
if (!ZEROTTL(header) && KEEPSTALE(qpdb) && stale_ttl > now) {
|
||||||
stale = true;
|
stale = true;
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
@@ -1073,6 +1078,7 @@ bindrdataset(qpcache_t *qpdb, qpcnode_t *node, dns_slabheader_t *header,
|
|||||||
rdataset->rdclass = qpdb->common.rdclass;
|
rdataset->rdclass = qpdb->common.rdclass;
|
||||||
rdataset->type = DNS_TYPEPAIR_TYPE(header->type);
|
rdataset->type = DNS_TYPEPAIR_TYPE(header->type);
|
||||||
rdataset->covers = DNS_TYPEPAIR_COVERS(header->type);
|
rdataset->covers = DNS_TYPEPAIR_COVERS(header->type);
|
||||||
|
rdataset->ttl = !ZEROTTL(header) ? header->ttl - now : 0;
|
||||||
rdataset->ttl = header->ttl - now;
|
rdataset->ttl = header->ttl - now;
|
||||||
rdataset->trust = header->trust;
|
rdataset->trust = header->trust;
|
||||||
rdataset->resign = 0;
|
rdataset->resign = 0;
|
||||||
@@ -1103,7 +1109,7 @@ bindrdataset(qpcache_t *qpdb, qpcnode_t *node, dns_slabheader_t *header,
|
|||||||
rdataset->attributes |= DNS_RDATASETATTR_STALE;
|
rdataset->attributes |= DNS_RDATASETATTR_STALE;
|
||||||
} else if (!ACTIVE(header, now)) {
|
} else if (!ACTIVE(header, now)) {
|
||||||
rdataset->attributes |= DNS_RDATASETATTR_ANCIENT;
|
rdataset->attributes |= DNS_RDATASETATTR_ANCIENT;
|
||||||
rdataset->ttl = header->ttl;
|
rdataset->ttl = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
rdataset->count = atomic_fetch_add_relaxed(&header->count, 1);
|
rdataset->count = atomic_fetch_add_relaxed(&header->count, 1);
|
||||||
@@ -1270,8 +1276,7 @@ check_stale_header(qpcnode_t *node, dns_slabheader_t *header,
|
|||||||
}
|
}
|
||||||
dns_slabheader_destroy(&header);
|
dns_slabheader_destroy(&header);
|
||||||
} else {
|
} else {
|
||||||
mark(header, DNS_SLABHEADERATTR_ANCIENT);
|
mark_ancient(header);
|
||||||
HEADERNODE(header)->dirty = 1;
|
|
||||||
*header_prev = header;
|
*header_prev = header;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -2233,8 +2238,7 @@ findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
|
|||||||
* non-zero. This is so because 'node' is an
|
* non-zero. This is so because 'node' is an
|
||||||
* argument to the function.
|
* argument to the function.
|
||||||
*/
|
*/
|
||||||
mark(header, DNS_SLABHEADERATTR_ANCIENT);
|
mark_ancient(header);
|
||||||
HEADERNODE(header)->dirty = 1;
|
|
||||||
}
|
}
|
||||||
} else if (EXISTS(header) && !ANCIENT(header)) {
|
} else if (EXISTS(header) && !ANCIENT(header)) {
|
||||||
if (header->type == matchtype) {
|
if (header->type == matchtype) {
|
||||||
@@ -2585,13 +2589,6 @@ qpdb_destroy(dns_db_t *arg) {
|
|||||||
qpcache_detach(&qpdb);
|
qpcache_detach(&qpdb);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
mark_ancient(dns_slabheader_t *header) {
|
|
||||||
setttl(header, 0);
|
|
||||||
mark(header, DNS_SLABHEADERATTR_ANCIENT);
|
|
||||||
HEADERNODE(header)->dirty = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*%
|
/*%
|
||||||
* Clean up dead nodes. These are nodes which have no references, and
|
* Clean up dead nodes. These are nodes which have no references, and
|
||||||
* have no data. They are dead but we could not or chose not to delete
|
* have no data. They are dead but we could not or chose not to delete
|
||||||
@@ -3162,7 +3159,6 @@ find_header:
|
|||||||
newheader->next = topheader->next;
|
newheader->next = topheader->next;
|
||||||
newheader->down = topheader;
|
newheader->down = topheader;
|
||||||
topheader->next = newheader;
|
topheader->next = newheader;
|
||||||
qpnode->dirty = 1;
|
|
||||||
mark_ancient(header);
|
mark_ancient(header);
|
||||||
if (sigheader != NULL) {
|
if (sigheader != NULL) {
|
||||||
mark_ancient(sigheader);
|
mark_ancient(sigheader);
|
||||||
|
@@ -417,9 +417,7 @@ check_stale_header(dns_rbtnode_t *node, dns_slabheader_t *header,
|
|||||||
}
|
}
|
||||||
dns_slabheader_destroy(&header);
|
dns_slabheader_destroy(&header);
|
||||||
} else {
|
} else {
|
||||||
dns__rbtdb_mark(header,
|
dns__rbtdb_mark_ancient(header);
|
||||||
DNS_SLABHEADERATTR_ANCIENT);
|
|
||||||
RBTDB_HEADERNODE(header)->dirty = 1;
|
|
||||||
*header_prev = header;
|
*header_prev = header;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1401,9 +1399,7 @@ cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
|
|||||||
* non-zero. This is so because 'node' is an
|
* non-zero. This is so because 'node' is an
|
||||||
* argument to the function.
|
* argument to the function.
|
||||||
*/
|
*/
|
||||||
dns__rbtdb_mark(header,
|
dns__rbtdb_mark_ancient(header);
|
||||||
DNS_SLABHEADERATTR_ANCIENT);
|
|
||||||
RBTDB_HEADERNODE(header)->dirty = 1;
|
|
||||||
}
|
}
|
||||||
} else if (EXISTS(header) && !ANCIENT(header)) {
|
} else if (EXISTS(header) && !ANCIENT(header)) {
|
||||||
if (header->type == matchtype) {
|
if (header->type == matchtype) {
|
||||||
@@ -1589,9 +1585,7 @@ void
|
|||||||
dns__cacherbt_expireheader(dns_slabheader_t *header,
|
dns__cacherbt_expireheader(dns_slabheader_t *header,
|
||||||
isc_rwlocktype_t *tlocktypep,
|
isc_rwlocktype_t *tlocktypep,
|
||||||
dns_expire_t reason DNS__DB_FLARG) {
|
dns_expire_t reason DNS__DB_FLARG) {
|
||||||
dns__rbtdb_setttl(header, 0);
|
dns__rbtdb_mark_ancient(header);
|
||||||
dns__rbtdb_mark(header, DNS_SLABHEADERATTR_ANCIENT);
|
|
||||||
RBTDB_HEADERNODE(header)->dirty = 1;
|
|
||||||
|
|
||||||
if (isc_refcount_current(&RBTDB_HEADERNODE(header)->references) == 0) {
|
if (isc_refcount_current(&RBTDB_HEADERNODE(header)->references) == 0) {
|
||||||
isc_rwlocktype_t nlocktype = isc_rwlocktype_write;
|
isc_rwlocktype_t nlocktype = isc_rwlocktype_write;
|
||||||
|
@@ -857,8 +857,8 @@ dns__rbtdb_mark(dns_slabheader_t *header, uint_least16_t flag) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
mark_ancient(dns_slabheader_t *header) {
|
dns__rbtdb_mark_ancient(dns_slabheader_t *header) {
|
||||||
dns__rbtdb_setttl(header, 0);
|
dns__rbtdb_setttl(header, 0);
|
||||||
dns__rbtdb_mark(header, DNS_SLABHEADERATTR_ANCIENT);
|
dns__rbtdb_mark(header, DNS_SLABHEADERATTR_ANCIENT);
|
||||||
RBTDB_HEADERNODE(header)->dirty = 1;
|
RBTDB_HEADERNODE(header)->dirty = 1;
|
||||||
@@ -2150,7 +2150,7 @@ dns__rbtdb_bindrdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
|
|||||||
* (these records should not be cached anyway).
|
* (these records should not be cached anyway).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (KEEPSTALE(rbtdb) && stale_ttl > now) {
|
if (!ZEROTTL(header) && KEEPSTALE(rbtdb) && stale_ttl > now) {
|
||||||
stale = true;
|
stale = true;
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
@@ -2165,7 +2165,7 @@ dns__rbtdb_bindrdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
|
|||||||
rdataset->rdclass = rbtdb->common.rdclass;
|
rdataset->rdclass = rbtdb->common.rdclass;
|
||||||
rdataset->type = DNS_TYPEPAIR_TYPE(header->type);
|
rdataset->type = DNS_TYPEPAIR_TYPE(header->type);
|
||||||
rdataset->covers = DNS_TYPEPAIR_COVERS(header->type);
|
rdataset->covers = DNS_TYPEPAIR_COVERS(header->type);
|
||||||
rdataset->ttl = header->ttl - now;
|
rdataset->ttl = !ZEROTTL(header) ? header->ttl - now : 0;
|
||||||
rdataset->trust = header->trust;
|
rdataset->trust = header->trust;
|
||||||
|
|
||||||
if (NEGATIVE(header)) {
|
if (NEGATIVE(header)) {
|
||||||
@@ -2194,7 +2194,7 @@ dns__rbtdb_bindrdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
|
|||||||
rdataset->attributes |= DNS_RDATASETATTR_STALE;
|
rdataset->attributes |= DNS_RDATASETATTR_STALE;
|
||||||
} else if (IS_CACHE(rbtdb) && !ACTIVE(header, now)) {
|
} else if (IS_CACHE(rbtdb) && !ACTIVE(header, now)) {
|
||||||
rdataset->attributes |= DNS_RDATASETATTR_ANCIENT;
|
rdataset->attributes |= DNS_RDATASETATTR_ANCIENT;
|
||||||
rdataset->ttl = header->ttl;
|
rdataset->ttl = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
rdataset->count = atomic_fetch_add_relaxed(&header->count, 1);
|
rdataset->count = atomic_fetch_add_relaxed(&header->count, 1);
|
||||||
@@ -2604,7 +2604,7 @@ dns__rbtdb_add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode,
|
|||||||
topheader != NULL;
|
topheader != NULL;
|
||||||
topheader = topheader->next)
|
topheader = topheader->next)
|
||||||
{
|
{
|
||||||
mark_ancient(topheader);
|
dns__rbtdb_mark_ancient(topheader);
|
||||||
}
|
}
|
||||||
goto find_header;
|
goto find_header;
|
||||||
}
|
}
|
||||||
@@ -2667,7 +2667,7 @@ dns__rbtdb_add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode,
|
|||||||
* The new rdataset is better. Expire the
|
* The new rdataset is better. Expire the
|
||||||
* ncache entry.
|
* ncache entry.
|
||||||
*/
|
*/
|
||||||
mark_ancient(topheader);
|
dns__rbtdb_mark_ancient(topheader);
|
||||||
topheader = NULL;
|
topheader = NULL;
|
||||||
goto find_header;
|
goto find_header;
|
||||||
}
|
}
|
||||||
@@ -3013,9 +3013,9 @@ find_header:
|
|||||||
changed->dirty = true;
|
changed->dirty = true;
|
||||||
}
|
}
|
||||||
if (rbtversion == NULL) {
|
if (rbtversion == NULL) {
|
||||||
mark_ancient(header);
|
dns__rbtdb_mark_ancient(header);
|
||||||
if (sigheader != NULL) {
|
if (sigheader != NULL) {
|
||||||
mark_ancient(sigheader);
|
dns__rbtdb_mark_ancient(sigheader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rbtversion != NULL && !header_nx) {
|
if (rbtversion != NULL && !header_nx) {
|
||||||
@@ -3119,7 +3119,7 @@ find_header:
|
|||||||
expireheader = newheader;
|
expireheader = newheader;
|
||||||
}
|
}
|
||||||
|
|
||||||
mark_ancient(expireheader);
|
dns__rbtdb_mark_ancient(expireheader);
|
||||||
/*
|
/*
|
||||||
* FIXME: In theory, we should mark the RRSIG
|
* FIXME: In theory, we should mark the RRSIG
|
||||||
* and the header at the same time, but there is
|
* and the header at the same time, but there is
|
||||||
|
@@ -486,6 +486,8 @@ dns__zonerbt_addwildcards(dns_rbtdb_t *rbtdb, const dns_name_t *name,
|
|||||||
* Cache-specific functions that are called from rbtdb.c
|
* Cache-specific functions that are called from rbtdb.c
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
|
dns__rbtdb_mark_ancient(dns_slabheader_t *header);
|
||||||
|
void
|
||||||
dns__cacherbt_expireheader(dns_slabheader_t *header,
|
dns__cacherbt_expireheader(dns_slabheader_t *header,
|
||||||
isc_rwlocktype_t *tlocktypep,
|
isc_rwlocktype_t *tlocktypep,
|
||||||
dns_expire_t reason DNS__DB_FLARG);
|
dns_expire_t reason DNS__DB_FLARG);
|
||||||
|
Reference in New Issue
Block a user