]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
2930. [experimental] New "rndc addzone" and "rndc delzone" commads
authorEvan Hunt <each@isc.org>
Sun, 11 Jul 2010 00:12:19 +0000 (00:12 +0000)
committerEvan Hunt <each@isc.org>
Sun, 11 Jul 2010 00:12:19 +0000 (00:12 +0000)
allow dynamic addition and deletion of zones.
To enable this feature, specify a "new-zone-file"
option at the view or options level in named.conf.
Zone configuration information for the new zones
will be written into that file.  To make the new
zones persist after a restart, "include" the file
into named.conf in the appropriate view.  (Note:
This feature is not yet documented, and its syntax
is expected to change.) [RT #19447]

13 files changed:
CHANGES
bin/named/control.c
bin/named/include/named/control.h
bin/named/include/named/server.h
bin/named/server.c
bin/named/zoneconf.c
bin/rndc/rndc.c
lib/dns/include/dns/resolver.h
lib/dns/include/dns/view.h
lib/dns/resolver.c
lib/dns/view.c
lib/isccfg/include/isccfg/namedconf.h
lib/isccfg/namedconf.c

diff --git a/CHANGES b/CHANGES
index 25a2c8b2c7ab9d766b9bdee4c831ead085a2815a..9bfb275eba732aa7486eaeba01b098d07991d345 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,14 @@
+2930.  [experimental]  New "rndc addzone" and "rndc delzone" commads
+                       allow dynamic addition and deletion of zones.
+                       To enable this feature, specify a "new-zone-file"
+                       option at the view or options level in named.conf.
+                       Zone configuration information for the new zones
+                       will be written into that file.  To make the new
+                       zones persist after a restart, "include" the file
+                       into named.conf in the appropriate view.  (Note:
+                       This feature is not yet documented, and its syntax
+                       is expected to change.) [RT #19447]
+
 2929.  [bug]           Improved handling of GSS security contexts: 
                         - added LRU expiration for generated TSIGs
                         - added the ability to use a non-default realm
index 60cdbba799c53bfd024d43e19a698f35d4668ec9..0ed4f47d422f8f90fc7a2932f11f3eef7a63f845 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: control.c,v 1.36.50.2 2010/06/25 23:46:33 tbox Exp $ */
+/* $Id: control.c,v 1.36.50.3 2010/07/11 00:12:18 each Exp $ */
 
 /*! \file */
 
@@ -191,6 +191,10 @@ ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text) {
                result = ns_server_validation(ns_g_server, command);
        } else if (command_compare(command, NS_COMMAND_SIGN)) {
                result = ns_server_sign(ns_g_server, command);
+       } else if (command_compare(command, NS_COMMAND_ADDZONE)) {
+               result = ns_server_add_zone(ns_g_server, command);
+       } else if (command_compare(command, NS_COMMAND_DELZONE)) {
+               result = ns_server_del_zone(ns_g_server, command);
        } else {
                isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
                              NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
index 953e34f8b0ec0a6e842decbb5034d0ff2bf8856c..acbcd1c18448d5172410726f60e2a8077932373a 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: control.h,v 1.27.50.2 2010/06/25 23:46:33 tbox Exp $ */
+/* $Id: control.h,v 1.27.50.3 2010/07/11 00:12:18 each Exp $ */
 
 #ifndef NAMED_CONTROL_H
 #define NAMED_CONTROL_H 1
@@ -59,6 +59,8 @@
 #define NS_COMMAND_NOTIFY      "notify"
 #define NS_COMMAND_VALIDATION  "validation"
 #define NS_COMMAND_SIGN        "sign"
+#define NS_COMMAND_ADDZONE     "addzone"
+#define NS_COMMAND_DELZONE     "delzone"
 
 isc_result_t
 ns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp);
index 3780578866deb0100e2891d0dfa7fb077e06e3eb..414ff78255de804036ad1cd70cd3075884e23fef 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: server.h,v 1.104.8.3 2010/06/25 03:51:06 marka Exp $ */
+/* $Id: server.h,v 1.104.8.4 2010/07/11 00:12:18 each Exp $ */
 
 #ifndef NAMED_SERVER_H
 #define NAMED_SERVER_H 1
@@ -319,4 +319,16 @@ ns_add_reserved_dispatch(ns_server_t *server, const isc_sockaddr_t *addr);
 isc_result_t
 ns_server_validation(ns_server_t *server, char *args);
 
+/*%
+ * Add a zone to a running process
+ */
+isc_result_t
+ns_server_add_zone(ns_server_t *server, char *args);
+
+/*%
+ * Deletes a zone from a running process
+ */
+isc_result_t
+ns_server_del_zone(ns_server_t *server, char *args);
+
 #endif /* NAMED_SERVER_H */
index b0182c96cf0b9181f0d99ea111278bae3cee0a61..f91ccdf8cf05e76e5dd4ae063f66637ccd962490 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: server.c,v 1.556.8.15 2010/06/25 23:46:33 tbox Exp $ */
+/* $Id: server.c,v 1.556.8.16 2010/07/11 00:12:18 each Exp $ */
 
 /*! \file */
 
@@ -24,6 +24,9 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <limits.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 #include <isc/app.h>
 #include <isc/base64.h>
@@ -291,6 +294,15 @@ 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;
+
 /*%
  * Configure a single view ACL at '*aclp'.  Get its configuration from
  * 'vconfig' (for per-view configuration) and maybe from 'config'
@@ -3999,6 +4011,17 @@ 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
@@ -4094,6 +4117,20 @@ load_configuration(const char *filename, ns_server_t *server,
                CHECK(configure_view(view, 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);
        }
@@ -4415,7 +4452,16 @@ load_configuration(const char *filename, ns_server_t *server,
        if (v6portset != NULL)
                isc_portset_destroy(ns_g_mctx, &v6portset);
 
-       cfg_aclconfctx_destroy(&aclconfctx);
+       /* 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);
+       }
 
        if (conf_parser != NULL) {
                if (config != NULL)
@@ -4623,6 +4669,12 @@ 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) {
@@ -5096,7 +5148,9 @@ next_token(char **stringp, const char *delim) {
  * set '*zonep' to NULL.
  */
 static isc_result_t
-zone_from_args(ns_server_t *server, char *args, dns_zone_t **zonep) {
+zone_from_args(ns_server_t *server, char *args, dns_zone_t **zonep,
+              const char **zonename)
+{
        char *input, *ptr;
        const char *zonetxt;
        char *classtxt;
@@ -5120,6 +5174,8 @@ zone_from_args(ns_server_t *server, char *args, dns_zone_t **zonep) {
        zonetxt = next_token(&input, " \t");
        if (zonetxt == NULL)
                return (ISC_R_SUCCESS);
+       if (zonename)
+               *zonename = zonetxt;
 
        /* Look for the optional class name. */
        classtxt = next_token(&input, " \t");
@@ -5180,7 +5236,7 @@ ns_server_retransfercommand(ns_server_t *server, char *args) {
        dns_zone_t *zone = NULL;
        dns_zonetype_t type;
 
-       result = zone_from_args(server, args, &zone);
+       result = zone_from_args(server, args, &zone, NULL);
        if (result != ISC_R_SUCCESS)
                return (result);
        if (zone == NULL)
@@ -5204,7 +5260,7 @@ ns_server_reloadcommand(ns_server_t *server, char *args, isc_buffer_t *text) {
        dns_zonetype_t type;
        const char *msg = NULL;
 
-       result = zone_from_args(server, args, &zone);
+       result = zone_from_args(server, args, &zone, NULL);
        if (result != ISC_R_SUCCESS)
                return (result);
        if (zone == NULL) {
@@ -5264,7 +5320,7 @@ ns_server_notifycommand(ns_server_t *server, char *args, isc_buffer_t *text) {
        dns_zone_t *zone = NULL;
        const unsigned char msg[] = "zone notify queued";
 
-       result = zone_from_args(server, args, &zone);
+       result = zone_from_args(server, args, &zone, NULL);
        if (result != ISC_R_SUCCESS)
                return (result);
        if (zone == NULL)
@@ -5289,7 +5345,7 @@ ns_server_refreshcommand(ns_server_t *server, char *args, isc_buffer_t *text) {
        const unsigned char msg2[] = "not a slave or stub zone";
        dns_zonetype_t type;
 
-       result = zone_from_args(server, args, &zone);
+       result = zone_from_args(server, args, &zone, NULL);
        if (result != ISC_R_SUCCESS)
                return (result);
        if (zone == NULL)
@@ -6385,7 +6441,7 @@ ns_server_sign(ns_server_t *server, char *args) {
        dns_zonetype_t type;
        isc_uint16_t keyopts;
 
-       result = zone_from_args(server, args, &zone);
+       result = zone_from_args(server, args, &zone, NULL);
        if (result != ISC_R_SUCCESS)
                return (result);
        if (zone == NULL)
@@ -6425,7 +6481,7 @@ ns_server_freeze(ns_server_t *server, isc_boolean_t freeze, char *args,
        isc_boolean_t frozen;
        const char *msg = NULL;
 
-       result = zone_from_args(server, args, &zone);
+       result = zone_from_args(server, args, &zone, NULL);
        if (result != ISC_R_SUCCESS)
                return (result);
        if (zone == NULL) {
@@ -6543,3 +6599,503 @@ ns_smf_add_message(isc_buffer_t *text) {
        return (ISC_R_SUCCESS);
 }
 #endif /* HAVE_LIBSCF */
+
+/*
+ * Act on an "addzone" command from the command channel.
+ */
+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;
+       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     *parms = NULL;
+       const cfg_obj_t     *obj = NULL;
+       const char          *zonename;
+       const char          *classname = NULL;
+       const char          *argp;
+       const char          *viewname = NULL;
+       dns_rdataclass_t     rdclass;
+       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);
+
+       /* Try to parse the argument string */
+       arglen = strlen(args);
+       isc_buffer_init(&argbuf, args, arglen);
+       isc_buffer_add(&argbuf, strlen(args));
+       CHECK(cfg_parser_create(server->mctx, ns_g_lctx, &parser));
+       CHECK(cfg_parse_buffer(parser, &argbuf, &cfg_type_addzoneconf,
+                              &config));
+       CHECK(cfg_map_get(config, "addzone", &parms));
+
+       zonename = cfg_obj_asstring(cfg_tuple_get(parms, "name"));
+       isc_buffer_init(&buf, zonename, strlen(zonename));
+       isc_buffer_add(&buf, strlen(zonename));
+       dns_name_init(&dnsname, NULL);
+       isc_buffer_allocate(server->mctx, &nbuf, 256);
+       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));
+       if (rdclass != dns_rdataclass_in && obj)
+               classname = cfg_obj_asstring(obj);
+
+       /* Make sense of optional view argument */
+       obj = cfg_tuple_get(parms, "view");
+       if (obj && cfg_obj_isstring(obj))
+               viewname = cfg_obj_asstring(obj);
+       if (viewname == NULL || *viewname == '\0')
+               viewname = "_default";
+       CHECK(dns_viewlist_find(&server->viewlist, viewname, rdclass, &view));
+
+       /* Zone shouldn't already exist */
+       result = dns_zt_find(view->zonetable, &dnsname, 0, NULL, &zone);
+       if (result == ISC_R_SUCCESS) {
+               result = ISC_R_EXISTS;
+               goto cleanup;
+       } else if (result == DNS_R_PARTIALMATCH) {
+               /* Create our sub-zone anyway */
+               dns_zone_detach(&zone);
+               zone = NULL;
+       }
+       else if (result != ISC_R_NOTFOUND)
+               goto cleanup;
+
+       /* Find configuration for this view */
+       (void)cfg_map_get(nzf_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);
+                               }
+                       }
+                       break;
+               }
+               vconfig = NULL;
+       }
+
+       /* Can we add and remove zones in this view? */
+       if (filename == NULL || *filename == '\0')
+               filename = nzf_file;
+
+       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. */
+       dns_view_thaw(view);
+       result = configure_zone(nzf_option, parms, vconfig,
+               server->mctx, view, &nzf_actx);
+       dns_view_freeze(view);
+       if (result != ISC_R_SUCCESS) {
+               goto cleanup;
+       }
+
+       /* Is it there yet? */
+       CHECK(dns_zt_find(view->zonetable, &dnsname, 0, NULL, &zone));
+
+       /*
+        * Load the zone from the master file.  If this fails, we'll
+        * need to undo the configuration we've done already.
+        */
+       result = dns_zone_loadnew(zone);
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                             NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+                             "addzone failed; reverting.");
+
+               /* If the zone loaded partially, unload it */
+               dns_db_t *dbp = NULL;
+               if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
+                       dns_db_detach(&dbp);
+                       dns_zone_unload(zone);
+               }
+
+               /* Remove the zone from the zone table */
+               dns_zt_unmount(view->zonetable, zone);
+               goto cleanup;
+       }
+
+       /* Write zone configuration out to our save file */
+       CHECK(isc_stdio_open(filename, "a", &fp));
+
+       /* 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));
+
+       /* 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));
+       }
+
+       /* 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));
+
+                       /* Make sure we end with a LF */
+                       if (argp[arglen-1] != '\n') {
+                               CHECK(isc_stdio_write("\n", 1, 1, fp, &len));
+                       }
+                       break;
+               }
+       }
+
+       CHECK(isc_stdio_close(fp));
+       fp = NULL;
+       isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                                 NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+                                 "zone %s added to view %s via addzone",
+                                 zonename, viewname);
+
+       result = ISC_R_SUCCESS;
+
+ cleanup:
+       if (fp != NULL)
+               isc_stdio_close(fp);
+       if (parser != NULL) {
+               if (config != NULL)
+                       cfg_obj_destroy(parser, &config);
+               cfg_parser_destroy(&parser);
+       }
+       if (zone != NULL)
+               dns_zone_detach(&zone);
+       if (view != NULL)
+               dns_view_detach(&view);
+       if (nbuf != NULL)
+               isc_buffer_free(&nbuf);
+
+       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(*p))
+               p++;
+       while (*p && !isspace(*p))
+               p++;
+
+       /* Look for an open quote */
+       while (isspace(*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.
+ */
+isc_result_t
+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 == '\\' ||
+#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 rest of params */
+       CHECK(zone_from_args(server, args, &zone, &zonename));
+       if (result != ISC_R_SUCCESS)
+               return (result);
+       if (zone == NULL) {
+               result = ISC_R_UNEXPECTEDEND;
+               goto cleanup;
+       }
+
+       if (zonename != NULL && *zonename != '\0')
+               znamelen = strlen(zonename);
+
+       /* Dig out configuration for this zone */
+       view = dns_zone_getview(zone);
+       (void)cfg_map_get(nzf_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 != NULL && !strcasecmp(vname, view->name)) {
+                       /* What is save file for this view? */
+                       if (vconfig != NULL) {
+                               const cfg_obj_t *voptions;
+                               voptions = cfg_tuple_get(vconfig, "options");
+                               if (voptions != NULL) {
+                                       obj = NULL;
+                                       result = cfg_map_get(voptions,
+                                                            "new-zone-file",
+                                                            &obj);
+                                       if (result == ISC_R_SUCCESS)
+                                               filename =
+                                                       cfg_obj_asstring(obj);
+                               }
+                       }
+                       break;
+               }
+               vconfig = NULL;
+       }
+
+       /* Can we add and remove zones in this view? */
+       if (filename == NULL || *filename == '\0')
+               filename = nzf_file;
+
+       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;
+       }
+
+       /* Rewrite zone list */
+       result = isc_stdio_open(filename, "r", &ifp);
+       if (ifp != NULL && result == ISC_R_SUCCESS) {
+               char *found = NULL, *p;
+               size_t n;
+               
+               /* Create a temporary file */
+               CHECK(isc_string_printf(buf, 1023, "%s.%d", filename,
+                                       getpid()));
+               if (!(tmpname = isc_mem_strdup(server->mctx, buf))) {
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+               CHECK(isc_stdio_open(tmpname, "w", &ofp));
+
+               /* Look for the entry for that zone */
+               while (fgets(buf, 1024, ifp)) {
+                       /* A 'zone' line */
+                       if (strncasecmp(buf, "zone", 4)) {
+                               fputs(buf, ofp);
+                               continue;
+                       }
+                       p = buf+4;
+
+                       /* Locate a name */
+                       while (*p && ((*p == '"') || isspace(*p)))
+                               p++;
+
+                       /* Is that the zone we're looking for */
+                       if (strncasecmp(p, zonename, znamelen)) {
+                               fputs(buf, ofp);
+                               continue;
+                       }
+
+                       /* And nothing else? */
+                       p += znamelen;
+                       if (isspace(*p) || *p == '"' || *p == '{') {
+                               /* This must be the entry */
+                               found = p;
+                               break;
+                       }
+
+                       /* Spit it out, keep looking */
+                       fputs(buf, ofp);
+               }
+
+               /* Skip over an option block (matching # of braces) */
+               if (found) {
+                       int obrace = 0, cbrace = 0;
+                       while (1) {
+                               while (*p) {
+                                       if (*p == '{') obrace++;
+                                       if (*p == '}') cbrace++;
+                                       p++;
+                               }
+                               if (obrace && (obrace == cbrace))
+                                       break;
+                               if (!fgets(buf, 1024, ifp))
+                                       break;
+                               p = buf;
+                       }
+               }
+
+               /* Just spool the remainder of the file out */
+               while ((n = fread(buf, 1, 1024, ifp)) > 0)
+                       fwrite(buf, 1, n, ofp);
+               
+               /* Move temporary into place */
+               CHECK(isc_file_rename(tmpname, filename));
+       }
+
+       /* Stop answering for this zone */
+       if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
+               dns_db_detach(&dbp);
+               dns_zone_unload(zone);
+       }
+
+       CHECK(dns_zt_unmount(view->zonetable, zone));
+
+       isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                                 NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+                                 "zone %s removed via delzone", zonename);
+
+       result = ISC_R_SUCCESS;
+
+ cleanup:
+       if (ifp != NULL)
+               isc_stdio_close(ifp);
+       if (ofp != NULL) {
+               isc_stdio_close(ofp);
+               isc_file_remove(tmpname);
+       }
+       if (tmpname != NULL)
+               isc_mem_free(server->mctx, tmpname);
+       if (zone != NULL)
+               dns_zone_detach(&zone);
+
+       return (result);
+}
index 5fc316c97cc9a2a51eed898cbee1f735ad004e8a..7a9e1964c79929f134eb7847234ded0f41fd6406 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: zoneconf.c,v 1.161 2009/12/04 21:09:32 marka Exp $ */
+/* $Id: zoneconf.c,v 1.161.4.1 2010/07/11 00:12:18 each Exp $ */
 
 /*% */
 
