]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
3636. [bug] Automatic empty zones now behave better with
authorMark Andrews <marka@isc.org>
Fri, 16 Aug 2013 03:54:23 +0000 (13:54 +1000)
committerMark Andrews <marka@isc.org>
Fri, 16 Aug 2013 05:11:17 +0000 (15:11 +1000)
                        forward only "zones" beneath them. [RT #34583]

(cherry picked from commit e548e07a9a2f1ec64774d7ae872d530eaf270eb7)

CHANGES
bin/named/server.c
bin/tests/system/forward/ns2/named.conf
bin/tests/system/forward/ns4/named.conf
bin/tests/system/forward/tests.sh

diff --git a/CHANGES b/CHANGES
index 379a99dfb12a6c2775d7951ec8cedc17ab1324d2..843c3ea5596d3ad561815b1c0d7299ce05355037 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,6 @@
+3636.  [bug]           Automatic empty zones now behave better with
+                       forward only "zones" beneath them. [RT #34583]
+
 3634.  [func]          Report build-id in rndc status. Report build-id
                        when building from a git repository. [RT #20422]
 
index cc1ad72ef42d1107d086bfa992de15cf52508d41..6f439f2b1ca3dead4159113b044ea347034b93d3 100644 (file)
 #include <dns/portlist.h>
 #include <dns/rbt.h>
 #include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
 #include <dns/rdataset.h>
 #include <dns/rdatastruct.h>
 #include <dns/resolver.h>
 #include <dns/rootns.h>
 #include <dns/secalg.h>
+#include <dns/soa.h>
 #include <dns/stats.h>
 #include <dns/tkey.h>
 #include <dns/tsig.h>
@@ -1055,6 +1057,278 @@ cache_reusable(dns_view_t *originview, dns_view_t *view,
        return (ISC_TRUE);
 }
 
+#define BINDABLE(name) \
+       ((name->attributes & (DNS_NAMEATTR_READONLY|DNS_NAMEATTR_DYNAMIC)) \
+        == 0)
+
+static isc_result_t
+dns_name_fromstring(dns_name_t *target, const char *src,
+                   unsigned int options, isc_mem_t *mctx)
+{
+       isc_result_t result;
+       isc_buffer_t buf;
+       dns_fixedname_t fn;
+       dns_name_t *name;
+
+       REQUIRE(src != NULL);
+
+       isc_buffer_constinit(&buf, src, strlen(src));
+       isc_buffer_add(&buf, strlen(src));
+       if (BINDABLE(target) && target->buffer != NULL)
+               name = target;
+       else {
+               dns_fixedname_init(&fn);
+               name = dns_fixedname_name(&fn);
+       }
+
+       result = dns_name_fromtext(name, &buf, dns_rootname, options, NULL);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       if (name != target)
+               result = dns_name_dupwithoffsets(name, mctx, target);
+       return (result);
+}
+
+static isc_result_t
+add_soa(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+       dns_name_t *origin, dns_name_t *contact)
+{
+       dns_dbnode_t *node = NULL;
+       dns_rdata_soa_t soa;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_rdatalist_t rdatalist;
+       dns_rdataset_t rdataset;
+       isc_buffer_t b;
+       isc_result_t result;
+       unsigned char buf[2 * DNS_NAME_MAXWIRE + 20];
+
+       dns_rdataset_init(&rdataset);
+       dns_rdatalist_init(&rdatalist);
+       isc_buffer_init(&b, buf, sizeof(buf));
+
+       soa.common.rdtype = dns_rdatatype_soa;
+       soa.common.rdclass = dns_db_class(db);
+       soa.mctx = NULL;
+       soa.serial = 0;
+       soa.refresh = 28800;
+       soa.retry = 7200;
+       soa.expire = 604800;
+       soa.minimum = 86400;
+       dns_name_init(&soa.origin, NULL);
+       dns_name_clone(origin, &soa.origin);
+       dns_name_init(&soa.contact, NULL);
+       dns_name_clone(contact, &soa.contact);
+
+       CHECK(dns_rdata_fromstruct(&rdata, dns_db_class(db), dns_rdatatype_soa,
+                                  &soa, &b));
+
+       rdatalist.type = rdata.type;
+       rdatalist.covers = 0;
+       rdatalist.rdclass = rdata.rdclass;
+       rdatalist.ttl = 86400;
+       ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
+       CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset));
+       CHECK(dns_db_findnode(db, name, ISC_TRUE, &node));
+       CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL));
+ cleanup:
+       if (node != NULL)
+               dns_db_detachnode(db, &node);
+       return (result);
+}
+
+static isc_result_t
+add_ns(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+       dns_name_t *nsname)
+{
+       dns_dbnode_t *node = NULL;
+       dns_rdata_ns_t ns;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_rdatalist_t rdatalist;
+       dns_rdataset_t rdataset;
+       isc_result_t result;
+       isc_buffer_t b;
+       unsigned char buf[DNS_NAME_MAXWIRE];
+
+       isc_buffer_init(&b, buf, sizeof(buf));
+
+       dns_rdataset_init(&rdataset);
+       dns_rdatalist_init(&rdatalist);
+       ns.common.rdtype = dns_rdatatype_ns;
+       ns.common.rdclass = dns_db_class(db);
+       ns.mctx = NULL;
+       dns_name_init(&ns.name, NULL);
+       dns_name_clone(nsname, &ns.name);
+       CHECK(dns_rdata_fromstruct(&rdata, dns_db_class(db), dns_rdatatype_ns,
+                                  &ns, &b));
+       rdatalist.type = rdata.type;
+       rdatalist.covers = 0;
+       rdatalist.rdclass = rdata.rdclass;
+       rdatalist.ttl = 86400;
+       ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
+       CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset));
+       CHECK(dns_db_findnode(db, name, ISC_TRUE, &node));
+       CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL));
+ cleanup:
+       if (node != NULL)
+               dns_db_detachnode(db, &node);
+       return (result);
+}
+
+static isc_result_t
+create_empty_zone(dns_zone_t *zone, dns_name_t *name, dns_view_t *view,
+                 const cfg_obj_t *zonelist, const char **empty_dbtype,
+                 int empty_dbtypec, isc_boolean_t zonestats_on)
+{
+       char namebuf[DNS_NAME_FORMATSIZE];
+       const cfg_listelt_t *element;
+       const cfg_obj_t *obj;
+       const cfg_obj_t *zconfig;
+       const cfg_obj_t *zoptions;
+       const char *rbt_dbtype[4] = { "rbt" };
+       const char *sep = ": view ";
+       const char *str;
+       const char *viewname = view->name;
+       dns_db_t *db = NULL;
+       dns_dbversion_t *version = NULL;
+       dns_fixedname_t cfixed;
+       dns_fixedname_t fixed;
+       dns_fixedname_t nsfixed;
+       dns_name_t *contact;
+       dns_name_t *ns;
+       dns_name_t *zname;
+       dns_zone_t *myzone = NULL;
+       int rbt_dbtypec = 1;
+       isc_result_t result;
+       dns_namereln_t namereln;
+       int order;
+       unsigned int nlabels;
+
+       dns_fixedname_init(&fixed);
+       zname = dns_fixedname_name(&fixed);
+       dns_fixedname_init(&nsfixed);
+       ns = dns_fixedname_name(&nsfixed);
+       dns_fixedname_init(&cfixed);
+       contact = dns_fixedname_name(&cfixed);
+
+       /*
+        * Look for forward "zones" beneath this empty zone and if so
+        * create a custom db for the empty zone.
+        */
+       for (element = cfg_list_first(zonelist);
+            element != NULL;
+            element = cfg_list_next(element)) {
+
+               zconfig = cfg_listelt_value(element);
+               str = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
+               CHECK(dns_name_fromstring(zname, str, 0, NULL));
+               namereln = dns_name_fullcompare(zname, name, &order, &nlabels);
+               if (namereln != dns_namereln_subdomain)
+                       continue;
+
+               zoptions = cfg_tuple_get(zconfig, "options");
+
+               obj = NULL;
+               (void)cfg_map_get(zoptions, "type", &obj);
+               INSIST(obj != NULL);
+               if (strcasecmp(cfg_obj_asstring(obj), "forward") != 0)
+                       continue;
+
+               obj = NULL;
+               (void)cfg_map_get(zoptions, "forward", &obj);
+               if (obj == NULL)
+                       continue;
+               if (strcasecmp(cfg_obj_asstring(obj), "only") != 0)
+                       continue;
+               if (db == NULL) {
+                       CHECK(dns_db_create(view->mctx, "rbt", name,
+                                           dns_dbtype_zone, view->rdclass,
+                                           0, NULL, &db));
+                       CHECK(dns_db_newversion(db, &version));
+                       if (strcmp(empty_dbtype[2], "@") == 0)
+                               dns_name_clone(name, ns);
+                       else
+                               CHECK(dns_name_fromstring(ns, empty_dbtype[2],
+                                                         0, NULL));
+                       CHECK(dns_name_fromstring(contact, empty_dbtype[3],
+                                                 0, NULL));
+                       CHECK(add_soa(db, version, name, ns, contact));
+                       CHECK(add_ns(db, version, name, ns));
+               }
+               CHECK(add_ns(db, version, zname, dns_rootname));
+       }
+
+       /*
+        * Is the existing zone the ok to use?
+        */
+       if (zone != NULL) {
+               if (db != NULL)
+                       check_dbtype(&zone, rbt_dbtypec, rbt_dbtype,
+                                    view->mctx);
+               else
+                       check_dbtype(&zone, empty_dbtypec, empty_dbtype,
+                                    view->mctx);
+               if (zone != NULL && dns_zone_gettype(zone) != dns_zone_master)
+                       zone = NULL;
+               if (zone != NULL && dns_zone_getfile(zone) != NULL)
+                       zone = NULL;
+       }
+
+       if (zone == NULL) {
+               CHECK(dns_zone_create(&myzone, view->mctx));
+               zone = myzone;
+               CHECK(dns_zone_setorigin(zone, name));
+               CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
+               if (db == NULL)
+                       CHECK(dns_zone_setdbtype(zone, empty_dbtypec,
+                                                empty_dbtype));
+               dns_zone_setclass(zone, view->rdclass);
+               dns_zone_settype(zone, dns_zone_master);
+               dns_zone_setstats(zone, ns_g_server->zonestats);
+       }
+
+       dns_zone_setoption(zone, ~DNS_ZONEOPT_NOCHECKNS, ISC_FALSE);
+       dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, ISC_TRUE);
+       dns_zone_setnotifytype(zone, dns_notifytype_no);
+       dns_zone_setdialup(zone, dns_dialuptype_no);
+       if (view->queryacl)
+               dns_zone_setqueryacl(zone, view->queryacl);
+       else
+               dns_zone_clearqueryacl(zone);
+       if (view->queryonacl)
+               dns_zone_setqueryonacl(zone, view->queryonacl);
+       else
+               dns_zone_clearqueryonacl(zone);
+       dns_zone_clearupdateacl(zone);
+       dns_zone_clearxfracl(zone);
+
+       CHECK(setquerystats(zone, view->mctx, zonestats_on));
+       if (db != NULL) {
+               dns_db_closeversion(db, &version, ISC_TRUE);
+               CHECK(dns_zone_replacedb(zone, db, ISC_FALSE));
+       }
+       dns_zone_setview(zone, view);
+       CHECK(dns_view_addzone(view, zone));
+
+       if (!strcmp(viewname, "_default")) {
+               sep = "";
+               viewname = "";
+       }
+       dns_name_format(name, namebuf, sizeof(namebuf));
+       isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+                     ISC_LOG_INFO, "automatic empty zone%s%s: %s",
+                     sep, viewname, namebuf);
+
+ cleanup:
+       if (myzone != NULL)
+               dns_zone_detach(&myzone);
+       if (version != NULL)
+               dns_db_closeversion(db, &version, ISC_FALSE);
+       if (db != NULL)
+               dns_db_detach(&db);
+       return (result);
+}
+
 /*
  * Configure 'view' according to 'vconfig', taking defaults from 'config'
  * where values are missing in 'vconfig'.
@@ -2045,43 +2319,13 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
                        if (pview != NULL) {
                                (void)dns_view_findzone(pview, name, &zone);
                                dns_view_detach(&pview);
-                               if (zone != NULL)
-                                       check_dbtype(&zone, empty_dbtypec,
-                                                    empty_dbtype, mctx);
-                               if (zone != NULL) {
-                                       dns_zone_setview(zone, view);
-                                       CHECK(dns_view_addzone(view, zone));
-                                       CHECK(setquerystats(zone, mctx,
-                                                           zonestats_on));
-                                       dns_zone_detach(&zone);
-                                       continue;
-                               }
                        }
 
-                       CHECK(dns_zone_create(&zone, mctx));
-                       CHECK(dns_zone_setorigin(zone, name));
-                       dns_zone_setview(zone, view);
-                       CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
-                       dns_zone_setclass(zone, view->rdclass);
-                       dns_zone_settype(zone, dns_zone_master);
-                       dns_zone_setstats(zone, ns_g_server->zonestats);
-                       CHECK(dns_zone_setdbtype(zone, empty_dbtypec,
-                                                empty_dbtype));
-                       if (view->queryacl != NULL)
-                               dns_zone_setqueryacl(zone, view->queryacl);
-                       if (view->queryonacl != NULL)
-                               dns_zone_setqueryonacl(zone, view->queryonacl);
-                       dns_zone_setdialup(zone, dns_dialuptype_no);
-                       dns_zone_setnotifytype(zone, dns_notifytype_no);
-                       dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS,
-                                          ISC_TRUE);
-                       CHECK(setquerystats(zone, mctx, zonestats_on));
-                       CHECK(dns_view_addzone(view, zone));
-                       isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
-                                     NS_LOGMODULE_SERVER, ISC_LOG_INFO,
-                                     "automatic empty zone%s%s: %s",
-                                     sep, viewname,  empty);
-                       dns_zone_detach(&zone);
+                       CHECK(create_empty_zone(zone, name, view, zonelist,
+                                               empty_dbtype, empty_dbtypec,
+                                               zonestats_on));
+                       if (zone != NULL)
+                               dns_zone_detach(&zone);
                }
        }
 
index d310bf247f29014000a970f6cba8e2be959652ea..73d5864a21e7b3dfed3755c55ffa9f1fdcd3ce94 100644 (file)
@@ -54,3 +54,8 @@ zone "example4." {
        type master;
        file "example.db";
 };
+
+zone "1.0.10.in-addr.arpa." {
+       type master;
+       file "example.db";
+};
index f817b8a3afc83fcb36411179a8ca4174e4f87271..a05a390e943773a08d7172c88e4f88f17cf48b99 100644 (file)
@@ -50,3 +50,9 @@ zone "example5." {
        forward only;
        forwarders { 10.53.0.2; };
 };
+
+zone "1.0.10.in-addr.arpa" {
+       type forward;
+       forward only;
+       forwarders { 10.53.0.2; };
+};
index dceddb8c6163a59164c4883281051b76c37f1700..3dc8f43e4b6c6d2e89c3709a43409617746df698 100644 (file)
@@ -101,5 +101,14 @@ $PERL ../start.pl --restart --noclean . ns4 || ret=1
 if [ $ret != 0 ]; then echo "I:failed"; fi
 status=`expr $status + $ret`
 
+echo "I:checking that forward only zone overrides empty zone"
+ret=0
+$DIG 1.0.10.in-addr.arpa TXT @10.53.0.4 -p 5300 > dig.out.f2
+grep "status: NOERROR" dig.out.f2 > /dev/null || ret=1
+$DIG 2.0.10.in-addr.arpa TXT @10.53.0.4 -p 5300 > dig.out.f2
+grep "status: NXDOMAIN" dig.out.f2 > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
 echo "I:exit status: $status"
 exit $status