mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-31 14:35:26 +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))
|
||||
# Check that expired records are not dumped.
|
||||
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
|
||||
status=$((status + ret))
|
||||
|
||||
@@ -1665,13 +1665,13 @@ status=$((status + ret))
|
||||
echo_i "check rndc dump expired data.example ($n)"
|
||||
ret=0
|
||||
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 \
|
||||
| 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 \
|
||||
| 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 \
|
||||
| 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.
|
||||
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
|
||||
|
@@ -1164,15 +1164,7 @@ again:
|
||||
if (STALE(rds)) {
|
||||
fprintf(f, "; stale\n");
|
||||
} else if (ANCIENT(rds)) {
|
||||
isc_buffer_t b;
|
||||
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);
|
||||
fprintf(f, "; expired (awaiting cleanup)\n");
|
||||
}
|
||||
result = dump_rdataset(mctx, name, rds, ctx, buffer, f);
|
||||
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.
|
||||
*/
|
||||
static void
|
||||
expireheader(dns_slabheader_t *header, isc_rwlocktype_t *nlocktypep,
|
||||
isc_rwlocktype_t *tlocktypep, dns_expire_t reason DNS__DB_FLARG) {
|
||||
setttl(header, 0);
|
||||
mark(header, DNS_SLABHEADERATTR_ANCIENT);
|
||||
HEADERNODE(header)->dirty = 1;
|
||||
mark_ancient(header);
|
||||
|
||||
if (isc_refcount_current(&HEADERNODE(header)->erefs) == 0) {
|
||||
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).
|
||||
*/
|
||||
|
||||
if (KEEPSTALE(qpdb) && stale_ttl > now) {
|
||||
if (!ZEROTTL(header) && KEEPSTALE(qpdb) && stale_ttl > now) {
|
||||
stale = true;
|
||||
} else {
|
||||
/*
|
||||
@@ -1073,6 +1078,7 @@ bindrdataset(qpcache_t *qpdb, qpcnode_t *node, dns_slabheader_t *header,
|
||||
rdataset->rdclass = qpdb->common.rdclass;
|
||||
rdataset->type = DNS_TYPEPAIR_TYPE(header->type);
|
||||
rdataset->covers = DNS_TYPEPAIR_COVERS(header->type);
|
||||
rdataset->ttl = !ZEROTTL(header) ? header->ttl - now : 0;
|
||||
rdataset->ttl = header->ttl - now;
|
||||
rdataset->trust = header->trust;
|
||||
rdataset->resign = 0;
|
||||
@@ -1103,7 +1109,7 @@ bindrdataset(qpcache_t *qpdb, qpcnode_t *node, dns_slabheader_t *header,
|
||||
rdataset->attributes |= DNS_RDATASETATTR_STALE;
|
||||
} else if (!ACTIVE(header, now)) {
|
||||
rdataset->attributes |= DNS_RDATASETATTR_ANCIENT;
|
||||
rdataset->ttl = header->ttl;
|
||||
rdataset->ttl = 0;
|
||||
}
|
||||
|
||||
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);
|
||||
} else {
|
||||
mark(header, DNS_SLABHEADERATTR_ANCIENT);
|
||||
HEADERNODE(header)->dirty = 1;
|
||||
mark_ancient(header);
|
||||
*header_prev = header;
|
||||
}
|
||||
} 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
|
||||
* argument to the function.
|
||||
*/
|
||||
mark(header, DNS_SLABHEADERATTR_ANCIENT);
|
||||
HEADERNODE(header)->dirty = 1;
|
||||
mark_ancient(header);
|
||||
}
|
||||
} else if (EXISTS(header) && !ANCIENT(header)) {
|
||||
if (header->type == matchtype) {
|
||||
@@ -2585,13 +2589,6 @@ qpdb_destroy(dns_db_t *arg) {
|
||||
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
|
||||
* 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->down = topheader;
|
||||
topheader->next = newheader;
|
||||
qpnode->dirty = 1;
|
||||
mark_ancient(header);
|
||||
if (sigheader != NULL) {
|
||||
mark_ancient(sigheader);
|
||||
|
@@ -417,9 +417,7 @@ check_stale_header(dns_rbtnode_t *node, dns_slabheader_t *header,
|
||||
}
|
||||
dns_slabheader_destroy(&header);
|
||||
} else {
|
||||
dns__rbtdb_mark(header,
|
||||
DNS_SLABHEADERATTR_ANCIENT);
|
||||
RBTDB_HEADERNODE(header)->dirty = 1;
|
||||
dns__rbtdb_mark_ancient(header);
|
||||
*header_prev = header;
|
||||
}
|
||||
} 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
|
||||
* argument to the function.
|
||||
*/
|
||||
dns__rbtdb_mark(header,
|
||||
DNS_SLABHEADERATTR_ANCIENT);
|
||||
RBTDB_HEADERNODE(header)->dirty = 1;
|
||||
dns__rbtdb_mark_ancient(header);
|
||||
}
|
||||
} else if (EXISTS(header) && !ANCIENT(header)) {
|
||||
if (header->type == matchtype) {
|
||||
@@ -1589,9 +1585,7 @@ void
|
||||
dns__cacherbt_expireheader(dns_slabheader_t *header,
|
||||
isc_rwlocktype_t *tlocktypep,
|
||||
dns_expire_t reason DNS__DB_FLARG) {
|
||||
dns__rbtdb_setttl(header, 0);
|
||||
dns__rbtdb_mark(header, DNS_SLABHEADERATTR_ANCIENT);
|
||||
RBTDB_HEADERNODE(header)->dirty = 1;
|
||||
dns__rbtdb_mark_ancient(header);
|
||||
|
||||
if (isc_refcount_current(&RBTDB_HEADERNODE(header)->references) == 0) {
|
||||
isc_rwlocktype_t nlocktype = isc_rwlocktype_write;
|
||||
|
@@ -857,8 +857,8 @@ dns__rbtdb_mark(dns_slabheader_t *header, uint_least16_t flag) {
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mark_ancient(dns_slabheader_t *header) {
|
||||
void
|
||||
dns__rbtdb_mark_ancient(dns_slabheader_t *header) {
|
||||
dns__rbtdb_setttl(header, 0);
|
||||
dns__rbtdb_mark(header, DNS_SLABHEADERATTR_ANCIENT);
|
||||
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).
|
||||
*/
|
||||
|
||||
if (KEEPSTALE(rbtdb) && stale_ttl > now) {
|
||||
if (!ZEROTTL(header) && KEEPSTALE(rbtdb) && stale_ttl > now) {
|
||||
stale = true;
|
||||
} else {
|
||||
/*
|
||||
@@ -2165,7 +2165,7 @@ dns__rbtdb_bindrdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
|
||||
rdataset->rdclass = rbtdb->common.rdclass;
|
||||
rdataset->type = DNS_TYPEPAIR_TYPE(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;
|
||||
|
||||
if (NEGATIVE(header)) {
|
||||
@@ -2194,7 +2194,7 @@ dns__rbtdb_bindrdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node,
|
||||
rdataset->attributes |= DNS_RDATASETATTR_STALE;
|
||||
} else if (IS_CACHE(rbtdb) && !ACTIVE(header, now)) {
|
||||
rdataset->attributes |= DNS_RDATASETATTR_ANCIENT;
|
||||
rdataset->ttl = header->ttl;
|
||||
rdataset->ttl = 0;
|
||||
}
|
||||
|
||||
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 = topheader->next)
|
||||
{
|
||||
mark_ancient(topheader);
|
||||
dns__rbtdb_mark_ancient(topheader);
|
||||
}
|
||||
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
|
||||
* ncache entry.
|
||||
*/
|
||||
mark_ancient(topheader);
|
||||
dns__rbtdb_mark_ancient(topheader);
|
||||
topheader = NULL;
|
||||
goto find_header;
|
||||
}
|
||||
@@ -3013,9 +3013,9 @@ find_header:
|
||||
changed->dirty = true;
|
||||
}
|
||||
if (rbtversion == NULL) {
|
||||
mark_ancient(header);
|
||||
dns__rbtdb_mark_ancient(header);
|
||||
if (sigheader != NULL) {
|
||||
mark_ancient(sigheader);
|
||||
dns__rbtdb_mark_ancient(sigheader);
|
||||
}
|
||||
}
|
||||
if (rbtversion != NULL && !header_nx) {
|
||||
@@ -3119,7 +3119,7 @@ find_header:
|
||||
expireheader = newheader;
|
||||
}
|
||||
|
||||
mark_ancient(expireheader);
|
||||
dns__rbtdb_mark_ancient(expireheader);
|
||||
/*
|
||||
* FIXME: In theory, we should mark the RRSIG
|
||||
* 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
|
||||
*/
|
||||
void
|
||||
dns__rbtdb_mark_ancient(dns_slabheader_t *header);
|
||||
void
|
||||
dns__cacherbt_expireheader(dns_slabheader_t *header,
|
||||
isc_rwlocktype_t *tlocktypep,
|
||||
dns_expire_t reason DNS__DB_FLARG);
|
||||
|
Reference in New Issue
Block a user