@@ -558,6 +558,28 @@ ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
        if (result == ISC_R_SUCCESS)
                filename = cfg_obj_asstring(obj);
 
+       /*
+        * Unless we're using some alternative database, a master zone
+        * will be needing a master file.
+        */
+       if (ztype == dns_zone_master && cpval == default_dbtype) {
+               if (filename == NULL) {
+                       isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                                     NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+                                     "zone '%s': 'file' not specified",
+                                     zname);
+                       return (ISC_R_FAILURE);
+               }
+
+               if (!isc_file_exists(filename)) {
+                       isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+                                     NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+                                     "zone '%s': master file not found",
+                                     zname);
+                       return (ISC_R_NOTFOUND);
+               }
+       }
+
        masterformat = dns_masterformat_text;
        obj = NULL;
        result= ns_config_get(maps, "masterfile-format", &obj);
index 5464d388413b03941e574b1e15f28f60cb4a35c2..74ecf8fc128ed77d4e702fdcad714d10e1ce649b 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rndc.c,v 1.126.66.3 2010/06/25 23:46:33 tbox Exp $ */
+/* $Id: rndc.c,v 1.126.66.4 2010/07/11 00:12:18 each Exp $ */
 
 /*! \file */
 
@@ -143,6 +143,10 @@ command is one of the following:\n\
   validation newstate [view]\n\
                Enable / disable DNSSEC validation.\n\
   *restart     Restart the server.\n\
