%{
/*
 * Copyright (C) 1996-2001  Internet Software Consortium.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Copyright (C) 1996-2000  Internet Software Consortium.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/* $Id: confparser.y.dirty,v 1.44.2.8 2001/03/21 18:34:31 bwelling Exp $ */

#include <config.h>

#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>

#include <sys/types.h>

#include <isc/dir.h>
#include <isc/error.h>
#include <isc/lex.h>
#include <isc/mem.h>
#include <isc/mutex.h>
#include <isc/net.h>
#include <isc/netaddr.h>
#include <isc/once.h>
#include <isc/string.h>
#include <isc/symtab.h>
#include <isc/util.h>

#include <dns/confcommon.h>
#include <dns/confctx.h>
#include <dns/confparser.h>
#include <dns/log.h>
#include <dns/name.h>
#include <dns/peer.h>
#include <dns/rdataclass.h>
#include <dns/rdatatype.h>
#include <dns/result.h>
#include <dns/ssu.h>
#include <dns/types.h>


/* Type keys for symtab lookup */
#define KEYWORD_SYM_TYPE 0x1
#define CLASS_SYM_TYPE 0x2
#define ACL_SYM_TYPE 0x3


/* used for holding a list of dns_rdatatype_t on the stack */
struct confrdtype_s {
	dns_rdatatype_t *types;
	isc_uint32_t idx;
};

/* used for holding ssu data on the stack */
struct confssu_s {
	isc_boolean_t grant;
	dns_name_t *ident;
	unsigned int matchtype;
	dns_name_t *name;
	struct confrdtype_s rdatatypes;
};

struct keydetails_s {
	char *algorithm;
	char *secret;
};


static isc_mutex_t		yacc_mutex;

/* All these statics are protected by the above yacc_mutex */
static dns_c_ctx_t	       *currcfg;
static isc_mem_t	       *memctx; /* used for internal allocations */
static isc_lex_t	       *mylexer;
static isc_symtab_t	       *keywords;
static dns_c_cbks_t	       *callbacks;
static isc_lexspecials_t	specials;


/*
 * XXXJAB The #define for the default OMAPI port is not available
 * to us, so we make our own.
 *
 * XXXBEW Same goes for the default LWRES port.
 */

#define OMAPI_DEFAULT_PORT 953
#define LWRES_DEFAULT_PORT 921

#define CONF_MAX_IDENT 1024

/* This should be sufficient to permit multiple parsers and lexers if needed */
#define yyparse dns__yyparse

#define YYDEBUG 1

/*
 * Specify a small parser stack size.  This is needed when
 * using bison the generate the parser, because it puts
 * the parser stack in a local variable, and the default
 * initial stack size of 200 is big enough to cause a
 * thread stack overflow.  Berkeley yacc does not suffer
 * from this problem as it allocates the parser stack
 * using malloc.
 */

#define YYMAXDEPTH 200
#define YYINITDEPTH 200


static isc_result_t	tmpres;
static in_port_t	default_port;

int			yyparse(void);
static int		yylex(void);
static void		parser_error(isc_boolean_t lasttoken,
				     const char *fmt, ...);
static void		parser_warning(isc_boolean_t lasttoken,
				       const char *fmt, ...);
static void		parser_complain(isc_boolean_t is_warning,
					isc_boolean_t last_token,
					const char *format, va_list args);
static isc_result_t	unit_to_uint32(char *in, isc_uint32_t *out);
static const char *	token_to_keyword(int token);
static void		yyerror(const char *);
static dns_peerlist_t	*currentpeerlist(dns_c_ctx_t *cfg,
					 isc_boolean_t createIfNeeded);
static isc_boolean_t	keydefinedinscope(dns_c_ctx_t *cfg,
					  const char *name);
static void		replace_zero_ports(dns_c_iplist_t *list,
					   in_port_t val);



/*
 * Returns true if (base * mult) would be too big.
 */
static isc_boolean_t	int_too_big(isc_uint32_t base, isc_uint32_t mult);

/*
 * #define global symbols that various versions of YACC export into our
 * namespace.  This won't work for all versions, but we hope to cover
 * the popular ones.
 */

/*
 * All YACCs
 */
#define yychar		dns__yychar
#define yydebug		dns__yydebug
#define yylval		dns__yylval
#define yynerrs		dns__yynerrs
/*
 * BYACC
 */
#define yyerrflag	dns__yyerrflag
#define yyss		dns__yyss
#define yyssp		dns__yyssp
#define yyval		dns__yyval
#define yyvs		dns__yyvs
#define yyvsp		dns__yyvsp
/*
 * AIX
 */
#define yyps		dns__yyps
#define yypv		dns__yypv
#define yypvt		dns__yypvt
#define yys		dns__yys
#define yystate		dns__yystate
#define yytmp		dns__yytmp
#define yyv		dns__yyv
#define yyval		dns__yyval
#define yyact		dns__yyact
#define yychk		dns__yychk
#define yydef		dns__yydef
#define yyexca		dns__yyexca
#define yypact		dns__yypact
#define yypgo		dns__yypgo
#define yyr1		dns__yyr1
#define yyr2		dns__yyr2
#define yyreds		dns__yyreds
#define yytoks		dns__yytoks

%}

%union {
	char		       *text;
	int			number;
	isc_int32_t		l_int;
	isc_uint32_t		ul_int;
	isc_uint16_t		port_int;
	dns_c_zonetype_t	ztype;
	struct in_addr		ip4_addr;
	struct in6_addr		ip6_addr;
	isc_sockaddr_t		ipaddress;

	struct keydetails_s	keydetails;
	struct confssu_s	ssu;
	struct confrdtype_s	rdatatypelist;
	dns_rdatatype_t		rdatatype;
	dns_c_addata_t		addata;

	isc_boolean_t		boolean;
	dns_rdataclass_t	rrclass;
	dns_severity_t		severity;
	dns_c_trans_t		transport;
	dns_transfer_format_t	tformat;
	dns_notifytype_t	notifytype;
	dns_dialuptype_t	dialuptype;

	dns_c_ipmatchelement_t	*ime;
	dns_c_ipmatchlist_t	*iml;

	dns_c_forw_t		forward;
	dns_c_rrso_t	       *rrorder;
	dns_c_rrsolist_t       *rrolist;
	dns_rdatatype_t		ordertype;
	dns_rdataclass_t	orderclass;
	dns_c_ordering_t	ordering;
	dns_c_iplist_t	       *iplist;
	dns_c_kidlist_t	       *kidlist;
	dns_c_searchlist_t     *searchlist;
}

/* Misc */
%token <text>		L_STRING
%token <text>		L_QSTRING
%token <l_int>		L_INTEGER
%token <ip4_addr>	L_IP4ADDR
%token <ip4_addr>	L_IP4PREFIX
%token <ip6_addr>	L_IP6ADDR

%token		L_ACL
%token		L_ADDITIONAL_DATA
%token		L_ADDRESS
%token		L_ALGID
%token		L_ALLOW
%token		L_ALLOW_NOTIFY
%token		L_ALLOW_QUERY
%token		L_ALLOW_RECURSION
%token		L_ALLOW_TRANSFER
%token		L_ALLOW_UPDATE
%token		L_ALLOW_UPDATE_FORWARDING
%token		L_ALSO_NOTIFY
%token		L_AUTH_NXDOMAIN
%token		L_BANG
%token		L_BLACKHOLE
%token		L_BOGUS
%token		L_CACHE_FILE
%token		L_CATEGORY
%token		L_CHANNEL
%token		L_CHECK_NAMES
%token		L_CLASS
%token		L_CLEAN_INTERVAL
%token		L_CONTROLS
%token		L_CORESIZE
%token		L_ZONE_STATISTICS
%token		L_DATASIZE
%token		L_DATABASE
%token		L_DEALLOC_ON_EXIT
%token		L_DEBUG
%token		L_DEFAULT
%token		L_DENY
%token		L_DIALUP
%token		L_DIRECTORY
%token		L_DUMP_FILE
%token		L_DYNAMIC
%token		L_ENABLE_ZONE
%token		L_END_INCLUDE
%token		L_EOS
%token		L_EXPERT_MODE
%token		L_EXPLICIT
%token		L_FAIL
%token		L_FAKE_IQUERY
%token		L_FALSE
%token		L_FETCH_GLUE
%token		L_FILE
%token		L_FILES
%token		L_FILE_IXFR
%token		L_FIRST
%token		L_FORWARD
%token		L_FORWARDERS
%token		L_ADDITIONAL_FROM_AUTH
%token		L_ADDITIONAL_FROM_CACHE
%token		L_GRANT
%token		L_GROUP
%token		L_HAS_OLD_CLIENTS
%token		L_HEARTBEAT
%token		L_HINT
%token		L_HOSTSTATS
%token		L_IF_NO_ANSWER
%token		L_IF_NO_DOMAIN
%token		L_IGNORE
%token		L_INCLUDE
%token		L_INET
%token		L_INTERFACE_INTERVAL
%token		L_INTERNAL
%token		L_IXFR_TMP
%token		L_KEYS
%token		L_LAME_TTL
%token		L_LBRACE
%token		L_LISTEN_ON
%token		L_LISTEN_ON_V6
%token		L_LOGGING
%token		L_LWRES
%token		L_MAINTAIN_IXFR_BASE
%token		L_MANY_ANSWERS
%token		L_MASTER
%token		L_MASTERS
%token		L_MATCH_CLIENTS
%token		L_MAXIMAL
%token		L_MAX_CACHE_SIZE
%token		L_MAX_CACHE_TTL
%token		L_MAX_LOG_SIZE_IXFR
%token		L_MAX_NCACHE_TTL
%token		L_MAX_REFRESH_TIME
%token		L_MAX_RETRY_TIME
%token		L_MAX_TRANSFER_IDLE_IN
%token		L_MAX_TRANSFER_IDLE_OUT
%token		L_MAX_TRANSFER_TIME_IN
%token		L_MAX_TRANSFER_TIME_OUT
%token		L_MEMSTATS_FILE
%token		L_MINIMAL
%token		L_MIN_REFRESH_TIME
%token		L_MIN_RETRY_TIME
%token		L_MIN_ROOTS
%token		L_MULTIPLE_CNAMES
%token		L_NAME
%token		L_NAMED_XFER
%token		L_NDOTS
%token		L_NO
%token		L_NOTIFY
%token		L_NOTIFY_SOURCE
%token		L_NOTIFY_SOURCE_V6
%token		L_NOTIFY_PASSIVE
%token		L_NULL_OUTPUT
%token		L_ONE_ANSWER
%token		L_ONLY
%token		L_OPTIONS
%token		L_ORDER
%token		L_OWNER
%token		L_RANDOM_DEVICE
%token		L_RANDOM_SEED_FILE
%token		L_PASSIVE
%token		L_PERM
%token		L_PIDFILE
%token		L_PORT
%token		L_PRINT_CATEGORY
%token		L_PRINT_SEVERITY
%token		L_PRINT_TIME
%token		L_PROVIDE_IXFR
%token		L_PUBKEY
%token		L_QUERY_SOURCE
%token		L_QUERY_SOURCE_V6
%token		L_RBRACE
%token		L_RECURSION
%token		L_RECURSIVE_CLIENTS
%token		L_REFRESH
%token		L_REQUEST_IXFR
%token		L_RESPONSE
%token		L_RFC2308_TYPE1
%token		L_RRSET_ORDER
%token		L_SEARCHLIST	/* L_SEARCH is defined by <sys/lkup.h> on AIX */
%token		L_SECRET
%token		L_SEC_KEY
%token		L_SELF
%token		L_SERIAL_QUERIES
%token		L_SERVER
%token		L_SEVERITY
%token		L_SIG_VALIDITY_INTERVAL
%token		L_SIZE
%token		L_SLASH
%token		L_SLAVE
%token		L_SORTLIST
%token		L_STACKSIZE
%token		L_STATS_FILE
%token		L_STATS_INTERVAL
%token		L_STDERR
%token		L_STUB
%token		L_SUBDOMAIN
%token		L_SUPPORT_IXFR
%token		L_SYSLOG
%token		L_TCP_CLIENTS
%token		L_TKEY_DHKEY
%token		L_TKEY_DOMAIN
%token		L_TKEY_GSSAPI_CREDENTIAL
%token		L_TOPOLOGY
%token		L_TRANSFERS
%token		L_TRANSFERS_IN
%token		L_TRANSFERS_OUT
%token		L_TRANSFERS_PER_NS
%token		L_TRANSFER_FORMAT
%token		L_TRANSFER_SOURCE
%token		L_TRANSFER_SOURCE_V6
%token		L_TREAT_CR_AS_SPACE
%token		L_TRUE
%token		L_TRUSTED_KEYS
%token		L_TYPE
%token		L_UNIX
%token		L_UNLIMITED
%token		L_UPDATE_POLICY
%token		L_USE_ID_POOL
%token		L_USE_IXFR
%token		L_VERSION
%token		L_VERSIONS
%token		L_VIEW
%token		L_WARN
%token		L_WILDCARD
%token		L_YES
%token		L_ZONE


%type <addata>		additional_data
%type <boolean>		grantp
%type <boolean>		yea_or_nay
%type <notifytype>	notify_setting
%type <dialuptype>	dialup_setting
%type <forward>		forward_opt
%type <forward>		zone_forward_opt
%type <ime>		address_match_element
%type <ime>		address_match_simple
%type <ime>		address_name
%type <iml>		address_match_list
%type <ipaddress>	in_addr_elem
%type <ipaddress>	ip4_address
%type <ipaddress>	ip4_prefix
%type <ipaddress>	ip6_address
%type <ipaddress>	ip_address
%type <ipaddress>	ip_prefix
%type <ipaddress>	maybe_wild_addr
%type <ipaddress>	maybe_wild_ip4_only_addr
%type <ipaddress>	maybe_wild_ip6_only_addr
%type <ipaddress>	query_source_v4
%type <ipaddress>	query_source_v6
%type <ipaddress>	ip_and_port_element
%type <iplist>		in_addr_list
%type <iplist>		opt_in_addr_list
%type <iplist>		opt_zone_forwarders_list
%type <iplist>		port_ip_list
%type <iplist>		ip_and_port_list
%type <number>		facility_name
%type <number>		maybe_syslog_facility
%type <orderclass>	ordering_class
%type <ordertype>	ordering_type
%type <port_int>	in_port
%type <port_int>	maybe_port
%type <port_int>	maybe_wild_port
%type <port_int>	maybe_zero_port
%type <port_int>	control_port
%type <rdatatype>	rdatatype
%type <rdatatypelist>	rdatatype_list
%type <rrclass>		class_name
%type <rrclass>		wild_class_name
%type <rrclass>		optional_class
%type <severity>	check_names_opt
%type <keydetails>	key_definition
%type <ssu>		grant_stmt
%type <text>		algorithm_id
%type <text>		any_string
%type <text>		category_name
%type <text>		channel_name
%type <text>		domain_name
%type <text>		key_value
%type <text>		maybe_key
%type <kidlist>		control_keys
%type <kidlist>		keyid_list
%type <searchlist>	searchlist
%type <text>		ordering_name
%type <text>		secret
%type <tformat>		transfer_format
%type <transport>	check_names_type;
%type <ul_int>		grant_match_type
%type <ul_int>		size_spec
%type <ztype>		zone_type

/* Miscellaneous items (used in several places): */

%%

config_file: /* nothing */
	| statement_list
	;

statement_list: statement
	| statement_list statement
	;

statement: include_stmt
	| options_stmt L_EOS
	| controls_stmt L_EOS
	| logging_stmt L_EOS
	| server_stmt L_EOS
	| zone_stmt L_EOS
	| trusted_keys_stmt L_EOS
	| acl_stmt L_EOS
	| key_stmt L_EOS
	| view_stmt L_EOS
	| lwres_stmt L_EOS
	| L_END_INCLUDE
	;

/*
 * Note that we must consume the semicolon ending the
 * include statement before switching input streams.
 */
include_stmt: L_INCLUDE L_QSTRING L_EOS
	{
		tmpres = isc_lex_openfile(mylexer, $2);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "can't open file %s: %s",
				     $2, isc_result_totext(tmpres));
			YYABORT;
		}

		isc_mem_free(memctx, $2);
	}
	;

options_stmt: L_OPTIONS
	{
		dns_c_options_t *options;

		if (currcfg->zlist != NULL || currcfg->views != NULL) {
			parser_error(ISC_FALSE,
				     "options must come before all "
				     "zones and views");
			YYABORT;
		}

		tmpres = dns_c_ctx_getoptions(currcfg, &options);
		if (tmpres == ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "cannot redefine options");

			/*
			 * Clean out options so rest of config won't fail
			 * or issue extra error messages
			 */
			dns_c_ctx_optionsdelete(&currcfg->options);
		}

		tmpres = dns_c_ctx_optionsnew(currcfg->mem, &currcfg->options);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to create options structure: %s",
				     isc_result_totext(tmpres));
			YYABORT;
		}

	} L_LBRACE options L_RBRACE {
		if (callbacks != NULL && callbacks->optscbk != NULL) {
			tmpres = callbacks->optscbk(currcfg,
						    callbacks->optscbkuap);
			if (tmpres != ISC_R_SUCCESS) {
				isc_log_write(dns_lctx,
					      DNS_LOGCATEGORY_CONFIG,
					      DNS_LOGMODULE_CONFIG,
					      ISC_LOG_ERROR,
					      "options configuration "
					      "failed: %s",
					      isc_result_totext(tmpres));
				YYABORT;
			}
		}
	}
	;


options_list: option L_EOS
	| options_list option L_EOS
	;

options: /* nothin */
	| options_list
	;


option: /* Empty */
	| L_VERSION L_QSTRING
	{
		tmpres = dns_c_ctx_setversion(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine version");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "set version error %s: %s",
				     isc_result_totext(tmpres), $2);
			YYABORT;
		}

		isc_mem_free(memctx, $2);
	}
	| L_DIRECTORY L_QSTRING
	{
		tmpres = dns_c_ctx_setdirectory(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine directory");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "error setting directory: %s: %s",
				     isc_result_totext(tmpres), $2);
			YYABORT;
		}

		isc_mem_free(memctx, $2);
	}
	| L_NAMED_XFER L_QSTRING
	{
		tmpres = dns_c_ctx_setnamedxfer(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine named-xfer");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "set named-xfer error: %s: %s",
				     isc_result_totext(tmpres), $2);
			YYABORT;
		}

		isc_mem_free(memctx, $2);
	}
	| L_RANDOM_DEVICE L_QSTRING
	{
		tmpres = dns_c_ctx_setrandomdevice(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine random-device");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "error setting random-device: %s: %s",
				     isc_result_totext(tmpres), $2);
			YYABORT;
		}

		isc_mem_free(memctx, $2);
	}
	| L_RANDOM_SEED_FILE L_QSTRING
	{
		tmpres = dns_c_ctx_setrandomseedfile(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine random-seed-file");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "error setting random-seed-file: %s: %s",
				     isc_result_totext(tmpres), $2);
			YYABORT;
		}

		isc_mem_free(memctx, $2);
	}
	| L_TKEY_DOMAIN L_QSTRING
	{
		tmpres = dns_c_ctx_settkeydomain(currcfg, $2);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine tkey-domain");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "set tkey-domain error: %s: %s",
				     isc_result_totext(tmpres), $2);
			YYABORT;
		}

		isc_mem_free(memctx, $2);
	}
	| L_TKEY_DHKEY L_QSTRING L_INTEGER
	{
		tmpres = dns_c_ctx_settkeydhkey(currcfg, $2, $3);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine tkey-dhkey");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "set tkey-dhkey error: %s: %s",
				     isc_result_totext(tmpres), $2);
			YYABORT;
		}

		isc_mem_free(memctx, $2);
	}
	| L_TKEY_GSSAPI_CREDENTIAL L_QSTRING
	{
		tmpres = dns_c_ctx_settkeygsscred(currcfg, $2);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine tkey-gssapi-credential");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "set tkey-gssapi-credential error: %s: %s",
				     isc_result_totext(tmpres), $2);
			YYABORT;
		}

		isc_mem_free(memctx, $2);
	}
	| L_PIDFILE L_QSTRING
	{
		tmpres = dns_c_ctx_setpidfilename(currcfg, $2);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine pid-file");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "set pidfile error %s: %s",
				     isc_result_totext(tmpres), $2);
			YYABORT;
		}

		isc_mem_free(memctx, $2);
	}
	| L_STATS_FILE L_QSTRING
	{
		tmpres = dns_c_ctx_setstatsfilename(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine statistics-file");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "set statsfile error %s: %s",
				     isc_result_totext(tmpres), $2);
			YYABORT;
		}

		isc_mem_free(memctx, $2);
	}
	| L_MEMSTATS_FILE L_QSTRING
	{
		tmpres = dns_c_ctx_setmemstatsfilename(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine memstatistics-file");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "set memstatsfile error %s: %s",
				     isc_result_totext(tmpres), $2);
			YYABORT;
		}

		isc_mem_free(memctx, $2);
	}
	| L_DUMP_FILE L_QSTRING
	{
		tmpres = dns_c_ctx_setdumpfilename(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine dump-file");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "set dumpfile error %s: %s",
				     isc_result_totext(tmpres), $2);
			YYABORT;
		}

		isc_mem_free(memctx, $2);
	}
	| L_CACHE_FILE L_QSTRING
	{
		tmpres = dns_c_ctx_setcachefile(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine cache-file");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "set cachefile error %s: %s",
				     isc_result_totext(tmpres), $2);
			YYABORT;
		}

		isc_mem_free(memctx, $2);
	}
	| L_EXPERT_MODE yea_or_nay
	{
		tmpres = dns_c_ctx_setexpertmode(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine expert-mode");
			YYABORT;
		}
	}
	| L_FAKE_IQUERY yea_or_nay
	{
		tmpres = dns_c_ctx_setfakeiquery(currcfg, ISC_FALSE);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine fake-iquery");
			YYABORT;
		}
	}
	| L_RECURSION yea_or_nay
	{
		tmpres = dns_c_ctx_setrecursion(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine recursion");
			YYABORT;
		}
	}
	| L_FETCH_GLUE yea_or_nay
	{
		tmpres = dns_c_ctx_setfetchglue(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine fetch-glue");
			YYABORT;
		}
	}
	| L_NOTIFY notify_setting
	{
		tmpres = dns_c_ctx_setnotify(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine notify");
			YYABORT;
		}
	}
	| L_HOSTSTATS yea_or_nay
	{
		tmpres = dns_c_ctx_sethoststatistics(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine host-statistics");
			YYABORT;
		}
	}
	| L_DEALLOC_ON_EXIT yea_or_nay
	{
		tmpres = dns_c_ctx_setdealloconexit(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine deallocate-on-exit");
			YYABORT;
		}
	}
	| L_USE_IXFR yea_or_nay
	{
		tmpres = dns_c_ctx_setuseixfr(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine use-ixfr");
			YYABORT;
		}
	}
	| L_MAINTAIN_IXFR_BASE yea_or_nay
	{
		tmpres = dns_c_ctx_setmaintainixfrbase(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine maintain-ixfr-base");
			YYABORT;
		}
	}
	| L_HAS_OLD_CLIENTS yea_or_nay
	{
		tmpres = dns_c_ctx_sethasoldclients(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine has-old-clients");
			YYABORT;
		}
	}
	| L_AUTH_NXDOMAIN yea_or_nay
	{
		tmpres = dns_c_ctx_setauthnxdomain(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine auth-nxdomain");
			YYABORT;
		}
	}
	| L_MULTIPLE_CNAMES yea_or_nay
	{
		tmpres = dns_c_ctx_setmultiplecnames(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine multiple-cnames");
			YYABORT;
		}
	}
	| L_CHECK_NAMES check_names_type check_names_opt
	{
		tmpres = dns_c_ctx_setchecknames(currcfg, $2, $3);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine check-names");
			YYABORT;
		}
	}
	| L_USE_ID_POOL yea_or_nay
	{
		tmpres = dns_c_ctx_setuseidpool(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine use-id-pool");
			YYABORT;
		}
	}
	| L_RFC2308_TYPE1 yea_or_nay
	{
		tmpres = dns_c_ctx_setrfc2308type1(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine rfc2308-type");
			YYABORT;
		}
	}
	| L_PROVIDE_IXFR yea_or_nay
	{
		tmpres = dns_c_ctx_setprovideixfr(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine provide-ixfr");
			YYABORT;
		}
	}
	| L_REQUEST_IXFR yea_or_nay
	{
		tmpres = dns_c_ctx_setrequestixfr(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine request-ixfr");
			YYABORT;
		}
	}
	| L_TREAT_CR_AS_SPACE yea_or_nay
	{
		tmpres = dns_c_ctx_settreatcrasspace(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine treat-cr-as-space");
			YYABORT;
		}
	}
	| L_ADDITIONAL_FROM_CACHE yea_or_nay
	{
		tmpres = dns_c_ctx_setadditionalfromcache(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine additional-from-cache");
			YYABORT;
		}
	}
	| L_ADDITIONAL_FROM_AUTH yea_or_nay
	{
		tmpres = dns_c_ctx_setadditionalfromauth(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine additional-from-auth");
			YYABORT;
		}
	}
	| L_LISTEN_ON maybe_port L_LBRACE address_match_list L_RBRACE
	{
		if ($4 == NULL) {
			parser_warning(ISC_FALSE,
				       "address-match-list empty implies "
				       "listen statement ignored");
		} else {
			tmpres = dns_c_ctx_addlisten_on(currcfg, $2, $4,
							ISC_FALSE);

			if (tmpres != ISC_R_SUCCESS) {
				parser_error(ISC_FALSE,
					     "failed to add listen statement");
				YYABORT;
			}
		}
	}
	| L_LISTEN_ON_V6 maybe_port L_LBRACE address_match_list L_RBRACE
	{
		if ($4 == NULL) {
			parser_warning(ISC_FALSE,
				       "address-match-list empty implies "
				       "listen statement ignored");
		} else {
			tmpres = dns_c_ctx_addv6listen_on(currcfg, $2, $4,
							  ISC_FALSE);

			if (tmpres != ISC_R_SUCCESS) {
				parser_error(ISC_FALSE,
					     "failed to add "
					     "v6listen statement");
				YYABORT;
			}
		}
	}
	| L_FORWARD forward_opt
	{
		tmpres = dns_c_ctx_setforward(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine forward");
			YYABORT;
		}
	}
	| L_FORWARDERS {
		dns_c_iplist_t *forwarders;

		tmpres = dns_c_ctx_getforwarders(currcfg, &forwarders);
		if (tmpres != ISC_R_NOTFOUND) {
			parser_warning(ISC_FALSE,
				       "cannot redefine options forwarders");
			dns_c_iplist_detach(&forwarders);
		}

		tmpres = dns_c_iplist_new(currcfg->mem, 5, &forwarders);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to create forwarders list");
			YYABORT;
		}

		tmpres = dns_c_ctx_setforwarders(currcfg, ISC_FALSE,
						 forwarders);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set forwarders list");
			YYABORT;
		}
	} L_LBRACE opt_forwarders_list L_RBRACE
	| L_QUERY_SOURCE query_source_v4
	{
		tmpres = dns_c_ctx_setquerysource(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine query-source");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set query-source");
			YYABORT;
		}
	}
	| L_QUERY_SOURCE_V6 query_source_v6
	{
		tmpres = dns_c_ctx_setquerysourcev6(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine query-source-v6");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set query-source-v6");
			YYABORT;
		}
	}
	| L_TRANSFER_SOURCE maybe_wild_ip4_only_addr
	{
		tmpres = dns_c_ctx_settransfersource(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine transfer-source");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set transfer-source");
			YYABORT;
		}
	}
	| L_TRANSFER_SOURCE maybe_wild_ip4_only_addr L_PORT maybe_wild_port
	{
		isc_sockaddr_setport(&$2, $4);
		tmpres = dns_c_ctx_settransfersource(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine transfer-source");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set transfer-source");
			YYABORT;
		}
	}
	| L_TRANSFER_SOURCE_V6 maybe_wild_ip6_only_addr
	{
		tmpres = dns_c_ctx_settransfersourcev6(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine transfer-source-v6");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set transfer-source-v6");
			YYABORT;
		}
	}
	| L_TRANSFER_SOURCE_V6 maybe_wild_ip6_only_addr L_PORT maybe_wild_port
	{
		isc_sockaddr_setport(&$2, $4);
		tmpres = dns_c_ctx_settransfersourcev6(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine transfer-source-v6");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set transfer-source-v6");
			YYABORT;
		}
	}
	| L_NOTIFY_SOURCE maybe_wild_ip4_only_addr
	{
		tmpres = dns_c_ctx_setnotifysource(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine notify-source");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set notify-source");
			YYABORT;
		}
	}
	| L_NOTIFY_SOURCE maybe_wild_ip4_only_addr L_PORT maybe_wild_port
	{
		isc_sockaddr_setport(&$2, $4);
		tmpres = dns_c_ctx_setnotifysource(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine notify-source");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set notify-source");
			YYABORT;
		}
	}
	| L_NOTIFY_SOURCE_V6 maybe_wild_ip6_only_addr
	{
		tmpres = dns_c_ctx_setnotifysourcev6(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine notify-source-v6");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set notify-source-v6");
			YYABORT;
		}
	}
	| L_NOTIFY_SOURCE_V6 maybe_wild_ip6_only_addr L_PORT maybe_wild_port
	{
		isc_sockaddr_setport(&$2, $4);
		tmpres = dns_c_ctx_setnotifysourcev6(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine notify-source-v6");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set notify-source-v6");
			YYABORT;
		}
	}
	| L_ALLOW_NOTIFY L_LBRACE address_match_list L_RBRACE
	{
		if ($3 == NULL)
			YYABORT;

		tmpres = dns_c_ctx_setallownotify(currcfg, $3);
		dns_c_ipmatchlist_detach(&$3);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine allow-notify list");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to set allow-notify");
			YYABORT;
		}
	}
	| L_ALLOW_QUERY L_LBRACE address_match_list L_RBRACE
	{
		if ($3 == NULL)
			YYABORT;

		tmpres = dns_c_ctx_setallowquery(currcfg, $3);
		dns_c_ipmatchlist_detach(&$3);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine allow-query list");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to set allow-query");
			YYABORT;
		}
	}
	| L_ALLOW_TRANSFER L_LBRACE address_match_list L_RBRACE
	{
		tmpres = dns_c_ctx_setallowtransfer(currcfg, $3);
		dns_c_ipmatchlist_detach(&$3);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine allow-transfer list");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set allow-transfer");
			YYABORT;
		}
	}
	| L_ALLOW_RECURSION L_LBRACE address_match_list L_RBRACE
	{
		tmpres = dns_c_ctx_setallowrecursion(currcfg, $3);
		dns_c_ipmatchlist_detach(&$3);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine allow-recursion list");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set allow-recursion");
			YYABORT;
		}
	}
	| L_SORTLIST  L_LBRACE address_match_list L_RBRACE
	{
		tmpres = dns_c_ctx_setsortlist(currcfg, $3);
		dns_c_ipmatchlist_detach(&$3);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine sortlist");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to set sortlist");
			YYABORT;
		}
	}
	| L_ALSO_NOTIFY port_ip_list
	{
		replace_zero_ports($2, default_port);
		tmpres = dns_c_ctx_setalsonotify(currcfg, $2);
		dns_c_iplist_detach(&$2);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine also-notify");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to set also-notify");
			YYABORT;
		}
	}
	| L_BLACKHOLE L_LBRACE address_match_list L_RBRACE
	{
		tmpres = dns_c_ctx_setblackhole(currcfg, $3);
		dns_c_ipmatchlist_detach(&$3);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine blackhole");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to set blackhole");
			YYABORT;
		}
	}
	| L_TOPOLOGY L_LBRACE address_match_list L_RBRACE
	{
		tmpres = dns_c_ctx_settopology(currcfg, $3);
		dns_c_ipmatchlist_detach(&$3);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine topology");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to set topology");
			YYABORT;
		}
	}
	| size_clause
	| transfer_clause
	| L_TRANSFER_FORMAT transfer_format
	{
		tmpres = dns_c_ctx_settransferformat(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine transfer-format");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set transfer-format");
			YYABORT;
		}
	}
	| L_MAX_TRANSFER_TIME_IN L_INTEGER
	{
		if (int_too_big($2, 60)) {
			parser_error(ISC_FALSE,
				     "integer value too big: %u", $2);
			YYABORT;
		}

		tmpres = dns_c_ctx_setmaxtransfertimein(currcfg, $2 * 60);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine max-transfer-time-in");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set max-transfer-time-in");
			YYABORT;
		}
	}
	| L_PORT in_port
	{
		tmpres = dns_c_ctx_setport(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine port");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set port");
			YYABORT;
		}
		default_port = $2;
	}
	| L_MAX_TRANSFER_TIME_OUT L_INTEGER
	{
		if (int_too_big($2, 60)) {
			parser_error(ISC_FALSE,
				     "integer value too big: %u", $2);
			YYABORT;
		}

		tmpres = dns_c_ctx_setmaxtransfertimeout(currcfg, $2 * 60);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine max-transfer-time-out");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set max-transfer-time-out");
			YYABORT;
		}
	}
	| L_MAX_TRANSFER_IDLE_IN L_INTEGER
	{
		if (int_too_big($2, 60)) {
			parser_error(ISC_FALSE,
				     "integer value too big: %u", $2);
			YYABORT;
		}

		tmpres = dns_c_ctx_setmaxtransferidlein(currcfg, $2 * 60);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine max-transfer-idle-in");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set max-transfer-idle-in");
			YYABORT;
		}
	}
	| L_MAX_TRANSFER_IDLE_OUT L_INTEGER
	{
		if (int_too_big($2, 60)) {
			parser_error(ISC_FALSE,
				     "integer value too big: %u", $2);
			YYABORT;
		}

		tmpres = dns_c_ctx_setmaxtransferidleout(currcfg, $2 * 60);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine max-transfer-idle-out");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set max-transfer-idle-out");
			YYABORT;
		}
	}
	| L_TCP_CLIENTS L_INTEGER
	{
		tmpres = dns_c_ctx_settcpclients(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine tcp-clients");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set tcp-clients");
			YYABORT;
		}
	}
	| L_SIG_VALIDITY_INTERVAL L_INTEGER
	{
		if (int_too_big($2, 60 * 60 * 24)) {
			parser_error(ISC_FALSE,
				     "integer value too big: %u", $2);
			YYABORT;
		}
		tmpres = dns_c_ctx_setsigvalidityinterval(currcfg,
							  $2 * 60 * 60 * 24);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine sig-validity-interval");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set sig-validity-interval");
			YYABORT;
		}
	}
	| L_LAME_TTL L_INTEGER
	{
		tmpres = dns_c_ctx_setlamettl(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine lame-ttl");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to set lame-ttl");
			YYABORT;
		}
	}
	| L_RECURSIVE_CLIENTS L_INTEGER
	{
		tmpres = dns_c_ctx_setrecursiveclients(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine recursive-clients");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set recursive-clients");
			YYABORT;
		}
	}
	| L_MIN_ROOTS L_INTEGER
	{
		tmpres = dns_c_ctx_setminroots(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine min-roots");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set min-roots");
			YYABORT;
		}
	}
	| L_SERIAL_QUERIES L_INTEGER
	{
		tmpres = dns_c_ctx_setserialqueries(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine serial-queries");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set serial-queries");
			YYABORT;
		}
	}
	| L_CLEAN_INTERVAL L_INTEGER
	{
		if (int_too_big($2, 60)) {
			parser_error(ISC_FALSE,
				     "integer value too big: %u", $2);
			YYABORT;
		}

		tmpres = dns_c_ctx_setcleaninterval(currcfg, $2 * 60);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine cleaning-interval");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set cleaning-interval");
			YYABORT;
		}
	}
	| L_INTERFACE_INTERVAL L_INTEGER
	{
		if (int_too_big($2, 60)) {
			parser_error(ISC_FALSE,
				     "integer value too big: %u", $2);
			YYABORT;
		}

		tmpres = dns_c_ctx_setinterfaceinterval(currcfg, $2 * 60);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine interface-interval");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set interface-interval");
			YYABORT;
		}
	}
	| L_STATS_INTERVAL L_INTEGER
	{
		if (int_too_big($2, 60)) {
			parser_error(ISC_FALSE,
				     "integer value too big: %u", $2);
			YYABORT;
		}

		tmpres = dns_c_ctx_setstatsinterval(currcfg, $2 * 60);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine statistics-interval");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set statistics-interval");
			YYABORT;
		}
	}
	| L_MAX_LOG_SIZE_IXFR L_INTEGER
	{
		tmpres = dns_c_ctx_setmaxlogsizeixfr(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine max-ixfr-log-size");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set max-ixfr-log-size");
			YYABORT;
		}
	}
	| L_MAX_NCACHE_TTL L_INTEGER
	{
		tmpres = dns_c_ctx_setmaxncachettl(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine max-ncache-ttl");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set max-ncache-ttl");
			YYABORT;
		}
	}
	| L_MAX_CACHE_TTL L_INTEGER
	{
		tmpres = dns_c_ctx_setmaxcachettl(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine max-cache-ttl");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set max-cache-ttl");
			YYABORT;
		}
	}
	| L_MAX_RETRY_TIME L_INTEGER
	{
		tmpres = dns_c_ctx_setmaxretrytime(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine max-retry-time");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set max-retry-time");
			YYABORT;
		}
	}
	| L_MIN_RETRY_TIME L_INTEGER
	{
		tmpres = dns_c_ctx_setminretrytime(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine min-retry-time");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set min-retry-time");
			YYABORT;
		}
	}
	| L_MAX_REFRESH_TIME L_INTEGER
	{
		tmpres = dns_c_ctx_setmaxrefreshtime(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine max-refresh-time");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set max-refresh-time");
			YYABORT;
		}
	}
	| L_MIN_REFRESH_TIME L_INTEGER
	{
		tmpres = dns_c_ctx_setminrefreshtime(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine min-refresh-time");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set min-refresh-time");
			YYABORT;
		}
	}
	| L_HEARTBEAT L_INTEGER
	{
		if (int_too_big($2, 60)) {
			parser_error(ISC_FALSE,
				     "integer value too big: %u", $2);
			YYABORT;
		}

		tmpres = dns_c_ctx_setheartbeatinterval(currcfg, $2 * 60);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine heartbeat-interval");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set heartbeat-interval");
			YYABORT;
		}
	}
	| L_ZONE_STATISTICS yea_or_nay
	{
		tmpres = dns_c_ctx_setstatistics(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone-statistics");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone-statistics");
			YYABORT;
		}
	}
	| L_DIALUP dialup_setting
	{
		tmpres = dns_c_ctx_setdialup(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine dialup");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to set dialup");
			YYABORT;
		}
	}
	| L_RRSET_ORDER
	{
		dns_c_rrsolist_t *ordering;

		tmpres = dns_c_ctx_getrrsetorderlist(currcfg, &ordering);
		if (tmpres != ISC_R_NOTFOUND) {
			parser_warning(ISC_FALSE,
				       "cannot redefine rrset-order list");
			dns_c_rrsolist_clear(ordering);
		} else {
			tmpres = dns_c_rrsolist_new(currcfg->mem, &ordering);
			if (tmpres != ISC_R_SUCCESS) {
				parser_error(ISC_FALSE,
					     "failed to create rrset-order "
					     "list");
				YYABORT;
			}
			tmpres = dns_c_ctx_setrrsetorderlist(currcfg,
							     ISC_FALSE,
							     ordering);
			if (tmpres != ISC_R_SUCCESS) {
				parser_error(ISC_FALSE,
					     "failed to set rrset-order");
				YYABORT;
			}
		}
	} L_LBRACE rrset_ordering_list L_RBRACE
	| L_ALLOW_UPDATE_FORWARDING L_LBRACE address_match_list L_RBRACE
	{
		tmpres = dns_c_ctx_setallowupdateforwarding(currcfg, $3);
		dns_c_ipmatchlist_detach(&$3);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine "
				     "allow-update-forwarding");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set allow-update-forwarding");
			YYABORT;
		}
	}
	| L_ADDITIONAL_DATA additional_data
	{
		tmpres = dns_c_ctx_setadditionaldata(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine additional-data");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set additional-data");
			YYABORT;
		}
	}
	;


