From: Aram Sargsyan Date: Fri, 7 Jun 2024 14:47:55 +0000 (+0000) Subject: Implement the 'request-ixfr-max-diffs' configuration option X-Git-Tag: v9.21.1~18^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c05a823e8b458121fab69e464e102f91cdc180b1;p=thirdparty%2Fbind9.git Implement the 'request-ixfr-max-diffs' configuration option This limits the maximum number of received incremental zone transfer differences for a secondary server. Upon reaching the confgiured limit, the secondary aborts IXFR and initiates a full zone transfer (AXFR). --- diff --git a/bin/named/config.c b/bin/named/config.c index 59f8902156e..8d962d78149 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -190,6 +190,7 @@ options {\n\ recursion true;\n\ request-expire true;\n\ request-ixfr true;\n\ + request-ixfr-max-diffs 0;\n\ require-server-cookie no;\n\ root-key-sentinel yes;\n\ servfail-ttl 1;\n\ diff --git a/bin/named/server.c b/bin/named/server.c index 57d1f9abf66..2156394dc48 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -1469,6 +1469,13 @@ configure_peer(const cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) { CHECK(dns_peer_setrequestixfr(peer, cfg_obj_asboolean(obj))); } + obj = NULL; + (void)cfg_map_get(cpeer, "request-ixfr-max-diffs", &obj); + if (obj != NULL) { + CHECK(dns_peer_setrequestixfrmaxdiffs(peer, + cfg_obj_asuint32(obj))); + } + obj = NULL; (void)cfg_map_get(cpeer, "request-nsid", &obj); if (obj != NULL) { diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index a78adae58a7..8129b01b176 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -1418,6 +1418,11 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, INSIST(result == ISC_R_SUCCESS); dns_zone_setrequestixfr(zone, cfg_obj_asboolean(obj)); + obj = NULL; + result = named_config_get(maps, "request-ixfr-max-diffs", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_zone_setrequestixfrmaxdiffs(zone, cfg_obj_asuint32(obj)); + obj = NULL; checknames(ztype, maps, &obj); INSIST(obj != NULL); diff --git a/bin/tests/system/checkconf/bad-master-request-ixfr-max-diffs.conf b/bin/tests/system/checkconf/bad-master-request-ixfr-max-diffs.conf new file mode 100644 index 00000000000..2fa30b62d01 --- /dev/null +++ b/bin/tests/system/checkconf/bad-master-request-ixfr-max-diffs.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. + */ + +/* + * request-ixfr clause is not allowed in zone of type primary. + */ + +zone dummy { + type primary; + request-ixfr-max-diffs 100; + file "xxxx"; +}; diff --git a/bin/tests/system/checkconf/good-server-christmas-tree.conf.in b/bin/tests/system/checkconf/good-server-christmas-tree.conf.in index f619913bee2..2eceee6baf6 100644 --- a/bin/tests/system/checkconf/good-server-christmas-tree.conf.in +++ b/bin/tests/system/checkconf/good-server-christmas-tree.conf.in @@ -29,6 +29,7 @@ server 0.0.0.0 { query-source 0.0.0.0; request-expire no; request-ixfr no; + request-ixfr-max-diffs 0; request-nsid no; require-cookie no; send-cookie no; @@ -52,6 +53,7 @@ server :: { query-source-v6 ::; request-expire no; request-ixfr no; + request-ixfr-max-diffs 0; request-nsid no; require-cookie no; send-cookie no; diff --git a/doc/misc/mirror.zoneopt b/doc/misc/mirror.zoneopt index e7cb0b9ccbf..72e90083bb8 100644 --- a/doc/misc/mirror.zoneopt +++ b/doc/misc/mirror.zoneopt @@ -34,6 +34,7 @@ zone [ ] { primaries [ port ] [ source ( | * ) ] [ source-v6 ( | * ) ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; request-expire ; request-ixfr ; + request-ixfr-max-diffs ; transfer-source ( | * ); transfer-source-v6 ( | * ); try-tcp-refresh ; diff --git a/doc/misc/options b/doc/misc/options index dabaad9b68e..2390a98a2ed 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -257,6 +257,7 @@ options { recursive-clients ; request-expire ; request-ixfr ; + request-ixfr-max-diffs ; request-nsid ; require-server-cookie ; resolver-query-timeout ; @@ -346,6 +347,7 @@ server { query-source-v6 [ address ] ( | * ); request-expire ; request-ixfr ; + request-ixfr-max-diffs ; request-nsid ; require-cookie ; send-cookie ; @@ -539,6 +541,7 @@ view [ ] { recursion ; request-expire ; request-ixfr ; + request-ixfr-max-diffs ; request-nsid ; require-server-cookie ; resolver-query-timeout ; @@ -564,6 +567,7 @@ view [ ] { query-source-v6 [ address ] ( | * ); request-expire ; request-ixfr ; + request-ixfr-max-diffs ; request-nsid ; require-cookie ; send-cookie ; diff --git a/doc/misc/secondary.zoneopt b/doc/misc/secondary.zoneopt index 610d32f2627..3a28af9d84f 100644 --- a/doc/misc/secondary.zoneopt +++ b/doc/misc/secondary.zoneopt @@ -51,6 +51,7 @@ zone [ ] { primaries [ port ] [ source ( | * ) ] [ source-v6 ( | * ) ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; request-expire ; request-ixfr ; + request-ixfr-max-diffs ; sig-signing-nodes ; sig-signing-signatures ; sig-signing-type ; diff --git a/lib/dns/include/dns/peer.h b/lib/dns/include/dns/peer.h index d32d0076a8b..d429d667d02 100644 --- a/lib/dns/include/dns/peer.h +++ b/lib/dns/include/dns/peer.h @@ -100,6 +100,12 @@ dns_peer_setrequestixfr(dns_peer_t *peer, bool newval); isc_result_t dns_peer_getrequestixfr(dns_peer_t *peer, bool *retval); +isc_result_t +dns_peer_setrequestixfrmaxdiffs(dns_peer_t *peer, uint32_t newval); + +isc_result_t +dns_peer_getrequestixfrmaxdiffs(dns_peer_t *peer, uint32_t *retval); + isc_result_t dns_peer_setprovideixfr(dns_peer_t *peer, bool newval); diff --git a/lib/dns/include/dns/xfrin.h b/lib/dns/include/dns/xfrin.h index d44b4401437..a024ce9ca3f 100644 --- a/lib/dns/include/dns/xfrin.h +++ b/lib/dns/include/dns/xfrin.h @@ -53,7 +53,7 @@ ISC_LANG_BEGINDECLS isc_result_t dns_xfrin_create(dns_zone_t *zone, dns_rdatatype_t xfrtype, - const isc_sockaddr_t *primaryaddr, + uint32_t ixfr_maxdiffs, const isc_sockaddr_t *primaryaddr, const isc_sockaddr_t *sourceaddr, dns_tsigkey_t *tsigkey, dns_transport_type_t soa_transport_type, dns_transport_t *transport, isc_tlsctx_cache_t *tlsctx_cache, diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 422448a1a72..57b55addb8b 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -2442,6 +2442,25 @@ dns_zone_setrequestixfr(dns_zone_t *zone, bool flag); * \li 'zone' to be valid. */ +bool +dns_zone_getrequestixfrmaxdiffs(dns_zone_t *zone); +/*% + * Returns the value of the request-ixfr-max-diffs option in the zone. + * + * Requires: + * \li 'zone' to be valid. + */ + +void +dns_zone_setrequestixfrmaxdiffs(dns_zone_t *zone, uint32_t maxmsgs); +/*% + * Sets the request-ixfr-max-diffs option for the zone. 0 means unlimited. The + * default value is determined by the setting of this option in the view. + * + * Requires: + * \li 'zone' to be valid. + */ + uint32_t dns_zone_getixfrratio(dns_zone_t *zone); /*% diff --git a/lib/dns/peer.c b/lib/dns/peer.c index c2f1d14dfae..41bd2426fa0 100644 --- a/lib/dns/peer.c +++ b/lib/dns/peer.c @@ -14,6 +14,7 @@ /*! \file */ #include +#include #include #include @@ -50,6 +51,7 @@ struct dns_peer { bool bogus; dns_transfer_format_t transfer_format; uint32_t transfers; + uint32_t request_ixfr_maxdiffs; bool support_ixfr; bool provide_ixfr; bool request_ixfr; @@ -78,22 +80,29 @@ struct dns_peer { /*% * Bit positions in the dns_peer_t structure flags field */ -#define BOGUS_BIT 0 -#define SERVER_TRANSFER_FORMAT_BIT 1 -#define TRANSFERS_BIT 2 -#define PROVIDE_IXFR_BIT 3 -#define REQUEST_IXFR_BIT 4 -#define SUPPORT_EDNS_BIT 5 -#define SERVER_UDPSIZE_BIT 6 -#define SERVER_MAXUDP_BIT 7 -#define REQUEST_NSID_BIT 8 -#define SEND_COOKIE_BIT 9 -#define REQUEST_EXPIRE_BIT 10 -#define EDNS_VERSION_BIT 11 -#define FORCE_TCP_BIT 12 -#define SERVER_PADDING_BIT 13 -#define REQUEST_TCP_KEEPALIVE_BIT 14 -#define REQUIRE_COOKIE_BIT 15 +enum { + BOGUS_BIT = 0, + SERVER_TRANSFER_FORMAT_BIT, + TRANSFERS_BIT, + PROVIDE_IXFR_BIT, + REQUEST_IXFR_BIT, + REQUEST_IXFRMAXDIFFS_BIT, + SUPPORT_EDNS_BIT, + SERVER_UDPSIZE_BIT, + SERVER_MAXUDP_BIT, + REQUEST_NSID_BIT, + SEND_COOKIE_BIT, + REQUEST_EXPIRE_BIT, + EDNS_VERSION_BIT, + FORCE_TCP_BIT, + SERVER_PADDING_BIT, + REQUEST_TCP_KEEPALIVE_BIT, + REQUIRE_COOKIE_BIT, + DNS_PEER_FLAGS_COUNT +}; + +STATIC_ASSERT(DNS_PEER_FLAGS_COUNT <= CHAR_BIT * sizeof(uint32_t), + "dns_peer_t structure flags fields are too many for uint32_t"); static void peerlist_delete(dns_peerlist_t **list); @@ -372,6 +381,8 @@ ACCESS_OPTION(maxudp, SERVER_MAXUDP_BIT, uint16_t, maxudp) ACCESS_OPTION(provideixfr, PROVIDE_IXFR_BIT, bool, provide_ixfr) ACCESS_OPTION(requestexpire, REQUEST_EXPIRE_BIT, bool, request_expire) ACCESS_OPTION(requestixfr, REQUEST_IXFR_BIT, bool, request_ixfr) +ACCESS_OPTION(requestixfrmaxdiffs, REQUEST_IXFRMAXDIFFS_BIT, uint32_t, + request_ixfr_maxdiffs) ACCESS_OPTION(requestnsid, REQUEST_NSID_BIT, bool, request_nsid) ACCESS_OPTION(requirecookie, REQUIRE_COOKIE_BIT, bool, require_cookie) ACCESS_OPTION(sendcookie, SEND_COOKIE_BIT, bool, send_cookie) diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index 1327fa130a1..99da559b895 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -178,6 +178,8 @@ struct dns_xfrin { dns_rdatacallbacks_t axfr; struct { + uint32_t diffs; + uint32_t maxdiffs; uint32_t request_serial; uint32_t current_serial; dns_journal_t *journal; @@ -212,7 +214,8 @@ typedef struct xfrin_work { static void xfrin_create(isc_mem_t *mctx, dns_zone_t *zone, dns_db_t *db, isc_loop_t *loop, dns_name_t *zonename, dns_rdataclass_t rdclass, - dns_rdatatype_t reqtype, const isc_sockaddr_t *primaryaddr, + dns_rdatatype_t reqtype, uint32_t ixfr_maxdiffs, + const isc_sockaddr_t *primaryaddr, const isc_sockaddr_t *sourceaddr, dns_tsigkey_t *tsigkey, dns_transport_type_t soa_transport_type, dns_transport_t *transport, isc_tlsctx_cache_t *tlsctx_cache, @@ -459,6 +462,7 @@ ixfr_putdata(dns_xfrin_t *xfr, dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl, dns_difftuple_create(xfr->diff.mctx, op, name, ttl, rdata, &tuple); dns_diff_append(&xfr->diff, &tuple); + xfr->ixfr.diffs++; failure: return (result); } @@ -863,7 +867,7 @@ failure: isc_result_t dns_xfrin_create(dns_zone_t *zone, dns_rdatatype_t xfrtype, - const isc_sockaddr_t *primaryaddr, + uint32_t ixfr_maxdiffs, const isc_sockaddr_t *primaryaddr, const isc_sockaddr_t *sourceaddr, dns_tsigkey_t *tsigkey, dns_transport_type_t soa_transport_type, dns_transport_t *transport, isc_tlsctx_cache_t *tlsctx_cache, @@ -889,7 +893,7 @@ dns_xfrin_create(dns_zone_t *zone, dns_rdatatype_t xfrtype, } xfrin_create(mctx, zone, db, loop, zonename, dns_zone_getclass(zone), - xfrtype, primaryaddr, sourceaddr, tsigkey, + xfrtype, ixfr_maxdiffs, primaryaddr, sourceaddr, tsigkey, soa_transport_type, transport, tlsctx_cache, &xfr); if (db != NULL) { @@ -1096,6 +1100,8 @@ xfrin_reset(dns_xfrin_t *xfr) { dns_diff_clear(&xfr->diff); + xfr->ixfr.diffs = 0; + if (xfr->ixfr.journal != NULL) { dns_journal_destroy(&xfr->ixfr.journal); } @@ -1145,7 +1151,8 @@ xfrin_fail(dns_xfrin_t *xfr, isc_result_t result, const char *msg) { static void xfrin_create(isc_mem_t *mctx, dns_zone_t *zone, dns_db_t *db, isc_loop_t *loop, dns_name_t *zonename, dns_rdataclass_t rdclass, - dns_rdatatype_t reqtype, const isc_sockaddr_t *primaryaddr, + dns_rdatatype_t reqtype, uint32_t ixfr_maxdiffs, + const isc_sockaddr_t *primaryaddr, const isc_sockaddr_t *sourceaddr, dns_tsigkey_t *tsigkey, dns_transport_type_t soa_transport_type, dns_transport_t *transport, isc_tlsctx_cache_t *tlsctx_cache, @@ -1157,6 +1164,7 @@ xfrin_create(isc_mem_t *mctx, dns_zone_t *zone, dns_db_t *db, isc_loop_t *loop, .shutdown_result = ISC_R_UNSET, .rdclass = rdclass, .reqtype = reqtype, + .ixfr.maxdiffs = ixfr_maxdiffs, .maxrecords = dns_zone_getmaxrecords(zone), .primaryaddr = *primaryaddr, .sourceaddr = *sourceaddr, @@ -1902,6 +1910,19 @@ xfrin_recv_done(isc_result_t result, isc_region_t *region, void *arg) { dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdataset_current(rds, &rdata); CHECK(xfr_rr(xfr, name, rds->ttl, &rdata)); + + /* + * Did we hit the maximum ixfr diffs limit? + */ + if (xfr->reqtype == dns_rdatatype_ixfr && + xfr->ixfr.maxdiffs != 0 && + xfr->ixfr.diffs >= xfr->ixfr.maxdiffs) + { + xfrin_log(xfr, ISC_LOG_DEBUG(3), + "too many diffs, " + "retrying with AXFR"); + goto try_axfr; + } } } } diff --git a/lib/dns/zone.c b/lib/dns/zone.c index 9d7af1f5c63..056caffec24 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -465,6 +465,7 @@ struct dns_zone { * whether ixfr is requested */ bool requestixfr; + uint32_t requestixfr_maxdiffs; uint32_t ixfr_ratio; /*% @@ -18260,6 +18261,7 @@ got_transfer_quota(void *arg) { char primary[ISC_SOCKADDR_FORMATSIZE]; char source[ISC_SOCKADDR_FORMATSIZE]; dns_rdatatype_t xfrtype; + uint32_t ixfr_maxdiffs = 0; isc_netaddr_t primaryip; isc_sockaddr_t primaryaddr; isc_sockaddr_t sourceaddr; @@ -18326,12 +18328,20 @@ got_transfer_quota(void *arg) { UNLOCK_ZONE(zone); } else { bool use_ixfr = true; + if (peer != NULL) { result = dns_peer_getrequestixfr(peer, &use_ixfr); } if (peer == NULL || result != ISC_R_SUCCESS) { use_ixfr = zone->requestixfr; } + if (peer != NULL) { + result = dns_peer_getrequestixfrmaxdiffs( + peer, &ixfr_maxdiffs); + } + if (peer == NULL || result != ISC_R_SUCCESS) { + ixfr_maxdiffs = zone->requestixfr_maxdiffs; + } if (!use_ixfr) { dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_DEBUG(1), @@ -18415,10 +18425,10 @@ got_transfer_quota(void *arg) { zmgr_tlsctx_attach(zone->zmgr, &zmgr_tlsctx_cache); - result = dns_xfrin_create(zone, xfrtype, &primaryaddr, &sourceaddr, - zone->tsigkey, soa_transport_type, - zone->transport, zmgr_tlsctx_cache, - zone->mctx, zone_xfrdone, &zone->xfr); + result = dns_xfrin_create( + zone, xfrtype, ixfr_maxdiffs, &primaryaddr, &sourceaddr, + zone->tsigkey, soa_transport_type, zone->transport, + zmgr_tlsctx_cache, zone->mctx, zone_xfrdone, &zone->xfr); isc_tlsctx_cache_detach(&zmgr_tlsctx_cache); @@ -23139,6 +23149,18 @@ dns_zone_getrequestixfr(dns_zone_t *zone) { return (zone->requestixfr); } +void +dns_zone_setrequestixfrmaxdiffs(dns_zone_t *zone, uint32_t maxdiffs) { + REQUIRE(DNS_ZONE_VALID(zone)); + zone->requestixfr_maxdiffs = maxdiffs; +} + +bool +dns_zone_getrequestixfrmaxdiffs(dns_zone_t *zone) { + REQUIRE(DNS_ZONE_VALID(zone)); + return (zone->requestixfr_maxdiffs); +} + void dns_zone_setixfrratio(dns_zone_t *zone, uint32_t ratio) { REQUIRE(DNS_ZONE_VALID(zone)); diff --git a/lib/isccfg/check.c b/lib/isccfg/check.c index 87c85921db1..df5551b7356 100644 --- a/lib/isccfg/check.c +++ b/lib/isccfg/check.c @@ -4344,6 +4344,13 @@ static struct { { "tcp-only", dns_peer_setforcetcp }, }; +static struct { + const char *name; + isc_result_t (*set)(dns_peer_t *peer, uint32_t newval); +} uint32s[] = { + { "request-ixfr-max-diffs", dns_peer_setrequestixfrmaxdiffs }, +}; + static isc_result_t check_servers(const cfg_obj_t *config, const cfg_obj_t *voptions, isc_symtab_t *symtab, isc_mem_t *mctx) { @@ -4502,6 +4509,22 @@ check_servers(const cfg_obj_t *config, const cfg_obj_t *voptions, } } } + for (i = 0; i < ARRAY_SIZE(uint32s); i++) { + const cfg_obj_t *opt = NULL; + cfg_map_get(v1, uint32s[i].name, &opt); + if (opt != NULL) { + tresult = (uint32s[i].set)( + peer, cfg_obj_asuint32(opt)); + if (tresult != ISC_R_SUCCESS) { + cfg_obj_log(opt, ISC_LOG_ERROR, + "setting server option " + "'%s' failed: %s", + uint32s[i].name, + isc_result_totext(tresult)); + result = ISC_R_FAILURE; + } + } + } dns_peer_detach(&peer); } return (result); diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 83a8a997bbf..947432e00ba 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -2484,6 +2484,8 @@ static cfg_clausedef_t zone_clauses[] = { CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR }, { "request-ixfr", &cfg_type_boolean, CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR }, + { "request-ixfr-max-diffs", &cfg_type_uint32, + CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR }, { "serial-update-method", &cfg_type_updatemethod, CFG_ZONE_PRIMARY }, { "sig-signing-nodes", &cfg_type_uint32, CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY }, @@ -2721,6 +2723,7 @@ static cfg_clausedef_t server_clauses[] = { { "query-source-v6", &cfg_type_querysource6, 0 }, { "request-expire", &cfg_type_boolean, 0 }, { "request-ixfr", &cfg_type_boolean, 0 }, + { "request-ixfr-max-diffs", &cfg_type_uint32, 0 }, { "request-nsid", &cfg_type_boolean, 0 }, { "request-sit", NULL, CFG_CLAUSEFLAG_ANCIENT }, { "require-cookie", &cfg_type_boolean, 0 },