PageRenderTime 133ms CodeModel.GetById 20ms app.highlight 99ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/bind9/lib/isccfg/parser.c

https://bitbucket.org/freebsd/freebsd-head/
C | 2477 lines | 2199 code | 202 blank | 76 comment | 267 complexity | 65d7ffeb7b54f4c36192aab7889403d0 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2 * Copyright (C) 2004-2012  Internet Systems Consortium, Inc. ("ISC")
   3 * Copyright (C) 2000-2003  Internet Software Consortium.
   4 *
   5 * Permission to use, copy, modify, and/or distribute this software for any
   6 * purpose with or without fee is hereby granted, provided that the above
   7 * copyright notice and this permission notice appear in all copies.
   8 *
   9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  15 * PERFORMANCE OF THIS SOFTWARE.
  16 */
  17
  18/* $Id$ */
  19
  20/*! \file */
  21
  22#include <config.h>
  23
  24#include <isc/buffer.h>
  25#include <isc/dir.h>
  26#include <isc/formatcheck.h>
  27#include <isc/lex.h>
  28#include <isc/log.h>
  29#include <isc/mem.h>
  30#include <isc/net.h>
  31#include <isc/netaddr.h>
  32#include <isc/netscope.h>
  33#include <isc/print.h>
  34#include <isc/string.h>
  35#include <isc/sockaddr.h>
  36#include <isc/symtab.h>
  37#include <isc/util.h>
  38
  39#include <isccfg/cfg.h>
  40#include <isccfg/grammar.h>
  41#include <isccfg/log.h>
  42
  43/* Shorthand */
  44#define CAT CFG_LOGCATEGORY_CONFIG
  45#define MOD CFG_LOGMODULE_PARSER
  46
  47#define MAP_SYM 1 	/* Unique type for isc_symtab */
  48
  49#define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
  50
  51/* Check a return value. */
  52#define CHECK(op) 						\
  53	do { result = (op); 					\
  54		if (result != ISC_R_SUCCESS) goto cleanup; 	\
  55	} while (0)
  56
  57/* Clean up a configuration object if non-NULL. */
  58#define CLEANUP_OBJ(obj) \
  59	do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
  60
  61
  62/*
  63 * Forward declarations of static functions.
  64 */
  65
  66static void
  67free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
  68
  69static isc_result_t
  70parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
  71
  72static void
  73print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
  74
  75static void
  76free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
  77
  78static isc_result_t
  79create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
  80
  81static isc_result_t
  82create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
  83	      cfg_obj_t **ret);
  84
  85static void
  86free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
  87
  88static isc_result_t
  89create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
  90
  91static void
  92free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
  93
  94static isc_result_t
  95parse_symtab_elt(cfg_parser_t *pctx, const char *name,
  96		 cfg_type_t *elttype, isc_symtab_t *symtab,
  97		 isc_boolean_t callback);
  98
  99static void
 100free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
 101
 102static isc_result_t
 103cfg_getstringtoken(cfg_parser_t *pctx);
 104
 105static void
 106parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
 107		unsigned int flags, const char *format, va_list args);
 108
 109/*
 110 * Data representations.  These correspond to members of the
 111 * "value" union in struct cfg_obj (except "void", which does
 112 * not need a union member).
 113 */
 114
 115cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
 116cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
 117cfg_rep_t cfg_rep_string = { "string", free_string };
 118cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
 119cfg_rep_t cfg_rep_map = { "map", free_map };
 120cfg_rep_t cfg_rep_list = { "list", free_list };
 121cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
 122cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
 123cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
 124cfg_rep_t cfg_rep_void = { "void", free_noop };
 125
 126/*
 127 * Configuration type definitions.
 128 */
 129
 130/*%
 131 * An implicit list.  These are formed by clauses that occur multiple times.
 132 */
 133static cfg_type_t cfg_type_implicitlist = {
 134	"implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL };
 135
 136/* Functions. */
 137
 138void
 139cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
 140	obj->type->print(pctx, obj);
 141}
 142
 143void
 144cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
 145	pctx->f(pctx->closure, text, len);
 146}
 147
 148static void
 149print_open(cfg_printer_t *pctx) {
 150	cfg_print_chars(pctx, "{\n", 2);
 151	pctx->indent++;
 152}
 153
 154static void
 155print_indent(cfg_printer_t *pctx) {
 156	int indent = pctx->indent;
 157	while (indent > 0) {
 158		cfg_print_chars(pctx, "\t", 1);
 159		indent--;
 160	}
 161}
 162
 163static void
 164print_close(cfg_printer_t *pctx) {
 165	pctx->indent--;
 166	print_indent(pctx);
 167	cfg_print_chars(pctx, "}", 1);
 168}
 169
 170isc_result_t
 171cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
 172	isc_result_t result;
 173	INSIST(ret != NULL && *ret == NULL);
 174	result = type->parse(pctx, type, ret);
 175	if (result != ISC_R_SUCCESS)
 176		return (result);
 177	INSIST(*ret != NULL);
 178	return (ISC_R_SUCCESS);
 179}
 180
 181void
 182cfg_print(const cfg_obj_t *obj,
 183	  void (*f)(void *closure, const char *text, int textlen),
 184	  void *closure)
 185{
 186	cfg_printer_t pctx;
 187	pctx.f = f;
 188	pctx.closure = closure;
 189	pctx.indent = 0;
 190	obj->type->print(&pctx, obj);
 191}
 192
 193
 194/* Tuples. */
 195
 196isc_result_t
 197cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
 198	isc_result_t result;
 199	const cfg_tuplefielddef_t *fields = type->of;
 200	const cfg_tuplefielddef_t *f;
 201	cfg_obj_t *obj = NULL;
 202	unsigned int nfields = 0;
 203	int i;
 204
 205	for (f = fields; f->name != NULL; f++)
 206		nfields++;
 207
 208	CHECK(cfg_create_obj(pctx, type, &obj));
 209	obj->value.tuple = isc_mem_get(pctx->mctx,
 210				       nfields * sizeof(cfg_obj_t *));
 211	if (obj->value.tuple == NULL) {
 212		result = ISC_R_NOMEMORY;
 213		goto cleanup;
 214	}
 215	for (f = fields, i = 0; f->name != NULL; f++, i++)
 216		obj->value.tuple[i] = NULL;
 217	*ret = obj;
 218	return (ISC_R_SUCCESS);
 219
 220 cleanup:
 221	if (obj != NULL)
 222		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
 223	return (result);
 224}
 225
 226isc_result_t
 227cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
 228{
 229	isc_result_t result;
 230	const cfg_tuplefielddef_t *fields = type->of;
 231	const cfg_tuplefielddef_t *f;
 232	cfg_obj_t *obj = NULL;
 233	unsigned int i;
 234
 235	CHECK(cfg_create_tuple(pctx, type, &obj));
 236	for (f = fields, i = 0; f->name != NULL; f++, i++)
 237		CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
 238
 239	*ret = obj;
 240	return (ISC_R_SUCCESS);
 241
 242 cleanup:
 243	CLEANUP_OBJ(obj);
 244	return (result);
 245}
 246
 247void
 248cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
 249	unsigned int i;
 250	const cfg_tuplefielddef_t *fields = obj->type->of;
 251	const cfg_tuplefielddef_t *f;
 252	isc_boolean_t need_space = ISC_FALSE;
 253
 254	for (f = fields, i = 0; f->name != NULL; f++, i++) {
 255		const cfg_obj_t *fieldobj = obj->value.tuple[i];
 256		if (need_space)
 257			cfg_print_chars(pctx, " ", 1);
 258		cfg_print_obj(pctx, fieldobj);
 259		need_space = ISC_TF(fieldobj->type->print != cfg_print_void);
 260	}
 261}
 262
 263void
 264cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
 265	const cfg_tuplefielddef_t *fields = type->of;
 266	const cfg_tuplefielddef_t *f;
 267	isc_boolean_t need_space = ISC_FALSE;
 268
 269	for (f = fields; f->name != NULL; f++) {
 270		if (need_space)
 271			cfg_print_chars(pctx, " ", 1);
 272		cfg_doc_obj(pctx, f->type);
 273		need_space = ISC_TF(f->type->print != cfg_print_void);
 274	}
 275}
 276
 277static void
 278free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
 279	unsigned int i;
 280	const cfg_tuplefielddef_t *fields = obj->type->of;
 281	const cfg_tuplefielddef_t *f;
 282	unsigned int nfields = 0;
 283
 284	if (obj->value.tuple == NULL)
 285		return;
 286
 287	for (f = fields, i = 0; f->name != NULL; f++, i++) {
 288		CLEANUP_OBJ(obj->value.tuple[i]);
 289		nfields++;
 290	}
 291	isc_mem_put(pctx->mctx, obj->value.tuple,
 292		    nfields * sizeof(cfg_obj_t *));
 293}
 294
 295isc_boolean_t
 296cfg_obj_istuple(const cfg_obj_t *obj) {
 297	REQUIRE(obj != NULL);
 298	return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
 299}
 300
 301const cfg_obj_t *
 302cfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) {
 303	unsigned int i;
 304	const cfg_tuplefielddef_t *fields;
 305	const cfg_tuplefielddef_t *f;
 306
 307	REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
 308
 309	fields = tupleobj->type->of;
 310	for (f = fields, i = 0; f->name != NULL; f++, i++) {
 311		if (strcmp(f->name, name) == 0)
 312			return (tupleobj->value.tuple[i]);
 313	}
 314	INSIST(0);
 315	return (NULL);
 316}
 317
 318isc_result_t
 319cfg_parse_special(cfg_parser_t *pctx, int special) {
 320	isc_result_t result;
 321	CHECK(cfg_gettoken(pctx, 0));
 322	if (pctx->token.type == isc_tokentype_special &&
 323	    pctx->token.value.as_char == special)
 324		return (ISC_R_SUCCESS);
 325
 326	cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
 327	return (ISC_R_UNEXPECTEDTOKEN);
 328 cleanup:
 329	return (result);
 330}
 331
 332/*
 333 * Parse a required semicolon.  If it is not there, log
 334 * an error and increment the error count but continue
 335 * parsing.  Since the next token is pushed back,
 336 * care must be taken to make sure it is eventually
 337 * consumed or an infinite loop may result.
 338 */
 339static isc_result_t
 340parse_semicolon(cfg_parser_t *pctx) {
 341	isc_result_t result;
 342	CHECK(cfg_gettoken(pctx, 0));
 343	if (pctx->token.type == isc_tokentype_special &&
 344	    pctx->token.value.as_char == ';')
 345		return (ISC_R_SUCCESS);
 346
 347	cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
 348	cfg_ungettoken(pctx);
 349 cleanup:
 350	return (result);
 351}
 352
 353/*
 354 * Parse EOF, logging and returning an error if not there.
 355 */
 356static isc_result_t
 357parse_eof(cfg_parser_t *pctx) {
 358	isc_result_t result;
 359	CHECK(cfg_gettoken(pctx, 0));
 360
 361	if (pctx->token.type == isc_tokentype_eof)
 362		return (ISC_R_SUCCESS);
 363
 364	cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
 365	return (ISC_R_UNEXPECTEDTOKEN);
 366 cleanup:
 367	return (result);
 368}
 369
 370/* A list of files, used internally for pctx->files. */
 371
 372static cfg_type_t cfg_type_filelist = {
 373	"filelist", NULL, print_list, NULL, &cfg_rep_list,
 374	&cfg_type_qstring
 375};
 376
 377isc_result_t
 378cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
 379	isc_result_t result;
 380	cfg_parser_t *pctx;
 381	isc_lexspecials_t specials;
 382
 383	REQUIRE(mctx != NULL);
 384	REQUIRE(ret != NULL && *ret == NULL);
 385
 386	pctx = isc_mem_get(mctx, sizeof(*pctx));
 387	if (pctx == NULL)
 388		return (ISC_R_NOMEMORY);
 389
 390	result = isc_refcount_init(&pctx->references, 1);
 391	if (result != ISC_R_SUCCESS) {
 392		isc_mem_put(mctx, pctx, sizeof(*pctx));
 393		return (result);
 394	}
 395
 396	pctx->mctx = mctx;
 397	pctx->lctx = lctx;
 398	pctx->lexer = NULL;
 399	pctx->seen_eof = ISC_FALSE;
 400	pctx->ungotten = ISC_FALSE;
 401	pctx->errors = 0;
 402	pctx->warnings = 0;
 403	pctx->open_files = NULL;
 404	pctx->closed_files = NULL;
 405	pctx->line = 0;
 406	pctx->callback = NULL;
 407	pctx->callbackarg = NULL;
 408	pctx->token.type = isc_tokentype_unknown;
 409	pctx->flags = 0;
 410
 411	memset(specials, 0, sizeof(specials));
 412	specials['{'] = 1;
 413	specials['}'] = 1;
 414	specials[';'] = 1;
 415	specials['/'] = 1;
 416	specials['"'] = 1;
 417	specials['!'] = 1;
 418
 419	CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer));
 420
 421	isc_lex_setspecials(pctx->lexer, specials);
 422	isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C |
 423					 ISC_LEXCOMMENT_CPLUSPLUS |
 424					 ISC_LEXCOMMENT_SHELL));
 425
 426	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
 427	CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
 428
 429	*ret = pctx;
 430	return (ISC_R_SUCCESS);
 431
 432 cleanup:
 433	if (pctx->lexer != NULL)
 434		isc_lex_destroy(&pctx->lexer);
 435	CLEANUP_OBJ(pctx->open_files);
 436	CLEANUP_OBJ(pctx->closed_files);
 437	isc_mem_put(mctx, pctx, sizeof(*pctx));
 438	return (result);
 439}
 440
 441static isc_result_t
 442parser_openfile(cfg_parser_t *pctx, const char *filename) {
 443	isc_result_t result;
 444	cfg_listelt_t *elt = NULL;
 445	cfg_obj_t *stringobj = NULL;
 446
 447	result = isc_lex_openfile(pctx->lexer, filename);
 448	if (result != ISC_R_SUCCESS) {
 449		cfg_parser_error(pctx, 0, "open: %s: %s",
 450			     filename, isc_result_totext(result));
 451		goto cleanup;
 452	}
 453
 454	CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
 455	CHECK(create_listelt(pctx, &elt));
 456	elt->obj = stringobj;
 457	ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
 458
 459	return (ISC_R_SUCCESS);
 460 cleanup:
 461	CLEANUP_OBJ(stringobj);
 462	return (result);
 463}
 464
 465void
 466cfg_parser_setcallback(cfg_parser_t *pctx,
 467		       cfg_parsecallback_t callback,
 468		       void *arg)
 469{
 470	pctx->callback = callback;
 471	pctx->callbackarg = arg;
 472}
 473
 474/*
 475 * Parse a configuration using a pctx where a lexer has already
 476 * been set up with a source.
 477 */
 478static isc_result_t
 479parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
 480	isc_result_t result;
 481	cfg_obj_t *obj = NULL;
 482
 483	result = cfg_parse_obj(pctx, type, &obj);
 484
 485	if (pctx->errors != 0) {
 486		/* Errors have been logged. */
 487		if (result == ISC_R_SUCCESS)
 488			result = ISC_R_FAILURE;
 489		goto cleanup;
 490	}
 491
 492	if (result != ISC_R_SUCCESS) {
 493		/* Parsing failed but no errors have been logged. */
 494		cfg_parser_error(pctx, 0, "parsing failed");
 495		goto cleanup;
 496	}
 497
 498	CHECK(parse_eof(pctx));
 499
 500	*ret = obj;
 501	return (ISC_R_SUCCESS);
 502
 503 cleanup:
 504	CLEANUP_OBJ(obj);
 505	return (result);
 506}
 507
 508isc_result_t
 509cfg_parse_file(cfg_parser_t *pctx, const char *filename,
 510	       const cfg_type_t *type, cfg_obj_t **ret)
 511{
 512	isc_result_t result;
 513
 514	REQUIRE(filename != NULL);
 515
 516	CHECK(parser_openfile(pctx, filename));
 517	CHECK(parse2(pctx, type, ret));
 518 cleanup:
 519	return (result);
 520}
 521
 522
 523isc_result_t
 524cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
 525	const cfg_type_t *type, cfg_obj_t **ret)
 526{
 527	isc_result_t result;
 528	REQUIRE(buffer != NULL);
 529	CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
 530	CHECK(parse2(pctx, type, ret));
 531 cleanup:
 532	return (result);
 533}
 534
 535void
 536cfg_parser_attach(cfg_parser_t *src, cfg_parser_t **dest) {
 537	REQUIRE(src != NULL);
 538	REQUIRE(dest != NULL && *dest == NULL);
 539	isc_refcount_increment(&src->references, NULL);
 540	*dest = src;
 541}
 542
 543void
 544cfg_parser_destroy(cfg_parser_t **pctxp) {
 545	cfg_parser_t *pctx = *pctxp;
 546	unsigned int refs;
 547
 548	isc_refcount_decrement(&pctx->references, &refs);
 549	if (refs == 0) {
 550		isc_lex_destroy(&pctx->lexer);
 551		/*
 552		 * Cleaning up open_files does not
 553		 * close the files; that was already done
 554		 * by closing the lexer.
 555		 */
 556		CLEANUP_OBJ(pctx->open_files);
 557		CLEANUP_OBJ(pctx->closed_files);
 558		isc_mem_put(pctx->mctx, pctx, sizeof(*pctx));
 559	}
 560	*pctxp = NULL;
 561}
 562
 563/*
 564 * void
 565 */
 566isc_result_t
 567cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
 568	UNUSED(type);
 569	return (cfg_create_obj(pctx, &cfg_type_void, ret));
 570}
 571
 572void
 573cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
 574	UNUSED(pctx);
 575	UNUSED(obj);
 576}
 577
 578void
 579cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
 580	UNUSED(pctx);
 581	UNUSED(type);
 582}
 583
 584isc_boolean_t
 585cfg_obj_isvoid(const cfg_obj_t *obj) {
 586	REQUIRE(obj != NULL);
 587	return (ISC_TF(obj->type->rep == &cfg_rep_void));
 588}
 589
 590cfg_type_t cfg_type_void = {
 591	"void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
 592	NULL };
 593
 594
 595/*
 596 * uint32
 597 */
 598isc_result_t
 599cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
 600	isc_result_t result;
 601	cfg_obj_t *obj = NULL;
 602	UNUSED(type);
 603
 604	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
 605	if (pctx->token.type != isc_tokentype_number) {
 606		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
 607		return (ISC_R_UNEXPECTEDTOKEN);
 608	}
 609
 610	CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
 611
 612	obj->value.uint32 = pctx->token.value.as_ulong;
 613	*ret = obj;
 614 cleanup:
 615	return (result);
 616}
 617
 618void
 619cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
 620	cfg_print_chars(pctx, s, strlen(s));
 621}
 622
 623void
 624cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
 625	char buf[32];
 626	snprintf(buf, sizeof(buf), "%u", u);
 627	cfg_print_cstr(pctx, buf);
 628}
 629
 630void
 631cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
 632	cfg_print_rawuint(pctx, obj->value.uint32);
 633}
 634
 635isc_boolean_t
 636cfg_obj_isuint32(const cfg_obj_t *obj) {
 637	REQUIRE(obj != NULL);
 638	return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
 639}
 640
 641isc_uint32_t
 642cfg_obj_asuint32(const cfg_obj_t *obj) {
 643	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
 644	return (obj->value.uint32);
 645}
 646
 647cfg_type_t cfg_type_uint32 = {
 648	"integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
 649	&cfg_rep_uint32, NULL
 650};
 651
 652
 653/*
 654 * uint64
 655 */
 656isc_boolean_t
 657cfg_obj_isuint64(const cfg_obj_t *obj) {
 658	REQUIRE(obj != NULL);
 659	return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
 660}
 661
 662isc_uint64_t
 663cfg_obj_asuint64(const cfg_obj_t *obj) {
 664	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
 665	return (obj->value.uint64);
 666}
 667
 668void
 669cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
 670	char buf[32];
 671	snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
 672		 obj->value.uint64);
 673	cfg_print_cstr(pctx, buf);
 674}
 675
 676cfg_type_t cfg_type_uint64 = {
 677	"64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
 678	&cfg_rep_uint64, NULL
 679};
 680
 681/*
 682 * qstring (quoted string), ustring (unquoted string), astring
 683 * (any string)
 684 */
 685
 686/* Create a string object from a null-terminated C string. */
 687static isc_result_t
 688create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
 689	      cfg_obj_t **ret)
 690{
 691	isc_result_t result;
 692	cfg_obj_t *obj = NULL;
 693	int len;
 694
 695	CHECK(cfg_create_obj(pctx, type, &obj));
 696	len = strlen(contents);
 697	obj->value.string.length = len;
 698	obj->value.string.base = isc_mem_get(pctx->mctx, len + 1);
 699	if (obj->value.string.base == 0) {
 700		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
 701		return (ISC_R_NOMEMORY);
 702	}
 703	memcpy(obj->value.string.base, contents, len);
 704	obj->value.string.base[len] = '\0';
 705
 706	*ret = obj;
 707 cleanup:
 708	return (result);
 709}
 710
 711isc_result_t
 712cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
 713	isc_result_t result;
 714	UNUSED(type);
 715
 716	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
 717	if (pctx->token.type != isc_tokentype_qstring) {
 718		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
 719		return (ISC_R_UNEXPECTEDTOKEN);
 720	}
 721	return (create_string(pctx,
 722			      TOKEN_STRING(pctx),
 723			      &cfg_type_qstring,
 724			      ret));
 725 cleanup:
 726	return (result);
 727}
 728
 729static isc_result_t
 730parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
 731	isc_result_t result;
 732	UNUSED(type);
 733
 734	CHECK(cfg_gettoken(pctx, 0));
 735	if (pctx->token.type != isc_tokentype_string) {
 736		cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
 737		return (ISC_R_UNEXPECTEDTOKEN);
 738	}
 739	return (create_string(pctx,
 740			      TOKEN_STRING(pctx),
 741			      &cfg_type_ustring,
 742			      ret));
 743 cleanup:
 744	return (result);
 745}
 746
 747isc_result_t
 748cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
 749		  cfg_obj_t **ret)
 750{
 751	isc_result_t result;
 752	UNUSED(type);
 753
 754	CHECK(cfg_getstringtoken(pctx));
 755	return (create_string(pctx,
 756			      TOKEN_STRING(pctx),
 757			      &cfg_type_qstring,
 758			      ret));
 759 cleanup:
 760	return (result);
 761}
 762
 763isc_boolean_t
 764cfg_is_enum(const char *s, const char *const *enums) {
 765	const char * const *p;
 766	for (p = enums; *p != NULL; p++) {
 767		if (strcasecmp(*p, s) == 0)
 768			return (ISC_TRUE);
 769	}
 770	return (ISC_FALSE);
 771}
 772
 773static isc_result_t
 774check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
 775	const char *s = obj->value.string.base;
 776	if (cfg_is_enum(s, enums))
 777		return (ISC_R_SUCCESS);
 778	cfg_parser_error(pctx, 0, "'%s' unexpected", s);
 779	return (ISC_R_UNEXPECTEDTOKEN);
 780}
 781
 782isc_result_t
 783cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
 784	isc_result_t result;
 785	cfg_obj_t *obj = NULL;
 786	CHECK(parse_ustring(pctx, NULL, &obj));
 787	CHECK(check_enum(pctx, obj, type->of));
 788	*ret = obj;
 789	return (ISC_R_SUCCESS);
 790 cleanup:
 791	CLEANUP_OBJ(obj);
 792	return (result);
 793}
 794
 795void
 796cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
 797	const char * const *p;
 798	cfg_print_chars(pctx, "( ", 2);
 799	for (p = type->of; *p != NULL; p++) {
 800		cfg_print_cstr(pctx, *p);
 801		if (p[1] != NULL)
 802			cfg_print_chars(pctx, " | ", 3);
 803	}
 804	cfg_print_chars(pctx, " )", 2);
 805}
 806
 807void
 808cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
 809	cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
 810}
 811
 812static void
 813print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
 814	cfg_print_chars(pctx, "\"", 1);
 815	cfg_print_ustring(pctx, obj);
 816	cfg_print_chars(pctx, "\"", 1);
 817}
 818
 819static void
 820free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
 821	isc_mem_put(pctx->mctx, obj->value.string.base,
 822		    obj->value.string.length + 1);
 823}
 824
 825isc_boolean_t
 826cfg_obj_isstring(const cfg_obj_t *obj) {
 827	REQUIRE(obj != NULL);
 828	return (ISC_TF(obj->type->rep == &cfg_rep_string));
 829}
 830
 831const char *
 832cfg_obj_asstring(const cfg_obj_t *obj) {
 833	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
 834	return (obj->value.string.base);
 835}
 836
 837/* Quoted string only */
 838cfg_type_t cfg_type_qstring = {
 839	"quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal,
 840	&cfg_rep_string, NULL
 841};
 842
 843/* Unquoted string only */
 844cfg_type_t cfg_type_ustring = {
 845	"string", parse_ustring, cfg_print_ustring, cfg_doc_terminal,
 846	&cfg_rep_string, NULL
 847};
 848
 849/* Any string (quoted or unquoted); printed with quotes */
 850cfg_type_t cfg_type_astring = {
 851	"string", cfg_parse_astring, print_qstring, cfg_doc_terminal,
 852	&cfg_rep_string, NULL
 853};
 854
 855/*
 856 * Booleans
 857 */
 858
 859isc_boolean_t
 860cfg_obj_isboolean(const cfg_obj_t *obj) {
 861	REQUIRE(obj != NULL);
 862	return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
 863}
 864
 865isc_boolean_t
 866cfg_obj_asboolean(const cfg_obj_t *obj) {
 867	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
 868	return (obj->value.boolean);
 869}
 870
 871isc_result_t
 872cfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
 873{
 874	isc_result_t result;
 875	isc_boolean_t value;
 876	cfg_obj_t *obj = NULL;
 877	UNUSED(type);
 878
 879	result = cfg_gettoken(pctx, 0);
 880	if (result != ISC_R_SUCCESS)
 881		return (result);
 882
 883	if (pctx->token.type != isc_tokentype_string)
 884		goto bad_boolean;
 885
 886	if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
 887	    (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
 888	    (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
 889		value = ISC_TRUE;
 890	} else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
 891		   (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
 892		   (strcmp(TOKEN_STRING(pctx), "0") == 0)) {
 893		value = ISC_FALSE;
 894	} else {
 895		goto bad_boolean;
 896	}
 897
 898	CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
 899	obj->value.boolean = value;
 900	*ret = obj;
 901	return (result);
 902
 903 bad_boolean:
 904	cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
 905	return (ISC_R_UNEXPECTEDTOKEN);
 906
 907 cleanup:
 908	return (result);
 909}
 910
 911void
 912cfg_print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
 913	if (obj->value.boolean)
 914		cfg_print_chars(pctx, "yes", 3);
 915	else
 916		cfg_print_chars(pctx, "no", 2);
 917}
 918
 919cfg_type_t cfg_type_boolean = {
 920	"boolean", cfg_parse_boolean, cfg_print_boolean, cfg_doc_terminal,
 921	&cfg_rep_boolean, NULL
 922};
 923
 924/*
 925 * Lists.
 926 */
 927
 928isc_result_t
 929cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
 930	isc_result_t result;
 931	CHECK(cfg_create_obj(pctx, type, obj));
 932	ISC_LIST_INIT((*obj)->value.list);
 933 cleanup:
 934	return (result);
 935}
 936
 937static isc_result_t
 938create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
 939	cfg_listelt_t *elt;
 940	elt = isc_mem_get(pctx->mctx, sizeof(*elt));
 941	if (elt == NULL)
 942		return (ISC_R_NOMEMORY);
 943	elt->obj = NULL;
 944	ISC_LINK_INIT(elt, link);
 945	*eltp = elt;
 946	return (ISC_R_SUCCESS);
 947}
 948
 949static void
 950free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
 951	cfg_obj_destroy(pctx, &elt->obj);
 952	isc_mem_put(pctx->mctx, elt, sizeof(*elt));
 953}
 954
 955static void
 956free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
 957	cfg_listelt_t *elt, *next;
 958	for (elt = ISC_LIST_HEAD(obj->value.list);
 959	     elt != NULL;
 960	     elt = next)
 961	{
 962		next = ISC_LIST_NEXT(elt, link);
 963		free_list_elt(pctx, elt);
 964	}
 965}
 966
 967isc_result_t
 968cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
 969		  cfg_listelt_t **ret)
 970{
 971	isc_result_t result;
 972	cfg_listelt_t *elt = NULL;
 973	cfg_obj_t *value = NULL;
 974
 975	CHECK(create_listelt(pctx, &elt));
 976
 977	result = cfg_parse_obj(pctx, elttype, &value);
 978	if (result != ISC_R_SUCCESS)
 979		goto cleanup;
 980
 981	elt->obj = value;
 982
 983	*ret = elt;
 984	return (ISC_R_SUCCESS);
 985
 986 cleanup:
 987	isc_mem_put(pctx->mctx, elt, sizeof(*elt));
 988	return (result);
 989}
 990
 991/*
 992 * Parse a homogeneous list whose elements are of type 'elttype'
 993 * and where each element is terminated by a semicolon.
 994 */
 995static isc_result_t
 996parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
 997{
 998	cfg_obj_t *listobj = NULL;
 999	const cfg_type_t *listof = listtype->of;
1000	isc_result_t result;
1001	cfg_listelt_t *elt = NULL;
1002
1003	CHECK(cfg_create_list(pctx, listtype, &listobj));
1004
1005	for (;;) {
1006		CHECK(cfg_peektoken(pctx, 0));
1007		if (pctx->token.type == isc_tokentype_special &&
1008		    pctx->token.value.as_char == /*{*/ '}')
1009			break;
1010		CHECK(cfg_parse_listelt(pctx, listof, &elt));
1011		CHECK(parse_semicolon(pctx));
1012		ISC_LIST_APPEND(listobj->value.list, elt, link);
1013		elt = NULL;
1014	}
1015	*ret = listobj;
1016	return (ISC_R_SUCCESS);
1017
1018 cleanup:
1019	if (elt != NULL)
1020		free_list_elt(pctx, elt);
1021	CLEANUP_OBJ(listobj);
1022	return (result);
1023}
1024
1025static void
1026print_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1027	const cfg_list_t *list = &obj->value.list;
1028	const cfg_listelt_t *elt;
1029
1030	for (elt = ISC_LIST_HEAD(*list);
1031	     elt != NULL;
1032	     elt = ISC_LIST_NEXT(elt, link)) {
1033		print_indent(pctx);
1034		cfg_print_obj(pctx, elt->obj);
1035		cfg_print_chars(pctx, ";\n", 2);
1036	}
1037}
1038
1039isc_result_t
1040cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
1041		     cfg_obj_t **ret)
1042{
1043	isc_result_t result;
1044	CHECK(cfg_parse_special(pctx, '{'));
1045	CHECK(parse_list(pctx, type, ret));
1046	CHECK(cfg_parse_special(pctx, '}'));
1047 cleanup:
1048	return (result);
1049}
1050
1051void
1052cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1053	print_open(pctx);
1054	print_list(pctx, obj);
1055	print_close(pctx);
1056}
1057
1058void
1059cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
1060	cfg_print_chars(pctx, "{ ", 2);
1061	cfg_doc_obj(pctx, type->of);
1062	cfg_print_chars(pctx, "; ... }", 7);
1063}
1064
1065/*
1066 * Parse a homogeneous list whose elements are of type 'elttype'
1067 * and where elements are separated by space.  The list ends
1068 * before the first semicolon.
1069 */
1070isc_result_t
1071cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
1072		    cfg_obj_t **ret)
1073{
1074	cfg_obj_t *listobj = NULL;
1075	const cfg_type_t *listof = listtype->of;
1076	isc_result_t result;
1077
1078	CHECK(cfg_create_list(pctx, listtype, &listobj));
1079
1080	for (;;) {
1081		cfg_listelt_t *elt = NULL;
1082
1083		CHECK(cfg_peektoken(pctx, 0));
1084		if (pctx->token.type == isc_tokentype_special &&
1085		    pctx->token.value.as_char == ';')
1086			break;
1087		CHECK(cfg_parse_listelt(pctx, listof, &elt));
1088		ISC_LIST_APPEND(listobj->value.list, elt, link);
1089	}
1090	*ret = listobj;
1091	return (ISC_R_SUCCESS);
1092
1093 cleanup:
1094	CLEANUP_OBJ(listobj);
1095	return (result);
1096}
1097
1098void
1099cfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1100	const cfg_list_t *list = &obj->value.list;
1101	const cfg_listelt_t *elt;
1102
1103	for (elt = ISC_LIST_HEAD(*list);
1104	     elt != NULL;
1105	     elt = ISC_LIST_NEXT(elt, link)) {
1106		cfg_print_obj(pctx, elt->obj);
1107		if (ISC_LIST_NEXT(elt, link) != NULL)
1108			cfg_print_chars(pctx, " ", 1);
1109	}
1110}
1111
1112isc_boolean_t
1113cfg_obj_islist(const cfg_obj_t *obj) {
1114	REQUIRE(obj != NULL);
1115	return (ISC_TF(obj->type->rep == &cfg_rep_list));
1116}
1117
1118const cfg_listelt_t *
1119cfg_list_first(const cfg_obj_t *obj) {
1120	REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
1121	if (obj == NULL)
1122		return (NULL);
1123	return (ISC_LIST_HEAD(obj->value.list));
1124}
1125
1126const cfg_listelt_t *
1127cfg_list_next(const cfg_listelt_t *elt) {
1128	REQUIRE(elt != NULL);
1129	return (ISC_LIST_NEXT(elt, link));
1130}
1131
1132/*
1133 * Return the length of a list object.  If obj is NULL or is not
1134 * a list, return 0.
1135 */
1136unsigned int
1137cfg_list_length(const cfg_obj_t *obj, isc_boolean_t recurse) {
1138	const cfg_listelt_t *elt;
1139	unsigned int count = 0;
1140
1141	if (obj == NULL || !cfg_obj_islist(obj))
1142		return (0U);
1143	for (elt = cfg_list_first(obj);
1144	     elt != NULL;
1145	     elt = cfg_list_next(elt)) {
1146		if (recurse && cfg_obj_islist(elt->obj)) {
1147			count += cfg_list_length(elt->obj, recurse);
1148		} else {
1149			count++;
1150		}
1151	}
1152	return (count);
1153}
1154
1155cfg_obj_t *
1156cfg_listelt_value(const cfg_listelt_t *elt) {
1157	REQUIRE(elt != NULL);
1158	return (elt->obj);
1159}
1160
1161/*
1162 * Maps.
1163 */
1164
1165/*
1166 * Parse a map body.  That's something like
1167 *
1168 *   "foo 1; bar { glub; }; zap true; zap false;"
1169 *
1170 * i.e., a sequence of option names followed by values and
1171 * terminated by semicolons.  Used for the top level of
1172 * the named.conf syntax, as well as for the body of the
1173 * options, view, zone, and other statements.
1174 */
1175isc_result_t
1176cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1177{
1178	const cfg_clausedef_t * const *clausesets = type->of;
1179	isc_result_t result;
1180	const cfg_clausedef_t * const *clauseset;
1181	const cfg_clausedef_t *clause;
1182	cfg_obj_t *value = NULL;
1183	cfg_obj_t *obj = NULL;
1184	cfg_obj_t *eltobj = NULL;
1185	cfg_obj_t *includename = NULL;
1186	isc_symvalue_t symval;
1187	cfg_list_t *list = NULL;
1188
1189	CHECK(create_map(pctx, type, &obj));
1190
1191	obj->value.map.clausesets = clausesets;
1192
1193	for (;;) {
1194		cfg_listelt_t *elt;
1195
1196	redo:
1197		/*
1198		 * Parse the option name and see if it is known.
1199		 */
1200		CHECK(cfg_gettoken(pctx, 0));
1201
1202		if (pctx->token.type != isc_tokentype_string) {
1203			cfg_ungettoken(pctx);
1204			break;
1205		}
1206
1207		/*
1208		 * We accept "include" statements wherever a map body
1209		 * clause can occur.
1210		 */
1211		if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
1212			/*
1213			 * Turn the file name into a temporary configuration
1214			 * object just so that it is not overwritten by the
1215			 * semicolon token.
1216			 */
1217			CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
1218			CHECK(parse_semicolon(pctx));
1219			CHECK(parser_openfile(pctx, includename->
1220					      value.string.base));
1221			 cfg_obj_destroy(pctx, &includename);
1222			 goto redo;
1223		}
1224
1225		clause = NULL;
1226		for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
1227			for (clause = *clauseset;
1228			     clause->name != NULL;
1229			     clause++) {
1230				if (strcasecmp(TOKEN_STRING(pctx),
1231					   clause->name) == 0)
1232					goto done;
1233			}
1234		}
1235	done:
1236		if (clause == NULL || clause->name == NULL) {
1237			cfg_parser_error(pctx, CFG_LOG_NOPREP, "unknown option");
1238			/*
1239			 * Try to recover by parsing this option as an unknown
1240			 * option and discarding it.
1241			 */
1242			CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, &eltobj));
1243			cfg_obj_destroy(pctx, &eltobj);
1244			CHECK(parse_semicolon(pctx));
1245			continue;
1246		}
1247
1248		/* Clause is known. */
1249
1250		/* Issue warnings if appropriate */
1251		if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0)
1252			cfg_parser_warning(pctx, 0, "option '%s' is obsolete",
1253				       clause->name);
1254		if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0)
1255			cfg_parser_warning(pctx, 0, "option '%s' is "
1256				       "not implemented", clause->name);
1257		if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0)
1258			cfg_parser_warning(pctx, 0, "option '%s' is "
1259				       "not implemented", clause->name);
1260
1261		if ((clause->flags & CFG_CLAUSEFLAG_NOTCONFIGURED) != 0) {
1262			cfg_parser_warning(pctx, 0, "option '%s' is not "
1263					   "configured", clause->name);
1264			result = ISC_R_FAILURE;
1265			goto cleanup;
1266		}
1267
1268		/*
1269		 * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
1270		 * set here - we need to log the *lack* of such an option,
1271		 * not its presence.
1272		 */
1273
1274		/* See if the clause already has a value; if not create one. */
1275		result = isc_symtab_lookup(obj->value.map.symtab,
1276					   clause->name, 0, &symval);
1277
1278		if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
1279			/* Multivalued clause */
1280			cfg_obj_t *listobj = NULL;
1281			if (result == ISC_R_NOTFOUND) {
1282				CHECK(cfg_create_list(pctx,
1283						  &cfg_type_implicitlist,
1284						  &listobj));
1285				symval.as_pointer = listobj;
1286				result = isc_symtab_define(obj->value.
1287						   map.symtab,
1288						   clause->name,
1289						   1, symval,
1290						   isc_symexists_reject);
1291				if (result != ISC_R_SUCCESS) {
1292					cfg_parser_error(pctx, CFG_LOG_NEAR,
1293						     "isc_symtab_define(%s) "
1294						     "failed", clause->name);
1295					isc_mem_put(pctx->mctx, list,
1296						    sizeof(cfg_list_t));
1297					goto cleanup;
1298				}
1299			} else {
1300				INSIST(result == ISC_R_SUCCESS);
1301				listobj = symval.as_pointer;
1302			}
1303
1304			elt = NULL;
1305			CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
1306			CHECK(parse_semicolon(pctx));
1307
1308			ISC_LIST_APPEND(listobj->value.list, elt, link);
1309		} else {
1310			/* Single-valued clause */
1311			if (result == ISC_R_NOTFOUND) {
1312				isc_boolean_t callback =
1313					ISC_TF((clause->flags &
1314						CFG_CLAUSEFLAG_CALLBACK) != 0);
1315				CHECK(parse_symtab_elt(pctx, clause->name,
1316						       clause->type,
1317						       obj->value.map.symtab,
1318						       callback));
1319				CHECK(parse_semicolon(pctx));
1320			} else if (result == ISC_R_SUCCESS) {
1321				cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
1322					     clause->name);
1323				result = ISC_R_EXISTS;
1324				goto cleanup;
1325			} else {
1326				cfg_parser_error(pctx, CFG_LOG_NEAR,
1327					     "isc_symtab_define() failed");
1328				goto cleanup;
1329			}
1330		}
1331	}
1332
1333
1334	*ret = obj;
1335	return (ISC_R_SUCCESS);
1336
1337 cleanup:
1338	CLEANUP_OBJ(value);
1339	CLEANUP_OBJ(obj);
1340	CLEANUP_OBJ(eltobj);
1341	CLEANUP_OBJ(includename);
1342	return (result);
1343}
1344
1345static isc_result_t
1346parse_symtab_elt(cfg_parser_t *pctx, const char *name,
1347		 cfg_type_t *elttype, isc_symtab_t *symtab,
1348		 isc_boolean_t callback)
1349{
1350	isc_result_t result;
1351	cfg_obj_t *obj = NULL;
1352	isc_symvalue_t symval;
1353
1354	CHECK(cfg_parse_obj(pctx, elttype, &obj));
1355
1356	if (callback && pctx->callback != NULL)
1357		CHECK(pctx->callback(name, obj, pctx->callbackarg));
1358
1359	symval.as_pointer = obj;
1360	CHECK(isc_symtab_define(symtab, name,
1361				1, symval,
1362				isc_symexists_reject));
1363	return (ISC_R_SUCCESS);
1364
1365 cleanup:
1366	CLEANUP_OBJ(obj);
1367	return (result);
1368}
1369
1370/*
1371 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
1372 */
1373isc_result_t
1374cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1375	isc_result_t result;
1376	CHECK(cfg_parse_special(pctx, '{'));
1377	CHECK(cfg_parse_mapbody(pctx, type, ret));
1378	CHECK(cfg_parse_special(pctx, '}'));
1379 cleanup:
1380	return (result);
1381}
1382
1383/*
1384 * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
1385 */
1386static isc_result_t
1387parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type,
1388		    cfg_obj_t **ret)
1389{
1390	isc_result_t result;
1391	cfg_obj_t *idobj = NULL;
1392	cfg_obj_t *mapobj = NULL;
1393
1394	CHECK(cfg_parse_obj(pctx, nametype, &idobj));
1395	CHECK(cfg_parse_map(pctx, type, &mapobj));
1396	mapobj->value.map.id = idobj;
1397	idobj = NULL;
1398	*ret = mapobj;
1399 cleanup:
1400	CLEANUP_OBJ(idobj);
1401	return (result);
1402}
1403
1404/*
1405 * Parse a map identified by a string name.  E.g., "name { foo 1; }".
1406 * Used for the "key" and "channel" statements.
1407 */
1408isc_result_t
1409cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1410	return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
1411}
1412
1413/*
1414 * Parse a map identified by a network address.
1415 * Used to be used for the "server" statement.
1416 */
1417isc_result_t
1418cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1419	return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
1420}
1421
1422/*
1423 * Parse a map identified by a network prefix.
1424 * Used for the "server" statement.
1425 */
1426isc_result_t
1427cfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1428	return (parse_any_named_map(pctx, &cfg_type_netprefix, type, ret));
1429}
1430
1431void
1432cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1433	isc_result_t result = ISC_R_SUCCESS;
1434
1435	const cfg_clausedef_t * const *clauseset;
1436
1437	for (clauseset = obj->value.map.clausesets;
1438	     *clauseset != NULL;
1439	     clauseset++)
1440	{
1441		isc_symvalue_t symval;
1442		const cfg_clausedef_t *clause;
1443
1444		for (clause = *clauseset;
1445		     clause->name != NULL;
1446		     clause++) {
1447			result = isc_symtab_lookup(obj->value.map.symtab,
1448						   clause->name, 0, &symval);
1449			if (result == ISC_R_SUCCESS) {
1450				cfg_obj_t *obj = symval.as_pointer;
1451				if (obj->type == &cfg_type_implicitlist) {
1452					/* Multivalued. */
1453					cfg_list_t *list = &obj->value.list;
1454					cfg_listelt_t *elt;
1455					for (elt = ISC_LIST_HEAD(*list);
1456					     elt != NULL;
1457					     elt = ISC_LIST_NEXT(elt, link)) {
1458						print_indent(pctx);
1459						cfg_print_cstr(pctx, clause->name);
1460						cfg_print_chars(pctx, " ", 1);
1461						cfg_print_obj(pctx, elt->obj);
1462						cfg_print_chars(pctx, ";\n", 2);
1463					}
1464				} else {
1465					/* Single-valued. */
1466					print_indent(pctx);
1467					cfg_print_cstr(pctx, clause->name);
1468					cfg_print_chars(pctx, " ", 1);
1469					cfg_print_obj(pctx, obj);
1470					cfg_print_chars(pctx, ";\n", 2);
1471				}
1472			} else if (result == ISC_R_NOTFOUND) {
1473				; /* do nothing */
1474			} else {
1475				INSIST(0);
1476			}
1477		}
1478	}
1479}
1480
1481void
1482cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
1483	const cfg_clausedef_t * const *clauseset;
1484	const cfg_clausedef_t *clause;
1485
1486	for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1487		for (clause = *clauseset;
1488		     clause->name != NULL;
1489		     clause++) {
1490			cfg_print_cstr(pctx, clause->name);
1491			cfg_print_chars(pctx, " ", 1);
1492			cfg_doc_obj(pctx, clause->type);
1493			cfg_print_chars(pctx, ";", 1);
1494			/* XXX print flags here? */
1495			cfg_print_chars(pctx, "\n\n", 2);
1496		}
1497	}
1498}
1499
1500static struct flagtext {
1501	unsigned int flag;
1502	const char *text;
1503} flagtexts[] = {
1504	{ CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
1505	{ CFG_CLAUSEFLAG_NYI, "not yet implemented" },
1506	{ CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
1507	{ CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
1508	{ CFG_CLAUSEFLAG_TESTONLY, "test only" },
1509	{ CFG_CLAUSEFLAG_NOTCONFIGURED, "not configured" },
1510	{ 0, NULL }
1511};
1512
1513void
1514cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1515	if (obj->value.map.id != NULL) {
1516		cfg_print_obj(pctx, obj->value.map.id);
1517		cfg_print_chars(pctx, " ", 1);
1518	}
1519	print_open(pctx);
1520	cfg_print_mapbody(pctx, obj);
1521	print_close(pctx);
1522}
1523
1524static void
1525print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
1526	struct flagtext *p;
1527	isc_boolean_t first = ISC_TRUE;
1528	for (p = flagtexts; p->flag != 0; p++) {
1529		if ((flags & p->flag) != 0) {
1530			if (first)
1531				cfg_print_chars(pctx, " // ", 4);
1532			else
1533				cfg_print_chars(pctx, ", ", 2);
1534			cfg_print_cstr(pctx, p->text);
1535			first = ISC_FALSE;
1536		}
1537	}
1538}
1539
1540void
1541cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
1542	const cfg_clausedef_t * const *clauseset;
1543	const cfg_clausedef_t *clause;
1544
1545	if (type->parse == cfg_parse_named_map) {
1546		cfg_doc_obj(pctx, &cfg_type_astring);
1547		cfg_print_chars(pctx, " ", 1);
1548	} else if (type->parse == cfg_parse_addressed_map) {
1549		cfg_doc_obj(pctx, &cfg_type_netaddr);
1550		cfg_print_chars(pctx, " ", 1);
1551	} else if (type->parse == cfg_parse_netprefix_map) {
1552		cfg_doc_obj(pctx, &cfg_type_netprefix);
1553		cfg_print_chars(pctx, " ", 1);
1554	}
1555
1556	print_open(pctx);
1557
1558	for (clauseset = type->of; *clauseset != NULL; clauseset++) {
1559		for (clause = *clauseset;
1560		     clause->name != NULL;
1561		     clause++) {
1562			print_indent(pctx);
1563			cfg_print_cstr(pctx, clause->name);
1564			if (clause->type->print != cfg_print_void)
1565				cfg_print_chars(pctx, " ", 1);
1566			cfg_doc_obj(pctx, clause->type);
1567			cfg_print_chars(pctx, ";", 1);
1568			print_clause_flags(pctx, clause->flags);
1569			cfg_print_chars(pctx, "\n", 1);
1570		}
1571	}
1572	print_close(pctx);
1573}
1574
1575isc_boolean_t
1576cfg_obj_ismap(const cfg_obj_t *obj) {
1577	REQUIRE(obj != NULL);
1578	return (ISC_TF(obj->type->rep == &cfg_rep_map));
1579}
1580
1581isc_result_t
1582cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
1583	isc_result_t result;
1584	isc_symvalue_t val;
1585	const cfg_map_t *map;
1586
1587	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1588	REQUIRE(name != NULL);
1589	REQUIRE(obj != NULL && *obj == NULL);
1590
1591	map = &mapobj->value.map;
1592
1593	result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
1594	if (result != ISC_R_SUCCESS)
1595		return (result);
1596	*obj = val.as_pointer;
1597	return (ISC_R_SUCCESS);
1598}
1599
1600const cfg_obj_t *
1601cfg_map_getname(const cfg_obj_t *mapobj) {
1602	REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
1603	return (mapobj->value.map.id);
1604}
1605
1606
1607/* Parse an arbitrary token, storing its raw text representation. */
1608static isc_result_t
1609parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1610	cfg_obj_t *obj = NULL;
1611	isc_result_t result;
1612	isc_region_t r;
1613
1614	UNUSED(type);
1615
1616	CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
1617	CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
1618	if (pctx->token.type == isc_tokentype_eof) {
1619		cfg_ungettoken(pctx);
1620		result = ISC_R_EOF;
1621		goto cleanup;
1622	}
1623
1624	isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
1625
1626	obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
1627	if (obj->value.string.base == NULL) {
1628		result = ISC_R_NOMEMORY;
1629		goto cleanup;
1630	}
1631	obj->value.string.length = r.length;
1632	memcpy(obj->value.string.base, r.base, r.length);
1633	obj->value.string.base[r.length] = '\0';
1634	*ret = obj;
1635	return (result);
1636
1637 cleanup:
1638	if (obj != NULL)
1639		isc_mem_put(pctx->mctx, obj, sizeof(*obj));
1640	return (result);
1641}
1642
1643cfg_type_t cfg_type_token = {
1644	"token", parse_token, cfg_print_ustring, cfg_doc_terminal,
1645	&cfg_rep_string, NULL
1646};
1647
1648/*
1649 * An unsupported option.  This is just a list of tokens with balanced braces
1650 * ending in a semicolon.
1651 */
1652
1653static isc_result_t
1654parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1655	cfg_obj_t *listobj = NULL;
1656	isc_result_t result;
1657	int braces = 0;
1658
1659	CHECK(cfg_create_list(pctx, type, &listobj));
1660
1661	for (;;) {
1662		cfg_listelt_t *elt = NULL;
1663
1664		CHECK(cfg_peektoken(pctx, 0));
1665		if (pctx->token.type == isc_tokentype_special) {
1666			if (pctx->token.value.as_char == '{')
1667				braces++;
1668			else if (pctx->token.value.as_char == '}')
1669				braces--;
1670			else if (pctx->token.value.as_char == ';')
1671				if (braces == 0)
1672					break;
1673		}
1674		if (pctx->token.type == isc_tokentype_eof || braces < 0) {
1675			cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
1676			result = ISC_R_UNEXPECTEDTOKEN;
1677			goto cleanup;
1678		}
1679
1680		CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
1681		ISC_LIST_APPEND(listobj->value.list, elt, link);
1682	}
1683	INSIST(braces == 0);
1684	*ret = listobj;
1685	return (ISC_R_SUCCESS);
1686
1687 cleanup:
1688	CLEANUP_OBJ(listobj);
1689	return (result);
1690}
1691
1692cfg_type_t cfg_type_unsupported = {
1693	"unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal,
1694	&cfg_rep_list, NULL
1695};
1696
1697/*
1698 * Try interpreting the current token as a network address.
1699 *
1700 * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
1701 * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set.  The
1702 * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
1703 * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
1704 * and the IPv6 wildcard address otherwise.
1705 */
1706static isc_result_t
1707token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1708	char *s;
1709	struct in_addr in4a;
1710	struct in6_addr in6a;
1711
1712	if (pctx->token.type != isc_tokentype_string)
1713		return (ISC_R_UNEXPECTEDTOKEN);
1714
1715	s = TOKEN_STRING(pctx);
1716	if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
1717		if ((flags & CFG_ADDR_V4OK) != 0) {
1718			isc_netaddr_any(na);
1719			return (ISC_R_SUCCESS);
1720		} else if ((flags & CFG_ADDR_V6OK) != 0) {
1721			isc_netaddr_any6(na);
1722			return (ISC_R_SUCCESS);
1723		} else {
1724			INSIST(0);
1725		}
1726	} else {
1727		if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
1728			if (inet_pton(AF_INET, s, &in4a) == 1) {
1729				isc_netaddr_fromin(na, &in4a);
1730				return (ISC_R_SUCCESS);
1731			}
1732		}
1733		if ((flags & CFG_ADDR_V4PREFIXOK) != 0 &&
1734		    strlen(s) <= 15U) {
1735			char buf[64];
1736			int i;
1737
1738			strcpy(buf, s);
1739			for (i = 0; i < 3; i++) {
1740				strcat(buf, ".0");
1741				if (inet_pton(AF_INET, buf, &in4a) == 1) {
1742					isc_netaddr_fromin(na, &in4a);
1743					return (ISC_R_SUCCESS);
1744				}
1745			}
1746		}
1747		if ((flags & CFG_ADDR_V6OK) != 0 &&
1748		    strlen(s) <= 127U) {
1749			char buf[128]; /* see lib/bind9/getaddresses.c */
1750			char *d; /* zone delimiter */
1751			isc_uint32_t zone = 0; /* scope zone ID */
1752
1753			strcpy(buf, s);
1754			d = strchr(buf, '%');
1755			if (d != NULL)
1756				*d = '\0';
1757
1758			if (inet_pton(AF_INET6, buf, &in6a) == 1) {
1759				if (d != NULL) {
1760#ifdef ISC_PLATFORM_HAVESCOPEID
1761					isc_result_t result;
1762
1763					result = isc_netscope_pton(AF_INET6,
1764								   d + 1,
1765								   &in6a,
1766								   &zone);
1767					if (result != ISC_R_SUCCESS)
1768						return (result);
1769#else
1770				return (ISC_R_BADADDRESSFORM);
1771#endif
1772				}
1773
1774				isc_netaddr_fromin6(na, &in6a);
1775				isc_netaddr_setzone(na, zone);
1776				return (ISC_R_SUCCESS);
1777			}
1778		}
1779	}
1780	return (ISC_R_UNEXPECTEDTOKEN);
1781}
1782
1783isc_result_t
1784cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
1785	isc_result_t result;
1786	const char *wild = "";
1787	const char *prefix = "";
1788
1789	CHECK(cfg_gettoken(pctx, 0));
1790	result = token_addr(pctx, flags, na);
1791	if (result == ISC_R_UNEXPECTEDTOKEN) {
1792		if ((flags & CFG_ADDR_WILDOK) != 0)
1793			wild = " or '*'";
1794		if ((flags & CFG_ADDR_V4PREFIXOK) != 0)
1795			wild = " or IPv4 prefix";
1796		if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V4OK)
1797			cfg_parser_error(pctx, CFG_LOG_NEAR,
1798					 "expected IPv4 address%s%s",
1799					 prefix, wild);
1800		else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK)
1801			cfg_parser_error(pctx, CFG_LOG_NEAR,
1802					 "expected IPv6 address%s%s",
1803					 prefix, wild);
1804		else
1805			cfg_parser_error(pctx, CFG_LOG_NEAR,
1806					 "expected IP address%s%s",
1807					 prefix, wild);
1808	}
1809 cleanup:
1810	return (result);
1811}
1812
1813isc_boolean_t
1814cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
1815	isc_result_t result;
1816	isc_netaddr_t na_dummy;
1817	result = token_addr(pctx, flags, &na_dummy);
1818	return (ISC_TF(result == ISC_R_SUCCESS));
1819}
1820
1821isc_result_t
1822cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
1823	isc_result_t result;
1824
1825	CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1826
1827	if ((flags & CFG_ADDR_WILDOK) != 0 &&
1828	    pctx->token.type == isc_tokentype_string &&
1829	    strcmp(TOKEN_STRING(pctx), "*") == 0) {
1830		*port = 0;
1831		return (ISC_R_SUCCESS);
1832	}
1833	if (pctx->token.type != isc_tokentype_number) {
1834		cfg_parser_error(pctx, CFG_LOG_NEAR,
1835			     "expected port number or '*'");
1836		return (ISC_R_UNEXPECTEDTOKEN);
1837	}
1838	if (pctx->token.value.as_ulong >= 65536U) {
1839		cfg_parser_error(pctx, CFG_LOG_NEAR,
1840			     "port number out of range");
1841		return (ISC_R_UNEXPECTEDTOKEN);
1842	}
1843	*port = (in_port_t)(pctx->token.value.as_ulong);
1844	return (ISC_R_SUCCESS);
1845 cleanup:
1846	return (result);
1847}
1848
1849void
1850cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
1851	isc_result_t result;
1852	char text[128];
1853	isc_buffer_t buf;
1854
1855	isc_buffer_init(&buf, text, sizeof(text));
1856	result = isc_netaddr_totext(na, &buf);
1857	RUNTIME_CHECK(result == ISC_R_SUCCESS);
1858	cfg_print_chars(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf));
1859}
1860
1861/* netaddr */
1862
1863static unsigned int netaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
1864static unsigned int netaddr4_flags = CFG_ADDR_V4OK;
1865static unsigned int netaddr4wild_flags = CFG_ADDR_V4OK | CFG_ADDR_WILDOK;
1866static unsigned int netaddr6_flags = CFG_ADDR_V6OK;
1867static unsigned int netaddr6wild_flags = CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
1868
1869static isc_result_t
1870parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1871	isc_result_t result;
1872	cfg_obj_t *obj = NULL;
1873	isc_netaddr_t netaddr;
1874	unsigned int flags = *(const unsigned int *)type->of;
1875
1876	CHECK(cfg_create_obj(pctx, type, &obj));
1877	CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
1878	isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
1879	*ret = obj;
1880	return (ISC_R_SUCCESS);
1881 cleanup:
1882	CLEANUP_OBJ(obj);
1883	return (result);
1884}
1885
1886static void
1887cfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
1888	const unsigned int *flagp = type->of;
1889	int n = 0;
1890	if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
1891		cfg_print_chars(pctx, "( ", 2);
1892	if (*flagp & CFG_ADDR_V4OK) {
1893		cfg_print_cstr(pctx, "<ipv4_address>");
1894		n++;
1895	}
1896	if (*flagp & CFG_ADDR_V6OK) {
1897		if (n != 0)
1898			cfg_print_chars(pctx, " | ", 3);
1899		cfg_print_cstr(pctx, "<ipv6_address>");
1900		n++;
1901	}
1902	if (*flagp & CFG_ADDR_WILDOK) {
1903		if (n != 0)
1904			cfg_print_chars(pctx, " | ", 3);
1905		cfg_print_chars(pctx, "*", 1);
1906		n++;
1907		POST(n);
1908	}
1909	if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
1910		cfg_print_chars(pctx, " )", 2);
1911}
1912
1913cfg_type_t cfg_type_netaddr = {
1914	"netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1915	&cfg_rep_sockaddr, &netaddr_flags
1916};
1917
1918cfg_type_t cfg_type_netaddr4 = {
1919	"netaddr4", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1920	&cfg_rep_sockaddr, &netaddr4_flags
1921};
1922
1923cfg_type_t cfg_type_netaddr4wild = {
1924	"netaddr4wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1925	&cfg_rep_sockaddr, &netaddr4wild_flags
1926};
1927
1928cfg_type_t cfg_type_netaddr6 = {
1929	"netaddr6", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1930	&cfg_rep_sockaddr, &netaddr6_flags
1931};
1932
1933cfg_type_t cfg_type_netaddr6wild = {
1934	"netaddr6wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
1935	&cfg_rep_sockaddr, &netaddr6wild_flags
1936};
1937
1938/* netprefix */
1939
1940isc_result_t
1941cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
1942		    cfg_obj_t **ret)
1943{
1944	cfg_obj_t *obj = NULL;
1945	isc_result_t result;
1946	isc_netaddr_t netaddr;
1947	unsigned int addrlen = 0, prefixlen;
1948	UNUSED(type);
1949
1950	CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
1951				CFG_ADDR_V6OK, &netaddr));
1952	switch (netaddr.family) {
1953	case AF_INET:
1954		addrlen = 32;
1955		break;
1956	case AF_INET6:
1957		addrlen = 128;
1958		break;
1959	default:
1960		INSIST(0);
1961		break;
1962	}
1963	CHECK(cfg_peektoken(pctx, 0));
1964	if (pctx->token.type == isc_tokentype_special &&
1965	    pctx->token.value.as_char == '/') {
1966		CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
1967		CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
1968		if (pctx->token.type != isc_tokentype_number) {
1969			cfg_parser_error(pctx, CFG_LOG_NEAR,
1970				     "expected prefix length");
1971			return (ISC_R_UNEXPECTEDTOKEN);
1972		}
1973		prefixlen = pctx->token.value.as_ulong;
1974		if (prefixlen > addrlen) {
1975			cfg_parser_error(pctx, CFG_LOG_NOPREP,
1976				     "invalid prefix length");
1977			return (ISC_R_RANGE);
1978		}
1979	} else {
1980		prefixlen = addrlen;
1981	}
1982	CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
1983	obj->value.netprefix.address = netaddr;
1984	obj->value.netprefix.prefixlen = prefixlen;
1985	*ret = obj;
1986	return (ISC_R_SUCCESS);
1987 cleanup:
1988	cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
1989	return (result);
1990}
1991
1992static void
1993print_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1994	const cfg_netprefix_t *p = &obj->value.netprefix;
1995
1996	cfg_print_rawaddr(pctx, &p->address);
1997	cfg_print_chars(pctx, "/", 1);
1998	cfg_print_rawuint(pctx, p->prefixlen);
1999}
2000
2001isc_boolean_t
2002cfg_obj_isnetprefix(const cfg_obj_t *obj) {
2003	REQUIRE(obj != NULL);
2004	return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
2005}
2006
2007void
2008cfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr,
2009		    unsigned int *prefixlen)
2010{
2011	REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
2012	REQUIRE(netaddr != NULL);
2013	REQUIRE(prefixlen != NULL);
2014
2015	*netaddr = obj->value.netprefix.address;
2016	*prefixlen = obj->value.netprefix.prefixlen;
2017}
2018
2019cfg_type_t cfg_type_netprefix = {
2020	"netprefix", cfg_parse_netpr

Large files files are truncated, but you can click here to view the full file