mirror of
https://github.com/isc-projects/bind9.git
synced 2026-04-15 22:09:31 -04:00
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]
This commit is contained in:
parent
773896a200
commit
86dcc40058
13 changed files with 734 additions and 36 deletions
11
CHANGES
11
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
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Id: control.c,v 1.38 2010/06/25 23:46:51 tbox Exp $ */
|
||||
/* $Id: control.c,v 1.39 2010/07/11 00:12:57 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,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Id: control.h,v 1.29 2010/06/25 23:46:51 tbox Exp $ */
|
||||
/* $Id: control.h,v 1.30 2010/07/11 00:12:57 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);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Id: server.h,v 1.107 2010/06/25 03:24:05 marka Exp $ */
|
||||
/* $Id: server.h,v 1.108 2010/07/11 00:12:57 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 */
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Id: server.c,v 1.571 2010/06/25 23:46:51 tbox Exp $ */
|
||||
/* $Id: server.c,v 1.572 2010/07/11 00:12:57 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.162 2010/07/11 00:12:57 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);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Id: rndc.c,v 1.129 2010/06/25 23:46:51 tbox Exp $ */
|
||||
/* $Id: rndc.c,v 1.130 2010/07/11 00:12:57 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",
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Id: resolver.h,v 1.66 2010/02/25 05:08:01 tbox Exp $ */
|
||||
/* $Id: resolver.h,v 1.67 2010/07/11 00:12:57 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:
|
||||
*
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Id: view.h,v 1.124 2010/06/22 03:58:38 marka Exp $ */
|
||||
/* $Id: view.h,v 1.125 2010/07/11 00:12:57 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,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Id: resolver.c,v 1.424 2010/07/04 00:48:57 marka Exp $ */
|
||||
/* $Id: resolver.c,v 1.425 2010/07/11 00:12:57 each Exp $ */
|
||||
|
||||
/*! \file */
|
||||
|
||||
|
|
@ -7684,13 +7684,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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Id: view.c,v 1.165 2010/06/22 03:58:38 marka Exp $ */
|
||||
/* $Id: view.c,v 1.166 2010/07/11 00:12:57 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) {
|
||||
|
|
|
|||
|
|
@ -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.16 2010/07/11 00:12:57 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. */
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $Id: namedconf.c,v 1.119 2010/06/25 03:24:05 marka Exp $ */
|
||||
/* $Id: namedconf.c,v 1.120 2010/07/11 00:12:57 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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue