From: Mark Andrews Date: Fri, 16 Aug 2013 03:54:23 +0000 (+1000) Subject: 3636. [bug] Automatic empty zones now behave better with X-Git-Tag: v9.6-ESV-R10rc2~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6e5bc5165e6dc759d5fd06193bcaae6483366737;p=thirdparty%2Fbind9.git 3636. [bug] Automatic empty zones now behave better with forward only "zones" beneath them. [RT #34583] (cherry picked from commit e548e07a9a2f1ec64774d7ae872d530eaf270eb7) --- diff --git a/CHANGES b/CHANGES index 379a99dfb12..843c3ea5596 100644 --- 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] diff --git a/bin/named/server.c b/bin/named/server.c index cc1ad72ef42..6f439f2b1ca 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -68,11 +68,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -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); } } diff --git a/bin/tests/system/forward/ns2/named.conf b/bin/tests/system/forward/ns2/named.conf index d310bf247f2..73d5864a21e 100644 --- a/bin/tests/system/forward/ns2/named.conf +++ b/bin/tests/system/forward/ns2/named.conf @@ -54,3 +54,8 @@ zone "example4." { type master; file "example.db"; }; + +zone "1.0.10.in-addr.arpa." { + type master; + file "example.db"; +}; diff --git a/bin/tests/system/forward/ns4/named.conf b/bin/tests/system/forward/ns4/named.conf index f817b8a3afc..a05a390e943 100644 --- a/bin/tests/system/forward/ns4/named.conf +++ b/bin/tests/system/forward/ns4/named.conf @@ -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; }; +}; diff --git a/bin/tests/system/forward/tests.sh b/bin/tests/system/forward/tests.sh index dceddb8c616..3dc8f43e4b6 100644 --- a/bin/tests/system/forward/tests.sh +++ b/bin/tests/system/forward/tests.sh @@ -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