/*
 * Controls.
 */
controls_stmt: L_CONTROLS
	{
		if (currcfg->controls != NULL) {
			parser_warning(ISC_FALSE, "cannot redefine controls");
			dns_c_ctrllist_delete(&currcfg->controls);
		}

		tmpres = dns_c_ctrllist_new(currcfg->mem,
					    &currcfg->controls);
		if (tmpres != ISC_R_SUCCESS) {
			YYABORT;
		}
	} L_LBRACE controls L_RBRACE
	;

controls: control L_EOS
	| controls control L_EOS
	;

control: /* Empty */
	| L_INET maybe_wild_addr control_port
	  L_ALLOW L_LBRACE address_match_list L_RBRACE control_keys
	{
		dns_c_ctrl_t *control;

		tmpres = dns_c_ctrlinet_new(currcfg->mem, &control,
					    $2, $3, $6, $8, ISC_FALSE);

		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to build inet control structure");
			YYABORT;
		}

		ISC_LIST_APPEND(currcfg->controls->elements, control, next);
	}
	| L_UNIX L_QSTRING L_PERM L_INTEGER L_OWNER L_INTEGER L_GROUP L_INTEGER
	{
		dns_c_ctrl_t *control;

		tmpres = dns_c_ctrlunix_new(currcfg->mem, &control,
					    $2, $4, $6, $8);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to build unix control structure");
			YYABORT;
		}

		ISC_LIST_APPEND(currcfg->controls->elements, control, next);

		isc_mem_free(memctx, $2);
	}
	;


control_keys: /* nothing */
	{
		$$ = NULL;
	}
	| L_KEYS L_LBRACE keyid_list L_RBRACE
	{
		$$ = $3;
	};



control_port: /* nothing */
	{
		$$ = OMAPI_DEFAULT_PORT;
	}
	| L_PORT in_port
	{
		$$ = $2;
	};


rrset_ordering_list: rrset_ordering_element L_EOS
	| rrset_ordering_list rrset_ordering_element L_EOS
	;

ordering_class: /* nothing */
	{
		$$ = dns_rdataclass_any;
	}
	| L_CLASS wild_class_name
	{
		$$ = $2;
	}
	;

ordering_type: /* nothing */
	{
		$$ = dns_rdatatype_any;
	}
	| L_TYPE any_string
	{
		isc_textregion_t reg;
		dns_rdatatype_t ty;

		if (strcmp($2, "*") == 0) {
			ty = dns_rdatatype_any;
		} else {
			reg.base = $2;
			reg.length = strlen($2);

			tmpres = dns_rdatatype_fromtext(&ty, &reg);
			if (tmpres != ISC_R_SUCCESS) {
				parser_warning(ISC_TRUE,
					       "unknown type, assuming '*'");
				ty = dns_rdatatype_any;
			}
		}

		isc_mem_free(memctx, $2);
		$$ = ty;
	}
	;


ordering_name: /* nothing */
	{
		$$ = isc_mem_strdup(memctx, "*");
	}
	| L_NAME domain_name
	{
		if (strcmp($2, ".") == 0 || strcmp($2, "*.") == 0) {
			$$ = isc_mem_strdup(memctx, "*");
			isc_mem_free(memctx, $2);
		} else {
			$$ = $2;
		}
	}


rrset_ordering_element: ordering_class ordering_type ordering_name
	L_ORDER L_STRING
	{
		dns_c_rrso_t *orderelem;
		dns_c_ordering_t o;

		tmpres = dns_c_string2ordering($5, &o);
		if (tmpres != ISC_R_SUCCESS) {
			parser_warning(ISC_FALSE,
				       "unknown ordering type '%s'"
				       " using default", $5);
			o = DNS_DEFAULT_ORDERING;
		}

		tmpres = dns_c_rrso_new(currcfg->mem,
					&orderelem, $1, $2, $3, o);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to create rrset-order element");
			YYABORT;
		}

		ISC_LIST_APPEND(currcfg->options->ordering->elements,
				orderelem, next);

		isc_mem_free(memctx, $5);
		isc_mem_free(memctx, $3);
	}


transfer_format: L_ONE_ANSWER
	{
		$$ = dns_one_answer;
	}
	| L_MANY_ANSWERS
	{
		$$ = dns_many_answers;
	}
	;

maybe_key: /* nothing */
	{
		$$ = NULL;
	}
	| L_SEC_KEY any_string
	{
		$$ = $2;
	};

maybe_wild_addr: ip4_address
	| ip6_address
	| L_STRING
	{
		isc_sockaddr_any(&$$);

		if (strcmp($1, "*") != 0)
			parser_error(ISC_TRUE,
				     "bad IP address, using IPv4 '*'");

		isc_mem_free(memctx, $1);
	}
	;

maybe_wild_ip4_only_addr: ip4_address
	{
		$$ = $1;
	}
	| ip6_address
	{
		parser_error(ISC_FALSE,
			     "cannot have an IPv6 address in this context");
		YYABORT;
	}
	| L_STRING
	{
		isc_sockaddr_any(&$$);

		if (strcmp($1, "*") != 0)
			parser_error(ISC_TRUE,
				     "bad IPv4 address, using '*'");

		isc_mem_free(memctx, $1);
	}
	;

maybe_wild_ip6_only_addr: ip6_address
	{
		$$ = $1;
	}
	| ip4_address
	{
		parser_error(ISC_FALSE,
			     "cannot have an IPv4 address in this context");
		YYABORT;
	}
	| L_STRING
	{
		isc_sockaddr_any6(&$$);

		if (strcmp($1, "*") != 0)
			parser_error(ISC_TRUE,
				     "bad IPv6 address, using '*'");

		isc_mem_free(memctx, $1);
	}
	;

maybe_wild_port: in_port
	{
		$$ = $1;
	}
	| L_STRING
	{
		$$ = 0;

		if (strcmp ($1, "*") != 0) {
			parser_error(ISC_TRUE,
				     "bad port specification using '*'");
		}

		isc_mem_free(memctx, $1);
	}
	;


port_ip_list: maybe_zero_port L_LBRACE ip_and_port_list L_RBRACE
	{
		in_port_t port = $1;
		dns_c_iplist_t *list = $3;

		if (port != 0)
			replace_zero_ports(list, port);

		$$ = list;
	};


ip_and_port_element: ip_address maybe_zero_port
	{
		isc_sockaddr_setport(&$1, $2);
		$$ = $1;
	};


ip_and_port_list: ip_and_port_element maybe_key L_EOS
	{
		dns_c_iplist_t *list;

		tmpres = dns_c_iplist_new(currcfg->mem, 5, &list);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_TRUE,
				     "failed to create new iplist");
			YYABORT;
		}

		tmpres = dns_c_iplist_append(list, $1, $2);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_TRUE,
				     "failed to append master address");
			YYABORT;
		}

		if ($2 != NULL) {
			isc_mem_free(memctx, $2);
		}

		$$ = list;
	}
	| ip_and_port_list ip_and_port_element maybe_key L_EOS
	{
		tmpres = dns_c_iplist_append($1, $2, $3);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_TRUE,
				     "failed to append master address");
			YYABORT;
		}

		if ($3 != NULL) {
			isc_mem_free(memctx, $3);
		}

		$$ = $1;
	}
	;



query_source_v6: L_ADDRESS maybe_wild_ip6_only_addr
	{
		isc_sockaddr_setport(&$2, 0); /* '0' is wild port  */
		$$ = $2;
	}
	| L_ADDRESS maybe_wild_ip6_only_addr L_PORT maybe_wild_port
	{
		isc_sockaddr_setport(&$2, $4);
		$$ = $2;
	}
	| L_PORT maybe_wild_port
	{
		isc_sockaddr_t addr;
		isc_sockaddr_fromin6(&addr, &in6addr_any, $2);
		$$ = addr;
	}
	| L_PORT maybe_wild_port L_ADDRESS maybe_wild_ip6_only_addr
	{
		isc_sockaddr_setport(&$4, $2);
		$$ = $4;
	}
	;

query_source_v4: L_ADDRESS maybe_wild_ip4_only_addr
	{
		isc_sockaddr_setport(&$2, 0); /* '0' is wild port  */
		$$ = $2;
	}
	| L_ADDRESS maybe_wild_ip4_only_addr L_PORT maybe_wild_port
	{
		isc_sockaddr_setport(&$2, $4);
		$$ = $2;
	}
	| L_PORT maybe_wild_port
	{
		struct in_addr any;
		isc_sockaddr_t addr;

		memset(&any, 0x0, sizeof any);
		any.s_addr = htonl(INADDR_ANY);
		isc_sockaddr_fromin(&addr, &any, $2);
		$$ = addr;
	}
	| L_PORT maybe_wild_port L_ADDRESS maybe_wild_ip4_only_addr
	{
		isc_sockaddr_setport(&$4, $2);
		$$ = $4;
	}
	;

maybe_port: /* nothing */
	{
		$$ = default_port;
	}
	| L_PORT in_port
	{
		$$ = $2;
	}
	;

maybe_zero_port : /* nothing */
	{
		$$ = 0;
	}
	| L_PORT in_port
	{
		$$ = $2;
	}
	;

additional_data: L_INTERNAL
	{
		$$ = dns_c_ad_internal;
	}
	| L_MINIMAL
	{
		$$ = dns_c_ad_minimal;
	}
	| L_MAXIMAL
	{
		$$ = dns_c_ad_maximal;
	};

yea_or_nay: L_YES
	{
		$$ = isc_boolean_true;
	}
	| L_TRUE
	{
		$$ = isc_boolean_true;
	}
	| L_NO
	{
		$$ = isc_boolean_false;
	}
	| L_FALSE
	{
		$$ = isc_boolean_false;
	}
	| L_INTEGER
	{
		if ($1 == 1) {
			$$ = isc_boolean_true;
		} else if ($1 == 0) {
			$$ = isc_boolean_false;
		} else {
			parser_warning(ISC_TRUE,
				       "number should be 0 or 1, assuming 1");
			$$ = isc_boolean_true;
		}
	}

notify_setting: yea_or_nay
	{
		if ($1)
			$$ = dns_notifytype_yes;
		else
			$$ = dns_notifytype_no;

	}
	| L_EXPLICIT
	{
		$$ = dns_notifytype_explicit;
	}
	;

dialup_setting: yea_or_nay
	{
		if ($1)
			$$ = dns_dialuptype_yes;
		else
			$$ = dns_dialuptype_no;
	}
	| L_NOTIFY
	{
		$$ = dns_dialuptype_notify;
	}
	| L_NOTIFY_PASSIVE
	{
		$$ = dns_dialuptype_notifypassive;
	}
	| L_REFRESH
	{
		$$ = dns_dialuptype_refresh;
	}
	| L_PASSIVE
	{
		$$ = dns_dialuptype_passive;
	}
	;

check_names_type: L_MASTER
	{
		$$ = dns_trans_primary;
	}
	| L_SLAVE
	{
		$$ = dns_trans_secondary;
	}
	| L_RESPONSE
	{
		$$ = dns_trans_response;
	}
	;

check_names_opt: L_WARN
	{
		$$ = dns_severity_warn;
	}
	| L_FAIL
	{
		$$ = dns_severity_fail;
	}
	| L_IGNORE
	{
		$$ = dns_severity_ignore;
	}
	;

forward_opt: L_ONLY
	{
		$$ = dns_c_forw_only;
	}
	| L_FIRST
	{
		$$ = dns_c_forw_first;
	}
	;



size_clause: L_DATASIZE size_spec
	{
		tmpres = dns_c_ctx_setdatasize(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine datasize");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to set datasize");
			YYABORT;
		}
	}
	| L_STACKSIZE size_spec
	{
		tmpres = dns_c_ctx_setstacksize(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine stacksize");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to set stacksize");
			YYABORT;
		}
	}
	| L_CORESIZE size_spec
	{
		tmpres = dns_c_ctx_setcoresize(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine coresize");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to set coresize");
			YYABORT;
		}
	}
	| L_FILES size_spec
	{
		tmpres = dns_c_ctx_setfiles(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine files");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to set files");
			YYABORT;
		}
	}
	| L_MAX_CACHE_SIZE size_spec
	{
		if ($2 == DNS_C_SIZE_SPEC_DEFAULT) {
			parser_error(ISC_FALSE,
				     "cannot specific 'default' for "
				     "'max-cache-size'");
			YYABORT;
		}

		tmpres = dns_c_ctx_setmaxcachesize(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine max-cache-size");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set max-cache-size");
			YYABORT;
		}
	}
	;


size_spec: any_string
	{
		isc_uint32_t result;

		tmpres = unit_to_uint32($1, &result);
		if (tmpres == ISC_R_SUCCESS) {
			$$ = result;
		} else if (tmpres == ISC_R_RANGE) {
			$$ = DNS_C_SIZE_SPEC_UNLIM;
			parser_warning(ISC_FALSE,
				       "invalid value %s: using 'unlimited'",
				       $1);
		} else if (tmpres == ISC_R_FAILURE) {
			parser_warning(ISC_FALSE,
				       "invalid unit string '%s', Using "
				       "'default'", $1);
			$$ = DNS_C_SIZE_SPEC_DEFAULT;
		} else {
			parser_warning(ISC_FALSE,
				       "unknown result: %s: using 'default'",
				       isc_result_totext(tmpres));
			$$ = DNS_C_SIZE_SPEC_DEFAULT;
		}

		isc_mem_free(memctx, $1);
	}
	| L_INTEGER
	{
		$$ = (isc_uint32_t)$1;
		if ($$ == DNS_C_SIZE_SPEC_DEFAULT) {
			isc_uint32_t newi = DNS_C_SIZE_SPEC_DEFAULT - 1;
			parser_warning(ISC_FALSE,
				       "value (%lu) too big, Reducing to %lu",
				       (unsigned long) $$,
				       (unsigned long) newi);
			$$ = newi;
		}
	}
	| L_DEFAULT
	{
		$$ = DNS_C_SIZE_SPEC_DEFAULT;
	}
	| L_UNLIMITED
	{
		$$ = DNS_C_SIZE_SPEC_UNLIM;
	}
	;



transfer_clause: L_TRANSFERS_IN L_INTEGER
	{
		tmpres = dns_c_ctx_settransfersin(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine transfers-in");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to set transfers-in");
			YYABORT;
		}
	}
	| L_TRANSFERS_OUT L_INTEGER
	{
		tmpres = dns_c_ctx_settransfersout(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine transfers-out");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set transfers-out");
			YYABORT;
		}
	}
	| L_TRANSFERS_PER_NS L_INTEGER
	{
		tmpres = dns_c_ctx_settransfersperns(currcfg, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine transfers-per-ns");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set transfers-per-ns");
			YYABORT;
		}
	}
	;


opt_forwarders_list: /* nothing */ {
	}
	| forwarders_in_addr_list
	;

forwarders_in_addr_list: forwarders_in_addr L_EOS
	| forwarders_in_addr_list forwarders_in_addr L_EOS
	;

forwarders_in_addr: ip_address
	{
		tmpres = dns_c_iplist_append(currcfg->options->forwarders,
					     $1, NULL);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to add forwarders "
				     "address element");
			YYABORT;
		}
	}
	;


/*
 * Logging
 */

logging_stmt: L_LOGGING
	{
		/* initialized in logging_init() */
		INSIST(currcfg->logging != NULL);
	}
	L_LBRACE logging_opts_list L_RBRACE
	;

logging_opts_list: logging_opt L_EOS
	| logging_opts_list logging_opt L_EOS
	;

logging_opt: category_stmt
	| channel_stmt
	;


channel_stmt:
	L_CHANNEL channel_name L_LBRACE L_FILE L_QSTRING {
		dns_c_logchan_t *newc;

		tmpres = dns_c_ctx_addfile_channel(currcfg,
						   $2, &newc);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "redefing channel %s", $2);
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to add new file channel");
			YYABORT;
		}

		INSIST(newc != NULL);

		tmpres = dns_c_logchan_setpath(newc, $5);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to add file channel's path");
			YYABORT;
		}

		isc_mem_free(memctx, $2);
		isc_mem_free(memctx, $5);
	}  maybe_file_modifiers L_EOS optional_channel_opt_list L_RBRACE
	| L_CHANNEL channel_name L_LBRACE L_SYSLOG maybe_syslog_facility {
		dns_c_logchan_t *newc;

		tmpres = dns_c_ctx_addsyslogchannel(currcfg,
						    $2, &newc);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine channel %s", $2);
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to add new syslog channel");
			YYABORT;
		}

		tmpres = dns_c_logchan_setfacility(newc, $5);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get set channel facility");
			YYABORT;
		}
		isc_mem_free(memctx, $2);
	} L_EOS optional_channel_opt_list L_RBRACE
	| L_CHANNEL channel_name L_LBRACE L_NULL_OUTPUT {
		dns_c_logchan_t *newc;

		tmpres = dns_c_ctx_addnullchannel(currcfg,
						  $2, &newc);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine channel %s", $2);
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to add new channel '%s'", $2);
			YYABORT;
		}

		isc_mem_free(memctx, $2);
	} L_EOS optional_channel_opt_list L_RBRACE
	| L_CHANNEL channel_name L_LBRACE L_STDERR {
		dns_c_logchan_t *newc;

		tmpres = dns_c_ctx_addstderrchannel(currcfg,
                                                    $2, &newc);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine channel %s", $2);
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to add new channel '%s'", $2);
			YYABORT;
		}

		isc_mem_free(memctx, $2);
	} L_EOS optional_channel_opt_list L_RBRACE
	| L_CHANNEL channel_name L_LBRACE logging_non_type_keywords {
		parser_error(ISC_FALSE,
			     "first statment inside a channel definition "
			     "must be 'file' or 'syslog' or 'null'");
		YYABORT;
	}
	;


logging_non_type_keywords: L_SEVERITY | L_PRINT_TIME | L_PRINT_CATEGORY |
	L_PRINT_SEVERITY
	;


optional_channel_opt_list: /* empty */
	| channel_opt_list
	;

