]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add a limit to the number of RR types for single name
authorOndřej Surý <ondrej@isc.org>
Sat, 25 May 2024 09:46:56 +0000 (11:46 +0200)
committerOndřej Surý <ondrej@isc.org>
Mon, 29 Jul 2024 10:27:25 +0000 (12:27 +0200)
Previously, the number of RR types for a single owner name was limited
only by the maximum number of the types (64k).  As the data structure
that holds the RR types for the database node is just a linked list, and
there are places where we just walk through the whole list (again and
again), adding a large number of RR types for a single owner named with
would slow down processing of such name (database node).

Add a configurable limit to cap the number of the RR types for a single
owner.  This is enforced at the database (rbtdb, qpzone, qpcache) level
and configured with new max-types-per-name configuration option that
can be configured globally, per-view and per-zone.

(cherry picked from commit 00d16211d6368b99f070c1182d8c76b3798ca1db)

19 files changed:
bin/named/config.c
bin/named/server.c
bin/named/zoneconf.c
bin/tests/system/dyndb/driver/db.c
doc/arm/reference.rst
lib/dns/cache.c
lib/dns/db.c
lib/dns/dnsrps.c
lib/dns/ecdb.c
lib/dns/include/dns/cache.h
lib/dns/include/dns/db.h
lib/dns/include/dns/view.h
lib/dns/include/dns/zone.h
lib/dns/rbtdb.c
lib/dns/sdb.c
lib/dns/sdlz.c
lib/dns/view.c
lib/dns/zone.c
lib/isccfg/namedconf.c

index 72dec1adef24d022fd2ee445c1314ccff3cc4b44..ec48840e4b40d07440992333133092309e4f96ec 100644 (file)
@@ -229,6 +229,7 @@ options {\n\
        max-records-per-type 100;\n\
        max-refresh-time 2419200; /* 4 weeks */\n\
        max-retry-time 1209600; /* 2 weeks */\n\
+       max-types-per-name 100;\n\
        max-transfer-idle-in 60;\n\
        max-transfer-idle-out 60;\n\
        max-transfer-time-in 120;\n\
index 72de0e23526b08904004e553cad145620e65aca8..1926dc651a125c99fc93e8b0f1b36897885ad147 100644 (file)
@@ -5461,6 +5461,15 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
        INSIST(result == ISC_R_SUCCESS);
        dns_view_setmaxrrperset(view, cfg_obj_asuint32(obj));
 
+       /*
+        * This is used for the cache and also as a default value
+        * for zone databases.
+        */
+       obj = NULL;
+       result = named_config_get(maps, "max-types-per-name", &obj);
+       INSIST(result == ISC_R_SUCCESS);
+       dns_view_setmaxtypepername(view, cfg_obj_asuint32(obj));
+
        obj = NULL;
        result = named_config_get(maps, "max-recursion-depth", &obj);
        INSIST(result == ISC_R_SUCCESS);
index 81f68797babe609698e3400290d36470ced58693..821e6a1e452b36d01c744ebda6497a909e7b3139 100644 (file)
@@ -1080,6 +1080,14 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
                dns_zone_setmaxrrperset(zone, 0);
        }
 
