]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
[master] rndc showzone / rndc delzone of non-added zones
authorEvan Hunt <each@isc.org>
Wed, 7 Jan 2015 06:57:57 +0000 (22:57 -0800)
committerEvan Hunt <each@isc.org>
Wed, 7 Jan 2015 06:57:57 +0000 (22:57 -0800)
4030. [func] "rndc delzone" is now applicable to zones that were
configured in named.conf, as well as zones that
were added via "rndc addzone". (Note, however, that
if named.conf is not also modified, the deleted zone
will return when named is reloaded.) [RT #37887]

4029. [func] "rndc showzone" displays the current configuration
of a specified zone. [RT #37887]

13 files changed:
CHANGES
bin/named/control.c
bin/named/include/named/control.h
bin/named/include/named/globals.h
bin/named/include/named/server.h
bin/named/server.c
bin/rndc/rndc.docbook
bin/tests/system/addzone/tests.sh
doc/arm/Bv9ARM-book.xml
doc/arm/notes.xml
lib/isccfg/include/isccfg/cfg.h
lib/isccfg/namedconf.c
lib/isccfg/parser.c

diff --git a/CHANGES b/CHANGES
index fb869c3153762e125a89bd068062f92dacb7fa2e..f0c22752abeffe366fdaf79a9555b3ee62ed59b5 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,12 @@
+4030.  [func]          "rndc delzone" is now applicable to zones that were
+                       configured in named.conf, as well as zones that
+                       were added via "rndc addzone". (Note, however, that
+                       if named.conf is not also modified, the deleted zone
+                       will return when named is reloaded.) [RT #37887]
+
+4029.  [func]          "rndc showzone" displays the current configuration
+                       of a specified zone. [RT #37887]
+
 4028.  [bug]           $GENERATE with a zero step was not being caught as a
                        error.  A $GENERATE with a / but no step was not being
                        caught as a error. [RT #38262]
index 41be1f36537d3537b94f259b114f7cecc8088842..cb91f2e4ca5449401f7d50ed2099509609676aac 100644 (file)
@@ -206,9 +206,11 @@ ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t **text) {
                   command_compare(command, NS_COMMAND_LOADKEYS)) {
                result = ns_server_rekey(ns_g_server, command, text);
        } else if (command_compare(command, NS_COMMAND_ADDZONE)) {
-               result = ns_server_add_zone(ns_g_server, command, text);
+               result = ns_server_addzone(ns_g_server, command, text);
        } else if (command_compare(command, NS_COMMAND_DELZONE)) {
-               result = ns_server_del_zone(ns_g_server, command, text);
+               result = ns_server_delzone(ns_g_server, command, text);
+       } else if (command_compare(command, NS_COMMAND_SHOWZONE)) {
+               result = ns_server_showzone(ns_g_server, command, text);
        } else if (command_compare(command, NS_COMMAND_SIGNING)) {
                result = ns_server_signing(ns_g_server, command, text);
        } else if (command_compare(command, NS_COMMAND_ZONESTATUS)) {
index ee1d6969453e0ea3e4fac64d0e7177afd3f4af59..2ecb4fa07fa515017dcf09e4d8035846c8643eab 100644 (file)
@@ -63,6 +63,7 @@
 #define NS_COMMAND_SIGN        "sign"
 #define NS_COMMAND_LOADKEYS    "loadkeys"
 #define NS_COMMAND_ADDZONE     "addzone"
+#define NS_COMMAND_SHOWZONE    "showzone"
 #define NS_COMMAND_DELZONE     "delzone"
 #define NS_COMMAND_SYNC                "sync"
 #define NS_COMMAND_SIGNING     "signing"
index 5b660d8add078953ce46181f09641314935e5df6..a575b95224fb9db92a99382c069e91a2221e0db2 100644 (file)
@@ -67,6 +67,7 @@ EXTERN unsigned int           ns_g_cpus_detected      INIT(1);
 EXTERN isc_timermgr_t *                ns_g_timermgr           INIT(NULL);
 EXTERN isc_socketmgr_t *       ns_g_socketmgr          INIT(NULL);
 EXTERN cfg_parser_t *          ns_g_parser             INIT(NULL);
+EXTERN cfg_parser_t *          ns_g_addparser          INIT(NULL);
 EXTERN const char *            ns_g_version            INIT(VERSION);
 EXTERN const char *            ns_g_product            INIT(PRODUCT);
 EXTERN const char *            ns_g_description        INIT(DESCRIPTION);
index 953eae77d3600aa1788dd1de492a600ed03f0c9e..5444e4fb308014ad2bd6193e5460041ba87d57cd 100644 (file)
@@ -373,13 +373,19 @@ ns_server_validation(ns_server_t *server, char *args, isc_buffer_t **text);
  * Add a zone to a running process
  */
 isc_result_t
-ns_server_add_zone(ns_server_t *server, char *args, isc_buffer_t **text);
+ns_server_addzone(ns_server_t *server, char *args, isc_buffer_t **text);
 
 /*%
  * Deletes a zone from a running process
  */
 isc_result_t
-ns_server_del_zone(ns_server_t *server, char *args, isc_buffer_t **text);
+ns_server_delzone(ns_server_t *server, char *args, isc_buffer_t **text);
+
+/*%
+ * Show current configuration for a given zone
+ */
+isc_result_t
+ns_server_showzone(ns_server_t *server, char *args, isc_buffer_t **text);
 
 /*%
  * Lists the status of the signing records for a given zone.
index 8b1c1e9f0f55be971a551b9208dbd22939fa6219..cc07901d956bb7be0851aaaa697cfe3606024817 100644 (file)
@@ -247,9 +247,10 @@ struct zonelistentry {
  */
 struct cfg_context {
        isc_mem_t *                     mctx;
-       cfg_parser_t *                  parser;
+       cfg_parser_t *                  conf_parser;
+       cfg_parser_t *                  add_parser;
        cfg_obj_t *                     config;
-       cfg_parser_t *                  nzparser;
+       cfg_obj_t *                     vconfig;
        cfg_obj_t *                     nzconfig;
        cfg_aclconfctx_t *              actx;
 };
@@ -5307,13 +5308,11 @@ configure_session_key(const cfg_obj_t **maps, ns_server_t *server,
 
 static isc_result_t
 setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
-              cfg_parser_t *parser, cfg_aclconfctx_t *actx)
+              cfg_parser_t *conf_parser, cfg_aclconfctx_t *actx)
 {
        isc_result_t result = ISC_R_SUCCESS;
        isc_boolean_t allow = ISC_FALSE;
        struct cfg_context *nzcfg = NULL;
-       cfg_parser_t *nzparser = NULL;
-       cfg_obj_t *nzconfig = NULL;
        const cfg_obj_t *maps[4];
        const cfg_obj_t *options = NULL, *voptions = NULL;
        const cfg_obj_t *nz = NULL;
@@ -5346,33 +5345,31 @@ setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
                return (ISC_R_NOMEMORY);
        }
 
+       memset(nzcfg, 0, sizeof(*nzcfg));
+
        dns_view_setnewzones(view, allow, nzcfg, newzone_cfgctx_destroy);
 
-       memset(nzcfg, 0, sizeof(*nzcfg));
-       isc_mem_attach(view->mctx, &nzcfg->mctx);
        cfg_obj_attach(config, &nzcfg->config);
-       cfg_parser_attach(parser, &nzcfg->parser);
-       cfg_aclconfctx_attach(actx, &nzcfg->actx);
+       if (vconfig != NULL)
+               cfg_obj_attach(vconfig, &nzcfg->vconfig);
 
        /*
-        * Attempt to create a parser and parse the newzones
-        * file.  If successful, preserve both; otherwise leave
-        * them NULL.
+        * We attach the parser that was used for config as well
+        * as the one that wil be used for added zones, to avoid
+        * a shutdown race later.
         */
-       result = cfg_parser_create(view->mctx, ns_g_lctx, &nzparser);
-       if (result == ISC_R_SUCCESS)
-               result = cfg_parse_file(nzparser, view->new_zone_file,
-                                       &cfg_type_newzones, &nzconfig);
-       if (result == ISC_R_SUCCESS) {
-               cfg_parser_attach(nzparser, &nzcfg->nzparser);
-               cfg_obj_attach(nzconfig, &nzcfg->nzconfig);
-       }
+       cfg_parser_attach(conf_parser, &nzcfg->conf_parser);
+       cfg_parser_attach(ns_g_addparser, &nzcfg->add_parser);
+       isc_mem_attach(view->mctx, &nzcfg->mctx);
+       cfg_aclconfctx_attach(actx, &nzcfg->actx);
 
-       if (nzparser != NULL) {
-               if (nzconfig != NULL)
-                       cfg_obj_destroy(nzparser, &nzconfig);
-               cfg_parser_destroy(&nzparser);
-       }
+       /*
+        * This may be called in multiple views, so we reset
+        * the parser each time.
+        */
+       cfg_parser_reset(ns_g_addparser);
+       result = cfg_parse_file(ns_g_addparser, view->new_zone_file,
+                               &cfg_type_newzones, &nzcfg->nzconfig);
 
        return (ISC_R_SUCCESS);
 }
@@ -5948,9 +5945,9 @@ load_configuration(const char *filename, ns_server_t *server,
                INSIST(view != NULL);
 
                num_zones += count_zones(voptions);
+
                CHECK(setup_newzones(view, config, vconfig, conf_parser,
                                     ns_g_aclconfctx));
-
                nzctx = view->new_zone_config;
                if (nzctx != NULL && nzctx->nzconfig != NULL)
                        num_zones += count_zones(nzctx->nzconfig);
@@ -5968,7 +5965,7 @@ load_configuration(const char *filename, ns_server_t *server,
 
                num_zones = count_zones(config);
 
-               CHECK(setup_newzones(view, config, NULL,  conf_parser,
+               CHECK(setup_newzones(view, config, NULL, conf_parser,
                                     ns_g_aclconfctx));
 
                nzctx = view->new_zone_config;
@@ -6614,6 +6611,9 @@ run_server(isc_task_t *task, isc_event_t *event) {
        CHECKFATAL(cfg_parser_create(ns_g_mctx, NULL, &ns_g_parser),
                   "creating default configuration parser");
 
+       CHECKFATAL(cfg_parser_create(ns_g_mctx, NULL, &ns_g_addparser),
+                  "creating additional configuration parser");
+
        if (ns_g_lwresdonly)
                CHECKFATAL(load_configuration(lwresd_g_conffile, server,
                                              ISC_TRUE),
@@ -6663,6 +6663,7 @@ 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);
+       cfg_parser_destroy(&ns_g_addparser);
 
        for (view = ISC_LIST_HEAD(server->viewlist);
             view != NULL;
@@ -8948,40 +8949,45 @@ add_comment(FILE *fp, const char *viewname) {
  * Act on an "addzone" command from the command channel.
  */
 isc_result_t
-ns_server_add_zone(ns_server_t *server, char *args, isc_buffer_t **text) {
-       isc_result_t         result, tresult;
-       isc_buffer_t         argbuf;
-       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_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;
-       const char          *viewname = NULL;
-       dns_rdataclass_t     rdclass;
-       dns_view_t          *view = NULL;
-       isc_buffer_t         buf;
-       dns_fixedname_t      fname;
-       dns_name_t          *dnsname;
-       dns_zone_t          *zone = NULL;
-       FILE                *fp = NULL;
-       struct cfg_context  *cfg = NULL;
-       char                namebuf[DNS_NAME_FORMATSIZE];
-       off_t               offset;
+ns_server_addzone(ns_server_t *server, char *args, isc_buffer_t **text) {
+       isc_result_t result, tresult;
+       isc_buffer_t argbuf;
+       size_t arglen;
+       cfg_obj_t *config = NULL;
+       const cfg_obj_t *zlist = NULL;
+       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 = NULL;
+       isc_buffer_t buf;
+       dns_fixedname_t fname;
+       dns_name_t *dnsname;
+       dns_zone_t *zone = NULL;
+       FILE *fp = NULL;
+       struct cfg_context *cfg = NULL;
+       char  namebuf[DNS_NAME_FORMATSIZE];
+       off_t offset;
 
        /* Try to parse the argument string */
        arglen = strlen(args);
        isc_buffer_init(&argbuf, args, (unsigned int)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));
+
+       /* convert "addzone" to "zone", for the parser's benefit */
+       isc_buffer_forward(&argbuf, 3);
+
+       cfg_parser_reset(ns_g_addparser);
+       CHECK(cfg_parse_buffer(ns_g_addparser, &argbuf,
+                              &cfg_type_addzoneconf, &config));
+       CHECK(cfg_map_get(config, "zone", &zlist));
+       if (! cfg_obj_islist(zlist))
+               CHECK(ISC_R_FAILURE);
+
+       parms = cfg_listelt_value(cfg_list_first(zlist));
 
        zonename = cfg_obj_asstring(cfg_tuple_get(parms, "name"));
        isc_buffer_constinit(&buf, zonename, strlen(zonename));
@@ -9026,24 +9032,9 @@ ns_server_add_zone(ns_server_t *server, char *args, isc_buffer_t **text) {
                /* Create our sub-zone anyway */
                dns_zone_detach(&zone);
                zone = NULL;
-       }
-       else if (result != ISC_R_NOTFOUND)
+       } else if (result != ISC_R_NOTFOUND)
                goto cleanup;
 
-       /* 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))
-                       break;
-               vconfig = NULL;
-       }
-
        /* Open save file for write configuration */
        result = isc_stdio_open(view->new_zone_file, "a", &fp);
        if (result != ISC_R_SUCCESS) {
@@ -9061,7 +9052,7 @@ ns_server_add_zone(ns_server_t *server, char *args, isc_buffer_t **text) {
        result = isc_task_beginexclusive(server->task);
        RUNTIME_CHECK(result == ISC_R_SUCCESS);
        dns_view_thaw(view);
-       result = configure_zone(cfg->config, parms, vconfig,
+       result = configure_zone(cfg->config, parms, cfg->vconfig,
                                server->mctx, view, NULL, cfg->actx,
                                ISC_FALSE, ISC_FALSE);
        dns_view_freeze(view);
@@ -9075,6 +9066,19 @@ ns_server_add_zone(ns_server_t *server, char *args, isc_buffer_t **text) {
        /* Is it there yet? */
        CHECK(dns_zt_find(view->zonetable, dnsname, 0, NULL, &zone));
 
+       /*
+        * If there wasn't a previous newzone config, just save the one
+        * we've created. If there was a previous one, merge the new
+        * zone into it.
+        */
+       if (cfg->nzconfig == NULL)
+               cfg_obj_attach(config, &cfg->nzconfig);
+       else {
+               cfg_obj_t *p;
+               DE_CONST(parms, p);
+               cfg_parser_mapadd(cfg->add_parser, cfg->nzconfig, p, "zone");
+       }
+
        /*
         * Load the zone from the master file.  If this fails, we'll
         * need to undo the configuration we've done already.
@@ -9148,13 +9152,10 @@ ns_server_add_zone(ns_server_t *server, char *args, isc_buffer_t **text) {
  cleanup:
        if (isc_buffer_usedlength(*text) > 0)
                (void) putnull(text);
+       if (config != NULL)
+               cfg_obj_destroy(ns_g_addparser, &config);
        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)
@@ -9165,11 +9166,11 @@ ns_server_add_zone(ns_server_t *server, char *args, isc_buffer_t **text) {
 
 static isc_boolean_t
 inuse(const char* file, isc_boolean_t first, isc_buffer_t **text) {
-#define INUSEMSG "The following files were in use and may now be removed:\n"
-
        if (file != NULL && isc_file_exists(file)) {
                if (first)
-                       (void) putstr(text, INUSEMSG);
+                       (void) putstr(text,
+                                     "The following files were in use "
+                                     "and may now be removed:\n");
                else
                        (void) putstr(text, "\n");
                (void) putstr(text, file);
@@ -9179,84 +9180,26 @@ inuse(const char* file, isc_boolean_t first, isc_buffer_t **text) {
        return (first);
 }
 
-/*
- * Act on a "delzone" command from the command channel.
- */
-isc_result_t
-ns_server_del_zone(ns_server_t *server, char *args, isc_buffer_t **text) {
+static isc_result_t
+nzf_remove(const char *nzfile, const char *viewname, const char *zonename) {
+       size_t znamelen;
        isc_result_t result;
-       dns_zone_t *zone = NULL;
-       dns_zone_t *raw = NULL;
-       dns_zone_t *mayberaw;
-       dns_view_t *view = NULL;
-       dns_db_t *dbp = NULL;
-       const char *filename = NULL;
-       char *tmpname = NULL;
-       char buf[1024];
-       const char *zonename = NULL;
-       size_t znamelen = 0;
        FILE *ifp = NULL, *ofp = NULL;
-       isc_boolean_t exclusive = ISC_FALSE;
-       isc_boolean_t cleanup = ISC_FALSE;
+       char tmp[1024], buf[1024];
        isc_boolean_t inheader = ISC_TRUE;
-       const char *file, *arg;
-
-       /* Parse parameters */
-       (void) next_token(&args, " \t");
-       arg = next_token(&args, " \t");
-       if (arg != NULL &&
-           (strcmp(arg, "-clean") == 0 || strcmp(arg, "-clear") == 0)) {
-               cleanup = ISC_TRUE;
-               arg = next_token(&args, " \t");
-       }
-
-       CHECK(zone_from_args(server, args, arg, &zone, &zonename,
-                            text, ISC_FALSE));
-       if (zone == NULL) {
-               result = ISC_R_UNEXPECTEDEND;
-               goto cleanup;
-       }
-
-       result = isc_task_beginexclusive(server->task);
-       RUNTIME_CHECK(result == ISC_R_SUCCESS);
-       exclusive = ISC_TRUE;
 
-       /*
-        * Was this zone originally added at runtime?
-        * If not, we can't delete it now.
-        */
-       if (!dns_zone_getadded(zone)) {
-               result = ISC_R_NOPERM;
-               goto cleanup;
-       }
-
-       INSIST(zonename != NULL);
        znamelen = strlen(zonename);
 
-       /* Dig out configuration for this zone */
-       view = dns_zone_getview(zone);
-       filename = view->new_zone_file;
-       if (filename == NULL) {
-               /* No adding zones in this view */
-               result = ISC_R_FAILURE;
-               goto cleanup;
-       }
-
        /* Rewrite zone list */
-       result = isc_stdio_open(filename, "r", &ifp);
+       result = isc_stdio_open(nzfile, "r", &ifp);
        if (ifp != NULL && result == ISC_R_SUCCESS) {
                char *found = NULL, *p = NULL;
                size_t n;
 
                /* Create a temporary file */
-               CHECK(isc_string_printf(buf, 1023, "%s.%ld", filename,
-                                       (long)getpid()));
-               if (!(tmpname = isc_mem_strdup(server->mctx, buf))) {
-                       result = ISC_R_NOMEMORY;
-                       goto cleanup;
-               }
-               CHECK(isc_stdio_open(tmpname, "w", &ofp));
-               CHECK(add_comment(ofp, view->name));
+               CHECK(isc_file_template("", "nzf-XXXXXXXX", tmp, sizeof(tmp)));
+               CHECK(isc_stdio_open(tmp, "w", &ofp));
+               CHECK(add_comment(ofp, viewname));
 
                /* Look for the entry for that zone */
                while (fgets(buf, 1024, ifp)) {
@@ -9302,20 +9245,20 @@ ns_server_del_zone(ns_server_t *server, char *args, isc_buffer_t **text) {
                                break;
                        }
 
-                       /* Copy the rest of the buffer out and continue */
+                       /* Copy the buffer out and continue */
                        fputs(buf, ofp);
                }
 
                /* Skip over an option block (matching # of braces) */
                if (found) {
-                       int obrace = 0, cbrace = 0;
+                       int openbrace = 0, closebrace = 0;
                        for (;;) {
                                while (*p) {
-                                       if (*p == '{') obrace++;
-                                       if (*p == '}') cbrace++;
+                                       if (*p == '{') openbrace++;
+                                       else if (*p == '}') closebrace++;
                                        p++;
                                }
-                               if (obrace && (obrace == cbrace))
+                               if (openbrace && (openbrace == closebrace))
                                        break;
                                if (!fgets(buf, 1024, ifp))
                                        break;
@@ -9333,7 +9276,7 @@ ns_server_del_zone(ns_server_t *server, char *args, isc_buffer_t **text) {
                        }
 
                        /* Move temporary into place */
-                       CHECK(isc_file_rename(tmpname, view->new_zone_file));
+                       CHECK(isc_file_rename(tmp, nzfile));
                } else {
                        isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
                                      NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
@@ -9343,44 +9286,111 @@ ns_server_del_zone(ns_server_t *server, char *args, isc_buffer_t **text) {
                }
        }
 
+ cleanup:
+       if (ifp != NULL)
+               isc_stdio_close(ifp);
+       if (ofp != NULL)
+               isc_stdio_close(ofp);
+
+       return (result);
+}
+
+/*
+ * Act on a "delzone" command from the command channel.
+ */
+isc_result_t
+ns_server_delzone(ns_server_t *server, char *args, isc_buffer_t **text) {
+       isc_result_t result, tresult;
+       dns_zone_t *zone = NULL;
+       dns_zone_t *raw = NULL;
+       dns_zone_t *mayberaw;
+       dns_view_t *view = NULL;
+       dns_db_t *dbp = NULL;
+       const char *zonename = NULL;
+       isc_boolean_t exclusive = ISC_FALSE;
+       isc_boolean_t cleanup = ISC_FALSE;
+       const char *file, *arg;
+       isc_boolean_t added;
+
+       /* Skip the command name. */
+       arg = next_token(&args, " \t");
+       if (arg == NULL)
+               return (ISC_R_UNEXPECTEDEND);
+
+       /* Find out what we are to do. */
+       arg = next_token(&args, " \t");
+       if (arg == NULL)
+               return (ISC_R_UNEXPECTEDEND);
+
+       if (strcmp(arg, "-clean") == 0 || strcmp(arg, "-clear") == 0) {
+               cleanup = ISC_TRUE;
+               arg = next_token(&args, " \t");
+       }
+
+       CHECK(zone_from_args(server, args, arg, &zone, &zonename,
+                            text, ISC_FALSE));
+       if (zone == NULL) {
+               result = ISC_R_UNEXPECTEDEND;
+               goto cleanup;
+       }
+
+       INSIST(zonename != NULL);
+
+       result = isc_task_beginexclusive(server->task);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+       exclusive = ISC_TRUE;
+
+       /* Dig out configuration for this zone */
+       view = dns_zone_getview(zone);
+
        /* Stop answering for this zone */
        if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
                dns_db_detach(&dbp);
                dns_zone_unload(zone);
        }
 
+       /* Remove the zone from the new_zone_file if applicable */
+       added = dns_zone_getadded(zone);
+       if (added && view->new_zone_file != NULL)
+               CHECK(nzf_remove(view->new_zone_file, view->name, zonename));
+
        /* Clean up stub / slave zone files */
        dns_zone_getraw(zone, &raw);
        mayberaw = (raw != NULL) ? raw : zone;
-       if (cleanup) {
-               isc_result_t tresult;
 
+       if (!added) {
+               TCHECK(putstr(text, "zone '"));
+               TCHECK(putstr(text, zonename));
+               TCHECK(putstr(text, "' is no longer loaded.\n"));
+               TCHECK(putstr(text, "To keep it from returning "));
+               TCHECK(putstr(text, "when the server is restarted, it\n"));
+               TCHECK(putstr(text, "must also be removed from named.conf."));
+       } else if (cleanup) {
                file = dns_zone_getfile(mayberaw);
-               if (isc_file_exists(file))
-                       isc_file_remove(file);
+               isc_file_remove(file);
 
                file = dns_zone_getjournal(mayberaw);
-               if (isc_file_exists(file))
-                       isc_file_remove(file);
+               isc_file_remove(file);
 
                if (zone != mayberaw) {
                        file = dns_zone_getfile(zone);
-                       if (isc_file_exists(file))
-                               isc_file_remove(file);
+                       isc_file_remove(file);
 
                        file = dns_zone_getjournal(zone);
-                       if (isc_file_exists(file))
-                               isc_file_remove(file);
+                       isc_file_remove(file);
                }
-               TCHECK(putstr(text, "zone "));
+               TCHECK(putstr(text, "zone '"));
                TCHECK(putstr(text, zonename));
-               TCHECK(putstr(text, " and associated files deleted"));
-               TCHECK(putnull(text));
+               TCHECK(putstr(text, "' and associated files were deleted."));
        } else if (dns_zone_gettype(mayberaw) == dns_zone_slave ||
                   dns_zone_gettype(mayberaw) == dns_zone_stub)
        {
                isc_boolean_t first;
 
+               TCHECK(putstr(text, "zone '"));
+               TCHECK(putstr(text, zonename));
+               TCHECK(putstr(text, "' was deleted.\n"));
+
                file = dns_zone_getfile(mayberaw);
                first = inuse(file, ISC_TRUE, text);
 
@@ -9392,9 +9402,8 @@ ns_server_del_zone(ns_server_t *server, char *args, isc_buffer_t **text) {
                        first = inuse(file, first, text);
 
                        file = dns_zone_getjournal(zone);
-                       (void)inuse(file, first, text);
+                       (void) inuse(file, first, text);
                }
-               (void) putnull(text);
        }
 
        CHECK(dns_zt_unmount(view->zonetable, zone));
@@ -9411,24 +9420,114 @@ ns_server_del_zone(ns_server_t *server, char *args, isc_buffer_t **text) {
  cleanup:
        if (isc_buffer_usedlength(*text) > 0)
                (void) putnull(text);
-       if (exclusive)
-               isc_task_endexclusive(server->task);
-       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 (raw != NULL)
                dns_zone_detach(&raw);
+       if (exclusive)
+               isc_task_endexclusive(server->task);
        if (zone != NULL)
                dns_zone_detach(&zone);
 
        return (result);
 }
 
+/*
+ * Act on a "showzone" command from the command channel.
+ */
+static const cfg_obj_t *
+find_name_in_list_from_map(const cfg_obj_t *config,
+                          const char *map_key_for_list,
+                          const char *name)
+{
+       const cfg_obj_t *list = NULL;
+       const cfg_listelt_t *element;
+       const cfg_obj_t *result;
+
+       cfg_map_get(config, map_key_for_list, &list);
+       for (element = cfg_list_first(list);
+            element != NULL;
+            element = cfg_list_next(element))
+       {
+               const char *vname;
+               result = cfg_listelt_value(element);
+               INSIST(result != NULL);
+               vname = cfg_obj_asstring(cfg_tuple_get(result, "name"));
+               if (vname != NULL && !strcasecmp(vname, name))
+                       break;
+               result = NULL;
+       }
+
+       return (result);
+}
+
+static void
+dumpit(void *arg, const char *buf, int len) {
+       isc_buffer_t **tpp = arg;
+       putmem(tpp, buf, len);
+}
+
+isc_result_t
+ns_server_showzone(ns_server_t *server, char *args, isc_buffer_t **text) {
+       isc_result_t result;
+       const cfg_obj_t *vconfig = NULL, *zconfig = NULL;
+       const char *zonename = NULL;
+       const cfg_obj_t *map;
+       dns_view_t *view = NULL;
+       dns_zone_t *zone = NULL;
+       struct cfg_context *cfg = NULL;
+       isc_boolean_t exclusive = ISC_FALSE;
+
+       /* Parse parameters */
+       CHECK(zone_from_args(server, args, NULL, &zone, &zonename,
+                            text, ISC_TRUE));
+       if (zone == NULL) {
+               result = ISC_R_UNEXPECTEDEND;
+               goto cleanup;
+       }
+
+       view = dns_zone_getview(zone);
+       dns_zone_detach(&zone);
+
+       cfg = (struct cfg_context *) view->new_zone_config;
+       if (cfg == NULL) {
+               result = ISC_R_FAILURE;
+               goto cleanup;
+       }
+
+       result = isc_task_beginexclusive(server->task);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+       exclusive = ISC_TRUE;
+
+       /* Find the view statement */
+       vconfig = find_name_in_list_from_map(cfg->config, "view", view->name);
+
+       /* Find the zone statement */
+       if (vconfig != NULL)
+               map = cfg_tuple_get(vconfig, "options");
+       else
+               map = cfg->config;
+
+       zconfig = find_name_in_list_from_map(map, "zone", zonename);
+       if (zconfig == NULL)
+               zconfig = find_name_in_list_from_map(cfg->nzconfig,
+                                                    "zone", zonename);
+       if (zconfig == NULL)
+               CHECK(ISC_R_NOTFOUND);
+
+       putstr(text, "zone ");
+       cfg_printx(zconfig, CFG_PRINTER_ONELINE, dumpit, text);
+       putstr(text, ";");
+
+       result = ISC_R_SUCCESS;
+
+ cleanup:
+       if (isc_buffer_usedlength(*text) > 0)
+               (void) putnull(text);
+       if (exclusive)
+               isc_task_endexclusive(server->task);
+
+       return (result);
+}
+
 static void
 newzone_cfgctx_destroy(void **cfgp) {
        struct cfg_context *cfg;
@@ -9437,20 +9536,22 @@ newzone_cfgctx_destroy(void **cfgp) {
 
        cfg = *cfgp;
 
-       if (cfg->actx != NULL)
-               cfg_aclconfctx_detach(&cfg->actx);
-
-       if (cfg->parser != NULL) {
+       if (cfg->conf_parser != NULL) {
                if (cfg->config != NULL)
-                       cfg_obj_destroy(cfg->parser, &cfg->config);
-               cfg_parser_destroy(&cfg->parser);
+                       cfg_obj_destroy(cfg->conf_parser, &cfg->config);
+               if (cfg->vconfig != NULL)
+                       cfg_obj_destroy(cfg->conf_parser, &cfg->vconfig);
+               cfg_parser_destroy(&cfg->conf_parser);
        }
-       if (cfg->nzparser != NULL) {
+       if (cfg->add_parser != NULL) {
                if (cfg->nzconfig != NULL)
-                       cfg_obj_destroy(cfg->nzparser, &cfg->nzconfig);
-               cfg_parser_destroy(&cfg->nzparser);
+                       cfg_obj_destroy(cfg->add_parser, &cfg->nzconfig);
+               cfg_parser_destroy(&cfg->add_parser);
        }
 
+       if (cfg->actx != NULL)
+               cfg_aclconfctx_detach(&cfg->actx);
+
        isc_mem_putanddetach(&cfg->mctx, cfg, sizeof(*cfg));
        *cfgp = NULL;
 }
index b688fe8e96aca4d73bd82966864aeecc91b04b78..55af7c67c6fe30772d3a2d98e8b32b3618b0221e 100644 (file)
         <listitem>
           <para>
             Delete a zone while the server is running.
-            Only zones that were originally added via
-            <command>rndc addzone</command> can be deleted
-            in this manner. 
           </para>
           <para>
             If the <option>-clean</option> is specified,
             be cleaned up will be reported in the output
             of the <command>rndc delzone</command> command.)
           </para>
+         <para>
+            If the zone was originally added via
+           <command>rndc addzone</command>, then it will be
+           removed permanently. However, if it was originally
+           configured in <filename>named.conf</filename>, then
+           that original configuration is still in place; when
+           the server is restarted or reconfigured, the zone will
+           come back. To remove it permanently, it must also be
+           removed from <filename>named.conf</filename>
+         </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><userinput>showzone <replaceable>zone</replaceable> <optional><replaceable>class</replaceable> <optional><replaceable>view</replaceable></optional></optional> </userinput></term>
+        <listitem>
+          <para>
+            Print the configuration of a running zone.
+          </para>
         </listitem>
       </varlistentry>
 
index ff7b5c05f09cde1cc9f3f97efae1997f223bda9a..a6fe69da24c6a8aa2aaf17cbd92e88a8cc1626bd 100755 (executable)
@@ -92,6 +92,15 @@ n=`expr $n + 1`
 if [ $ret != 0 ]; then echo "I:failed"; fi
 status=`expr $status + $ret`
 
+echo "I:checking rndc showzone with previously added zone ($n)"
+ret=0
+$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 showzone previous.example > rndc.out.ns2.$n
+expected='zone "previous.example" { type master; file "previous.db"; };'
+[ "`cat rndc.out.ns2.$n`" = "$expected" ] || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
 echo "I:deleting previously added zone ($n)"
 ret=0
 $RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 delzone previous.example 2>&1 | sed 's/^/I:ns2 /'
@@ -130,13 +139,20 @@ n=`expr $n + 1`
 if [ $ret != 0 ]; then echo "I:failed"; fi
 status=`expr $status + $ret`
 
-echo "I:attempt to delete a normally-loaded zone ($n)"
+echo "I:checking rndc showzone with a normally-loaded zone ($n)"
+ret=0
+$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 showzone normal.example > rndc.out.ns2.$n
+expected='zone "normal.example" { type master; file "normal.db"; };'
+[ "`cat rndc.out.ns2.$n`" = "$expected" ] || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:delete a normally-loaded zone ($n)"
 ret=0
 $RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 delzone normal.example 2> rndc.out.ns2.$n
-grep "permission denied" rndc.out.ns2.$n > /dev/null || ret=1
 $DIG $DIGOPTS @10.53.0.2 a.normal.example a > dig.out.ns2.$n
-grep 'status: NOERROR' dig.out.ns2.$n > /dev/null || ret=1
-grep '^a.normal.example' dig.out.ns2.$n > /dev/null || ret=1
+grep 'status: REFUSED' dig.out.ns2.$n > /dev/null || ret=1
 n=`expr $n + 1`
 if [ $ret != 0 ]; then echo "I:failed"; fi
 status=`expr $status + $ret`
@@ -267,6 +283,14 @@ n=`expr $n + 1`
 if [ $ret != 0 ]; then echo "I:failed"; fi
 status=`expr $status + $ret`
 
+echo "I:checking rndc showzone with newly added zone ($n)"
+ret=0
+$RNDC -c ../common/rndc.conf -s 10.53.0.2 -p 9953 showzone added.example in external > rndc.out.ns2.$n
+expected='zone "added.example" in external { type master; file "added.db"; };'
+[ "`cat rndc.out.ns2.$n`" = "$expected" ] || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
 
 echo "I:deleting newly added zone ($n)"
 ret=0
index b459d6a04de77fe8d4641de90bc9dc9becbf367a..406dc646d993f2fe53d324637873baf2a550c04a 100644 (file)
@@ -5894,8 +5894,7 @@ options {
              <listitem>
                <para>
                  If <userinput>yes</userinput>, then zones can be
-                 added at runtime via <command>rndc addzone</command>
-                 or deleted via <command>rndc delzone</command>.
+                 added at runtime via <command>rndc addzone</command>.
                  The default is <userinput>no</userinput>.
                </para>
              </listitem>
index e0f733887239c1cceb1f61284b5524b47fce5281..52ffd6c9a5c452c3fe3807372f8b38bd29e3c1ae 100644 (file)
          line option.
        </para>
       </listitem>
+      <listitem>
+       <para>
+         <command>rndc delzone</command> can now be applied to zones
+         which were configured in <filename>named.conf</filename>;
+         it is no longer restricted to zones which were added by
+         <command>rndc addzone</command>.  (Note, however, that
+         this does not edit <filename>named.conf</filename>; the zone
+         must be removed from the configuration or it will return
+         when <command>named</command> is restarted or reloaded.)
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         <command>rndc showzone</command> displays the current
+         configuration for a specified zone.
+       </para>
+      </listitem>
     </itemizedlist>
   </sect2>
   <sect2 id="relnotes_changes">
index e6176ecbc49a085f4e20c77427526208c3170611..a0af6bfb0761c446a598141d08427469bd9b9189 100644 (file)
@@ -39,7 +39,6 @@
 #include <isc/types.h>
 #include <isc/list.h>
 
-
 /***
  *** Types
  ***/
@@ -144,6 +143,26 @@ cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
  *\li      others                            - file contains errors
  */
 
+isc_result_t
+cfg_parser_mapadd(cfg_parser_t *pctx, cfg_obj_t *mapobj,
+                 cfg_obj_t *obj, const char *clause);
+/*%<
+ * Add the object 'obj' to the specified clause in mapbody 'mapobj'.
+ * Used for adding new zones.
+ *
+ * Require:
+ * \li     'obj' is a valid cfg_obj_t.
+ * \li     'mapobj' is a valid cfg_obj_t of type map.
+ * \li     'pctx' is a valid cfg_parser_t.
+ */
+
+void
+cfg_parser_reset(cfg_parser_t *pctx);
+/*%<
+ * Reset an existing parser so it can be re-used for a new file or
+ * buffer.
+ */
+
 void
 cfg_parser_destroy(cfg_parser_t **pctxp);
 /*%<
@@ -412,6 +431,7 @@ cfg_printx(const cfg_obj_t *obj, unsigned int flags,
           void *closure);
 
 #define CFG_PRINTER_XKEY        0x1     /* '?' out shared keys. */
+#define CFG_PRINTER_ONELINE     0x2     /* print config as a single line */
 
 /*%<
  * Print the configuration object 'obj' by repeatedly calling the
@@ -475,7 +495,6 @@ cfg_obj_line(const cfg_obj_t *obj);
  * Return the line in file where this object was defined.
  */
 
-
 ISC_LANG_ENDDECLS
 
 #endif /* ISCCFG_CFG_H */
index ed8424d559acf4b7facf9eee89cce66f9d8db3f3..0fffdd49d67c5717f001136d1d68c6eb6dc9c726 100644 (file)
@@ -304,9 +304,9 @@ parse_matchname(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
 
 static void
 doc_matchname(cfg_printer_t *pctx, const cfg_type_t *type) {
-       cfg_print_chars(pctx, "[ ", 2);
+       cfg_print_cstr(pctx, "[ ");
        cfg_doc_obj(pctx, type->of);
-       cfg_print_chars(pctx, " ]", 2);
+       cfg_print_cstr(pctx, " ]");
 }
 
 static const char *matchtype_enums[] = {
@@ -739,11 +739,11 @@ parse_boolorauto(cfg_parser_t *pctx, const cfg_type_t *type,
 static void
 print_boolorauto(cfg_printer_t *pctx, const cfg_obj_t *obj) {
        if (obj->type->rep == &cfg_rep_void)
-               cfg_print_chars(pctx, "auto", 4);
+               cfg_print_cstr(pctx, "auto");
        else if (obj->value.boolean)
-               cfg_print_chars(pctx, "yes", 3);
+               cfg_print_cstr(pctx, "yes");
        else
-               cfg_print_chars(pctx, "no", 2);
+               cfg_print_cstr(pctx, "no");
 }
 
 static void
@@ -1117,18 +1117,18 @@ doc_rpz_policy(cfg_printer_t *pctx, const cfg_type_t *type) {
        /*
         * This is cfg_doc_enum() without the trailing " )".
         */
-       cfg_print_chars(pctx, "( ", 2);
+       cfg_print_cstr(pctx, "( ");
        for (p = type->of; *p != NULL; p++) {
                cfg_print_cstr(pctx, *p);
                if (p[1] != NULL)
-                       cfg_print_chars(pctx, " | ", 3);
+                       cfg_print_cstr(pctx, " | ");
        }
 }
 
 static void
 doc_rpz_cname(cfg_printer_t *pctx, const cfg_type_t *type) {
        cfg_doc_terminal(pctx, type);
-       cfg_print_chars(pctx, " )", 2);
+       cfg_print_cstr(pctx, " )");
 }
 
 /*
@@ -1232,9 +1232,9 @@ cfg_print_kv_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
                if (fieldobj->type->print == cfg_print_void)
                        continue;
                if (i != 0) {
-                       cfg_print_chars(pctx, " ", 1);
+                       cfg_print_cstr(pctx, " ");
                        cfg_print_cstr(pctx, f->name);
-                       cfg_print_chars(pctx, " ", 1);
+                       cfg_print_cstr(pctx, " ");
                }
                cfg_print_obj(pctx, fieldobj);
        }
@@ -1247,14 +1247,14 @@ cfg_doc_kv_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
        fields = type->of;
        for (f = fields; f->name != NULL; f++) {
                if (f != fields) {
-                       cfg_print_chars(pctx, " [ ", 3);
+                       cfg_print_cstr(pctx, " [ ");
                        cfg_print_cstr(pctx, f->name);
                        if (f->type->doc != cfg_doc_void)
-                               cfg_print_chars(pctx, " ", 1);
+                               cfg_print_cstr(pctx, " ");
                }
                cfg_doc_obj(pctx, f->type);
                if (f != fields)
-                       cfg_print_chars(pctx, " ]", 2);
+                       cfg_print_cstr(pctx, " ]");
        }
 }
 
@@ -1931,11 +1931,11 @@ static cfg_tuplefielddef_t addzone_fields[] = {
        { 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 };
+       "zone", 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 },
+       { "zone", &cfg_type_addzone, CFG_CLAUSEFLAG_MULTI },
        { NULL, NULL, 0 }
 };
 
@@ -2119,7 +2119,7 @@ static void
 print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj) {
        const keyword_type_t *kw = obj->type->of;
        cfg_print_cstr(pctx, kw->name);
-       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, " ");
        kw->type->print(pctx, obj);
 }
 
@@ -2127,18 +2127,18 @@ static void
 doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) {
        const keyword_type_t *kw = type->of;
        cfg_print_cstr(pctx, kw->name);
-       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, " ");
        cfg_doc_obj(pctx, kw->type);
 }
 
 static void
 doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) {
        const keyword_type_t *kw = type->of;
-       cfg_print_chars(pctx, "[ ", 2);
+       cfg_print_cstr(pctx, "[ ");
        cfg_print_cstr(pctx, kw->name);
-       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, " ");
        cfg_doc_obj(pctx, kw->type);
-       cfg_print_chars(pctx, " ]", 2);
+       cfg_print_cstr(pctx, " ]");
 }
 
 static const char *dialup_enums[] = {
@@ -2283,9 +2283,9 @@ doc_geoip(cfg_printer_t *pctx, const cfg_type_t *type) {
        cfg_print_cstr(pctx, "[ db ");
        cfg_doc_enum(pctx, &cfg_type_geoipdb);
        cfg_print_cstr(pctx, " ]");
-       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, " ");
        cfg_doc_enum(pctx, &cfg_type_geoiptype);
-       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, " ");
        cfg_print_cstr(pctx, "<quoted_string>");
 }
 #endif /* HAVE_GEOIP */
@@ -2399,11 +2399,11 @@ static cfg_type_t cfg_type_controls = {
 static void
 doc_optional_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
        const keyword_type_t *kw = type->of;
-       cfg_print_chars(pctx, "[ ", 2);
+       cfg_print_cstr(pctx, "[ ");
        cfg_print_cstr(pctx, kw->name);
-       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, " ");
        cfg_doc_obj(pctx, kw->type);
-       cfg_print_chars(pctx, " ]", 2);
+       cfg_print_cstr(pctx, " ]");
 }
 
 static cfg_type_t cfg_type_optional_allow = {
@@ -2646,7 +2646,7 @@ static cfg_tuplefielddef_t negated_fields[] = {
 
 static void
 print_negated(cfg_printer_t *pctx, const cfg_obj_t *obj) {
-       cfg_print_chars(pctx, "!", 1);
+       cfg_print_cstr(pctx, "!");
        cfg_print_tuple(pctx, obj);
 }
 
@@ -2881,9 +2881,9 @@ static void
 doc_logfile(cfg_printer_t *pctx, const cfg_type_t *type) {
        UNUSED(type);
        cfg_print_cstr(pctx, "<quoted_string>");
-       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, " ");
        cfg_print_cstr(pctx, "[ versions ( \"unlimited\" | <integer> ) ]");
-       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, " ");
        cfg_print_cstr(pctx, "[ size <size> ]");
 }
 
@@ -3049,25 +3049,25 @@ static cfg_type_t cfg_type_nameport = {
 static void
 doc_sockaddrnameport(cfg_printer_t *pctx, const cfg_type_t *type) {
        UNUSED(type);
-       cfg_print_chars(pctx, "( ", 2);
+       cfg_print_cstr(pctx, "( ");
        cfg_print_cstr(pctx, "<quoted_string>");
-       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, " ");
        cfg_print_cstr(pctx, "[ port <integer> ]");
-       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, " ");
        cfg_print_cstr(pctx, "[ dscp <integer> ]");
-       cfg_print_chars(pctx, " | ", 3);
+       cfg_print_cstr(pctx, " | ");
        cfg_print_cstr(pctx, "<ipv4_address>");
-       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, " ");
        cfg_print_cstr(pctx, "[ port <integer> ]");
-       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, " ");
        cfg_print_cstr(pctx, "[ dscp <integer> ]");
-       cfg_print_chars(pctx, " | ", 3);
+       cfg_print_cstr(pctx, " | ");
        cfg_print_cstr(pctx, "<ipv6_address>");
-       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, " ");
        cfg_print_cstr(pctx, "[ port <integer> ]");
-       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, " ");
        cfg_print_cstr(pctx, "[ dscp <integer> ]");
-       cfg_print_chars(pctx, " )", 2);
+       cfg_print_cstr(pctx, " )");
 }
 
 static isc_result_t
@@ -3141,17 +3141,17 @@ static cfg_type_t cfg_type_nameportiplist = {
 static void
 doc_masterselement(cfg_printer_t *pctx, const cfg_type_t *type) {
        UNUSED(type);
-       cfg_print_chars(pctx, "( ", 2);
+       cfg_print_cstr(pctx, "( ");
        cfg_print_cstr(pctx, "<masters>");
-       cfg_print_chars(pctx, " | ", 3);
+       cfg_print_cstr(pctx, " | ");
        cfg_print_cstr(pctx, "<ipv4_address>");
-       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, " ");
        cfg_print_cstr(pctx, "[ port <integer> ]");
-       cfg_print_chars(pctx, " | ", 3);
+       cfg_print_cstr(pctx, " | ");
        cfg_print_cstr(pctx, "<ipv6_address>");
-       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, " ");
        cfg_print_cstr(pctx, "[ port <integer> ]");
-       cfg_print_chars(pctx, " )", 2);
+       cfg_print_cstr(pctx, " )");
 }
 
 static isc_result_t
index 3c4444d57ea7a02d4e77cacc1856c1163afeb9af..956853b0397a46dbf21e7b974ef0ece49d693c0a 100644 (file)
@@ -147,24 +147,34 @@ cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
 
 static void
 print_open(cfg_printer_t *pctx) {
-       cfg_print_chars(pctx, "{\n", 2);
-       pctx->indent++;
+       if ((pctx->flags & CFG_PRINTER_ONELINE) != 0)
+               cfg_print_cstr(pctx, "{ ");
+       else {
+               cfg_print_cstr(pctx, "{\n");
+               pctx->indent++;
+       }
 }
 
 static void
 print_indent(cfg_printer_t *pctx) {
        int indent = pctx->indent;
+       if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) {
+               cfg_print_cstr(pctx, " ");
+               return;
+       }
        while (indent > 0) {
-               cfg_print_chars(pctx, "\t", 1);
+               cfg_print_cstr(pctx, "\t");
                indent--;
        }
 }
 
 static void
 print_close(cfg_printer_t *pctx) {
-       pctx->indent--;
-       print_indent(pctx);
-       cfg_print_chars(pctx, "}", 1);
+       if ((pctx->flags & CFG_PRINTER_ONELINE) == 0) {
+               pctx->indent--;
+               print_indent(pctx);
+       }
+       cfg_print_cstr(pctx, "}");
 }
 
 isc_result_t
@@ -262,7 +272,7 @@ cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
        for (f = fields, i = 0; f->name != NULL; f++, i++) {
                const cfg_obj_t *fieldobj = obj->value.tuple[i];
                if (need_space)
-                       cfg_print_chars(pctx, " ", 1);
+                       cfg_print_cstr(pctx, " ");
                cfg_print_obj(pctx, fieldobj);
                need_space = ISC_TF(fieldobj->type->print != cfg_print_void);
        }
@@ -276,7 +286,7 @@ cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
 
        for (f = fields; f->name != NULL; f++) {
                if (need_space)
-                       cfg_print_chars(pctx, " ", 1);
+                       cfg_print_cstr(pctx, " ");
                cfg_doc_obj(pctx, f->type);
                need_space = ISC_TF(f->type->print != cfg_print_void);
        }
@@ -481,6 +491,20 @@ cfg_parser_setcallback(cfg_parser_t *pctx,
        pctx->callbackarg = arg;
 }
 
+void
+cfg_parser_reset(cfg_parser_t *pctx) {
+       REQUIRE(pctx != NULL);
+
+       if (pctx->lexer != NULL)
+               isc_lex_close(pctx->lexer);
+
+       pctx->seen_eof = ISC_FALSE;
+       pctx->ungotten = ISC_FALSE;
+       pctx->errors = 0;
+       pctx->warnings = 0;
+       pctx->line = 0;
+}
+
 /*
  * Parse a configuration using a pctx where a lexer has already
  * been set up with a source.
@@ -535,9 +559,12 @@ cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
        const cfg_type_t *type, cfg_obj_t **ret)
 {
        isc_result_t result;
+
        REQUIRE(buffer != NULL);
+
        CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
        CHECK(parse2(pctx, type, ret));
+
  cleanup:
        return (result);
 }
@@ -821,13 +848,13 @@ cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
 void
 cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
        const char * const *p;
-       cfg_print_chars(pctx, "( ", 2);
+       cfg_print_cstr(pctx, "( ");
        for (p = type->of; *p != NULL; p++) {
                cfg_print_cstr(pctx, *p);
                if (p[1] != NULL)
-                       cfg_print_chars(pctx, " | ", 3);
+                       cfg_print_cstr(pctx, " | ");
        }
-       cfg_print_chars(pctx, " )", 2);
+       cfg_print_cstr(pctx, " )");
 }
 
 void
@@ -837,21 +864,21 @@ cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
 
 static void
 print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
-       cfg_print_chars(pctx, "\"", 1);
+       cfg_print_cstr(pctx, "\"");
        cfg_print_ustring(pctx, obj);
-       cfg_print_chars(pctx, "\"", 1);
+       cfg_print_cstr(pctx, "\"");
 }
 
 static void
 print_sstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
-       cfg_print_chars(pctx, "\"", 1);
+       cfg_print_cstr(pctx, "\"");
        if ((pctx->flags & CFG_PRINTER_XKEY) != 0) {
                unsigned int len = obj->value.string.length;
                while (len-- > 0)
-                       cfg_print_chars(pctx, "?", 1);
+                       cfg_print_cstr(pctx, "?");
        } else
                cfg_print_ustring(pctx, obj);
-       cfg_print_chars(pctx, "\"", 1);
+       cfg_print_cstr(pctx, "\"");
 }
 
 static void
@@ -958,9 +985,9 @@ cfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
 void
 cfg_print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
        if (obj->value.boolean)
-               cfg_print_chars(pctx, "yes", 3);
+               cfg_print_cstr(pctx, "yes");
        else
-               cfg_print_chars(pctx, "no", 2);
+               cfg_print_cstr(pctx, "no");
 }
 
 cfg_type_t cfg_type_boolean = {
@@ -994,8 +1021,9 @@ create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
 }
 
 static void
-free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
-       cfg_obj_destroy(pctx, &elt->obj);
+free_listelt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
+       if (elt->obj != NULL)
+               cfg_obj_destroy(pctx, &elt->obj);
        isc_mem_put(pctx->mctx, elt, sizeof(*elt));
 }
 
@@ -1007,7 +1035,7 @@ free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
             elt = next)
        {
                next = ISC_LIST_NEXT(elt, link);
-               free_list_elt(pctx, elt);
+               free_listelt(pctx, elt);
        }
 }
 
@@ -1064,7 +1092,7 @@ parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
 
  cleanup:
        if (elt != NULL)
-               free_list_elt(pctx, elt);
+               free_listelt(pctx, elt);
        CLEANUP_OBJ(listobj);
        return (result);
 }
@@ -1076,10 +1104,16 @@ print_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
 
        for (elt = ISC_LIST_HEAD(*list);
             elt != NULL;
-            elt = ISC_LIST_NEXT(elt, link)) {
-               print_indent(pctx);
-               cfg_print_obj(pctx, elt->obj);
-               cfg_print_chars(pctx, ";\n", 2);
+            elt = ISC_LIST_NEXT(elt, link))
+       {
+               if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) {
+                       cfg_print_obj(pctx, elt->obj);
+                       cfg_print_cstr(pctx, "; ");
+               } else {
+                       print_indent(pctx);
+                       cfg_print_obj(pctx, elt->obj);
+                       cfg_print_cstr(pctx, ";\n");
+               }
        }
 }
 
@@ -1104,9 +1138,9 @@ cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
 
 void
 cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
-       cfg_print_chars(pctx, "{ ", 2);
+       cfg_print_cstr(pctx, "{ ");
        cfg_doc_obj(pctx, type->of);
-       cfg_print_chars(pctx, "; ... }", 7);
+       cfg_print_cstr(pctx, "; ... }");
 }
 
 /*
@@ -1152,7 +1186,7 @@ cfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) {
             elt = ISC_LIST_NEXT(elt, link)) {
                cfg_print_obj(pctx, elt->obj);
                if (ISC_LIST_NEXT(elt, link) != NULL)
-                       cfg_print_chars(pctx, " ", 1);
+                       cfg_print_cstr(pctx, " ");
        }
 }
 
@@ -1476,10 +1510,24 @@ cfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **
        return (parse_any_named_map(pctx, &cfg_type_netprefix, type, ret));
 }
 
+static void
+print_symval(cfg_printer_t *pctx, const char *name, cfg_obj_t *obj) {
+       if ((pctx->flags & CFG_PRINTER_ONELINE) == 0)
+               print_indent(pctx);
+
+       cfg_print_cstr(pctx, name);
+       cfg_print_cstr(pctx, " ");
+       cfg_print_obj(pctx, obj);
+
+       if ((pctx->flags & CFG_PRINTER_ONELINE) == 0)
+               cfg_print_cstr(pctx, ";\n");
+       else
+               cfg_print_cstr(pctx, "; ");
+}
+
 void
 cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) {
        isc_result_t result = ISC_R_SUCCESS;
-
        const cfg_clausedef_t * const *clauseset;
 
        for (clauseset = obj->value.map.clausesets;
@@ -1503,19 +1551,13 @@ cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) {
                                        for (elt = ISC_LIST_HEAD(*list);
                                             elt != NULL;
                                             elt = ISC_LIST_NEXT(elt, link)) {
-                                               print_indent(pctx);
-                                               cfg_print_cstr(pctx, clause->name);
-                                               cfg_print_chars(pctx, " ", 1);
-                                               cfg_print_obj(pctx, elt->obj);
-                                               cfg_print_chars(pctx, ";\n", 2);
+                                               print_symval(pctx,
+                                                            clause->name,
+                                                            elt->obj);
                                        }
                                } else {
                                        /* Single-valued. */
-                                       print_indent(pctx);
-                                       cfg_print_cstr(pctx, clause->name);
-                                       cfg_print_chars(pctx, " ", 1);
-                                       cfg_print_obj(pctx, obj);
-                                       cfg_print_chars(pctx, ";\n", 2);
+                                       print_symval(pctx, clause->name, obj);
                                }
                        } else if (result == ISC_R_NOTFOUND) {
                                ; /* do nothing */
@@ -1536,11 +1578,10 @@ cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
                     clause->name != NULL;
                     clause++) {
                        cfg_print_cstr(pctx, clause->name);
-                       cfg_print_chars(pctx, " ", 1);
+                       cfg_print_cstr(pctx, " ");
                        cfg_doc_obj(pctx, clause->type);
-                       cfg_print_chars(pctx, ";", 1);
                        /* XXX print flags here? */
-                       cfg_print_chars(pctx, "\n\n", 2);
+                       cfg_print_cstr(pctx, ";\n\n");
                }
        }
 }
@@ -1562,7 +1603,7 @@ void
 cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) {
        if (obj->value.map.id != NULL) {
                cfg_print_obj(pctx, obj->value.map.id);
-               cfg_print_chars(pctx, " ", 1);
+               cfg_print_cstr(pctx, " ");
        }
        print_open(pctx);
        cfg_print_mapbody(pctx, obj);
@@ -1576,9 +1617,9 @@ print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
        for (p = flagtexts; p->flag != 0; p++) {
                if ((flags & p->flag) != 0) {
                        if (first)
-                               cfg_print_chars(pctx, " // ", 4);
+                               cfg_print_cstr(pctx, " // ");
                        else
-                               cfg_print_chars(pctx, ", ", 2);
+                               cfg_print_cstr(pctx, ", ");
                        cfg_print_cstr(pctx, p->text);
                        first = ISC_FALSE;
                }
@@ -1592,13 +1633,13 @@ cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
 
        if (type->parse == cfg_parse_named_map) {
                cfg_doc_obj(pctx, &cfg_type_astring);
-               cfg_print_chars(pctx, " ", 1);
+               cfg_print_cstr(pctx, " ");
        } else if (type->parse == cfg_parse_addressed_map) {
                cfg_doc_obj(pctx, &cfg_type_netaddr);
-               cfg_print_chars(pctx, " ", 1);
+               cfg_print_cstr(pctx, " ");
        } else if (type->parse == cfg_parse_netprefix_map) {
                cfg_doc_obj(pctx, &cfg_type_netprefix);
-               cfg_print_chars(pctx, " ", 1);
+               cfg_print_cstr(pctx, " ");
        }
 
        print_open(pctx);
@@ -1610,11 +1651,11 @@ cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
                        print_indent(pctx);
                        cfg_print_cstr(pctx, clause->name);
                        if (clause->type->print != cfg_print_void)
-                               cfg_print_chars(pctx, " ", 1);
+                               cfg_print_cstr(pctx, " ");
                        cfg_doc_obj(pctx, clause->type);
-                       cfg_print_chars(pctx, ";", 1);
+                       cfg_print_cstr(pctx, ";");
                        print_clause_flags(pctx, clause->flags);
-                       cfg_print_chars(pctx, "\n", 1);
+                       cfg_print_cstr(pctx, "\n");
                }
        }
        print_close(pctx);
@@ -1910,7 +1951,8 @@ cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
        isc_buffer_init(&buf, text, sizeof(text));
        result = isc_netaddr_totext(na, &buf);
        RUNTIME_CHECK(result == ISC_R_SUCCESS);
-       cfg_print_chars(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf));
+       cfg_print_chars(pctx, isc_buffer_base(&buf),
+                       isc_buffer_usedlength(&buf));
 }
 
 isc_result_t
@@ -1965,26 +2007,26 @@ cfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
        const unsigned int *flagp = type->of;
        int n = 0;
        if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
-               cfg_print_chars(pctx, "( ", 2);
+               cfg_print_cstr(pctx, "( ");
        if (*flagp & CFG_ADDR_V4OK) {
                cfg_print_cstr(pctx, "<ipv4_address>");
                n++;
        }
        if (*flagp & CFG_ADDR_V6OK) {
                if (n != 0)
-                       cfg_print_chars(pctx, " | ", 3);
+                       cfg_print_cstr(pctx, " | ");
                cfg_print_cstr(pctx, "<ipv6_address>");
                n++;
        }
        if (*flagp & CFG_ADDR_WILDOK) {
                if (n != 0)
-                       cfg_print_chars(pctx, " | ", 3);
-               cfg_print_chars(pctx, "*", 1);
+                       cfg_print_cstr(pctx, " | ");
+               cfg_print_cstr(pctx, "*");
                n++;
                POST(n);
        }
        if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
-               cfg_print_chars(pctx, " )", 2);
+               cfg_print_cstr(pctx, " )");
 }
 
 cfg_type_t cfg_type_netaddr = {
@@ -2071,7 +2113,7 @@ print_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) {
        const cfg_netprefix_t *p = &obj->value.netprefix;
 
        cfg_print_rawaddr(pctx, &p->address);
-       cfg_print_chars(pctx, "/", 1);
+       cfg_print_cstr(pctx, "/");
        cfg_print_rawuint(pctx, p->prefixlen);
 }
 
@@ -2180,11 +2222,11 @@ cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) {
        cfg_print_cstr(pctx, buf);
        port = isc_sockaddr_getport(&obj->value.sockaddr);
        if (port != 0) {
-               cfg_print_chars(pctx, " port ", 6);
+               cfg_print_cstr(pctx, " port ");
                cfg_print_rawuint(pctx, port);
        }
        if (obj->value.sockaddrdscp.dscp != -1) {
-               cfg_print_chars(pctx, " dscp ", 6);
+               cfg_print_cstr(pctx, " dscp ");
                cfg_print_rawuint(pctx, obj->value.sockaddrdscp.dscp);
        }
 }
@@ -2193,25 +2235,25 @@ void
 cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
        const unsigned int *flagp = type->of;
        int n = 0;
-       cfg_print_chars(pctx, "( ", 2);
+       cfg_print_cstr(pctx, "( ");
        if (*flagp & CFG_ADDR_V4OK) {
                cfg_print_cstr(pctx, "<ipv4_address>");
                n++;
        }
        if (*flagp & CFG_ADDR_V6OK) {
                if (n != 0)
-                       cfg_print_chars(pctx, " | ", 3);
+                       cfg_print_cstr(pctx, " | ");
                cfg_print_cstr(pctx, "<ipv6_address>");
                n++;
        }
        if (*flagp & CFG_ADDR_WILDOK) {
                if (n != 0)
-                       cfg_print_chars(pctx, " | ", 3);
-               cfg_print_chars(pctx, "*", 1);
+                       cfg_print_cstr(pctx, " | ");
+               cfg_print_cstr(pctx, "*");
                n++;
                POST(n);
        }
-       cfg_print_chars(pctx, " ) ", 3);
+       cfg_print_cstr(pctx, " ) ");
        if (*flagp & CFG_ADDR_WILDOK) {
                cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
        } else {
@@ -2510,7 +2552,6 @@ map_symtabitem_destroy(char *key, unsigned int type,
        cfg_obj_destroy(pctx, &obj);
 }
 
-
 static isc_result_t
 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
        isc_result_t result;
@@ -2587,9 +2628,9 @@ cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
 
 void
 cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) {
-       cfg_print_chars(pctx, "<", 1);
+       cfg_print_cstr(pctx, "<");
        cfg_print_cstr(pctx, type->name);
-       cfg_print_chars(pctx, ">", 1);
+       cfg_print_cstr(pctx, ">");
 }
 
 void
@@ -2604,3 +2645,72 @@ cfg_print_grammar(const cfg_type_t *type,
        pctx.flags = 0;
        cfg_doc_obj(&pctx, type);
 }
+
+isc_result_t
+cfg_parser_mapadd(cfg_parser_t *pctx, cfg_obj_t *mapobj,
+                 cfg_obj_t *obj, const char *clausename)
+{
+       isc_result_t result = ISC_R_SUCCESS;
+       const cfg_map_t *map;
+       isc_symvalue_t symval;
+       cfg_obj_t *destobj = NULL;
+       cfg_listelt_t *elt = NULL;
+       const cfg_clausedef_t * const *clauseset;
+       const cfg_clausedef_t *clause;
+
+       REQUIRE(pctx != NULL);
+       REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
+       REQUIRE(obj != NULL);
+
+       map = &mapobj->value.map;
+
+       clause = NULL;
+       for (clauseset = map->clausesets; *clauseset != NULL; clauseset++) {
+               for (clause = *clauseset; clause->name != NULL; clause++) {
+                       if (strcasecmp(clause->name, clausename) == 0) {
+                               goto breakout;
+                       }
+               }
+       }
+
+ breakout:
+       if (clause == NULL || clause->name == NULL)
+               return (ISC_R_FAILURE);
+
+       result = isc_symtab_lookup(map->symtab, clausename, 0, &symval);
+       if (result == ISC_R_NOTFOUND) {
+               if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
+                       CHECK(cfg_create_list(pctx, &cfg_type_implicitlist,
+                                             &destobj));
+                       CHECK(create_listelt(pctx, &elt));
+                       cfg_obj_attach(obj, &elt->obj);
+                       ISC_LIST_APPEND(destobj->value.list, elt, link);
+                       symval.as_pointer = destobj;
+               } else
+                       symval.as_pointer = obj;
+
+               CHECK(isc_symtab_define(map->symtab, clausename, 1, symval,
+                                       isc_symexists_reject));
+       } else {
+               cfg_obj_t *destobj2 = symval.as_pointer;
+
+               INSIST(result == ISC_R_SUCCESS);
+
+               if (destobj2->type == &cfg_type_implicitlist) {
+                       CHECK(create_listelt(pctx, &elt));
+                       cfg_obj_attach(obj, &elt->obj);
+                       ISC_LIST_APPEND(destobj2->value.list, elt, link);
+               } else
+                       result = ISC_R_EXISTS;
+       }
+
+       destobj = NULL;
+       elt = NULL;
+
+ cleanup:
+       if (elt != NULL)
+               free_listelt(pctx, elt);
+       CLEANUP_OBJ(destobj);
+
+       return (result);
+}