+  addzone [\"file\"] zone [class [view]] { zone-options }\n\
+               Add zone to given view. Requires new-zone-file option.\n\
+  delzone [\"file\"] zone [class [view]]\n\
+               Removes zone from given view. Requires new-zone-file option.\n\
 \n\
 * == not yet implemented\n\
 Version: %s\n",
index de5b7c92494b999fdd3b83a3ed30a78bed46538d..8823602a89d77e28cef1657a25de675c205ff5a0 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: resolver.h,v 1.64.104.2 2010/02/25 05:25:53 tbox Exp $ */
+/* $Id: resolver.h,v 1.64.104.3 2010/07/11 00:12:19 each Exp $ */
 
 #ifndef DNS_RESOLVER_H
 #define DNS_RESOLVER_H 1
@@ -180,7 +180,7 @@ dns_resolver_freeze(dns_resolver_t *res);
  *
  * Requires:
  *
- *\li  'res' is a valid, unfrozen resolver.
+ *\li  'res' is a valid resolver.
  *
  * Ensures:
  *
index 40a7bf19cdff33de4be2983779c92c054767e04a..6e4ca624857120a0580381e128eca3e967558be2 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: view.h,v 1.120.8.4 2010/06/22 04:02:45 marka Exp $ */
+/* $Id: view.h,v 1.120.8.5 2010/07/11 00:12:19 each Exp $ */
 
 #ifndef DNS_VIEW_H
 #define DNS_VIEW_H 1
@@ -416,7 +416,7 @@ dns_view_addzone(dns_view_t *view, dns_zone_t *zone);
 void
 dns_view_freeze(dns_view_t *view);
 /*%<
- * Freeze view.
+ * Freeze view.  No changes can be made to view configuration while frozen.
  *
  * Requires:
  *
@@ -427,6 +427,21 @@ dns_view_freeze(dns_view_t *view);
  *\li  'view' is frozen.
  */
 
+void
+dns_view_thaw(dns_view_t *view);
+/*%<
+ * Thaw view.  This allows zones to be added or removed at runtime.  This is
+ * NOT thread-safe; the caller MUST have run isc_task_exclusive() prior to
+ * thawing the view.
+ *
+ * Requires:
+ *
+ *\li  'view' is a valid, frozen view.
+ *
+ * Ensures:
+ *
+ *\li  'view' is no longer frozen.
+ */
 isc_result_t
 dns_view_find(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
              isc_stdtime_t now, unsigned int options, isc_boolean_t use_hints,
index 9fb12388ef57c45a5945a72c5219d5035507e769..fdf6c442edf49ff48392bfb9abfbec4053f7a889 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: resolver.c,v 1.413.14.10 2010/06/23 23:46:36 tbox Exp $ */
+/* $Id: resolver.c,v 1.413.14.11 2010/07/11 00:12:18 each Exp $ */
 
 /*! \file */
 
@@ -7655,13 +7655,11 @@ dns_resolver_prime(dns_resolver_t *res) {
 
 void
 dns_resolver_freeze(dns_resolver_t *res) {
-
        /*
         * Freeze resolver.
         */
 
        REQUIRE(VALID_RESOLVER(res));
-       REQUIRE(!res->frozen);
 
        res->frozen = ISC_TRUE;
 }
index bf7070b98333e73b70b4f75141737b9801e6a9eb..8f4452cac3f1bb073f205085a3523f2b0758f8b7 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: view.c,v 1.159.8.6 2010/06/22 04:02:44 marka Exp $ */
+/* $Id: view.c,v 1.159.8.7 2010/07/11 00:12:19 each Exp $ */
 
 /*! \file */
 
@@ -709,7 +709,27 @@ dns_view_setdstport(dns_view_t *view, in_port_t dstport) {
        view->dstport = dstport;
 }
 
+void
+dns_view_freeze(dns_view_t *view) {
+       REQUIRE(DNS_VIEW_VALID(view));
+       REQUIRE(!view->frozen);
+
+       if (view->resolver != NULL) {
+               INSIST(view->cachedb != NULL);
+               dns_resolver_freeze(view->resolver);
+       }
+       view->frozen = ISC_TRUE;
+}
+
 #ifdef BIND9
+void
+dns_view_thaw(dns_view_t *view) {
+       REQUIRE(DNS_VIEW_VALID(view));
+       REQUIRE(view->frozen);
+
+       view->frozen = ISC_FALSE;
+}
+
 isc_result_t
 dns_view_addzone(dns_view_t *view, dns_zone_t *zone) {
        isc_result_t result;
@@ -723,18 +743,6 @@ dns_view_addzone(dns_view_t *view, dns_zone_t *zone) {
 }
 #endif
 
-void
-dns_view_freeze(dns_view_t *view) {
-       REQUIRE(DNS_VIEW_VALID(view));
-       REQUIRE(!view->frozen);
-
-       if (view->resolver != NULL) {
-               INSIST(view->cachedb != NULL);
-               dns_resolver_freeze(view->resolver);
-       }
-       view->frozen = ISC_TRUE;
-}
-
 #ifdef BIND9
 isc_result_t
 dns_view_findzone(dns_view_t *view, dns_name_t *name, dns_zone_t **zonep) {
index ea9dc1192c4ff54523839df1ff7b680b99e1f684..7a3bcfb2ab4f5aab4ed42b967686997de0eddecf 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: namedconf.h,v 1.15 2009/07/31 23:43:23 each Exp $ */
+/* $Id: namedconf.h,v 1.15.120.1 2010/07/11 00:12:19 each Exp $ */
 
 #ifndef ISCCFG_NAMEDCONF_H
 #define ISCCFG_NAMEDCONF_H 1
@@ -36,6 +36,9 @@ LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_namedconf;
 LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_bindkeys;
 /*%< A bind.keys file. */
 
+LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_addzoneconf;
+/*%< A single zone passed via the addzone rndc command. */
+
 LIBISCCFG_EXTERNAL_DATA extern cfg_type_t cfg_type_rndcconf;
 /*%< A complete rndc.conf file. */
 
index 5b43c6fdb29d4e533c5b55c3417a7c62ea767c2e..c75736f2bec3582988d73f1c0104c66fb5cd0d6a 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: namedconf.c,v 1.113.4.6 2010/06/25 03:51:07 marka Exp $ */
+/* $Id: namedconf.c,v 1.113.4.7 2010/07/11 00:12:19 each Exp $ */
 
 /*! \file */
 
@@ -101,6 +101,7 @@ static cfg_type_t cfg_type_negated;
 static cfg_type_t cfg_type_notifytype;
 static cfg_type_t cfg_type_optional_allow;
 static cfg_type_t cfg_type_optional_class;
+static cfg_type_t cfg_type_optional_qstring;
 static cfg_type_t cfg_type_optional_facility;
 static cfg_type_t cfg_type_optional_keyref;
 static cfg_type_t cfg_type_optional_port;
@@ -888,6 +889,7 @@ options_clauses[] = {
        { "use-ixfr", &cfg_type_boolean, 0 },
        { "version", &cfg_type_qstringornone, 0 },
        { "flush-zones-on-shutdown", &cfg_type_boolean, 0 },
+       { "new-zone-file", &cfg_type_qstringornone, 0 },
        { NULL, NULL, 0 }
 };
 
@@ -1057,6 +1059,7 @@ view_clauses[] = {
        { "transfer-format", &cfg_type_transferformat, 0 },
        { "use-queryport-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
        { "zero-no-soa-ttl-cache", &cfg_type_boolean, 0 },
+       { "new-zone-file", &cfg_type_qstringornone, 0 },
 #ifdef ALLOW_FILTER_AAAA_ON_V4
        { "filter-aaaa", &cfg_type_bracketed_aml, 0 },
        { "filter-aaaa-on-v4", &cfg_type_v4_aaaa, 0 },
@@ -1396,6 +1399,42 @@ static cfg_type_t cfg_type_logging = {
        "logging", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, logging_clausesets };
 
 
+/*%
+ * For parsing an 'addzone' statement
+ */
+
+/*%
+ * A zone statement.
+ */
+static cfg_tuplefielddef_t addzone_fields[] = {
+       { "filepart", &cfg_type_optional_qstring, 0 },
+       { "name", &cfg_type_astring, 0 },
+       { "class", &cfg_type_optional_class, 0 },
+       { "view", &cfg_type_optional_class, 0 },
+       { "options", &cfg_type_zoneopts, 0 },
+       { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_addzone = {
+       "addzone", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, addzone_fields };
+
+static cfg_clausedef_t
+addzoneconf_clauses[] = {
+       { "addzone", &cfg_type_addzone, 0 },
+       { NULL, NULL, 0 }
+};
+
+static cfg_clausedef_t *
+addzoneconf_clausesets[] = {
+       addzoneconf_clauses,
+       NULL
+};
+
+LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_addzoneconf = {
+       "addzoneconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
+       &cfg_rep_map, addzoneconf_clausesets
+};
+
+
 static isc_result_t
 parse_unitstring(char *str, isc_resourcevalue_t *valuep) {
        char *endp;
@@ -1798,6 +1837,30 @@ static cfg_type_t cfg_type_optional_class = {
        NULL, NULL
 };
 
+/*%
+ * An optional string, distinguished by being in quotes
+ */
+static isc_result_t
+parse_optional_qstr(cfg_parser_t *pctx, const cfg_type_t *type,
+                    cfg_obj_t **ret)
+{
+       isc_result_t result;
+       UNUSED(type);
+       CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
+       if (pctx->token.type == isc_tokentype_qstring)
+               CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, ret));
+       else
+               CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
+ cleanup:
+       return (result);
+}
+
+
+static cfg_type_t cfg_type_optional_qstring = {
+       "optional_quoted_string", parse_optional_qstr, NULL, cfg_doc_terminal,
+       NULL, NULL
+};
+
 static isc_result_t
 parse_querysource(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
        isc_result_t result;