category_stmt: L_CATEGORY category_name {
		dns_c_logcat_t *cat;

		tmpres = dns_c_ctx_addcategory(currcfg, $2, &cat);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine category '%s'", $2);
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to add new logging category");
			isc_mem_free(memctx, $2);
			YYABORT;
		}
		isc_mem_free(memctx, $2);
	} L_LBRACE channel_list L_RBRACE
	;


channel_severity: any_string
	{
		dns_c_logseverity_t severity;
		dns_c_logchan_t *chan;

		tmpres = dns_c_string2logseverity($1, &severity);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "unknown severity '%s'", $1);
			YYABORT;
		}

		tmpres = dns_c_ctx_currchannel(currcfg, &chan);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get current channel");
			YYABORT;
		}

		tmpres = dns_c_logchan_setseverity(chan, severity);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine severity");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get set channel severity");
			YYABORT;
		}

		isc_mem_free(memctx, $1);
	}
	| L_DEBUG
	{
		dns_c_logchan_t *chan;

		tmpres = dns_c_ctx_currchannel(currcfg, &chan);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get current channel");
			YYABORT;
		}

		tmpres = dns_c_logchan_setseverity(chan,
						   dns_c_log_debug);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine severity");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get set channel severity(debug)");
			YYABORT;
		}
	}
	| L_DEBUG L_INTEGER
	{
		dns_c_logchan_t *chan;

		tmpres = dns_c_ctx_currchannel(currcfg, &chan);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get current channel");
			YYABORT;
		}

		tmpres = dns_c_logchan_setseverity(chan,
						   dns_c_log_debug);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine severity");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get set channel "
				     "severity (debug)");
			YYABORT;
		}

		tmpres = dns_c_logchan_setdebuglevel(chan, $2);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get set channel "
				     "severity debug level");
			YYABORT;
		}
	}
	| L_DYNAMIC
	{
		dns_c_logchan_t *chan;

		tmpres = dns_c_ctx_currchannel(currcfg, &chan);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get current channel");
			YYABORT;
		}

		tmpres = dns_c_logchan_setseverity(chan,
						   dns_c_log_dynamic);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine severity");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get set channel "
				     "severity (dynamic)");
			YYABORT;
		}
	}
	;

version_modifier: L_VERSIONS L_INTEGER
	{
		dns_c_logchan_t *chan;

		tmpres = dns_c_ctx_currchannel(currcfg, &chan);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get current channel");
			YYABORT;
		}

		tmpres = dns_c_logchan_setversions(chan, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine versions");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get set channel versions");
			YYABORT;
		}
	}
	| L_VERSIONS L_UNLIMITED
	{
		dns_c_logchan_t *chan;

		tmpres = dns_c_ctx_currchannel(currcfg, &chan);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get current channel");
			YYABORT;
		}

		tmpres = dns_c_logchan_setversions(chan, 0xffffffffU);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine versions");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get set channel "
				     "versions (unlimited)");
			YYABORT;
		}
	}
	;

size_modifier: L_SIZE size_spec
	{
		dns_c_logchan_t *chan;

		tmpres = dns_c_ctx_currchannel(currcfg, &chan);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get current channel");
			YYABORT;
		}

		tmpres = dns_c_logchan_setsize(chan, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine size");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get set channel size");
			YYABORT;
		}
	}
	;

maybe_file_modifiers: /* nothing */
	| version_modifier
	| size_modifier
	| version_modifier size_modifier
	| size_modifier version_modifier
	;

facility_name: any_string
	{
		tmpres = dns_c_string2facility($1, &$$);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_TRUE, "unknown syslog facility");
			$$ = LOG_DAEMON;
		}

		isc_mem_free(memctx, $1);
	}
	| L_SYSLOG
	{
		$$ = LOG_SYSLOG;
	}
	;

maybe_syslog_facility: /* nothing */
	{
		$$ = LOG_DAEMON;
	}
	| facility_name
	{
		$$ = $1;
	}
	;


channel_opt_list: channel_opt L_EOS
	| channel_opt_list channel_opt L_EOS
	;


channel_opt: L_SEVERITY channel_severity { /* nothing to do */ }
	| L_PRINT_TIME yea_or_nay
	{
		dns_c_logchan_t *chan;

		tmpres = dns_c_ctx_currchannel(currcfg, &chan);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "can't get current channel");
			YYABORT;
		}

		tmpres = dns_c_logchan_setprinttime(chan, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine print-time");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get set channel print-time");
			YYABORT;
		}
	}
	| L_PRINT_CATEGORY yea_or_nay
	{
		dns_c_logchan_t *chan;

		tmpres = dns_c_ctx_currchannel(currcfg, &chan);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "can't get current channel");
			YYABORT;
		}

		tmpres = dns_c_logchan_setprintcat(chan, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine print-category");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get set channel print-category");
			YYABORT;
		}
	}
	| L_PRINT_SEVERITY yea_or_nay
	{
		dns_c_logchan_t *chan;

		tmpres = dns_c_ctx_currchannel(currcfg, &chan);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "can't get current channel");
			YYABORT;
		}

		tmpres = dns_c_logchan_setprintsev(chan, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine print-severity");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't get set channel print-severity");
			YYABORT;
		}
	}
	;


channel_name: any_string
	| L_NULL_OUTPUT
	{
		$$ = isc_mem_strdup(memctx, "null");
	}
	;


channel: channel_name
	{
		dns_c_logcat_t *cat;

		/*
		 * XXX validate the channel name refers to a previously
		 * defined channel
		 */
		tmpres = dns_c_ctx_currcategory(currcfg, &cat);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "can't get current category");
			YYABORT;
		}

		tmpres = dns_c_logcat_addname(cat, $1);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "can't add new name to category");
			YYABORT;
		}

		isc_mem_free(memctx, $1);
	}
	;


channel_list: channel L_EOS
	| channel_list channel L_EOS
	;


category_name: any_string
	{
		$$ = $1;
	}
	| L_DEFAULT
	{
		const char *name = token_to_keyword(L_DEFAULT);

		REQUIRE(name != NULL);

		$$ = isc_mem_strdup(memctx, name);
	}
	| L_NOTIFY
	{
		const char *name = token_to_keyword(L_NOTIFY);

		REQUIRE(name != NULL);

		$$ = isc_mem_strdup(memctx, name);
	}
	;

/*
 * Server Information
 */

server_stmt: L_SERVER ip_address
	{
		isc_netaddr_t netaddr;
		dns_peer_t *peer = NULL;
		dns_peerlist_t *peers = currentpeerlist(currcfg, ISC_TRUE);

		isc_netaddr_fromsockaddr(&netaddr, &$2);

		/*
		 * Check that this IP hasn't already been used.
		 */
		tmpres = dns_peerlist_peerbyaddr(peers, &netaddr, &peer);
		if (tmpres == ISC_R_SUCCESS) {
			dns_peer_detach(&peer);
			parser_error(ISC_TRUE, "cannot redefine peer");
			YYABORT;
		}

		tmpres = dns_peer_new(currcfg->mem, &netaddr, &peer);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to create peer structure");
			YYABORT;
		}

		dns_peerlist_addpeer(peers, peer);
		dns_peerlist_detach(&peers);
		dns_peer_detach(&peer);
	}
	L_LBRACE server_info_list L_RBRACE
	;

server_info_list: server_info L_EOS
	| server_info_list server_info L_EOS
	;

server_info: L_BOGUS yea_or_nay
	{
		dns_peer_t *peer = NULL;
		dns_peerlist_t *peerlist = currentpeerlist(currcfg, ISC_FALSE);

		REQUIRE(peerlist != NULL);

		dns_peerlist_currpeer(peerlist, &peer);

		INSIST(peer != NULL);

		tmpres = dns_peer_setbogus(peer, $2);
		dns_peer_detach(&peer);
		dns_peerlist_detach(&peerlist);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine server bogus value");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "error setting server bogus value");
			YYABORT;
		}

	}
	| L_SUPPORT_IXFR yea_or_nay
	{
		/*
		 * Backwards compatibility, equivalent to request-ixfr.
		 */
		dns_peer_t *peer = NULL;
		dns_peerlist_t *peerlist = currentpeerlist(currcfg, ISC_FALSE);

		REQUIRE(peerlist != NULL);

		dns_peerlist_currpeer(peerlist, &peer);

		INSIST(peer != NULL);

		tmpres = dns_peer_setrequestixfr(peer, $2);
		dns_peer_detach(&peer);
		dns_peerlist_detach(&peerlist);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine peer "
				     "request-ixfr value");
			YYABORT;
		} else if(tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "error setting peer "
				     "request-ixfr value");
			YYABORT;
		}
	}
	| L_PROVIDE_IXFR yea_or_nay
	{
		dns_peer_t *peer = NULL;
		dns_peerlist_t *peerlist = currentpeerlist(currcfg, ISC_FALSE);

		REQUIRE(peerlist != NULL);

		dns_peerlist_currpeer(peerlist, &peer);

		INSIST(peer != NULL);

		tmpres = dns_peer_setprovideixfr(peer, $2);
		dns_peer_detach(&peer);
		dns_peerlist_detach(&peerlist);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine peer "
				     "provide-ixfr value");
			YYABORT;
		} else if(tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "error setting peer "
				     "provide-ixfr value");
			YYABORT;
		}
	}
	| L_REQUEST_IXFR yea_or_nay
	{
		dns_peer_t *peer = NULL;
		dns_peerlist_t *peerlist = currentpeerlist(currcfg, ISC_FALSE);

		REQUIRE(peerlist != NULL);

		dns_peerlist_currpeer(peerlist, &peer);

		INSIST(peer != NULL);

		tmpres = dns_peer_setrequestixfr(peer, $2);
		dns_peer_detach(&peer);
		dns_peerlist_detach(&peerlist);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine peer "
				     "request-ixfr value");
			YYABORT;
		} else if(tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "error setting peer "
				     "request-ixfr value");
			YYABORT;
		}
	}
	| L_TRANSFERS L_INTEGER
	{
		dns_peer_t *peer = NULL;
		dns_peerlist_t *peerlist = currentpeerlist(currcfg, ISC_FALSE);

		REQUIRE(peerlist != NULL);

		dns_peerlist_currpeer(peerlist, &peer);

		INSIST(peer != NULL);

		tmpres = dns_peer_settransfers(peer, $2);
		dns_peer_detach(&peer);
		dns_peerlist_detach(&peerlist);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine peer transfers value");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "error setting peer transfers value");
			YYABORT;
		}
	}
	| L_TRANSFER_FORMAT transfer_format
	{
		dns_peer_t *peer = NULL;
		dns_peerlist_t *peerlist = currentpeerlist(currcfg, ISC_FALSE);

		REQUIRE(peerlist != NULL);

		dns_peerlist_currpeer(peerlist, &peer);

		INSIST(peer != NULL);

		tmpres = dns_peer_settransferformat(peer, $2);
		dns_peer_detach(&peer);
		dns_peerlist_detach(&peerlist);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine peer transfer-format "
				     "value");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "error setting peer transfer-format "
				     "value");
			YYABORT;
		}
	}
	| L_KEYS key_value {
		dns_name_t *name = NULL;
		dns_peer_t *peer = NULL;
		dns_peerlist_t *peerlist = currentpeerlist(currcfg, ISC_FALSE);

		REQUIRE(peerlist != NULL);

		dns_peerlist_currpeer(peerlist, &peer);

		INSIST(peer != NULL);

		if (!keydefinedinscope(currcfg, $2)) {
			parser_error(ISC_FALSE,
				     "undefined key '%s' referenced", $2);
			YYABORT;
		}

		tmpres = dns_c_charptoname(peer->mem, $2, &name);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "error creating key name value");
			YYABORT;
		}

		tmpres = dns_peer_setkey(peer, &name);
		isc_mem_free(memctx, $2);
		dns_peer_detach(&peer);
		dns_peerlist_detach(&peerlist);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine peer key value");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "error setting peer key value");
			YYABORT;
		}
	}
	;


key_value: L_LBRACE any_string maybe_eos L_RBRACE
	{
		$$ = $2;
	}
	| any_string
	{
		$$ = $1;
	};


keyid_list: /* nothing */
	{
		dns_c_kidlist_t *kidlist = NULL;

		tmpres = dns_c_kidlist_new(currcfg->mem, &kidlist);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to create kidlist");
			YYABORT;
		}

		$$ = kidlist;
	}
	| keyid_list any_string L_EOS
	{
		dns_c_kid_t *kid = NULL;

		tmpres = dns_c_kid_new($$->mem, $2, &kid);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to create key id");
			dns_c_kidlist_delete(&$$);
			$$ = NULL;
			YYABORT;
		}

		isc_mem_free(memctx, $2);

		dns_c_kidlist_append($$, kid);
	};


searchlist: /* nothing */
	{
		dns_c_searchlist_t *searchlist = NULL;

		tmpres = dns_c_searchlist_new(currcfg->mem, &searchlist);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to create searchlist");
			YYABORT;
		}

		$$ = searchlist;
	}
	| searchlist any_string L_EOS
	{
		dns_c_search_t *search = NULL;

		tmpres = dns_c_search_new($$->mem, $2, &search);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to create searchlist item");
			dns_c_searchlist_delete(&$$);
			$$ = NULL;
			YYABORT;
		}

		isc_mem_free(memctx, $2);

		dns_c_searchlist_append($$, search);
	};


/*
 * Address Matching
 */

address_match_list: address_match_element L_EOS
	{
		dns_c_ipmatchlist_t *ml = 0;

		if ($1 != NULL) {
			tmpres = dns_c_ipmatchlist_new(currcfg->mem, &ml);
			if (tmpres != ISC_R_SUCCESS) {
				parser_error(ISC_FALSE, "insufficient memory");
				dns_c_ipmatchelement_delete(currcfg->mem,
							    &$1);
				YYABORT;
			}

			ISC_LIST_APPEND(ml->elements, $1, next);
		}

		$$ = ml;
	}
	| address_match_list address_match_element L_EOS
	{
		dns_c_ipmatchlist_t *ml = $1;

		if (ml == NULL && $2 != NULL) {
			tmpres = dns_c_ipmatchlist_new(currcfg->mem, &ml);
			if (tmpres != ISC_R_SUCCESS) {
				parser_error(ISC_FALSE, "insufficient memory");
				dns_c_ipmatchelement_delete(currcfg->mem,
							    &$2);
				YYABORT;
			}
		}

		if ($2 != NULL) {
			ISC_LIST_APPEND(ml->elements, $2, next);
		}

		$$ = ml;
	}
	;

address_match_element: address_match_simple
	| L_BANG address_match_simple
	{
		if ($2 != NULL) {
			dns_c_ipmatch_negate($2);
		}
		$$ = $2;
	}
	| L_SEC_KEY any_string
	{
		dns_c_ipmatchelement_t *ime = NULL;

		if (!keydefinedinscope(currcfg, $2)) {
			parser_error(ISC_FALSE,
				     "address match key element (%s) "
				     "referenced before defined", $2);
			YYABORT;
		} else {
			tmpres = dns_c_ipmatchkey_new(currcfg->mem, &ime, $2);
			if (tmpres != ISC_R_SUCCESS) {
				parser_error(ISC_TRUE,
					     "failed to create address match "
					     "key element for %s", $2);
				YYABORT;
			}
		}

		isc_mem_free(memctx, $2);
		$$ = ime;
	}
	;

address_match_simple: ip_address
	{
		dns_c_ipmatchelement_t *ime = NULL;
		unsigned int prefixlen = 0;

		switch ($1.type.sa.sa_family) {
		case AF_INET:
			prefixlen = 32;
			break;
		case AF_INET6:
			prefixlen = 128;
			break;
		default:
			INSIST(0);
			break;
		}
		tmpres = dns_c_ipmatchpattern_new(currcfg->mem, &ime, $1,
						  prefixlen);
		switch (tmpres) {
		case ISC_R_FAILURE:
			parser_error(ISC_FALSE, "bad address match element");
			YYABORT;
			break;

		case ISC_R_NOMEMORY:
			parser_error(ISC_FALSE,
				     "insufficient memory available");
			YYABORT;
			break;

		case ISC_R_SUCCESS:
			break;
		}

		$$ = ime;
	}
	| ip_prefix L_SLASH L_INTEGER
	{
		dns_c_ipmatchelement_t *ime = NULL;

		if ($3 < 0 ||
		    ($1.type.sa.sa_family == AF_INET && $3 > 32) ||
		    ($1.type.sa.sa_family == AF_INET6 && $3 > 128)) {
			parser_error(ISC_FALSE,
				     "mask bits (%d) out of range", (int)$3);
			YYABORT;
		} else {
			tmpres = dns_c_ipmatchpattern_new(currcfg->mem, &ime,
							  $1, $3);
			switch (tmpres) {
			case ISC_R_FAILURE:
				parser_error(ISC_FALSE,
					     "bad address match element");
				YYABORT;
				break;

			case ISC_R_NOMEMORY:
				parser_error(ISC_FALSE,
					     "insufficient memory available");
				YYABORT;
				break;

			case ISC_R_SUCCESS:
				break;
			}
		}

		$$ = ime;
	}
	| L_INTEGER L_SLASH L_INTEGER
	{
		struct in_addr ia;
		dns_c_ipmatchelement_t *ime = NULL;
		isc_sockaddr_t address;

		if ($1 > 255) {
			parser_error(ISC_FALSE,
				     "address out of range; skipping");
			YYABORT;
		} else {
			if ($3 < 0 || $3 > 32) {
				parser_error(ISC_FALSE,
					     "mask bits (%d) out of range",
					     (int) $3);
				YYABORT;
			} else {
				ia.s_addr = htonl(($1 & 0xff) << 24);
				isc_sockaddr_fromin(&address, &ia, 0);

				tmpres =
					dns_c_ipmatchpattern_new(currcfg->mem,
								 &ime,
								 address,
								 $3);
				switch (tmpres) {
				case ISC_R_FAILURE:
					parser_error(ISC_FALSE,
						     "bad address match "
						     "element");
					YYABORT;
					break;

				case ISC_R_NOMEMORY:
					parser_error(ISC_FALSE,
						     "insufficient memory "
						     "available");
					YYABORT;
					break;

				case ISC_R_SUCCESS:
					break;
				}
			}
		}

		$$ = ime;
	}
	| address_name
	| L_LBRACE address_match_list L_RBRACE
	{
		dns_c_ipmatchelement_t *ime = NULL;

		if ($2 != NULL) {
			tmpres = dns_c_ipmatchindirect_new(currcfg->mem, &ime,
							   $2, NULL);
			if (tmpres != ISC_R_SUCCESS) {
				parser_error(ISC_FALSE,
					     "failed to create indirect "
					     "ipmatch list");
				YYABORT;
			}
		}

		dns_c_ipmatchlist_detach(&$2);

		$$ = ime;
	}
	;

