diff --git a/bin/include/defaultconfig.h b/bin/include/defaultconfig.h index 8e24757260..9ecd96588e 100644 --- a/bin/include/defaultconfig.h +++ b/bin/include/defaultconfig.h @@ -215,6 +215,7 @@ options {\n\ min-transfer-rate-in 10240 5;\n\ multi-master no;\n\ notify yes;\n\ + notify-cds no;\n\ notify-defer 0;\n\ notify-delay 5;\n\ notify-to-soa no;\n\ diff --git a/bin/named/server.c b/bin/named/server.c index 1cabf96e72..7e43ef6c11 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -1845,7 +1845,8 @@ dns64_reverse(dns_view_t *view, isc_mem_t *mctx, isc_netaddr_t *na, dns_zone_setqueryonacl(zone, view->queryonacl); } dns_zone_setcheckdstype(zone, dns_checkdstype_no); - dns_zone_setnotifytype(zone, dns_notifytype_no); + dns_zone_setnotifytype(zone, dns_rdatatype_soa, dns_notifytype_no); + dns_zone_setnotifytype(zone, dns_rdatatype_cds, dns_notifytype_no); dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, true); setquerystats(zone, mctx, dns_zonestat_none); CHECK(dns_view_addzone(view, zone)); @@ -3220,7 +3221,8 @@ create_empty_zone(dns_zone_t *pzone, dns_name_t *name, dns_view_t *view, dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, true); dns_zone_setoption(zone, DNS_ZONEOPT_ZONEVERSION, false); dns_zone_setcheckdstype(zone, dns_checkdstype_no); - dns_zone_setnotifytype(zone, dns_notifytype_no); + dns_zone_setnotifytype(zone, dns_rdatatype_soa, dns_notifytype_no); + dns_zone_setnotifytype(zone, dns_rdatatype_cds, dns_notifytype_no); dns_zone_setautomatic(zone, true); if (view->queryacl != NULL) { dns_zone_setqueryacl(zone, view->queryacl); @@ -3319,7 +3321,10 @@ create_ipv4only_zone(dns_zone_t *pzone, dns_view_t *view, dns_zone_setstats(zone, named_g_server->zonestats); dns_zone_setdbtype(zone, dbtypec, dbtype); dns_zone_setcheckdstype(zone, dns_checkdstype_no); - dns_zone_setnotifytype(zone, dns_notifytype_no); + dns_zone_setnotifytype(zone, dns_rdatatype_soa, + dns_notifytype_no); + dns_zone_setnotifytype(zone, dns_rdatatype_cds, + dns_notifytype_no); dns_zone_setautomatic(zone, true); dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, true); } else { @@ -6394,7 +6399,8 @@ add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx) { dns_acl_detach(&none); dns_zone_setcheckdstype(zone, dns_checkdstype_no); - dns_zone_setnotifytype(zone, dns_notifytype_no); + dns_zone_setnotifytype(zone, dns_rdatatype_soa, dns_notifytype_no); + dns_zone_setnotifytype(zone, dns_rdatatype_cds, dns_notifytype_no); dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, true); dns_zone_setjournalsize(zone, 0); diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index 1d9ab722cf..1af25b8adc 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -1209,9 +1209,20 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, notifytype = process_notifytype(notifytype, ztype, zname, nodefault); if (raw != NULL) { - dns_zone_setnotifytype(raw, dns_notifytype_no); + dns_zone_setnotifytype(raw, dns_rdatatype_soa, + dns_notifytype_no); } - dns_zone_setnotifytype(zone, notifytype); + dns_zone_setnotifytype(zone, dns_rdatatype_soa, notifytype); + + obj = NULL; + result = named_config_get(maps, "notify-cds", &obj); + INSIST(result == ISC_R_SUCCESS && obj != NULL); + if (raw != NULL) { + dns_zone_setnotifytype(raw, dns_rdatatype_cds, + dns_notifytype_no); + } + dns_zone_setnotifytype(zone, dns_rdatatype_cds, + cfg_obj_asboolean(obj)); obj = NULL; result = named_config_get(maps, "also-notify", &obj); @@ -1473,7 +1484,10 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, } } } else if (ztype == dns_zone_redirect) { - dns_zone_setnotifytype(zone, dns_notifytype_no); + dns_zone_setnotifytype(zone, dns_rdatatype_soa, + dns_notifytype_no); + dns_zone_setnotifytype(zone, dns_rdatatype_cds, + dns_notifytype_no); obj = NULL; result = named_config_get(maps, "max-journal-size", &obj); diff --git a/bin/tests/system/checkconf/bad-notifycdstype.conf b/bin/tests/system/checkconf/bad-notifycdstype.conf new file mode 100644 index 0000000000..6dc6ff9678 --- /dev/null +++ b/bin/tests/system/checkconf/bad-notifycdstype.conf @@ -0,0 +1,22 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * Bad notify-cds type + */ + +zone dummy { + type primary; + file "xxxx"; + notify-cds explicit; +}; diff --git a/bin/tests/system/checkconf/good.conf.j2 b/bin/tests/system/checkconf/good.conf.j2 index fc33cb6503..2f98e67bf9 100644 --- a/bin/tests/system/checkconf/good.conf.j2 +++ b/bin/tests/system/checkconf/good.conf.j2 @@ -194,6 +194,7 @@ view "fourth" { 1.2.3.5; }; dnssec-policy "test"; + notify-cds no; parental-source 10.10.10.10; }; zone "dnssec-default" { @@ -203,6 +204,7 @@ view "fourth" { "parents"; }; dnssec-policy "default"; + notify-cds yes; }; zone "dnssec-inherit" { type primary; diff --git a/bin/tests/system/multisigner/ns1/named.conf.j2 b/bin/tests/system/multisigner/ns1/named.conf.j2 new file mode 100644 index 0000000000..86cd34ca3a --- /dev/null +++ b/bin/tests/system/multisigner/ns1/named.conf.j2 @@ -0,0 +1,33 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +// NS1 + +options { + query-source address 10.53.0.1; + notify-source 10.53.0.1; + transfer-source 10.53.0.1; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.1; }; + listen-on-v6 { none; }; + recursion no; + notify yes; +}; + +zone "." { + type primary; + file "root.db.signed"; +}; + +include "trusted.conf"; diff --git a/bin/tests/system/multisigner/ns1/root.db.in b/bin/tests/system/multisigner/ns1/root.db.in new file mode 100644 index 0000000000..e96ade64a8 --- /dev/null +++ b/bin/tests/system/multisigner/ns1/root.db.in @@ -0,0 +1,30 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +. IN SOA gson.nominum.com. a.root.servers.nil. ( + 2000042100 ; serial + 600 ; refresh + 600 ; retry + 1200 ; expire + 600 ; minimum + ) +. NS a.root-servers.nil. +a.root-servers.nil. A 10.53.0.1 + +multisigner. NS ns2.multisigner. +ns2.multisigner. A 10.53.0.2 + +bad-dsync. NS ns2.bad-dsync. +ns2.bad-dsync. A 10.53.0.2 + +secondary. NS ns2.secondary. +ns2.secondary. A 10.53.0.2 diff --git a/bin/tests/system/multisigner/ns1/setup.sh b/bin/tests/system/multisigner/ns1/setup.sh new file mode 100644 index 0000000000..b6cf3028d1 --- /dev/null +++ b/bin/tests/system/multisigner/ns1/setup.sh @@ -0,0 +1,41 @@ +#!/bin/sh -e + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +# shellcheck source=conf.sh +. ../../conf.sh + +set -e + +zone=. +infile=root.db.in +zonefile=root.db + +echo_i "ns1/setup.sh" + +for tld in multisigner bad-dsync secondary; do + cp "../ns2/dsset-${tld}." . +done + +KSK=$($KEYGEN -q -fk -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS $zone) +ZSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS $zone) + +cat $infile $KSK.key $ZSK.key >$zonefile + +$SIGNER -g -o $zone $zonefile >/dev/null 2>&1 + +# Configure the resolving server with a static key. +keyfile_to_static_ds "$KSK" >trusted.conf +cp trusted.conf ../ns2/trusted.conf +cp trusted.conf ../ns3/trusted.conf +cp trusted.conf ../ns4/trusted.conf +cp trusted.conf ../ns5/trusted.conf diff --git a/bin/tests/system/multisigner/ns2/bad-dsync.db.in.j2 b/bin/tests/system/multisigner/ns2/bad-dsync.db.in.j2 new file mode 100644 index 0000000000..b84eb5afec --- /dev/null +++ b/bin/tests/system/multisigner/ns2/bad-dsync.db.in.j2 @@ -0,0 +1,35 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +$ORIGIN bad-dsync. + +bad-dsync. IN SOA mname1. . ( + 1 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + + NS ns2 +ns2 A 10.53.0.2 + +scanner A 10.53.0.2 + +model2 NS ns3 + NS ns4 + +ns3.model2 A 10.53.0.3 +ns4.model2 A 10.53.0.4 + +*._dsync DSYNC CDS NOTIFY @PORT@ scanner1 +*._dsync DSYNC CDS NOTIFY @PORT@ scanner2 diff --git a/bin/tests/system/multisigner/ns2/multisigner.db.in.j2 b/bin/tests/system/multisigner/ns2/multisigner.db.in.j2 new file mode 100644 index 0000000000..db9c07981c --- /dev/null +++ b/bin/tests/system/multisigner/ns2/multisigner.db.in.j2 @@ -0,0 +1,34 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +$ORIGIN multisigner. + +multisigner. IN SOA mname1. . ( + 1 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + + NS ns2 +ns2 A 10.53.0.2 + +scanner A 10.53.0.2 + +model2 NS ns3 + NS ns4 + +ns3.model2 A 10.53.0.3 +ns4.model2 A 10.53.0.4 + +*._dsync DSYNC CDS NOTIFY @PORT@ scanner diff --git a/bin/tests/system/multisigner/ns2/named.conf.j2 b/bin/tests/system/multisigner/ns2/named.conf.j2 new file mode 100644 index 0000000000..f2ef9302af --- /dev/null +++ b/bin/tests/system/multisigner/ns2/named.conf.j2 @@ -0,0 +1,58 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +// NS2 + +options { + query-source address 10.53.0.2; + notify-source 10.53.0.2; + transfer-source 10.53.0.2; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.2; }; + listen-on-v6 { none; }; + allow-transfer { any; }; + allow-notify { 10.53.0.3; 10.53.0.4; }; + dnssec-validation no; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.2 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "multisigner" { + type primary; + file "multisigner.db.signed"; +}; + +zone "bad-dsync" { + type primary; + file "bad-dsync.db.signed"; +}; + +zone "secondary" { + type primary; + file "secondary.db.signed"; +}; + +zone "." { + type hint; + file "../../_common/root.hint"; +}; + +include "trusted.conf"; diff --git a/bin/tests/system/multisigner/ns2/secondary.db.in.j2 b/bin/tests/system/multisigner/ns2/secondary.db.in.j2 new file mode 100644 index 0000000000..d108187ff2 --- /dev/null +++ b/bin/tests/system/multisigner/ns2/secondary.db.in.j2 @@ -0,0 +1,36 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +$ORIGIN secondary. + +secondary. IN SOA mname1. . ( + 1 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + + NS ns2 +ns2 A 10.53.0.2 + +scanner A 10.53.0.2 + +model2 NS ns3 + NS ns4 + +ns3.model2 A 10.53.0.3 +ns4.model2 A 10.53.0.4 + +model2._dsync DSYNC CSYNC NOTIFY @PORT@ scanner ; ignored, unknown rrtype +model2._dsync DSYNC DSYNC NOTIFY @PORT@ scanner ; ignored, bad rrtype +model2._dsync DSYNC CDS NOTIFY @PORT@ scanner diff --git a/bin/tests/system/multisigner/ns2/setup.sh b/bin/tests/system/multisigner/ns2/setup.sh new file mode 100644 index 0000000000..4fd349125d --- /dev/null +++ b/bin/tests/system/multisigner/ns2/setup.sh @@ -0,0 +1,39 @@ +#!/bin/sh -e + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +# shellcheck source=conf.sh +. ../../conf.sh + +echo_i "ns2/setup.sh" + +setup() { + zone="$1" + echo_i "setting up zone: $zone" + infile="${zone}.db.in" + zonefile="${zone}.db" + + cp ../ns3/dsset-ns3-model2.$zone. . + cp ../ns4/dsset-ns4-model2.$zone. . + + KSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -L 3600 -f KSK $zone) + ZSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -L 3600 $zone) + $DSFROMKEY $KSK.key >dsset-ns2-${zone}. + + cat $infile $KSK.key $ZSK.key >$zonefile + $SIGNER -g -o $zone $zonefile + # >/dev/null 2>&1 +} + +setup "multisigner" +setup "bad-dsync" +setup "secondary" diff --git a/bin/tests/system/multisigner/ns3/model2.bad-dsync.db b/bin/tests/system/multisigner/ns3/model2.bad-dsync.db new file mode 100644 index 0000000000..5850e016b9 --- /dev/null +++ b/bin/tests/system/multisigner/ns3/model2.bad-dsync.db @@ -0,0 +1,26 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +@ IN SOA mname1. . ( + 1 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + + NS ns3 +ns3 A 10.53.0.3 + +a A 10.0.0.1 +b A 10.0.0.2 +c A 10.0.0.3 diff --git a/bin/tests/system/multisigner/ns3/named.conf.j2 b/bin/tests/system/multisigner/ns3/named.conf.j2 index 7cf25bbc44..b56f01bf27 100644 --- a/bin/tests/system/multisigner/ns3/named.conf.j2 +++ b/bin/tests/system/multisigner/ns3/named.conf.j2 @@ -24,9 +24,9 @@ options { listen-on { 10.53.0.3; }; listen-on-v6 { none; }; allow-transfer { any; }; - recursion no; key-directory "."; - dnssec-validation no; + dnssec-validation yes; + notify-cds yes; }; key rndc_key { @@ -46,10 +46,26 @@ zone "model2.multisigner." { inline-signing no; }; +zone "model2.bad-dsync." { + type primary; + allow-update { any; }; + file "model2.bad-dsync.db"; + dnssec-policy model2; + inline-signing no; +}; + zone "model2.secondary." { type secondary; primaries { 10.53.0.5; }; file "model2.secondary.db"; dnssec-policy model2; inline-signing yes; + notify no; }; + +zone "." { + type hint; + file "../../_common/root.hint"; +}; + +include "trusted.conf"; diff --git a/bin/tests/system/multisigner/ns3/setup.sh b/bin/tests/system/multisigner/ns3/setup.sh index 80327a1300..50f26480f7 100644 --- a/bin/tests/system/multisigner/ns3/setup.sh +++ b/bin/tests/system/multisigner/ns3/setup.sh @@ -23,20 +23,29 @@ zsktimes="-P now -A now" zone="model2.multisigner" echo_i "setting up zone: $zone" zonefile="${zone}.db" - -KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -f KSK -L 3600 $ksktimes $zone 2>keygen.out.$zone.1) -ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsktimes $zone 2>keygen.out.$zone.2) +KSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -f KSK -L 3600 $ksktimes $zone) +ZSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -L 3600 $zsktimes $zone) $SETTIME -s -g $O -k $O now -r $O now -d $O now "$KSK" >settime.out.$zone.1 2>&1 $SETTIME -s -g $O -k $O now -z $O now "$ZSK" >settime.out.$zone.2 2>&1 +$DSFROMKEY $KSK.key >dsset-ns3-${zone}. + +zone="model2.bad-dsync" +echo_i "setting up zone: $zone" +zonefile="${zone}.db" +KSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -f KSK -L 3600 $ksktimes $zone) +ZSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -L 3600 $zsktimes $zone) +$SETTIME -s -g $O -k $O now -r $O now -d $O now "$KSK" >settime.out.$zone.1 2>&1 +$SETTIME -s -g $O -k $O now -z $O now "$ZSK" >settime.out.$zone.2 2>&1 +$DSFROMKEY $KSK.key >dsset-ns3-${zone}. zone="model2.secondary" echo_i "setting up zone: $zone" zonefile="${zone}.db" cp "../ns5/${zonefile}.in" "$zonefile" - -KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -f KSK -L 3600 $ksktimes $zone 2>keygen.out.$zone.1) -ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsktimes $zone 2>keygen.out.$zone.2) +KSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -f KSK -L 3600 $ksktimes $zone) +ZSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -L 3600 $zsktimes $zone) $SETTIME -s -g $O -k $O now -r $O now -d $O now "$KSK" >settime.out.$zone.1 2>&1 $SETTIME -s -g $O -k $O now -z $O now "$ZSK" >settime.out.$zone.2 2>&1 +$DSFROMKEY $KSK.key >dsset-ns3-${zone}. # ZSK will be added to the other provider with nsupdate. cat "${ZSK}.key" | grep -v ";.*" >"${zone}.zsk" diff --git a/bin/tests/system/multisigner/ns4/model2.bad-dsync.db b/bin/tests/system/multisigner/ns4/model2.bad-dsync.db new file mode 100644 index 0000000000..86a1708b45 --- /dev/null +++ b/bin/tests/system/multisigner/ns4/model2.bad-dsync.db @@ -0,0 +1,26 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +@ IN SOA mname1. . ( + 1 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + + NS ns4 +ns4 A 10.53.0.4 + +a A 10.0.0.1 +b A 10.0.0.2 +c A 10.0.0.3 diff --git a/bin/tests/system/multisigner/ns4/named.conf.j2 b/bin/tests/system/multisigner/ns4/named.conf.j2 index 3f36f76cd5..3d551d04c4 100644 --- a/bin/tests/system/multisigner/ns4/named.conf.j2 +++ b/bin/tests/system/multisigner/ns4/named.conf.j2 @@ -24,9 +24,9 @@ options { listen-on { 10.53.0.4; }; listen-on-v6 { none; }; allow-transfer { any; }; - recursion no; key-directory "."; - dnssec-validation no; + dnssec-validation yes; + notify-cds yes; }; key rndc_key { @@ -46,10 +46,26 @@ zone "model2.multisigner." { inline-signing yes; }; +zone "model2.bad-dsync." { + type primary; + allow-update { any; }; + file "model2.bad-dsync.db"; + dnssec-policy model2; + inline-signing yes; +}; + zone "model2.secondary." { type secondary; primaries { 10.53.0.5; }; file "model2.secondary.db"; dnssec-policy model2; inline-signing yes; + notify no; }; + +zone "." { + type hint; + file "../../_common/root.hint"; +}; + +include "trusted.conf"; diff --git a/bin/tests/system/multisigner/ns4/setup.sh b/bin/tests/system/multisigner/ns4/setup.sh index c58ddce495..bb13b8bede 100644 --- a/bin/tests/system/multisigner/ns4/setup.sh +++ b/bin/tests/system/multisigner/ns4/setup.sh @@ -23,20 +23,29 @@ zsktimes="-P now -A now" zone="model2.multisigner" echo_i "setting up zone: $zone" zonefile="${zone}.db" - -KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -f KSK -L 3600 $ksktimes $zone 2>keygen.out.$zone.1) -ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsktimes $zone 2>keygen.out.$zone.2) +KSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -f KSK -L 3600 $ksktimes $zone) +ZSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -L 3600 $zsktimes $zone) $SETTIME -s -g $O -k $O now -r $O now -d $O now "$KSK" >settime.out.$zone.1 2>&1 $SETTIME -s -g $O -k $O now -z $O now "$ZSK" >settime.out.$zone.2 2>&1 +$DSFROMKEY $KSK.key >dsset-ns4-${zone}. + +zone="model2.bad-dsync" +echo_i "setting up zone: $zone" +zonefile="${zone}.db" +KSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -f KSK -L 3600 $ksktimes $zone) +ZSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -L 3600 $zsktimes $zone) +$SETTIME -s -g $O -k $O now -r $O now -d $O now "$KSK" >settime.out.$zone.1 2>&1 +$SETTIME -s -g $O -k $O now -z $O now "$ZSK" >settime.out.$zone.2 2>&1 +$DSFROMKEY $KSK.key >dsset-ns4-${zone}. zone="model2.secondary" echo_i "setting up zone: $zone" zonefile="${zone}.db" cp "../ns5/${zonefile}.in" "$zonefile" - -KSK=$($KEYGEN -a $DEFAULT_ALGORITHM -f KSK -L 3600 $ksktimes $zone 2>keygen.out.$zone.1) -ZSK=$($KEYGEN -a $DEFAULT_ALGORITHM -L 3600 $zsktimes $zone 2>keygen.out.$zone.2) +KSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -f KSK -L 3600 $ksktimes $zone) +ZSK=$($KEYGEN -q -a $DEFAULT_ALGORITHM -L 3600 $zsktimes $zone) $SETTIME -s -g $O -k $O now -r $O now -d $O now "$KSK" >settime.out.$zone.1 2>&1 $SETTIME -s -g $O -k $O now -z $O now "$ZSK" >settime.out.$zone.2 2>&1 +$DSFROMKEY $KSK.key >dsset-ns4-${zone}. # ZSK will be added to the other provider with nsupdate. cat "${ZSK}.key" | grep -v ";.*" >"${zone}.zsk" diff --git a/bin/tests/system/multisigner/ns5/named.conf.j2 b/bin/tests/system/multisigner/ns5/named.conf.j2 index eeae1309c5..f6cf15e991 100644 --- a/bin/tests/system/multisigner/ns5/named.conf.j2 +++ b/bin/tests/system/multisigner/ns5/named.conf.j2 @@ -24,7 +24,6 @@ options { listen-on { 10.53.0.5; }; listen-on-v6 { none; }; allow-transfer { any; }; - recursion no; key-directory "."; dnssec-validation no; notify-delay 0; @@ -44,3 +43,10 @@ zone "model2.secondary." { allow-update { any; }; file "model2.secondary.db"; }; + +zone "." { + type hint; + file "../../_common/root.hint"; +}; + +include "trusted.conf"; diff --git a/bin/tests/system/multisigner/setup.sh b/bin/tests/system/multisigner/setup.sh index af72460917..12d7073d26 100644 --- a/bin/tests/system/multisigner/setup.sh +++ b/bin/tests/system/multisigner/setup.sh @@ -16,6 +16,7 @@ set -e +# multi-signers ( cd ns3 $SHELL setup.sh @@ -28,3 +29,13 @@ set -e cd ns5 $SHELL setup.sh ) +# tld +( + cd ns2 + $SHELL setup.sh +) +# root +( + cd ns1 + $SHELL setup.sh +) diff --git a/bin/tests/system/multisigner/tests_multisigner.py b/bin/tests/system/multisigner/tests_multisigner.py index 6fdb177bd9..15726f1eb1 100644 --- a/bin/tests/system/multisigner/tests_multisigner.py +++ b/bin/tests/system/multisigner/tests_multisigner.py @@ -33,16 +33,20 @@ pytestmark = pytest.mark.extra_artifacts( "secondary.cds.ns*", "unused.*", "verify.out.*", - "ns*/K*", - "ns*/db-*", - "ns*/keygen.out.*", + "ns*/*.db", + "ns*/*.db.in", "ns*/*.jbk", "ns*/*.jnl", - "ns*/*.zsk", - "ns*/*.signed", "ns*/*.journal.out.*", + "ns*/*.signed", + "ns*/*.zsk", + "ns*/db-*", + "ns*/dsset-*", + "ns*/K*", + "ns*/keygen.out.*", + "ns*/managed-keys.bind*", "ns*/settime.out.*", - "ns*/model2.secondary.db", + "ns*/trusted.conf", ] ) @@ -503,7 +507,7 @@ def check_remove_cds( check_dnssec(server, zone, keys, expected) -def test_multisigner(ns3, ns4): +def test_multisigner(ns2, ns3, ns4): zone = "model2.multisigner" keyprops = [ f"ksk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent", @@ -515,6 +519,23 @@ def test_multisigner(ns3, ns4): isctest.kasp.wait_keymgr_done(ns3, zone) isctest.kasp.wait_keymgr_done(ns4, zone) + with ns3.watch_log_from_start() as watcher: + watcher.wait_for_line( + f"zone {zone}/IN: dsyncfetch: send NOTIFY(CDS) query to scanner.multisigner" + ) + + with ns4.watch_log_from_start() as watcher: + watcher.wait_for_line( + f"zone {zone}/IN (signed): dsyncfetch: send NOTIFY(CDS) query to scanner.multisigner" + ) + + with ns2.watch_log_from_start() as watcher: + # Receiving NOTIFY(CDS) has not been implemented yet. Until + # then, notifies for child zones towards the parent result in + # not authoritative (unless child and parent are served by the + # same name server). + watcher.wait_for_line(f"received notify for zone '{zone}': NOTAUTH") + keys3 = isctest.kasp.keydir_to_keylist(zone, ns3.identifier) ksks3 = [k for k in keys3 if k.is_ksk()] zsks3 = [k for k in keys3 if not k.is_ksk()] @@ -574,7 +595,26 @@ def test_multisigner(ns3, ns4): check_no_dnssec_in_journal(ns4, zone) -def test_multisigner_secondary(ns3, ns4, ns5): +def test_multisigner_bad_dsync(ns3, ns4): + zone = "model2.bad-dsync" + + # First make sure the zone is properly signed. + isctest.log.info(f"basic DNSSEC tests for {zone}") + isctest.kasp.wait_keymgr_done(ns3, zone) + isctest.kasp.wait_keymgr_done(ns4, zone) + + with ns3.watch_log_from_start() as watcher: + watcher.wait_for_line( + f"zone {zone}/IN: dsyncfetch: multiple DSYNC records matching NOTIFY scheme and CDS RRtype, dropping response" + ) + + with ns4.watch_log_from_start() as watcher: + watcher.wait_for_line( + f"zone {zone}/IN (signed): dsyncfetch: multiple DSYNC records matching NOTIFY scheme and CDS RRtype, dropping response" + ) + + +def test_multisigner_secondary(ns2, ns3, ns4, ns5): zone = "model2.secondary" keyprops = [ f"ksk 0 {ALGORITHM} {SIZE} goal:omnipresent dnskey:omnipresent krrsig:omnipresent ds:omnipresent", @@ -586,6 +626,24 @@ def test_multisigner_secondary(ns3, ns4, ns5): isctest.kasp.wait_keymgr_done(ns3, zone) isctest.kasp.wait_keymgr_done(ns4, zone) + for server in [ns3, ns4]: + with server.watch_log_from_start() as watcher: + watcher.wait_for_line( + f"zone {zone}/IN (signed): dsyncfetch: send NOTIFY(CDS) query to scanner.secondary" + ) + + msg = f"zone {zone}/IN (signed): dsyncfetch: DSYNC RRtype CSYNC not supported, ignoring" + assert msg in server.log + msg = f"zone {zone}/IN (signed): dsyncfetch: DSYNC RRtype DSYNC not supported, ignoring" + assert msg in server.log + + with ns2.watch_log_from_start() as watcher: + # Receiving NOTIFY(CDS) has not been implemented yet. Until + # then, notifies for child zones towards the parent result in + # not authoritative (unless child and parent are served by the + # same name server). + watcher.wait_for_line(f"received notify for zone '{zone}': NOTAUTH") + keys3 = isctest.kasp.keydir_to_keylist(zone, ns3.identifier) ksks3 = [k for k in keys3 if k.is_ksk()] zsks3 = [k for k in keys3 if not k.is_ksk()] diff --git a/bin/tests/system/notify/tests.sh b/bin/tests/system/notify/tests.sh index 05a9aa8280..d83f5cbee0 100644 --- a/bin/tests/system/notify/tests.sh +++ b/bin/tests/system/notify/tests.sh @@ -45,7 +45,7 @@ for i in 1 2 3 4 5 6 7 8 9 10; do $DIG +tcp -p "${PORT}" example @10.53.0.3 soa >dig.out.ns3.test$n || ret=1 grep "status: NOERROR" dig.out.ns3.test$n >/dev/null || ret=1 grep "flags:.* aa[ ;]" dig.out.ns3.test$n >/dev/null || ret=1 - nr=$(grep -c 'x[0-9].*sending notify to' ns2/named.run) + nr=$(grep -c 'x[0-9].*sending notify(SOA) to' ns2/named.run) [ "$nr" -ge 22 ] || ret=1 [ $ret = 0 ] && break sleep 1 @@ -64,7 +64,7 @@ digcomp dig.out.ns2.test$n dig.out.ns3.test$n || ret=1 test_end test_start "checking startup notify rate limit" -awk '/x[0-9].*sending notify to/ { +awk '/x[0-9].*sending notify\(SOA\) to/ { split($1, ts, "T"); split(ts[2], a, ":"); this = a[1] * 3600 + a[2] * 60 + a[3]; @@ -101,7 +101,7 @@ test_end # See [GL#4689] test_start "checking server behaviour with invalid notify-source-v6 address" -grep "zone ./IN: sending notify to fd92:7065:b8e:fffe::a35:4#" ns1/named.run >/dev/null || ret=1 +grep "zone ./IN: sending notify(SOA) to fd92:7065:b8e:fffe::a35:4#" ns1/named.run >/dev/null || ret=1 grep "dns_request_create: failed address not available" ns1/named.run >/dev/null || ret=1 test_end @@ -121,15 +121,15 @@ test_end if $FEATURETEST --have-fips-dh; then test_start "checking notify over TLS successful" - grep "zone tls-x1/IN: notify to 10.53.0.2#${TLSPORT} successful" ns3/named.run >/dev/null || ret=1 - grep "zone tls-x2/IN: notify to 10.53.0.2#${EXTRAPORT1} successful" ns3/named.run >/dev/null || ret=1 - grep "zone tls-x3/IN: notify to 10.53.0.2#${EXTRAPORT1} successful" ns3/named.run >/dev/null || ret=1 - grep "zone tls-x5/IN: notify to 10.53.0.2#${EXTRAPORT3} successful" ns3/named.run >/dev/null || ret=1 + grep "zone tls-x1/IN: notify(SOA) to 10.53.0.2#${TLSPORT} successful" ns3/named.run >/dev/null || ret=1 + grep "zone tls-x2/IN: notify(SOA) to 10.53.0.2#${EXTRAPORT1} successful" ns3/named.run >/dev/null || ret=1 + grep "zone tls-x3/IN: notify(SOA) to 10.53.0.2#${EXTRAPORT1} successful" ns3/named.run >/dev/null || ret=1 + grep "zone tls-x5/IN: notify(SOA) to 10.53.0.2#${EXTRAPORT3} successful" ns3/named.run >/dev/null || ret=1 test_end test_start "checking notify over TLS failed" - grep "zone tls-x4/IN: notify to 10.53.0.2#${EXTRAPORT1} failed: TLS peer certificate verification failed" ns3/named.run >/dev/null || ret=1 - grep "zone tls-x6/IN: notify to 10.53.0.2#${EXTRAPORT4} failed: TLS peer certificate verification failed" ns3/named.run >/dev/null || ret=1 + grep "zone tls-x4/IN: notify(SOA) to 10.53.0.2#${EXTRAPORT1} failed: TLS peer certificate verification failed" ns3/named.run >/dev/null || ret=1 + grep "zone tls-x6/IN: notify(SOA) to 10.53.0.2#${EXTRAPORT4} failed: TLS peer certificate verification failed" ns3/named.run >/dev/null || ret=1 test_end fi @@ -218,8 +218,8 @@ for i in 1 2 3 4 5 6 7 8 9; do done grep "test string" "$fnb" >/dev/null || ret=1 grep "test string" "$fnc" >/dev/null || ret=1 -grep "sending notify to 10.53.0.5#[0-9]* : TSIG (b)" ns5/named.run >/dev/null || ret=1 -grep "sending notify to 10.53.0.5#[0-9]* : TSIG (c)" ns5/named.run >/dev/null || ret=1 +grep "sending notify(SOA) to 10.53.0.5#[0-9]* : TSIG (b)" ns5/named.run >/dev/null || ret=1 +grep "sending notify(SOA) to 10.53.0.5#[0-9]* : TSIG (c)" ns5/named.run >/dev/null || ret=1 test_end # notify messages were sent to unresponsive 10.53.0.6 during the tests diff --git a/bin/tests/system/nsec3/tests_nsec3_retransfer.py b/bin/tests/system/nsec3/tests_nsec3_retransfer.py index 96a23761b5..7ba4a44845 100644 --- a/bin/tests/system/nsec3/tests_nsec3_retransfer.py +++ b/bin/tests/system/nsec3/tests_nsec3_retransfer.py @@ -123,7 +123,7 @@ def test_nsec3_retransfer(servers, templates): with ns3.watch_log_from_here() as watcher: ns3.rndc(f"retransfer {zone}") # When sending notifies, the zone should be up to date. - watcher.wait_for_line(f"zone {zone}/IN (signed): sending notify to 10.53.0.4") + watcher.wait_for_line(f"zone_needdump: zone {zone}/IN (signed): enter") salt = perform_nsec3_tests(ns3, params) assert prevsalt == salt diff --git a/bin/tests/system/rollover-algo-csk/tests_rollover_algo_csk_initial.py b/bin/tests/system/rollover-algo-csk/tests_rollover_algo_csk_initial.py index 131933f40f..069583179a 100644 --- a/bin/tests/system/rollover-algo-csk/tests_rollover_algo_csk_initial.py +++ b/bin/tests/system/rollover-algo-csk/tests_rollover_algo_csk_initial.py @@ -80,3 +80,5 @@ def test_algoroll_csk_initial(tld, ns3): "nextev": TIMEDELTA["PT1H"], } isctest.kasp.check_rollover_step(ns3, config, policy, step) + + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log diff --git a/bin/tests/system/rollover-algo-csk/tests_rollover_algo_csk_reconfig.py b/bin/tests/system/rollover-algo-csk/tests_rollover_algo_csk_reconfig.py index 254f438dd8..5ef6131a85 100644 --- a/bin/tests/system/rollover-algo-csk/tests_rollover_algo_csk_reconfig.py +++ b/bin/tests/system/rollover-algo-csk/tests_rollover_algo_csk_reconfig.py @@ -138,6 +138,8 @@ def test_algoroll_csk_reconfig_step1(tld, ns3, alg, size): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -175,6 +177,8 @@ def test_algoroll_csk_reconfig_step2(tld, ns3, alg, size): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -244,6 +248,11 @@ def test_algoroll_csk_reconfig_step3(tld, ns3, alg, size): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + with ns3.watch_log_from_start() as watcher: + watcher.wait_for_line( + f"zone {zone}/IN (signed): dsyncfetch: send NOTIFY(CDS) query to scanner.{tld}" + ) + @pytest.mark.parametrize( "tld", @@ -299,6 +308,8 @@ def test_algoroll_csk_reconfig_step4(tld, ns3, alg, size): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -333,6 +344,8 @@ def test_algoroll_csk_reconfig_step5(tld, ns3, alg, size): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -365,3 +378,5 @@ def test_algoroll_csk_reconfig_step6(tld, ns3, alg, size): "verbose": True, } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log diff --git a/bin/tests/system/rollover-algo-ksk-zsk/tests_rollover_algo_ksk_zsk_initial.py b/bin/tests/system/rollover-algo-ksk-zsk/tests_rollover_algo_ksk_zsk_initial.py index 2f74cd0fa4..826d8a1254 100644 --- a/bin/tests/system/rollover-algo-ksk-zsk/tests_rollover_algo_ksk_zsk_initial.py +++ b/bin/tests/system/rollover-algo-ksk-zsk/tests_rollover_algo_ksk_zsk_initial.py @@ -77,3 +77,5 @@ def test_algoroll_ksk_zsk_initial(tld, ns3): "nextev": TIMEDELTA["PT1H"], } isctest.kasp.check_rollover_step(ns3, config, policy, step) + + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log diff --git a/bin/tests/system/rollover-algo-ksk-zsk/tests_rollover_algo_ksk_zsk_reconfig.py b/bin/tests/system/rollover-algo-ksk-zsk/tests_rollover_algo_ksk_zsk_reconfig.py index 67de084d46..a1b2688212 100644 --- a/bin/tests/system/rollover-algo-ksk-zsk/tests_rollover_algo_ksk_zsk_reconfig.py +++ b/bin/tests/system/rollover-algo-ksk-zsk/tests_rollover_algo_ksk_zsk_reconfig.py @@ -141,6 +141,8 @@ def test_algoroll_ksk_zsk_reconfig_step1(tld, ns3, alg, size): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -180,6 +182,8 @@ def test_algoroll_ksk_zsk_reconfig_step2(tld, ns3, alg, size): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -253,6 +257,11 @@ def test_algoroll_ksk_zsk_reconfig_step3(tld, ns3, alg, size): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + with ns3.watch_log_from_start() as watcher: + watcher.wait_for_line( + f"zone {zone}/IN (signed): dsyncfetch: send NOTIFY(CDS) query to scanner.{tld}" + ) + @pytest.mark.parametrize( "tld", @@ -315,6 +324,8 @@ def test_algoroll_ksk_zsk_reconfig_step4(tld, ns3, alg, size): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -353,6 +364,8 @@ def test_algoroll_ksk_zsk_reconfig_step5(tld, ns3, alg, size): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -387,3 +400,5 @@ def test_algoroll_ksk_zsk_reconfig_step6(tld, ns3, alg, size): "verbose": True, } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log diff --git a/bin/tests/system/rollover-csk-roll1/tests_rollover_csk_roll1.py b/bin/tests/system/rollover-csk-roll1/tests_rollover_csk_roll1.py index 5e496dbff6..03acab2ce7 100644 --- a/bin/tests/system/rollover-csk-roll1/tests_rollover_csk_roll1.py +++ b/bin/tests/system/rollover-csk-roll1/tests_rollover_csk_roll1.py @@ -123,6 +123,8 @@ def test_csk_roll1_step1(tld, ns3, alg, size): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -181,6 +183,8 @@ def test_csk_roll1_step2(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -268,6 +272,11 @@ def test_csk_roll1_step3(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + with ns3.watch_log_from_start() as watcher: + watcher.wait_for_line( + f"zone {zone}/IN (signed): dsyncfetch: send NOTIFY(CDS) query to scanner.{tld}" + ) + @pytest.mark.parametrize( "tld", @@ -333,6 +342,8 @@ def test_csk_roll1_step4(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -366,6 +377,8 @@ def test_csk_roll1_step5(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -402,6 +415,8 @@ def test_csk_roll1_step6(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -461,3 +476,5 @@ def test_csk_roll1_step8(tld, alg, size, ns3): "nextev": None, } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log diff --git a/bin/tests/system/rollover-csk-roll2/tests_rollover_csk_roll2.py b/bin/tests/system/rollover-csk-roll2/tests_rollover_csk_roll2.py index 529fd836ee..d08aaa2d40 100644 --- a/bin/tests/system/rollover-csk-roll2/tests_rollover_csk_roll2.py +++ b/bin/tests/system/rollover-csk-roll2/tests_rollover_csk_roll2.py @@ -126,6 +126,8 @@ def test_csk_roll2_step1(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -184,6 +186,8 @@ def test_csk_roll2_step2(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -271,6 +275,11 @@ def test_csk_roll2_step3(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + with ns3.watch_log_from_start() as watcher: + watcher.wait_for_line( + f"zone {zone}/IN (signed): dsyncfetch: send NOTIFY(CDS) query to scanner.{tld}" + ) + @pytest.mark.parametrize( "tld", @@ -310,6 +319,8 @@ def test_csk_roll2_step4(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -374,6 +385,8 @@ def test_csk_roll2_step5(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -409,6 +422,8 @@ def test_csk_roll2_step6(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -439,3 +454,5 @@ def test_csk_roll2_step7(tld, alg, size, ns3): "verbose": True, } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log diff --git a/bin/tests/system/rollover-enable-dnssec/tests_rollover_enable_dnssec.py b/bin/tests/system/rollover-enable-dnssec/tests_rollover_enable_dnssec.py index 23d47bdbb6..818df48f7d 100644 --- a/bin/tests/system/rollover-enable-dnssec/tests_rollover_enable_dnssec.py +++ b/bin/tests/system/rollover-enable-dnssec/tests_rollover_enable_dnssec.py @@ -122,6 +122,8 @@ def test_rollover_enable_dnssec_step1(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -154,6 +156,8 @@ def test_rollover_enable_dnssec_step2(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -208,6 +212,11 @@ def test_rollover_enable_dnssec_step3(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + with ns3.watch_log_from_start() as watcher: + watcher.wait_for_line( + f"zone {zone}/IN (signed): dsyncfetch: send NOTIFY(CDS) query to scanner.{tld}" + ) + @pytest.mark.parametrize( "tld", @@ -237,3 +246,5 @@ def test_rollover_enable_dnssec_step4(tld, alg, size, ns3): "nextev": TIMEDELTA["PT1H"], } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log diff --git a/bin/tests/system/rollover-going-insecure/tests_rollover_going_insecure_initial.py b/bin/tests/system/rollover-going-insecure/tests_rollover_going_insecure_initial.py index 92950124ed..fc83ae5beb 100644 --- a/bin/tests/system/rollover-going-insecure/tests_rollover_going_insecure_initial.py +++ b/bin/tests/system/rollover-going-insecure/tests_rollover_going_insecure_initial.py @@ -70,3 +70,5 @@ def test_going_insecure_initial(zone, ns3, alg, size): "nextev": None, } isctest.kasp.check_rollover_step(ns3, config, policy, step) + + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log diff --git a/bin/tests/system/rollover-going-insecure/tests_rollover_going_insecure_reconfig.py b/bin/tests/system/rollover-going-insecure/tests_rollover_going_insecure_reconfig.py index 5cd8d65816..485d70d434 100644 --- a/bin/tests/system/rollover-going-insecure/tests_rollover_going_insecure_reconfig.py +++ b/bin/tests/system/rollover-going-insecure/tests_rollover_going_insecure_reconfig.py @@ -63,14 +63,14 @@ def after_servers_start(ns3, templates): def test_going_insecure_reconfig_step1(zone, alg, size, ns3): config = DEFAULT_CONFIG policy = "insecure" - zone = f"step1.{zone}" + szone = f"step1.{zone}" - isctest.kasp.wait_keymgr_done(ns3, zone, reconfig=True) + isctest.kasp.wait_keymgr_done(ns3, szone, reconfig=True) # Key goal states should be HIDDEN. # The DS may be removed if we are going insecure. step = { - "zone": zone, + "zone": szone, "cdss": CDSS, "keyprops": [ f"ksk 0 {alg} {size} goal:hidden dnskey:omnipresent krrsig:omnipresent ds:unretentive offset:{-DURATION['P10D']}", @@ -85,6 +85,16 @@ def test_going_insecure_reconfig_step1(zone, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, config, policy, step) + with ns3.watch_log_from_start() as watcher: + if "dynamic" in zone: + watcher.wait_for_line( + f"zone {szone}/IN: dsyncfetch: send NOTIFY(CDS) query to scanner.kasp" + ) + else: + watcher.wait_for_line( + f"zone {szone}/IN (signed): dsyncfetch: send NOTIFY(CDS) query to scanner.kasp" + ) + @pytest.mark.parametrize( "zone", @@ -119,3 +129,5 @@ def test_going_insecure_reconfig_step2(zone, alg, size, ns3): "check-keytimes": False, } isctest.kasp.check_rollover_step(ns3, config, policy, step) + + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log diff --git a/bin/tests/system/rollover-ksk-3crowd/tests_rollover_three_is_a_crowd.py b/bin/tests/system/rollover-ksk-3crowd/tests_rollover_three_is_a_crowd.py index 8ced405fbc..401324d9fb 100644 --- a/bin/tests/system/rollover-ksk-3crowd/tests_rollover_three_is_a_crowd.py +++ b/bin/tests/system/rollover-ksk-3crowd/tests_rollover_three_is_a_crowd.py @@ -115,3 +115,8 @@ def test_rollover_ksk_three_is_a_crowd(alg, size, ns3): expected[1].timing["Removed"] = now + KSK_IPUB + KSK_IRET isctest.kasp.check_keytimes(keys, expected) + + with ns3.watch_log_from_start() as watcher: + watcher.wait_for_line( + f"zone {zone}/IN (signed): dsyncfetch: send NOTIFY(CDS) query to scanner.kasp" + ) diff --git a/bin/tests/system/rollover-ksk-doubleksk/tests_rollover_ksk_doubleksk.py b/bin/tests/system/rollover-ksk-doubleksk/tests_rollover_ksk_doubleksk.py index 0eaa236b6d..8ef59716a3 100644 --- a/bin/tests/system/rollover-ksk-doubleksk/tests_rollover_ksk_doubleksk.py +++ b/bin/tests/system/rollover-ksk-doubleksk/tests_rollover_ksk_doubleksk.py @@ -107,6 +107,8 @@ def test_ksk_doubleksk_step1(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -166,6 +168,8 @@ def test_ksk_doubleksk_step2(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -246,6 +250,11 @@ def test_ksk_doubleksk_step3(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step) + with ns3.watch_log_from_start() as watcher: + watcher.wait_for_line( + f"zone {zone}/IN (signed): dsyncfetch: send NOTIFY(CDS) query to scanner.{tld}" + ) + @pytest.mark.parametrize( "tld", @@ -312,6 +321,8 @@ def test_ksk_doubleksk_step4(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -349,6 +360,8 @@ def test_ksk_doubleksk_step5(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -376,3 +389,5 @@ def test_ksk_doubleksk_step6(tld, alg, size, ns3): "nextev": None, } isctest.kasp.check_rollover_step(ns3, KSK_CONFIG, policy, step) + + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log diff --git a/bin/tests/system/rollover-lifetime/tests_rollover_lifetime_initial.py b/bin/tests/system/rollover-lifetime/tests_rollover_lifetime_initial.py index a7f5ead0fa..8f24c13b91 100644 --- a/bin/tests/system/rollover-lifetime/tests_rollover_lifetime_initial.py +++ b/bin/tests/system/rollover-lifetime/tests_rollover_lifetime_initial.py @@ -48,3 +48,5 @@ def test_lifetime_initial(zone, policy, lifetime, alg, size, ns3): "nextev": None, } isctest.kasp.check_rollover_step(ns3, config, policy, step) + + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log diff --git a/bin/tests/system/rollover-lifetime/tests_rollover_lifetime_reconfig.py b/bin/tests/system/rollover-lifetime/tests_rollover_lifetime_reconfig.py index d28a7e4cd5..77529993c6 100644 --- a/bin/tests/system/rollover-lifetime/tests_rollover_lifetime_reconfig.py +++ b/bin/tests/system/rollover-lifetime/tests_rollover_lifetime_reconfig.py @@ -63,3 +63,5 @@ def test_lifetime_reconfig(zone, policy, lifetime, alg, size, ns3): "nextev": None, } isctest.kasp.check_rollover_step(ns3, config, policy, step) + + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log diff --git a/bin/tests/system/rollover-straight2none/tests_rollover_straight2none_initial.py b/bin/tests/system/rollover-straight2none/tests_rollover_straight2none_initial.py index c38e5ddd49..7a364c3149 100644 --- a/bin/tests/system/rollover-straight2none/tests_rollover_straight2none_initial.py +++ b/bin/tests/system/rollover-straight2none/tests_rollover_straight2none_initial.py @@ -68,3 +68,5 @@ def test_straight2none_initial(zone, ns3, alg, size): "nextev": None, } isctest.kasp.check_rollover_step(ns3, config, policy, step) + + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log diff --git a/bin/tests/system/rollover-straight2none/tests_rollover_straight2none_reconfig.py b/bin/tests/system/rollover-straight2none/tests_rollover_straight2none_reconfig.py index 5cfb49653b..d54d98d65a 100644 --- a/bin/tests/system/rollover-straight2none/tests_rollover_straight2none_reconfig.py +++ b/bin/tests/system/rollover-straight2none/tests_rollover_straight2none_reconfig.py @@ -77,3 +77,5 @@ def test_straight2none_reconfig(zone, ns3, alg, size): "nextev": None, } isctest.kasp.check_rollover_step(ns3, config, policy, step) + + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log diff --git a/bin/tests/system/rollover-zsk-prepub/tests_rollover_zsk_prepublication.py b/bin/tests/system/rollover-zsk-prepub/tests_rollover_zsk_prepublication.py index 2720275c66..9a93911e18 100644 --- a/bin/tests/system/rollover-zsk-prepub/tests_rollover_zsk_prepublication.py +++ b/bin/tests/system/rollover-zsk-prepub/tests_rollover_zsk_prepublication.py @@ -116,6 +116,8 @@ def test_zsk_prepub_step1(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -173,6 +175,8 @@ def test_zsk_prepub_step2(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -251,6 +255,8 @@ def test_zsk_prepub_step3(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + # Force full resign and check all signatures have been replaced. with ns3.watch_log_from_here() as watcher: ns3.rndc(f"sign {zone}") @@ -321,6 +327,8 @@ def test_zsk_prepub_step4(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -356,6 +364,8 @@ def test_zsk_prepub_step5(tld, alg, size, ns3): } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log + @pytest.mark.parametrize( "tld", @@ -382,3 +392,5 @@ def test_zsk_prepub_step6(tld, alg, size, ns3): "nextev": None, } isctest.kasp.check_rollover_step(ns3, CONFIG, policy, step) + + assert f"zone {zone}/IN (signed): dsyncfetch" not in ns3.log diff --git a/bin/tests/system/rollover/ns3/named.common.conf.j2 b/bin/tests/system/rollover/ns3/named.common.conf.j2 index 813aa919e1..2c64dbeaa3 100644 --- a/bin/tests/system/rollover/ns3/named.common.conf.j2 +++ b/bin/tests/system/rollover/ns3/named.common.conf.j2 @@ -30,6 +30,7 @@ options { allow-transfer { any; }; recursion yes; dnssec-validation @dnssec_validation@; + notify-cds yes; }; key rndc_key { diff --git a/bin/tests/system/xfer-servers-list/tests_xfer_servers_list.py b/bin/tests/system/xfer-servers-list/tests_xfer_servers_list.py index 54cf2fcaf2..cb7553d171 100644 --- a/bin/tests/system/xfer-servers-list/tests_xfer_servers_list.py +++ b/bin/tests/system/xfer-servers-list/tests_xfer_servers_list.py @@ -33,7 +33,7 @@ def wait_for_initial_xfrin(ns): def wait_for_sending_notify(ns1, ns, key_name): pattern = Re( - f"zone test/IN: sending notify to {ns.ip}#[0-9]+ : TSIG \\({key_name}\\)" + f"zone test/IN: sending notify\\(SOA\\) to {ns.ip}#[0-9]+ : TSIG \\({key_name}\\)" ) with ns1.watch_log_from_start() as watcher: watcher.wait_for_line(pattern) diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index bc680650c6..e238195748 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -2097,9 +2097,9 @@ Boolean Options .. namedconf:statement:: notify :tags: transfer - :short: Controls whether ``NOTIFY`` messages are sent on zone changes. + :short: Controls whether ``NOTIFY(SOA)`` messages are sent on zone changes. - If set to ``yes`` (the default), DNS NOTIFY messages are sent when a + If set to ``yes`` (the default), DNS NOTIFY(SOA) messages are sent when a zone the server is authoritative for changes; see :ref:`using notify`. The messages are sent to the servers listed in the zone's NS records (except the primary server identified in the SOA MNAME field), and to @@ -2115,6 +2115,22 @@ Boolean Options statement. It would only be necessary to turn off this option if it caused secondary zones to crash. +.. namedconf:statement:: notify-cds + :tags: dnssec + :short: Controls whether ``NOTIFY(CDS)`` messages are sent on zone changes. + + If set to ``yes``, DNS NOTIFY(CDS) messages are sent when the CDS or CDNSKEY + RRset changes. The messages are sent to the servers listed in the parent + zone's matching DSYNC records. A DSYNC record matches if the owner name under + `_dsync` subdomain of the parent zone corresponds to the given zone. For + example, the zone `child.example` should have a DSYNC record at + `child._dsync.example`. In addition, the RRtype field of the record must be + `CDS` and the Scheme field must be 1 (NOTIFY). + + The default is ``no``. The :namedconf:ref:`notify-cds` option may also be + specified in the :any:`zone` statement, in which case it overrides the + ``options notify-cds`` statement. + .. namedconf:statement:: notify-to-soa :tags: transfer :short: Controls whether the name servers in the NS RRset are checked against the SOA MNAME. diff --git a/doc/misc/mirror.zoneopt b/doc/misc/mirror.zoneopt index 346f093d33..2cdf57f2af 100644 --- a/doc/misc/mirror.zoneopt +++ b/doc/misc/mirror.zoneopt @@ -29,6 +29,7 @@ zone [ ] { min-transfer-rate-in ; multi-master ; notify ( explicit | master-only | primary-only | ); + notify-cds ; notify-defer ; notify-delay ; notify-source ( | * ); diff --git a/doc/misc/options b/doc/misc/options index 39b7d2f4a9..2f1f03ec98 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -210,6 +210,7 @@ options { no-case-compress { ; ... }; nocookie-udp-size ; notify ( explicit | master-only | primary-only | ); + notify-cds ; notify-defer ; notify-delay ; notify-rate ; @@ -415,6 +416,7 @@ template { min-transfer-rate-in ; multi-master ; notify ( explicit | master-only | primary-only | ); + notify-cds ; notify-defer ; notify-delay ; notify-source ( | * ); @@ -586,6 +588,7 @@ view [ ] { no-case-compress { ; ... }; nocookie-udp-size ; notify ( explicit | master-only | primary-only | ); + notify-cds ; notify-defer ; notify-delay ; notify-source ( | * ); diff --git a/doc/misc/primary.zoneopt b/doc/misc/primary.zoneopt index dfebff879b..a5e26bee89 100644 --- a/doc/misc/primary.zoneopt +++ b/doc/misc/primary.zoneopt @@ -44,6 +44,7 @@ zone [ ] { max-types-per-name ; max-zone-ttl ( unlimited | ); // deprecated notify ( explicit | master-only | primary-only | ); + notify-cds ; notify-defer ; notify-delay ; notify-source ( | * ); diff --git a/doc/misc/secondary.zoneopt b/doc/misc/secondary.zoneopt index cbbd5fea52..cc1d28a984 100644 --- a/doc/misc/secondary.zoneopt +++ b/doc/misc/secondary.zoneopt @@ -41,6 +41,7 @@ zone [ ] { min-transfer-rate-in ; multi-master ; notify ( explicit | master-only | primary-only | ); + notify-cds ; notify-defer ; notify-delay ; notify-source ( | * ); diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c index 2874341be0..c5be6eda9a 100644 --- a/lib/dns/dnssec.c +++ b/lib/dns/dnssec.c @@ -1825,8 +1825,9 @@ add_cds(dns_dnsseckey_t *key, dns_rdata_t *keyrdata, const char *keystr, "CDS (%s) for key %s is now published", algbuf, keystr); addrdata(&cdsrdata, diff, origin, ttl, mctx); + return ISC_R_SUCCESS; } - return ISC_R_SUCCESS; + return DNS_R_UNCHANGED; } static isc_result_t @@ -1850,8 +1851,9 @@ delete_cds(dns_dnsseckey_t *key, dns_rdata_t *keyrdata, const char *keystr, "CDS (%s) for key %s is now deleted", algbuf, keystr); delrdata(&cdsrdata, diff, origin, cds->ttl, mctx); + return ISC_R_SUCCESS; } - return ISC_R_SUCCESS; + return DNS_R_UNCHANGED; } isc_result_t @@ -1861,9 +1863,10 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys, bool gencdnskey, dns_ttl_t ttl, dns_diff_t *diff, isc_mem_t *mctx) { unsigned char keybuf[DST_KEY_MAXSIZE]; - isc_result_t result; + isc_result_t result = DNS_R_UNCHANGED; dns_ttl_t cdsttl = ttl; dns_ttl_t cdnskeyttl = ttl; + bool changed = false; REQUIRE(digests != NULL); REQUIRE(keys != NULL); @@ -1890,9 +1893,15 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys, dst_key_format(key->key, keystr, sizeof(keystr)); ISC_LIST_FOREACH(*digests, alg, link) { - CHECK(add_cds(key, &cdnskeyrdata, - (const char *)keystr, cds, - alg->digest, cdsttl, diff, mctx)); + result = add_cds(key, &cdnskeyrdata, + (const char *)keystr, cds, + alg->digest, cdsttl, diff, + mctx); + if (result == ISC_R_SUCCESS) { + changed = true; + } else if (result != DNS_R_UNCHANGED) { + goto cleanup; + } } if (gencdnskey && @@ -1906,6 +1915,7 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys, keystr); addrdata(&cdnskeyrdata, diff, origin, cdnskeyttl, mctx); + changed = true; } } @@ -1915,15 +1925,32 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys, if (dns_rdataset_isassociated(cds)) { /* Delete all possible CDS records */ - delete_cds(key, &cdnskeyrdata, - (const char *)keystr, cds, - DNS_DSDIGEST_SHA1, diff, mctx); - delete_cds(key, &cdnskeyrdata, - (const char *)keystr, cds, - DNS_DSDIGEST_SHA256, diff, mctx); - delete_cds(key, &cdnskeyrdata, - (const char *)keystr, cds, - DNS_DSDIGEST_SHA384, diff, mctx); + for (dns_dsdigest_t digest = DNS_DSDIGEST_SHA1; + digest < DNS_DSDIGEST_TOTAL; digest++) + { + result = delete_cds( + key, &cdnskeyrdata, + (const char *)keystr, cds, + digest, diff, mctx); + + switch (result) { + case ISC_R_SUCCESS: + changed = true; + break; + case DNS_R_UNCHANGED: + case ISC_R_NOTIMPLEMENTED: + /* + * Either the digest is not + * supported and we cannot + * construct the CDS for it, or + * the CDS with this digest is + * not present in the CDS RRset. + */ + break; + default: + goto cleanup; + } + } } if (dns_rdataset_isassociated(cdnskey)) { @@ -1936,6 +1963,7 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys, keystr); delrdata(&cdnskeyrdata, diff, origin, cdnskey->ttl, mctx); + changed = true; } } } @@ -1944,7 +1972,10 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys, if (!dns_rdataset_isassociated(cds) && !dns_rdataset_isassociated(cdnskey)) { - return ISC_R_SUCCESS; + if (changed) { + return ISC_R_SUCCESS; + } + return DNS_R_UNCHANGED; } /* @@ -1961,12 +1992,30 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys, &cdnskeyrdata)); if (dns_rdataset_isassociated(cds)) { - delete_cds(key, &cdnskeyrdata, (const char *)keystr, - cds, DNS_DSDIGEST_SHA1, diff, mctx); - delete_cds(key, &cdnskeyrdata, (const char *)keystr, - cds, DNS_DSDIGEST_SHA256, diff, mctx); - delete_cds(key, &cdnskeyrdata, (const char *)keystr, - cds, DNS_DSDIGEST_SHA384, diff, mctx); + for (dns_dsdigest_t digest = DNS_DSDIGEST_SHA1; + digest < DNS_DSDIGEST_TOTAL; digest++) + { + result = delete_cds(key, &cdnskeyrdata, + (const char *)keystr, cds, + digest, diff, mctx); + switch (result) { + case ISC_R_SUCCESS: + changed = true; + break; + case DNS_R_UNCHANGED: + case ISC_R_NOTIMPLEMENTED: + /* + * Either the digest is not + * supported and we cannot + * construct the CDS for it, or + * the CDS with this digest is + * not present in the CDS RRset. + */ + break; + default: + goto cleanup; + } + } } if (dns_rdataset_isassociated(cdnskey)) { @@ -1978,11 +2027,15 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys, keystr); delrdata(&cdnskeyrdata, diff, origin, cdnskey->ttl, mctx); + changed = true; } } } - result = ISC_R_SUCCESS; + if (changed) { + return ISC_R_SUCCESS; + } + return DNS_R_UNCHANGED; cleanup: return result; @@ -1999,6 +2052,7 @@ dns_dnssec_syncdelete(dns_rdataset_t *cds, dns_rdataset_t *cdnskey, dns_rdata_t cds_delete = DNS_RDATA_INIT; dns_rdata_t cdnskey_delete = DNS_RDATA_INIT; isc_region_t r; + bool changed = false; r.base = keybuf; r.length = sizeof(keybuf); @@ -2021,6 +2075,7 @@ dns_dnssec_syncdelete(dns_rdataset_t *cds, dns_rdataset_t *cdnskey, "published", namebuf); addrdata(&cds_delete, diff, origin, ttl, mctx); + changed = true; } } else { if (dns_rdataset_isassociated(cds) && exists(cds, &cds_delete)) @@ -2031,6 +2086,7 @@ dns_dnssec_syncdelete(dns_rdataset_t *cds, dns_rdataset_t *cdnskey, "deleted", namebuf); delrdata(&cds_delete, diff, origin, cds->ttl, mctx); + changed = true; } } @@ -2044,6 +2100,7 @@ dns_dnssec_syncdelete(dns_rdataset_t *cds, dns_rdataset_t *cdnskey, "published", namebuf); addrdata(&cdnskey_delete, diff, origin, ttl, mctx); + changed = true; } } else { if (dns_rdataset_isassociated(cdnskey) && @@ -2056,10 +2113,14 @@ dns_dnssec_syncdelete(dns_rdataset_t *cds, dns_rdataset_t *cdnskey, namebuf); delrdata(&cdnskey_delete, diff, origin, cdnskey->ttl, mctx); + changed = true; } } - return ISC_R_SUCCESS; + if (changed) { + return ISC_R_SUCCESS; + } + return DNS_R_UNCHANGED; } /* diff --git a/lib/dns/include/dns/dnssec.h b/lib/dns/include/dns/dnssec.h index eed02b00da..14f9e468ae 100644 --- a/lib/dns/include/dns/dnssec.h +++ b/lib/dns/include/dns/dnssec.h @@ -403,6 +403,7 @@ dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys, * * Returns: *\li ISC_R_SUCCESS + *\li DNS_R_UNCHANGED - if the CDS and CDNSKEY sets have not changed *\li Other values indicate error */ @@ -421,6 +422,7 @@ dns_dnssec_syncdelete(dns_rdataset_t *cds, dns_rdataset_t *cdnskey, * * Returns: *\li ISC_R_SUCCESS + *\li DNS_R_UNCHANGED - if the CDS and CDNSKEY sets have not changed *\li Other values indicate error */ diff --git a/lib/dns/include/dns/ds.h b/lib/dns/include/dns/ds.h index 9f1e7938de..edad308ea1 100644 --- a/lib/dns/include/dns/ds.h +++ b/lib/dns/include/dns/ds.h @@ -31,6 +31,9 @@ #define DNS_DSDIGEST_SHA256PRIVATE (7) #define DNS_DSDIGEST_SHA384PRIVATE (8) #define DNS_DSDIGEST_SM3PRIVATE (9) +#define DNS_DSDIGEST_TOTAL (10) +#else +#define DNS_DSDIGEST_TOTAL (7) #endif #define DNS_DSDIGEST_MAX (255) diff --git a/lib/dns/include/dns/notify.h b/lib/dns/include/dns/notify.h index 11aaffc66a..6755c2831a 100644 --- a/lib/dns/include/dns/notify.h +++ b/lib/dns/include/dns/notify.h @@ -29,6 +29,8 @@ struct dns_notifyctx { dns_acl_t *notify_acl; + dns_rdatatype_t type; + isc_sockaddr_t notifyfrom; dns_notifylist_t notifies; @@ -49,6 +51,8 @@ struct dns_notify { dns_adbfind_t *find; dns_request_t *request; dns_name_t ns; + dns_rdatatype_t type; + in_port_t port; isc_sockaddr_t src; isc_sockaddr_t dst; dns_tsigkey_t *key; @@ -64,7 +68,18 @@ typedef enum dns_notify_flags { } dns_notify_flags_t; void -dns_notify_create(isc_mem_t *mctx, unsigned int flags, dns_notify_t **notifyp); +dns_notifyctx_init(dns_notifyctx_t *nctx, dns_rdatatype_t type); +/*% + * Initializes a notify context for the RRtype 'type'. + * + * Requires: + * 'nctx' is not NULL. + * + */ + +void +dns_notify_create(isc_mem_t *mctx, dns_rdatatype_t type, in_port_t port, + unsigned int flags, dns_notify_t **notifyp); /*%< * Create a notify structure to maintain state. * @@ -84,14 +99,15 @@ dns_notify_destroy(dns_notify_t *notify, bool zone_locked); */ bool -dns_notify_isqueued(dns_notifyctx_t *nctx, unsigned int flags, dns_name_t *name, - isc_sockaddr_t *addr, dns_tsigkey_t *key, - dns_transport_t *transport); +dns_notify_isqueued(dns_notifyctx_t *nctx, dns_rdatatype_t type, in_port_t port, + unsigned int flags, dns_name_t *name, isc_sockaddr_t *addr, + dns_tsigkey_t *key, dns_transport_t *transport); /*%< - * Check if we already have a notify queued matching name, destination - * address, TSIG key, and transport. Will requeue on the normal notify - * ratelimiter if the notify was enqueued on the startup ratelimiter and - * this is not a startup notify. + * Check if we already have a notify queued matching name, type, + * destination address and port, TSIG key, and transport. Will + * requeue on the normal notify ratelimiter if the notify was + * enqueued on the startup ratelimiter and this is not a startup + * notify. * * Requires: * 'nctx' is not NULL diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 04aa03dd92..11a9daed4c 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -682,6 +682,20 @@ dns_zone_setalsonotify(dns_zone_t *zone, isc_sockaddr_t *addresses, *\li 'count' to be the number of notifiees. */ +void +dns_zone_setcdsendpoints(dns_zone_t *zone, isc_sockaddr_t *addresses, + isc_sockaddr_t *sources, dns_name_t **keynames, + dns_name_t **tlsnames, uint32_t count); +/*%< + * Set the list of servers to be notified when the zone changes + * its CDS/CDNSKEY RRset. To clear the list use 'count = 0'. + * + * Require: + *\li 'zone' to be a valid zone. + *\li 'addresses' to be non-NULL if count != 0. + *\li 'count' to be the number of notifiees. + */ + void dns_zone_unload(dns_zone_t *zone); /*%< @@ -1541,9 +1555,10 @@ dns_zone_getrequesttransporttype(dns_zone_t *zone); */ void -dns_zone_setnotifytype(dns_zone_t *zone, dns_notifytype_t notifytype); +dns_zone_setnotifytype(dns_zone_t *zone, dns_rdatatype_t type, + dns_notifytype_t notifytype); /*%< - * Sets zone notify method to "notifytype" + * Sets zone notify(type) method to "notifytype" */ void diff --git a/lib/dns/include/dns/zonefetch.h b/lib/dns/include/dns/zonefetch.h index 2ea5108281..0a3a357644 100644 --- a/lib/dns/include/dns/zonefetch.h +++ b/lib/dns/include/dns/zonefetch.h @@ -31,20 +31,22 @@ * the last position of the enum. */ typedef enum { + ZONEFETCHTYPE_DSYNC, ZONEFETCHTYPE_KEY, ZONEFETCHTYPE_NS, ZONEFETCHTYPE_COUNT, } dns_zonefetch_type_t; -typedef struct dns_keyfetch dns_keyfetch_t; -typedef struct dns_nsfetch dns_nsfetch_t; -typedef struct dns_zonefetch dns_zonefetch_t; +typedef struct dns_dsyncfetch dns_dsyncfetch_t; +typedef struct dns_keyfetch dns_keyfetch_t; +typedef struct dns_nsfetch dns_nsfetch_t; +typedef struct dns_zonefetch dns_zonefetch_t; /* * Fetch methods. */ typedef struct dns_zonefetch_methods { - void (*start_fetch)(dns_zonefetch_t *fetch); + isc_result_t (*start_fetch)(dns_zonefetch_t *fetch); void (*continue_fetch)(dns_zonefetch_t *fetch); void (*cancel_fetch)(dns_zonefetch_t *fetch); void (*cleanup_fetch)(dns_zonefetch_t *fetch); @@ -55,6 +57,12 @@ typedef struct dns_zonefetch_methods { /* * Fetch contexts. */ +struct dns_dsyncfetch { + dns_fixedname_t fname; + dns_name_t pname; + dns_name_t dsyncname; +}; + struct dns_keyfetch { dns_rdataset_t keydataset; dns_db_t *db; @@ -65,8 +73,9 @@ struct dns_nsfetch { }; typedef union dns_fetchdata { - dns_keyfetch_t keyfetch; - dns_nsfetch_t nsfetch; + dns_dsyncfetch_t dsyncfetch; + dns_keyfetch_t keyfetch; + dns_nsfetch_t nsfetch; } dns_zonefetch_data_t; struct dns_zonefetch { diff --git a/lib/dns/notify.c b/lib/dns/notify.c index da06276393..98619c1115 100644 --- a/lib/dns/notify.c +++ b/lib/dns/notify.c @@ -40,7 +40,21 @@ notify_log(dns_notify_t *notify, int level, const char *fmt, ...) { } void -dns_notify_create(isc_mem_t *mctx, unsigned int flags, dns_notify_t **notifyp) { +dns_notifyctx_init(dns_notifyctx_t *nctx, dns_rdatatype_t type) { + dns_notifyctx_t ctx = { + .type = type, + .notifytype = dns_notifytype_yes, + .notifies = ISC_LIST_INITIALIZER, + }; + isc_sockaddr_any(&ctx.notifysrc4); + isc_sockaddr_any6(&ctx.notifysrc6); + + *nctx = ctx; +} + +void +dns_notify_create(isc_mem_t *mctx, dns_rdatatype_t type, in_port_t port, + unsigned int flags, dns_notify_t **notifyp) { dns_notify_t *notify; REQUIRE(notifyp != NULL && *notifyp == NULL); @@ -48,6 +62,8 @@ dns_notify_create(isc_mem_t *mctx, unsigned int flags, dns_notify_t **notifyp) { notify = isc_mem_get(mctx, sizeof(*notify)); *notify = (dns_notify_t){ .flags = flags, + .port = port, + .type = type, }; isc_mem_attach(mctx, ¬ify->mctx); @@ -71,7 +87,7 @@ dns_notify_destroy(dns_notify_t *notify, bool locked) { dns__zone_lock(notify->zone); } REQUIRE(dns__zone_locked(notify->zone)); - nctx = dns__zone_getnotifyctx(notify->zone); + nctx = dns__zone_getnotifyctx(notify->zone, notify->type); if (ISC_LINK_LINKED(notify, link)) { ISC_LIST_UNLINK(nctx->notifies, notify, link); } @@ -113,11 +129,15 @@ notify_done(void *arg) { isc_buffer_t buf; char rcode[128]; char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + char typebuf[DNS_RDATATYPE_FORMATSIZE]; REQUIRE(DNS_NOTIFY_VALID(notify)); isc_buffer_init(&buf, rcode, sizeof(rcode)); isc_sockaddr_format(¬ify->dst, addrbuf, sizeof(addrbuf)); + + dns_rdatatype_format(notify->type, typebuf, sizeof(typebuf)); + /* WMM: This is changing the mctx from zone to notify. */ dns_message_create(notify->mctx, NULL, NULL, DNS_MESSAGE_INTENTPARSE, &message); @@ -136,32 +156,33 @@ notify_done(void *arg) { result = dns_rcode_totext(message->rcode, &buf); if (result == ISC_R_SUCCESS) { notify_log(notify, ISC_LOG_DEBUG(3), - "notify response from %s: %.*s", addrbuf, - (int)buf.used, rcode); + "notify(%s) response from %s: %.*s", typebuf, + addrbuf, (int)buf.used, rcode); } fail: dns_message_detach(&message); if (result == ISC_R_SUCCESS) { - notify_log(notify, ISC_LOG_DEBUG(1), "notify to %s successful", - addrbuf); + notify_log(notify, ISC_LOG_DEBUG(1), + "notify(%s) to %s successful", typebuf, addrbuf); } else if (result == ISC_R_SHUTTINGDOWN || result == ISC_R_CANCELED) { /* just destroy the notify */ } else if ((notify->flags & DNS_NOTIFY_TCP) == 0) { notify_log(notify, ISC_LOG_NOTICE, - "notify to %s failed: %s: retrying over TCP", - addrbuf, isc_result_totext(result)); + "notify(%s) to %s failed: %s: retrying over TCP", + typebuf, addrbuf, isc_result_totext(result)); notify->flags |= DNS_NOTIFY_TCP; dns_request_destroy(¬ify->request); dns_notify_queue(notify, notify->flags & DNS_NOTIFY_STARTUP); return; } else if (result == ISC_R_TIMEDOUT) { notify_log(notify, ISC_LOG_WARNING, - "notify to %s failed: %s: retries exceeded", addrbuf, - isc_result_totext(result)); + "notify(%s) to %s failed: %s: retries exceeded", + typebuf, addrbuf, isc_result_totext(result)); } else { - notify_log(notify, ISC_LOG_WARNING, "notify to %s failed: %s", - addrbuf, isc_result_totext(result)); + notify_log(notify, ISC_LOG_WARNING, + "notify(%s) to %s failed: %s", typebuf, addrbuf, + isc_result_totext(result)); } dns_notify_destroy(notify, false); } @@ -308,6 +329,7 @@ notify_send_toaddr(void *arg) { isc_netaddr_t dstip; dns_tsigkey_t *key = NULL; char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + char typebuf[DNS_RDATATYPE_FORMATSIZE]; isc_sockaddr_t src; unsigned int options; bool have_notifysource = false; @@ -317,13 +339,16 @@ notify_send_toaddr(void *arg) { dns__zone_lock(notify->zone); - notifyctx = dns__zone_getnotifyctx(notify->zone); + notifyctx = dns__zone_getnotifyctx(notify->zone, notify->type); zmgr = dns_zone_getmgr(notify->zone); view = dns_zone_getview(notify->zone); loop = dns_zone_getloop(notify->zone); result = dns_zone_getdb(notify->zone, &zonedb); + isc_sockaddr_format(¬ify->dst, addrbuf, sizeof(addrbuf)); + dns_rdatatype_format(notify->type, typebuf, sizeof(typebuf)); + if (!dns__zone_loaded(notify->zone) || notify->rlevent->canceled || dns__zone_exiting(notify->zone) || zmgr == NULL || view == NULL || view->requestmgr == NULL || loop == NULL || zonedb == NULL || @@ -341,8 +366,8 @@ notify_send_toaddr(void *arg) { IN6_IS_ADDR_V4MAPPED(¬ify->dst.type.sin6.sin6_addr)) { notify_log(notify, ISC_LOG_DEBUG(3), - "notify: ignoring IPv6 mapped IPV4 address: %s", - addrbuf); + "notify(%s): ignoring IPv6 mapped IPV4 address: %s", + typebuf, addrbuf); result = ISC_R_CANCELED; goto cleanup; } @@ -358,9 +383,9 @@ notify_send_toaddr(void *arg) { result = dns_view_getpeertsig(view, &dstip, &key); if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { notify_log(notify, ISC_LOG_ERROR, - "NOTIFY to %s not sent. " + "NOTIFY(%s) to %s not sent. " "Peer TSIG key lookup failure.", - addrbuf); + typebuf, addrbuf); goto cleanup_message; } } @@ -370,11 +395,11 @@ notify_send_toaddr(void *arg) { dns_name_format(key->name, namebuf, sizeof(namebuf)); notify_log(notify, ISC_LOG_INFO, - "sending notify to %s : TSIG (%s)", addrbuf, - namebuf); + "sending notify(%s) to %s : TSIG (%s)", typebuf, + addrbuf, namebuf); } else { - notify_log(notify, ISC_LOG_INFO, "sending notify to %s", - addrbuf); + notify_log(notify, ISC_LOG_INFO, "sending notify(%s) to %s", + typebuf, addrbuf); } options = 0; if (view->peers != NULL) { @@ -450,8 +475,8 @@ again: goto cleanup_key; } else if ((notify->flags & DNS_NOTIFY_TCP) == 0) { notify_log(notify, ISC_LOG_NOTICE, - "notify to %s failed: %s: retrying over TCP", - addrbuf, isc_result_totext(result)); + "notify(%s) to %s failed: %s: retrying over TCP", + typebuf, addrbuf, isc_result_totext(result)); notify->flags |= DNS_NOTIFY_TCP; goto again; } @@ -475,8 +500,9 @@ cleanup: if (result != ISC_R_SUCCESS) { isc_sockaddr_format(¬ify->dst, addrbuf, sizeof(addrbuf)); - notify_log(notify, ISC_LOG_WARNING, "notify to %s failed: %s", - addrbuf, isc_result_totext(result)); + notify_log(notify, ISC_LOG_WARNING, + "notify(%s) to %s failed: %s", typebuf, addrbuf, + isc_result_totext(result)); dns_notify_destroy(notify, false); } } @@ -512,9 +538,9 @@ dns_notify_queue(dns_notify_t *notify, bool startup) { } bool -dns_notify_isqueued(dns_notifyctx_t *nctx, unsigned int flags, dns_name_t *name, - isc_sockaddr_t *addr, dns_tsigkey_t *key, - dns_transport_t *transport) { +dns_notify_isqueued(dns_notifyctx_t *nctx, dns_rdatatype_t type, in_port_t port, + unsigned int flags, dns_name_t *name, isc_sockaddr_t *addr, + dns_tsigkey_t *key, dns_transport_t *transport) { dns_notify_t *notify = NULL; isc_result_t result; @@ -524,10 +550,14 @@ dns_notify_isqueued(dns_notifyctx_t *nctx, unsigned int flags, dns_name_t *name, if (n->request != NULL) { continue; } + if (n->type != type) { + continue; + } if ((name != NULL && dns_name_dynamic(&n->ns) && dns_name_equal(name, &n->ns)) || (addr != NULL && isc_sockaddr_equal(addr, &n->dst) && - n->key == key && n->transport == transport)) + n->port == port && n->key == key && + n->transport == transport)) { notify = n; goto requeue; @@ -571,7 +601,7 @@ notify_isself(dns_notify_t *notify, isc_sockaddr_t *dst) { dns_isselffunc_t isselffunc; void *isselfarg = NULL; - notifyctx = dns__zone_getnotifyctx(notify->zone); + notifyctx = dns__zone_getnotifyctx(notify->zone, notify->type); view = dns_zone_getview(notify->zone); dns__zone_getisself(notify->zone, &isselffunc, &isselfarg); if (view == NULL || isselffunc == NULL) { @@ -629,12 +659,12 @@ notify_send(dns_notify_t *notify) { if (dns__zone_exiting(notify->zone)) { return; } - notifyctx = dns__zone_getnotifyctx(notify->zone); + notifyctx = dns__zone_getnotifyctx(notify->zone, notify->type); ISC_LIST_FOREACH(notify->find->list, ai, publink) { dst = ai->sockaddr; - if (dns_notify_isqueued(notifyctx, notify->flags, NULL, &dst, - NULL, NULL)) + if (dns_notify_isqueued(notifyctx, notify->type, notify->port, + notify->flags, NULL, &dst, NULL, NULL)) { continue; } @@ -643,7 +673,8 @@ notify_send(dns_notify_t *notify) { } newnotify = NULL; flags = notify->flags & DNS_NOTIFY_NOSOA; - dns_notify_create(notify->mctx, flags, &newnotify); + dns_notify_create(notify->mctx, notify->type, notify->port, + flags, &newnotify); dns__zone_iattach_locked(notify->zone, &newnotify->zone); ISC_LIST_APPEND(notifyctx->notifies, newnotify, link); newnotify->dst = dst; @@ -718,7 +749,7 @@ dns_notify_find_address(dns_notify_t *notify) { } result = dns_adb_createfind(adb, loop, process_notify_adb_event, notify, - ¬ify->ns, options, 0, view->dstport, 0, + ¬ify->ns, options, 0, notify->port, 0, NULL, NULL, NULL, ¬ify->find); dns_adb_detach(&adb); diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 64658232af..c4359cf7e1 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -340,7 +341,10 @@ struct dns_zone { uint32_t fetchcount[ZONEFETCHTYPE_COUNT]; dns_remote_t alsonotify; - dns_notifyctx_t notifyctx; + dns_notifyctx_t notifysoa; + + dns_remote_t cds_endpoints; + dns_notifyctx_t notifycds; isc_sockaddr_t parentalsrc4; isc_sockaddr_t parentalsrc6; @@ -906,6 +910,8 @@ zone_maintenance(dns_zone_t *zone); static void zone_notify(dns_zone_t *zone, isc_time_t *now); static void +zone_notifycds(dns_zone_t *zone); +static void dump_done(void *arg, isc_result_t result); static isc_result_t zone_signwithkey(dns_zone_t *zone, dst_algorithm_t algorithm, uint16_t keyid, @@ -1085,13 +1091,6 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx, isc_tid_t tid) { dns_remote_t r = { .magic = DNS_REMOTE_MAGIC, }; - dns_notifyctx_t nc = { - .notifytype = dns_notifytype_yes, - .notifies = ISC_LIST_INITIALIZER, - }; - isc_sockaddr_any(&nc.notifysrc4); - isc_sockaddr_any6(&nc.notifysrc6); - zone->notifyctx = nc; isc_mem_attach(mctx, &zone->mctx); isc_mutex_init(&zone->lock); @@ -1108,9 +1107,13 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx, isc_tid_t tid) { zone->primaries = r; zone->parentals = r; zone->alsonotify = r; + zone->cds_endpoints = r; zone->defaultkasp = NULL; ISC_LIST_INIT(zone->keyring); + dns_notifyctx_init(&zone->notifysoa, dns_rdatatype_soa); + dns_notifyctx_init(&zone->notifycds, dns_rdatatype_cds); + isc_stats_create(mctx, &zone->gluecachestats, dns_gluecachestatscounter_max); @@ -1243,6 +1246,7 @@ dns__zone_free(dns_zone_t *zone) { dns_zone_setparentals(zone, NULL, NULL, NULL, NULL, 0); dns_zone_setprimaries(zone, NULL, NULL, NULL, NULL, 0); dns_zone_setalsonotify(zone, NULL, NULL, NULL, NULL, 0); + dns_zone_setcdsendpoints(zone, NULL, NULL, NULL, NULL, 0); zone->check_names = dns_severity_ignore; if (zone->update_acl != NULL) { @@ -1251,8 +1255,11 @@ dns__zone_free(dns_zone_t *zone) { if (zone->forward_acl != NULL) { dns_acl_detach(&zone->forward_acl); } - if (zone->notifyctx.notify_acl != NULL) { - dns_acl_detach(&zone->notifyctx.notify_acl); + if (zone->notifysoa.notify_acl != NULL) { + dns_acl_detach(&zone->notifysoa.notify_acl); + } + if (zone->notifycds.notify_acl != NULL) { + dns_acl_detach(&zone->notifycds.notify_acl); } if (zone->query_acl != NULL) { dns_acl_detach(&zone->query_acl); @@ -1366,11 +1373,23 @@ dns_zone_getclass(dns_zone_t *zone) { } void -dns_zone_setnotifytype(dns_zone_t *zone, dns_notifytype_t notifytype) { +dns_zone_setnotifytype(dns_zone_t *zone, dns_rdatatype_t type, + dns_notifytype_t notifytype) { REQUIRE(DNS_ZONE_VALID(zone)); LOCK_ZONE(zone); - zone->notifyctx.notifytype = notifytype; + switch (type) { + case dns_rdatatype_soa: + zone->notifysoa.notifytype = notifytype; + break; + case dns_rdatatype_cds: + INSIST(notifytype == dns_notifytype_no || + notifytype == dns_notifytype_yes); + zone->notifycds.notifytype = notifytype; + break; + default: + UNREACHABLE(); + } UNLOCK_ZONE(zone); } @@ -6339,7 +6358,8 @@ dns_zone_setnotifysrc4(dns_zone_t *zone, const isc_sockaddr_t *notifysrc) { REQUIRE(notifysrc != NULL); LOCK_ZONE(zone); - zone->notifyctx.notifysrc4 = *notifysrc; + zone->notifysoa.notifysrc4 = *notifysrc; + zone->notifycds.notifysrc4 = *notifysrc; UNLOCK_ZONE(zone); } @@ -6349,7 +6369,8 @@ dns_zone_getnotifysrc4(dns_zone_t *zone, isc_sockaddr_t *notifysrc) { REQUIRE(notifysrc != NULL); LOCK_ZONE(zone); - *notifysrc = zone->notifyctx.notifysrc4; + *notifysrc = zone->notifysoa.notifysrc4; + *notifysrc = zone->notifycds.notifysrc4; UNLOCK_ZONE(zone); } @@ -6359,7 +6380,8 @@ dns_zone_setnotifysrc6(dns_zone_t *zone, const isc_sockaddr_t *notifysrc) { REQUIRE(notifysrc != NULL); LOCK_ZONE(zone); - zone->notifyctx.notifysrc6 = *notifysrc; + zone->notifysoa.notifysrc6 = *notifysrc; + zone->notifycds.notifysrc6 = *notifysrc; UNLOCK_ZONE(zone); } @@ -6369,47 +6391,8 @@ dns_zone_getnotifysrc6(dns_zone_t *zone, isc_sockaddr_t *notifysrc) { REQUIRE(notifysrc != NULL); LOCK_ZONE(zone); - *notifysrc = zone->notifyctx.notifysrc6; - UNLOCK_ZONE(zone); -} - -void -dns_zone_setalsonotify(dns_zone_t *zone, isc_sockaddr_t *addresses, - isc_sockaddr_t *sources, dns_name_t **keynames, - dns_name_t **tlsnames, uint32_t count) { - dns_remote_t remote; - - REQUIRE(DNS_ZONE_VALID(zone)); - - LOCK_ZONE(zone); - - remote.magic = DNS_REMOTE_MAGIC; - remote.addresses = addresses; - remote.sources = sources; - remote.keynames = keynames; - remote.tlsnames = tlsnames; - remote.addrcnt = count; - - if (dns_remote_equal(&zone->alsonotify, &remote)) { - goto unlock; - } - - dns_remote_clear(&zone->alsonotify); - - /* - * If count == 0, don't allocate any space for servers to notify. - */ - if (count == 0) { - goto unlock; - } - - /* - * Now set up the notify address and key lists. - */ - dns_remote_init(&zone->alsonotify, count, addresses, sources, keynames, - tlsnames, true, zone->mctx); - -unlock: + *notifysrc = zone->notifysoa.notifysrc6; + *notifysrc = zone->notifycds.notifysrc6; UNLOCK_ZONE(zone); } @@ -6439,57 +6422,81 @@ report_no_active_addresses(dns_zone_t *zone, isc_sockaddr_t *addresses, } } -void -dns_zone_setprimaries(dns_zone_t *zone, isc_sockaddr_t *addresses, - isc_sockaddr_t *sources, dns_name_t **keynames, - dns_name_t **tlsnames, uint32_t count) { - dns_remote_t remote; +static void +setremote(dns_zone_t *zone, dns_remote_t *remote, const char *remotestr, + isc_sockaddr_t *addresses, isc_sockaddr_t *sources, + dns_name_t **keynames, dns_name_t **tlsnames, bool refresh, + bool report, uint32_t count) { + dns_remote_t newremote; REQUIRE(DNS_ZONE_VALID(zone)); + REQUIRE(DNS_REMOTE_VALID(remote)); - LOCK_ZONE(zone); + newremote.magic = DNS_REMOTE_MAGIC; + newremote.addresses = addresses; + newremote.sources = sources; + newremote.keynames = keynames; + newremote.tlsnames = tlsnames; + newremote.addrcnt = count; - remote.magic = DNS_REMOTE_MAGIC; - remote.addresses = addresses; - remote.sources = sources; - remote.keynames = keynames; - remote.tlsnames = tlsnames; - remote.addrcnt = count; + if (dns_remote_equal(remote, &newremote)) { + return; + } /* - * The refresh code assumes that 'primaries' wouldn't change under it. + * The refresh code assumes that 'servers' wouldn't change under it. * If it will change then kill off any current refresh in progress * and update the primaries info. If it won't change then we can just * unlock and exit. */ - if (!dns_remote_equal(&zone->primaries, &remote)) { - if (zone->request != NULL) { - dns_request_cancel(zone->request); - } - } else { - goto unlock; + if (zone->request != NULL && refresh) { + dns_request_cancel(zone->request); } - dns_remote_clear(&zone->primaries); + dns_remote_clear(remote); /* - * If count == 0, don't allocate any space for primaries. + * If count == 0, don't allocate any space for servers. */ if (count == 0) { - goto unlock; + return; } - report_no_active_addresses(zone, addresses, count, "primaries"); - /* - * Now set up the primaries and primary key lists. + * Now set up the address and key lists. */ - dns_remote_init(&zone->primaries, count, addresses, sources, keynames, - tlsnames, true, zone->mctx); + if (report) { + report_no_active_addresses(zone, addresses, count, remotestr); + } + dns_remote_init(remote, count, addresses, sources, keynames, tlsnames, + true, zone->mctx); +} + +void +dns_zone_setalsonotify(dns_zone_t *zone, isc_sockaddr_t *addresses, + isc_sockaddr_t *sources, dns_name_t **keynames, + dns_name_t **tlsnames, uint32_t count) { + bool refresh = false; + bool report = false; + + LOCK_ZONE(zone); + setremote(zone, &zone->alsonotify, "also-notify", addresses, sources, + keynames, tlsnames, refresh, report, count); + UNLOCK_ZONE(zone); +} + +void +dns_zone_setprimaries(dns_zone_t *zone, isc_sockaddr_t *addresses, + isc_sockaddr_t *sources, dns_name_t **keynames, + dns_name_t **tlsnames, uint32_t count) { + bool refresh = true; + bool report = true; + + LOCK_ZONE(zone); + setremote(zone, &zone->primaries, "primaries", addresses, sources, + keynames, tlsnames, refresh, report, count); DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NOPRIMARIES); - -unlock: UNLOCK_ZONE(zone); } @@ -6497,43 +6504,25 @@ void dns_zone_setparentals(dns_zone_t *zone, isc_sockaddr_t *addresses, isc_sockaddr_t *sources, dns_name_t **keynames, dns_name_t **tlsnames, uint32_t count) { - dns_remote_t remote; - - REQUIRE(DNS_ZONE_VALID(zone)); + bool refresh = false; + bool report = true; LOCK_ZONE(zone); + setremote(zone, &zone->parentals, "parental-agents", addresses, sources, + keynames, tlsnames, refresh, report, count); + UNLOCK_ZONE(zone); +} - remote.magic = DNS_REMOTE_MAGIC; - remote.addresses = addresses; - remote.sources = sources; - remote.keynames = keynames; - remote.tlsnames = tlsnames; - remote.addrcnt = count; +void +dns_zone_setcdsendpoints(dns_zone_t *zone, isc_sockaddr_t *addresses, + isc_sockaddr_t *sources, dns_name_t **keynames, + dns_name_t **tlsnames, uint32_t count) { + bool refresh = false; + bool report = false; - if (dns_remote_equal(&zone->parentals, &remote)) { - goto unlock; - } - - dns_remote_clear(&zone->parentals); - - /* - * If count == 0, don't allocate any space for parentals. - */ - if (count == 0) { - goto unlock; - } - - report_no_active_addresses(zone, addresses, count, "parental-agents"); - - /* - * Now set up the parentals and parental key lists. - */ - dns_remote_init(&zone->parentals, count, addresses, sources, keynames, - tlsnames, true, zone->mctx); - - dns_zone_log(zone, ISC_LOG_NOTICE, "checkds: set %u parentals", count); - -unlock: + LOCK_ZONE(zone); + setremote(zone, &zone->cds_endpoints, "cds-endpoints", addresses, + sources, keynames, tlsnames, refresh, report, count); UNLOCK_ZONE(zone); } @@ -10498,12 +10487,14 @@ revocable(dns_zonefetch_t *fetch, dns_rdata_keydata_t *keydata) { /* * Fetch DNSKEY records at the trust anchor name. */ -static void +static isc_result_t keyfetch_start(dns_zonefetch_t *fetch) { REQUIRE(fetch->fetchtype == ZONEFETCHTYPE_KEY); fetch->qname = dns_fixedname_name(&fetch->name); fetch->qtype = dns_rdatatype_dnskey; + + return ISC_R_SUCCESS; } static void @@ -10519,12 +10510,12 @@ keyfetch_cancel(dns_zonefetch_t *fetch) { dns_zone_t *zone; REQUIRE(fetch->fetchtype == ZONEFETCHTYPE_KEY); + REQUIRE(DNS_ZONE_VALID(fetch->zone)); + REQUIRE(LOCKED_ZONE(fetch->zone)); kfetch = &fetch->fetchdata.keyfetch; zone = fetch->zone; - INSIST(LOCKED_ZONE(zone)); - /* * Error during a key fetch; cancel and retry in an hour. */ @@ -10597,6 +10588,8 @@ keyfetch_done(dns_zonefetch_t *fetch, isc_result_t eresult) { REQUIRE(fetch != NULL); REQUIRE(fetch->fetchtype == ZONEFETCHTYPE_KEY); + REQUIRE(DNS_ZONE_VALID(fetch->zone)); + REQUIRE(LOCKED_ZONE(fetch->zone)); kfetch = &fetch->fetchdata.keyfetch; zone = fetch->zone; @@ -12463,7 +12456,7 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) { DNS_ZONEFLG_NEEDSTARTUPNOTIFY | DNS_ZONEFLG_NOTIFYNODEFER | DNS_ZONEFLG_NOTIFYDEFERRED); - notifytype = zone->notifyctx.notifytype; + notifytype = zone->notifysoa.notifytype; DNS_ZONE_TIME_ADD(now, zone->notifydelay, &zone->notifytime); UNLOCK_ZONE(zone); @@ -12582,7 +12575,8 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) { goto next; } - if (dns_notify_isqueued(&zone->notifyctx, flags, NULL, &dst, + if (dns_notify_isqueued(&zone->notifysoa, dns_rdatatype_soa, + zone->view->dstport, flags, NULL, &dst, key, transport)) { if (key != NULL) { @@ -12594,7 +12588,8 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) { goto next; } - dns_notify_create(zone->mctx, flags, ¬ify); + dns_notify_create(zone->mctx, dns_rdatatype_soa, + zone->view->dstport, flags, ¬ify); zone_iattach(zone, ¬ify->zone); notify->src = src; notify->dst = dst; @@ -12612,7 +12607,7 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) { transport = NULL; } - ISC_LIST_APPEND(zone->notifyctx.notifies, notify, link); + ISC_LIST_APPEND(zone->notifysoa.notifies, notify, link); result = dns_notify_queue(notify, startup); if (result != ISC_R_SUCCESS) { dns_notify_destroy(notify, true); @@ -12671,17 +12666,19 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) { } LOCK_ZONE(zone); - isqueued = dns_notify_isqueued(&zone->notifyctx, flags, - &ns.name, NULL, NULL, NULL); + isqueued = dns_notify_isqueued( + &zone->notifysoa, dns_rdatatype_soa, + zone->view->dstport, flags, &ns.name, NULL, NULL, NULL); UNLOCK_ZONE(zone); if (isqueued) { continue; } - dns_notify_create(zone->mctx, flags, ¬ify); + dns_notify_create(zone->mctx, dns_rdatatype_soa, + zone->view->dstport, flags, ¬ify); dns_zone_iattach(zone, ¬ify->zone); dns_name_dup(&ns.name, zone->mctx, ¬ify->ns); LOCK_ZONE(zone); - ISC_LIST_APPEND(zone->notifyctx.notifies, notify, link); + ISC_LIST_APPEND(zone->notifysoa.notifies, notify, link); UNLOCK_ZONE(zone); dns_notify_find_address(notify); } @@ -14609,7 +14606,8 @@ zone_shutdown(void *arg) { checkds_cancel(zone); - dns_notify_cancel(&zone->notifyctx); + dns_notify_cancel(&zone->notifysoa); + dns_notify_cancel(&zone->notifycds); forward_cancel(zone); @@ -15000,13 +14998,13 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from, /* * Accept notify requests from non primaries if they are on - * 'zone->notifyctx.notify_acl'. + * 'zone->notifysoa.notify_acl'. */ tsigkey = dns_message_gettsigkey(msg); tsig = dns_tsigkey_identity(tsigkey); if (i >= dns_remote_count(&zone->primaries) && - zone->notifyctx.notify_acl != NULL && - (dns_acl_match(&netaddr, tsig, zone->notifyctx.notify_acl, + zone->notifysoa.notify_acl != NULL && + (dns_acl_match(&netaddr, tsig, zone->notifysoa.notify_acl, zone->view->aclenv, &match, NULL) == ISC_R_SUCCESS) && match > 0) @@ -15070,7 +15068,7 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from, */ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_REFRESH)) { DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NEEDREFRESH); - zone->notifyctx.notifyfrom = *from; + zone->notifysoa.notifyfrom = *from; UNLOCK_ZONE(zone); if (have_serial) { dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, @@ -15096,7 +15094,7 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from, dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_INFO, "notify from %s: no serial", fromtext); } - zone->notifyctx.notifyfrom = *from; + zone->notifysoa.notifyfrom = *from; UNLOCK_ZONE(zone); if (to != NULL) { @@ -15110,11 +15108,11 @@ void dns_zone_setnotifyacl(dns_zone_t *zone, dns_acl_t *acl) { REQUIRE(DNS_ZONE_VALID(zone)); + dns_zone_clearnotifyacl(zone); + LOCK_ZONE(zone); - if (zone->notifyctx.notify_acl != NULL) { - dns_acl_detach(&zone->notifyctx.notify_acl); - } - dns_acl_attach(acl, &zone->notifyctx.notify_acl); + dns_acl_attach(acl, &zone->notifysoa.notify_acl); + dns_acl_attach(acl, &zone->notifycds.notify_acl); UNLOCK_ZONE(zone); } @@ -15240,8 +15238,11 @@ dns_zone_clearnotifyacl(dns_zone_t *zone) { REQUIRE(DNS_ZONE_VALID(zone)); LOCK_ZONE(zone); - if (zone->notifyctx.notify_acl != NULL) { - dns_acl_detach(&zone->notifyctx.notify_acl); + if (zone->notifysoa.notify_acl != NULL) { + dns_acl_detach(&zone->notifysoa.notify_acl); + } + if (zone->notifycds.notify_acl != NULL) { + dns_acl_detach(&zone->notifycds.notify_acl); } UNLOCK_ZONE(zone); } @@ -15663,10 +15664,18 @@ dns_zone_getrdclass(dns_zone_t *zone) { } dns_notifyctx_t * -dns__zone_getnotifyctx(dns_zone_t *zone) { +dns__zone_getnotifyctx(dns_zone_t *zone, dns_rdatatype_t type) { REQUIRE(DNS_ZONE_VALID(zone)); - return &zone->notifyctx; + switch (type) { + case dns_rdatatype_soa: + return &zone->notifysoa; + case dns_rdatatype_cds: + return &zone->notifycds; + default: + UNREACHABLE(); + } + return NULL; } void @@ -20856,7 +20865,7 @@ checkds_send(dns_zone_t *zone) { /* * Fetch NS records from parent zone. */ -static void +static isc_result_t nsfetch_start(dns_zonefetch_t *fetch) { dns_nsfetch_t *nsfetch; unsigned int nlabels = 1; @@ -20872,6 +20881,8 @@ nsfetch_start(dns_zonefetch_t *fetch) { fetch->qtype = dns_rdatatype_ns; fetch->qname = &nsfetch->pname; + + return ISC_R_SUCCESS; } /* @@ -20910,11 +20921,11 @@ nsfetch_cancel(dns_zonefetch_t *fetch) { dns_zone_t *zone; REQUIRE(fetch->fetchtype == ZONEFETCHTYPE_NS); + REQUIRE(DNS_ZONE_VALID(fetch->zone)); + REQUIRE(LOCKED_ZONE(fetch->zone)); zone = fetch->zone; - INSIST(LOCKED_ZONE(zone)); - zone->fetchcount[ZONEFETCHTYPE_NS]--; } @@ -20929,7 +20940,7 @@ nsfetch_cleanup(dns_zonefetch_t *fetch) { * agents. */ static isc_result_t -nsfetch_done(dns_zonefetch_t *fetch, isc_result_t eresult) { +nsfetch_checkds(dns_zonefetch_t *fetch, isc_result_t eresult) { dns_nsfetch_t *nsfetch; isc_result_t result = ISC_R_NOMORE; dns_zone_t *zone = NULL; @@ -20939,6 +20950,8 @@ nsfetch_done(dns_zonefetch_t *fetch, isc_result_t eresult) { REQUIRE(fetch != NULL); REQUIRE(fetch->fetchtype == ZONEFETCHTYPE_NS); + REQUIRE(DNS_ZONE_VALID(fetch->zone)); + REQUIRE(LOCKED_ZONE(fetch->zone)); nsfetch = &fetch->fetchdata.nsfetch; zone = fetch->zone; @@ -20949,7 +20962,7 @@ nsfetch_done(dns_zonefetch_t *fetch, isc_result_t eresult) { dns_name_format(pname, pnamebuf, sizeof(pnamebuf)); dnssec_log(zone, ISC_LOG_DEBUG(3), - "Returned from '%s' NS fetch in nsfetch_done(): %s", + "Returned from '%s' NS fetch in nsfetch_checkds(): %s", pnamebuf, isc_result_totext(eresult)); if (eresult == DNS_R_NCACHENXRRSET || eresult == DNS_R_NXRRSET) { @@ -21090,7 +21103,7 @@ zone_checkds(dns_zone_t *zone) { .continue_fetch = nsfetch_continue, .cancel_fetch = nsfetch_cancel, .cleanup_fetch = nsfetch_cleanup, - .done_fetch = nsfetch_done, + .done_fetch = nsfetch_checkds, }, }; isc_mem_attach(zone->mctx, &fetch->mctx); @@ -21115,6 +21128,374 @@ zone_checkds(dns_zone_t *zone) { #endif /* ifdef ENABLE_AFL */ } +static isc_result_t +dsyncfetch_start(dns_zonefetch_t *fetch) { + dns_dsyncfetch_t *dsyncfetch; + dns_zone_t *zone; + dns_fixedname_t fndl, fnp; + dns_name_t *name, *dsyncname, *dsynclabel, *prefix; + unsigned int nlabels; + isc_result_t result; + + REQUIRE(fetch->fetchtype == ZONEFETCHTYPE_DSYNC); + REQUIRE(DNS_ZONE_VALID(fetch->zone)); + + dsyncfetch = &fetch->fetchdata.dsyncfetch; + zone = fetch->zone; + + /* + * The dsync owner name is build up of ._dsync.. + * The prefix is the relative domain name of the child consisting of + * the labels under the zonecut. + */ + dsyncname = dns_fixedname_initname(&dsyncfetch->fname); + dsynclabel = dns_fixedname_initname(&fndl); + dns_name_fromstring(dsynclabel, "_dsync", NULL, 0, fetch->mctx); + result = dns_name_concatenate(dsynclabel, &dsyncfetch->pname, + dsyncname); + if (result != ISC_R_SUCCESS) { + dnssec_log(zone, ISC_LOG_ERROR, + "dsyncfetch: failed to create parent DSYNC fetch " + "(parent part): %s", + isc_result_totext(result)); + return result; + } + + name = dns_fixedname_name(&fetch->name); + nlabels = dns_name_countlabels(&dsyncfetch->pname); + prefix = dns_fixedname_initname(&fnp); + dns_name_split(name, nlabels, prefix, NULL); + result = dns_name_concatenate(prefix, dsyncname, dsyncname); + if (result != ISC_R_SUCCESS) { + dnssec_log(zone, ISC_LOG_ERROR, + "dsyncfetch: failed to create parent DSYNC fetch " + "(child part): %s", + isc_result_totext(result)); + return result; + } + + dns_name_init(&dsyncfetch->dsyncname); + dns_name_clone(dsyncname, &dsyncfetch->dsyncname); + + fetch->qtype = dns_rdatatype_dsync; + fetch->qname = &dsyncfetch->dsyncname; + + return ISC_R_SUCCESS; +} + +/* + * Retry an DSYNC RRset lookup. + */ +static void +dsyncfetch_continue(dns_zonefetch_t *fetch) { + dns_zone_t *zone; + + REQUIRE(fetch->fetchtype == ZONEFETCHTYPE_DSYNC); + REQUIRE(DNS_ZONE_VALID(fetch->zone)); + + zone = fetch->zone; + +#ifdef ENABLE_AFL + if (!dns_fuzzing_resolver) { +#endif /* ifdef ENABLE_AFL */ + LOCK_ZONE(zone); + zone->fetchcount[ZONEFETCHTYPE_DSYNC]++; + + dns_zonefetch_reschedule(fetch); + + if (isc_log_wouldlog(ISC_LOG_DEBUG(3))) { + dnssec_log(zone, ISC_LOG_DEBUG(3), + "Creating parent DSYNC fetch in " + "dsyncfetch_continue()"); + } + UNLOCK_ZONE(zone); +#ifdef ENABLE_AFL + } +#endif /* ifdef ENABLE_AFL */ +} + +static void +dsyncfetch_cancel(dns_zonefetch_t *fetch) { + dns_zone_t *zone; + + REQUIRE(fetch->fetchtype == ZONEFETCHTYPE_DSYNC); + REQUIRE(DNS_ZONE_VALID(fetch->zone)); + REQUIRE(LOCKED_ZONE(fetch->zone)); + + zone = fetch->zone; + + zone->fetchcount[ZONEFETCHTYPE_DSYNC]--; +} + +static void +dsyncfetch_cleanup(dns_zonefetch_t *fetch) { + REQUIRE(fetch->fetchtype == ZONEFETCHTYPE_DSYNC); +} + +/* + * A DSYNC RRset has been fetched; scan the RRset and start sending + * NOTIFY(CDS) queries to them. + */ +static isc_result_t +dsyncfetch_done(dns_zonefetch_t *fetch, isc_result_t eresult) { + dns_dsyncfetch_t *dsyncfetch; + isc_result_t result = ISC_R_NOMORE; + dns_notify_t *notify = NULL; + dns_zone_t *zone = NULL; + dns_name_t *dsyncname = NULL; + char dsyncnamebuf[DNS_NAME_FORMATSIZE]; + dns_rdataset_t *rrset = NULL; + + REQUIRE(fetch != NULL); + REQUIRE(fetch->fetchtype == ZONEFETCHTYPE_DSYNC); + REQUIRE(DNS_ZONE_VALID(fetch->zone)); + REQUIRE(LOCKED_ZONE(fetch->zone)); + + dsyncfetch = &fetch->fetchdata.dsyncfetch; + zone = fetch->zone; + rrset = &fetch->rrset; + dsyncname = &dsyncfetch->dsyncname; + + zone->fetchcount[ZONEFETCHTYPE_DSYNC]--; + + dns_name_format(dsyncname, dsyncnamebuf, sizeof(dsyncnamebuf)); + dns_zone_log(zone, ISC_LOG_DEBUG(3), + "dsyncfetch: Returned from '%s' DSYNC fetch in " + "dsyncfetch_done(): %s", + dsyncnamebuf, isc_result_totext(eresult)); + + result = dns_zonefetch_verify(fetch, eresult, dns_trust_secure); + if (result != ISC_R_SUCCESS) { + goto done; + } + + UNLOCK_ZONE(zone); + + /* Notify targets. */ + dns_rdata_dsync_t dsync; + unsigned int count = 0; + for (result = dns_rdataset_first(rrset); result == ISC_R_SUCCESS; + result = dns_rdataset_next(rrset)) + { + dns_rdata_t rdata = DNS_RDATA_INIT; + + dns_rdataset_current(rrset, &rdata); + result = dns_rdata_tostruct(&rdata, &dsync, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + dns_rdata_reset(&rdata); + if (dsync.scheme != DNS_DSYNCSCHEME_NOTIFY) { + dns_zone_log(zone, ISC_LOG_DEBUG(1), + "dsyncfetch: unsupported DSYNC scheme %u, " + "ignoring", + dsync.scheme); + continue; + } + + if (dsync.type != dns_rdatatype_cds) { + char typebuf[DNS_RDATATYPE_FORMATSIZE]; + dns_rdatatype_format(dsync.type, typebuf, + sizeof(typebuf)); + + dns_zone_log(zone, ISC_LOG_DEBUG(1), + "dsyncfetch: DSYNC RRtype %s not " + "supported, ignoring", + result == ISC_R_SUCCESS ? typebuf + : "UNKNOWN"); + continue; + } + + count++; + if (count > 1) { + dns_zone_log(zone, ISC_LOG_WARNING, + "dsyncfetch: multiple DSYNC records " + "matching NOTIFY scheme and CDS RRtype, " + "dropping response"); + result = DNS_R_INVALIDDSYNC; + break; + } + } + + LOCK_ZONE(zone); + + if (result == ISC_R_NOMORE) { + result = ISC_R_SUCCESS; + } else { + goto done; + } + + bool isqueued = dns_notify_isqueued(&zone->notifycds, dns_rdatatype_cds, + dsync.port, 0, &dsync.target, NULL, + NULL, NULL); + + UNLOCK_ZONE(zone); + + if (!isqueued) { + dns_notify_create(zone->mctx, dns_rdatatype_cds, dsync.port, + DNS_NOTIFY_NOSOA, ¬ify); + if (isc_log_wouldlog(ISC_LOG_DEBUG(3))) { + char tbuf[DNS_NAME_FORMATSIZE]; + dns_name_format(&dsync.target, tbuf, sizeof(tbuf)); + dns_zone_log(zone, ISC_LOG_DEBUG(3), + "dsyncfetch: send NOTIFY(CDS) query to %s", + tbuf); + } + dns_zone_iattach(zone, ¬ify->zone); + dns_name_dup(&dsync.target, zone->mctx, ¬ify->ns); + LOCK_ZONE(zone); + ISC_LIST_APPEND(zone->notifycds.notifies, notify, link); + UNLOCK_ZONE(zone); + dns_notify_find_address(notify); + } + + LOCK_ZONE(zone); + +done: + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_DEBUG(3), + "dsyncfetch: error processing DSYNC RRset: %s", + isc_result_totext(result)); + } + + return result; +} + +/* + * An NS RRset has been fetched from the parent of a zone whose DSYNC RRset + * needs to be queried; scan the RRset and start resolving those queries. + */ +static isc_result_t +nsfetch_dsync(dns_zonefetch_t *fetch, isc_result_t eresult) { + dns_nsfetch_t *nsfetch; + isc_result_t result = ISC_R_NOMORE; + dns_zone_t *zone = NULL; + dns_name_t *pname = NULL; + char pnamebuf[DNS_NAME_FORMATSIZE]; + + REQUIRE(fetch != NULL); + REQUIRE(fetch->fetchtype == ZONEFETCHTYPE_NS); + REQUIRE(DNS_ZONE_VALID(fetch->zone)); + REQUIRE(LOCKED_ZONE(fetch->zone)); + + nsfetch = &fetch->fetchdata.nsfetch; + zone = fetch->zone; + pname = &nsfetch->pname; + + zone->fetchcount[ZONEFETCHTYPE_NS]--; + + dns_name_format(pname, pnamebuf, sizeof(pnamebuf)); + dnssec_log(zone, ISC_LOG_DEBUG(3), + "Returned from '%s' NS fetch in nsfetch_dsync(): %s", + pnamebuf, isc_result_totext(eresult)); + + if (eresult == DNS_R_NCACHENXRRSET || eresult == DNS_R_NXRRSET) { + dnssec_log(zone, ISC_LOG_DEBUG(3), + "NODATA response for NS '%s', level up", pnamebuf); + return DNS_R_CONTINUE; + } + + result = dns_zonefetch_verify(fetch, eresult, dns_trust_secure); + if (result != ISC_R_SUCCESS) { + goto done; + } + +#ifdef ENABLE_AFL + if (!dns_fuzzing_resolver) { +#endif /* ifdef ENABLE_AFL */ + dns_zonefetch_t *zfetch = NULL; + dns_dsyncfetch_t *dsyncfetch; + + zfetch = isc_mem_get(zone->mctx, sizeof(dns_zonefetch_t)); + *zfetch = (dns_zonefetch_t){ + .zone = zone, + .options = DNS_FETCHOPT_UNSHARED | + DNS_FETCHOPT_NOCACHED, + .fetchtype = ZONEFETCHTYPE_DSYNC, + .fetchmethods = + (dns_zonefetch_methods_t){ + .start_fetch = dsyncfetch_start, + .continue_fetch = dsyncfetch_continue, + .cancel_fetch = dsyncfetch_cancel, + .cleanup_fetch = dsyncfetch_cleanup, + .done_fetch = dsyncfetch_done, + }, + }; + isc_mem_attach(zone->mctx, &zfetch->mctx); + + zone->fetchcount[ZONEFETCHTYPE_DSYNC]++; + + dsyncfetch = &zfetch->fetchdata.dsyncfetch; + dns_name_init(&dsyncfetch->pname); + dns_name_clone(pname, &dsyncfetch->pname); + + dns_zonefetch_schedule(zfetch, &zone->origin); + + if (isc_log_wouldlog(ISC_LOG_DEBUG(3))) { + dnssec_log(zone, ISC_LOG_DEBUG(3), + "Creating parent DSYNC fetch in " + "nsfetch_dsync()"); + } +#ifdef ENABLE_AFL + } +#endif /* ifdef ENABLE_AFL */ + +done: + return result; +} + +static void +zone_notifycds(dns_zone_t *zone) { + dns_notifytype_t notifytype = zone->notifycds.notifytype; + + if (notifytype == dns_notifytype_no) { + return; + } + + INSIST(notifytype == dns_notifytype_yes); + +#ifdef ENABLE_AFL + if (!dns_fuzzing_resolver) { +#endif /* ifdef ENABLE_AFL */ + dns_zonefetch_t *fetch = NULL; + dns_nsfetch_t *nsfetch; + + fetch = isc_mem_get(zone->mctx, sizeof(dns_zonefetch_t)); + *fetch = (dns_zonefetch_t){ + .zone = zone, + .options = DNS_FETCHOPT_UNSHARED | + DNS_FETCHOPT_NOCACHED, + .fetchtype = ZONEFETCHTYPE_NS, + .fetchmethods = + (dns_zonefetch_methods_t){ + .start_fetch = nsfetch_start, + .continue_fetch = nsfetch_continue, + .cancel_fetch = nsfetch_cancel, + .cleanup_fetch = nsfetch_cleanup, + .done_fetch = nsfetch_dsync, + }, + }; + isc_mem_attach(zone->mctx, &fetch->mctx); + + LOCK_ZONE(zone); + zone->fetchcount[ZONEFETCHTYPE_NS]++; + + nsfetch = &fetch->fetchdata.nsfetch; + dns_name_init(&nsfetch->pname); + dns_name_clone(&zone->origin, &nsfetch->pname); + + dns_zonefetch_schedule(fetch, &zone->origin); + + if (isc_log_wouldlog(ISC_LOG_DEBUG(3))) { + dnssec_log( + zone, ISC_LOG_DEBUG(3), + "Creating parent NS fetch in zone_notifyds()"); + } + UNLOCK_ZONE(zone); +#ifdef ENABLE_AFL + } +#endif /* ifdef ENABLE_AFL */ +} + static void update_ttl(dns_rdataset_t *rdataset, dns_name_t *name, dns_ttl_t ttl, dns_diff_t *diff) { @@ -21260,6 +21641,7 @@ zone_rekey(dns_zone_t *zone) { bool commit = false, newactive = false; bool newalg = false; bool fullsign; + bool notifycds = false; bool offlineksk = false; bool kasp_change = false; uint8_t options = 0; @@ -21624,7 +22006,9 @@ zone_rekey(dns_zone_t *zone) { result = dns_dnssec_syncupdate(&dnskeys, &rmkeys, &cdsset, &cdnskeyset, now, &digests, cdnskeypub, ttl, &diff, mctx); - if (result != ISC_R_SUCCESS) { + if (result == ISC_R_SUCCESS) { + notifycds = true; + } else if (result != DNS_R_UNCHANGED) { dnssec_log(zone, ISC_LOG_ERROR, "zone_rekey:couldn't update CDS/CDNSKEY: %s", isc_result_totext(result)); @@ -21660,7 +22044,9 @@ zone_rekey(dns_zone_t *zone) { result = dns_dnssec_syncdelete( &cdsset, &cdnskeyset, &zone->origin, zone->rdclass, ttl, &diff, mctx, cdsdel, cdnskeydel); - if (result != ISC_R_SUCCESS) { + if (result == ISC_R_SUCCESS) { + notifycds = true; + } else if (result != DNS_R_UNCHANGED) { dnssec_log(zone, ISC_LOG_ERROR, "zone_rekey:couldn't update CDS/CDNSKEY " "DELETE records: %s", @@ -21975,6 +22361,13 @@ zone_rekey(dns_zone_t *zone) { ISC_LIST_APPEND(zone->keyring, key, link); } + /* + * If the CDS/CDNSKEY RRset has changed, send NOTIFY(CDS) to endpoints. + */ + if (notifycds) { + zone_notifycds(zone); + } + result = ISC_R_SUCCESS; cleanup: diff --git a/lib/dns/zone_p.h b/lib/dns/zone_p.h index 9a7d3481fa..158ee0d8a8 100644 --- a/lib/dns/zone_p.h +++ b/lib/dns/zone_p.h @@ -108,7 +108,7 @@ dns__zone_stats_increment(dns_zone_t *zone, isc_statscounter_t counter); */ dns_notifyctx_t * -dns__zone_getnotifyctx(dns_zone_t *zone); +dns__zone_getnotifyctx(dns_zone_t *zone, dns_rdatatype_t type); /*%< * Returns the notify context. * diff --git a/lib/dns/zonefetch.c b/lib/dns/zonefetch.c index 27319cc9ef..d8a5df9b18 100644 --- a/lib/dns/zonefetch.c +++ b/lib/dns/zonefetch.c @@ -43,7 +43,10 @@ dns_zonefetch_run(void *arg) { INSIST(view != NULL); INSIST(loop != NULL); - fetch->fetchmethods.start_fetch(fetch); + result = fetch->fetchmethods.start_fetch(fetch); + if (result != ISC_R_SUCCESS) { + goto cancel; + } result = dns_view_getresolver(view, &resolver); if (result != ISC_R_SUCCESS) { diff --git a/lib/isc/include/isc/result.h b/lib/isc/include/isc/result.h index 34f08a7f74..3c5d6a75d7 100644 --- a/lib/isc/include/isc/result.h +++ b/lib/isc/include/isc/result.h @@ -209,6 +209,7 @@ typedef enum isc_result { DNS_R_NOSKRFILE, DNS_R_NOSKRBUNDLE, DNS_R_LOOPDETECTED, + DNS_R_INVALIDDSYNC, DST_R_UNSUPPORTEDALG, DST_R_CRYPTOFAILURE, diff --git a/lib/isc/result.c b/lib/isc/result.c index de78e4422c..f362d812f7 100644 --- a/lib/isc/result.c +++ b/lib/isc/result.c @@ -212,6 +212,7 @@ static const char *description[ISC_R_NRESULTS] = { [DNS_R_NOSKRFILE] = "no SKR file", [DNS_R_NOSKRBUNDLE] = "no available SKR bundle", [DNS_R_LOOPDETECTED] = "fetch loop detected", + [DNS_R_INVALIDDSYNC] = "invalid DSYNC response", [DST_R_UNSUPPORTEDALG] = "algorithm is unsupported", [DST_R_CRYPTOFAILURE] = "crypto failure", @@ -445,6 +446,7 @@ static const char *identifier[ISC_R_NRESULTS] = { [DNS_R_NOSKRFILE] = "DNS_R_NOSKRFILE", [DNS_R_NOSKRBUNDLE] = "DNS_R_NOSKRBUNDLE", [DNS_R_LOOPDETECTED] = "DNS_R_LOOPDETECTED", + [DNS_R_INVALIDDSYNC] = "DNS_R_INVALIDDSYNC", [DST_R_UNSUPPORTEDALG] = "DST_R_UNSUPPORTEDALG", [DST_R_CRYPTOFAILURE] = "DST_R_CRYPTOFAILURE", diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 90fc49a74e..f50c20d31e 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -2685,6 +2685,8 @@ static cfg_clausedef_t zone_clauses[] = { CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB, NULL }, { "notify", &cfg_type_notifytype, CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR, NULL }, + { "notify-cds", &cfg_type_boolean, + CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR, NULL }, { "notify-defer", &cfg_type_uint32, CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR, NULL }, { "notify-delay", &cfg_type_uint32,