Merge branch '4170-extend-expire-opt-support-into-xfrin-c' into 'main'

Resolve "Extend EXPIRE opt support into xfrin.c"

Closes #4170

See merge request isc-projects/bind9!8064
This commit is contained in:
Mark Andrews
2023-08-31 08:03:32 +00:00
10 changed files with 220 additions and 28 deletions
+3
View File
@@ -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.
@@ -1,3 +1,3 @@
messages=1
records=5
bytes=204
bytes=223
@@ -0,0 +1,3 @@
messages=1
records=5
bytes=215
+5 -5
View File
@@ -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))
+1 -1
View File
@@ -1,3 +1,3 @@
messages=16
records=10003
bytes=218227
bytes=218403
+10 -2
View File
@@ -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
@@ -541,7 +549,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
)
+4
View File
@@ -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
~~~~~~~~~
+1 -1
View File
@@ -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 *);
+112 -4
View File
@@ -30,6 +30,7 @@
#include <dns/journal.h>
#include <dns/log.h>
#include <dns/message.h>
#include <dns/peer.h>
#include <dns/rdataclass.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
@@ -141,7 +142,8 @@ struct dns_xfrin {
xfrin_state_t state;
uint32_t end_serial;
bool is_ixfr;
uint32_t expireopt;
bool edns, is_ixfr, expireoptset;
unsigned int nmsg; /*%< Number of messages recvd */
unsigned int nrecs; /*%< Number of records recvd */
@@ -833,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, result);
(xfr->done)(xfr->zone,
xfr->expireoptset ? &xfr->expireopt : NULL,
result);
xfr->done = NULL;
}
xfr->shutdown_result = result;
@@ -860,6 +864,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,
};
@@ -1147,6 +1152,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 +1197,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 = xfr->edns;
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 +1239,24 @@ xfrin_send_request(dns_xfrin_t *xfr) {
&xfr->ixfr.request_serial));
}
if (edns && 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;
@@ -1267,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;
@@ -1324,7 +1415,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 &&
@@ -1353,6 +1454,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");
@@ -1518,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;
@@ -1545,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, 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);
}
+80 -14
View File
@@ -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);
@@ -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) {
@@ -17079,20 +17116,23 @@ zone_detachdb(dns_zone_t *zone) {
}
static void
zone_xfrdone(dns_zone_t *zone, isc_result_t result) {
isc_time_t now;
zone_xfrdone(dns_zone_t *zone, uint32_t *expireopt, isc_result_t result) {
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;
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)
@@ -17133,6 +17173,8 @@ again:
goto same_primary;
}
oldexpire = zone->expire;
/*
* Update the zone structure's data from the actual
* SOA received.
@@ -17185,19 +17227,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) {
@@ -17224,15 +17278,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) ||
@@ -17715,7 +17781,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) {