]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Introduce min/max TTL bounds for delegations
authorColin Vidal <colin@isc.org>
Tue, 26 May 2026 13:56:03 +0000 (15:56 +0200)
committerColin Vidal <colin@isc.org>
Wed, 1 Jul 2026 06:40:05 +0000 (08:40 +0200)
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`.

lib/dns/deleg.c
lib/dns/include/dns/deleg.h
tests/dns/deleg_test.c

index 630ac3bef716d55da44c8aca3a67da11d55cf393..48dd2351e1b617a48417e01bfe4f6c2816c49af9 100644 (file)
@@ -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);
 
        /*
index 4b6213452125768016a70a40dedef3a587088c53..901d3ebb98197bbea948481f52bf734e0e53d309 100644 (file)
@@ -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;
 
 /*
index 8c673098fa361bf31555e91b5ca0e002dac5fb9c..93d422ab95ed595d1d19a63e8f5454ee493cc086 100644 (file)
@@ -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)