address_name: any_string
	{
		dns_c_ipmatchelement_t *elem;
		dns_c_acl_t *acl;

		if (strcmp($1, "any") == 0) {
			tmpres = dns_c_ipmatchany_new(currcfg->mem, &elem);
			if (tmpres != ISC_R_SUCCESS) {
				parser_error(ISC_FALSE,
					     "failed to create 'any'"
					     " ipmatch element");
				YYABORT;
			}
		} else if (strcmp($1, "none") == 0) {
			tmpres = dns_c_ipmatchany_new(currcfg->mem, &elem);
			if (tmpres != ISC_R_SUCCESS) {
				parser_error(ISC_FALSE,
					     "failed to create 'none'"
					     " ipmatch element");
				YYABORT;
			}
			dns_c_ipmatch_negate(elem);
		} else if (strcmp($1, "localhost") == 0) {
			tmpres = dns_c_ipmatchlocalhost_new(currcfg->mem,
							    &elem);
			if (tmpres != ISC_R_SUCCESS) {
				parser_error(ISC_FALSE,
					     "failed to create 'localhost'"
					     " ipmatch element");
				YYABORT;
			}
		} else if (strcmp($1, "localnets") == 0) {
			tmpres = dns_c_ipmatchlocalnets_new(currcfg->mem,
							    &elem);
			if (tmpres != ISC_R_SUCCESS) {
				parser_error(ISC_FALSE,
					     "failed to create 'localnets'"
					     " ipmatch element");
				YYABORT;
			}
		} else {
			tmpres = dns_c_acltable_getacl(currcfg->acls,
						       $1, &acl);
			if (tmpres == ISC_R_NOTFOUND) {
				parser_error(ISC_FALSE,
					     "undefined acl '%s' "
					     "referenced", $1);
				YYABORT;
			} else {
				tmpres = dns_c_ipmatch_aclnew(currcfg->mem,
							      &elem, $1);
				if (tmpres != ISC_R_SUCCESS) {
					parser_error(ISC_FALSE,
						     "failed to create "
						     "IPE-ACL");
					YYABORT;
				}
			}
		}

		isc_mem_free(memctx, $1);
		$$ = elem;
	}
	;

/*
 * Keys
 */


key_stmt: L_SEC_KEY any_string L_LBRACE key_definition L_RBRACE
	{
		dns_c_kdef_t *keydef;
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);
		dns_c_kdeflist_t *list = NULL;

		if (view == NULL) {
			tmpres = dns_c_ctx_getkdeflist(currcfg, &list);
		} else {
			tmpres = dns_c_view_getkeydefs(view, &list);
		}

		if (tmpres == ISC_R_NOTFOUND) {
			tmpres = dns_c_kdeflist_new(currcfg->mem,
						    &list);
			if (tmpres != ISC_R_SUCCESS) {
				parser_error(ISC_FALSE,
					     "failed to create keylist");
				isc_mem_free(memctx, $2);
				isc_mem_free(memctx, $4.algorithm);
				isc_mem_free(memctx, $4.secret);

				YYABORT;
			}

			if (view == NULL) {
				tmpres = dns_c_ctx_setkdeflist(currcfg,
							       list,
							       ISC_FALSE);
			} else {
				tmpres = dns_c_view_setkeydefs(view, list);
			}

			if (tmpres != ISC_R_SUCCESS) {
				parser_error(ISC_FALSE,
					     "failed to set keylist");
				dns_c_kdeflist_delete(&list);
				isc_mem_free(memctx, $2);
				isc_mem_free(memctx, $4.algorithm);
				isc_mem_free(memctx, $4.secret);

				YYABORT;
			}
		}

		tmpres = dns_c_kdef_new(currcfg->mem, $2, &keydef);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to create key");
			isc_mem_free(memctx, $2);
			isc_mem_free(memctx, $4.algorithm);
			isc_mem_free(memctx, $4.secret);
			YYABORT;
		}

		tmpres = dns_c_kdef_setalgorithm(keydef, $4.algorithm);
		if (tmpres == ISC_R_SUCCESS) {
			tmpres = dns_c_kdef_setsecret(keydef, $4.secret);
		}

		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set key details");
			isc_mem_free(memctx, $2);
			isc_mem_free(memctx, $4.algorithm);
			isc_mem_free(memctx, $4.secret);
		}

		dns_c_kdeflist_append(list, keydef, ISC_FALSE);

		isc_mem_free(memctx, $2);
		isc_mem_free(memctx, $4.algorithm);
		isc_mem_free(memctx, $4.secret);
	}
	;

key_definition: algorithm_id secret
	{
		$$.algorithm = $1;
		$$.secret = $2;
	}
	| secret algorithm_id
	{
		$$.algorithm = $2;
		$$.secret = $1;
	}
	;

algorithm_id: L_ALGID any_string L_EOS
	{
		$$ = $2;
	}
	;

secret: L_SECRET any_string L_EOS
	{
		$$ = $2;
	}
	;


/*
 * Views
 */


view_stmt: L_VIEW any_string optional_class L_LBRACE
	{
		dns_c_view_t *view;
		dns_rdataclass_t rdclass;

		if (currcfg->views == NULL) {
			tmpres = dns_c_viewtable_new(currcfg->mem,
						     &currcfg->views);
			if (tmpres != ISC_R_SUCCESS) {
				isc_mem_free(memctx, $2);
				parser_error(ISC_FALSE,
					     "failed to create viewtable");
				YYABORT;
			}
		}
		rdclass = ($3 == dns_rdataclass_reserved0) ?
			  dns_rdataclass_in : $3;

		tmpres = dns_c_view_new(currcfg->mem, $2, rdclass, &view);
		if (tmpres != ISC_R_SUCCESS) {
			isc_mem_free(memctx, $2);
			parser_error(ISC_FALSE,
				     "failed to create view %s", $2);
			YYABORT;
		}

		tmpres = dns_c_viewtable_addview(currcfg->views, view);
		if (tmpres != ISC_R_SUCCESS) {
			dns_c_view_delete(&view);
			parser_error(ISC_FALSE,
				     "view '%s' already exists", $2);
			isc_mem_free(memctx, $2);
			YYABORT;
		}
		dns_c_ctx_setcurrview(currcfg, view);

		isc_mem_free(memctx, $2);
	} optional_view_options_list L_RBRACE {
		dns_c_ctx_setcurrview(currcfg, NULL);
	};

optional_view_options_list:
	| view_options_list
	;

view_options_list: view_option L_EOS
	| view_options_list view_option L_EOS;


view_option: L_FORWARD zone_forward_opt
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setforward(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view forward");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view forward");
			YYABORT;
		}
	}
	| L_FORWARDERS L_LBRACE opt_in_addr_list L_RBRACE
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setforwarders(view,
						  $3, ISC_FALSE);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view forwarders");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view forwarders");
			YYABORT;
		}
		dns_c_iplist_detach(&$3);
	}
	| L_ALLOW_NOTIFY L_LBRACE address_match_list L_RBRACE
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setallownotify(view, $3);
		dns_c_ipmatchlist_detach(&$3);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view allow-notify");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view allow-notify");
			YYABORT;
		}
	}
	| L_ALLOW_QUERY L_LBRACE address_match_list L_RBRACE
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setallowquery(view, $3);
		dns_c_ipmatchlist_detach(&$3);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view allow-query");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view allow-query");
			YYABORT;
		}
	}
	| L_ALLOW_UPDATE_FORWARDING L_LBRACE address_match_list L_RBRACE
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setallowupdateforwarding(view, $3);
		dns_c_ipmatchlist_detach(&$3);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view "
				     "allow-update-forwarding");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view "
				     "allow-update-forwarding");
			YYABORT;
		}
	}
	| L_ALLOW_TRANSFER L_LBRACE address_match_list L_RBRACE
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_settransferacl(view, $3);
		dns_c_ipmatchlist_detach(&$3);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view allow-transfer");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view allow-transfer");
			YYABORT;
		}
	}
	| L_ALLOW_RECURSION L_LBRACE address_match_list L_RBRACE
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setrecursionacl(view, $3);
		dns_c_ipmatchlist_detach(&$3);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view allow-recursion");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view allow-recursion");
			YYABORT;
		}
	}
	| L_SORTLIST L_LBRACE address_match_list L_RBRACE
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setsortlist(view, $3);
		dns_c_ipmatchlist_detach(&$3);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view sortlist");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view sortlist");
			YYABORT;
		}
	}
	| L_TOPOLOGY L_LBRACE address_match_list L_RBRACE
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_settopology(view, $3);
		dns_c_ipmatchlist_detach(&$3);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view topology");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view topology");
			YYABORT;
		}
	}
	| L_MATCH_CLIENTS L_LBRACE address_match_list L_RBRACE
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setmatchclients(view, $3);
		dns_c_ipmatchlist_detach(&$3);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view match-clients");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view match-clients");
			YYABORT;
		}
	}
	| L_CHECK_NAMES check_names_type check_names_opt
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setchecknames(view, $2, $3);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view check-names");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view check-names");
			YYABORT;
		}
	}
	| L_AUTH_NXDOMAIN yea_or_nay
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setauthnxdomain(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view auth-nxdomain");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view auth-nxdomain");
			YYABORT;
		}
	}
	| L_RECURSION yea_or_nay
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setrecursion(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view recursion");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view recursion");
			YYABORT;
		}
	}
	| L_PROVIDE_IXFR yea_or_nay
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setprovideixfr(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view provide-ixfr");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view provide-ixfr");
			YYABORT;
		}
	}
	| L_REQUEST_IXFR yea_or_nay
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setrequestixfr(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view request-ixfr");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view request-ixfr");
			YYABORT;
		}
	}
	| L_FETCH_GLUE yea_or_nay
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setfetchglue(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view fetch-glue");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view fetch-glue");
			YYABORT;
		}
	}
	| L_NOTIFY notify_setting
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setnotify(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view notify");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view notify");
			YYABORT;
		}
	}
	| L_RFC2308_TYPE1 yea_or_nay
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setrfc2308type1(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view rfc2308-type1");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view rfc2308-type1");
			YYABORT;
		}
	}
	| L_ADDITIONAL_FROM_CACHE yea_or_nay
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setadditionalfromcache(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view additional-from-cache");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view additional-from-cache");
			YYABORT;
		}
	}
	| L_ADDITIONAL_FROM_AUTH yea_or_nay
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setadditionalfromauth(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view additional-from-auth");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view additional-from-auth");
			YYABORT;
		}
	}
	| L_QUERY_SOURCE query_source_v4
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setquerysource(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view query-source");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view query-source");
			YYABORT;
		}
	}
	| L_QUERY_SOURCE_V6 query_source_v6
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setquerysourcev6(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view query-source-v6");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view query-source-v6");
			YYABORT;
		}
	}
	| L_TRANSFER_SOURCE maybe_wild_ip4_only_addr
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_settransfersource(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view transfer-source");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view transfer-source");
			YYABORT;
		}
	}
	| L_TRANSFER_SOURCE maybe_wild_ip4_only_addr L_PORT maybe_wild_port
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		isc_sockaddr_setport(&$2, $4);
		tmpres = dns_c_view_settransfersource(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view transfer-source");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view transfer-source");
			YYABORT;
		}
	}
	| L_TRANSFER_SOURCE_V6 maybe_wild_ip6_only_addr
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_settransfersourcev6(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view "
				     "transfer-source-v6");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view transfer-source-v6");
			YYABORT;
		}
	}
	| L_TRANSFER_SOURCE_V6 maybe_wild_ip6_only_addr L_PORT maybe_wild_port
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		isc_sockaddr_setport(&$2, $4);
		tmpres = dns_c_view_settransfersourcev6(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view "
				     "transfer-source-v6");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view transfer-source-v6");
			YYABORT;
		}
	}
	| L_NOTIFY_SOURCE maybe_wild_ip4_only_addr
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setnotifysource(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view notify-source");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view notify-source");
			YYABORT;
		}
	}
	| L_NOTIFY_SOURCE maybe_wild_ip4_only_addr L_PORT maybe_wild_port
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		isc_sockaddr_setport(&$2, $4);
		tmpres = dns_c_view_setnotifysource(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view notify-source");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view notify-source");
			YYABORT;
		}
	}
	| L_NOTIFY_SOURCE_V6 maybe_wild_ip6_only_addr
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setnotifysourcev6(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view "
				     "notify-source-v6");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view notify-source-v6");
			YYABORT;
		}
	}
	| L_NOTIFY_SOURCE_V6 maybe_wild_ip6_only_addr L_PORT maybe_wild_port
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		isc_sockaddr_setport(&$2, $4);
		tmpres = dns_c_view_setnotifysourcev6(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view "
				     "notify-source-v6");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view notify-source-v6");
			YYABORT;
		}
	}
	| L_MAX_TRANSFER_TIME_OUT L_INTEGER
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		if (int_too_big($2, 60)) {
			parser_error(ISC_FALSE,
				     "integer value too big: %u", $2);
			YYABORT;
		}

		tmpres = dns_c_view_setmaxtransfertimeout(view, $2 * 60);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view "
				     "max-transfer-time-out");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view "
				     "max-transfer-time-out");
			YYABORT;
		}
	}
	| L_MAX_TRANSFER_IDLE_OUT L_INTEGER
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		if (int_too_big($2, 60)) {
			parser_error(ISC_FALSE,
				     "integer value too big: %u", $2);
			YYABORT;
		}

		tmpres = dns_c_view_setmaxtransferidleout(view, $2 * 60);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view "
				     "max-transfer-idle-out");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view "
				     "max-transfer-idle-out");
			YYABORT;
		}
	}
	| L_CLEAN_INTERVAL L_INTEGER
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		if (int_too_big($2, 60)) {
			parser_error(ISC_FALSE,
				     "integer value too big: %u", $2);
			YYABORT;
		}

		tmpres = dns_c_view_setcleaninterval(view, $2 * 60);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view "
				     "cleaning-interval");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view cleaning-interval");
			YYABORT;
		}
	}
	| L_MIN_ROOTS L_INTEGER
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setminroots(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view min-roots");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view min-roots");
			YYABORT;
		}
	}
	| L_LAME_TTL L_INTEGER
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setlamettl(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view lame-ttl");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view lame-ttl");
			YYABORT;
		}
	}
	| L_MAX_NCACHE_TTL L_INTEGER
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setmaxncachettl(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view max-ncache-ttl");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view max-ncache-ttl");
			YYABORT;
		}
	}
	| L_MAX_CACHE_TTL L_INTEGER
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setmaxcachettl(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view max-cache-ttl");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view max-cache-ttl");
			YYABORT;
		}
	}
	| L_MAX_RETRY_TIME L_INTEGER
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setmaxretrytime(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view max-retry-time");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view max-retry-time");
			YYABORT;
		}
	}
	| L_MIN_RETRY_TIME L_INTEGER
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setminretrytime(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view min-retry-time");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view min-retry-time");
			YYABORT;
		}
	}
	| L_MAX_REFRESH_TIME L_INTEGER
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setmaxrefreshtime(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view max-refresh-time");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view max-refresh-time");
			YYABORT;
		}
	}
	| L_MIN_REFRESH_TIME L_INTEGER
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setminrefreshtime(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view min-refresh-time");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view min-refresh-time");
			YYABORT;
		}
	}
	| L_SIG_VALIDITY_INTERVAL L_INTEGER
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		if (int_too_big($2, 60 * 60 * 24)) {
			parser_error(ISC_FALSE,
				     "integer value too big: %u", $2);
			YYABORT;
		}
		tmpres = dns_c_view_setsigvalidityinterval(view,
							   $2 * 60 * 60 * 24);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view "
				     "sig-validity-interval");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view "
				     "sig-validity-interval");
			YYABORT;
		}
	}
	| L_ADDITIONAL_DATA additional_data
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setadditionaldata(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view additional-data");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view additional-data");
			YYABORT;
		}
	}
	| L_TRANSFER_FORMAT transfer_format
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_settransferformat(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view transfer-format");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view transfer-format");
			YYABORT;
		}
	}
	| L_ALSO_NOTIFY port_ip_list
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		replace_zero_ports($2, default_port);

		INSIST(view != NULL);

		tmpres = dns_c_view_setalsonotify(view, $2);
		dns_c_iplist_detach(&$2);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view also-notify");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view also-notify");
			YYABORT;
		}
	}
	| L_MAX_CACHE_SIZE size_spec
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		if ($2 == DNS_C_SIZE_SPEC_DEFAULT) {
			parser_error(ISC_FALSE,
				     "cannot specific 'default' for "
				     "'max-cache-size'");
			YYABORT;
		}

		tmpres = dns_c_view_setmaxcachesize(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine view max-cache-size");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set view max-cache-size");
			YYABORT;
		}
	}
	| L_CACHE_FILE L_QSTRING
	{
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		INSIST(view != NULL);

		tmpres = dns_c_view_setcachefile(view, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine cache-file");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "set cachefile error %s: %s",
				     isc_result_totext(tmpres), $2);
			YYABORT;
		}

		isc_mem_free(memctx, $2);
	}
	| key_stmt
	| zone_stmt
	| server_stmt
	| trusted_keys_stmt
	;


zone_update_policy: L_UPDATE_POLICY L_LBRACE {

	} zone_grant_stmt_list L_RBRACE;

zone_grant_stmt_list: /* nothing */ | zone_grant_stmt_list zone_ssu_stmt L_EOS;

zone_ssu_stmt: grant_stmt {
		dns_ssutable_t *ssutable = NULL;
		isc_boolean_t ok = ISC_TRUE;
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		REQUIRE(zone != NULL);

		switch(zone->ztype) {
		case dns_c_zone_hint:
			parser_error(ISC_FALSE,
				     "hint zones do not have grant/deny "
				     "statements");
			ok = ISC_FALSE;
			break;

		case dns_c_zone_forward:
			parser_error(ISC_FALSE,
				     "forward zones do not have grant/deny "
				     "statements");
			ok = ISC_FALSE;
			break;

		default:
			/* nothing */
			break;
		}

		if (ok) {
			tmpres = dns_c_zone_getssuauth(zone, &ssutable);
			if (tmpres == ISC_R_NOTFOUND) {
				REQUIRE(ssutable == NULL);

				tmpres = dns_ssutable_create(currcfg->mem,
						     &ssutable);
				if (tmpres != ISC_R_SUCCESS) {
					parser_error(ISC_FALSE,
						     "failed to create "
						     "ssutable");
					ok = ISC_FALSE;
				}
			}

			if (ok) {
				dns_c_zone_setssuauth(zone, ssutable);
			}
		}

		tmpres = dns_ssutable_addrule(ssutable, $1.grant,
					      $1.ident, $1.matchtype,
					      $1.name,
					      $1.rdatatypes.idx,
					      $1.rdatatypes.types);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "error creating ssu "
				     "identity value");
			ok = ISC_FALSE;
		}

		dns_name_free($1.ident, memctx);
		dns_name_free($1.name, memctx);

		isc_mem_put(memctx, $1.rdatatypes.types,
			    sizeof ($1.rdatatypes.types[0]) * 256);

		isc_mem_put(memctx, $1.ident, sizeof (*$1.ident));
		isc_mem_put(memctx, $1.name, sizeof (*$1.name));

		if (!ok) {
			YYABORT;
		}
	};

grant_stmt: grantp any_string grant_match_type any_string rdatatype_list
	{
		dns_name_t *name = NULL;
		dns_name_t *identity = NULL;
		isc_boolean_t ok = ISC_TRUE;

		tmpres = dns_c_charptoname(memctx, $4, &name);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "error creating ssu name value");
			ok = ISC_FALSE;
		}

		if (ok) {
			tmpres = dns_c_charptoname(memctx, $2, &identity);
			if (tmpres != ISC_R_SUCCESS) {
				parser_error(ISC_FALSE,
					     "error creating ssu "
					     "identity value");
				ok = ISC_FALSE;
			}
		}

		if (ok) {
			if (!dns_name_isabsolute(identity)) {
				parser_error(ISC_FALSE,
					     "identity (%s) must be an "
					     "absolute (not relative) name",
					     $2);
				ok = ISC_FALSE;
			}

			if (!dns_name_isabsolute(name)) {
				parser_error(ISC_FALSE,
					     "name (%s) must be an "
					     "absolute (not relative) name",
					     $4);
				ok = ISC_FALSE;
			}

			if ($3 == DNS_SSUMATCHTYPE_WILDCARD &&
			    !dns_name_iswildcard(name)) {
				parser_error(ISC_FALSE,
					     "name (%s) has no wildcard "
					     "character ",
					     $4);
				ok = ISC_FALSE;
			}
		}

		isc_mem_free(memctx, $2);
		isc_mem_free(memctx, $4);

		if (ok) {
			$$.grant = $1;
			$$.ident = identity;
			$$.matchtype = $3;
			$$.name = name;
			$$.rdatatypes = $5;
		} else {
			if (identity != NULL) {
				dns_name_free(identity, memctx);
				isc_mem_put(memctx, identity,
					    sizeof *identity);
			}

			if (name != NULL) {
				dns_name_free(name, memctx);
				isc_mem_put(memctx, name, sizeof *name);
			}

			REQUIRE($5.types != NULL);

			isc_mem_put(memctx, $5.types,
				    sizeof (dns_rdatatype_t) * 256);

			YYABORT;
		}
	};

