Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
26 July 2026: Jared
- Fall back to AXFR when an IXFR response is SERVFAIL (if
allow-axfr-fallback is enabled), matching the existing NOTIMPL
and FORMERR fallback. This recovers automatically when a primary
cannot serve IXFR due to inconsistent history (GitHub #479).

16 July 2026: Willem
- Merge #500: Support for the "oots" SVCB Service Parameter Key

Expand Down
3 changes: 2 additions & 1 deletion doc/README
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,8 @@ are made over TCP. If you want the IXFR request be transmitted over UDP, use
request-xfr: 168.192.199.2 NOKEY

By default, a slave will fallback to AXFR requests if the master told us it does
not support IXFR. You can configure the slave not to do AXFR fallback with:
not support IXFR, or if an IXFR fails with SERVFAIL. You can configure the slave
not to do AXFR fallback with:

allow-axfr-fallback: "no"

Expand Down
4 changes: 2 additions & 2 deletions doc/manual/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ are made over TCP. If you want the IXFR request be transmitted over UDP, use
request-xfr: 168.192.199.2 NOKEY

By default, a secondary will fallback to AXFR requests if the primary told us it
does not support IXFR. You can configure the secondary not to do AXFR fallback
with:
does not support IXFR, or if an IXFR fails with SERVFAIL. You can configure the
secondary not to do AXFR fallback with:

.. code-block:: text

Expand Down
3 changes: 2 additions & 1 deletion nsd.conf.5.in
Original file line number Diff line number Diff line change
Expand Up @@ -1003,7 +1003,8 @@ not be sent. Support for TLS 1.3 is required for XFR-over-TLS.
.TP
.B allow\-axfr\-fallback:\fR <yes or no>
This option should be accompanied by request\-xfr. It (dis)allows NSD (as secondary)
to fallback to AXFR if the primary name server does not support IXFR. Default is yes.
to fallback to AXFR if the primary name server does not support IXFR (NOTIMPL or
FORMERR) or cannot serve the requested IXFR (SERVFAIL). Default is yes.
.TP
.B size\-limit\-xfr:\fR <number>
This option should be accompanied by request\-xfr. It specifies XFR temporary file size limit. It can be used to stop very large zone retrieval, that could otherwise use up a lot of memory and disk space.
Expand Down
3 changes: 2 additions & 1 deletion nsd.conf.sample.in
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,8 @@ remote-control:
#request-xfr: 192.0.2.2 the_tsig_key_name the_tls_auth_name
# Attention: You cannot use UDP and AXFR together. AXFR is always over
# TCP. If you use UDP, we highly recommend you to deploy TSIG.
# Allow AXFR fallback if the primary does not support IXFR. Default
# Allow AXFR fallback if the primary does not support IXFR
# (NOTIMPL/FORMERR) or cannot serve the IXFR (SERVFAIL). Default
# is yes.
#allow-axfr-fallback: yes
# set local interface for sending zone transfer requests.
Expand Down
12 changes: 12 additions & 0 deletions tpkg/axfr_fallback_servfail.tdir/axfr_fallback_servfail.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
server:
logfile: "nsd.log"
xfrdfile: xfrd.state
zonesdir: ""
zonelistfile: "zone.list"
interface: 127.0.0.1

zone:
name: example.net
zonefile: axfr_fallback_servfail.zone
request-xfr: UDP 127.0.0.1@RANDOM NOKEY
allow-notify: 127.0.0.1@RANDOM NOKEY
32 changes: 32 additions & 0 deletions tpkg/axfr_fallback_servfail.tdir/axfr_fallback_servfail.datafile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
$ORIGIN example.net.
$TTL 7200

ENTRY_BEGIN
MATCH opcode qtype qname
REPLY QUERY
REPLY SERVFAIL
REPLY AA AD
ADJUST copy_id ; copy_id copies the ID from the query to the answer.
SECTION QUESTION
example.net. IN IXFR
SECTION ANSWER
SECTION AUTHORITY
SECTION ADDITIONAL
ENTRY_END

ENTRY_BEGIN
MATCH opcode qtype qname
MATCH TCP
REPLY QUERY
REPLY NOERROR
REPLY AA AD
ADJUST copy_id ; copy_id copies the ID from the query to the answer.
SECTION QUESTION
example.net. IN AXFR
SECTION ANSWER
example.net. IN SOA nibbler.example.net. leela.example.net. 2 2000 3000 4000 5000
www.example.net. IN A 127.0.0.2
example.net. IN SOA nibbler.example.net. leela.example.net. 2 2000 3000 4000 5000
SECTION AUTHORITY
SECTION ADDITIONAL
ENTRY_END
17 changes: 17 additions & 0 deletions tpkg/axfr_fallback_servfail.tdir/axfr_fallback_servfail.dsc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
BaseName: axfr_fallback_servfail
Version: 1.0
Description: Test AXFR fallback when IXFR gets SERVFAIL (GitHub #479)
CreationDate: Sun Jul 26 19:48:00 EDT 2026
Maintainer: Jared Mauch
Category: running
Component:
CmdDepends:
Depends: 0000_nsd-compile.tpkg
Help: axfr_fallback_servfail.help
Pre: axfr_fallback_servfail.pre
Post: axfr_fallback_servfail.post
Test: axfr_fallback_servfail.test
AuxFiles: axfr_fallback_servfail.known_good, axfr_fallback_servfail.conf, axfr_fallback_servfail.datafile,
axfr_fallback_servfail.zone
Passed:
Failure:
3 changes: 3 additions & 0 deletions tpkg/axfr_fallback_servfail.tdir/axfr_fallback_servfail.help
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This test makes use of ldns-testns, nsd and dig.
It takes no arguments.
It tests whether NSD falls back to AXFR if IXFR fails with SERVFAIL.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
www.example.net. 7200 IN A 127.0.0.2
21 changes: 21 additions & 0 deletions tpkg/axfr_fallback_servfail.tdir/axfr_fallback_servfail.post
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# #-- axfr_fallback_servfail.post --#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# source the test var file when it's there
[ -f .tpkg.var.test ] && source .tpkg.var.test
#
# do your teardown here
. ../common.sh
kill_pid $TESTNS_PID

rm -f xfrd.state
rm -f axfr_fallback_servfail.current

# do your teardown here
if [ -z $TPKG_NSD_PID ]; then
exit 0
fi

# kill NSD
NSD_PID=`cat $TPKG_NSD_PID`
kill_pid $NSD_PID
23 changes: 23 additions & 0 deletions tpkg/axfr_fallback_servfail.tdir/axfr_fallback_servfail.pre
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# #-- axfr_fallback_servfail.pre--#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# use .tpkg.var.test for in test variable passing
[ -f .tpkg.var.test ] && source .tpkg.var.test
. ../common.sh

# start NSD
get_random_port 1
LDNS_PORT=$RND_PORT
echo port: $LDNS_PORT

# start ldns-testns, be extra verbose
ldns-testns -vvv -p $LDNS_PORT axfr_fallback_servfail.datafile >testns.log 2>&1 &
echo "export TESTNS_PID=$!" >> .tpkg.var.test
echo "export LDNS_PORT=$LDNS_PORT" >> .tpkg.var.test
wait_ldns_testns_up testns.log

# replace RANDOM with $LDNS_PORT and put it in nsd_axfr.conf
cat axfr_fallback_servfail.conf | sed "s/RANDOM/$LDNS_PORT/g" > nsd_axfr.conf
if [[ $? -ne 0 ]]; then
exit 1
fi
43 changes: 43 additions & 0 deletions tpkg/axfr_fallback_servfail.tdir/axfr_fallback_servfail.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# #-- axfr_fallback_servfail.test --#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# use .tpkg.var.test for in test variable passing
[ -f .tpkg.var.test ] && source .tpkg.var.test
. ../common.sh

# start NSD
get_random_port 1
TPKG_PORT=$RND_PORT

PRE="../.."
TPKG_NSD_PID="nsd.pid.$$"
TPKG_NSD="$PRE/nsd"
# share the vars
echo "export TPKG_PORT=$TPKG_PORT" >> .tpkg.var.test
echo "export TPKG_NSD_PID=$TPKG_NSD_PID" >> .tpkg.var.test

$TPKG_NSD -c nsd_axfr.conf -u "" -p $TPKG_PORT -P $TPKG_NSD_PID -V 2 -l nsd.log
wait_nsd_up nsd.log
sleep 3

# now query for the update I sent and compare that with a known good one
echo port: $TPKG_PORT
dig @127.0.0.1 -p $TPKG_PORT a www.example.net
dig @127.0.0.1 -p $TPKG_PORT a www.example.net |grep "^www.example.net" > axfr_fallback_servfail.current

echo "CURRENT:"
cat axfr_fallback_servfail.current

echo "KNOWN GOOD:"
cat axfr_fallback_servfail.known_good

diff -u axfr_fallback_servfail.current axfr_fallback_servfail.known_good
if [[ $? -ne 0 ]]; then
echo testns.log
cat testns.log
echo nsd.log
cat nsd.log
exit 1
else
exit 0
fi
4 changes: 4 additions & 0 deletions tpkg/axfr_fallback_servfail.tdir/axfr_fallback_servfail.zone
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
; nothing fancy here
$TTL 3600
$ORIGIN example.net.
example.net. IN SOA nibbler.example.net. leela.example.net. 1 2000 3000 4000 5000
3 changes: 2 additions & 1 deletion tpkg/checkconf.tdir/checkconf.nsd11.conf
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,8 @@ pattern:
#request-xfr: 192.0.2.2 the_tsig_key_name the_tls_auth_name
# Attention: You cannot use UDP and AXFR together. AXFR is always over
# TCP. If you use UDP, we highly recommend you to deploy TSIG.
# Allow AXFR fallback if the master does not support IXFR. Default
# Allow AXFR fallback if the master does not support IXFR, or if
# IXFR fails with SERVFAIL. Default
# is yes.
#allow-axfr-fallback: yes
# set local interface for sending zone transfer requests.
Expand Down
7 changes: 6 additions & 1 deletion xfrd.c
Original file line number Diff line number Diff line change
Expand Up @@ -2296,7 +2296,12 @@ xfrd_parse_received_xfr_packet(xfrd_zone_type* zone, buffer_type* packet,
zone->master->ip_address_spec);
}
if (RCODE(packet) == RCODE_IMPL ||
RCODE(packet) == RCODE_FORMAT) {
RCODE(packet) == RCODE_FORMAT ||
RCODE(packet) == RCODE_SERVFAIL) {
/* NOTIMPL/FORMERR: primary does not support IXFR.
* SERVFAIL: primary cannot serve this IXFR (for
* example inconsistent IXFR history). Fall back to
* AXFR when allow-axfr-fallback is enabled. */
return xfrd_packet_notimpl;
}
if (RCODE(packet) != RCODE_NOTAUTH) {
Expand Down
2 changes: 1 addition & 1 deletion xfrd.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ enum xfrd_packet_result {
xfrd_packet_bad, /* drop the packet/connection */
xfrd_packet_drop, /* drop the connection, but not report bad */
xfrd_packet_more, /* more packets to follow on tcp */
xfrd_packet_notimpl, /* server responded with NOTIMPL or FORMATERR */
xfrd_packet_notimpl, /* NOTIMPL, FORMERR, or SERVFAIL (IXFR->AXFR) */
xfrd_packet_tcp, /* try tcp connection */
xfrd_packet_transfer, /* server responded with transfer*/
xfrd_packet_newlease /* no changes, soa OK */
Expand Down
Loading