From: Colin Vidal Date: Tue, 26 May 2026 13:56:03 +0000 (+0200) Subject: Introduce min/max TTL bounds for delegations X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=dd37b0fdd81ebd89e9c04b7ee5d10bfa3e4661db;p=thirdparty%2Fbind9.git Introduce min/max TTL bounds for delegations The TTL of cached delegations can now have a minimum bound and a maximum bound. By default, delegdb does not enable TTL bound checking, but this can be configured from the caller using `dns_delegdb_config_t`. --- diff --git a/lib/dns/deleg.c b/lib/dns/deleg.c index 630ac3bef71..48dd2351e1b 100644 --- a/lib/dns/deleg.c +++ b/lib/dns/deleg.c @@ -515,27 +515,46 @@ delegset_size(dns_delegset_t *delegset) { return sz; } +static dns_ttl_t +normalize_ttl(dns_delegdb_t *delegdb, dns_ttl_t ttl) { + dns_ttl_t minttl = delegdb->config.minttl; + dns_ttl_t maxttl = delegdb->config.maxttl; + + if (minttl > 0 && ttl < minttl) { + return minttl; + } + + if (maxttl > 0 && ttl > maxttl) { + return maxttl; + } + + /* + * Even if the min ttl is disabled, it doesn't make sense to add an + * already expired delegation. So give it at least one second. + */ + return ttl == 0 ? 1 : ttl; +} + static size_t -delegdb_node_prepare(qplru_t *qplru, isc_stdtime_t now, dns_ttl_t ttl, +delegdb_node_prepare(dns_delegdb_t *delegdb, isc_stdtime_t now, dns_ttl_t ttl, const dns_name_t *zonecut, dns_delegset_t *delegset, delegdb_node_t **nodep) { - if (ttl == 0) { - ttl = 1; - } + ttl = normalize_ttl(delegdb, ttl); delegset->expires = ttl + now; isc_region_t zonecut_r = { 0 }; dns_name_toregion(zonecut, &zonecut_r); - delegdb_node_t *node = isc_mem_get(qplru->mctx, + delegdb_node_t *node = isc_mem_get(delegdb->qplru->mctx, sizeof(*node) + zonecut_r.length); *node = (delegdb_node_t){ + .magic = DELEGDB_NODE_MAGIC, .references = ISC_REFCOUNT_INITIALIZER(1), .link = ISC_LINK_INITIALIZER, .deadlink = ISC_LINK_INITIALIZER, .zonecut = DNS_NAME_INITEMPTY, - .qplru = qplru_ref(qplru), + .qplru = qplru_ref(delegdb->qplru), }; dns_delegset_attach(delegset, &node->delegset); @@ -597,8 +616,8 @@ dns_delegset_insert(dns_delegdb_t *delegdb, const dns_name_t *zonecut, * clean up expired/least recently used delegation, then allocate and * initialize a new node. */ - size_t requested = delegdb_node_prepare(delegdb->qplru, now, ttl, - zonecut, delegset, &node) + + size_t requested = delegdb_node_prepare(delegdb, now, ttl, zonecut, + delegset, &node) + delegset_size(delegset); /* diff --git a/lib/dns/include/dns/deleg.h b/lib/dns/include/dns/deleg.h index 4b621345212..901d3ebb981 100644 --- a/lib/dns/include/dns/deleg.h +++ b/lib/dns/include/dns/deleg.h @@ -29,6 +29,13 @@ typedef struct { * entries are discarded. Value `0` means there is no limitation. */ size_t dbsize; + + /* + * Confgure minimum and maximum TTL of a delegation. A value of 0 means + * there is no limits. + */ + dns_ttl_t minttl; + dns_ttl_t maxttl; } dns_delegdb_config_t; /* diff --git a/tests/dns/deleg_test.c b/tests/dns/deleg_test.c index 8c673098fa3..93d422ab95e 100644 --- a/tests/dns/deleg_test.c +++ b/tests/dns/deleg_test.c @@ -370,7 +370,7 @@ basictests(ISC_ATTR_UNUSED void *arg) { } static void -ttl0tests(ISC_ATTR_UNUSED void *arg) { +ttltests(ISC_ATTR_UNUSED void *arg) { isc_result_t result; dns_delegdb_t *db = NULL; dns_deleg_t *deleg = NULL; @@ -397,6 +397,10 @@ ttl0tests(ISC_ATTR_UNUSED void *arg) { writedb(db, "bar.stuff.", 0, &delegset, true); deleg = NULL; + /* + * This is possible because delegdb internally forces TTL of 1 if the + * caller TTL is 0, in the case of the minttl config is disabled. + */ result = lookupdb(db, "baz.bar.stuff.", now, 0, "bar.stuff.", &delegset); assert_int_equal(result, ISC_R_SUCCESS); @@ -405,6 +409,51 @@ ttl0tests(ISC_ATTR_UNUSED void *arg) { result = lookupdb(db, "baz.bar.stuff.", now + 1, 0, "", &delegset); assert_int_equal(result, ISC_R_NOTFOUND); + dns_delegdb_setconfig(db, &(dns_delegdb_config_t){ .minttl = 60 }); + dns_delegset_allocset(db, &delegset); + + dns_delegset_allocdeleg(delegset, DNS_DELEGTYPE_DELEG_NAMES, &deleg); + addnamedeleg("ns.gee.bar.stuff.", delegset, deleg, dns_delegset_addns); + deleg = NULL; + + dns_delegset_allocdeleg(delegset, DNS_DELEGTYPE_DELEG_ADDRESSES, + &deleg); + addipdeleg(AF_INET6, "3333::2222", delegset, deleg); + deleg = NULL; + + writedb(db, "gee.bar.stuff.", 2, &delegset, true); + deleg = NULL; + + result = lookupdb(db, "gee.bar.stuff.", now + 59, 0, "gee.bar.stuff.", + &delegset); + assert_int_equal(result, ISC_R_SUCCESS); + dns_delegset_detach(&delegset); + + result = lookupdb(db, "gee.bar.stuff.", now + 61, 0, "", &delegset); + assert_int_equal(result, ISC_R_NOTFOUND); + + dns_delegdb_setconfig(db, &(dns_delegdb_config_t){ .maxttl = 160 }); + dns_delegset_allocset(db, &delegset); + + dns_delegset_allocdeleg(delegset, DNS_DELEGTYPE_DELEG_NAMES, &deleg); + addnamedeleg("ns.gee.", delegset, deleg, dns_delegset_addns); + deleg = NULL; + + dns_delegset_allocdeleg(delegset, DNS_DELEGTYPE_DELEG_ADDRESSES, + &deleg); + addipdeleg(AF_INET6, "4444::2222", delegset, deleg); + deleg = NULL; + + writedb(db, "gee.", 200, &delegset, true); + deleg = NULL; + + result = lookupdb(db, "gee.", now + 159, 0, "gee.", &delegset); + assert_int_equal(result, ISC_R_SUCCESS); + dns_delegset_detach(&delegset); + + result = lookupdb(db, "gee.", now + 200, 0, "", &delegset); + assert_int_equal(result, ISC_R_NOTFOUND); + shutdowntest(&db); } @@ -764,7 +813,7 @@ longnametests(ISC_ATTR_UNUSED void *arg) { } ISC_RUN_TEST_IMPL(dns_deleg_basictests) { rundelegtest(basictests); } -ISC_RUN_TEST_IMPL(dns_deleg_ttl0tests) { rundelegtest(ttl0tests); } +ISC_RUN_TEST_IMPL(dns_deleg_ttltests) { rundelegtest(ttltests); } ISC_RUN_TEST_IMPL(dns_deleg_noexacttests) { rundelegtest(noexacttests); } ISC_RUN_TEST_IMPL(dns_deleg_deletetests) { rundelegtest(deletetests); } ISC_RUN_TEST_IMPL(dns_deleg_cleanuptests) { rundelegtest(cleanuptests); } @@ -772,7 +821,7 @@ ISC_RUN_TEST_IMPL(dns_deleg_longnametests) { rundelegtest(longnametests); } ISC_TEST_LIST_START ISC_TEST_ENTRY(dns_deleg_basictests) -ISC_TEST_ENTRY(dns_deleg_ttl0tests) +ISC_TEST_ENTRY(dns_deleg_ttltests) ISC_TEST_ENTRY(dns_deleg_noexacttests) ISC_TEST_ENTRY(dns_deleg_deletetests) ISC_TEST_ENTRY(dns_deleg_cleanuptests)