Commit cfd26204 authored by Evan Hunt's avatar Evan Hunt

2936. [func] Improved configuration syntax and multiple-view

			support for addzone/delzone feature (see change
			#2930).  Removed "new-zone-file" option, replaced
			with "allow-new-zones (yes|no)".  The new-zone-file
			for each view is now created automatically, with
			a filename generated from a hash of the view name.
			It is no longer necessary to "include" the
			new-zone-file in named.conf; this happens
			automatically.  Zones that were not added via
			"rndc addzone" can no longer be removed with
			"rndc delzone". [RT #19447]
parent 7d7cdece
2936. [func] Improved configuration syntax and multiple-view
support for addzone/delzone feature (see change
#2930). Removed "new-zone-file" option, replaced
with "allow-new-zones (yes|no)". The new-zone-file
for each view is now created automatically, with
a filename generated from a hash of the view name.
It is no longer necessary to "include" the
new-zone-file in named.conf; this happens
automatically. Zones that were not added via
"rndc addzone" can no longer be removed with
"rndc delzone". [RT #19447]
2935. [bug] nsupdate: improve 'file not found' error message.
[RT #21871]
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: config.c,v 1.111 2010/06/25 03:24:05 marka Exp $ */
/* $Id: config.c,v 1.112 2010/08/11 18:14:18 each Exp $ */
/*! \file */
......@@ -159,6 +159,7 @@ options {\n\
max-clients-per-query 100;\n\
zero-no-soa-ttl-cache no;\n\
nsec3-test-zone no;\n\
allow-new-zones no;\n\
"
#ifdef ALLOW_FILTER_AAAA_ON_V4
" filter-aaaa-on-v4 no;\n\
......@@ -218,6 +219,7 @@ options {\n\
view \"_bind\" chaos {\n\
recursion no;\n\
notify no;\n\
allow-new-zones no;\n\
\n\
zone \"version.bind\" chaos {\n\
type master;\n\
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: server.c,v 1.577 2010/07/20 04:52:21 marka Exp $ */
/* $Id: server.c,v 1.578 2010/08/11 18:14:18 each Exp $ */
/*! \file */
......@@ -203,6 +203,17 @@ struct zonelistentry {
ISC_LINK(struct zonelistentry) link;
};
/*%
* Configuration context to retain for each view that allows
* new zones to be added at runtime
*/
struct cfg_context {
isc_mem_t * mctx;
cfg_obj_t * config;
cfg_parser_t * parser;
cfg_aclconfctx_t actx;
};
/*
* These zones should not leak onto the Internet.
*/
......@@ -286,7 +297,7 @@ configure_alternates(const cfg_obj_t *config, dns_view_t *view,
static isc_result_t
configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
cfg_aclconfctx_t *aclconf);
cfg_aclconfctx_t *aclconf, isc_boolean_t added);
static isc_result_t
add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx);
......@@ -294,14 +305,8 @@ add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx);
static void
end_reserved_dispatches(ns_server_t *server, isc_boolean_t all);
/*
* Stores config for building zones after the fact
*/
static cfg_obj_t *nzf_config = NULL;
static cfg_parser_t *nzf_parser = NULL;
static const char *nzf_file = NULL;
static const cfg_obj_t *nzf_option = NULL;
static cfg_aclconfctx_t nzf_actx;
static void
cfgctx_destroy(void **cfgp);
/*%
* Configure a single view ACL at '*aclp'. Get its configuration from
......@@ -1293,10 +1298,11 @@ cache_sharable(dns_view_t *originview, dns_view_t *view,
* global defaults in 'config' used exclusively.
*/
static isc_result_t
configure_view(dns_view_t *view, const cfg_obj_t *config,
const cfg_obj_t *vconfig, ns_cachelist_t *cachelist,
const cfg_obj_t *bindkeys, isc_mem_t *mctx,
cfg_aclconfctx_t *actx, isc_boolean_t need_hints)
configure_view(dns_view_t *view, cfg_parser_t* parser,
cfg_obj_t *config, cfg_obj_t *vconfig,
ns_cachelist_t *cachelist, const cfg_obj_t *bindkeys,
isc_mem_t *mctx, cfg_aclconfctx_t *actx,
isc_boolean_t need_hints)
{
const cfg_obj_t *maps[4];
const cfg_obj_t *cfgmaps[3];
......@@ -1349,6 +1355,8 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
isc_boolean_t auto_dlv = ISC_FALSE;
ns_cache_t *nsc;
isc_boolean_t zero_no_soattl;
cfg_parser_t *newzones_parser = NULL;
cfg_obj_t *nzfconf = NULL;
REQUIRE(DNS_VIEW_VALID(view));
......@@ -1453,7 +1461,62 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
{
const cfg_obj_t *zconfig = cfg_listelt_value(element);
CHECK(configure_zone(config, zconfig, vconfig, mctx, view,
actx));
actx, ISC_FALSE));
}
/*
* Are we allowing zones to be added and deleted dynamically?
*/
obj = NULL;
result = ns_config_get(maps, "allow-new-zones", &obj);
if (result == ISC_R_SUCCESS) {
isc_boolean_t allow = cfg_obj_asboolean(obj);
struct cfg_context *cfg = NULL;
if (allow) {
cfg = isc_mem_get(view->mctx, sizeof(*cfg));
if (cfg == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
memset(cfg, 0, sizeof(*cfg));
isc_mem_attach(view->mctx, &cfg->mctx);
if (config != NULL)
cfg_obj_attach(config, &cfg->config);
cfg_parser_attach(parser, &cfg->parser);
cfg_aclconfctx_clone(actx, &cfg->actx);
}
dns_view_setnewzones(view, allow, cfg, cfgctx_destroy);
}
/*
* If we're allowing added zones, then load zone configuration
* from the newzone file for zones that were added during previous
* runs.
*/
if (view->new_zone_file != NULL) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_INFO,
"loading additional zones for view '%s'",
view->name);
CHECK(cfg_parser_create(view->mctx, ns_g_lctx,
&newzones_parser));
result = cfg_parse_file(newzones_parser, view->new_zone_file,
&cfg_type_newzones, &nzfconf);
if (result == ISC_R_SUCCESS) {
zonelist = NULL;
cfg_map_get(nzfconf, "zone", &zonelist);
for (element = cfg_list_first(zonelist);
element != NULL;
element = cfg_list_next(element))
{
const cfg_obj_t *zconfig =
cfg_listelt_value(element);
CHECK(configure_zone(config, zconfig, vconfig,
mctx, view, actx,
ISC_TRUE));
}
}
}
#ifdef DLZ
......@@ -1991,8 +2054,10 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
* "allow-recursion", and "allow-recursion-on" acls if
* configured in named.conf.
*/
CHECK(configure_view_acl(vconfig, config, "allow-query-cache", NULL,
actx, ns_g_mctx, &view->queryacl));
if (view->queryacl == NULL)
CHECK(configure_view_acl(vconfig, config,
"allow-query-cache", NULL,
actx, ns_g_mctx, &view->queryacl));
CHECK(configure_view_acl(vconfig, config, "allow-query-cache-on", NULL,
actx, ns_g_mctx, &view->queryonacl));
if (view->queryonacl == NULL)
......@@ -2043,9 +2108,7 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
"allow-query-cache", NULL,
actx, ns_g_mctx,
&view->queryacl));
else {
if (view->queryacl != NULL)
dns_acl_detach(&view->queryacl);
else if (view->new_zone_file == NULL) {
CHECK(dns_acl_none(ns_g_mctx, &view->queryacl));
}
}
......@@ -2465,6 +2528,12 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
if (cache != NULL)
dns_cache_detach(&cache);
if (newzones_parser != NULL) {
if (nzfconf != NULL)
cfg_obj_destroy(newzones_parser, &nzfconf);
cfg_parser_destroy(&newzones_parser);
}
return (result);
}
......@@ -2718,7 +2787,7 @@ create_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist,
static isc_result_t
configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
cfg_aclconfctx_t *aclconf)
cfg_aclconfctx_t *aclconf, isc_boolean_t added)
{
dns_view_t *pview = NULL; /* Production view */
dns_zone_t *zone = NULL; /* New or reused zone */
......@@ -2931,6 +3000,11 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
CHECK(dns_view_adddelegationonly(view, origin));
}
/*
* Mark whether the zone was originally added at runtime or not
*/
dns_zone_setadded(zone, added);
/*
* Configure the zone.
*/
......@@ -4011,17 +4085,6 @@ load_configuration(const char *filename, ns_server_t *server,
}
}
/* Are we preserving config for adding zones dynamically? */
obj = NULL;
result = cfg_map_get(options, "new-zone-file", &obj);
if (obj && nzf_option == NULL) {
nzf_file = cfg_obj_asstring(obj);
if (nzf_file && *nzf_file) {
/* Remember this configuration */
nzf_option = config;
}
}
/*
* Rescan the interface list to pick up changes in the
* listen-on option. It's important that we do this before we try
......@@ -4109,28 +4172,14 @@ load_configuration(const char *filename, ns_server_t *server,
element != NULL;
element = cfg_list_next(element))
{
const cfg_obj_t *vconfig = cfg_listelt_value(element);
cfg_obj_t *vconfig = cfg_listelt_value(element);
view = NULL;
CHECK(create_view(vconfig, &viewlist, &view));
INSIST(view != NULL);
CHECK(configure_view(view, config, vconfig,
CHECK(configure_view(view, conf_parser, config, vconfig,
&cachelist, bindkeys,
ns_g_mctx, &aclconfctx, ISC_TRUE));
if (vconfig != NULL) {
/*
* Are we preserving config for dynamically added
* zones?
*/
const cfg_obj_t *voptions;
voptions = cfg_tuple_get(vconfig, "options");
obj = NULL;
result = cfg_map_get(voptions, "new-zone-file", &obj);
if (obj && nzf_option == NULL)
nzf_option = config;
}
dns_view_freeze(view);
dns_view_detach(&view);
}
......@@ -4147,7 +4196,7 @@ load_configuration(const char *filename, ns_server_t *server,
* In either case, we need to configure and freeze it.
*/
CHECK(create_view(NULL, &viewlist, &view));
CHECK(configure_view(view, config, NULL,
CHECK(configure_view(view, conf_parser, config, NULL,
&cachelist, bindkeys,
ns_g_mctx, &aclconfctx, ISC_TRUE));
dns_view_freeze(view);
......@@ -4164,10 +4213,10 @@ load_configuration(const char *filename, ns_server_t *server,
element != NULL;
element = cfg_list_next(element))
{
const cfg_obj_t *vconfig = cfg_listelt_value(element);
cfg_obj_t *vconfig = cfg_listelt_value(element);
CHECK(create_view(vconfig, &builtin_viewlist, &view));
CHECK(configure_view(view, config, vconfig,
CHECK(configure_view(view, conf_parser, config, vconfig,
&cachelist, bindkeys,
ns_g_mctx, &aclconfctx, ISC_FALSE));
dns_view_freeze(view);
......@@ -4452,16 +4501,7 @@ load_configuration(const char *filename, ns_server_t *server,
if (v6portset != NULL)
isc_portset_destroy(ns_g_mctx, &v6portset);
/* Preserve config, we'll need it when adding zones */
if (nzf_option != NULL) {
nzf_parser = conf_parser;
conf_parser = NULL;
nzf_config = config;
config = NULL;
memcpy(&nzf_actx, &aclconfctx, sizeof(cfg_aclconfctx_t));
} else {
cfg_aclconfctx_destroy(&aclconfctx);
}
cfg_aclconfctx_clear(&aclconfctx);
if (conf_parser != NULL) {
if (config != NULL)
......@@ -4669,12 +4709,6 @@ shutdown_server(isc_task_t *task, isc_event_t *event) {
cfg_obj_destroy(ns_g_parser, &ns_g_config);
cfg_parser_destroy(&ns_g_parser);
if (nzf_config) {
cfg_aclconfctx_destroy(&nzf_actx);
cfg_obj_destroy(nzf_parser, &nzf_config);
cfg_parser_destroy(&nzf_parser);
}
for (view = ISC_LIST_HEAD(server->viewlist);
view != NULL;
view = view_next) {
......@@ -6607,14 +6641,14 @@ isc_result_t
ns_server_add_zone(ns_server_t *server, char *args) {
isc_result_t result;
isc_buffer_t argbuf;
size_t arglen, len;
size_t arglen;
cfg_parser_t *parser = NULL;
cfg_obj_t *config = NULL;
const cfg_obj_t *vconfig = NULL;
const cfg_obj_t *views = NULL;
const cfg_listelt_t *element;
const cfg_obj_t *vconfig = NULL;
const cfg_obj_t *views = NULL;
const cfg_obj_t *parms = NULL;
const cfg_obj_t *obj = NULL;
const cfg_listelt_t *element;
const char *zonename;
const char *classname = NULL;
const char *argp;
......@@ -6623,16 +6657,9 @@ ns_server_add_zone(ns_server_t *server, char *args) {
dns_view_t *view = 0;
isc_buffer_t buf, *nbuf = NULL;
dns_name_t dnsname;
const char *filename = 0;
const char *filepart = NULL;
char fnamebuf[512];
struct stat sb;
dns_zone_t *zone = NULL;
FILE *fp = NULL;
/* Are we accepting new zones? */
if (nzf_option == NULL)
return (ISC_R_FAILURE);
struct cfg_context *cfg = NULL;
/* Try to parse the argument string */
arglen = strlen(args);
......@@ -6651,33 +6678,6 @@ ns_server_add_zone(ns_server_t *server, char *args) {
dns_name_setbuffer(&dnsname, nbuf);
CHECK(dns_name_fromtext(&dnsname, &buf, dns_rootname, ISC_FALSE, NULL));
/*
* If new-zone-file indicates a directory rather than a file,
* then "filepart" is the filename in the directory in which to
* write the zone configuration text.
*/
obj = cfg_tuple_get(parms, "filepart");
if (obj && cfg_obj_isstring(obj))
filepart = cfg_obj_asstring(obj);
if (filepart != NULL && *filepart != '\0') {
/* No hidden fles or full paths */
if (*filepart == '.' ||
#ifdef WIN32
*filepart == '\\' ||
#endif
*filepart == '/')
{
result = ISC_R_INVALIDFILE;
goto cleanup;
}
/* No crawling up the directory tree */
if (strstr(filepart, "..") != NULL) {
result = ISC_R_INVALIDFILE;
goto cleanup;
}
}
/* Make sense of optional class argument */
obj = cfg_tuple_get(parms, "class");
CHECK(ns_config_getclass(obj, dns_rdataclass_in, &rdclass));
......@@ -6692,6 +6692,18 @@ ns_server_add_zone(ns_server_t *server, char *args) {
viewname = "_default";
CHECK(dns_viewlist_find(&server->viewlist, viewname, rdclass, &view));
/* Are we accepting new zones? */
if (view->new_zone_file == NULL) {
result = ISC_R_NOPERM;
goto cleanup;
}
cfg = (struct cfg_context *) view->new_zone_config;
if (cfg == NULL) {
result = ISC_R_FAILURE;
goto cleanup;
}
/* Zone shouldn't already exist */
result = dns_zt_find(view->zonetable, &dnsname, 0, NULL, &zone);
if (result == ISC_R_SUCCESS) {
......@@ -6705,66 +6717,27 @@ ns_server_add_zone(ns_server_t *server, char *args) {
else if (result != ISC_R_NOTFOUND)
goto cleanup;
/* Find configuration for this view */
(void)cfg_map_get(nzf_config, "view", &views);
/* Find the view statement */
cfg_map_get(cfg->config, "view", &views);
for (element = cfg_list_first(views);
element != NULL;
element = cfg_list_next(element))
{
const char *vname;
vconfig = cfg_listelt_value(element);
vname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name"));
if (vname && !strcasecmp(vname, viewname)) {
/* What is save file for this view? */
if (vconfig != NULL) {
const cfg_obj_t *voptions;
voptions = cfg_tuple_get(vconfig, "options");
if (voptions) {
obj = NULL;
result = cfg_map_get(voptions,
"new-zone-file",
&obj);
if (result == ISC_R_SUCCESS)
filename =
cfg_obj_asstring(obj);
}
}
if (vname && !strcasecmp(vname, viewname))
break;
}
vconfig = NULL;
}
/* Can we add and remove zones in this view? */
if (filename == NULL || *filename == '\0')
filename = nzf_file;
/* Open save file for write configuration */
CHECK(isc_stdio_open(view->new_zone_file, "a", &fp));
if (filename == NULL || *filename == '\0') {
/* No adding zones in this view */
result = ISC_R_FAILURE;
goto cleanup;
}
/* Possibly contruct a full path */
if (filepart != NULL && *filepart != '\0') {
snprintf(fnamebuf, 512, "%s/%s", filename, filepart);
filename = fnamebuf;
}
/* Path must be an existing file */
if (stat(filename, &sb) < 0) {
result = ISC_R_FILENOTFOUND;
goto cleanup;
}
if (!S_ISREG(sb.st_mode)) {
result = ISC_R_FILENOTFOUND;
goto cleanup;
}
/* Mark zone unfrozen so that zone can be added. */
/* Mark view unfrozen so that zone can be added */
dns_view_thaw(view);
result = configure_zone(nzf_option, parms, vconfig,
server->mctx, view, &nzf_actx);
result = configure_zone(cfg->config, parms, vconfig,
server->mctx, view, &cfg->actx, ISC_FALSE);
dns_view_freeze(view);
if (result != ISC_R_SUCCESS) {
goto cleanup;
......@@ -6795,30 +6768,30 @@ ns_server_add_zone(ns_server_t *server, char *args) {
goto cleanup;
}
/* Write zone configuration out to our save file */
CHECK(isc_stdio_open(filename, "a", &fp));
/* Flag the zone as having been added at runtime */
dns_zone_setadded(zone, ISC_TRUE);
/* Emit just the zone name from args */
CHECK(isc_stdio_write("zone ", 5, 1, fp, &len));
CHECK(isc_stdio_write(zonename, strlen(zonename), 1, fp, &len));
CHECK(isc_stdio_write(" ", 1, 1, fp, &len));
CHECK(isc_stdio_write("zone ", 5, 1, fp, NULL));
CHECK(isc_stdio_write(zonename, strlen(zonename), 1, fp, NULL));
CHECK(isc_stdio_write(" ", 1, 1, fp, NULL));
/* Classname, if not default */
if (classname != NULL && *classname != '\0') {
CHECK(isc_stdio_write(classname, strlen(classname), 1, fp,
&len));
CHECK(isc_stdio_write(" ", 1, 1, fp, &len));
NULL));
CHECK(isc_stdio_write(" ", 1, 1, fp, NULL));
}
/* Find beginning of option block from args */
for (argp = args; *argp; argp++, arglen--) {
if (*argp == '{') { /* Assume matching '}' */
/* Add that to our file */
CHECK(isc_stdio_write(argp, arglen, 1, fp, &len));
CHECK(isc_stdio_write(argp, arglen, 1, fp, NULL));
/* Make sure we end with a LF */
if (argp[arglen-1] != '\n') {
CHECK(isc_stdio_write("\n", 1, 1, fp, &len));
CHECK(isc_stdio_write("\n", 1, 1, fp, NULL));
}
break;
}
......@@ -6851,50 +6824,6 @@ ns_server_add_zone(ns_server_t *server, char *args) {
return (result);
}
/*
* Pull an optional quoted filepart out of an arglist, shuffling memory
* so we can hand it off to zone_from_args() later
*/
static char *
extract_optional_qstring(char **args) {
char *p = *args;
char *str, *d;
char quote;
/* Skip past the command name */
while (isspace((unsigned char)*p))
p++;
while (*p && !isspace((unsigned char)*p))
p++;
/* Look for an open quote */
while (isspace((unsigned char)*p))
p++;
if (*p != '\'' && *p != '"')
return (NULL);
/* Move that string to the front of the buf */
quote = *p++;
str = d = *args;
while (*p && *p != quote)
*d++ = *p++;
if (!*p)
return (NULL); /* No matching close quote */
/* End that string */
*d++ = 0;
*args = d;
/* A bogus command name to placate zone_from_args() */
*d++ = 'X';
/* Cover over any remainder with spaces */
while (d <= p)
*d++ = ' ';
return (str);
}
/*
* Act on a "delzone" command from the command channel.
*/
......@@ -6903,45 +6832,15 @@ ns_server_del_zone(ns_server_t *server, char *args) {
isc_result_t result;
dns_zone_t *zone = NULL;
dns_view_t *view = NULL;
const cfg_obj_t *views = NULL;
const cfg_obj_t *obj = NULL;
const cfg_obj_t *vconfig = NULL;
dns_db_t *dbp = NULL;
const char *filename = NULL;
char *filepart = NULL;
char fnamebuf[512];
char *tmpname = NULL;
const cfg_listelt_t *element;
char buf[1024];
const char *zonename = NULL;
size_t znamelen = 0;
FILE *ifp = NULL, *ofp = NULL;
/* Only accept removes if we're accepting adds */
if (nzf_option == NULL)
return (ISC_R_FAILURE);
/* Possibly a filename in quotes */
filepart = extract_optional_qstring(&args);
if (filepart != NULL && *filepart != '\0') {
/* No hidden fles or full paths */
if (*filepart == '.' ||
#ifdef WIN32
*filepart == '\\'