grantp: L_GRANT {
		$$ = ISC_TRUE;
	}
	| L_DENY {
		$$ = ISC_FALSE;
	};

grant_match_type: L_NAME {
		$$ = DNS_SSUMATCHTYPE_NAME;
	}
	| L_SUBDOMAIN {
		$$ = DNS_SSUMATCHTYPE_SUBDOMAIN;
	}
	| L_WILDCARD {
		$$ = DNS_SSUMATCHTYPE_WILDCARD;
	}
	| L_SELF {
		$$ = DNS_SSUMATCHTYPE_SELF;
	};

rdatatype_list: /* nothing */
	{
		$$.types = isc_mem_get(memctx, sizeof(dns_rdatatype_t) * 256);
		$$.idx = 0;
	}
	| rdatatype_list rdatatype {
		$1.types[$1.idx++] = $2;
		$$ = $1;
	};

rdatatype: any_string {
		isc_textregion_t reg;
		dns_rdatatype_t ty;

		reg.base = $1;
		reg.length = strlen($1);

		tmpres = dns_rdatatype_fromtext(&ty, &reg);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_TRUE, "unknown rdatatype");
			YYABORT;
		}

		isc_mem_free(memctx, $1);
		$$ = ty;
	};



/*
 * ACLs
 */

acl_stmt: L_ACL any_string L_LBRACE address_match_list L_RBRACE
	{
		dns_c_acl_t *acl;

		INSIST(currcfg->acls != NULL);

		tmpres = dns_c_acl_new(currcfg->acls,
				       $2, ISC_FALSE, &acl);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to create acl %s", $2);
			YYABORT;
		}

		dns_c_acl_setipml(acl, $4, ISC_FALSE);

		isc_mem_free(memctx, $2);
	}
	;


/*
 * Zones
 */

domain_name: L_QSTRING
	{
		$$ = $1;
	}
	;

/*
 * 'type' is no longer optional and must be the first statement in the
 * zone block.
 */
zone_stmt: L_ZONE domain_name optional_class L_LBRACE L_TYPE zone_type L_EOS
	{
		dns_c_zone_t *zone;
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);
		dns_rdataclass_t rdclass;

		if (currcfg->zlist == NULL) {
			tmpres = dns_c_zonelist_new(currcfg->mem,
						    &currcfg->zlist);
			if (tmpres != ISC_R_SUCCESS) {
				isc_log_write(dns_lctx,
					      DNS_LOGCATEGORY_CONFIG,
					      DNS_LOGMODULE_CONFIG,
					      ISC_LOG_ERROR,
					      "Failed to create zone list");
				YYABORT;
			}
		}
		rdclass = $3;
		if (rdclass == dns_rdataclass_reserved0) {
			if (view != NULL)
				(void)dns_c_view_getviewclass(view, &rdclass);
			else
				rdclass = dns_rdataclass_in;
		}

		/* XXX internal name support needed! */
		tmpres = dns_c_zone_new(currcfg->mem,
					$6, rdclass, $2, $2, &zone);
		if (tmpres != ISC_R_SUCCESS) {
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
				      DNS_LOGMODULE_CONFIG,
				      ISC_LOG_ERROR,
				      "error creating new zone");
			YYABORT;
		}

		tmpres = dns_c_zonelist_addzone(currcfg->zlist, zone);
		if (tmpres != ISC_R_SUCCESS) {
			dns_c_zone_detach(&zone);
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
				      DNS_LOGMODULE_CONFIG,
				      ISC_LOG_ERROR,
				      "error adding new zone to list");
			YYABORT;
		}

		dns_c_ctx_setcurrzone(currcfg, zone);

		isc_mem_free(memctx, $2);
	} optional_zone_options_list L_RBRACE {
		dns_c_zone_t *zone;
		dns_c_view_t *view;

		zone = dns_c_ctx_getcurrzone(currcfg);
		view = dns_c_ctx_getcurrview(currcfg);

		zone->view = view;

		if (view != NULL) {
			dns_c_view_addzone(view, zone);
		}

		dns_c_ctx_setcurrzone(currcfg, NULL);

		if (zone != NULL &&
		    callbacks != NULL && callbacks->zonecbk != NULL) {
			tmpres = dns_c_zone_validate(zone);
			if (tmpres != ISC_R_SUCCESS) {
				YYABORT;
			}

			tmpres = callbacks->zonecbk(currcfg,
						    zone,
						    view,
						    callbacks->zonecbkuap);
			if (tmpres != ISC_R_SUCCESS) {
				isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
					      DNS_LOGMODULE_CONFIG,
					      ISC_LOG_ERROR,
					      "zone configuration "
					      "for '%s' failed: %s",
					      zone->name,
					      isc_result_totext(tmpres));
				YYABORT;
			}

			dns_c_zonelist_rmzone(currcfg->zlist, zone);
		}
	}
	| L_ZONE domain_name optional_class L_LBRACE zone_non_type_keywords
	{
		parser_error(ISC_FALSE,
			     "first statement in a zone definition must "
			     "be 'type'");
		YYABORT;
	}
	| L_ZONE domain_name
	{
		parser_warning(ISC_FALSE,
			       "references to zones not implemented yet");
		isc_mem_free(memctx, $2);
	}
	;

optional_zone_options_list: /* Empty */
	| zone_option_list
	;

class_name: any_string
	{
		isc_textregion_t reg;
		dns_rdataclass_t cl;

		reg.base = $1;
		reg.length = strlen($1);

		tmpres = dns_rdataclass_fromtext(&cl, &reg);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_TRUE, "unknown class");
			YYABORT;
		}

		isc_mem_free(memctx, $1);
		$$ = cl;
	}

wild_class_name: any_string
	{
		isc_textregion_t reg;
		dns_rdataclass_t cl;

		if (strcmp($1, "*") == 0) {
			cl = dns_rdataclass_any;
		} else {
			reg.base = $1;
			reg.length = strlen($1);

			tmpres = dns_rdataclass_fromtext(&cl, &reg);
			if (tmpres != ISC_R_SUCCESS) {
				parser_error(ISC_TRUE,
					     "unknown class, assuming '*'");
				cl = dns_rdataclass_any;
			}
		}

		isc_mem_free(memctx, $1);
		$$ = cl;
	}

optional_class: /* Empty */
	{
		$$ = dns_rdataclass_reserved0;
	}
	| class_name
	;

zone_type: L_MASTER
	{
		$$ = dns_c_zone_master;
	}
	| L_SLAVE
	{
		$$ = dns_c_zone_slave;
	}
	| L_HINT
	{
		$$ = dns_c_zone_hint;
	}
	| L_STUB
	{
		$$ = dns_c_zone_stub;
	}
	| L_FORWARD
	{
		$$ = dns_c_zone_forward;
	}
	;



zone_option_list: zone_option L_EOS
	| zone_option_list zone_option L_EOS
	;


/*
 * This rule is used in enforcing the requirement that zone_type must be
 * the first element in a zone statement
 */
zone_non_type_keywords: L_FILE | L_FILE_IXFR | L_IXFR_TMP | L_MASTERS |
	L_TRANSFER_SOURCE | L_CHECK_NAMES | L_ALLOW_UPDATE |
	L_ALLOW_UPDATE_FORWARDING | L_ALLOW_NOTIFY | L_ALLOW_QUERY |
	L_ALLOW_TRANSFER | L_FORWARD | L_FORWARDERS | L_MAX_TRANSFER_TIME_IN |
	L_TCP_CLIENTS | L_RECURSIVE_CLIENTS | L_UPDATE_POLICY | L_DENY |
	L_MAX_TRANSFER_TIME_OUT | L_MAX_TRANSFER_IDLE_IN |
	L_MAX_TRANSFER_IDLE_OUT | L_MAX_LOG_SIZE_IXFR | L_NOTIFY |
	L_MAINTAIN_IXFR_BASE | L_PUBKEY | L_ALSO_NOTIFY | L_DIALUP |
	L_ENABLE_ZONE | L_DATABASE | L_PORT | L_MIN_RETRY_TIME |
	L_MAX_RETRY_TIME | L_MIN_REFRESH_TIME | L_MAX_REFRESH_TIME |
	L_ZONE_STATISTICS | L_NOTIFY_SOURCE | L_NOTIFY_SOURCE_V6 |
	;


zone_option: L_FILE L_QSTRING
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setfile(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone filename");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone file name");
			YYABORT;
		}
		isc_mem_free(memctx, $2);
	}
	| L_FILE_IXFR L_QSTRING
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setixfrbase(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine ixfr-base");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone ixfr_base");
			YYABORT;
		}
		isc_mem_free(memctx, $2);
	}
	| L_IXFR_TMP L_QSTRING
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setixfrtmp(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine ixfr-tmp-file");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone ixfr_tmp-file");
			YYABORT;
		}
		isc_mem_free(memctx, $2);
	}
	| L_MASTERS port_ip_list
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		replace_zero_ports($2, default_port);

		tmpres = dns_c_zone_setmasterips(zone, $2, ISC_FALSE);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone masters ips");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone masters ips");
			YYABORT;
		}
	}
	| L_TRANSFER_SOURCE maybe_wild_ip4_only_addr
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_settransfersource(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone transfer-source");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone transfer-source");
			YYABORT;
		}
	}
	| L_TRANSFER_SOURCE maybe_wild_ip4_only_addr L_PORT maybe_wild_port
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		isc_sockaddr_setport(&$2, $4);
		tmpres = dns_c_zone_settransfersource(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone transfer-source");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone transfer-source");
			YYABORT;
		}
	}
	| L_TRANSFER_SOURCE_V6 maybe_wild_ip6_only_addr
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_settransfersourcev6(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone "
				     "transfer-source-v6");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone transfer-source-v6");
			YYABORT;
		}
	}
	| L_TRANSFER_SOURCE_V6 maybe_wild_ip6_only_addr L_PORT maybe_wild_port
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		isc_sockaddr_setport(&$2, $4);
		tmpres = dns_c_zone_settransfersourcev6(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone "
				     "transfer-source-v6");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone transfer-source-v6");
			YYABORT;
		}
	}
	| L_NOTIFY_SOURCE maybe_wild_ip4_only_addr
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setnotifysource(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone notify-source");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone notify-source");
			YYABORT;
		}
	}
	| L_NOTIFY_SOURCE maybe_wild_ip4_only_addr L_PORT maybe_wild_port
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		isc_sockaddr_setport(&$2, $4);
		tmpres = dns_c_zone_setnotifysource(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone notify-source");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone notify-source");
			YYABORT;
		}
	}
	| L_NOTIFY_SOURCE_V6 maybe_wild_ip6_only_addr
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setnotifysourcev6(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone "
				     "notify-source-v6");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone notify-source-v6");
			YYABORT;
		}
	}
	| L_NOTIFY_SOURCE_V6 maybe_wild_ip6_only_addr L_PORT maybe_wild_port
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		isc_sockaddr_setport(&$2, $4);
		tmpres = dns_c_zone_setnotifysourcev6(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone "
				     "notify-source-v6");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone notify-source-v6");
			YYABORT;
		}
	}
	| L_CHECK_NAMES check_names_opt
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setchecknames(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone check-names");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone check-names");
			YYABORT;
		}
	}
	| L_ALLOW_UPDATE L_LBRACE address_match_list L_RBRACE
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setallowupd(zone,
						$3, ISC_FALSE);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone allow-update");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone allow-update");
			YYABORT;
		}
	}
	| L_ALLOW_UPDATE_FORWARDING L_LBRACE address_match_list L_RBRACE
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setallowupdateforwarding(zone,
							     $3, ISC_FALSE);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone "
				     "allow-update-forwarding");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone "
				     "allow-update-forwarding");
			YYABORT;
		}
	}
	| L_ALLOW_NOTIFY L_LBRACE address_match_list L_RBRACE
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setallownotify(zone,
						  $3, ISC_FALSE);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone allow-notify");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone allow-notify");
			YYABORT;
		}
	}
	| L_ALLOW_QUERY L_LBRACE address_match_list L_RBRACE
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setallowquery(zone,
						  $3, ISC_FALSE);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone allow-query");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone allow-query");
			YYABORT;
		}
	}
	| L_ALLOW_TRANSFER L_LBRACE address_match_list L_RBRACE
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setallowtransfer(zone,
						     $3, ISC_FALSE);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone allow-transfer");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone allow-transfer");
			YYABORT;
		}
	}
	| L_FORWARD zone_forward_opt
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setforward(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone forward");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone forward");
			YYABORT;
		}
	}
	| L_FORWARDERS L_LBRACE opt_zone_forwarders_list L_RBRACE
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);
		dns_c_iplist_t *iplist;

		INSIST(zone != NULL);

		if ($3 == NULL) {	/* user defined empty list */
			tmpres = dns_c_iplist_new(currcfg->mem,
						  5, &iplist);
			if (tmpres != ISC_R_SUCCESS) {
				parser_error(ISC_TRUE,
					     "failed to create new zone "
					     "iplist");
				YYABORT;
			}
		} else {
			iplist = $3;
		}

		tmpres = dns_c_zone_setforwarders(zone,
						  iplist, ISC_FALSE);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone forwarders");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone forwarders");
			YYABORT;
		}
	}
	| L_MAX_TRANSFER_TIME_IN L_INTEGER
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		if (int_too_big($2, 60)) {
			parser_error(ISC_FALSE,
				     "integer value too big: %u", $2);
			YYABORT;
		}

		tmpres = dns_c_zone_setmaxtranstimein(zone, $2 * 60);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone "
				     "max-transfer-time-in");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone "
				     "max-transfer-time-in");
			YYABORT;
		}
	}
	| L_MAX_TRANSFER_TIME_OUT L_INTEGER
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		if (int_too_big($2, 60)) {
			parser_error(ISC_FALSE,
				     "integer value too big: %u", $2);
			YYABORT;
		}

		tmpres = dns_c_zone_setmaxtranstimeout(zone, $2 * 60);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone "
				     "max-transfer-time-out");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone "
				     "max-transfer-time-out");
			YYABORT;
		}
	}
	| L_MAX_TRANSFER_IDLE_IN L_INTEGER
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		if (int_too_big($2, 60)) {
			parser_error(ISC_FALSE,
				     "integer value too big: %u", $2);
			YYABORT;
		}

		tmpres = dns_c_zone_setmaxtransidlein(zone, $2 * 60);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone "
				     "max-transfer-idle-in");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone "
				     "max-transfer-idle-in");
			YYABORT;
		}
	}
	| L_MAX_TRANSFER_IDLE_OUT L_INTEGER
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		if (int_too_big($2, 60)) {
			parser_error(ISC_FALSE,
				     "integer value too big: %u", $2);
			YYABORT;
		}

		tmpres = dns_c_zone_setmaxtransidleout(zone, $2 * 60);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone "
				     "max-transfer-idle-out");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone "
				     "max-transfer-idle-out");
			YYABORT;
		}
	}
	| L_SIG_VALIDITY_INTERVAL L_INTEGER
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		if (int_too_big($2, 60 * 60 * 24)) {
			parser_error(ISC_FALSE,
				     "integer value too big: %u", $2);
			YYABORT;
		}
		tmpres = dns_c_zone_setsigvalidityinterval(zone,
							   $2 * 60 * 60 * 24);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone "
				     "sig-validity-interval");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone "
				     "sig-validity-interval");
			YYABORT;
		}
	}
	| L_MIN_RETRY_TIME L_INTEGER
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setminretrytime(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone min-retry-time");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone min-retry-time");
			YYABORT;
		}
	}
	| L_MAX_RETRY_TIME L_INTEGER
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setmaxretrytime(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone max-retry-time");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone max-retry-time");
			YYABORT;
		}
	}
	| L_MIN_REFRESH_TIME L_INTEGER
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setminrefreshtime(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone min-refresh-time");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone min-refresh-time");
			YYABORT;
		}
	}
	| L_MAX_REFRESH_TIME L_INTEGER
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setmaxrefreshtime(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone max-refresh-time");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone max-refresh-time");
			YYABORT;
		}
	}
	| L_MAX_LOG_SIZE_IXFR L_INTEGER
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setmaxixfrlog(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone "
				     "max-ixfr-log-size");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone max-ixfr-log-size");
			YYABORT;
		}
	}
	| L_NOTIFY notify_setting
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setnotify(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone notify");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone notify");
			YYABORT;
		}
	}
	| L_MAINTAIN_IXFR_BASE yea_or_nay
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setmaintixfrbase(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone "
				     "maintain-ixfr-base");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone maintain-ixfr-base");
			YYABORT;
		}
	}
	| L_PUBKEY L_INTEGER L_INTEGER L_INTEGER L_QSTRING
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);
		dns_c_pubkey_t *pubkey;

		INSIST(zone != NULL);

		tmpres = dns_c_pubkey_new(currcfg->mem, $2,
					  $3, $4, $5, &pubkey);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_TRUE,
				     "failed to create a zone pubkey");
			YYABORT;
		}

		tmpres = dns_c_zone_addpubkey(zone, pubkey,
					      ISC_FALSE);
		if (tmpres != ISC_R_SUCCESS) {
			dns_c_pubkey_delete(&pubkey);
			parser_error(ISC_FALSE,
				     "failed to add a zone pubkey");
			YYABORT;
		}

		isc_mem_free(memctx, $5);
	}
	| L_ALSO_NOTIFY port_ip_list
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		replace_zero_ports($2, default_port);

		tmpres = dns_c_zone_setalsonotify(zone, $2, ISC_FALSE);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone also-notify");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone also-notify");
			YYABORT;
		}
	}
	| L_DIALUP dialup_setting
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setdialup(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone dialup");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone dialup");
			YYABORT;
		}
	}
	| L_ZONE_STATISTICS yea_or_nay
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setstatistics(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone statistics");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone statistics");
			YYABORT;
		}
	}
	| L_ENABLE_ZONE yea_or_nay
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setenabled(zone, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine enable-zone");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set enable-zone");
			YYABORT;
		}
	}
	| L_DATABASE L_QSTRING
	{
		dns_c_zone_t *zone = dns_c_ctx_getcurrzone(currcfg);

		INSIST(zone != NULL);

		tmpres = dns_c_zone_setdatabase(zone, $2);
		isc_mem_free(memctx, $2);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE,
				     "cannot redefine zone database");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE,
				     "failed to set zone database");
			YYABORT;
		}

	}
	| zone_update_policy
	;


ip4_address: L_IP4ADDR
	{
		isc_sockaddr_fromin(&$$, &$1, 0);
	}
	;

ip4_prefix: L_IP4PREFIX
	{
		isc_sockaddr_fromin(&$$, &$1, 0);
	}
	;

