From 87912e4bb8410ffe1d88295b22d319f5cd410e2c Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 29 Jun 2023 15:52:32 +1000 Subject: [PATCH 1/9] Provide a mechanism to return the expire option value to the zone code so that it can be used to adjust the expire time. --- lib/dns/include/dns/types.h | 2 +- lib/dns/xfrin.c | 4 ++-- lib/dns/zone.c | 13 ++++++++----- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h index b334eff0a2..70e0b02274 100644 --- a/lib/dns/include/dns/types.h +++ b/lib/dns/include/dns/types.h @@ -434,7 +434,7 @@ typedef isc_result_t (*dns_additionaldatafunc_t)( typedef isc_result_t (*dns_digestfunc_t)(void *, isc_region_t *); -typedef void (*dns_xfrindone_t)(dns_zone_t *, isc_result_t); +typedef void (*dns_xfrindone_t)(dns_zone_t *, uint32_t *, isc_result_t); typedef void (*dns_updatecallback_t)(void *, isc_result_t, dns_message_t *); diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index fba413cba6..be7a427226 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -833,7 +833,7 @@ xfrin_fail(dns_xfrin_t *xfr, isc_result_t result, const char *msg) { dns_journal_destroy(&xfr->ixfr.journal); } if (xfr->done != NULL) { - (xfr->done)(xfr->zone, result); + (xfr->done)(xfr->zone, NULL, result); xfr->done = NULL; } xfr->shutdown_result = result; @@ -1545,7 +1545,7 @@ xfrin_recv_done(isc_result_t result, isc_region_t *region, void *arg) { if (xfr->done != NULL) { LIBDNS_XFRIN_DONE_CALLBACK_BEGIN(xfr, xfr->info, result); - (xfr->done)(xfr->zone, ISC_R_SUCCESS); + (xfr->done)(xfr->zone, NULL, ISC_R_SUCCESS); xfr->done = NULL; LIBDNS_XFRIN_DONE_CALLBACK_END(xfr, xfr->info, result); } diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 2ecc27557c..8a13a2dcc4 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -862,7 +862,7 @@ zone_catz_disable(dns_zone_t *zone); static isc_result_t default_journal(dns_zone_t *zone); static void -zone_xfrdone(dns_zone_t *zone, isc_result_t result); +zone_xfrdone(dns_zone_t *zone, uint32_t *expireopt, isc_result_t result); static isc_result_t zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, isc_result_t result); @@ -17079,7 +17079,7 @@ zone_detachdb(dns_zone_t *zone) { } static void -zone_xfrdone(dns_zone_t *zone, isc_result_t result) { +zone_xfrdone(dns_zone_t *zone, uint32_t *expireopt, isc_result_t result) { isc_time_t now; bool again = false; unsigned int soacount; @@ -17091,8 +17091,11 @@ zone_xfrdone(dns_zone_t *zone, isc_result_t result) { REQUIRE(DNS_ZONE_VALID(zone)); - dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_DEBUG(1), - "zone transfer finished: %s", isc_result_totext(result)); + dns_zone_logc( + zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_DEBUG(1), + expireopt == NULL ? "zone transfer finished: %s" + : "zone transfer finished: %s, expire=%u", + isc_result_totext(result), expireopt != NULL ? *expireopt : 0); /* * Obtaining a lock on the zone->secure (see zone_send_secureserial) @@ -17715,7 +17718,7 @@ failure: * zmgr->xfrin_in_progress. */ if (result != ISC_R_SUCCESS) { - zone_xfrdone(zone, result); + zone_xfrdone(zone, NULL, result); } if (zmgr_tlsctx_cache != NULL) { From 690fd050a0b8720a4094f315e3c313a1a1fceea0 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 29 Jun 2023 17:25:15 +1000 Subject: [PATCH 2/9] Allow EDNS to be used when making requests in xfrin This allow for the EDNS options EXPIRE and NSID to be sent when when making requests. The existing controls controlling whether EDNS is used and whether EXPIRE or NSID are sent are honoured. Adjust the expected byte counts in the xfer system test to reflect the EDNS overhead. Adjust the dig call to match named's behavior (don't set +expire as we are talking to a secondary). --- ...stats.good => ixfr-stats-with-expire.good} | 2 +- .../ixfr/ixfr-stats-without-expire.good | 3 + bin/tests/system/ixfr/tests.sh | 10 ++-- bin/tests/system/xfer/axfr-stats.good | 2 +- bin/tests/system/xfer/tests.sh | 2 +- lib/dns/xfrin.c | 55 +++++++++++++++++++ 6 files changed, 66 insertions(+), 8 deletions(-) rename bin/tests/system/ixfr/{ixfr-stats.good => ixfr-stats-with-expire.good} (67%) create mode 100644 bin/tests/system/ixfr/ixfr-stats-without-expire.good diff --git a/bin/tests/system/ixfr/ixfr-stats.good b/bin/tests/system/ixfr/ixfr-stats-with-expire.good similarity index 67% rename from bin/tests/system/ixfr/ixfr-stats.good rename to bin/tests/system/ixfr/ixfr-stats-with-expire.good index 3d0d2dde32..c02992b14f 100644 --- a/bin/tests/system/ixfr/ixfr-stats.good +++ b/bin/tests/system/ixfr/ixfr-stats-with-expire.good @@ -1,3 +1,3 @@ messages=1 records=5 -bytes=204 +bytes=223 diff --git a/bin/tests/system/ixfr/ixfr-stats-without-expire.good b/bin/tests/system/ixfr/ixfr-stats-without-expire.good new file mode 100644 index 0000000000..0713c0b8ee --- /dev/null +++ b/bin/tests/system/ixfr/ixfr-stats-without-expire.good @@ -0,0 +1,3 @@ +messages=1 +records=5 +bytes=215 diff --git a/bin/tests/system/ixfr/tests.sh b/bin/tests/system/ixfr/tests.sh index f1e3b62241..4b61a2c9e1 100644 --- a/bin/tests/system/ixfr/tests.sh +++ b/bin/tests/system/ixfr/tests.sh @@ -388,9 +388,9 @@ status=$((status+ret)) n=$((n+1)) echo_i "checking whether dig calculates IXFR statistics correctly ($n)" ret=0 -$DIG $DIGOPTS +noedns +stat -b 10.53.0.4 @10.53.0.4 test. ixfr=2 > dig.out1.test$n +$DIG $DIGOPTS +expire +nocookie +stat -b 10.53.0.4 @10.53.0.4 test. ixfr=2 > dig.out1.test$n get_dig_xfer_stats dig.out1.test$n > stats.dig -diff ixfr-stats.good stats.dig > /dev/null || ret=1 +diff ixfr-stats-with-expire.good stats.dig > /dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) @@ -400,20 +400,20 @@ status=$((status+ret)) _wait_for_stats () { get_named_xfer_stats ns4/named.run "$1" test "$2" > "$3" - diff ixfr-stats.good "$3" > /dev/null || return 1 + diff "$4" "$3" > /dev/null || return 1 return 0 } n=$((n+1)) echo_i "checking whether named calculates incoming IXFR statistics correctly ($n)" ret=0 -retry_quiet 10 _wait_for_stats 10.53.0.3 "Transfer completed" stats.incoming || ret=1 +retry_quiet 10 _wait_for_stats 10.53.0.3 "Transfer completed" stats.incoming ixfr-stats-without-expire.good || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) n=$((n+1)) echo_i "checking whether named calculates outgoing IXFR statistics correctly ($n)" -retry_quiet 10 _wait_for_stats 10.53.0.4 "IXFR ended" stats.outgoing || ret=1 +retry_quiet 10 _wait_for_stats 10.53.0.4 "IXFR ended" stats.outgoing ixfr-stats-with-expire.good || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) diff --git a/bin/tests/system/xfer/axfr-stats.good b/bin/tests/system/xfer/axfr-stats.good index 264af09705..616854e646 100644 --- a/bin/tests/system/xfer/axfr-stats.good +++ b/bin/tests/system/xfer/axfr-stats.good @@ -1,3 +1,3 @@ messages=16 records=10003 -bytes=218227 +bytes=218403 diff --git a/bin/tests/system/xfer/tests.sh b/bin/tests/system/xfer/tests.sh index 33eee6ae47..6595927e7e 100755 --- a/bin/tests/system/xfer/tests.sh +++ b/bin/tests/system/xfer/tests.sh @@ -541,7 +541,7 @@ tmp=0 # Use -b so that we can discern between incoming and outgoing transfers in ns3 # logs later on. wait_for_xfer() ( - $DIG $DIGOPTS +noedns +stat -b 10.53.0.2 @10.53.0.3 xfer-stats. AXFR > dig.out.ns3.test$n + $DIG $DIGOPTS +edns +nocookie +noexpire +stat -b 10.53.0.2 @10.53.0.3 xfer-stats. AXFR > dig.out.ns3.test$n grep "; Transfer failed" dig.out.ns3.test$n > /dev/null || return 0 return 1 ) diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index be7a427226..3961f8eb84 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -1147,6 +1148,38 @@ request_type(dns_xfrin_t *xfr) { } } +static isc_result_t +add_opt(dns_message_t *message, uint16_t udpsize, bool reqnsid, + bool reqexpire) { + isc_result_t result; + dns_rdataset_t *rdataset = NULL; + dns_ednsopt_t ednsopts[DNS_EDNSOPTIONS]; + int count = 0; + + /* Set EDNS options if applicable. */ + if (reqnsid) { + INSIST(count < DNS_EDNSOPTIONS); + ednsopts[count].code = DNS_OPT_NSID; + ednsopts[count].length = 0; + ednsopts[count].value = NULL; + count++; + } + if (reqexpire) { + INSIST(count < DNS_EDNSOPTIONS); + ednsopts[count].code = DNS_OPT_EXPIRE; + ednsopts[count].length = 0; + ednsopts[count].value = NULL; + count++; + } + result = dns_message_buildopt(message, &rdataset, 0, udpsize, 0, + ednsopts, count); + if (result != ISC_R_SUCCESS) { + return (result); + } + + return (dns_message_setopt(message, rdataset)); +} + /* * Build an *XFR request and send its length prefix. */ @@ -1160,6 +1193,10 @@ xfrin_send_request(dns_xfrin_t *xfr) { dns_name_t *qname = NULL; dns_dbversion_t *ver = NULL; dns_name_t *msgsoaname = NULL; + bool edns = true; + bool reqnsid = xfr->view->requestnsid; + bool reqexpire = dns_zone_getrequestexpire(xfr->zone); + uint16_t udpsize = dns_view_getudpsize(xfr->view); LIBDNS_XFRIN_RECV_SEND_REQUEST(xfr, xfr->info); @@ -1198,6 +1235,24 @@ xfrin_send_request(dns_xfrin_t *xfr) { &xfr->ixfr.request_serial)); } + if (xfr->view->peers != NULL) { + dns_peer_t *peer = NULL; + isc_netaddr_t primaryip; + isc_netaddr_fromsockaddr(&primaryip, &xfr->primaryaddr); + result = dns_peerlist_peerbyaddr(xfr->view->peers, &primaryip, + &peer); + if (result == ISC_R_SUCCESS) { + (void)dns_peer_getsupportedns(peer, &edns); + (void)dns_peer_getudpsize(peer, &udpsize); + (void)dns_peer_getrequestnsid(peer, &reqnsid); + (void)dns_peer_getrequestexpire(peer, &reqexpire); + } + } + + if (edns) { + CHECK(add_opt(msg, udpsize, reqnsid, reqexpire)); + } + xfr->nmsg = 0; xfr->nrecs = 0; xfr->nbytes = 0; From be21d31840bb91b51c00fac6d48d900f9843d63e Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Tue, 4 Jul 2023 14:22:29 +1000 Subject: [PATCH 3/9] Handle EDNS induced FORMERR responses If we are talking to a non EDNS aware primary that returns FORMERR to EDNS requests retry the request without using EDNS. --- lib/dns/xfrin.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index 3961f8eb84..07b624c5f1 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -142,7 +142,7 @@ struct dns_xfrin { xfrin_state_t state; uint32_t end_serial; - bool is_ixfr; + bool edns, is_ixfr; unsigned int nmsg; /*%< Number of messages recvd */ unsigned int nrecs; /*%< Number of records recvd */ @@ -861,6 +861,7 @@ xfrin_create(isc_mem_t *mctx, dns_zone_t *zone, dns_db_t *db, .primaryaddr = *primaryaddr, .sourceaddr = *sourceaddr, .firstsoa = DNS_RDATA_INIT, + .edns = true, .magic = XFRIN_MAGIC, }; @@ -1193,7 +1194,7 @@ xfrin_send_request(dns_xfrin_t *xfr) { dns_name_t *qname = NULL; dns_dbversion_t *ver = NULL; dns_name_t *msgsoaname = NULL; - bool edns = true; + bool edns = xfr->edns; bool reqnsid = xfr->view->requestnsid; bool reqexpire = dns_zone_getrequestexpire(xfr->zone); uint16_t udpsize = dns_view_getudpsize(xfr->view); @@ -1235,7 +1236,7 @@ xfrin_send_request(dns_xfrin_t *xfr) { &xfr->ixfr.request_serial)); } - if (xfr->view->peers != NULL) { + if (edns && xfr->view->peers != NULL) { dns_peer_t *peer = NULL; isc_netaddr_t primaryip; isc_netaddr_fromsockaddr(&primaryip, &xfr->primaryaddr); @@ -1379,7 +1380,17 @@ xfrin_recv_done(isc_result_t result, isc_region_t *region, void *arg) { if (result != ISC_R_SUCCESS || msg->rcode != dns_rcode_noerror || msg->opcode != dns_opcode_query || msg->rdclass != xfr->rdclass) { - if (result == ISC_R_SUCCESS && msg->rcode != dns_rcode_noerror) + if (result == ISC_R_SUCCESS && + msg->rcode == dns_rcode_formerr && xfr->edns && + (xfr->state == XFRST_SOAQUERY || + xfr->state == XFRST_INITIALSOA)) + { + xfr->edns = false; + dns_message_detach(&msg); + xfrin_reset(xfr); + goto try_again; + } else if (result == ISC_R_SUCCESS && + msg->rcode != dns_rcode_noerror) { result = dns_result_fromrcode(msg->rcode); } else if (result == ISC_R_SUCCESS && @@ -1408,6 +1419,7 @@ xfrin_recv_done(isc_result_t result, isc_region_t *region, void *arg) { xfrin_reset(xfr); xfr->reqtype = dns_rdatatype_soa; xfr->state = XFRST_SOAQUERY; + try_again: result = xfrin_start(xfr); if (result != ISC_R_SUCCESS) { xfrin_fail(xfr, result, "failed setting up socket"); From 0b4200c010737a3d74903ab95ce94dfa68e1bd42 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 29 Jun 2023 17:59:24 +1000 Subject: [PATCH 4/9] Extract the expire option from the response --- lib/dns/xfrin.c | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index 07b624c5f1..079d75a083 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -142,7 +142,8 @@ struct dns_xfrin { xfrin_state_t state; uint32_t end_serial; - bool edns, is_ixfr; + uint32_t expireopt; + bool edns, is_ixfr, expireoptset; unsigned int nmsg; /*%< Number of messages recvd */ unsigned int nrecs; /*%< Number of records recvd */ @@ -834,7 +835,9 @@ xfrin_fail(dns_xfrin_t *xfr, isc_result_t result, const char *msg) { dns_journal_destroy(&xfr->ixfr.journal); } if (xfr->done != NULL) { - (xfr->done)(xfr->zone, NULL, result); + (xfr->done)(xfr->zone, + xfr->expireoptset ? &xfr->expireopt : NULL, + result); xfr->done = NULL; } xfr->shutdown_result = result; @@ -1323,6 +1326,38 @@ failure: dns_xfrin_detach(&xfr); } +static void +get_edns_expire(dns_xfrin_t *xfr, dns_message_t *msg) { + isc_result_t result; + dns_rdata_t rdata = DNS_RDATA_INIT; + isc_buffer_t optbuf; + uint16_t optcode; + uint16_t optlen; + + result = dns_rdataset_first(msg->opt); + if (result == ISC_R_SUCCESS) { + dns_rdataset_current(msg->opt, &rdata); + isc_buffer_init(&optbuf, rdata.data, rdata.length); + isc_buffer_add(&optbuf, rdata.length); + while (isc_buffer_remaininglength(&optbuf) >= 4) { + optcode = isc_buffer_getuint16(&optbuf); + optlen = isc_buffer_getuint16(&optbuf); + /* + * A EDNS EXPIRE response has a length of 4. + */ + if (optcode != DNS_OPT_EXPIRE || optlen != 4) { + isc_buffer_forward(&optbuf, optlen); + continue; + } + xfr->expireopt = isc_buffer_getuint32(&optbuf); + xfr->expireoptset = true; + dns_zone_log(xfr->zone, ISC_LOG_DEBUG(1), + "got EDNS EXPIRE of %u", xfr->expireopt); + break; + } + } +} + static void xfrin_recv_done(isc_result_t result, isc_region_t *region, void *arg) { dns_xfrin_t *xfr = (dns_xfrin_t *)arg; @@ -1585,6 +1620,10 @@ xfrin_recv_done(isc_result_t result, isc_region_t *region, void *arg) { xfr->tsigctx = msg->tsigctx; msg->tsigctx = NULL; + if (!xfr->expireoptset && msg->opt != NULL) { + get_edns_expire(xfr, msg); + } + switch (xfr->state) { case XFRST_GOTSOA: xfr->reqtype = dns_rdatatype_axfr; @@ -1612,7 +1651,9 @@ xfrin_recv_done(isc_result_t result, isc_region_t *region, void *arg) { if (xfr->done != NULL) { LIBDNS_XFRIN_DONE_CALLBACK_BEGIN(xfr, xfr->info, result); - (xfr->done)(xfr->zone, NULL, ISC_R_SUCCESS); + (xfr->done)(xfr->zone, + xfr->expireoptset ? &xfr->expireopt : NULL, + ISC_R_SUCCESS); xfr->done = NULL; LIBDNS_XFRIN_DONE_CALLBACK_END(xfr, xfr->info, result); } From b04d9413541e06a5e7b77f9934b781ea6e33e2b9 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Mon, 3 Jul 2023 17:10:28 +1000 Subject: [PATCH 5/9] Trim the effective expire time based on expire option and use that adjusted time to set the modification times. --- lib/dns/zone.c | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 8a13a2dcc4..64064a4157 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -17080,11 +17080,11 @@ zone_detachdb(dns_zone_t *zone) { static void zone_xfrdone(dns_zone_t *zone, uint32_t *expireopt, isc_result_t result) { - isc_time_t now; + isc_time_t now, expiretime; bool again = false; unsigned int soacount; unsigned int nscount; - uint32_t serial, refresh, retry, expire, minimum, soattl; + uint32_t serial, refresh, retry, expire, minimum, soattl, oldexpire; isc_result_t xfrresult = result; bool free_needed; dns_zone_t *secure = NULL; @@ -17136,6 +17136,8 @@ again: goto same_primary; } + oldexpire = zone->expire; + /* * Update the zone structure's data from the actual * SOA received. @@ -17188,19 +17190,31 @@ again: } /* - * Set our next update/expire times. + * Set our next refresh time. */ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NEEDREFRESH)) { DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NEEDREFRESH); zone->refreshtime = now; - DNS_ZONE_TIME_ADD(&now, zone->expire, - &zone->expiretime); } else { DNS_ZONE_JITTER_ADD(&now, zone->refresh, &zone->refreshtime); - DNS_ZONE_TIME_ADD(&now, zone->expire, - &zone->expiretime); } + + /* + * Set our next expire time. If the parent returned + * an EXPIRE option use that to update zone->expiretime. + */ + expire = zone->expire; + if (expireopt != NULL && *expireopt < expire) { + expire = *expireopt; + } + DNS_ZONE_TIME_ADD(&now, expire, &expiretime); + if (oldexpire != zone->expire || + isc_time_compare(&expiretime, &zone->expiretime) > 0) + { + zone->expiretime = expiretime; + } + if (result == ISC_R_SUCCESS && xfrresult == ISC_R_SUCCESS) { char buf[DNS_NAME_FORMATSIZE + sizeof(": TSIG ''")]; if (zone->tsigkey != NULL) { @@ -17227,15 +17241,27 @@ again: */ if (zone->masterfile != NULL || zone->journal != NULL) { unsigned int delay = DNS_DUMP_DELAY; + isc_interval_t i; + isc_time_t when; + + /* + * Compute effective modification time. + */ + isc_interval_set(&i, zone->expire, 0); + result = isc_time_subtract(&zone->expiretime, &i, + &when); + if (result != ISC_R_SUCCESS) { + when = now; + } result = ISC_R_FAILURE; if (zone->journal != NULL) { - result = isc_file_settime(zone->journal, &now); + result = isc_file_settime(zone->journal, &when); } if (result != ISC_R_SUCCESS && zone->masterfile != NULL) { result = isc_file_settime(zone->masterfile, - &now); + &when); } if ((DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NODELAY) != 0) || From 9e03b5f5cc3d6cb63a19be24f9cd7a1329b0cd7f Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Tue, 4 Jul 2023 11:57:56 +1000 Subject: [PATCH 6/9] Set the modification time of the zone file after dumping For secondary, mirror and redirect zones the expiry time is set from the zone file's modification time on restart. As zone dumping take time, set the modification time of the zone file to the expire time less the expire interval. --- lib/dns/zone.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 64064a4157..d463490908 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -11390,6 +11390,27 @@ dump_done(void *arg, isc_result_t result) { ENTER; + /* + * Adjust modification time of zone file to preserve expire timing. + */ + if ((zone->type == dns_zone_secondary || + zone->type == dns_zone_mirror || + zone->type == dns_zone_redirect) && + result == ISC_R_SUCCESS) + { + LOCK_ZONE(zone); + isc_time_t when; + isc_interval_t i; + isc_interval_set(&i, zone->expire, 0); + result = isc_time_subtract(&zone->expiretime, &i, &when); + if (result == ISC_R_SUCCESS) { + (void)isc_file_settime(zone->masterfile, &when); + } else { + result = ISC_R_SUCCESS; + } + UNLOCK_ZONE(zone); + } + if (result == ISC_R_SUCCESS && zone->journal != NULL) { /* * We don't own these, zone->dctx must stay valid. @@ -11573,6 +11594,22 @@ redo: } else { result = dns_master_dump(zone->mctx, db, version, masterstyle, masterfile, masterformat, &rawdata); + if ((zone->type == dns_zone_secondary || + zone->type == dns_zone_mirror || + zone->type == dns_zone_redirect) && + result == ISC_R_SUCCESS) + { + isc_time_t when; + isc_interval_t i; + isc_interval_set(&i, zone->expire, 0); + result = isc_time_subtract(&zone->expiretime, &i, + &when); + if (result == ISC_R_SUCCESS) { + (void)isc_file_settime(zone->masterfile, &when); + } else { + result = ISC_R_SUCCESS; + } + } } fail: if (version != NULL) { From cf03b1ed952d500cb34eb68dcf6105374fa1c5c5 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Tue, 4 Jul 2023 17:39:29 +1000 Subject: [PATCH 7/9] Check EDNS EXPIRE is returned with AXFR --- bin/tests/system/xfer/tests.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/bin/tests/system/xfer/tests.sh b/bin/tests/system/xfer/tests.sh index 6595927e7e..cd1a50a3e4 100755 --- a/bin/tests/system/xfer/tests.sh +++ b/bin/tests/system/xfer/tests.sh @@ -464,7 +464,15 @@ $DIGCMD nil. TXT | grep 'SOA mismatch AXFR' >/dev/null && { } n=$((n+1)) -echo_i "check that we ask for and get a EDNS EXPIRE response ($n)" +echo_i "check that we ask for and got a EDNS EXPIRE response when transfering from a secondary ($n)" +tmp=0 +msg="zone edns-expire/IN: zone transfer finished: success, expire=1814[0-4][0-9][0-9]" +grep "$msg" ns7/named.run > /dev/null || tmp=1 +[ "$tmp" -ne 0 ] && echo_i "failed" +status=$((status+tmp)) + +n=$((n+1)) +echo_i "check that we ask for and get a EDNS EXPIRE response when refreshing ($n)" # force a refresh query $RNDCCMD 10.53.0.7 refresh edns-expire 2>&1 | sed 's/^/ns7 /' | cat_i sleep 10 From b05fce45bfd46c74950eed9ab804227c16f09daf Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Tue, 4 Jul 2023 13:26:01 +1000 Subject: [PATCH 8/9] Add CHANGES note for [GL #4170] --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index 15210d2985..eb54983f9b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +6233. [func] Extend client side support for the EDNS EXPIRE option + to IXFR and AXFR query types. [GL #4170] + 6232. [bug] Following the introduction of krb5-subdomain-self-rhs and ms-subdomain-self-rhs update rules, removal of nonexistent PTR and SRV records via UPDATE could fail. From ac682009a831a0d80c9f3978216ef53f9e11b40d Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Tue, 4 Jul 2023 13:46:02 +1000 Subject: [PATCH 9/9] Add release note for [GL #4170] --- doc/notes/notes-current.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index 6f293436b9..c425485832 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -35,6 +35,10 @@ Feature Changes - Make :iscman:`nsupdate` honor the ``-v`` option for SOA queries, that is send the request over TCP, only if the server is specified. :gl:`#1181` +- Extend client side support for the EDNS EXPIRE option to IXFR and + AXFR query types. ``named`` will now be making EDNS queries AXFR + and IXFR queries with EDNS options present. :gl:`#4170` + Bug Fixes ~~~~~~~~~