diff --git a/bin/named/config.c b/bin/named/config.c index babf61ad8f..cc36edad98 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -387,6 +387,23 @@ named_config_get(cfg_obj_t const *const *maps, const char *name, return ISC_R_NOTFOUND; } +isc_result_t +named_config_findopt(const cfg_obj_t *opts1, const cfg_obj_t *opts2, + const char *name, const cfg_obj_t **objp) { + isc_result_t result = ISC_R_NOTFOUND; + + REQUIRE(*objp == NULL); + + if (opts1 != NULL) { + result = cfg_map_get(opts1, name, objp); + } + if (*objp == NULL && opts2 != NULL) { + result = cfg_map_get(opts2, name, objp); + } + + return result; +} + isc_result_t named_checknames_get(const cfg_obj_t **maps, const char *const names[], const cfg_obj_t **obj) { diff --git a/bin/named/include/named/config.h b/bin/named/include/named/config.h index 0be3b12b9b..62f0e3c815 100644 --- a/bin/named/include/named/config.h +++ b/bin/named/include/named/config.h @@ -67,3 +67,7 @@ named_config_getport(const cfg_obj_t *config, const char *type, isc_result_t named_config_getkeyalgorithm(const char *str, unsigned int *typep, uint16_t *digestbits); + +isc_result_t +named_config_findopt(const cfg_obj_t *opts1, const cfg_obj_t *opts2, + const char *name, const cfg_obj_t **objp); diff --git a/bin/named/include/named/zoneconf.h b/bin/named/include/named/zoneconf.h index a10a650fc7..e96be44d3e 100644 --- a/bin/named/include/named/zoneconf.h +++ b/bin/named/include/named/zoneconf.h @@ -63,7 +63,7 @@ named_zone_inlinesigning(const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, isc_result_t named_zone_configure_writeable_dlz(dns_dlzdb_t *dlzdatabase, dns_zone_t *zone, dns_rdataclass_t rdclass, dns_name_t *name); -/*%> +/*%< * configure a DLZ zone, setting up the database methods and calling * postload to load the origin values * @@ -73,3 +73,11 @@ named_zone_configure_writeable_dlz(dns_dlzdb_t *dlzdatabase, dns_zone_t *zone, * \li 'rdclass' to be a valid rdataclass * \li 'name' to be a valid zone origin name */ + +const cfg_obj_t * +named_zone_templateopts(const cfg_obj_t *config, const cfg_obj_t *zoptions); +/*%< + * If a zone with options `zoptions` specifies a zone template, look + * the template options and return them. If no such template is found, + * return NULL. + */ diff --git a/bin/named/server.c b/bin/named/server.c index 7c35cc4a51..cccd192405 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -3097,12 +3097,12 @@ cleanup: static isc_result_t create_empty_zone(dns_zone_t *pzone, dns_name_t *name, dns_view_t *view, - const cfg_obj_t *zonelist, const char **empty_dbtype, - int empty_dbtypec, dns_zonestat_level_t statlevel) { + const cfg_obj_t *config, const cfg_obj_t *voptions, + const char **empty_dbtype, int empty_dbtypec, + dns_zonestat_level_t statlevel) { char namebuf[DNS_NAME_FORMATSIZE]; const cfg_obj_t *obj = NULL; - const cfg_obj_t *zconfig = NULL; - const cfg_obj_t *zoptions = NULL; + const cfg_obj_t *zonelist = NULL; const char *default_dbtype[4] = { ZONEDB_DEFAULT }; const char *sep = ": view "; const char *str = NULL; @@ -3126,12 +3126,20 @@ create_empty_zone(dns_zone_t *pzone, dns_name_t *name, dns_view_t *view, ns = dns_fixedname_initname(&nsfixed); contact = dns_fixedname_initname(&cfixed); + if (voptions != NULL) { + (void)cfg_map_get(voptions, "zone", &zonelist); + } else { + (void)cfg_map_get(config, "zone", &zonelist); + } /* * Look for forward "zones" beneath this empty zone and if so * create a custom db for the empty zone. */ CFG_LIST_FOREACH (zonelist, element) { - zconfig = cfg_listelt_value(element); + const cfg_obj_t *zconfig = cfg_listelt_value(element); + const cfg_obj_t *zoptions = cfg_tuple_get(zconfig, "options"); + const cfg_obj_t *toptions = NULL; + str = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); CHECK(dns_name_fromstring(zname, str, dns_rootname, 0, NULL)); namereln = dns_name_fullcompare(zname, name, &order, &nlabels); @@ -3139,15 +3147,16 @@ create_empty_zone(dns_zone_t *pzone, dns_name_t *name, dns_view_t *view, continue; } - zoptions = cfg_tuple_get(zconfig, "options"); + toptions = named_zone_templateopts(config, zoptions); obj = NULL; - (void)cfg_map_get(zoptions, "type", &obj); + (void)named_config_findopt(zoptions, toptions, "type", &obj); if (obj != NULL && strcasecmp(cfg_obj_asstring(obj), "forward") == 0) { obj = NULL; - (void)cfg_map_get(zoptions, "forward", &obj); + (void)named_config_findopt(zoptions, toptions, + "forward", &obj); if (obj == NULL) { continue; } @@ -5468,9 +5477,9 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, dns_view_detach(&pview); } - CHECK(create_empty_zone(zone, name, view, zonelist, - empty_dbtype, empty_dbtypec, - statlevel)); + CHECK(create_empty_zone(zone, name, view, config, + voptions, empty_dbtype, + empty_dbtypec, statlevel)); if (zone != NULL) { dns_zone_detach(&zone); } @@ -6151,6 +6160,7 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, dns_zone_t *dupzone = NULL; const cfg_obj_t *options = NULL; const cfg_obj_t *zoptions = NULL; + const cfg_obj_t *toptions = NULL; const cfg_obj_t *typeobj = NULL; const cfg_obj_t *forwarders = NULL; const cfg_obj_t *forwardtype = NULL; @@ -6160,10 +6170,10 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, isc_result_t tresult; isc_buffer_t buffer; dns_fixedname_t fixorigin; - dns_name_t *origin; - const char *zname; + dns_name_t *origin = NULL; + const char *zname = NULL; dns_rdataclass_t zclass; - const char *ztypestr; + const char *ztypestr = NULL; dns_rpz_num_t rpz_num; bool zone_is_catz = false; bool zone_maybe_inline = false; @@ -6174,6 +6184,7 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, (void)cfg_map_get(config, "options", &options); zoptions = cfg_tuple_get(zconfig, "options"); + toptions = named_zone_templateopts(config, zoptions); /* * Get the zone origin as a dns_name_t. @@ -6258,7 +6269,7 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, goto cleanup; } - (void)cfg_map_get(zoptions, "type", &typeobj); + (void)named_config_findopt(zoptions, toptions, "type", &typeobj); if (typeobj == NULL) { cfg_obj_log(zconfig, ISC_LOG_ERROR, "zone '%s' 'type' not specified", zname); @@ -6273,7 +6284,9 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, */ if (strcasecmp(ztypestr, "hint") == 0) { const cfg_obj_t *fileobj = NULL; - if (cfg_map_get(zoptions, "file", &fileobj) != ISC_R_SUCCESS) { + (void)named_config_findopt(zoptions, toptions, "file", + &fileobj); + if (fileobj == NULL) { isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, "zone '%s': 'file' not specified", zname); @@ -6303,8 +6316,10 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, forwardtype = NULL; forwarders = NULL; - (void)cfg_map_get(zoptions, "forward", &forwardtype); - (void)cfg_map_get(zoptions, "forwarders", &forwarders); + (void)named_config_findopt(zoptions, toptions, "forward", + &forwardtype); + (void)named_config_findopt(zoptions, toptions, "forwarders", + &forwarders); CHECK(configure_forward(config, view, origin, forwarders, forwardtype)); goto cleanup; @@ -6463,9 +6478,11 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, * selective forwarding. */ forwarders = NULL; - if (cfg_map_get(zoptions, "forwarders", &forwarders) == ISC_R_SUCCESS) { + named_config_findopt(zoptions, toptions, "forwarders", &forwarders); + if (forwarders != NULL) { forwardtype = NULL; - (void)cfg_map_get(zoptions, "forward", &forwardtype); + named_config_findopt(zoptions, toptions, "forward", + &forwardtype); CHECK(configure_forward(config, view, origin, forwarders, forwardtype)); } @@ -6497,9 +6514,9 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig, dns_zone_setstats(raw, named_g_server->zonestats); CHECK(dns_zone_link(zone, raw)); } - if (cfg_map_get(zoptions, "ixfr-from-differences", - &ixfrfromdiffs) == ISC_R_SUCCESS) - { + named_config_findopt(zoptions, toptions, + "ixfr-from-differences", &ixfrfromdiffs); + if (ixfrfromdiffs != NULL) { isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, ISC_LOG_INFO, "zone '%s': 'ixfr-from-differences' is " @@ -12459,11 +12476,9 @@ nzd_save(MDB_txn **txnp, MDB_dbi dbi, dns_zone_t *zone, } } else { /* We're creating or overwriting the zone */ - const cfg_obj_t *zoptions; + const cfg_obj_t *zoptions = cfg_tuple_get(zconfig, "options"); isc_buffer_allocate(view->mctx, &text, 256); - - zoptions = cfg_tuple_get(zconfig, "options"); if (zoptions == NULL) { isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR, @@ -12785,16 +12800,14 @@ load_nzf(dns_view_t *view, ns_cfgctx_t *nzcfg) { isc_buffer_allocate(view->mctx, &text, 256); CFG_LIST_FOREACH (zonelist, element) { - const cfg_obj_t *zconfig; + const cfg_obj_t *zconfig = cfg_listelt_value(element); const cfg_obj_t *zoptions; char zname[DNS_NAME_FORMATSIZE]; dns_fixedname_t fname; - dns_name_t *name; - const char *origin; + dns_name_t *name = NULL; + const char *origin = NULL; isc_buffer_t b; - zconfig = cfg_listelt_value(element); - origin = cfg_obj_asstring(cfg_tuple_get(zconfig, "name")); if (origin == NULL) { result = ISC_R_FAILURE; @@ -12943,10 +12956,15 @@ newzone_parse(named_server_t *server, char *command, dns_view_t **viewp, if (obj != NULL) { (void)putstr(text, "'in-view' zones not supported by "); (void)putstr(text, bn); - } else { - (void)putstr(text, "zone type not specified"); + CHECK(ISC_R_FAILURE); + } + + (void)cfg_map_get(zoptions, "template", &obj); + if (obj == NULL) { + (void)putstr(text, "no zone type or " + "template specified"); + CHECK(ISC_R_FAILURE); } - CHECK(ISC_R_FAILURE); } if (strcasecmp(cfg_obj_asstring(obj), "hint") == 0 || @@ -13518,14 +13536,12 @@ named_server_changezone(named_server_t *server, char *command, (void)putstr(text, "Not allowing new zones in view '"); (void)putstr(text, view->name); (void)putstr(text, "'"); - result = ISC_R_NOPERM; - goto cleanup; + CHECK(ISC_R_NOPERM); } cfg = (ns_cfgctx_t *)view->new_zone_config; if (cfg == NULL) { - result = ISC_R_FAILURE; - goto cleanup; + CHECK(ISC_R_FAILURE); } zonename = cfg_obj_asstring(cfg_tuple_get(zoneobj, "name")); @@ -13922,12 +13938,16 @@ find_name_in_list_from_map(const cfg_obj_t *config, if (result == ISC_R_SUCCESS && dns_name_equal(name1, name2)) { - const cfg_obj_t *zoptions; + const cfg_obj_t *zoptions = + cfg_tuple_get(obj, "options"); const cfg_obj_t *typeobj = NULL; - zoptions = cfg_tuple_get(obj, "options"); if (zoptions != NULL) { - cfg_map_get(zoptions, "type", &typeobj); + const cfg_obj_t *toptions = + named_zone_templateopts( + config, zoptions); + named_config_findopt(zoptions, toptions, + "type", &typeobj); } if (redirect && typeobj != NULL && strcasecmp(cfg_obj_asstring(typeobj), diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index ca24ec9851..0e069b1309 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -77,12 +77,12 @@ configure_zone_acl(const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, void (*setzacl)(dns_zone_t *, dns_acl_t *), void (*clearzacl)(dns_zone_t *)) { isc_result_t result; - const cfg_obj_t *maps[5] = { NULL, NULL, NULL, NULL, NULL }; + const cfg_obj_t *maps[6] = { 0 }; const cfg_obj_t *aclobj = NULL; int i = 0; dns_acl_t **aclp = NULL, *acl = NULL; const char *aclname; - dns_view_t *view; + dns_view_t *view = NULL; view = dns_zone_getview(zone); @@ -129,7 +129,7 @@ configure_zone_acl(const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, /* First check to see if ACL is defined within the zone */ if (zconfig != NULL) { - maps[0] = cfg_tuple_get(zconfig, "options"); + maps[i] = cfg_tuple_get(zconfig, "options"); (void)named_config_get(maps, aclname, &aclobj); if (aclobj != NULL) { aclp = NULL; @@ -137,6 +137,14 @@ configure_zone_acl(const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, } } + if (config != NULL && maps[i] != NULL) { + const cfg_obj_t *toptions = named_zone_templateopts(config, + maps[i]); + if (toptions != NULL) { + maps[i++] = toptions; + } + } + /* Failing that, see if there's a default ACL already in the view */ if (aclp != NULL && *aclp != NULL) { (*setzacl)(zone, *aclp); @@ -187,8 +195,8 @@ parse_acl: * Parse the zone update-policy statement. */ static isc_result_t -configure_zone_ssutable(const cfg_obj_t *zconfig, dns_zone_t *zone, - const char *zname) { +configure_zone_ssutable(const cfg_obj_t *zconfig, const cfg_obj_t *tconfig, + dns_zone_t *zone, const char *zname) { const cfg_obj_t *updatepolicy = NULL; dns_ssutable_t *table = NULL; isc_mem_t *mctx = dns_zone_getmctx(zone); @@ -200,8 +208,8 @@ configure_zone_ssutable(const cfg_obj_t *zconfig, dns_zone_t *zone, isc_buffer_init(&dbuf, debug, sizeof(debug)); isc_buffer_setmctx(&dbuf, mctx); - (void)cfg_map_get(zconfig, "update-policy", &updatepolicy); - + (void)named_config_findopt(zconfig, tconfig, "update-policy", + &updatepolicy); if (updatepolicy == NULL) { dns_zone_setssutable(zone, NULL); return ISC_R_SUCCESS; @@ -540,8 +548,8 @@ configure_staticstub_servernames(const cfg_obj_t *zconfig, dns_zone_t *zone, * Configure static-stub zone. */ static isc_result_t -configure_staticstub(const cfg_obj_t *zconfig, dns_zone_t *zone, - const char *zname, const char *dbtype) { +configure_staticstub(const cfg_obj_t *zconfig, const cfg_obj_t *tconfig, + dns_zone_t *zone, const char *zname, const char *dbtype) { int i = 0; const cfg_obj_t *obj; isc_mem_t *mctx = dns_zone_getmctx(zone); @@ -583,18 +591,16 @@ configure_staticstub(const cfg_obj_t *zconfig, dns_zone_t *zone, /* Prepare zone RRs from the configuration */ obj = NULL; - result = cfg_map_get(zconfig, "server-addresses", &obj); - if (result == ISC_R_SUCCESS) { - INSIST(obj != NULL); + (void)named_config_findopt(zconfig, tconfig, "server-addresses", &obj); + if (obj != NULL) { CHECK(configure_staticstub_serveraddrs(obj, zone, &rdatalist_ns, &rdatalist_a, &rdatalist_aaaa)); } obj = NULL; - result = cfg_map_get(zconfig, "server-names", &obj); - if (result == ISC_R_SUCCESS) { - INSIST(obj != NULL); + (void)named_config_findopt(zconfig, tconfig, "server-names", &obj); + if (obj != NULL) { CHECK(configure_staticstub_servernames(obj, zone, &rdatalist_ns, zname)); } @@ -682,12 +688,11 @@ cleanup: * Convert a config file zone type into a server zone type. */ static dns_zonetype_t -zonetype_fromconfig(const cfg_obj_t *map) { +zonetype_fromconfig(const cfg_obj_t *zmap, const cfg_obj_t *tmap) { const cfg_obj_t *obj = NULL; - isc_result_t result; - result = cfg_map_get(map, "type", &obj); - INSIST(result == ISC_R_SUCCESS && obj != NULL); + (void)named_config_findopt(zmap, tmap, "type", &obj); + INSIST(obj != NULL); return named_config_getzonetype(obj); } @@ -872,11 +877,13 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, const char *zname; dns_rdataclass_t zclass; dns_rdataclass_t vclass; - const cfg_obj_t *maps[5]; - const cfg_obj_t *nodefault[4]; + const cfg_obj_t *maps[6]; + const cfg_obj_t *nodefault[5]; + const cfg_obj_t *nooptions[3]; const cfg_obj_t *zoptions = NULL; + const cfg_obj_t *toptions = NULL; const cfg_obj_t *options = NULL; - const cfg_obj_t *obj; + const cfg_obj_t *obj = NULL; const char *filename = NULL; const char *initial_file = NULL; const char *kaspname = NULL; @@ -910,23 +917,34 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, bool transferinsecs = ns_server_getoption(named_g_server->sctx, NS_SERVER_TRANSFERINSECS); + REQUIRE(config != NULL); + REQUIRE(zconfig != NULL); + i = 0; - if (zconfig != NULL) { - zoptions = cfg_tuple_get(zconfig, "options"); - nodefault[i] = maps[i] = zoptions; + + zoptions = cfg_tuple_get(zconfig, "options"); + INSIST(zoptions != NULL); + nodefault[i] = nooptions[i] = maps[i] = zoptions; + i++; + + toptions = named_zone_templateopts(config, zoptions); + if (toptions != NULL) { + nodefault[i] = nooptions[i] = maps[i] = toptions; i++; } + + nooptions[i] = NULL; if (vconfig != NULL) { nodefault[i] = maps[i] = cfg_tuple_get(vconfig, "options"); i++; } - if (config != NULL) { - (void)cfg_map_get(config, "options", &options); - if (options != NULL) { - nodefault[i] = maps[i] = options; - i++; - } + + (void)cfg_map_get(config, "options", &options); + if (options != NULL) { + nodefault[i] = maps[i] = options; + i++; } + nodefault[i] = NULL; maps[i++] = named_g_defaults; maps[i] = NULL; @@ -951,7 +969,7 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, dns_zone_setclass(raw, zclass); } - ztype = zonetype_fromconfig(zoptions); + ztype = zonetype_fromconfig(zoptions, toptions); if (raw != NULL) { dns_zone_settype(raw, ztype); dns_zone_settype(zone, dns_zone_primary); @@ -960,13 +978,13 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, } obj = NULL; - result = cfg_map_get(zoptions, "database", &obj); + result = named_config_get(nooptions, "database", &obj); if (result == ISC_R_SUCCESS) { cpval = isc_mem_strdup(mctx, cfg_obj_asstring(obj)); } obj = NULL; - result = cfg_map_get(zoptions, "dlz", &obj); + result = named_config_get(nooptions, "dlz", &obj); if (result == ISC_R_SUCCESS) { const char *dlzname = cfg_obj_asstring(obj); size_t len = strlen(dlzname) + 5; @@ -992,13 +1010,13 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, } obj = NULL; - result = cfg_map_get(zoptions, "file", &obj); + result = named_config_get(nooptions, "file", &obj); if (result == ISC_R_SUCCESS) { filename = cfg_obj_asstring(obj); } obj = NULL; - result = cfg_map_get(zoptions, "initial-file", &obj); + result = named_config_get(nooptions, "initial-file", &obj); if (result == ISC_R_SUCCESS) { initial_file = cfg_obj_asstring(obj); } @@ -1074,7 +1092,7 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, } obj = NULL; - result = cfg_map_get(zoptions, "journal", &obj); + result = named_config_get(nooptions, "journal", &obj); if (result == ISC_R_SUCCESS) { dns_zone_setjournal(mayberaw, cfg_obj_asstring(obj)); } @@ -1433,7 +1451,8 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, cfg_obj_asboolean(obj)); obj = NULL; - result = cfg_map_get(zoptions, "log-report-channel", &obj); + result = named_config_get(nooptions, "log-report-channel", + &obj); if (result == ISC_R_SUCCESS) { logreports = cfg_obj_asboolean(obj); dns_zone_setoption(zone, DNS_ZONEOPT_LOGREPORTS, @@ -1533,7 +1552,8 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, zname); } - CHECK(configure_zone_ssutable(zoptions, mayberaw, zname)); + CHECK(configure_zone_ssutable(zoptions, toptions, mayberaw, + zname)); } /* @@ -1618,7 +1638,8 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, */ if (ztype == dns_zone_primary || ztype == dns_zone_secondary) { const cfg_obj_t *parentals = NULL; - (void)cfg_map_get(zoptions, "parental-agents", &parentals); + (void)named_config_get(nooptions, "parental-agents", + &parentals); if (parentals != NULL) { dns_ipkeylist_t ipkl; dns_ipkeylist_init(&ipkl); @@ -1769,7 +1790,7 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, * are explicitly enabled by zone configuration. */ obj = NULL; - (void)cfg_map_get(zoptions, "allow-transfer", &obj); + (void)named_config_get(nooptions, "allow-transfer", &obj); if (obj == NULL) { dns_acl_t *none; CHECK(dns_acl_none(mctx, &none)); @@ -1782,9 +1803,9 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, case dns_zone_redirect: count = 0; obj = NULL; - (void)cfg_map_get(zoptions, "primaries", &obj); + (void)named_config_get(nooptions, "primaries", &obj); if (obj == NULL) { - (void)cfg_map_get(zoptions, "masters", &obj); + (void)named_config_get(nooptions, "masters", &obj); } /* @@ -1886,7 +1907,7 @@ named_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig, break; case dns_zone_staticstub: - CHECK(configure_staticstub(zoptions, zone, zname, + CHECK(configure_staticstub(zoptions, toptions, zone, zname, default_dbtype)); break; @@ -1927,20 +1948,22 @@ named_zone_reusable(dns_zone_t *zone, const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, const cfg_obj_t *config, dns_kasplist_t *kasplist) { const cfg_obj_t *zoptions = NULL; + const cfg_obj_t *toptions = NULL; const cfg_obj_t *obj = NULL; - const char *cfilename; - const char *zfilename; + const char *cfilename = NULL; + const char *zfilename = NULL; dns_zone_t *raw = NULL; bool has_raw, inline_signing; dns_zonetype_t ztype; zoptions = cfg_tuple_get(zconfig, "options"); + toptions = named_zone_templateopts(config, zoptions); /* * We always reconfigure a static-stub zone for simplicity, assuming * the amount of data to be loaded is small. */ - if (zonetype_fromconfig(zoptions) == dns_zone_staticstub) { + if (zonetype_fromconfig(zoptions, toptions) == dns_zone_staticstub) { dns_zone_log(zone, ISC_LOG_DEBUG(1), "not reusable: staticstub"); return false; @@ -1971,14 +1994,14 @@ named_zone_reusable(dns_zone_t *zone, const cfg_obj_t *zconfig, return false; } - if (zonetype_fromconfig(zoptions) != ztype) { + if (zonetype_fromconfig(zoptions, toptions) != ztype) { dns_zone_log(zone, ISC_LOG_DEBUG(1), "not reusable: type mismatch"); return false; } obj = NULL; - (void)cfg_map_get(zoptions, "file", &obj); + (void)named_config_findopt(zoptions, toptions, "file", &obj); if (obj != NULL) { cfilename = cfg_obj_asstring(obj); } else { @@ -1999,15 +2022,27 @@ named_zone_reusable(dns_zone_t *zone, const cfg_obj_t *zconfig, bool named_zone_inlinesigning(const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, const cfg_obj_t *config, dns_kasplist_t *kasplist) { - const cfg_obj_t *maps[4]; + const cfg_obj_t *maps[5] = { 0 }, *noopts[3] = { 0 }; const cfg_obj_t *signing = NULL; const cfg_obj_t *policy = NULL; + const cfg_obj_t *toptions = NULL; dns_kasp_t *kasp = NULL; isc_result_t res; bool inline_signing = false; int i = 0; - maps[i++] = cfg_tuple_get(zconfig, "options"); + noopts[i] = maps[i] = cfg_tuple_get(zconfig, "options"); + i++; + + if (config != NULL) { + toptions = named_zone_templateopts(config, maps[0]); + if (toptions != NULL) { + noopts[i] = maps[i] = toptions; + i++; + } + } + + noopts[i] = NULL; if (vconfig != NULL) { maps[i++] = cfg_tuple_get(vconfig, "options"); } @@ -2041,13 +2076,35 @@ named_zone_inlinesigning(const cfg_obj_t *zconfig, const cfg_obj_t *vconfig, /* * The zone option 'inline-signing' may override the value in - * dnssec-policy. This is a zone-only option, so look in maps[0] - * only. + * dnssec-policy. This is a zone-only option, so look in the + * zone and template blocks only. */ - res = cfg_map_get(maps[0], "inline-signing", &signing); + res = named_config_get(noopts, "inline-signing", &signing); if (res == ISC_R_SUCCESS && cfg_obj_isboolean(signing)) { return cfg_obj_asboolean(signing); } return inline_signing; } + +const cfg_obj_t * +named_zone_templateopts(const cfg_obj_t *config, const cfg_obj_t *zoptions) { + const cfg_obj_t *templates = NULL; + const cfg_obj_t *obj = NULL; + + (void)cfg_map_get(config, "template", &templates); + (void)cfg_map_get(zoptions, "template", &obj); + if (obj != NULL && templates != NULL) { + const char *tmplname = cfg_obj_asstring(obj); + CFG_LIST_FOREACH (templates, e) { + const cfg_obj_t *t = cfg_tuple_get(cfg_listelt_value(e), + "name"); + if (strcasecmp(cfg_obj_asstring(t), tmplname) == 0) { + return cfg_tuple_get(cfg_listelt_value(e), + "options"); + } + } + } + + return NULL; +} diff --git a/bin/tests/system/addzone/ns2/added.db b/bin/tests/system/addzone/ns2/added.db index 286e717532..849821078c 100644 --- a/bin/tests/system/addzone/ns2/added.db +++ b/bin/tests/system/addzone/ns2/added.db @@ -9,7 +9,6 @@ ; See the COPYRIGHT file distributed with this work for additional ; information regarding copyright ownership. -;$ORIGIN added.example. $TTL 300 ; 5 minutes @ IN SOA mname1. . ( 1 ; serial diff --git a/bin/tests/system/addzone/ns2/named1.conf.in b/bin/tests/system/addzone/ns2/named1.conf.in index bd94f6e751..e85ad22054 100644 --- a/bin/tests/system/addzone/ns2/named1.conf.in +++ b/bin/tests/system/addzone/ns2/named1.conf.in @@ -28,6 +28,12 @@ controls { inet 10.53.0.2 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; }; +template primary { + type primary; + file "$view-$name.db"; + initial-file "added.db"; +}; + zone "." { type hint; file "../../_common/root.hint"; diff --git a/bin/tests/system/addzone/ns2/named2.conf.in b/bin/tests/system/addzone/ns2/named2.conf.in index ef9adeb22f..74bc2659e0 100644 --- a/bin/tests/system/addzone/ns2/named2.conf.in +++ b/bin/tests/system/addzone/ns2/named2.conf.in @@ -27,6 +27,12 @@ options { dnssec-validation no; }; +template primary { + type primary; + file "$view-$name.db"; + initial-file "added.db"; +}; + view internal { match-clients { 10.53.0.2; }; allow-new-zones no; diff --git a/bin/tests/system/addzone/ns2/named3.conf.in b/bin/tests/system/addzone/ns2/named3.conf.in index ca934c4671..0642ac8d5d 100644 --- a/bin/tests/system/addzone/ns2/named3.conf.in +++ b/bin/tests/system/addzone/ns2/named3.conf.in @@ -27,6 +27,12 @@ options { dnssec-validation no; }; +template primary { + type primary; + file "$view-$name.db"; + initial-file "added.db"; +}; + view internal { match-clients { 10.53.0.2; }; allow-new-zones no; diff --git a/bin/tests/system/addzone/tests.sh b/bin/tests/system/addzone/tests.sh index 6d5939c896..5809709656 100755 --- a/bin/tests/system/addzone/tests.sh +++ b/bin/tests/system/addzone/tests.sh @@ -745,5 +745,29 @@ if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status + ret)) n=$((n + 1)) +echo_i "checking addzone with zone template (primary) ($n)" +ret=0 +$RNDCCMD 10.53.0.2 addzone 'template.example in external { template primary; };' 2>&1 | sed 's/^/I:ns2 /' +$DIG +norec $DIGOPTS @10.53.0.2 -b 10.53.0.2 a.template.example a >dig.out.ns2.int.$n || ret=1 +grep 'status: NOERROR' dig.out.ns2.int.$n >/dev/null || ret=1 +grep 'ANSWER: 0' dig.out.ns2.int.$n >/dev/null || ret=1 +$DIG +norec $DIGOPTS @10.53.0.2 -b 10.53.0.4 a.template.example a >dig.out.ns2.ext.$n || ret=1 +grep 'status: NOERROR' dig.out.ns2.ext.$n >/dev/null || ret=1 +grep 'ANSWER: 1' dig.out.ns2.ext.$n >/dev/null || ret=1 +test -f ns2/external-template.example.db +n=$((n + 1)) +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +echo_i "checking addzone with nonexistent template ($n)" +ret=0 +nextpart ns2/named.run >/dev/null +$RNDCCMD 10.53.0.2 addzone 'wrong.example in external { template nope; };' 2>&1 | grep -qF "failure" || ret=1 +nextpart ns2/named.run | grep -qF "zone 'wrong.example': template 'nope' not found" || ret=1 +test -f ns2/wrong-template.example.db && ret=1 +n=$((n + 1)) +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + echo_i "exit status: $status" [ $status -eq 0 ] || exit 1 diff --git a/bin/tests/system/addzone/tests_sh_addzone.py b/bin/tests/system/addzone/tests_sh_addzone.py index 4ca4440c04..af7bf4b2aa 100644 --- a/bin/tests/system/addzone/tests_sh_addzone.py +++ b/bin/tests/system/addzone/tests_sh_addzone.py @@ -26,6 +26,7 @@ pytestmark = pytest.mark.extra_artifacts( "ns2/K*.private", "ns2/K*.state", "ns2/external.nzd", + "ns2/external-template.example.db", "ns2/extra.nzd", "ns2/inline.db.jbk", "ns2/inline.db.signed", diff --git a/bin/tests/system/checkconf/bad-template-1.conf b/bin/tests/system/checkconf/bad-template-1.conf new file mode 100644 index 0000000000..1240d757ba --- /dev/null +++ b/bin/tests/system/checkconf/bad-template-1.conf @@ -0,0 +1,23 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +template a { + type primary; + file "$name.db"; + initial-file "template.db"; +}; + +zone example { + template a; + type secondary; +}; diff --git a/bin/tests/system/checkconf/bad-template-2.conf b/bin/tests/system/checkconf/bad-template-2.conf new file mode 100644 index 0000000000..7502f6c97c --- /dev/null +++ b/bin/tests/system/checkconf/bad-template-2.conf @@ -0,0 +1,22 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +template a { + type primary; + file "$name.db"; + initial-file "template.db"; +}; + +template b { + template a; +}; diff --git a/bin/tests/system/checkconf/bad-template-3.conf b/bin/tests/system/checkconf/bad-template-3.conf new file mode 100644 index 0000000000..728f4e2406 --- /dev/null +++ b/bin/tests/system/checkconf/bad-template-3.conf @@ -0,0 +1,23 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +template a { + type primary; + file "$name.db"; + initial-file "template.db"; +}; + +zone example.com { + # specify an undefined template + template c; +}; diff --git a/bin/tests/system/checkconf/bad-template-4.conf b/bin/tests/system/checkconf/bad-template-4.conf new file mode 100644 index 0000000000..d98dd43882 --- /dev/null +++ b/bin/tests/system/checkconf/bad-template-4.conf @@ -0,0 +1,17 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +zone example.com { + # specify a template, but there are no templates + template c; +}; diff --git a/bin/tests/system/checkconf/good-template-1.conf b/bin/tests/system/checkconf/good-template-1.conf new file mode 100644 index 0000000000..5637590ce7 --- /dev/null +++ b/bin/tests/system/checkconf/good-template-1.conf @@ -0,0 +1,23 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +template a { + type primary; + file "$name.db"; + initial-file "template.db"; +}; + +zone example { + template a; + file "othername.db"; +}; diff --git a/bin/tests/system/masterfile/ns2/named.conf.j2 b/bin/tests/system/masterfile/ns2/named.conf.j2 index 442d273a8c..4e66b4f1c0 100644 --- a/bin/tests/system/masterfile/ns2/named.conf.j2 +++ b/bin/tests/system/masterfile/ns2/named.conf.j2 @@ -34,14 +34,23 @@ zone "." { file "../../_common/root.hint"; }; -zone "example" { +template primary { type primary; file "$name.db"; }; +zone "example" { + template primary; +}; + zone "missing" { - type primary; - file "$name.db"; + template primary; +}; + +zone "different" { + template primary; + initial-file "example.db"; + file "alternate.db"; }; zone "initial" { diff --git a/bin/tests/system/masterfile/tests_masterfile.py b/bin/tests/system/masterfile/tests_masterfile.py index 8b25f6db07..862be26bae 100644 --- a/bin/tests/system/masterfile/tests_masterfile.py +++ b/bin/tests/system/masterfile/tests_masterfile.py @@ -17,7 +17,9 @@ import dns.zone import isctest import pytest -pytestmark = pytest.mark.extra_artifacts(["ns2/copied.db", "ns2/present.db"]) +pytestmark = pytest.mark.extra_artifacts( + ["ns2/copied.db", "ns2/present.db", "ns2/alternate.db"] +) def test_masterfile_include_semantics(): @@ -91,7 +93,20 @@ example. 300 IN SOA mname1. . 2010042407 20 20 1814400 3600 def test_masterfile_initial_file(): - """Test zone configuration with initial template files""" + """Test zone configurations with initial template files""" + # example inherited its configuration from the template, + # make sure it works + msg_soa = dns.message.make_query("example.", "SOA") + res_soa = isctest.query.tcp(msg_soa, "10.53.0.2") + expected_soa_rr = """;ANSWER +example. 300 IN SOA mname1. . 2010042407 20 20 1814400 3600 +""" + expected = dns.message.from_text(expected_soa_rr) + isctest.check.rrsets_equal(res_soa.answer, expected.answer) + + # initial uses an initial-file option with the "file" + # option set to "copied.db". make sure it works and that + # copied.db has been populated. msg_soa = dns.message.make_query("initial.", "SOA") res_soa = isctest.query.tcp(msg_soa, "10.53.0.2") expected_soa_rr = """;ANSWER @@ -101,13 +116,30 @@ initial. 300 IN SOA mname1. . 2010042407 20 20 1814400 3600 isctest.check.rrsets_equal(res_soa.answer, expected.answer) isctest.check.file_contents_equal("ns2/example.db", "ns2/copied.db") - # the 'present.db' file already existed and shouldn't load + # present uses an initial-file option, but the file 'present.db' + # already exists and is empty, so the initial-file should not be + # copied into place and the zone should not load. msg_soa = dns.message.make_query("present.", "SOA") res_soa = isctest.query.tcp(msg_soa, "10.53.0.2") isctest.check.servfail(res_soa) isctest.check.file_empty("ns2/present.db") +def test_masterfile_template_override(): + """Test zone configurations with overridden template options""" + # different inherited configuration from the template, but + # overrides the "file" option to 'alternate.db'. + msg_soa = dns.message.make_query("different.", "SOA") + res_soa = isctest.query.tcp(msg_soa, "10.53.0.2") + expected_soa_rr = """;ANSWER +different. 300 IN SOA mname1. . 2010042407 20 20 1814400 3600 +""" + expected = dns.message.from_text(expected_soa_rr) + isctest.check.rrsets_equal(res_soa.answer, expected.answer) + isctest.check.file_contents_equal("ns2/example.db", "ns2/alternate.db") + assert not os.path.exists("ns2/different.db") + + def test_masterfile_missing_master_file_servfail(): """Test nameserver returning SERVFAIL for a missing master file""" msg_soa = dns.message.make_query("missing.", "SOA") diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 63535eddde..a26db7103a 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -7038,6 +7038,13 @@ mid-1970s. Zone data for it can be specified with the ``CHAOS`` class. Zone Options ^^^^^^^^^^^^ +.. namedconf:statement:: template + :tags: zone + :short: Specifies a template to use for zone configuration. + + This specifies a zone template from which to import other zone options. + See :ref:`zone_templates` for details. + :any:`allow-notify` See the description of :any:`allow-notify` in :ref:`access_control`. @@ -7377,6 +7384,52 @@ Zone Options :any:`send-report-channel` See the description of :any:`send-report-channel` in :namedconf:ref:`options`. +.. _zone_templates: + +Zone Templates +^^^^^^^^^^^^^^ + +To simplify the configuration of multiple similar zones, BIND 9 +supports a zone template mechanism. ``template`` blocks can be +defined at the top level of the configuration; these blocks can +contain any set of options that could be set in a :any:`zone` +statement, with the exceptions of :any:`in-view` and :any:`template`. + +Once a template has been defined, it can be referenced in a +:any:`zone` statement; the zone is then configured using the +options specified in the :any:`template` as defaults. +Options that are locally defined within the :any:`zone` statement +override the template. + +For example, the following configuration would define two primary +and two secondary zones: + + :: + + template primary { + type primary; + file "$type/$name.db"; + initial-file "initial.db"; + }; + + template secondary { + type secondary; + file "$type/$name.db"; + primaries { 192.0.2.1; }; + }; + + zone example.com { template primary; }; + zone example.org { template primary; }; + zone example.net { template secondary; }; + zone example.edu { template secondary; }; + +Templates can also be used for zones that are added using +``rndc addzone`` (see :any:`allow-new-zones`): + + :: + + $ rndc addzone example.biz '{ template secondary; };' + .. _dynamic_update_policies: Dynamic Update Policies diff --git a/doc/misc/forward.zoneopt b/doc/misc/forward.zoneopt index a0d26b12d8..af060cf347 100644 --- a/doc/misc/forward.zoneopt +++ b/doc/misc/forward.zoneopt @@ -2,4 +2,5 @@ zone [ ] { type forward; forward ( first | only ); forwarders [ port ] [ tls ] { ( | ) [ port ] [ tls ]; ... }; + template ; }; diff --git a/doc/misc/hint.zoneopt b/doc/misc/hint.zoneopt index 2d2c98de4d..260db7fb5f 100644 --- a/doc/misc/hint.zoneopt +++ b/doc/misc/hint.zoneopt @@ -2,4 +2,5 @@ zone [ ] { type hint; check-names ( fail | warn | ignore ); file ; + template ; }; diff --git a/doc/misc/mirror.zoneopt b/doc/misc/mirror.zoneopt index 2f49a34e9c..aa193235a7 100644 --- a/doc/misc/mirror.zoneopt +++ b/doc/misc/mirror.zoneopt @@ -38,6 +38,7 @@ zone [ ] { request-expire ; request-ixfr ; request-ixfr-max-diffs ; + template ; transfer-source ( | * ); transfer-source-v6 ( | * ); try-tcp-refresh ; diff --git a/doc/misc/options b/doc/misc/options index 1cb0d96443..3215fc7af7 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -362,6 +362,93 @@ statistics-channels { inet ( | | * ) [ port ( | * ) ] [ allow { ; ... } ]; // may occur multiple times }; // optional (only available if configured), may occur multiple times +template { + allow-notify { ; ... }; + allow-query { ; ... }; + allow-query-on { ; ... }; + allow-transfer [ port ] [ transport ] { ; ... }; + allow-update { ; ... }; + allow-update-forwarding { ; ... }; + also-notify [ port ] [ source ( | * ) ] [ source-v6 ( | * ) ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + check-dup-records ( fail | warn | ignore ); + check-integrity ; + check-mx ( fail | warn | ignore ); + check-mx-cname ( fail | warn | ignore ); + check-names ( fail | warn | ignore ); + check-sibling ; + check-spf ( warn | ignore ); + check-srv-cname ( fail | warn | ignore ); + check-svcb ; + check-wildcard ; + checkds ( explicit | ); + database ; + dlz ; + dnskey-sig-validity ; // obsolete + dnssec-dnskey-kskonly ; // obsolete + dnssec-loadkeys-interval ; + dnssec-policy ; + dnssec-secure-to-insecure ; // obsolete + dnssec-update-mode ( maintain | no-resign ); // obsolete + file ; + forward ( first | only ); + forwarders [ port ] [ tls ] { ( | ) [ port ] [ tls ]; ... }; + initial-file ; + inline-signing ; + ixfr-from-differences ; + journal ; + key-directory ; + log-report-channel ; + masterfile-format ( raw | text ); + masterfile-style ( full | relative ); + max-ixfr-ratio ( unlimited | ); + max-journal-size ( default | unlimited | ); + max-records ; + max-records-per-type ; + max-refresh-time ; + max-retry-time ; + max-transfer-idle-in ; + max-transfer-idle-out ; + max-transfer-time-in ; + max-transfer-time-out ; + max-types-per-name ; + max-zone-ttl ( unlimited | ); // deprecated + min-refresh-time ; + min-retry-time ; + min-transfer-rate-in ; + multi-master ; + notify ( explicit | master-only | primary-only | ); + notify-defer ; + notify-delay ; + notify-source ( | * ); + notify-source-v6 ( | * ); + notify-to-soa ; + nsec3-test-zone ; // test only + parental-agents [ port ] [ source ( | * ) ] [ source-v6 ( | * ) ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + parental-source ( | * ); + parental-source-v6 ( | * ); + primaries [ port ] [ source ( | * ) ] [ source-v6 ( | * ) ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + provide-zoneversion ; + request-expire ; + request-ixfr ; + request-ixfr-max-diffs ; + send-report-channel ; + serial-update-method ( date | increment | unixtime ); + server-addresses { ( | ); ... }; + server-names { ; ... }; + sig-signing-nodes ; + sig-signing-signatures ; + sig-signing-type ; + sig-validity-interval [ ]; // obsolete + transfer-source ( | * ); + transfer-source-v6 ( | * ); + try-tcp-refresh ; + type ( primary | master | secondary | slave | mirror | forward | hint | redirect | static-stub | stub ); + update-check-ksk ; // obsolete + update-policy ( local | { ( deny | grant ) ( 6to4-self | external | krb5-self | krb5-selfsub | krb5-subdomain | krb5-subdomain-self-rhs | ms-self | ms-selfsub | ms-subdomain | ms-subdomain-self-rhs | name | self | selfsub | selfwild | subdomain | tcp-self | wildcard | zonesub ) [ ] ; ... } ); + zero-no-soa-ttl ; + zone-statistics ( full | terse | none | ); +}; // may occur multiple times + tls { ca-file ; cert-file ; diff --git a/doc/misc/primary.zoneopt b/doc/misc/primary.zoneopt index 74a314999a..dd1b94756b 100644 --- a/doc/misc/primary.zoneopt +++ b/doc/misc/primary.zoneopt @@ -60,6 +60,7 @@ zone [ ] { sig-signing-signatures ; sig-signing-type ; sig-validity-interval [ ]; // obsolete + template ; update-check-ksk ; // obsolete update-policy ( local | { ( deny | grant ) ( 6to4-self | external | krb5-self | krb5-selfsub | krb5-subdomain | krb5-subdomain-self-rhs | ms-self | ms-selfsub | ms-subdomain | ms-subdomain-self-rhs | name | self | selfsub | selfwild | subdomain | tcp-self | wildcard | zonesub ) [ ] ; ... } ); zero-no-soa-ttl ; diff --git a/doc/misc/redirect.zoneopt b/doc/misc/redirect.zoneopt index f457c807c9..e338b6e231 100644 --- a/doc/misc/redirect.zoneopt +++ b/doc/misc/redirect.zoneopt @@ -11,5 +11,6 @@ zone [ ] { max-types-per-name ; max-zone-ttl ( unlimited | ); // deprecated primaries [ port ] [ source ( | * ) ] [ source-v6 ( | * ) ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + template ; zone-statistics ( full | terse | none | ); }; diff --git a/doc/misc/secondary.zoneopt b/doc/misc/secondary.zoneopt index b1dbcea978..7529112a33 100644 --- a/doc/misc/secondary.zoneopt +++ b/doc/misc/secondary.zoneopt @@ -60,6 +60,7 @@ zone [ ] { sig-signing-signatures ; sig-signing-type ; sig-validity-interval [ ]; // obsolete + template ; transfer-source ( | * ); transfer-source-v6 ( | * ); try-tcp-refresh ; diff --git a/doc/misc/static-stub.zoneopt b/doc/misc/static-stub.zoneopt index 40a340f629..14928922dd 100644 --- a/doc/misc/static-stub.zoneopt +++ b/doc/misc/static-stub.zoneopt @@ -9,5 +9,6 @@ zone [ ] { max-types-per-name ; server-addresses { ( | ); ... }; server-names { ; ... }; + template ; zone-statistics ( full | terse | none | ); }; diff --git a/doc/misc/stub.zoneopt b/doc/misc/stub.zoneopt index 97b9ba0578..4d25095484 100644 --- a/doc/misc/stub.zoneopt +++ b/doc/misc/stub.zoneopt @@ -21,6 +21,7 @@ zone [ ] { min-transfer-rate-in ; multi-master ; primaries [ port ] [ source ( | * ) ] [ source-v6 ( | * ) ] { ( | [ port ] | [ port ] ) [ key ] [ tls ]; ... }; + template ; transfer-source ( | * ); transfer-source-v6 ( | * ); zone-statistics ( full | terse | none | ); diff --git a/lib/isccfg/check.c b/lib/isccfg/check.c index 22cb3df899..a3c79085da 100644 --- a/lib/isccfg/check.c +++ b/lib/isccfg/check.c @@ -2923,14 +2923,15 @@ check: } /* - * Try to find a zone option in one of up to three levels of options: - * for example, the zone, view, and global option blocks. + * Try to find a zone option in one of up to four levels of options: + * for example, the zone, template, view, and global option blocks. * (Fewer levels can be specified for options that aren't defined at - * all three levels.) + * all four levels.) */ static isc_result_t get_zoneopt(const cfg_obj_t *opts1, const cfg_obj_t *opts2, - const cfg_obj_t *opts3, const char *name, const cfg_obj_t **objp) { + const cfg_obj_t *opts3, const cfg_obj_t *opts4, const char *name, + const cfg_obj_t **objp) { isc_result_t result = ISC_R_NOTFOUND; REQUIRE(*objp == NULL); @@ -2944,6 +2945,9 @@ get_zoneopt(const cfg_obj_t *opts1, const cfg_obj_t *opts2, if (*objp == NULL && opts3 != NULL) { result = cfg_map_get(opts3, name, objp); } + if (*objp == NULL && opts4 != NULL) { + result = cfg_map_get(opts4, name, objp); + } return result; } @@ -2958,13 +2962,14 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, const char *znamestr = NULL; const char *typestr = NULL; const char *target = NULL; + const char *tmplname = NULL; int ztype; - const cfg_obj_t *zoptions, *goptions = NULL; + const cfg_obj_t *zoptions = NULL, *toptions = NULL, *goptions = NULL; const cfg_obj_t *obj = NULL, *kasp = NULL; - const cfg_obj_t *inviewobj = NULL; + const cfg_obj_t *templates = NULL, *inviewobj = NULL; isc_result_t result = ISC_R_SUCCESS; isc_result_t tresult; - unsigned int i; + unsigned int i = 0; dns_rdataclass_t zclass; dns_fixedname_t fixedname; dns_name_t *zname = NULL; /* NULL if parsing of zone name fails. */ @@ -2976,6 +2981,7 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, bool ddns = false; bool has_dnssecpolicy = false; bool kasp_inlinesigning = false; + bool inline_signing = false; const void *clauses = NULL; const char *option = NULL; const char *kaspname = NULL; @@ -2995,14 +3001,37 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, cfg_map_get(config, "options", &goptions); } - inviewobj = NULL; + /* If the zone specifies a template, find it too */ + (void)cfg_map_get(config, "template", &templates); + (void)cfg_map_get(zoptions, "template", &obj); + if (obj != NULL) { + tmplname = cfg_obj_asstring(obj); + + CFG_LIST_FOREACH (templates, e) { + const cfg_obj_t *t = cfg_tuple_get(cfg_listelt_value(e), + "name"); + if (strcasecmp(cfg_obj_asstring(t), tmplname) == 0) { + toptions = cfg_tuple_get(cfg_listelt_value(e), + "options"); + break; + } + } + + if (toptions == NULL) { + cfg_obj_log(zconfig, ISC_LOG_ERROR, + "zone '%s': template '%s' not found", + znamestr, tmplname); + return ISC_R_FAILURE; + } + } + (void)cfg_map_get(zoptions, "in-view", &inviewobj); if (inviewobj != NULL) { target = cfg_obj_asstring(inviewobj); ztype = CFG_ZONE_INVIEW; } else { obj = NULL; - (void)cfg_map_get(zoptions, "type", &obj); + (void)get_zoneopt(zoptions, toptions, NULL, NULL, "type", &obj); if (obj == NULL) { cfg_obj_log(zconfig, ISC_LOG_ERROR, "zone '%s': type not present", znamestr); @@ -3195,7 +3224,8 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, * Check if a dnssec-policy is set. */ obj = NULL; - (void)get_zoneopt(zoptions, voptions, goptions, "dnssec-policy", &obj); + (void)get_zoneopt(zoptions, toptions, voptions, goptions, + "dnssec-policy", &obj); if (obj != NULL) { kaspname = cfg_obj_asstring(obj); if (strcmp(kaspname, "default") == 0) { @@ -3258,8 +3288,8 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, * */ if (has_dnssecpolicy) { obj = NULL; - (void)get_zoneopt(zoptions, voptions, goptions, "max-zone-ttl", - &obj); + (void)get_zoneopt(zoptions, toptions, voptions, goptions, + "max-zone-ttl", &obj); if (obj != NULL) { cfg_obj_log(obj, ISC_LOG_ERROR, "zone '%s': option 'max-zone-ttl' " @@ -3278,13 +3308,19 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, option = cfg_map_firstclause(&cfg_type_zoneopts, &clauses, &i); while (option != NULL) { obj = NULL; - if (cfg_map_get(zoptions, option, &obj) == ISC_R_SUCCESS && - obj != NULL && !cfg_clause_validforzone(option, ztype)) - { + bool topt = false; + (void)cfg_map_get(zoptions, option, &obj); + if (obj == NULL && toptions != NULL) { + (void)cfg_map_get(toptions, option, &obj); + topt = true; + } + if (obj != NULL && !cfg_clause_validforzone(option, ztype)) { cfg_obj_log(obj, ISC_LOG_WARNING, "option '%s' is not allowed " - "in '%s' zone '%s'", - option, typestr, znamestr); + "in '%s' zone '%s'%s%s%s", + option, typestr, znamestr, + topt ? " (referencing template '" : "", + topt ? tmplname : "", topt ? "')" : ""); result = ISC_R_FAILURE; } option = cfg_map_nextclause(&cfg_type_zoneopts, &clauses, &i); @@ -3321,9 +3357,9 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, bool donotify = true; obj = NULL; - tresult = get_zoneopt(zoptions, voptions, goptions, "notify", - &obj); - if (tresult == ISC_R_SUCCESS) { + (void)get_zoneopt(zoptions, toptions, voptions, goptions, + "notify", &obj); + if (obj != NULL) { if (cfg_obj_isboolean(obj)) { donotify = cfg_obj_asboolean(obj); } else { @@ -3338,18 +3374,19 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, } obj = NULL; - tresult = cfg_map_get(zoptions, "also-notify", &obj); - if (tresult == ISC_R_SUCCESS && !donotify) { + (void)get_zoneopt(zoptions, toptions, NULL, NULL, "also-notify", + &obj); + if (obj != NULL && !donotify) { cfg_obj_log(zoptions, ISC_LOG_WARNING, "zone '%s': 'also-notify' set but " "'notify' is disabled", znamestr); } - if (tresult != ISC_R_SUCCESS) { - tresult = get_zoneopt(voptions, goptions, NULL, - "also-notify", &obj); + if (obj == NULL) { + (void)get_zoneopt(voptions, goptions, NULL, NULL, + "also-notify", &obj); } - if (tresult == ISC_R_SUCCESS && donotify) { + if (obj != NULL && donotify) { uint32_t count; tresult = validate_remotes(obj, config, &count, mctx); if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS) @@ -3370,15 +3407,18 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, !dns_name_equal(zname, dns_rootname))) { obj = NULL; - (void)cfg_map_get(zoptions, "primaries", &obj); + (void)get_zoneopt(zoptions, toptions, NULL, NULL, "primaries", + &obj); if (obj == NULL) { /* If "primaries" was unset, check for "masters" */ - (void)cfg_map_get(zoptions, "masters", &obj); + (void)get_zoneopt(zoptions, toptions, NULL, NULL, + "masters", &obj); } else { const cfg_obj_t *obj2 = NULL; /* ...bug if it was set, "masters" must not be. */ - (void)cfg_map_get(zoptions, "masters", &obj2); + (void)get_zoneopt(zoptions, toptions, NULL, NULL, + "masters", &obj2); if (obj2 != NULL) { cfg_obj_log(obj, ISC_LOG_ERROR, "'primaries' and 'masters' cannot " @@ -3414,7 +3454,8 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, */ if (ztype == CFG_ZONE_PRIMARY || ztype == CFG_ZONE_SECONDARY) { obj = NULL; - (void)cfg_map_get(zoptions, "parental-agents", &obj); + (void)get_zoneopt(zoptions, toptions, NULL, NULL, + "parental-agents", &obj); if (obj != NULL) { uint32_t count; tresult = validate_remotes(obj, config, &count, mctx); @@ -3451,22 +3492,22 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, */ if (ztype == CFG_ZONE_PRIMARY || ztype == CFG_ZONE_SECONDARY) { bool signing = false; - isc_result_t res1, res2, res3; - const cfg_obj_t *au = NULL; + const cfg_obj_t *au = NULL, *up = NULL; - obj = NULL; - res1 = cfg_map_get(zoptions, "allow-update", &au); - obj = NULL; - res2 = cfg_map_get(zoptions, "update-policy", &obj); - if (res1 == ISC_R_SUCCESS && res2 == ISC_R_SUCCESS) { - cfg_obj_log(obj, ISC_LOG_ERROR, + (void)get_zoneopt(zoptions, toptions, NULL, NULL, + "allow-update", &au); + (void)get_zoneopt(zoptions, toptions, NULL, NULL, + "update-policy", &up); + + if (au != NULL && up != NULL) { + cfg_obj_log(au, ISC_LOG_ERROR, "zone '%s': 'allow-update' is ignored " "when 'update-policy' is present", znamestr); result = ISC_R_FAILURE; - } else if (res2 == ISC_R_SUCCESS) { - res3 = check_update_policy(obj); - if (res3 != ISC_R_SUCCESS) { + } else if (up != NULL) { + tresult = check_update_policy(up); + if (tresult != ISC_R_SUCCESS) { result = ISC_R_FAILURE; } } @@ -3476,18 +3517,18 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, * we should also check for allow-update at the * view and options levels. */ - if (res1 != ISC_R_SUCCESS) { - res1 = get_zoneopt(voptions, goptions, NULL, - "allow-update", &au); + if (au == NULL) { + (void)get_zoneopt(voptions, goptions, NULL, NULL, + "allow-update", &au); } - if (res2 == ISC_R_SUCCESS) { + if (up != NULL) { ddns = true; - } else if (res1 == ISC_R_SUCCESS) { + } else if (au != NULL) { dns_acl_t *acl = NULL; - res1 = cfg_acl_fromconfig(au, config, actx, mctx, 0, - &acl); - if (res1 != ISC_R_SUCCESS) { + tresult = cfg_acl_fromconfig(au, config, actx, mctx, 0, + &acl); + if (tresult != ISC_R_SUCCESS) { cfg_obj_log(au, ISC_LOG_ERROR, "acl expansion failed: %s", isc_result_totext(result)); @@ -3501,9 +3542,10 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, } obj = NULL; - res1 = cfg_map_get(zoptions, "inline-signing", &obj); - if (res1 == ISC_R_SUCCESS) { - signing = cfg_obj_asboolean(obj); + (void)get_zoneopt(zoptions, toptions, NULL, NULL, + "inline-signing", &obj); + if (obj != NULL) { + inline_signing = signing = cfg_obj_asboolean(obj); } else if (has_dnssecpolicy) { signing = kasp_inlinesigning; } @@ -3527,8 +3569,9 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, } obj = NULL; - res1 = cfg_map_get(zoptions, "sig-signing-type", &obj); - if (res1 == ISC_R_SUCCESS) { + (void)get_zoneopt(zoptions, toptions, NULL, NULL, + "sig-signing-type", &obj); + if (obj != NULL) { uint32_t type = cfg_obj_asuint32(obj); if (type < 0xff00U || type > 0xffffU) { cfg_obj_log(obj, ISC_LOG_ERROR, @@ -3540,10 +3583,9 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, } obj = NULL; - res1 = cfg_map_get(zoptions, "dnssec-loadkeys-interval", &obj); - if (res1 == ISC_R_SUCCESS && ztype == CFG_ZONE_SECONDARY && - !signing) - { + (void)get_zoneopt(zoptions, toptions, NULL, NULL, + "dnssec-loadkeys-interval", &obj); + if (obj != NULL && ztype == CFG_ZONE_SECONDARY && !signing) { cfg_obj_log(obj, ISC_LOG_ERROR, "dnssec-loadkeys-interval: requires " "inline-signing when used in secondary " @@ -3557,7 +3599,8 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, */ obj = NULL; if (root) { - (void)get_zoneopt(voptions, goptions, NULL, "forwarders", &obj); + (void)get_zoneopt(voptions, goptions, NULL, NULL, "forwarders", + &obj); } if (check_forward(config, zoptions, obj) != ISC_R_SUCCESS) { result = ISC_R_FAILURE; @@ -3569,13 +3612,15 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, */ if (ztype == CFG_ZONE_FORWARD && (rfc1918 || ula)) { obj = NULL; - (void)cfg_map_get(zoptions, "forward", &obj); + (void)get_zoneopt(zoptions, toptions, NULL, NULL, "forward", + &obj); if (obj == NULL) { /* - * Forward mode not explicitly configured. + * Forward mode not explicitly configured + * at the zone or template level. */ - (void)get_zoneopt(voptions, goptions, NULL, "forward", - &obj); + (void)get_zoneopt(voptions, goptions, NULL, NULL, + "forward", &obj); if (obj == NULL || strcasecmp(cfg_obj_asstring(obj), "first") == 0) { @@ -3593,7 +3638,8 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, * Check validity of static stub server addresses. */ obj = NULL; - (void)cfg_map_get(zoptions, "server-addresses", &obj); + (void)get_zoneopt(zoptions, toptions, NULL, NULL, "server-addresses", + &obj); if (ztype == CFG_ZONE_STATICSTUB && obj != NULL) { CFG_LIST_FOREACH (obj, element) { isc_sockaddr_t sa; @@ -3616,7 +3662,7 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, * Check validity of static stub server names. */ obj = NULL; - (void)cfg_map_get(zoptions, "server-names", &obj); + (void)get_zoneopt(zoptions, toptions, NULL, NULL, "server-names", &obj); if (zname != NULL && ztype == CFG_ZONE_STATICSTUB && obj != NULL) { CFG_LIST_FOREACH (obj, element) { const char *snamestr = NULL; @@ -3649,7 +3695,8 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, } obj = NULL; - (void)cfg_map_get(zoptions, "send-report-channel", &obj); + (void)get_zoneopt(zoptions, toptions, NULL, NULL, "send-report-channel", + &obj); if (obj != NULL) { const char *str = cfg_obj_asstring(obj); dns_fixedname_t fad; @@ -3677,7 +3724,8 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, * Warn if key-directory doesn't exist */ obj = NULL; - (void)get_zoneopt(zoptions, voptions, goptions, "key-directory", &obj); + (void)get_zoneopt(zoptions, toptions, voptions, goptions, + "key-directory", &obj); if (obj != NULL) { dir = cfg_obj_asstring(obj); @@ -3726,8 +3774,9 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, */ if (ztype == CFG_ZONE_PRIMARY || ztype == CFG_ZONE_SECONDARY) { obj = NULL; - tresult = cfg_map_get(zoptions, "log-report-channel", &obj); - if (tresult == ISC_R_SUCCESS && cfg_obj_asboolean(obj) && + (void)get_zoneopt(zoptions, toptions, NULL, NULL, + "log-report-channel", &obj); + if (obj != NULL && cfg_obj_asboolean(obj) && dns_name_equal(zname, dns_rootname)) { cfg_obj_log(zconfig, ISC_LOG_ERROR, @@ -3754,14 +3803,14 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, */ obj = NULL; dlz = false; - tresult = cfg_map_get(zoptions, "dlz", &obj); - if (tresult == ISC_R_SUCCESS) { + (void)get_zoneopt(zoptions, toptions, NULL, NULL, "dlz", &obj); + if (obj != NULL) { dlz = true; } obj = NULL; - tresult = cfg_map_get(zoptions, "database", &obj); - if (dlz && tresult == ISC_R_SUCCESS) { + (void)get_zoneopt(zoptions, toptions, NULL, NULL, "database", &obj); + if (dlz && obj != NULL) { cfg_obj_log(zconfig, ISC_LOG_ERROR, "zone '%s': cannot specify both 'dlz' " "and 'database'", @@ -3769,28 +3818,23 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, if (result == ISC_R_SUCCESS) { result = ISC_R_FAILURE; } - } else if (!dlz && - (tresult == ISC_R_NOTFOUND || - (tresult == ISC_R_SUCCESS && - strcmp(ZONEDB_DEFAULT, cfg_obj_asstring(obj)) == 0))) + } else if (!dlz && (obj == NULL || + strcmp(ZONEDB_DEFAULT, cfg_obj_asstring(obj)) == 0)) { - isc_result_t res1; const cfg_obj_t *fileobj = NULL; - tresult = cfg_map_get(zoptions, "file", &fileobj); - obj = NULL; - res1 = cfg_map_get(zoptions, "inline-signing", &obj); - if (tresult != ISC_R_SUCCESS && + (void)get_zoneopt(zoptions, toptions, NULL, NULL, "file", + &fileobj); + if (fileobj == NULL && (ztype == CFG_ZONE_PRIMARY || ztype == CFG_ZONE_HINT || - (ztype == CFG_ZONE_SECONDARY && res1 == ISC_R_SUCCESS && - cfg_obj_asboolean(obj)))) + (ztype == CFG_ZONE_SECONDARY && inline_signing))) { cfg_obj_log(zconfig, ISC_LOG_ERROR, "zone '%s': missing 'file' entry", znamestr); if (result == ISC_R_SUCCESS) { - result = tresult; + result = ISC_R_FAILURE; } - } else if (tresult == ISC_R_SUCCESS && files != NULL && + } else if (fileobj != NULL && files != NULL && (ztype == CFG_ZONE_SECONDARY || ztype == CFG_ZONE_MIRROR || ddns || has_dnssecpolicy)) @@ -3800,7 +3844,7 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, { result = tresult; } - } else if (tresult == ISC_R_SUCCESS && files != NULL && + } else if (fileobj != NULL && files != NULL && (ztype == CFG_ZONE_PRIMARY || ztype == CFG_ZONE_HINT)) { @@ -3817,13 +3861,13 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, * consistent. */ obj = NULL; - tresult = get_zoneopt(zoptions, voptions, goptions, "masterfile-format", - &obj); + tresult = get_zoneopt(zoptions, toptions, voptions, goptions, + "masterfile-format", &obj); if (tresult == ISC_R_SUCCESS && strcasecmp(cfg_obj_asstring(obj), "raw") == 0) { obj = NULL; - tresult = get_zoneopt(zoptions, voptions, goptions, + tresult = get_zoneopt(zoptions, toptions, voptions, goptions, "masterfile-style", &obj); if (tresult == ISC_R_SUCCESS) { cfg_obj_log(obj, ISC_LOG_ERROR, @@ -3838,8 +3882,8 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, } obj = NULL; - (void)get_zoneopt(zoptions, voptions, goptions, "max-journal-size", - &obj); + (void)get_zoneopt(zoptions, toptions, voptions, goptions, + "max-journal-size", &obj); if (obj != NULL && cfg_obj_isuint64(obj)) { uint64_t value = cfg_obj_asuint64(obj); if (value > DNS_JOURNAL_SIZE_MAX) { @@ -3854,8 +3898,8 @@ isccfg_check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, } obj = NULL; - (void)get_zoneopt(zoptions, voptions, goptions, "min-transfer-rate-in", - &obj); + (void)get_zoneopt(zoptions, toptions, voptions, goptions, + "min-transfer-rate-in", &obj); if (obj != NULL) { uint32_t traffic_bytes = cfg_obj_asuint32(cfg_tuple_get(obj, "traffic_bytes")); diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 82c50890f9..d5c61b2402 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -143,6 +143,8 @@ static cfg_type_t cfg_type_sizeval; static cfg_type_t cfg_type_sockaddr4wild; static cfg_type_t cfg_type_sockaddr6wild; static cfg_type_t cfg_type_statschannels; +static cfg_type_t cfg_type_template; +static cfg_type_t cfg_type_templateopts; static cfg_type_t cfg_type_tlsconf; static cfg_type_t cfg_type_view; static cfg_type_t cfg_type_viewopts; @@ -471,6 +473,18 @@ static cfg_type_t cfg_type_zone = { "zone", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, zone_fields }; +/*% + * A zone statement. + */ +static cfg_tuplefielddef_t template_fields[] = { + { "name", &cfg_type_astring, 0 }, + { "options", &cfg_type_templateopts, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_template = { "template", cfg_parse_tuple, + cfg_print_tuple, cfg_doc_tuple, + &cfg_rep_tuple, template_fields }; + /*% * A dnssec-policy statement. */ @@ -1155,6 +1169,7 @@ static cfg_clausedef_t namedconf_clauses[] = { { "statistics-channels", &cfg_type_statschannels, CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_NOTCONFIGURED }, #endif + { "template", &cfg_type_template, CFG_CLAUSEFLAG_MULTI }, { "tls", &cfg_type_tlsconf, CFG_CLAUSEFLAG_MULTI }, { "view", &cfg_type_view, CFG_CLAUSEFLAG_MULTI }, { NULL, NULL, 0 } @@ -2429,7 +2444,6 @@ static cfg_clausedef_t zone_only_clauses[] = { { "file", &cfg_type_qstring, CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | CFG_ZONE_STUB | CFG_ZONE_HINT | CFG_ZONE_REDIRECT }, - { "in-view", &cfg_type_astring, CFG_ZONE_INVIEW }, { "initial-file", &cfg_type_qstring, CFG_ZONE_PRIMARY }, { "inline-signing", &cfg_type_boolean, CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY }, @@ -2457,6 +2471,15 @@ static cfg_clausedef_t zone_only_clauses[] = { { NULL, NULL, 0 } }; +static cfg_clausedef_t non_template_clauses[] = { + { "in-view", &cfg_type_astring, CFG_ZONE_INVIEW }, + { "template", &cfg_type_astring, + CFG_ZONE_PRIMARY | CFG_ZONE_SECONDARY | CFG_ZONE_MIRROR | + CFG_ZONE_STUB | CFG_ZONE_STATICSTUB | CFG_ZONE_DELEGATION | + CFG_ZONE_HINT | CFG_ZONE_REDIRECT | CFG_ZONE_FORWARD }, + { NULL, NULL, 0 } +}; + /*% The top-level named.conf syntax. */ static cfg_clausedef_t *namedconf_clausesets[] = { namedconf_clauses, @@ -2493,11 +2516,24 @@ static cfg_type_t cfg_type_viewopts = { "view", cfg_parse_map, /*% The "zone" statement syntax. */ -static cfg_clausedef_t *zone_clausesets[] = { zone_only_clauses, zone_clauses, +static cfg_clausedef_t *zone_clausesets[] = { non_template_clauses, + zone_only_clauses, zone_clauses, NULL }; cfg_type_t cfg_type_zoneopts = { "zoneopts", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, zone_clausesets }; +/*% + * The "template" statement syntax: any clause that "zone" can take, + * except that zones can have a "template" option and templates cannot. + */ + +static cfg_clausedef_t *template_clausesets[] = { zone_only_clauses, + zone_clauses, NULL }; +static cfg_type_t cfg_type_templateopts = { + "templateopts", cfg_parse_map, cfg_print_map, + cfg_doc_map, &cfg_rep_map, template_clausesets +}; + /*% The "dnssec-policy" statement syntax. */ static cfg_clausedef_t *dnssecpolicy_clausesets[] = { dnssecpolicy_clauses, NULL }; @@ -3845,6 +3881,14 @@ cfg_clause_validforzone(const char *name, unsigned int ztype) { } valid = true; } + for (clause = non_template_clauses; clause->name != NULL; clause++) { + if ((clause->flags & ztype) == 0 || + strcmp(clause->name, name) != 0) + { + continue; + } + valid = true; + } return valid; } @@ -3853,23 +3897,25 @@ void cfg_print_zonegrammar(const unsigned int zonetype, unsigned int flags, void (*f)(void *closure, const char *text, int textlen), void *closure) { -#define NCLAUSES \ - (((sizeof(zone_clauses) + sizeof(zone_only_clauses)) / \ - sizeof(clause[0])) - \ - 1) +#define NCLAUSES \ + ARRAY_SIZE(non_template_clauses) + ARRAY_SIZE(zone_clauses) + \ + ARRAY_SIZE(zone_only_clauses) - 2 cfg_printer_t pctx; - cfg_clausedef_t *clause = NULL; cfg_clausedef_t clauses[NCLAUSES]; + cfg_clausedef_t *clause = clauses; pctx.f = f; pctx.closure = closure; pctx.indent = 0; pctx.flags = flags; - memmove(clauses, zone_clauses, sizeof(zone_clauses)); - memmove(clauses + sizeof(zone_clauses) / sizeof(zone_clauses[0]) - 1, - zone_only_clauses, sizeof(zone_only_clauses)); + memmove(clause, zone_clauses, sizeof(zone_clauses)); + clause += ARRAY_SIZE(zone_clauses) - 1; + memmove(clause, zone_only_clauses, sizeof(zone_only_clauses)); + clause += ARRAY_SIZE(zone_only_clauses) - 1; + memmove(clause, non_template_clauses, sizeof(non_template_clauses)); + qsort(clauses, NCLAUSES - 1, sizeof(clause[0]), cmp_clause); cfg_print_cstr(&pctx, "zone [ ] {\n");