ip6_address: L_IP6ADDR
	{
		isc_sockaddr_fromin6(&$$, &$1, 0);
	};


ip_prefix: ip4_address | ip4_prefix | ip6_address
	;

ip_address: ip4_address | ip6_address
	;

in_addr_elem: ip_address
	;

opt_in_addr_list: /* nothing */
	{
		dns_c_iplist_t *list;

		tmpres = dns_c_iplist_new(currcfg->mem, 5, &list);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_TRUE,
				     "failed to create new iplist");
			YYABORT;
		}

		$$ = list;
	}
	| in_addr_list
	;

in_addr_list: in_addr_elem L_EOS
	{
		dns_c_iplist_t *list;

		tmpres = dns_c_iplist_new(currcfg->mem, 5, &list);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_TRUE,
				     "failed to create new iplist");
			YYABORT;
		}

		tmpres = dns_c_iplist_append(list, $1, NULL);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_TRUE,
				     "failed to append master address");
			YYABORT;
		}

		$$ = list;
	}
	| in_addr_list in_addr_elem L_EOS
	{
		tmpres = dns_c_iplist_append($1, $2, NULL);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_TRUE,
				     "failed to append master address");
			YYABORT;
		}

		$$ = $1;
	}
	;

zone_forward_opt: L_ONLY
	{
		$$ = dns_c_forw_only;
	}
	| L_FIRST
	{
		$$ = dns_c_forw_first;
	}
	;

opt_zone_forwarders_list: opt_in_addr_list
	;

/*
 * Trusted Key statement
 */

trusted_keys_stmt: L_TRUSTED_KEYS
	{
		dns_c_tkeylist_t *newlist;
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);

		if (view == NULL) {
			tmpres = dns_c_ctx_gettrustedkeys(currcfg,
							  &newlist);
		} else {
			tmpres = dns_c_view_gettrustedkeys(view, &newlist);
		}

		if (tmpres == ISC_R_NOTFOUND) {
			tmpres = dns_c_tkeylist_new(currcfg->mem, &newlist);
			if (tmpres != ISC_R_SUCCESS) {
				isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
					      DNS_LOGMODULE_CONFIG,
					      ISC_LOG_ERROR,
					      "Failed to create trusted key"
					      " list");
				YYABORT;
			}

			if (view == NULL) {
				tmpres = dns_c_ctx_settrustedkeys(currcfg,
								  newlist,
								  ISC_FALSE);
			} else {
				tmpres = dns_c_view_settrustedkeys(view,
								   newlist,
								   ISC_FALSE);
			}

			if (tmpres != ISC_R_SUCCESS) {
				isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
					      DNS_LOGMODULE_CONFIG,
					      ISC_LOG_ERROR,
					      "Failed to set trusted keys");
				YYABORT;
			}
		}
	} L_LBRACE trusted_keys_list L_RBRACE
	;

trusted_keys_list: trusted_key L_EOS
	| trusted_keys_list trusted_key L_EOS
	;


trusted_key: any_string L_INTEGER L_INTEGER L_INTEGER L_QSTRING
	{
		dns_c_tkey_t *tkey;
		dns_c_tkeylist_t *list;
		dns_c_view_t *view = dns_c_ctx_getcurrview(currcfg);
		isc_mem_t *mem;

		if (view == NULL) {
			tmpres = dns_c_ctx_gettrustedkeys(currcfg, &list);
			mem = currcfg->mem;
		} else {
			tmpres = dns_c_view_gettrustedkeys(view, &list);
			mem = view->mem;
		}

		if (tmpres != ISC_R_SUCCESS) {
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
				      DNS_LOGMODULE_CONFIG,
				      ISC_LOG_ERROR,
				      "No trusted key list defined!");
			YYABORT;
		}

		tmpres = dns_c_tkey_new(mem, $1, $2, $3,
					$4, $5, &tkey);
		if (tmpres != ISC_R_SUCCESS) {
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
				      DNS_LOGMODULE_CONFIG,
				      ISC_LOG_ERROR,
				      "Failed to create trusted key");
			YYABORT;
		}

		tmpres = dns_c_tkeylist_append(list,
					       tkey, ISC_FALSE);
		if (tmpres != ISC_R_SUCCESS) {
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
				      DNS_LOGMODULE_CONFIG,
				      ISC_LOG_ERROR,
				      "Failed to append trusted key");
			YYABORT;
		}

		isc_mem_free(memctx, $1);
		isc_mem_free(memctx, $5);
	}
	;

/*
 * Lightweight resolver daemon
 */


lwres_stmt: L_LWRES
	{
		dns_c_lwres_t *lwres = NULL;
		tmpres = dns_c_lwres_new(currcfg->mem, &lwres);
		if (tmpres != ISC_R_SUCCESS) {
			YYABORT;
		}
		if (currcfg->lwres == NULL) {
			tmpres = dns_c_lwreslist_new(currcfg->mem,
						     &currcfg->lwres);
			if (tmpres != ISC_R_SUCCESS) {
				YYABORT;
			}
		}
		ISC_LIST_APPEND(currcfg->lwres->lwreslist, lwres, next);
		
	} L_LBRACE optional_lwres_options_list L_RBRACE
	;

optional_lwres_options_list: /* empty */
	| lwres_options_list
	;

lwres_options_list: lwres_option L_EOS
	| lwres_options_list lwres_option L_EOS;

lwres_option: L_LISTEN_ON port_ip_list
	{
		dns_c_lwres_t *lwres;

		replace_zero_ports($2, LWRES_DEFAULT_PORT);

		lwres = ISC_LIST_TAIL(currcfg->lwres->lwreslist);
		tmpres = dns_c_lwres_setlistenon(lwres, $2);
		dns_c_iplist_detach(&$2);

		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine listen-on");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to set listen-on");
			YYABORT;
		}
	}
	| L_VIEW any_string optional_class
	{
		dns_c_lwres_t *lwres;
		dns_rdataclass_t rdclass;

		lwres = ISC_LIST_TAIL(currcfg->lwres->lwreslist);
		rdclass = ($3 == dns_rdataclass_reserved0) ?
			  dns_rdataclass_in : $3;
		tmpres = dns_c_lwres_setview(lwres, $2, rdclass);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine view");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to set view");
			YYABORT;
		}
		isc_mem_free(memctx, $2);
	}
	| L_SEARCHLIST L_LBRACE searchlist L_RBRACE
	{
		dns_c_lwres_t *lwres;
		lwres = ISC_LIST_TAIL(currcfg->lwres->lwreslist);
		tmpres = dns_c_lwres_setsearchlist(lwres, $3);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine searchlist");
			dns_c_searchlist_delete(&$3);
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to set searchlist");
			dns_c_searchlist_delete(&$3);
			YYABORT;
		}
	}
	| L_NDOTS L_INTEGER
	{
		dns_c_lwres_t *lwres;
		lwres = ISC_LIST_TAIL(currcfg->lwres->lwreslist);
		tmpres = dns_c_lwres_setndots(lwres, $2);
		if (tmpres == ISC_R_EXISTS) {
			parser_error(ISC_FALSE, "cannot redefine ndots");
			YYABORT;
		} else if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to set ndots");
			YYABORT;
		} else if ($2 <= 0) {
			parser_error(ISC_FALSE, "ndots must be >= 1");
			YYABORT;
		}
	}
	;


/*
 * Misc.
 */

in_port: L_INTEGER
	{
		if ($1 < 0 || $1 > 65535) {
			parser_error(ISC_TRUE,
				     "invalid IP port number '%d'", (int)$1);
			YYABORT;
		} else {
			$$ = $1;
		}
	}
	;


any_string: L_STRING
	| L_QSTRING
	;

maybe_eos: | L_EOS ;

%%

static int		intuit_token(const char *string);

static isc_boolean_t	is_ip4addr(const char *string, struct in_addr *addr);
static isc_boolean_t	is_ip4prefix(const char *string, struct in_addr *addr);
static isc_boolean_t	is_ip6addr(const char *string, struct in6_addr *addr);
static isc_result_t	keyword_init(void);
static char *		token_to_text(int token, YYSTYPE lval);
static int		token_value(isc_token_t *token,
				    isc_symtab_t *symtable);
static void		init_action(void);

static YYSTYPE		lastyylval;
static int		lasttoken;


/*
 * Definition of all unique keyword tokens to be recognised by the
 * lexer. All the 'L_' tokens defined in parser.y must be defined here too.
 */
struct token
{
	const char *token;
	int yaccval;
};

static struct token keyword_tokens [] = {
	{ "{",				L_LBRACE },
	{ "}",				L_RBRACE },
	{ ";",				L_EOS },
	{ "/",				L_SLASH },
	{ "!",				L_BANG },

	{ "acl",			L_ACL },
	{ "additional-data",		L_ADDITIONAL_DATA },
	{ "address",			L_ADDRESS },
	{ "algorithm",			L_ALGID },
	{ "allow",			L_ALLOW },
	{ "allow-notify",		L_ALLOW_NOTIFY },
	{ "allow-query",		L_ALLOW_QUERY },
	{ "allow-recursion",		L_ALLOW_RECURSION },
	{ "allow-transfer",		L_ALLOW_TRANSFER },
	{ "allow-update",		L_ALLOW_UPDATE },
	{ "allow-update-forwarding",	L_ALLOW_UPDATE_FORWARDING },
	{ "also-notify",		L_ALSO_NOTIFY },
	{ "auth-nxdomain",		L_AUTH_NXDOMAIN },
	{ "blackhole",			L_BLACKHOLE },
	{ "bogus",			L_BOGUS },
	{ "cache-file",			L_CACHE_FILE },
	{ "category",			L_CATEGORY },
	{ "channel",			L_CHANNEL },
	{ "check-names",		L_CHECK_NAMES },
	{ "class",			L_CLASS },
	{ "cleaning-interval",		L_CLEAN_INTERVAL },
	{ "controls",			L_CONTROLS },
	{ "coresize",			L_CORESIZE },
	{ "zone-statistics",		L_ZONE_STATISTICS },
	{ "database",			L_DATABASE },
	{ "datasize",			L_DATASIZE },
	{ "deallocate-on-exit",		L_DEALLOC_ON_EXIT },
	{ "debug",			L_DEBUG },
	{ "default",			L_DEFAULT },
	{ "deny",			L_DENY },
	{ "dialup",			L_DIALUP },
	{ "directory",			L_DIRECTORY },
	{ "dump-file",			L_DUMP_FILE },
	{ "dynamic",			L_DYNAMIC },
	{ "enable-zone",		L_ENABLE_ZONE },
	{ "expert-mode",		L_EXPERT_MODE },
	{ "explicit",			L_EXPLICIT },
	{ "fail",			L_FAIL },
	{ "fake-iquery",		L_FAKE_IQUERY },
	{ "false",			L_FALSE },
	{ "fetch-glue",			L_FETCH_GLUE },
	{ "file",			L_FILE },
	{ "files",			L_FILES },
	{ "first",			L_FIRST },
	{ "forward",			L_FORWARD },
	{ "forwarders",			L_FORWARDERS },
	{ "additional-from-auth",	L_ADDITIONAL_FROM_AUTH },
	{ "additional-from-cache",	L_ADDITIONAL_FROM_CACHE },
	{ "grant",			L_GRANT },
	{ "group",			L_GROUP },
	{ "has-old-clients",		L_HAS_OLD_CLIENTS },
	{ "heartbeat-interval",		L_HEARTBEAT },
	{ "hint",			L_HINT },
	{ "host-statistics",		L_HOSTSTATS },
	{ "if-no-answer",		L_IF_NO_ANSWER },
	{ "if-no-domain",		L_IF_NO_DOMAIN },
	{ "ignore",			L_IGNORE },
	{ "include",			L_INCLUDE },
	{ "inet",			L_INET },
	{ "interface-interval",		L_INTERFACE_INTERVAL },
	{ "internal",			L_INTERNAL },
	{ "ixfr-base",			L_FILE_IXFR },
	{ "ixfr-tmp-file",		L_IXFR_TMP },
	{ "key",			L_SEC_KEY },
	{ "keys",			L_KEYS },
	{ "lame-ttl",			L_LAME_TTL },
	{ "listen-on",			L_LISTEN_ON },
	{ "listen-on-v6",		L_LISTEN_ON_V6 },
	{ "logging",			L_LOGGING },
	{ "lwres",			L_LWRES },
	{ "maintain-ixfr-base",		L_MAINTAIN_IXFR_BASE },
	{ "many-answers",		L_MANY_ANSWERS },
	{ "master",			L_MASTER },
	{ "masters",			L_MASTERS },
	{ "match-clients",		L_MATCH_CLIENTS },
	{ "max-cache-size",		L_MAX_CACHE_SIZE },
	{ "max-cache-ttl",		L_MAX_CACHE_TTL },
	{ "max-ixfr-log-size",		L_MAX_LOG_SIZE_IXFR },
	{ "max-ncache-ttl",		L_MAX_NCACHE_TTL },
	{ "max-transfer-idle-in",	L_MAX_TRANSFER_IDLE_IN },
	{ "max-transfer-idle-out",	L_MAX_TRANSFER_IDLE_OUT },
	{ "max-transfer-time-in",	L_MAX_TRANSFER_TIME_IN },
	{ "max-transfer-time-out",	L_MAX_TRANSFER_TIME_OUT },
	{ "min-retry-time",		L_MIN_RETRY_TIME },
	{ "max-retry-time",		L_MAX_RETRY_TIME },
	{ "min-refresh-time",		L_MIN_REFRESH_TIME },
	{ "max-refresh-time",		L_MAX_REFRESH_TIME },
	{ "maximal",			L_MAXIMAL },
	{ "memstatistics-file",		L_MEMSTATS_FILE },
	{ "min-roots",			L_MIN_ROOTS },
	{ "minimal",			L_MINIMAL },
	{ "multiple-cnames",		L_MULTIPLE_CNAMES },
	{ "name",			L_NAME },
	{ "named-xfer",			L_NAMED_XFER },
	{ "ndots",			L_NDOTS },
	{ "no",				L_NO },
	{ "notify",			L_NOTIFY },
	{ "notify-source",		L_NOTIFY_SOURCE },
	{ "notify-source-v6",		L_NOTIFY_SOURCE_V6 },
	{ "notify-passive",		L_NOTIFY_PASSIVE },
	{ "null",			L_NULL_OUTPUT },
	{ "one-answer",			L_ONE_ANSWER },
	{ "only",			L_ONLY },
	{ "options",			L_OPTIONS },
	{ "order",			L_ORDER },
	{ "owner",			L_OWNER },
	{ "passive",			L_PASSIVE },
	{ "perm",			L_PERM },
	{ "pid-file",			L_PIDFILE },
	{ "port",			L_PORT },
	{ "print-category",		L_PRINT_CATEGORY },
	{ "print-severity",		L_PRINT_SEVERITY },
	{ "print-time",			L_PRINT_TIME },
	{ "provide-ixfr",		L_PROVIDE_IXFR },
	{ "pubkey",			L_PUBKEY },
	{ "query-source",		L_QUERY_SOURCE },
	{ "query-source-v6",		L_QUERY_SOURCE_V6 },
	{ "random-device",		L_RANDOM_DEVICE },
	{ "random-seed-file",		L_RANDOM_SEED_FILE },
	{ "recursion",			L_RECURSION },
	{ "recursive-clients",		L_RECURSIVE_CLIENTS },
	{ "refresh",			L_REFRESH },
	{ "request-ixfr",		L_REQUEST_IXFR },
	{ "response",			L_RESPONSE },
	{ "rfc2308-type1",		L_RFC2308_TYPE1 },
	{ "rrset-order",		L_RRSET_ORDER },
	{ "search",			L_SEARCHLIST },
	{ "secret",			L_SECRET },
	{ "self",			L_SELF },
	{ "serial-queries",		L_SERIAL_QUERIES },
	{ "server",			L_SERVER },
	{ "severity",			L_SEVERITY },
	{ "sig-validity-interval",	L_SIG_VALIDITY_INTERVAL },
	{ "size",			L_SIZE },
	{ "slave",			L_SLAVE },
	{ "sortlist",			L_SORTLIST },
	{ "stacksize",			L_STACKSIZE },
	{ "statistics-file",		L_STATS_FILE },
	{ "statistics-interval",	L_STATS_INTERVAL },
	{ "stderr",			L_STDERR },
	{ "stub",			L_STUB },
	{ "subdomain",			L_SUBDOMAIN },
	{ "support-ixfr",		L_SUPPORT_IXFR },
	{ "syslog",			L_SYSLOG },
	{ "tcp-clients",		L_TCP_CLIENTS },
	{ "tkey-dhkey",			L_TKEY_DHKEY },
	{ "tkey-domain",		L_TKEY_DOMAIN },
	{ "tkey-gssapi-credential",	L_TKEY_GSSAPI_CREDENTIAL },
	{ "topology",			L_TOPOLOGY },
	{ "transfer-format",		L_TRANSFER_FORMAT },
	{ "transfer-source",		L_TRANSFER_SOURCE },
	{ "transfer-source-v6",		L_TRANSFER_SOURCE_V6 },
	{ "transfers",			L_TRANSFERS },
	{ "transfers-in",		L_TRANSFERS_IN },
	{ "transfers-out",		L_TRANSFERS_OUT },
	{ "transfers-per-ns",		L_TRANSFERS_PER_NS },
	{ "treat-cr-as-space",		L_TREAT_CR_AS_SPACE },
	{ "true",			L_TRUE },
	{ "trusted-keys",		L_TRUSTED_KEYS },
	{ "type",			L_TYPE },
	{ "unix",			L_UNIX },
	{ "unlimited",			L_UNLIMITED },
	{ "update-policy",		L_UPDATE_POLICY },
	{ "use-id-pool",		L_USE_ID_POOL },
	{ "use-ixfr",			L_USE_IXFR },
	{ "version",			L_VERSION },
	{ "versions",			L_VERSIONS },
	{ "view",			L_VIEW },
	{ "warn",			L_WARN },
	{ "wildcard",			L_WILDCARD },
	{ "yes",			L_YES },
	{ "zone",			L_ZONE },

	{ NULL, 0 }
};


static struct token class_symbol_tokens[] = {
	{ "IN", dns_rdataclass_in },
#if 0					/* XXX expand */
	{ "CHAOS", dns_rdataclass_chaos },
	{ "HS", dns_rdataclass_hs },
	{ "HESIOD", dns_rdataclass_hs },
#endif
	{ "ANY", dns_rdataclass_any },
	{ "NONE", dns_rdataclass_none },
	{ NULL, 0 }
};


static isc_once_t once = ISC_ONCE_INIT;


static void
init_action(void)
{
	isc_mutex_init(&yacc_mutex);
}


/*
 * XXX Need a parameter to specify where error messages should go (syslog,
 * FILE, /dev/null etc.) Also some way to tell the function to obey logging
 * statments as appropriate.
 */