+       obj = NULL;
+       result = named_config_get(maps, "max-types-per-name", &obj);
+       INSIST(result == ISC_R_SUCCESS && obj != NULL);
+       dns_zone_setmaxtypepername(mayberaw, cfg_obj_asuint32(obj));
+       if (zone != mayberaw) {
+               dns_zone_setmaxtypepername(zone, 0);
+       }
+
        if (raw != NULL && filename != NULL) {
 #define SIGNED ".signed"
                size_t signedlen = strlen(filename) + sizeof(SIGNED);
index 727268fc3942b71fdf44f64614d413b84f7e7639..398589a521d5afff609fba6426d349a50c890829 100644 (file)
@@ -623,7 +623,8 @@ static dns_dbmethods_t sampledb_methods = {
        NULL, /* getservestalerefresh */
        NULL, /* setgluecachestats */
        NULL, /* adjusthashsize */
-       NULL  /* setmaxrrperset */
+       NULL, /* setmaxrrperset */
+       NULL  /* setmaxtypepername */
 };
 
 /* Auxiliary driver functions. */
index fbd8edb3bfcd67d985c62692a93366eb4b69d9b0..0ce2d1f989322b3e377c5d50401e3a4a36f908e4 100644 (file)
@@ -2957,6 +2957,18 @@ system.
    a failure.  If set to 0, there is no cap on RRset size.  The default is
    100.
 
+``max-types-per-name``
+   This sets the maximum number of resource record types that can be stored
+   for a single owner name in a database. When configured in ``options``
+   or ``view``, it controls the cache database, and also sets
+   the default value for zone databases, which can be overridden by setting
+   it at the ``zone`` level
+
+   If set to a positive value, any attempt to cache or to add to a zone an owner
+   name with more than the specified number of resource record types will result
+   in a failure.  If set to 0, there is no cap on RR types number.  The default is
+   100.
+   
 ``recursive-clients``
    This sets the maximum number (a "hard quota") of simultaneous recursive lookups
    the server performs on behalf of clients. The default is
index 8cb442b45170396b40c2e291e4d3dabba6718718..125cfdc76bf520b1f14b7a068af19ccb2437f189 100644 (file)
@@ -153,6 +153,7 @@ struct dns_cache {
        /* Access to the on-disk cache file is also locked by 'filelock'. */
 
        uint32_t maxrrperset;
+       uint32_t maxtypepername;
 };
 
 /***
@@ -187,6 +188,7 @@ cache_create_db(dns_cache_t *cache, dns_db_t **db) {
 
        dns_db_setservestalettl(*db, cache->serve_stale_ttl);
        dns_db_setmaxrrperset(*db, cache->maxrrperset);
+       dns_db_setmaxtypepername(*db, cache->maxtypepername);
 
        if (cache->taskmgr == NULL) {
                return (ISC_R_SUCCESS);
@@ -1329,6 +1331,16 @@ dns_cache_setmaxrrperset(dns_cache_t *cache, uint32_t value) {
        }
 }
 
+void
+dns_cache_setmaxtypepername(dns_cache_t *cache, uint32_t value) {
+       REQUIRE(VALID_CACHE(cache));
+
+       cache->maxtypepername = value;
+       if (cache->db != NULL) {
+               dns_db_setmaxtypepername(cache->db, value);
+       }
+}
+
 /*
  * XXX: Much of the following code has been copied in from statschannel.c.
  * We should refactor this into a generic function in stats.c that can be
index a575d6208fca620772853457e5f2b6c1e2eae46b..1f19016d73013df6c3e06c33723012f6c16bf96b 100644 (file)
@@ -1146,3 +1146,12 @@ dns_db_setmaxrrperset(dns_db_t *db, uint32_t value) {
                (db->methods->setmaxrrperset)(db, value);
        }
 }
+
+void
+dns_db_setmaxtypepername(dns_db_t *db, uint32_t value) {
+       REQUIRE(DNS_DB_VALID(db));
+
+       if (db->methods->setmaxtypepername != NULL) {
+               (db->methods->setmaxtypepername)(db, value);
+       }
+}
index f4d9bae5f05f97c82b8f81f0fb0b45a2444c2091..43c28aefd9b49d6cce8834e9b92c0ac3de10f34b 100644 (file)
@@ -976,7 +976,8 @@ static dns_dbmethods_t rpsdb_db_methods = {
        NULL, /* getservestalerefresh */
        NULL, /* setgluecachestats */
        NULL, /* adjusthashsize */
-       NULL  /* setmaxrrperset */
+       NULL, /* setmaxrrperset */
+       NULL  /* setmaxtypepername */
 };
 
 static dns_rdatasetmethods_t rpsdb_rdataset_methods = {
index 42a1248790c5266708834598b81443c2ec542064..3e562d876f3a15c92997df24f061bfbc1ce22715 100644 (file)
@@ -564,7 +564,8 @@ static dns_dbmethods_t ecdb_methods = {
        NULL, /* getservestalerefresh */
        NULL, /* setgluecachestats */
        NULL, /* adjusthashsize */
-       NULL  /* setmaxrrperset */
+       NULL, /* setmaxrrperset */
+       NULL  /* setmaxtypepername */
 };
 
 static isc_result_t
index 34539eb9791652eb43c88375b3940ee9501372a0..eb6f1e929493d678e2465b469e69f5eec5f50227 100644 (file)
@@ -340,6 +340,12 @@ dns_cache_setmaxrrperset(dns_cache_t *cache, uint32_t value);
  * Set the maximum resource records per RRSet that can be cached.
  */
 
+void
+dns_cache_setmaxtypepername(dns_cache_t *cache, uint32_t value);
+/*%<
+ * Set the maximum resource record types per owner name that can be cached.
+ */
+
 #ifdef HAVE_LIBXML2
 int
 dns_cache_renderxml(dns_cache_t *cache, void *writer0);
index e0d17f26321621682f6712640e9db2a0c2dfd036..3c6c7f21071de2f763d1a717dc97001fc810439f 100644 (file)
@@ -185,6 +185,7 @@ typedef struct dns_dbmethods {
        isc_result_t (*setgluecachestats)(dns_db_t *db, isc_stats_t *stats);
        isc_result_t (*adjusthashsize)(dns_db_t *db, size_t size);
        void (*setmaxrrperset)(dns_db_t *db, uint32_t value);
+       void (*setmaxtypepername)(dns_db_t *db, uint32_t value);
 } dns_dbmethods_t;
 
 typedef isc_result_t (*dns_dbcreatefunc_t)(isc_mem_t       *mctx,
@@ -1804,6 +1805,16 @@ dns_db_setmaxrrperset(dns_db_t *db, uint32_t value);
  * is nonzero, then any subsequent attempt to add an rdataset with
  * more than 'value' RRs will return ISC_R_NOSPACE.
  */
+
+void
+dns_db_setmaxtypepername(dns_db_t *db, uint32_t value);
+/*%<
+ * Set the maximum permissible number of RR types per owner name.
+ *
+ * If 'value' is nonzero, then any subsequent attempt to add an rdataset with a
+ * RR type that would exceed the number of already stored RR types will return
+ * ISC_R_NOSPACE.
+ */
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_DB_H */
index b7cf663cb58880c2381b20dffe478632ed3e8ad9..4151c1130ddd059827b86342fae57ce8c0230af8 100644 (file)
@@ -189,6 +189,7 @@ struct dns_view {
        uint32_t          fail_ttl;
        dns_badcache_t   *failcache;
        uint32_t          maxrrperset;
+       uint32_t          maxtypepername;
 
        /*
         * Configurable data for server use only,
@@ -1361,6 +1362,12 @@ dns_view_setmaxrrperset(dns_view_t *view, uint32_t value);
  * Set the maximum resource records per RRSet that can be cached.
  */
 
+void
+dns_view_setmaxtypepername(dns_view_t *view, uint32_t value);
+/*%<
+ * Set the maximum resource record types per owner name that can be cached.
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_VIEW_H */
index a98b28f225583504b9ed99896de285be3f2f9473..d893dd26942a62d1b83ca2200bbbf9dc5e617515 100644 (file)
@@ -358,6 +358,19 @@ dns_zone_setmaxrrperset(dns_zone_t *zone, uint32_t maxrrperset);
  *\li  void
  */
 
+void
+dns_zone_setmaxtypepername(dns_zone_t *zone, uint32_t maxtypepername);
+/*%<
+ *     Sets the maximum number of resource record types per owner name
+ *     permitted in a zone.  0 implies unlimited.
+ *
+ * Requires:
+ *\li  'zone' to be valid initialised zone.
+ *
+ * Returns:
+ *\li  void
+ */
+
 void
 dns_zone_setmaxttl(dns_zone_t *zone, uint32_t maxttl);
 /*%<
index 06228c910e3add3bb01f39b324a502a6fdf8786b..4332215c77b63620d9ac706489c274b83d33a527 100644 (file)
@@ -497,6 +497,7 @@ struct dns_rbtdb {
        rbtdb_serial_t least_serial;
        rbtdb_serial_t next_serial;
        uint32_t maxrrperset;
+       uint32_t maxtypepername;
        rbtdb_version_t *current_version;
        rbtdb_version_t *future_version;
        rbtdb_versionlist_t open_versions;
@@ -6369,6 +6370,7 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, const dns_name_t *nodename,
                                        set_ttl(rbtdb, topheader, 0);
                                        mark_header_ancient(rbtdb, topheader);
                                }
+                               ntypes = 0; /* Always add the negative entry */
                                goto find_header;
                        }
                        /*
@@ -6393,9 +6395,11 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, const dns_name_t *nodename,
                         * check for an extant non-ancient NODATA ncache
                         * entry which covers the same type as the RRSIG.
                         */
+                       ntypes = 0;
                        for (topheader = rbtnode->data; topheader != NULL;
                             topheader = topheader->next)
                        {
+                               ++ntypes;
                                if ((topheader->type ==
                                     RBTDB_RDATATYPE_NCACHEANY) ||
                                    (newheader->type == sigtype &&
@@ -6440,16 +6444,12 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, const dns_name_t *nodename,
                }
        }
 
+       ntypes = 0;
        for (topheader = rbtnode->data; topheader != NULL;
             topheader = topheader->next)
        {
-               if (IS_CACHE(rbtdb) && ACTIVE(topheader, now)) {
-                       ++ntypes;
-                       expireheader = topheader;
-               } else if (!IS_CACHE(rbtdb)) {
-                       ++ntypes;
-               }
-               if (prio_header(topheader)) {
+               ++ntypes;
+               if (prio_type(topheader->type)) {
                        prioheader = topheader;
                }
                if (topheader->type == newheader->type ||
@@ -6806,10 +6806,12 @@ find_header:
                        /*
                         * No rdatasets of the given type exist at the node.
                         */
-                       if (!IS_CACHE(rbtdb) && overmaxtype(rbtdb, ntypes)) {
+                       if (rbtdb->maxtypepername > 0 &&
+                           ntypes >= rbtdb->maxtypepername)
+                       {
                                free_rdataset(rbtdb, rbtdb->common.mctx,
                                              newheader);
-                               return (ISC_R_QUOTA);
+                               return (DNS_R_TOOMANYRECORDS);
                        }
 
                        newheader->down = NULL;
@@ -8627,6 +8629,15 @@ setmaxrrperset(dns_db_t *db, uint32_t maxrrperset) {
        rbtdb->maxrrperset = maxrrperset;
 }
 
+static void
+setmaxtypepername(dns_db_t *db, uint32_t maxtypepername) {
+       dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
+
+       REQUIRE(VALID_RBTDB(rbtdb));
+
+       rbtdb->maxtypepername = maxtypepername;
+}
+
 static dns_stats_t *
 getrrsetstats(dns_db_t *db) {
        dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
@@ -8751,7 +8762,8 @@ static dns_dbmethods_t zone_methods = { attach,
                                        NULL, /* getservestalerefresh */
                                        setgluecachestats,
                                        adjusthashsize,
-                                       setmaxrrperset };
+                                       setmaxrrperset,
+                                       setmaxtypepername };
 
 static dns_dbmethods_t cache_methods = { attach,
                                         detach,
@@ -8804,7 +8816,8 @@ static dns_dbmethods_t cache_methods = { attach,
                                         getservestalerefresh,
                                         NULL,
                                         adjusthashsize,
-                                        setmaxrrperset };
+                                        setmaxrrperset,
+                                        setmaxtypepername };
 
 isc_result_t
 dns_rbtdb_create(isc_mem_t *mctx, const dns_name_t *origin, dns_dbtype_t type,
index 282c3271f5bccca83d5a03f7fa7e41bc9ac67f60..4438091350c8026045b51df640c0cd3f4b76796d 100644 (file)
@@ -1320,7 +1320,8 @@ static dns_dbmethods_t sdb_methods = {
        NULL, /* getservestalerefresh */
        NULL, /* setgluecachestats */
        NULL, /* adjusthashsize */
-       NULL  /* setmaxrrperset */
+       NULL, /* setmaxrrperset */
+       NULL  /* setmaxtypepername */
 };
 
 static isc_result_t
index 299c7d857d84dd2f709cf7cfe1c67bbb8a6090ae..a8556dfaefc9729bbedf92886c8385446233c52c 100644 (file)
@@ -1293,7 +1293,8 @@ static dns_dbmethods_t sdlzdb_methods = {
        NULL, /* getservestalerefresh */
        NULL, /* setgluecachestats */
        NULL, /* adjusthashsize */
-       NULL  /* setmaxrrperset */
+       NULL, /* setmaxrrperset */
+       NULL  /* setmaxtypepername */
 };
 
 /*
index 9f5daf793bb9d035fcbfdb09d68c3e9b75f2b5af..0de880d2a661378b9afd7f466912bab84fe9613a 100644 (file)
@@ -873,6 +873,7 @@ dns_view_setcache(dns_view_t *view, dns_cache_t *cache, bool shared) {
        INSIST(DNS_DB_VALID(view->cachedb));
 
        dns_cache_setmaxrrperset(view->cache, view->maxrrperset);
+       dns_cache_setmaxtypepername(view->cache, view->maxtypepername);
 }
 
 bool
@@ -2651,3 +2652,12 @@ dns_view_setmaxrrperset(dns_view_t *view, uint32_t value) {
                dns_cache_setmaxrrperset(view->cache, value);
        }
 }
+
+void
+dns_view_setmaxtypepername(dns_view_t *view, uint32_t value) {
+       REQUIRE(DNS_VIEW_VALID(view));
+       view->maxtypepername = value;
+       if (view->cache != NULL) {
+               dns_cache_setmaxtypepername(view->cache, value);
+       }
+}
index ff06849cd93f772bbffacc1d86445ede9f836883..c0262c51903e163515b2e45f7ac93bd3ed797672 100644 (file)
@@ -308,6 +308,7 @@ struct dns_zone {
 
        uint32_t maxrecords;
        uint32_t maxrrperset;
+       uint32_t maxtypepername;
 
        isc_sockaddr_t *masters;
        isc_dscp_t *masterdscps;
@@ -12306,6 +12307,16 @@ dns_zone_setmaxrrperset(dns_zone_t *zone, uint32_t val) {
        }
 }
 
+void
+dns_zone_setmaxtypepername(dns_zone_t *zone, uint32_t val) {
+       REQUIRE(DNS_ZONE_VALID(zone));
+
+       zone->maxtypepername = val;
+       if (zone->db != NULL) {
+               dns_db_setmaxtypepername(zone->db, val);
+       }
+}
+
 static bool
 notify_isqueued(dns_zone_t *zone, unsigned int flags, dns_name_t *name,
                isc_sockaddr_t *addr, dns_tsigkey_t *key) {
@@ -14720,6 +14731,8 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) {
                        }
                        dns_db_settask(stub->db, zone->task, zone->task);
                        dns_db_setmaxrrperset(stub->db, zone->maxrrperset);
+                       dns_db_setmaxtypepername(stub->db,
+                                                zone->maxtypepername);
                }
 
                result = dns_db_newversion(stub->db, &stub->version);
@@ -17463,6 +17476,7 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, bool dump) {
        zone_attachdb(zone, db);
        dns_db_settask(zone->db, zone->task, zone->task);
        dns_db_setmaxrrperset(zone->db, zone->maxrrperset);
+       dns_db_setmaxtypepername(zone->db, zone->maxtypepername);
        DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED | DNS_ZONEFLG_NEEDNOTIFY);
        return (ISC_R_SUCCESS);
 
@@ -23646,6 +23660,7 @@ dns_zone_makedb(dns_zone_t *zone, dns_db_t **dbp) {
 
        dns_db_settask(db, zone->task, zone->task);
        dns_db_setmaxrrperset(db, zone->maxrrperset);
+       dns_db_setmaxtypepername(db, zone->maxtypepername);
 
        *dbp = db;
 
index ffe22b39c4f924c92ee9d7124ea5c7a2c19a4e1f..69525dcc902146902c733e8eebe48cb6f018839b 100644 (file)
@@ -2244,6 +2244,9 @@ static cfg_clausedef_t zone_clauses[] = {
        { "max-records-per-type", &cfg_type_uint32,
          CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR |
                  CFG_ZONE_STUB | CFG_ZONE_STATICSTUB | CFG_ZONE_REDIRECT },
+       { "max-types-per-name", &cfg_type_uint32,
+         CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR |
+                 CFG_ZONE_STUB | CFG_ZONE_STATICSTUB | CFG_ZONE_REDIRECT },
        { "max-refresh-time", &cfg_type_uint32,
          CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB },
        { "max-retry-time", &cfg_type_uint32,