isc_result_t
dns_c_parse_namedconf(const char *filename, isc_mem_t *mem,
		      dns_c_ctx_t **configctx, dns_c_cbks_t *cbks)
{
	isc_result_t res;
	const char *funcname = "dns_parse_namedconf";

	RUNTIME_CHECK(isc_once_do(&once, init_action) == ISC_R_SUCCESS);

	/* Lock down whole parser. */
	if (isc_mutex_lock(&yacc_mutex) != ISC_R_SUCCESS) {
		return (ISC_R_UNEXPECTED);
	}

	REQUIRE(currcfg == NULL);
	REQUIRE(filename != NULL);
	REQUIRE(configctx != NULL);
	INSIST(mylexer == NULL);
	INSIST(memctx == NULL);
	INSIST(keywords == NULL);
	INSIST(callbacks == NULL);

	specials['{'] = 1;
	specials['}'] = 1;
	specials[';'] = 1;
	specials['/'] = 1;
	specials['"'] = 1;
	specials['!'] = 1;

	default_port = DNS_C_DEFAULTPORT;

	/*
	 * This memory context is only used by the lexer routines (and must
	 * stay that way). Any memory that must live past the return of
	 * yyparse() must be allocated via the 'mem' parameter to this
	 * function.
	 */
	res = isc_mem_create(0, 0, &memctx);
	if (res != ISC_R_SUCCESS) {
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
			      DNS_LOGMODULE_CONFIG, ISC_LOG_CRITICAL,
			      "%s: error creating mem context",
			      funcname);
		goto done;
	}

	res = keyword_init();
	if (res != ISC_R_SUCCESS) {
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
			      DNS_LOGMODULE_CONFIG, ISC_LOG_CRITICAL,
			      "%s: error initializing keywords",
			      funcname);
		goto done;
	}

	res = dns_c_ctx_new(mem, &currcfg);
	if (res != ISC_R_SUCCESS) {
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
			      DNS_LOGMODULE_CONFIG, ISC_LOG_CRITICAL,
			      "%s: error creating config context",
			      funcname);
		goto done;
	}

	res = isc_lex_create(memctx, CONF_MAX_IDENT, &mylexer);
	if (res != ISC_R_SUCCESS) {
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
			      DNS_LOGMODULE_CONFIG, ISC_LOG_CRITICAL,
			      "%s: error creating lexer",
			      funcname);
		goto done;
	}

	isc_lex_setspecials(mylexer, specials);
	isc_lex_setcomments(mylexer, (ISC_LEXCOMMENT_C |
				      ISC_LEXCOMMENT_CPLUSPLUS |
				      ISC_LEXCOMMENT_SHELL));

	res = isc_lex_openfile(mylexer, filename);
	if (res != ISC_R_SUCCESS) {
		isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
			      DNS_LOGMODULE_CONFIG, ISC_LOG_CRITICAL,
			      "%s: open: %s", filename,
			      isc_result_totext(res));
		goto done;
	}

	callbacks = cbks;

	if (yyparse() != 0) {
		res = ISC_R_FAILURE;

		/*
		 * Syntax errors in the config file make it very difficult
		 * to clean up memory properly (which causes assertion
		 * failure when the memory manager is destroyed).
		 */
		isc_mem_setdestroycheck(memctx, ISC_FALSE);

		dns_c_ctx_delete(&currcfg);
		currcfg = NULL;
	} else {
		res = ISC_R_SUCCESS;
	}


 done:
	if (mylexer != NULL)
		isc_lex_destroy(&mylexer);

	isc_symtab_destroy(&keywords);

	isc_mem_destroy(&memctx);

	if (res == ISC_R_SUCCESS) {
		res = dns_c_checkconfig(currcfg);
		if (res != ISC_R_SUCCESS) {
			dns_c_ctx_delete(&currcfg);
		}
	} else if (currcfg != NULL)
		dns_c_ctx_delete(&currcfg);

	*configctx = currcfg;

	callbacks = NULL;
	currcfg = NULL;
	memctx = NULL;
	mylexer = NULL;

	RUNTIME_CHECK(isc_mutex_unlock(&yacc_mutex) == ISC_R_SUCCESS);

	return (res);
}



/***
 *** PRIVATE
 ***/

static isc_result_t
keyword_init(void)
{
	struct token *tok;
	isc_symvalue_t symval;

	RUNTIME_CHECK(isc_symtab_create(memctx, 97 /* prime < 100 */,
					NULL, NULL, ISC_FALSE,
					&keywords) == ISC_R_SUCCESS);


	/* Stick all the keywords into the main symbol table. */
	for (tok = &keyword_tokens[0] ; tok->token != NULL ; tok++) {
		symval.as_integer = tok->yaccval;
		RUNTIME_CHECK(isc_symtab_define(keywords, tok->token,
						KEYWORD_SYM_TYPE, symval,
						isc_symexists_reject) ==
			      ISC_R_SUCCESS);
	}

	/* Now the class names */
	for (tok = &class_symbol_tokens[0] ; tok->token != NULL ; tok++) {
		symval.as_integer = tok->yaccval;
		RUNTIME_CHECK(isc_symtab_define(keywords, tok->token,
						CLASS_SYM_TYPE, symval,
						isc_symexists_reject) ==
			      ISC_R_SUCCESS);
	}

	return (ISC_R_SUCCESS);
}



static int
yylex(void)
{
	isc_token_t token;
	isc_result_t res;
	int options = (ISC_LEXOPT_EOF |
		       ISC_LEXOPT_NUMBER |
		       ISC_LEXOPT_CNUMBER |
		       ISC_LEXOPT_QSTRING |
		       ISC_LEXOPT_NOMORE);

	INSIST(mylexer != NULL);

	res = isc_lex_gettoken(mylexer, options, &token);

	switch(res) {
	case ISC_R_SUCCESS:
		res = token_value(&token, keywords); /* modifies yylval */
		break;

	case ISC_R_EOF:
		res = 0;
		break;

	case ISC_R_NOSPACE:
		parser_error(ISC_TRUE, "token too big");
		res = -1;
		break;

	default:
		parser_error(ISC_TRUE, "%s", isc_result_totext(res));
		res = -1;
		break;
	}


	lastyylval = yylval;
	lasttoken = res;

	return (res);
}



static char *
token_to_text(int token, YYSTYPE lval) {
	static char buffer[1024];
	const char *tk;
	const char *p;

	/*
	 * Yacc keeps token numbers above 128, it seems.
	 */
	if (token < 128) {
		if (token == 0)
			strncpy(buffer, "<end of file>", sizeof buffer);
		else if (token < 0)
			strncpy(buffer, "<invalid token>", sizeof buffer);
		else
			if ((unsigned int) sprintf(buffer, "'%c'", token)
			    >= sizeof buffer) {
				abort();
			}
	} else {
		switch (token) {
		case L_STRING:
			if ((unsigned int) sprintf(buffer, "'%s'",
						   lval.text) >=
			    sizeof buffer) {
				abort();
			}
			break;
		case L_QSTRING:
			if ((unsigned int) sprintf(buffer, "\"%s\"",
						   lval.text) >=
			    sizeof buffer) {
				abort();
			}
			break;
		case L_IP6ADDR:
			p = inet_ntop(AF_INET6, lval.ip6_addr.s6_addr,
				      &buffer[1], (sizeof buffer) - 2);
			if (p == NULL)
				strcpy(buffer, "UNAVAILABLE-IPV6-ADDRESS");
			else {
				buffer[0] = '\'';
				strcat(buffer, "'");
			}
			break;
		case L_IP4ADDR:
			p = inet_ntop(AF_INET, &lval.ip4_addr.s_addr,
				      &buffer[1], (sizeof buffer) - 2);
			if (p == NULL)
				strcpy(buffer, "UNAVAILABLE-IPV4-ADDRESS");
			else {
				buffer[0] = '\'';
				strcat(buffer, "'");
			}
			break;
		case L_IP4PREFIX: {
			int i;
			p = inet_ntop(AF_INET, &lval.ip4_addr.s_addr,
				      &buffer[1], (sizeof buffer) - 2);
			if (p == NULL)
				strcpy(buffer, "UNAVAILABLE-IPV4-ADDRESS");
			else {
				buffer[0] = '\'';
				/*
				 * Remove trailing zeros added in. We may
				 * remove too many but we need to remove at
				 * least one.
				 */
				for (i = 0; i < 3; i++)
					if (strcmp(&buffer[strlen(buffer) - 2],
						   ".0") == 0)
						buffer[strlen(buffer) - 2] =
							'\0';
				strcat(buffer, "'");
			}
			break;
		}
		case L_INTEGER:
			sprintf(buffer, "'%lu'", (unsigned long)lval.ul_int);
			break;
		case L_END_INCLUDE:
			strcpy (buffer, "<end of include>");
			break;
		default:
			tk = token_to_keyword(token);
			if (tk == NULL) {
				sprintf(buffer, "UNKNOWN-TOKEN-TYPE (%d)",
					(int)token);
			} else {
				strncpy(buffer, tk, sizeof buffer - 1);
				buffer[sizeof buffer - 1] = '\0';
			}
			break;
		}
	}

	return (buffer);
}

static const char *
token_to_keyword(int token) {
	int i;

	for (i = 0 ; keyword_tokens[i].token != NULL ; i++) {
		if (keyword_tokens[i].yaccval == token) {
			break;
		}
	}

	return (keyword_tokens[i].token);
}



static void
parser_complain(isc_boolean_t is_warning, isc_boolean_t print_last_token,
		const char *format, va_list args)
{
	static char where[ISC_DIR_PATHMAX + 100];
	static char message[2048];
	int level = ISC_LOG_ERROR;
	const char *filename = isc_lex_getsourcename(mylexer);
	unsigned long lineno = isc_lex_getsourceline(mylexer);

	/*
	 * We can't get a trace of the include files we may be nested in
	 * (lex.c has the structures hidden). So we only report the current
	 * file.
	 */
	if (filename == NULL) {
		filename = "(none)";
	}

	if (is_warning) {
		level = ISC_LOG_WARNING;
	}

	sprintf(where, "%s:%lu: ", filename, lineno);
	if ((unsigned int)vsprintf(message, format, args) >= sizeof message)
		FATAL_ERROR(__FILE__, __LINE__,
			    "error message would overflow");

	if (print_last_token) {
		if (dns_lctx != NULL) {
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
				       DNS_LOGMODULE_CONFIG, level,
				      "%s%s near %s", where, message,
				       token_to_text(lasttoken, lastyylval));
		} else {
			fprintf(stderr, "%s%s near %s\n", where, message,
				token_to_text(lasttoken, lastyylval));
		}
	} else {
		if (dns_lctx != NULL) {
			isc_log_write(dns_lctx, DNS_LOGCATEGORY_CONFIG,
				       DNS_LOGMODULE_CONFIG, level,
				      "%s%s", where, message);
		} else {
			fprintf(stderr, "%s%s\n", where, message);
		}
	}
}




/*
 * For reporting items that are semantic, but not syntactic errors
 */
static void
parser_error(isc_boolean_t lasttoken, const char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);
	parser_complain(ISC_FALSE, lasttoken, fmt, args);
	va_end(args);

	currcfg->errors++;
}


static void
parser_warning(isc_boolean_t lasttoken, const char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);
	parser_complain(ISC_TRUE, lasttoken, fmt, args);
	va_end(args);

	currcfg->warnings++;
}


static isc_boolean_t
int_too_big(isc_uint32_t base, isc_uint32_t mult) {
	isc_uint32_t max = UINT_MAX;

	if ((max / mult) < base) {
		return ISC_TRUE;
	} else {
		return ISC_FALSE;
	}
}


static void
yyerror(const char *string)
{
	parser_error(ISC_TRUE, string);
}



static int
token_value(isc_token_t *token, isc_symtab_t *symtable)
{
	int res = -1;
	const char *tokstring;
	char tmpident [2];
	isc_symvalue_t keywordtok;

	switch (token->type) {
	case isc_tokentype_unknown:
		res = -1;
		break;

	case isc_tokentype_special:
	case isc_tokentype_string:
		if (token->type == isc_tokentype_special) {
			tmpident[0] = token->value.as_char;
			tmpident[1] = '\0';
			tokstring = tmpident;
		} else {
			tokstring = token->value.as_textregion.base;
		}

		res = isc_symtab_lookup(symtable, tokstring,
					KEYWORD_SYM_TYPE, &keywordtok);

		if (res != ISC_R_SUCCESS) {
			res = intuit_token(tokstring);
		} else {
			res = keywordtok.as_integer;
		}
		break;

	case isc_tokentype_number:
		yylval.ul_int = (isc_uint32_t)token->value.as_ulong;
		res = L_INTEGER;
		break;

	case isc_tokentype_qstring:
		yylval.text = isc_mem_strdup(memctx,
					     token->value.as_textregion.base);
		if (yylval.text == NULL) {
			res = -1;
		} else {
			res = L_QSTRING;
		}
		break;

	case isc_tokentype_eof:
		res = isc_lex_close(mylexer);
		INSIST(res == ISC_R_NOMORE || res == ISC_R_SUCCESS);

		if (isc_lex_getsourcename(mylexer) == NULL) {
			/* the only way to tell that we
			 *  closed the main file and not an included file
			 */
			res = 0;
		} else {
			res = L_END_INCLUDE;
		}
		break;

	case isc_tokentype_initialws:
		res = -1;
		break;

	case isc_tokentype_eol:
		res = -1;
		break;

	case isc_tokentype_nomore:
		res = -1;
		break;
	}

	return (res);
}




static int
intuit_token(const char *string)
{
	int resval;

	if (is_ip4addr(string, &yylval.ip4_addr)) {
		resval = L_IP4ADDR;
	} else if (is_ip4prefix(string, &yylval.ip4_addr)) {
		resval = L_IP4PREFIX;
	} else if (is_ip6addr(string, &yylval.ip6_addr)) {
		resval = L_IP6ADDR;
	} else {
		yylval.text = isc_mem_strdup(memctx, string);
		if (yylval.text == NULL) {
			resval = -1;
		} else {
			resval = L_STRING;
		}
	}

	return (resval);
}


/*
 * Conversion Routines
 */

static isc_result_t
unit_to_uint32(char *in, isc_uint32_t *out) {
	char *start = in;
	int c, units_done = 0;
	isc_uint32_t result = 0L;
	isc_uint32_t maxK = 4194304;	/* 2^32 / 1024 */
	isc_uint32_t maxM = 4096;	/* 2^32 / (1024 * 1024) */
	isc_uint32_t maxG = 4;		/* 2^32 / (1024 * 1024 * 1024) */

	INSIST(in != NULL);

	for (; (c = *in) != '\0'; in++) {
		if (units_done)
			return (ISC_R_FAILURE);
		if (isdigit((unsigned char)c)) {
			result *= 10;
			result += (c - '0');
		} else {
			if (start == in) {
				return (ISC_R_FAILURE);
			}
			switch (c) {
			case 'k':
			case 'K':
				if (result > maxK) {
					return (ISC_R_RANGE);
				}
				result *= 1024;
				units_done = 1;
				break;
			case 'm':
			case 'M':
				if (result > maxM) {
					return (ISC_R_RANGE);
				}
				result *= (1024*1024);
				units_done = 1;
				break;
			case 'g':
			case 'G':
				if (result > maxG) {
					return (ISC_R_RANGE);
				}
				result *= (1024*1024*1024);
				units_done = 1;
				break;
			default:
				return (ISC_R_FAILURE);
			}
		}
	}

	*out = result;
	return (ISC_R_SUCCESS);
}


static isc_boolean_t
is_ip6addr(const char *string, struct in6_addr *addr)
{
	if (inet_pton(AF_INET6, string, addr) != 1) {
		return ISC_FALSE;
	}
	return ISC_TRUE;
}

static isc_boolean_t
is_ip4addr(const char *string, struct in_addr *addr)
{
	char addrbuf[sizeof "xxx.xxx.xxx.xxx" + 1];
	const char *p = string;
	int dots = 0;
	char dot = '.';
	isc_boolean_t lastdot = ISC_TRUE;

	while (*p) {
		if (!isdigit(*p & 0xff) && *p != dot) {
			return (ISC_FALSE);
		} else if (lastdot && p[0] == '0' &&
			   (p[1] != '.' && p[1] != '\0')) {
			return (ISC_FALSE);
		} else if (!isdigit(*p & 0xff)) {
			dots++;
			lastdot = ISC_TRUE;
		} else {
			lastdot = ISC_FALSE;
		}
		p++;
	}

	if (dots != 3)
		return (ISC_FALSE);

	if (strlen(string) < sizeof addrbuf)
		strcpy (addrbuf, string);
	else
		return (ISC_FALSE);

	if (inet_pton(AF_INET, addrbuf, addr) != 1) {
		return ISC_FALSE;
	}
	return ISC_TRUE;
}

static isc_boolean_t
is_ip4prefix(const char *string, struct in_addr *addr)
{
	char addrbuf[sizeof "xxx.xxx.xxx.xxx" + 1];
	const char *p = string;
	int dots = 0;
	char dot = '.';
	isc_boolean_t lastdot = ISC_TRUE;

	while (*p) {
		if (!isdigit(*p & 0xff) && *p != dot) {
			return (ISC_FALSE);
		} else if (lastdot && p[0] == '0' &&
			   (p[1] != '.' && p[1] != '\0')) {
			return (ISC_FALSE);
		} else if (!isdigit(*p & 0xff)) {
			dots++;
			lastdot = ISC_TRUE;
		} else {
			lastdot = ISC_FALSE;
		}
		p++;
	}

	if (dots > 3) {
		return (ISC_FALSE);
	} else if (dots < 3) {
		if (dots == 1) {
			if (strlen(string) + 5 <= sizeof (addrbuf)) {
				strcpy(addrbuf, string);
				strcat(addrbuf, ".0.0");
			} else {
				return (ISC_FALSE);
			}
		} else if (dots == 2) {
			if (strlen(string) + 3 <= sizeof (addrbuf)) {
				strcpy(addrbuf, string);
				strcat(addrbuf, ".0");
			} else {
				return (ISC_FALSE);
			}
		}
	} else if (strlen(string) < sizeof addrbuf) {
		strcpy (addrbuf, string);
	} else {
		return (ISC_FALSE);
	}

	if (inet_pton(AF_INET, addrbuf, addr) != 1) {
		return ISC_FALSE;
	}
	return ISC_TRUE;
}



static dns_peerlist_t *
currentpeerlist(dns_c_ctx_t *cfg, isc_boolean_t createIfNeeded)
{
	dns_peerlist_t *peers = NULL;
	dns_c_view_t *view = NULL;
	isc_result_t result;

	view = dns_c_ctx_getcurrview(cfg);

	if (view == NULL) {
		result = dns_c_ctx_getpeerlist(cfg, &peers);
	} else {
		result = dns_c_view_getpeerlist(view, &peers);
	}

	if (result == ISC_R_NOTFOUND && createIfNeeded) {
		result = dns_peerlist_new(currcfg->mem, &peers);
		if (tmpres != ISC_R_SUCCESS) {
			parser_error(ISC_FALSE, "failed to create peer list");
			return NULL;
		}

		if (view == NULL) {
			dns_c_ctx_setpeerlist(currcfg, peers);
		} else {
			dns_c_view_setpeerlist(view, peers);
		}
	} else if (result == ISC_R_NOTFOUND) {
		/* nothing */
	} else if (result != ISC_R_SUCCESS) {
		REQUIRE(result == ISC_R_SUCCESS);
	}

	return peers;
}



static isc_boolean_t
keydefinedinscope(dns_c_ctx_t *cfg, const char *name)
{
	dns_c_view_t *view = dns_c_ctx_getcurrview(cfg);
	isc_boolean_t rval = ISC_FALSE;

	if (view != NULL) {
		rval = dns_c_view_keydefinedp(view, name);
	}

	if (!rval) {
		rval = dns_c_ctx_keydefinedp(cfg, name);
	}

	return (rval);
}

static void
replace_zero_ports(dns_c_iplist_t *list, in_port_t val) {
	unsigned int i;

	for (i = 0 ; i < list->nextidx ; i++) {
		if (isc_sockaddr_getport(&list->ips[i]) == 0)
			isc_sockaddr_setport(&list->ips[i], val);
	}
}
